Skip to content

Commit

Permalink
chore: update charm libraries (#137)
Browse files Browse the repository at this point in the history
Co-authored-by: Github Actions <[email protected]>
  • Loading branch information
observability-noctua-bot and Github Actions authored Mar 10, 2023
1 parent 0917773 commit 27d4167
Showing 1 changed file with 82 additions and 27 deletions.
109 changes: 82 additions & 27 deletions lib/charms/prometheus_k8s/v0/prometheus_scrape.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ def _on_scrape_targets_changed(self, event):

# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 31
LIBPATCH = 32

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -1125,6 +1125,7 @@ def jobs(self) -> list:

return scrape_jobs

@property
def alerts(self) -> dict:
"""Fetch alerts for all relations.
Expand Down Expand Up @@ -1175,59 +1176,69 @@ def alerts(self) -> dict:
if not alert_rules:
continue

try:
scrape_metadata = json.loads(relation.data[relation.app]["scrape_metadata"])
identifier = JujuTopology.from_dict(scrape_metadata).identifier
alerts[identifier] = self._tool.apply_label_matchers(alert_rules)

except KeyError as e:
logger.debug(
"Relation %s has no 'scrape_metadata': %s",
relation.id,
e,
)
identifier = self._get_identifier_by_alert_rules(alert_rules)
alert_rules = self._inject_alert_expr_labels(alert_rules)

identifier, topology = self._get_identifier_by_alert_rules(alert_rules)
if not topology:
try:
scrape_metadata = json.loads(relation.data[relation.app]["scrape_metadata"])
identifier = JujuTopology.from_dict(scrape_metadata).identifier
alerts[identifier] = self._tool.apply_label_matchers(alert_rules) # type: ignore

except KeyError as e:
logger.debug(
"Relation %s has no 'scrape_metadata': %s",
relation.id,
e,
)

if not identifier:
logger.error(
"Alert rules were found but no usable group or identifier was present"
"Alert rules were found but no usable group or identifier was present."
)
continue

alerts[identifier] = alert_rules

_, errmsg = self._tool.validate_alert_rules(alert_rules)
if errmsg:
if alerts[identifier]:
del alerts[identifier]
relation.data[self._charm.app]["event"] = json.dumps({"errors": errmsg})
continue

alerts[identifier] = alert_rules

return alerts

def _get_identifier_by_alert_rules(self, rules: dict) -> Union[str, None]:
def _get_identifier_by_alert_rules(
self, rules: dict
) -> Tuple[Union[str, None], Union[JujuTopology, None]]:
"""Determine an appropriate dict key for alert rules.
The key is used as the filename when writing alerts to disk, so the structure
and uniqueness is important.
Args:
rules: a dict of alert rules
Returns:
A tuple containing an identifier, if found, and a JujuTopology, if it could
be constructed.
"""
if "groups" not in rules:
logger.debug("No alert groups were found in relation data")
return None
return None, None

# Construct an ID based on what's in the alert rules if they have labels
for group in rules["groups"]:
try:
labels = group["rules"][0]["labels"]
identifier = "{}_{}_{}".format(
labels["juju_model"],
labels["juju_model_uuid"],
labels["juju_application"],
topology = JujuTopology(
# Don't try to safely get required constructor fields. There's already
# a handler for KeyErrors
model_uuid=labels["juju_model_uuid"],
model=labels["juju_model"],
application=labels["juju_application"],
unit=labels.get("juju_unit", ""),
charm_name=labels.get("juju_charm", ""),
)
return identifier
return topology.identifier, topology
except KeyError:
logger.debug("Alert rules were found but no usable labels were present")
continue
Expand All @@ -1238,11 +1249,55 @@ def _get_identifier_by_alert_rules(self, rules: dict) -> Union[str, None]:
)
try:
for group in rules["groups"]:
return group["name"]
return group["name"], None
except KeyError:
logger.debug("No group name was found to use as identifier")

return None
return None, None

def _inject_alert_expr_labels(self, rules: Dict[str, Any]) -> Dict[str, Any]:
"""Iterate through alert rules and inject topology into expressions.
Args:
rules: a dict of alert rules
"""
if "groups" not in rules:
return rules

modified_groups = []
for group in rules["groups"]:
# Copy off rules, so we don't modify an object we're iterating over
rules_copy = group["rules"]
for idx, rule in enumerate(rules_copy):
labels = rule.get("labels")

if labels:
try:
topology = JujuTopology(
# Don't try to safely get required constructor fields. There's already
# a handler for KeyErrors
model_uuid=labels["juju_model_uuid"],
model=labels["juju_model"],
application=labels["juju_application"],
unit=labels.get("juju_unit", ""),
charm_name=labels.get("juju_charm", ""),
)

# Inject topology and put it back in the list
rule["expr"] = self._tool.inject_label_matchers(
re.sub(r"%%juju_topology%%,?", "", rule["expr"]),
topology.label_matcher_dict,
)
except KeyError:
# Some required JujuTopology key is missing. Just move on.
pass

group["rules"][idx] = rule

modified_groups.append(group)

rules["groups"] = modified_groups
return rules

def _static_scrape_config(self, relation) -> list:
"""Generate the static scrape configuration for a single relation.
Expand Down

0 comments on commit 27d4167

Please sign in to comment.