Skip to content

Commit

Permalink
Add interface for retrieving alerting config from charmed AlertManager
Browse files Browse the repository at this point in the history
  • Loading branch information
relaxdiego committed Jun 2, 2020
1 parent 1ca7454 commit 3a780f5
Show file tree
Hide file tree
Showing 10 changed files with 332 additions and 49 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.idea
.tox
.orig
*.egg-info
**/__pycache__
.coverage
Expand Down
3 changes: 3 additions & 0 deletions metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ series:
provides:
http-api:
interface: prometheus-http-api
requires:
alertmanager:
interface: prometheus-alerting-config
resources:
prometheus-image:
type: oci-image
Expand Down
8 changes: 7 additions & 1 deletion src/adapters/framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class FrameworkAdapter:
def __init__(self, framework):
self._framework = framework

def am_i_leader(self):
def unit_is_leader(self):
return self._framework.model.unit.is_leader()

def get_app_name(self):
Expand All @@ -86,9 +86,15 @@ def get_image_meta(self, image_name):
def get_model_name(self):
return self._framework.model.name

def get_relations(self, relation_name):
return self._framework.model.relations[relation_name]

def get_resources_repo(self):
return self._framework.model.resources

def get_unit(self):
return self._framework.model.unit

def get_unit_name(self):
return self._framework.model.unit.name

Expand Down
27 changes: 23 additions & 4 deletions src/charm.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/env python3
import json
import logging

logger = logging.getLogger()
Expand All @@ -24,6 +25,7 @@
)
from adapters import k8s
from exceptions import CharmError
from interface_alertmanager import AlertManagerInterface
from interface_http import PrometheusInterface


Expand All @@ -46,13 +48,15 @@ def __init__(self, *args):
# adapter and not directly with the framework.
self.fw_adapter = FrameworkAdapter(self.framework)
self.prometheus = PrometheusInterface(self, 'http-api')

self.alertmanager = AlertManagerInterface(self, 'alertmanager')
# Bind event handlers to events
event_handler_bindings = {
self.on.start: self.on_start,
self.on.config_changed: self.on_config_changed,
self.on.upgrade_charm: self.on_upgrade,
self.on.stop: self.on_stop,
self.alertmanager.on.new_relation:
self.on_new_alertmanager_relation
}
for event, handler in event_handler_bindings.items():
self.fw_adapter.observe(event, handler)
Expand All @@ -73,6 +77,9 @@ def __init__(self, *args):
def on_config_changed(self, event):
on_config_changed_handler(event, self.fw_adapter, self._stored)

def on_new_alertmanager_relation(self, event):
on_new_alertmanager_relation_handler(event, self.fw_adapter)

def on_start(self, event):
on_start_handler(event, self.fw_adapter)

Expand Down Expand Up @@ -113,6 +120,11 @@ def on_config_changed_handler(event, fw_adapter, state):
time.sleep(1)


def on_new_alertmanager_relation_handler(event, fw_adapter):
alerting_config = json.loads(event.data.get('alerting_config', '{}'))
set_juju_pod_spec(fw_adapter, alerting_config)


def on_start_handler(event, fw_adapter):
set_juju_pod_spec(fw_adapter)

Expand All @@ -125,17 +137,24 @@ def on_stop_handler(event, fw_adapter):
fw_adapter.set_unit_status(MaintenanceStatus("Pod is terminating"))


def set_juju_pod_spec(fw_adapter):
if not fw_adapter.am_i_leader():
def set_juju_pod_spec(fw_adapter, alerting_config={}):
if not fw_adapter.unit_is_leader():
logging.debug("Unit is not a leader, skip pod spec configuration")
return

if alerting_config:
logger.debug(
"Got alerting config: {} {}".format(type(alerting_config),
alerting_config)
)

logging.debug("Building Juju pod spec")
try:
juju_pod_spec = build_juju_pod_spec(
app_name=fw_adapter.get_app_name(),
charm_config=fw_adapter.get_config(),
image_meta=fw_adapter.get_image_meta('prometheus-image')
image_meta=fw_adapter.get_image_meta('prometheus-image'),
alerting_config=alerting_config
)
except CharmError as e:
fw_adapter.set_unit_status(
Expand Down
14 changes: 11 additions & 3 deletions src/domain.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import copy
import json
import yaml
import logging
import yaml

logger = logging.getLogger()

import sys
sys.path.append('lib')

logger = logging.getLogger()

from ops.model import (
ActiveStatus,
MaintenanceStatus,
Expand Down Expand Up @@ -87,10 +89,11 @@ class PrometheusConfigFile:
https://prometheus.io/docs/prometheus/latest/configuration/configuration
'''

def __init__(self, global_opts):
def __init__(self, global_opts, alerting={}):
self._config_dict = {
'global': global_opts,
'scrape_configs': [],
'alerting': alerting
}

def add_scrape_config(self, scrape_config):
Expand All @@ -102,6 +105,9 @@ def add_scrape_config(self, scrape_config):
def yaml_dump(self):
return yaml.dump(self._config_dict)

def __repr__(self):
return str(self._config_dict)


# DOMAIN SERVICES

Expand Down Expand Up @@ -189,7 +195,8 @@ def build_prometheus_cli_args(charm_config):

def build_juju_pod_spec(app_name,
charm_config,
image_meta):
image_meta,
alerting_config={}):

# There is never ever a need to customize the advertised port of a
# containerized Prometheus instance so we are removing that config
Expand Down Expand Up @@ -340,4 +347,5 @@ def build_prometheus_config(charm_config):
for scrape_config in k8s_scrape_configs:
prometheus_config.add_scrape_config(scrape_config)

logger.debug("Build prom config: {}".format(prometheus_config))
return prometheus_config
55 changes: 55 additions & 0 deletions src/interface_alertmanager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import json
import logging

logger = logging.getLogger()

from ops.framework import (
EventSource,
Object,
ObjectEvents,
)
from ops.framework import EventBase
from adapters.framework import FrameworkAdapter


class NewAlertManagerRelationEvent(EventBase):

def __init__(self, handle, remote_data):
super().__init__(handle)
self.data = dict(remote_data)

# The Operator Framework will serialize and deserialize this event object
# as it passes it to the charm. The following snapshot and restore methos
# ensure that our underlying data don't get lost along the way.

def snapshot(self):
return json.dumps(self.data)

def restore(self, snapshot):
self.data = json.loads(snapshot)


class AlertManagerEvents(ObjectEvents):
new_relation = EventSource(NewAlertManagerRelationEvent)


class AlertManagerInterface(Object):
on = AlertManagerEvents()

def __init__(self, charm, relation_name):
super().__init__(charm, relation_name)

self.fw_adapter = FrameworkAdapter(self.framework)
self.relation_name = relation_name

self.fw_adapter.observe(charm.on[relation_name].relation_changed,
self.on_relation_changed)

def on_relation_changed(self, event):
remote_data = event.relation.data[event.unit]
logging.debug(
"Received remote_data: {}".format(dict(remote_data))
)

logger.debug("Emitting new_relation event")
self.on.new_relation.emit(remote_data)
Loading

0 comments on commit 3a780f5

Please sign in to comment.