Skip to content

Commit

Permalink
chore: update charm libraries (#117)
Browse files Browse the repository at this point in the history
Co-authored-by: Github Actions <github-actions@github.com>
  • Loading branch information
observability-noctua-bot and Github Actions authored Jan 23, 2023
1 parent a0b0ecc commit 9196c48
Show file tree
Hide file tree
Showing 4 changed files with 301 additions and 218 deletions.
78 changes: 49 additions & 29 deletions lib/charms/grafana_k8s/v0/grafana_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ 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 = 21
LIBPATCH = 22

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -1047,7 +1047,7 @@ def __init__(
self._dashboards_path = dashboards_path

# No peer relation bucket we can rely on providers, keep StoredState here, too
self._stored.set_default(dashboard_templates={})
self._stored.set_default(dashboard_templates={}) # type: ignore

self.framework.observe(self._charm.on.leader_elected, self._update_all_dashboards_from_dir)
self.framework.observe(self._charm.on.upgrade_charm, self._update_all_dashboards_from_dir)
Expand All @@ -1073,7 +1073,7 @@ def add_dashboard(self, content: str, inject_dropdowns: bool = True) -> None:
"""
# Update of storage must be done irrespective of leadership, so
# that the stored state is there when this unit becomes leader.
stored_dashboard_templates = self._stored.dashboard_templates
stored_dashboard_templates = self._stored.dashboard_templates # type: Any

encoded_dashboard = _encode_dashboard_content(content)

Expand All @@ -1094,7 +1094,7 @@ def remove_non_builtin_dashboards(self) -> None:
"""Remove all dashboards to the relation added via :method:`add_dashboard`."""
# Update of storage must be done irrespective of leadership, so
# that the stored state is there when this unit becomes leader.
stored_dashboard_templates = self._stored.dashboard_templates
stored_dashboard_templates = self._stored.dashboard_templates # type: Any

for dashboard_id in list(stored_dashboard_templates.keys()):
if dashboard_id.startswith("prog:"):
Expand All @@ -1121,7 +1121,7 @@ def _update_all_dashboards_from_dir(
# Ensure we do not leave outdated dashboards by removing from stored all
# the encoded dashboards that start with "file/".
if self._dashboards_path:
stored_dashboard_templates = self._stored.dashboard_templates
stored_dashboard_templates = self._stored.dashboard_templates # type: Any

for dashboard_id in list(stored_dashboard_templates.keys()):
if dashboard_id.startswith("file:"):
Expand Down Expand Up @@ -1175,7 +1175,7 @@ def _reinitialize_dashboard_data(self, inject_dropdowns: bool = True) -> None:
e.grafana_dashboards_absolute_path,
e.message,
)
stored_dashboard_templates = self._stored.dashboard_templates
stored_dashboard_templates = self._stored.dashboard_templates # type: Any

for dashboard_id in list(stored_dashboard_templates.keys()):
if dashboard_id.startswith("file:"):
Expand Down Expand Up @@ -1205,7 +1205,7 @@ def _on_grafana_dashboard_relation_changed(self, event: RelationChangedEvent) ->
event: The `RelationChangedEvent` that triggered this handler.
"""
if self._charm.unit.is_leader():
data = json.loads(event.relation.data[event.app].get("event", "{}"))
data = json.loads(event.relation.data[event.app].get("event", "{}")) # type: ignore

if not data:
return
Expand Down Expand Up @@ -1251,7 +1251,7 @@ def _juju_topology(self) -> Dict:
@property
def dashboard_templates(self) -> List:
"""Return a list of the known dashboard templates."""
return [v for v in self._stored.dashboard_templates.values()]
return [v for v in self._stored.dashboard_templates.values()] # type: ignore


class GrafanaDashboardConsumer(Object):
Expand Down Expand Up @@ -1305,7 +1305,7 @@ def __init__(
self._relation_name = relation_name
self._tranformer = CosTool(self._charm)

self._stored.set_default(dashboards=dict())
self._stored.set_default(dashboards=dict()) # type: ignore

self.framework.observe(
self._charm.on[self._relation_name].relation_changed,
Expand Down Expand Up @@ -1588,9 +1588,8 @@ class GrafanaDashboardAggregator(Object):
The :class:`GrafanaDashboardAggregator` object provides a way to
collate and aggregate Grafana dashboards from reactive/machine charms
and transport them into Charmed Operators, using Juju topology.
For detailed usage instructions, see the documentation for
:module:`lma-proxy-operator`, as this class is intended for use as a
:module:`cos-proxy-operator`, as this class is intended for use as a
single point of intersection rather than use in individual charms.
Since :class:`GrafanaDashboardAggregator` serves as a bridge between
Expand All @@ -1602,7 +1601,6 @@ class GrafanaDashboardAggregator(Object):
In its most streamlined usage, :class:`GrafanaDashboardAggregator` is
integrated in a charmed operator as follows:
self.grafana = GrafanaDashboardAggregator(self)
Args:
Expand Down Expand Up @@ -1630,7 +1628,7 @@ def __init__(

# Reactive charms may be RPC-ish and not leave reliable data around. Keep
# StoredState here
self._stored.set_default(
self._stored.set_default( # type: ignore
dashboard_templates={},
id_mappings={},
)
Expand Down Expand Up @@ -1672,11 +1670,11 @@ def _upset_dashboards_on_event(self, event: RelationEvent) -> None:
return

for id in dashboards:
self._stored.dashboard_templates[id] = self._content_to_dashboard_object(
self._stored.dashboard_templates[id] = self._content_to_dashboard_object( # type: ignore
dashboards[id], event
)

self._stored.id_mappings[event.app.name] = dashboards
self._stored.id_mappings[event.app.name] = dashboards # type: ignore
self._update_remote_grafana(event)

def _update_remote_grafana(self, _: Optional[RelationEvent] = None) -> None:
Expand All @@ -1692,11 +1690,11 @@ def _update_remote_grafana(self, _: Optional[RelationEvent] = None) -> None:

def remove_dashboards(self, event: RelationBrokenEvent) -> None:
"""Remove a dashboard if the relation is broken."""
app_ids = _type_convert_stored(self._stored.id_mappings[event.app.name])
app_ids = _type_convert_stored(self._stored.id_mappings[event.app.name]) # type: ignore

del self._stored.id_mappings[event.app.name]
del self._stored.id_mappings[event.app.name] # type: ignore
for id in app_ids:
del self._stored.dashboard_templates[id]
del self._stored.dashboard_templates[id] # type: ignore

stored_data = {
"templates": _type_convert_stored(self._stored.dashboard_templates),
Expand Down Expand Up @@ -1740,6 +1738,19 @@ def _strip_existing_datasources(self, template: dict) -> dict: # noqa: C901
and dash["templating"]["list"][i]["name"] == "host"
):
dash["templating"]["list"][i] = REACTIVE_CONVERTER

# Strip out newly-added 'juju_application' template variables which
# don't line up with our drop-downs
dash_mutable = dash
for i in range(len(dash["templating"]["list"])):
if (
"name" in dash["templating"]["list"][i]
and dash["templating"]["list"][i]["name"] == "app"
):
del dash_mutable["templating"]["list"][i]

if dash_mutable:
dash = dash_mutable
except KeyError:
logger.debug("No existing templating data in dashboard")

Expand All @@ -1764,17 +1775,18 @@ def _handle_reactive_dashboards(self, event: RelationEvent) -> Optional[Dict]:
# Reactive data can reliably be pulled out of events. In theory, if we got an event,
# it's on the bucket, but using event explicitly keeps the mental model in
# place for reactive
for k in event.relation.data[event.unit].keys():
for k in event.relation.data[event.unit].keys(): # type: ignore
if k.startswith("request_"):
templates.append(json.loads(event.relation.data[event.unit][k])["dashboard"])
templates.append(json.loads(event.relation.data[event.unit][k])["dashboard"]) # type: ignore

for k in event.relation.data[event.app].keys():
for k in event.relation.data[event.app].keys(): # type: ignore
if k.startswith("request_"):
templates.append(json.loads(event.relation.data[event.app][k])["dashboard"])
templates.append(json.loads(event.relation.data[event.app][k])["dashboard"]) # type: ignore

builtins = self._maybe_get_builtin_dashboards(event)

if not templates and not builtins:
logger.warning("NOTHING!")
return {}

dashboards = {}
Expand All @@ -1793,11 +1805,19 @@ def _handle_reactive_dashboards(self, event: RelationEvent) -> Optional[Dict]:
# Replace the old-style datasource templates
dash = re.sub(r"<< datasource >>", r"${prometheusds}", dash)
dash = re.sub(r'"datasource": "prom.*?"', r'"datasource": "${prometheusds}"', dash)
dash = re.sub(
r'"datasource": "(!?\w)[\w|\s|-]+?Juju generated.*?"',
r'"datasource": "${prometheusds}"',
dash,
)

# Yank out "new"+old LMA topology
dash = re.sub(r'(,?juju_application=~)"\$app"', r'\1"\$juju_application"', dash)

from jinja2 import Template

content = _encode_dashboard_content(
Template(dash).render(host=event.unit.name, datasource="prometheus")
Template(dash).render(host=r"$host", datasource=r"${prometheusds}") # type: ignore
)
id = "prog:{}".format(content[-24:-16])

Expand Down Expand Up @@ -1828,12 +1848,12 @@ def _maybe_get_builtin_dashboards(self, event: RelationEvent) -> Dict:

if dashboards_path:

def _is_dashboard(p: Path) -> bool:
def is_dashboard(p: Path) -> bool:
return p.is_file() and p.name.endswith((".json", ".json.tmpl", ".tmpl"))

for path in filter(_is_dashboard, Path(dashboards_path).glob("*")):
for path in filter(is_dashboard, Path(dashboards_path).glob("*")):
# path = Path(path)
if event.app.name in path.name:
if event.app.name in path.name: # type: ignore
id = "file:{}".format(path.stem)
builtins[id] = self._content_to_dashboard_object(
_encode_dashboard_content(path.read_bytes()), event
Expand All @@ -1843,7 +1863,7 @@ def _is_dashboard(p: Path) -> bool:

def _content_to_dashboard_object(self, content: str, event: RelationEvent) -> Dict:
return {
"charm": event.app.name,
"charm": event.app.name, # type: ignore
"content": content,
"juju_topology": self._juju_topology(event),
"inject_dropdowns": True,
Expand All @@ -1856,8 +1876,8 @@ def _juju_topology(self, event: RelationEvent) -> Dict:
return {
"model": self._charm.model.name,
"model_uuid": self._charm.model.uuid,
"application": event.app.name,
"unit": event.unit.name,
"application": event.app.name, # type: ignore
"unit": event.unit.name, # type: ignore
}


Expand Down
11 changes: 7 additions & 4 deletions lib/charms/grafana_k8s/v0/grafana_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ 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 = 13
LIBPATCH = 14

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -459,6 +459,9 @@ def _set_unit_details(self, _: Union[BoundEvent, RelationEvent, Relation]):
"""
for relation in self._charm.model.relations[self._relation_name]:
url = self._source_url or "{}:{}".format(socket.getfqdn(), self._source_port)
if self._source_type == "mimir":
url = f"{url}/prometheus"

relation.data[self._charm.unit]["grafana_source_host"] = url


Expand Down Expand Up @@ -495,7 +498,7 @@ def __init__(

# We're stuck with this forever now so upgrades work, or until such point as we can
# break compatibility
self._stored.set_default(
self._stored.set_default( # type: ignore
sources=dict(),
sources_to_delete=set(),
)
Expand Down Expand Up @@ -664,13 +667,13 @@ def upgrade_keys(self) -> None:
)

# If there's stored data, merge it and purge it
if self._stored.sources:
if self._stored.sources: # type: ignore
self._stored.sources = {}
peer_sources = self.get_peer_data("sources")
sources.update(peer_sources)
self.set_peer_data("sources", sources)

if self._stored.sources_to_delete:
if self._stored.sources_to_delete: # type: ignore
old_sources_to_delete = _type_convert_stored(self._stored.sources_to_delete)
self._stored.sources_to_delete = set()
peer_sources_to_delete = set(self.get_peer_data("sources_to_delete"))
Expand Down
Loading

0 comments on commit 9196c48

Please sign in to comment.