@@ -340,8 +340,8 @@ def _on_scrape_targets_changed(self, event):
340
340
341
341
import yaml
342
342
from cosl import JujuTopology
343
- from cosl .rules import AlertRules
344
- from ops .charm import CharmBase , RelationRole
343
+ from cosl .rules import AlertRules , generic_alert_groups
344
+ from ops .charm import CharmBase , RelationJoinedEvent , RelationRole
345
345
from ops .framework import (
346
346
BoundEvent ,
347
347
EventBase ,
@@ -362,7 +362,7 @@ def _on_scrape_targets_changed(self, event):
362
362
363
363
# Increment this PATCH version before using `charmcraft publish-lib` or reset
364
364
# to 0 if you are raising the major API version
365
- LIBPATCH = 47
365
+ LIBPATCH = 50
366
366
367
367
PYDEPS = ["cosl" ]
368
368
@@ -1309,6 +1309,8 @@ def __init__(
1309
1309
refresh_event : Optional [Union [BoundEvent , List [BoundEvent ]]] = None ,
1310
1310
external_url : str = "" ,
1311
1311
lookaside_jobs_callable : Optional [Callable ] = None ,
1312
+ * ,
1313
+ forward_alert_rules : bool = True ,
1312
1314
):
1313
1315
"""Construct a metrics provider for a Prometheus charm.
1314
1316
@@ -1411,6 +1413,7 @@ def __init__(
1411
1413
files. Defaults to "./prometheus_alert_rules",
1412
1414
resolved relative to the directory hosting the charm entry file.
1413
1415
The alert rules are automatically updated on charm upgrade.
1416
+ forward_alert_rules: a boolean flag to toggle forwarding of charmed alert rules.
1414
1417
refresh_event: an optional bound event or list of bound events which
1415
1418
will be observed to re-set scrape job data (IP address and others)
1416
1419
external_url: an optional argument that represents an external url that
@@ -1449,6 +1452,7 @@ def __init__(
1449
1452
1450
1453
self ._charm = charm
1451
1454
self ._alert_rules_path = alert_rules_path
1455
+ self ._forward_alert_rules = forward_alert_rules
1452
1456
self ._relation_name = relation_name
1453
1457
# sanitize job configurations to the supported subset of parameters
1454
1458
jobs = [] if jobs is None else jobs
@@ -1530,7 +1534,11 @@ def set_scrape_job_spec(self, _=None):
1530
1534
return
1531
1535
1532
1536
alert_rules = AlertRules (query_type = "promql" , topology = self .topology )
1533
- alert_rules .add_path (self ._alert_rules_path , recursive = True )
1537
+ if self ._forward_alert_rules :
1538
+ alert_rules .add_path (self ._alert_rules_path , recursive = True )
1539
+ alert_rules .add (
1540
+ generic_alert_groups .application_rules , group_name_prefix = self .topology .identifier
1541
+ )
1534
1542
alert_rules_as_dict = alert_rules .as_dict ()
1535
1543
1536
1544
for relation in self ._charm .model .relations [self ._relation_name ]:
@@ -1776,6 +1784,9 @@ def __init__(
1776
1784
relation_names : Optional [dict ] = None ,
1777
1785
relabel_instance = True ,
1778
1786
resolve_addresses = False ,
1787
+ path_to_own_alert_rules : Optional [str ] = None ,
1788
+ * ,
1789
+ forward_alert_rules : bool = True ,
1779
1790
):
1780
1791
"""Construct a `MetricsEndpointAggregator`.
1781
1792
@@ -1795,6 +1806,8 @@ def __init__(
1795
1806
resolve_addresses: A boolean flag indiccating if the aggregator
1796
1807
should attempt to perform DNS lookups of targets and append
1797
1808
a `dns_name` label
1809
+ path_to_own_alert_rules: Optionally supply a path for alert rule files
1810
+ forward_alert_rules: a boolean flag to toggle forwarding of charmed alert rules
1798
1811
"""
1799
1812
self ._charm = charm
1800
1813
@@ -1807,15 +1820,21 @@ def __init__(
1807
1820
self ._alert_rules_relation = relation_names .get ("alert_rules" , "prometheus-rules" )
1808
1821
1809
1822
super ().__init__ (charm , self ._prometheus_relation )
1823
+ self .topology = JujuTopology .from_charm (charm )
1824
+
1810
1825
self ._stored .set_default (jobs = [], alert_rules = [])
1811
1826
1812
1827
self ._relabel_instance = relabel_instance
1813
1828
self ._resolve_addresses = resolve_addresses
1814
1829
1830
+ self ._forward_alert_rules = forward_alert_rules
1831
+
1815
1832
# manage Prometheus charm relation events
1816
1833
prometheus_events = self ._charm .on [self ._prometheus_relation ]
1817
1834
self .framework .observe (prometheus_events .relation_joined , self ._set_prometheus_data )
1818
1835
1836
+ self .path_to_own_alert_rules = path_to_own_alert_rules
1837
+
1819
1838
# manage list of Prometheus scrape jobs from related scrape targets
1820
1839
target_events = self ._charm .on [self ._target_relation ]
1821
1840
self .framework .observe (target_events .relation_changed , self ._on_prometheus_targets_changed )
@@ -1828,7 +1847,7 @@ def __init__(
1828
1847
self .framework .observe (alert_rule_events .relation_changed , self ._on_alert_rules_changed )
1829
1848
self .framework .observe (alert_rule_events .relation_departed , self ._on_alert_rules_departed )
1830
1849
1831
- def _set_prometheus_data (self , event ):
1850
+ def _set_prometheus_data (self , event : Optional [ RelationJoinedEvent ] = None ):
1832
1851
"""Ensure every new Prometheus instances is updated.
1833
1852
1834
1853
Any time a new Prometheus unit joins the relation with
@@ -1838,6 +1857,7 @@ def _set_prometheus_data(self, event):
1838
1857
if not self ._charm .unit .is_leader ():
1839
1858
return
1840
1859
1860
+ # Gather the scrape jobs
1841
1861
jobs = [] + _type_convert_stored (
1842
1862
self ._stored .jobs # pyright: ignore
1843
1863
) # list of scrape jobs, one per relation
@@ -1846,6 +1866,7 @@ def _set_prometheus_data(self, event):
1846
1866
if targets and relation .app :
1847
1867
jobs .append (self ._static_scrape_job (targets , relation .app .name ))
1848
1868
1869
+ # Gather the alert rules
1849
1870
groups = [] + _type_convert_stored (
1850
1871
self ._stored .alert_rules # pyright: ignore
1851
1872
) # list of alert rule groups
@@ -1856,9 +1877,23 @@ def _set_prometheus_data(self, event):
1856
1877
rules = self ._label_alert_rules (unit_rules , appname )
1857
1878
group = {"name" : self .group_name (appname ), "rules" : rules }
1858
1879
groups .append (group )
1859
-
1860
- event .relation .data [self ._charm .app ]["scrape_jobs" ] = json .dumps (jobs )
1861
- event .relation .data [self ._charm .app ]["alert_rules" ] = json .dumps ({"groups" : groups })
1880
+ alert_rules = AlertRules (query_type = "promql" , topology = self .topology )
1881
+ # Add alert rules from file
1882
+ if self .path_to_own_alert_rules :
1883
+ alert_rules .add_path (self .path_to_own_alert_rules , recursive = True )
1884
+ # Add generic alert rules
1885
+ alert_rules .add (
1886
+ generic_alert_groups .application_rules , group_name_prefix = self .topology .identifier
1887
+ )
1888
+ groups .extend (alert_rules .as_dict ()["groups" ])
1889
+
1890
+ # Set scrape jobs and alert rules in relation data
1891
+ relations = [event .relation ] if event else self .model .relations [self ._prometheus_relation ]
1892
+ for rel in relations :
1893
+ rel .data [self ._charm .app ]["scrape_jobs" ] = json .dumps (jobs ) # type: ignore
1894
+ rel .data [self ._charm .app ]["alert_rules" ] = json .dumps ( # type: ignore
1895
+ {"groups" : groups if self ._forward_alert_rules else []}
1896
+ )
1862
1897
1863
1898
def _on_prometheus_targets_changed (self , event ):
1864
1899
"""Update scrape jobs in response to scrape target changes.
@@ -2129,7 +2164,9 @@ def set_alert_rule_data(self, name: str, unit_rules: dict, label_rules: bool = T
2129
2164
2130
2165
if updated_group ["name" ] not in [g ["name" ] for g in groups ]:
2131
2166
groups .append (updated_group )
2132
- relation .data [self ._charm .app ]["alert_rules" ] = json .dumps ({"groups" : groups })
2167
+ relation .data [self ._charm .app ]["alert_rules" ] = json .dumps (
2168
+ {"groups" : groups if self ._forward_alert_rules else []}
2169
+ )
2133
2170
2134
2171
if not _type_convert_stored (self ._stored .alert_rules ) == groups : # pyright: ignore
2135
2172
self ._stored .alert_rules = groups
@@ -2177,8 +2214,8 @@ def remove_alert_rules(self, group_name: str, unit_name: str) -> None:
2177
2214
changed_group ["rules" ] = rules_kept # type: ignore
2178
2215
groups .append (changed_group )
2179
2216
2180
- relation .data [self ._charm .app ]["alert_rules" ] = (
2181
- json . dumps ( {"groups" : groups }) if groups else "{}"
2217
+ relation .data [self ._charm .app ]["alert_rules" ] = json . dumps (
2218
+ {"groups" : groups if self . _forward_alert_rules else []}
2182
2219
)
2183
2220
2184
2221
if not _type_convert_stored (self ._stored .alert_rules ) == groups : # pyright: ignore
@@ -2364,12 +2401,9 @@ def _get_tool_path(self) -> Optional[Path]:
2364
2401
arch = "amd64" if arch == "x86_64" else arch
2365
2402
res = "cos-tool-{}" .format (arch )
2366
2403
try :
2367
- path = Path (res ).resolve ()
2368
- path .chmod (0o777 )
2404
+ path = Path (res ).resolve (strict = True )
2369
2405
return path
2370
- except NotImplementedError :
2371
- logger .debug ("System lacks support for chmod" )
2372
- except FileNotFoundError :
2406
+ except (FileNotFoundError , OSError ):
2373
2407
logger .debug ('Could not locate cos-tool at: "{}"' .format (res ))
2374
2408
return None
2375
2409
0 commit comments