Skip to content

Commit

Permalink
feat: support external ca from cloud-integrator (#330)
Browse files Browse the repository at this point in the history
* feat: support external ca from cloud-integrator

* add comment from pr review
  • Loading branch information
lucabello authored Nov 14, 2024
1 parent 3c30f33 commit a6e47de
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 12 deletions.
49 changes: 38 additions & 11 deletions lib/charms/grafana_cloud_integrator/v0/cloud_config_requirer.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
"""Grafana Cloud Integrator Configuration Requirer."""

import logging

from ops.framework import EventBase, EventSource, Object, ObjectEvents


LIBID = "e6f580481c1b4388aa4d2cdf412a47fa"
LIBAPI = 0
LIBPATCH = 7
LIBPATCH = 8

DEFAULT_RELATION_NAME = "grafana-cloud-config"

logger = logging.getLogger(__name__)


class Credentials:
"""Credentials for the remote endpoints."""

def __init__(self, username, password):
self.username = username
self.password = password
Expand All @@ -25,23 +27,27 @@ class CloudConfigAvailableEvent(EventBase):
def __init__(self, handle):
super().__init__(handle)


class CloudConfigRevokedEvent(EventBase):
"""Event emitted when cloud config is available."""

def __init__(self, handle):
super().__init__(handle)


class GrafanaCloudConfigEvents(ObjectEvents):
"""Event descriptor for events raised by `GrafanaCloudConfigRequirer`."""

cloud_config_available = EventSource(CloudConfigAvailableEvent)
cloud_config_revoked = EventSource(CloudConfigRevokedEvent)


class GrafanaCloudConfigRequirer(Object):
"""Requirer side of the Grafana Cloud Config relation."""

on = GrafanaCloudConfigEvents() # pyright: ignore

def __init__(self, charm, relation_name = DEFAULT_RELATION_NAME):
def __init__(self, charm, relation_name=DEFAULT_RELATION_NAME):
super().__init__(charm, relation_name)
self._charm = charm
self._relation_name = relation_name
Expand All @@ -63,18 +69,15 @@ def _is_not_empty(self, s):

@property
def _change_events(self):
return [
return [
self._events.relation_joined,
self._events.relation_changed,
self._events.relation_created,
]

@property
def _broken_events(self):
return [
self._events.relation_departed,
self._events.relation_broken
]
return [self._events.relation_departed, self._events.relation_broken]

@property
def _events(self):
Expand All @@ -83,12 +86,15 @@ def _events(self):
@property
def credentials(self):
"""Return the credentials, if any; otherwise, return None."""
if (username := self._data.get("username", "").strip()) and (password := self._data.get("password", "").strip()):
if (username := self._data.get("username", "").strip()) and (
password := self._data.get("password", "").strip()
):
return Credentials(username, password)
return None

@property
def loki_ready(self):
"""Check whether there is a non-empty Loki url in relation data."""
return self._is_not_empty(self.loki_url)

@property
Expand All @@ -100,17 +106,27 @@ def loki_endpoint(self) -> dict:
endpoint = {}
endpoint["url"] = self.loki_url
if self.credentials:
endpoint["basic_auth"] = {"username": self.credentials.username, "password": self.credentials.password}
endpoint["basic_auth"] = {
"username": self.credentials.username,
"password": self.credentials.password,
}
return endpoint

@property
def prometheus_ready(self):
"""Check whether there is a non-empty Prometheus url in relation data."""
return self._is_not_empty(self.prometheus_url)

@property
def tempo_ready(self):
"""Check whether there is a non-empty Tempo url in relation data."""
return self._is_not_empty(self.tempo_url)

@property
def tls_ca_ready(self):
"""Check whether there is a TLS CA in relation data."""
return self._is_not_empty(self.tls_ca)

@property
def prometheus_endpoint(self) -> dict:
"""Return the prometheus endpoint dict."""
Expand All @@ -120,21 +136,32 @@ def prometheus_endpoint(self) -> dict:
endpoint = {}
endpoint["url"] = self.prometheus_url
if self.credentials:
endpoint["basic_auth"] = {"username": self.credentials.username, "password": self.credentials.password}
endpoint["basic_auth"] = {
"username": self.credentials.username,
"password": self.credentials.password,
}
return endpoint

@property
def loki_url(self) -> str:
"""The Loki endpoint from relation data."""
return self._data.get("loki_url", "")

@property
def tempo_url(self) -> str:
"""The Tempo endpoint from relation data."""
return self._data.get("tempo_url", "")

@property
def prometheus_url(self) -> str:
"""The Prometheus endpoint from relation data."""
return self._data.get("prometheus_url", "")

@property
def tls_ca(self) -> str:
"""TLS CA from relation data."""
return self._data.get("tls-ca", "")

@property
def _data(self):
for relation in self._charm.model.relations[self._relation_name]:
Expand Down
10 changes: 9 additions & 1 deletion src/grafana_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ class GrafanaAgentCharm(CharmBase):
_key_path = "/tmp/agent/grafana-agent.key"
_ca_path = "/usr/local/share/ca-certificates/grafana-agent-operator.crt"
_ca_folder_path = "/usr/local/share/ca-certificates"
# We have a `limit: 1` on the cloud integrator relation so we expect only one such cert.
_cloud_ca_path = "/usr/local/share/ca-certificates/cloud-integrator.crt"

# mapping from tempo-supported receivers to the receiver ports to be opened on the grafana-agent host
_tracing_receivers_ports: Dict[ReceiverProtocol, int] = {
Expand Down Expand Up @@ -349,6 +351,12 @@ def _on_config_changed(self, _event=None):

def _on_cloud_config_available(self, _) -> None:
logger.info("cloud config available")
# Write CA from cloud config
if self._cloud.tls_ca_ready:
self.write_file(self._cloud_ca_path, self._cloud.tls_ca)
else:
self._delete_file_if_exists(self._cloud_ca_path)
self.run(["update-ca-certificates", "--fresh"])
self._update_config()
self._update_tracing_provider()

Expand Down Expand Up @@ -1034,7 +1042,7 @@ def _loki_config(self) -> Dict[str, Union[Any, List[Any]]]:
a dict with Loki config
"""
configs = []
if self._loki_consumer.loki_endpoints:
if self._loki_consumer.loki_endpoints or self._cloud.loki_ready:
configs.append(
{
"name": "push_api_server",
Expand Down

0 comments on commit a6e47de

Please sign in to comment.