diff --git a/src/charm.py b/src/charm.py index 1729194..8b0ef9c 100755 --- a/src/charm.py +++ b/src/charm.py @@ -9,6 +9,7 @@ """Charm for Gunicorn on kubernetes.""" import json import logging +import textwrap import typing from collections.abc import MutableMapping from typing import Union @@ -187,35 +188,6 @@ def _get_gunicorn_pebble_config(self, event: ops.framework.EventBase) -> ops.peb pebble_config["services"]["gunicorn"]["environment"] = pod_env_config return typing.cast(ops.pebble.LayerDict, pebble_config) - def _get_statsd_pebble_config(self) -> ops.pebble.LayerDict: - """Generate statsd exporter pebble config. - - Returns: - Statsd container's pebble config - """ - pebble_config = { - "summary": "statsd exporter layer", - "description": "statsd exporter layer", - "services": { - "statsd-prometheus-exporter": { - "override": "replace", - "summary": "statsd exporter service", - "user": "nobody", - "command": "/bin/statsd_exporter", - "startup": "enabled", - } - }, - "checks": { - "container-ready": { - "override": "replace", - "level": "ready", - "http": {"url": "http://localhost:9102/metrics"}, - }, - }, - } - - return typing.cast(ops.pebble.LayerDict, pebble_config) - def _on_config_changed(self, event: ops.framework.EventBase) -> None: """Handle the config changed event. @@ -246,13 +218,49 @@ def _on_show_environment_context_action(self, event: ops.charm.ActionEvent) -> N event.set_results({"available-variables": json.dumps(ctx, indent=4)}) - def _on_statsd_prometheus_exporter_pebble_ready(self, event: ops.framework.EventBase) -> None: + def _on_statsd_prometheus_exporter_pebble_ready(self, _event: ops.framework.EventBase) -> None: """Handle the workload ready event. Args: - event: Event triggering this handler. + _event: Event triggering this handler. """ - self._configure_workload(event) + statsd_container = self.unit.get_container("statsd-prometheus-exporter") + statsd_layer = ops.pebble.LayerDict( + **{ + "summary": "statsd exporter layer", + "description": "statsd exporter layer", + "services": { + "statsd-prometheus-exporter": { + "override": "replace", + "summary": "statsd exporter service", + "user": "nobody", + "command": "/bin/statsd_exporter --statsd.mapping-config=/statsd.conf", + "startup": "enabled", + } + }, + "checks": { + "container-ready": { + "override": "replace", + "level": "ready", + "http": {"url": "http://localhost:9102/metrics"}, + }, + }, + } + ) + statsd_container.push( + "/statsd.conf", + textwrap.dedent( + """\ + mappings: + - match: gunicorn.request.status.* + name: gunicorn_response_code + labels: + status: $1 + """ + ), + ) + statsd_container.add_layer("statsd-prometheus-exporter", statsd_layer, combine=True) + statsd_container.pebble.replan_services() def _configure_workload(self, event: ops.charm.EventBase) -> None: """Configure the workload container. @@ -264,7 +272,6 @@ def _configure_workload(self, event: ops.charm.EventBase) -> None: if not gunicorn_pebble_config: # Charm will be in blocked status. return - statsd_pebble_config = self._get_statsd_pebble_config() # Ensure the ingress relation has the external hostname. self.ingress.update_config( @@ -275,9 +282,8 @@ def _configure_workload(self, event: ops.charm.EventBase) -> None: ) gunicorn_container = self.unit.get_container("gunicorn") - statsd_container = self.unit.get_container("statsd-prometheus-exporter") # pebble may not be ready, in which case we just return - if not gunicorn_container.can_connect() or not statsd_container.can_connect(): + if not gunicorn_container.can_connect(): self.unit.status = MaintenanceStatus("waiting for pebble to start") logger.debug("waiting for pebble to start") return @@ -294,11 +300,6 @@ def _configure_workload(self, event: ops.charm.EventBase) -> None: ) return - statsd_container.add_layer( - "statsd-prometheus-exporter", statsd_pebble_config, combine=True - ) - statsd_container.pebble.replan_services() - self.unit.status = ActiveStatus() def _init_postgresql_relation(self) -> None: diff --git a/src/grafana_dashboards/gunicorn.json b/src/grafana_dashboards/gunicorn.json index 56458af..734fb4b 100644 --- a/src/grafana_dashboards/gunicorn.json +++ b/src/grafana_dashboards/gunicorn.json @@ -1,842 +1,948 @@ { - "__inputs": [ + "annotations": { + "list": [ { - "name": "prometheusds", - "label": "Prometheus", - "description": "Gunicorn Metrics", - "type": "datasource", - "pluginId": "prometheus", - "pluginName": "Prometheus" + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" } - ], - "__elements": [], - "__requires": [ - { - "type": "grafana", - "id": "grafana", - "name": "Grafana", - "version": "8.4.3" + ] + }, + "description": "Dashboard for the Gunicorn Operator, powered by Juju.", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" }, - { - "type": "datasource", - "id": "prometheus", - "name": "Prometheus", - "version": "1.0.0" + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] }, - { - "type": "panel", - "id": "stat", - "name": "Stat", - "version": "" + "gridPos": { + "h": 6, + "w": 5, + "x": 0, + "y": 0 }, - { - "type": "panel", - "id": "timeseries", - "name": "Time series", - "version": "" - } - ], - "annotations": { - "list": [ + "id": 9, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.4.3", + "targets": [ { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" }, - "type": "dashboard" + "exemplar": true, + "expr": "increase(gunicorn_requests{juju_unit=~\"$juju_unit\",juju_application=~\"$juju_application\",juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\"}[24h])", + "interval": "", + "legendFormat": "", + "refId": "A" } - ] + ], + "timeFrom": "24h", + "title": "Request Count", + "type": "stat" }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": null, - "iteration": 1650904622261, - "links": [ - { - "icon": "doc", - "tags": [], - "targetBlank": true, - "title": "Docs", - "tooltip": "Official documentation of Gunicorn Operator", - "type": "link", - "url": "https://charmhub.io/gunicorn-k8s" + { + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" + }, + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 7, + "x": 5, + "y": 0 + }, + "id": 11, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false }, + "textMode": "value_and_name" + }, + "pluginVersion": "8.4.3", + "targets": [ { - "icon": "info", - "tags": [], - "targetBlank": true, - "title": "GitHub", - "tooltip": "Gunicorn Operator sources on GitHub", - "type": "link", - "url": "https://github.com/canonical/gunicorn-k8s-operator" + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" + }, + "exemplar": true, + "expr": "increase(gunicorn_response_code{juju_unit=~\"$juju_unit\",juju_application=~\"$juju_application\",juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\"}[24h])", + "interval": "", + "legendFormat": "{{status}}", + "refId": "A" } - ], - "liveNow": false, - "panels": [ - { - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 5, - "x": 0, - "y": 0 - }, - "id": 9, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.4.3", - "targets": [ - { - "datasource": "${prometheusds}", - "exemplar": true, - "expr": "gunicorn_requests{app=\"$app_name\"}", - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "timeFrom": "24h", - "title": "Request Count", - "type": "stat" + ], + "timeFrom": "24h", + "title": "Status Code Count", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" }, - { - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 7, - "x": 5, - "y": 0 - }, - "id": 11, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "center", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "value_and_name" - }, - "pluginVersion": "8.4.3", - "targets": [ - { - "datasource": "${prometheusds}", - "exemplar": true, - "expr": "gunicorn_response_code{app=\"$app_name\"}", - "interval": "", - "legendFormat": "{{status}}", - "refId": "A" - } - ], - "timeFrom": "24h", - "title": "Status Code Count", - "type": "stat" - }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } }, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 0 + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" }, - "id": 7, - "options": { - "legend": { - "calcs": [], - "displayMode": "hidden", - "placement": "bottom" + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": "${prometheusds}", - "exemplar": true, - "expr": "rate(gunicorn_requests{app=\"$app_name\"}[1m])", - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "title": "Request Per Sec", - "type": "timeseries" + "exemplar": true, + "expr": "rate(gunicorn_requests{juju_unit=~\"$juju_unit\",juju_application=~\"$juju_application\",juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\"}[3m])", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Request Per Sec", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "area" - } + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" }, - "mappings": [], - "max": 1, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "green", - "value": 0.8 - } - ] + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" }, - "unit": "percentunit" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 6, - "x": 0, - "y": 6 - }, - "id": 2, - "options": { - "legend": { - "calcs": [], - "displayMode": "hidden", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } + "thresholdsStyle": { + "mode": "area" + } + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 0.8 + } + ] + }, + "unit": "percentunit" }, - "targets": [ - { - "datasource": "${prometheusds}", - "exemplar": true, - "expr": "sum(gunicorn_response_code{app=\"$app_name\", status=~\"2.*\"}) / sum(gunicorn_response_code{app=\"$app_name\"})", - "interval": "", - "legendFormat": "2XX Rate", - "refId": "A" - } - ], - "title": "2XX Rate", - "type": "timeseries" + "overrides": [] }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" + "gridPos": { + "h": 6, + "w": 6, + "x": 0, + "y": 6 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" + }, + "exemplar": true, + "expr": "sum(gunicorn_response_code{juju_unit=~\"$juju_unit\",juju_application=~\"$juju_application\",juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\", status=~\"2.*\"}) / sum(gunicorn_response_code{juju_unit=~\"$juju_unit\",juju_application=~\"$juju_application\",juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\"})", + "interval": "", + "legendFormat": "2XX Rate", + "refId": "A" + } + ], + "title": "2XX Rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" }, - "mappings": [], - "max": 1, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" }, - "unit": "percentunit" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 6, - "x": 6, - "y": 6 - }, - "id": 3, - "options": { - "legend": { - "calcs": [], - "displayMode": "hidden", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" }, - "targets": [ - { - "datasource": "${prometheusds}", - "exemplar": true, - "expr": "sum(gunicorn_response_code{app=\"$app_name\", status=~\"3.*\"}) / sum(gunicorn_response_code{app=\"$app_name\"})", - "interval": "", - "legendFormat": "3XX Rate", - "refId": "A" - } - ], - "title": "3XX Rate", - "type": "timeseries" + "overrides": [] }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" + "gridPos": { + "h": 6, + "w": 6, + "x": 6, + "y": 6 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" + }, + "exemplar": true, + "expr": "sum(gunicorn_response_code{juju_unit=~\"$juju_unit\",juju_application=~\"$juju_application\",juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\", status=~\"3.*\"}) / sum(gunicorn_response_code{juju_unit=~\"$juju_unit\",juju_application=~\"$juju_application\",juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\"})", + "interval": "", + "legendFormat": "3XX Rate", + "refId": "A" + } + ], + "title": "3XX Rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" }, - "mappings": [], - "max": 1, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" }, - "unit": "percentunit" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 6, - "x": 12, - "y": 6 - }, - "id": 4, - "options": { - "legend": { - "calcs": [], - "displayMode": "hidden", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" }, - "targets": [ - { - "datasource": "${prometheusds}", - "exemplar": true, - "expr": "sum(gunicorn_response_code{app=\"$app_name\", status=~\"4.*\"}) / sum(gunicorn_response_code{app=\"$app_name\"})", - "interval": "", - "legendFormat": "4XX Rate", - "refId": "A" - } - ], - "title": "4XX Rate", - "type": "timeseries" + "overrides": [] }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" + "gridPos": { + "h": 6, + "w": 6, + "x": 12, + "y": 6 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" + }, + "exemplar": true, + "expr": "sum(gunicorn_response_code{juju_unit=~\"$juju_unit\",juju_application=~\"$juju_application\",juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\", status=~\"4.*\"}) / sum(gunicorn_response_code{juju_unit=~\"$juju_unit\",juju_application=~\"$juju_application\",juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\"})", + "interval": "", + "legendFormat": "4XX Rate", + "refId": "A" + } + ], + "title": "4XX Rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "area" - } + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" }, - "mappings": [], - "max": 1, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 0.1 - } - ] + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" }, - "unit": "percentunit" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 6, - "x": 18, - "y": 6 - }, - "id": 5, - "options": { - "legend": { - "calcs": [], - "displayMode": "hidden", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } + "thresholdsStyle": { + "mode": "area" + } + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 0.1 + } + ] + }, + "unit": "percentunit" }, - "targets": [ - { - "datasource": "${prometheusds}", - "exemplar": true, - "expr": "sum(gunicorn_response_code{app=\"$app_name\", status=~\"5.*\"}) / sum(gunicorn_response_code{app=\"$app_name\"})", - "interval": "", - "legendFormat": "5XX Rate", - "refId": "A" - } - ], - "title": "5XX Rate", - "type": "timeseries" + "overrides": [] }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 6 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" + }, + "exemplar": true, + "expr": "sum(gunicorn_response_code{juju_unit=~\"$juju_unit\",juju_application=~\"$juju_application\",juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\", status=~\"5.*\"}) / sum(gunicorn_response_code{juju_unit=~\"$juju_unit\",juju_application=~\"$juju_application\",juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\"})", + "interval": "", + "legendFormat": "5XX Rate", + "refId": "A" + } + ], + "title": "5XX Rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 12 - }, - "id": 13, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" }, - "targets": [ - { - "datasource": "${prometheusds}", - "exemplar": true, - "expr": "gunicorn_request_duration_sum{app=\"$app_name\"} / gunicorn_request_duration_count{app=\"$app_name\"}", - "interval": "", - "legendFormat": "{{app}}", - "refId": "A" - } - ], - "title": "Request Average Duration", - "type": "timeseries" + "overrides": [] }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 12 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" + }, + "exemplar": true, + "expr": "gunicorn_request_duration_sum{juju_unit=~\"$juju_unit\",juju_application=~\"$juju_application\",juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\"} / gunicorn_request_duration_count{juju_unit=~\"$juju_unit\",juju_application=~\"$juju_application\",juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\"}", + "interval": "", + "legendFormat": "{{app}}", + "refId": "A" + } + ], + "title": "Request Average Duration", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 12 - }, - "id": 15, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 12 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" + }, + "exemplar": true, + "expr": "gunicorn_request_duration{juju_unit=~\"$juju_unit\",juju_application=~\"$juju_application\",juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\", quantile=\"0.5\"}", + "interval": "", + "legendFormat": "PR 50", + "refId": "A" }, - "targets": [ - { - "datasource": "${prometheusds}", - "exemplar": true, - "expr": "gunicorn_request_duration{app=\"$app_name\", quantile=\"0.5\"}", - "interval": "", - "legendFormat": "PR 50", - "refId": "A" - }, - { - "datasource": "${prometheusds}", - "exemplar": true, - "expr": "gunicorn_request_duration{app=\"$app_name\", quantile=\"0.9\"}", - "hide": false, - "interval": "", - "legendFormat": "PR 90", - "refId": "B" - }, - { - "datasource": "${prometheusds}", - "exemplar": true, - "expr": "gunicorn_request_duration{app=\"$app_name\", quantile=\"0.99\"}", - "hide": false, - "interval": "", - "legendFormat": "PR 99", - "refId": "C" - } - ], - "title": "Request Duration Percentile", - "type": "timeseries" - } - ], - "refresh": "5s", - "schemaVersion": 35, - "style": "dark", - "tags": [ - "gunicorn", - "prometheus", - "gunicorn prometheus exporter" - ], - "templating": { - "list": [ { - "current": {}, - "datasource": "${prometheusds}", - "definition": "label_values(gunicorn_workers{}, app)", - "hide": 0, - "includeAll": false, - "label": "Application Name", - "multi": false, - "name": "app_name", - "options": [], - "query": { - "query": "label_values(gunicorn_workers{}, app)", - "refId": "StandardVariableQuery" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "type": "query" + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" + }, + "exemplar": true, + "expr": "gunicorn_request_duration{juju_unit=~\"$juju_unit\",juju_application=~\"$juju_application\",juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\", quantile=\"0.9\"}", + "hide": false, + "interval": "", + "legendFormat": "PR 90", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${prometheusds}" + }, + "exemplar": true, + "expr": "gunicorn_request_duration{juju_unit=~\"$juju_unit\",juju_application=~\"$juju_application\",juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\", quantile=\"0.99\"}", + "hide": false, + "interval": "", + "legendFormat": "PR 99", + "refId": "C" } - ] - }, - "time": { - "from": "now-15m", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "Gunicorn Operator", - "uid": "nN74v-wnz11", - "version": 1, - "weekStart": "", - "gnetId": null, - "description": "Dashboard for the Gunicorn Operator, powered by Juju." - } + ], + "title": "Request Duration Percentile", + "type": "timeseries" + } + ], + "refresh": "5s", + "schemaVersion": 35, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": ".*", + "datasource": { + "uid": "${prometheusds}" + }, + "definition": "label_values(gunicorn_requests{juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\",juju_application=~\"$juju_application\"},juju_unit)", + "hide": 0, + "includeAll": true, + "label": "Juju unit", + "multi": true, + "name": "juju_unit", + "options": [], + "query": { + "query": "label_values(gunicorn_requests{juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\",juju_application=~\"$juju_application\"},juju_unit)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "uid": "${prometheusds}" + }, + "definition": "label_values(gunicorn_requests{juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\"},juju_application)", + "hide": 0, + "includeAll": true, + "label": "Juju application", + "multi": true, + "name": "juju_application", + "options": [], + "query": { + "query": "label_values(gunicorn_requests{juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\"},juju_application)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "uid": "${prometheusds}" + }, + "definition": "label_values(gunicorn_requests{juju_model=~\"$juju_model\"},juju_model_uuid)", + "hide": 0, + "includeAll": true, + "label": "Juju model uuid", + "multi": true, + "name": "juju_model_uuid", + "options": [], + "query": { + "query": "label_values(gunicorn_requests{juju_model=~\"$juju_model\"},juju_model_uuid)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "uid": "${prometheusds}" + }, + "definition": "label_values(up,juju_model)", + "hide": 0, + "includeAll": true, + "label": "Juju model", + "multi": true, + "name": "juju_model", + "options": [], + "query": { + "query": "label_values(up,juju_model)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Gunicorn Operator", + "uid": null, + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py index f8d6f44..6f4fc2a 100755 --- a/tests/unit/test_charm.py +++ b/tests/unit/test_charm.py @@ -9,7 +9,7 @@ from unittest.mock import MagicMock, patch from ops import pebble, testing -from ops.model import ActiveStatus, BlockedStatus, MaintenanceStatus +from ops.model import ActiveStatus, BlockedStatus from scenario import ( # pylint: disable=import-error JUJU_DEFAULT_CONFIG, TEST_PG_CONNSTR, @@ -509,28 +509,6 @@ def test_configure_workload_no_problem(self): result = self.harness.charm._configure_workload(mock_event) self.assertEqual(result, expected_ret) - def test_configure_workload_gunicorn_pebble_not_ready(self): - """ - arrange: given the deployed charm's statsd container - act: mark it as ready - assert: the deployment must be in maintenance - """ - self.harness.container_pebble_ready("statsd-prometheus-exporter") - self.assertEqual( - self.harness.model.unit.status, MaintenanceStatus("waiting for pebble to start") - ) - - def test_configure_workload_statsd_pebble_not_ready(self): - """ - arrange: given the deployed charm's gunicorn container - act: mark it as ready - assert: the deployment must be in maintenance - """ - self.harness.container_pebble_ready("gunicorn") - self.assertEqual( - self.harness.model.unit.status, MaintenanceStatus("waiting for pebble to start") - ) - def test_configure_workload_exception(self): """ arrange: given the deployed charm's containers @@ -540,7 +518,6 @@ def test_configure_workload_exception(self): with patch("ops.model.Container.pebble", return_value=MagicMock()) as pebble_mock: pebble_mock.replan_services.side_effect = pebble.ChangeError("abc", "def") self.harness.container_pebble_ready("gunicorn") - self.harness.container_pebble_ready("statsd-prometheus-exporter") self.assertEqual( self.harness.model.unit.status, BlockedStatus("Charm's startup command may be wrong, please check the config"),