Skip to content

Commit

Permalink
Update charm libs
Browse files Browse the repository at this point in the history
  • Loading branch information
canonical-iam committed Aug 26, 2024
1 parent 33fb57d commit 674b8c6
Show file tree
Hide file tree
Showing 9 changed files with 297 additions and 141 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ def _on_certificate_removed(self, event: CertificateRemovedEvent):
"""


import json
import logging
from typing import List, Mapping
Expand All @@ -113,7 +112,7 @@ def _on_certificate_removed(self, event: CertificateRemovedEvent):

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

PYDEPS = ["jsonschema"]

Expand Down
3 changes: 2 additions & 1 deletion lib/charms/grafana_k8s/v0/grafana_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ def __init__(self, *args):
# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version

LIBPATCH = 35
LIBPATCH = 36

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -1050,6 +1050,7 @@ def __init__(

self.framework.observe(self._charm.on.leader_elected, self._update_all_dashboards_from_dir)
self.framework.observe(self._charm.on.upgrade_charm, self._update_all_dashboards_from_dir)
self.framework.observe(self._charm.on.config_changed, self._update_all_dashboards_from_dir)

self.framework.observe(
self._charm.on[self._relation_name].relation_created,
Expand Down
14 changes: 9 additions & 5 deletions lib/charms/hydra/v0/oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def _set_client_config(self):
import jsonschema
from ops.charm import CharmBase, RelationBrokenEvent, RelationChangedEvent, RelationCreatedEvent
from ops.framework import EventBase, EventSource, Handle, Object, ObjectEvents
from ops.model import Relation, Secret, TooManyRelatedAppsError
from ops.model import Relation, Secret, SecretNotFoundError, TooManyRelatedAppsError

# The unique Charmhub library identifier, never change it
LIBID = "a3a301e325e34aac80a2d633ef61fe97"
Expand All @@ -67,7 +67,7 @@ def _set_client_config(self):

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

PYDEPS = ["jsonschema"]

Expand Down Expand Up @@ -748,8 +748,12 @@ def _create_juju_secret(self, client_secret: str, relation: Relation) -> Secret:
return juju_secret

def _delete_juju_secret(self, relation: Relation) -> None:
secret = self.model.get_secret(label=self._get_secret_label(relation))
secret.remove_all_revisions()
try:
secret = self.model.get_secret(label=self._get_secret_label(relation))
except SecretNotFoundError:
return
else:
secret.remove_all_revisions()

def set_provider_info_in_relation_data(
self,
Expand Down Expand Up @@ -799,5 +803,5 @@ def set_client_credentials_in_relation_data(
# TODO: What if we are refreshing the client_secret? We need to add a
# new revision for that
secret = self._create_juju_secret(client_secret, relation)
data = dict(client_id=client_id, client_secret_id=secret.id)
data = {"client_id": client_id, "client_secret_id": secret.id}
relation.data[self.model.app].update(_dump_data(data))
7 changes: 5 additions & 2 deletions lib/charms/kratos/v0/kratos_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# See LICENSE file for licensing details.

"""Interface library for sharing kratos info.
This library provides a Python API for both requesting and providing kratos deployment info,
such as endpoints, namespace and ConfigMap details.
## Getting Started
Expand Down Expand Up @@ -52,7 +53,7 @@ def some_event_function():

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

RELATION_NAME = "kratos-info"
INTERFACE_NAME = "kratos_info"
Expand Down Expand Up @@ -93,8 +94,9 @@ def send_info_relation_data(
providers_configmap_name: str,
schemas_configmap_name: str,
configmaps_namespace: str,
mfa_enabled: bool,
) -> None:
"""Updates relation with endpoints and configmaps info."""
"""Updates relation with endpoints, config and configmaps info."""
if not self._charm.unit.is_leader():
return

Expand All @@ -107,6 +109,7 @@ def send_info_relation_data(
"providers_configmap_name": providers_configmap_name,
"schemas_configmap_name": schemas_configmap_name,
"configmaps_namespace": configmaps_namespace,
"mfa_enabled": str(mfa_enabled),
}

for relation in relations:
Expand Down
88 changes: 63 additions & 25 deletions lib/charms/loki_k8s/v0/loki_push_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
implement the provider side of the `loki_push_api` relation interface. For instance, a Loki charm.
The provider side of the relation represents the server side, to which logs are being pushed.
- `LokiPushApiConsumer`: This object is meant to be used by any Charmed Operator that needs to
send log to Loki by implementing the consumer side of the `loki_push_api` relation interface.
For instance, a Promtail or Grafana agent charm which needs to send logs to Loki.
- `LokiPushApiConsumer`: Used to obtain the loki api endpoint. This is useful for configuring
applications such as pebble, or charmed operators of workloads such as grafana-agent or promtail,
that can communicate with loki directly.
- `LogProxyConsumer`: This object can be used by any Charmed Operator which needs to
send telemetry, such as logs, to Loki through a Log Proxy by implementing the consumer side of the
`loki_push_api` relation interface.
- `LogProxyConsumer`: DEPRECATED.
This object can be used by any Charmed Operator which needs to send telemetry, such as logs, to
Loki through a Log Proxy by implementing the consumer side of the `loki_push_api` relation
interface.
Filtering logs in Loki is largely performed on the basis of labels. In the Juju ecosystem, Juju
topology labels are used to uniquely identify the workload which generates telemetry like logs.
Expand All @@ -38,13 +39,14 @@
- `charm`: A reference to the parent (Loki) charm.
- `relation_name`: The name of the relation that the charm uses to interact
with its clients, which implement `LokiPushApiConsumer` or `LogProxyConsumer`.
with its clients, which implement `LokiPushApiConsumer` or `LogProxyConsumer`
(note that LogProxyConsumer is deprecated).
If provided, this relation name must match a provided relation in metadata.yaml with the
`loki_push_api` interface.
The default relation name is "logging" for `LokiPushApiConsumer` and "log-proxy" for
`LogProxyConsumer`.
`LogProxyConsumer` (note that LogProxyConsumer is deprecated).
For example, a provider's `metadata.yaml` file may look as follows:
Expand Down Expand Up @@ -219,6 +221,9 @@ def __init__(self, *args):
## LogProxyConsumer Library Usage
> Note: This object is deprecated. Consider migrating to LogForwarder (see v1/loki_push_api) with
> the release of Juju 3.6 LTS.
Let's say that we have a workload charm that produces logs, and we need to send those logs to a
workload implementing the `loki_push_api` interface, such as `Loki` or `Grafana Agent`.
Expand Down Expand Up @@ -456,7 +461,7 @@ def _alert_rules_error(self, event):
from urllib.error import HTTPError

import yaml
from charms.observability_libs.v0.juju_topology import JujuTopology
from cosl import JujuTopology
from ops.charm import (
CharmBase,
HookEvent,
Expand All @@ -480,7 +485,9 @@ def _alert_rules_error(self, event):

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

PYDEPS = ["cosl"]

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -604,7 +611,9 @@ def _validate_relation_by_interface_and_direction(
actual_relation_interface = relation.interface_name
if actual_relation_interface != expected_relation_interface:
raise RelationInterfaceMismatchError(
relation_name, expected_relation_interface, actual_relation_interface
relation_name,
expected_relation_interface,
actual_relation_interface, # pyright: ignore
)

if expected_relation_role == RelationRole.provides:
Expand Down Expand Up @@ -866,20 +875,20 @@ def _from_dir(self, dir_path: Path, recursive: bool) -> List[dict]:

return alert_groups

def add_path(self, path: str, *, recursive: bool = False):
def add_path(self, path_str: str, *, recursive: bool = False):
"""Add rules from a dir path.
All rules from files are aggregated into a data structure representing a single rule file.
All group names are augmented with juju topology.
Args:
path: either a rules file or a dir of rules files.
path_str: either a rules file or a dir of rules files.
recursive: whether to read files recursively or not (no impact if `path` is a file).
Raises:
InvalidAlertRulePathError: if the provided path is invalid.
"""
path = Path(path) # type: Path
path = Path(path_str) # type: Path
if path.is_dir():
self.alert_groups.extend(self._from_dir(path, recursive))
elif path.is_file():
Expand Down Expand Up @@ -992,6 +1001,8 @@ def __init__(self, handle, relation, relation_id, app=None, unit=None):

def snapshot(self) -> Dict:
"""Save event information."""
if not self.relation:
return {}
snapshot = {"relation_name": self.relation.name, "relation_id": self.relation.id}
if self.app:
snapshot["app_name"] = self.app.name
Expand Down Expand Up @@ -1052,7 +1063,7 @@ class LokiPushApiEvents(ObjectEvents):
class LokiPushApiProvider(Object):
"""A LokiPushApiProvider class."""

on = LokiPushApiEvents()
on = LokiPushApiEvents() # pyright: ignore

def __init__(
self,
Expand Down Expand Up @@ -1146,11 +1157,11 @@ def _on_logging_relation_changed(self, event: HookEvent):
event: a `CharmEvent` in response to which the consumer
charm must update its relation data.
"""
should_update = self._process_logging_relation_changed(event.relation)
should_update = self._process_logging_relation_changed(event.relation) # pyright: ignore
if should_update:
self.on.loki_push_api_alert_rules_changed.emit(
relation=event.relation,
relation_id=event.relation.id,
relation=event.relation, # pyright: ignore
relation_id=event.relation.id, # pyright: ignore
app=self._charm.app,
unit=self._charm.unit,
)
Expand Down Expand Up @@ -1517,7 +1528,7 @@ def loki_endpoints(self) -> List[dict]:
class LokiPushApiConsumer(ConsumerBase):
"""Loki Consumer class."""

on = LokiPushApiEvents()
on = LokiPushApiEvents() # pyright: ignore

def __init__(
self,
Expand All @@ -1533,7 +1544,8 @@ def __init__(
the Loki API endpoint to push logs. It is intended for workloads that can speak
loki_push_api (https://grafana.com/docs/loki/latest/api/#push-log-entries-to-loki), such
as grafana-agent.
(If you only need to forward a few workload log files, then use LogProxyConsumer.)
(If you need to forward workload stdout logs, then use v1/loki_push_api.LogForwarder; if
you need to forward log files, then use LogProxyConsumer.)
`LokiPushApiConsumer` can be instantiated as follows:
Expand Down Expand Up @@ -1722,6 +1734,9 @@ class LogProxyEvents(ObjectEvents):
class LogProxyConsumer(ConsumerBase):
"""LogProxyConsumer class.
> Note: This object is deprecated. Consider migrating to v1/loki_push_api.LogForwarder with the
> release of Juju 3.6 LTS.
The `LogProxyConsumer` object provides a method for attaching `promtail` to
a workload in order to generate structured logging data from applications
which traditionally log to syslog or do not have native Loki integration.
Expand Down Expand Up @@ -1760,7 +1775,7 @@ class LogProxyConsumer(ConsumerBase):
role.
"""

on = LogProxyEvents()
on = LogProxyEvents() # pyright: ignore

def __init__(
self,
Expand Down Expand Up @@ -1885,7 +1900,7 @@ def _on_relation_departed(self, _: RelationEvent) -> None:
self._container.stop(WORKLOAD_SERVICE_NAME)
self.on.log_proxy_endpoint_departed.emit()

def _get_container(self, container_name: str = "") -> Container:
def _get_container(self, container_name: str = "") -> Container: # pyright: ignore
"""Gets a single container by name or using the only container running in the Pod.
If there is more than one container in the Pod a `PromtailDigestError` is emitted.
Expand Down Expand Up @@ -1959,7 +1974,9 @@ def _add_pebble_layer(self, workload_binary_path: str) -> None:
}
},
}
self._container.add_layer(self._container_name, pebble_layer, combine=True)
self._container.add_layer(
self._container_name, pebble_layer, combine=True # pyright: ignore
)

def _create_directories(self) -> None:
"""Creates the directories for Promtail binary and config file."""
Expand Down Expand Up @@ -1996,7 +2013,11 @@ def _push_binary_to_workload(self, binary_path: str, workload_binary_path: str)
"""
with open(binary_path, "rb") as f:
self._container.push(
workload_binary_path, f, permissions=0o755, encoding=None, make_dirs=True
workload_binary_path,
f,
permissions=0o755,
encoding=None, # pyright: ignore
make_dirs=True,
)
logger.debug("The promtail binary file has been pushed to the workload container.")

Expand Down Expand Up @@ -2105,7 +2126,24 @@ def _download_and_push_promtail_to_workload(self, promtail_info: dict) -> None:
- "zipsha": sha256 sum of zip file of promtail binary
- "binsha": sha256 sum of unpacked promtail binary
"""
with request.urlopen(promtail_info["url"]) as r:
# Check for Juju proxy variables and fall back to standard ones if not set
# If no Juju proxy variable was set, we set proxies to None to let the ProxyHandler get
# the proxy env variables from the environment
proxies = {
# The ProxyHandler uses only the protocol names as keys
# https://docs.python.org/3/library/urllib.request.html#urllib.request.ProxyHandler
"https": os.environ.get("JUJU_CHARM_HTTPS_PROXY", ""),
"http": os.environ.get("JUJU_CHARM_HTTP_PROXY", ""),
# The ProxyHandler uses `no` for the no_proxy key
# https://github.com/python/cpython/blob/3.12/Lib/urllib/request.py#L2553
"no": os.environ.get("JUJU_CHARM_NO_PROXY", ""),
}
proxies = {k: v for k, v in proxies.items() if v != ""} or None

proxy_handler = request.ProxyHandler(proxies)
opener = request.build_opener(proxy_handler)

with opener.open(promtail_info["url"]) as r:
file_bytes = r.read()
file_path = os.path.join(BINARY_DIR, promtail_info["filename"] + ".gz")
with open(file_path, "wb") as f:
Expand Down
6 changes: 3 additions & 3 deletions lib/charms/oathkeeper/v0/oathkeeper_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def some_event_function():

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

RELATION_NAME = "oathkeeper-info"
INTERFACE_NAME = "oathkeeper_info"
Expand All @@ -70,7 +70,7 @@ class OathkeeperInfoProvider(Object):

on = OathkeeperInfoProviderEvents()

def __init__(self, charm: CharmBase, relation_name: str = RELATION_NAME):
def __init__(self, charm: CharmBase, relation_name: str = RELATION_NAME) -> None:
super().__init__(charm, relation_name)

self._charm = charm
Expand Down Expand Up @@ -128,7 +128,7 @@ def __init__(self, message: str) -> None:
class OathkeeperInfoRequirer(Object):
"""Requirer side of the oathkeeper-info relation."""

def __init__(self, charm: CharmBase, relation_name: str = RELATION_NAME):
def __init__(self, charm: CharmBase, relation_name: str = RELATION_NAME) -> None:
super().__init__(charm, relation_name)
self._charm = charm
self._relation_name = relation_name
Expand Down
Loading

0 comments on commit 674b8c6

Please sign in to comment.