diff --git a/lib/charms/grafana_k8s/v0/grafana_dashboard.py b/lib/charms/grafana_k8s/v0/grafana_dashboard.py index cc984900..2a76dbd7 100644 --- a/lib/charms/grafana_k8s/v0/grafana_dashboard.py +++ b/lib/charms/grafana_k8s/v0/grafana_dashboard.py @@ -218,7 +218,8 @@ def __init__(self, *args): # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version -LIBPATCH = 25 + +LIBPATCH = 26 logger = logging.getLogger(__name__) @@ -256,7 +257,7 @@ def __init__(self, *args): { "allValue": None, "datasource": "${prometheusds}", - "definition": 'label_values(up{juju_model="$juju_model"},juju_model_uuid)', + "definition": 'label_values(up{juju_model=~"$juju_model"},juju_model_uuid)', "description": None, "error": None, "hide": 0, @@ -265,7 +266,7 @@ def __init__(self, *args): "multi": True, "name": "juju_model_uuid", "query": { - "query": 'label_values(up{juju_model="$juju_model"},juju_model_uuid)', + "query": 'label_values(up{juju_model=~"$juju_model"},juju_model_uuid)', "refId": "StandardVariableQuery", }, "refresh": 1, @@ -281,7 +282,7 @@ def __init__(self, *args): { "allValue": None, "datasource": "${prometheusds}", - "definition": 'label_values(up{juju_model="$juju_model",juju_model_uuid="$juju_model_uuid"},juju_application)', + "definition": 'label_values(up{juju_model=~"$juju_model",juju_model_uuid=~"$juju_model_uuid"},juju_application)', "description": None, "error": None, "hide": 0, @@ -290,7 +291,7 @@ def __init__(self, *args): "multi": True, "name": "juju_application", "query": { - "query": 'label_values(up{juju_model="$juju_model",juju_model_uuid="$juju_model_uuid"},juju_application)', + "query": 'label_values(up{juju_model=~"$juju_model",juju_model_uuid=~"$juju_model_uuid"},juju_application)', "refId": "StandardVariableQuery", }, "refresh": 1, @@ -306,7 +307,7 @@ def __init__(self, *args): { "allValue": None, "datasource": "${prometheusds}", - "definition": 'label_values(up{juju_model="$juju_model",juju_model_uuid="$juju_model_uuid",juju_application="$juju_application"},juju_unit)', + "definition": 'label_values(up{juju_model=~"$juju_model",juju_model_uuid=~"$juju_model_uuid",juju_application=~"$juju_application"},juju_unit)', "description": None, "error": None, "hide": 0, @@ -315,7 +316,7 @@ def __init__(self, *args): "multi": True, "name": "juju_unit", "query": { - "query": 'label_values(up{juju_model="$juju_model",juju_model_uuid="$juju_model_uuid",juju_application="$juju_application"},juju_unit)', + "query": 'label_values(up{juju_model=~"$juju_model",juju_model_uuid=~"$juju_model_uuid",juju_application=~"$juju_application"},juju_unit)', "refId": "StandardVariableQuery", }, "refresh": 1, @@ -336,7 +337,7 @@ def __init__(self, *args): "error": None, "hide": 0, "includeAll": True, - "label": None, + "label": "Prometheus datasource", "multi": True, "name": "prometheusds", "options": [], @@ -351,7 +352,7 @@ def __init__(self, *args): "error": None, "hide": 0, "includeAll": True, - "label": None, + "label": "Loki datasource", "multi": True, "name": "lokids", "options": [], @@ -1684,8 +1685,9 @@ def _update_remote_grafana(self, _: Optional[RelationEvent] = None) -> None: "uuid": str(uuid.uuid4()), } - for grafana_relation in self.model.relations[self._grafana_relation]: - grafana_relation.data[self._charm.app]["dashboards"] = json.dumps(stored_data) + if self._charm.unit.is_leader(): + for grafana_relation in self.model.relations[self._grafana_relation]: + grafana_relation.data[self._charm.app]["dashboards"] = json.dumps(stored_data) def remove_dashboards(self, event: RelationBrokenEvent) -> None: """Remove a dashboard if the relation is broken.""" @@ -1700,11 +1702,12 @@ def remove_dashboards(self, event: RelationBrokenEvent) -> None: "uuid": str(uuid.uuid4()), } - for grafana_relation in self.model.relations[self._grafana_relation]: - grafana_relation.data[self._charm.app]["dashboards"] = json.dumps(stored_data) + if self._charm.unit.is_leader(): + for grafana_relation in self.model.relations[self._grafana_relation]: + grafana_relation.data[self._charm.app]["dashboards"] = json.dumps(stored_data) # Yes, this has a fair amount of branching. It's not that complex, though - def _strip_existing_datasources(self, template: dict) -> dict: # noqa: C901 + def _strip_existing_datasources(self, dash: dict) -> dict: # noqa: C901 """Remove existing reactive charm datasource templating out. This method iterates through *known* places where reactive charms may set @@ -1723,7 +1726,6 @@ def _strip_existing_datasources(self, template: dict) -> dict: # noqa: C901 Further properties may be discovered. """ - dash = template["dashboard"] try: if "list" in dash["templating"]: for i in range(len(dash["templating"]["list"])): @@ -1763,11 +1765,13 @@ def _strip_existing_datasources(self, template: dict) -> dict: # noqa: C901 else: del dash["__inputs"] - template["dashboard"] = dash - return template + return dash def _handle_reactive_dashboards(self, event: RelationEvent) -> Optional[Dict]: """Look for a dashboard in relation data (during a reactive hook) or builtin by name.""" + if not self._charm.unit.is_leader(): + return {} + templates = [] id = "" @@ -1790,9 +1794,6 @@ def _handle_reactive_dashboards(self, event: RelationEvent) -> Optional[Dict]: dashboards = {} for t in templates: - # Replace values with LMA-style templating - t = self._strip_existing_datasources(t) - # This seems ridiculous, too, but to get it from a "dashboards" key in serialized JSON # in the bucket back out to the actual "dashboard" we _need_, this is the way # This is not a mistake -- there's a double nesting in reactive charms, and @@ -1803,6 +1804,9 @@ def _handle_reactive_dashboards(self, event: RelationEvent) -> Optional[Dict]: # Apparently SOME newer dashboards (such as Ceph) do not have this double nesting, so # now we get to account for both :toot: dash = t.get("dashboard", {}) or t + + # Replace values with LMA-style templating + dash = self._strip_existing_datasources(dash) dash = json.dumps(dash) # Replace the old-style datasource templates @@ -1984,7 +1988,7 @@ def inject_label_matchers(self, expression: str, topology: dict, type: str) -> s args.extend(["--", "{}".format(expression)]) # noinspection PyBroadException try: - return self._exec(args) + return re.sub(r'="\$juju', r'=~"$juju', self._exec(args)) except subprocess.CalledProcessError as e: logger.debug('Applying the expression failed: "%s", falling back to the original', e) return expression