Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autogenerate dashboard_alt_uid on the provider side #224

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 19 additions & 10 deletions lib/charms/grafana_agent/v0/cos_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ def __init__(self, *args):
)

import pydantic
from cosl import GrafanaDashboard, JujuTopology
from cosl import DashboardPath40UID, JujuTopology, LZMABase64
from cosl.rules import AlertRules
from ops.charm import RelationChangedEvent
from ops.framework import EventBase, EventSource, Object, ObjectEvents
Expand All @@ -252,9 +252,10 @@ class _MetricsEndpointDict(TypedDict):

LIBID = "dc15fa84cef84ce58155fb84f6c6213a"
LIBAPI = 0
LIBPATCH = 12
LIBPATCH = 13

PYDEPS = ["cosl", "pydantic"]
# TODO revert to "cosl" after merged
PYDEPS = ["cosl@git+https://github.com/canonical/cos-lib.git@feature/dashboard_module", "pydantic"]

DEFAULT_RELATION_NAME = "cos-agent"
DEFAULT_PEER_RELATION_NAME = "peers"
Expand Down Expand Up @@ -475,7 +476,7 @@ class CosAgentProviderUnitData(DatabagModel):
# this needs to make its way to the gagent leader
metrics_alert_rules: dict
log_alert_rules: dict
dashboards: List[GrafanaDashboard]
dashboards: List[str]
# subordinate is no longer used but we should keep it until we bump the library to ensure
# we don't break compatibility.
subordinate: Optional[bool] = None
Expand Down Expand Up @@ -508,7 +509,7 @@ class CosAgentPeersUnitData(DatabagModel):
# of the outgoing o11y relations.
metrics_alert_rules: Optional[dict]
log_alert_rules: Optional[dict]
dashboards: Optional[List[GrafanaDashboard]]
dashboards: Optional[List[str]]

# when this whole datastructure is dumped into a databag, it will be nested under this key.
# while not strictly necessary (we could have it 'flattened out' into the databag),
Expand Down Expand Up @@ -736,12 +737,20 @@ def _log_alert_rules(self) -> Dict:
return alert_rules.as_dict()

@property
def _dashboards(self) -> List[GrafanaDashboard]:
dashboards: List[GrafanaDashboard] = []
def _dashboards(self) -> List[str]:
dashboards: List[str] = []
for d in self._dashboard_dirs:
for path in Path(d).glob("*"):
dashboard = GrafanaDashboard._serialize(path.read_bytes())
dashboards.append(dashboard)
with open(path, "rt") as fp:
dashboard = json.load(fp)
rel_path = str(
path.relative_to(self._charm.charm_dir) if path.is_absolute() else path
)
# COSAgentProvider is somewhat analogous to GrafanaDashboardProvider. We need to overwrite the uid here
# because there is currently no other way to communicate the dashboard path separately.
# https://github.com/canonical/grafana-k8s-operator/pull/363
dashboard["uid"] = DashboardPath40UID.generate(self._charm.meta.name, rel_path)
dashboards.append(LZMABase64.compress(json.dumps(dashboard)))
return dashboards

@property
Expand Down Expand Up @@ -1286,7 +1295,7 @@ def dashboards(self) -> List[Dict[str, str]]:
seen_apps.append(app_name)

for encoded_dashboard in data.dashboards or ():
content = GrafanaDashboard(encoded_dashboard)._deserialize()
content = json.loads(LZMABase64.decompress(encoded_dashboard))

title = content.get("title", "no_title")

Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
# FIXME: Packing the charm with 2.2.0+139.gd011d92 will not include dependencies in PYDEPS key:
# https://chat.charmhub.io/charmhub/pl/wngp665ycjnb78ar9ojrfhxjkr
# That's why we are including cosl here until the bug in charmcraft is solved
cosl
#cosl
cosl@git+https://github.com/canonical/cos-lib.git@feature/dashboard_module
ops > 2.5.0
pydantic < 2
requests
Expand Down
12 changes: 6 additions & 6 deletions tests/scenario/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

import pydantic
import pytest
from charms.grafana_agent.v0.cos_agent import CosAgentProviderUnitData, GrafanaDashboard
from charms.grafana_agent.v0.cos_agent import CosAgentProviderUnitData, LZMABase64


class Foo(pydantic.BaseModel):
dash: List[GrafanaDashboard]
dash: List[str]


def test_dashboard_validation():
Expand All @@ -20,7 +20,7 @@ def test_dashboard_validation():

def test_dashboard_serialization():
raw_dash = {"title": "foo", "bar": "baz"}
encoded_dashboard = GrafanaDashboard._serialize(json.dumps(raw_dash))
encoded_dashboard = LZMABase64.compress(json.dumps(raw_dash))
data = Foo(dash=[encoded_dashboard])
assert data.json() == '{"dash": ["{encoded_dashboard}"]}'.replace(
"{encoded_dashboard}", encoded_dashboard
Expand All @@ -29,7 +29,7 @@ def test_dashboard_serialization():

def test_cos_agent_provider_unit_data_dashboard_serialization():
raw_dash = {"title": "title", "foo": "bar"}
encoded_dashboard = GrafanaDashboard()._serialize(json.dumps(raw_dash))
encoded_dashboard = LZMABase64.compress(json.dumps(raw_dash))
data = CosAgentProviderUnitData(
metrics_alert_rules={},
log_alert_rules={},
Expand All @@ -51,7 +51,7 @@ def test_cos_agent_provider_unit_data_dashboard_serialization():

def test_dashboard_deserialization_roundtrip():
raw_dash = {"title": "title", "foo": "bar"}
encoded_dashboard = GrafanaDashboard()._serialize(json.dumps(raw_dash))
encoded_dashboard = LZMABase64.compress(json.dumps(raw_dash))
raw = {
"metrics_alert_rules": {},
"log_alert_rules": {},
Expand All @@ -60,7 +60,7 @@ def test_dashboard_deserialization_roundtrip():
"dashboards": [encoded_dashboard],
}
data = CosAgentProviderUnitData(**raw)
assert GrafanaDashboard(data.dashboards[0])._deserialize() == raw_dash
assert json.loads(LZMABase64.decompress(data.dashboards[0])) == raw_dash


def test_cos_agent_provider_tracing_protocols_are_passed():
Expand Down
6 changes: 3 additions & 3 deletions tests/scenario/test_peer_relation.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@
from charms.prometheus_k8s.v1.prometheus_remote_write import (
PrometheusRemoteWriteConsumer,
)
from cosl import GrafanaDashboard
from cosl import LZMABase64
from ops.charm import CharmBase
from ops.framework import Framework
from ops.testing import Context, PeerRelation, State, SubordinateRelation


def encode_as_dashboard(dct: dict):
return GrafanaDashboard._serialize(json.dumps(dct).encode("utf-8"))
return LZMABase64.compress(json.dumps(dct))


def test_fetch_data_from_relation():
Expand Down Expand Up @@ -48,7 +48,7 @@ def test_fetch_data_from_relation():
data_peer_1 = data[0]
assert len(data_peer_1.dashboards) == 1
dash_out_raw = data_peer_1.dashboards[0]
assert GrafanaDashboard(dash_out_raw)._deserialize() == py_dash
assert json.loads(LZMABase64.decompress(dash_out_raw)) == py_dash


class MyRequirerCharm(CharmBase):
Expand Down
6 changes: 3 additions & 3 deletions tests/scenario/test_relation_priority.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from unittest.mock import patch

import pytest
from cosl import GrafanaDashboard
from cosl import LZMABase64
from ops.testing import Context, PeerRelation, State, SubordinateRelation

import charm
Expand Down Expand Up @@ -90,7 +90,7 @@ def test_cos_machine_relation(mock_run, charm_config):
"relation_name": "peers",
"metrics_alert_rules": {},
"log_alert_rules": {},
"dashboards": [GrafanaDashboard._serialize('{"very long": "dashboard"}')],
"dashboards": [LZMABase64.compress(json.dumps('{"very long": "dashboard"}'))],
}
)
}
Expand Down Expand Up @@ -147,7 +147,7 @@ def test_both_relations(mock_run, charm_config):
"relation_name": "peers",
"metrics_alert_rules": {},
"log_alert_rules": {},
"dashboards": [GrafanaDashboard._serialize('{"very long": "dashboard"}')],
"dashboards": [LZMABase64.compress(json.dumps('{"very long": "dashboard"}'))],
}
)
}
Expand Down
Loading