Skip to content

Commit

Permalink
Use helper for mandatory relation pairs (#286)
Browse files Browse the repository at this point in the history
  • Loading branch information
sed-i authored Apr 10, 2024
1 parent 6e26257 commit d98de15
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 65 deletions.
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ lightkube-models
cryptography
jsonschema < 4 # Pin prevents the machine charm error "ModuleNotFoundError: No module named 'rpds.rpds'"
# Deps: tracing
opentelemetry-exporter-otlp-proto-http>=1.21.0
opentelemetry-exporter-otlp-proto-http>=1.21.0
82 changes: 26 additions & 56 deletions src/grafana_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from charms.prometheus_k8s.v1.prometheus_remote_write import (
PrometheusRemoteWriteConsumer,
)
from cosl import MandatoryRelationPairs
from ops.charm import CharmBase
from ops.model import ActiveStatus, BlockedStatus, WaitingStatus
from ops.pebble import APIError, PathError
Expand Down Expand Up @@ -446,70 +447,39 @@ def _update_status(self, *_):
self.unit.status = self.status.validation_error
return

has_incoming = False
outgoing_rels = {"has": False, "message": ""}

# Make sure every incoming relation has at least one matching outgoing relation
for incoming, outgoings in self.mandatory_relation_pairs.items():
if not self.model.relations.get(incoming):
continue

has_incoming = True
outgoing_rels = self._has_outgoings(outgoings)

if not has_incoming:
self.unit.status = BlockedStatus("Missing incoming ('requires') relation")
# Put charm in blocked status if all incoming relations are missing
active_relations = {k for k, v in self.model.relations.items() if v}
if not set(self.mandatory_relation_pairs.keys()).intersection(active_relations):
self.unit.status = BlockedStatus(
"Missing incoming ('requires') relation: {}".format(
"|".join(self.mandatory_relation_pairs.keys())
)
)
return

if not outgoing_rels["has"]:
self.unit.status = BlockedStatus(f"{outgoing_rels['message']}")
if missing := MandatoryRelationPairs(self.mandatory_relation_pairs).get_missing_as_str(
*active_relations
):
self.unit.status = BlockedStatus(f"Missing {missing}")
return

if not self.is_ready:
self.unit.status = WaitingStatus("waiting for the agent to start")
return

self.unit.status = ActiveStatus(f"{outgoing_rels['message']}")

def _has_outgoings(self, outgoings: List[Set[str]]) -> Dict[str, Any]:
missing_rels = set()
active_rels = set()

for outgoing_list in outgoings:
for outgoing in outgoing_list:
relation = self.model.relations.get(outgoing, False)

if not relation:
missing_rels.add(outgoing)
continue

try:
units = relation[0].units # pyright: ignore
except IndexError:
missing_rels.add(outgoing)
units = None

if not units:
missing_rels.add(outgoing)

if relation and units:
active_rels.add(outgoing)

return {"has": bool(active_rels), "message": self._status_message(missing_rels)}

def _status_message(self, missing_relations: set) -> str:
if not missing_relations:
return ""

# The grafana-cloud-config relation is established
if "grafana-cloud-config" not in missing_relations:
return ""

# The other 3 relations (logs, metrics, dashboards) are established
if len(missing_relations) == 1 and "grafana-cloud-config" in missing_relations:
return ""

return ", ".join([f"{x}: off" for x in missing_relations])
# If only _some_ of the COS relations are present, we do not want to block, but we do want
# to inform via the Active message that they are in fact missing ("soft" warning).
cos_rels = {
"send-remote-write",
"logging-consumer",
"grafana-dashboards-provider",
}
missing_rels = (
cos_rels.difference(active_relations)
if cos_rels.intersection(active_relations)
else set()
)
self.unit.status = ActiveStatus(", ".join([f"{x}: off" for x in missing_rels]))

def _update_config(self) -> None:
if not self.is_ready:
Expand Down
4 changes: 3 additions & 1 deletion tests/scenario/test_setup_statuses.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ def test_charm_start_with_container(charm_type, substrate, vroot):
state = State(containers=[agent])
out = context.run(agent.pebble_ready_event, state)

assert out.unit_status == BlockedStatus("Missing incoming ('requires') relation")
assert out.unit_status == BlockedStatus(
"Missing incoming ('requires') relation: metrics-endpoint|logging-provider|grafana-dashboards-consumer"
)
agent_out = out.get_container("agent")
assert agent_out.services["agent"].current == pebble.ServiceStatus.ACTIVE
12 changes: 6 additions & 6 deletions tests/unit/test_relation_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,21 @@ def test_with_relations(self):
]:
with self.subTest(incoming=incoming, outgoing=outgoing):
# WHEN an incoming relation is added
rel_incoming_id = self.harness.add_relation(incoming, "grafana-agent")
self.harness.add_relation_unit(rel_incoming_id, "grafana-agent/0")
rel_incoming_id = self.harness.add_relation(incoming, "incoming")
self.harness.add_relation_unit(rel_incoming_id, "incoming/0")
self.harness.update_relation_data(
rel_incoming_id, "grafana-agent/0", {"sample": "value"}
rel_incoming_id, "incoming/0", {"sample": "value"}
)

# THEN the charm goes into blocked status
self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus)

# AND WHEN an appropriate outgoing relation is added
rel_outgoing_id = self.harness.add_relation(outgoing, "grafana-agent")
self.harness.add_relation_unit(rel_outgoing_id, "grafana-agent/0")
rel_outgoing_id = self.harness.add_relation(outgoing, "outgoing")
self.harness.add_relation_unit(rel_outgoing_id, "outgoing/0")

# THEN the charm goes into active status
self.assertIsInstance(self.harness.charm.unit.status, ActiveStatus)

# Remove incoming relation.
# Remove incoming relation (cleanup for the next subTest).
self.harness.remove_relation(rel_incoming_id)
2 changes: 1 addition & 1 deletion tests/unit/test_scrape_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ def test_remote_write_configuration(self):
self.assertEqual(
DeepDiff(expected_config, self.harness.charm._generate_config(), ignore_order=True), {}
)
self.assertEqual(self.harness.model.unit.status, ActiveStatus())
self.assertIsInstance(self.harness.model.unit.status, ActiveStatus)

# Test scale down
self.harness.remove_relation_unit(rel_id, "prometheus/1")
Expand Down

0 comments on commit d98de15

Please sign in to comment.