Integrate with Sloth¶
This guide explains how to provide SLO specifications to sloth-k8s.
There are two approaches to integrate with Sloth:
Via
cos-configuration-k8s(recommended) — deploy a dedicated configuration charm that reads SLO spec files from a git repository and forwards them to Sloth. No changes to your application charm are needed.Via the SlothProvider library — add code to your charm so it sends SLO specs directly over the
slothrelation. Use this when your charm needs to generate or parametrize SLO specs dynamically at runtime.
Option 1: Integrate via cos-configuration-k8s (no charm changes required)¶
cos-configuration-k8s is a Canonical charm that
periodically syncs a git repository and forwards its contents to COS components, including
sloth-k8s. This is the recommended approach when you want to add SLOs to an existing
deployment without modifying any application charm.
1. Write your SLO specification files¶
Create one or more YAML files in the Sloth format
and commit them to a git repository, inside a directory (default: slos/). For example:
# slos/my-service.yaml
version: "prometheus/v1"
service: "my-service"
labels:
team: my-team
slos:
- name: "availability"
objective: 99.9
description: "99.9% of requests succeed"
sli:
events:
error_query: 'sum(rate(http_requests_total{status=~"5.."}[{{.window}}]))'
total_query: 'sum(rate(http_requests_total[{{.window}}]))'
alerting:
name: MyServiceHighErrorRate
See SLO specification format for the full field reference and annotated example.
2. Deploy and configure cos-configuration-k8s¶
juju deploy cos-configuration-k8s cos-config
juju config cos-config \
git_repo=https://github.com/your-org/your-repo \
git_branch=main \
slos_path=slos
slos_path is the path inside the repository where the SLO YAML files live (default: slos).
For private repositories, set git_ssh_key_secret to a Juju secret containing your SSH private
key.
3. Relate cos-configuration-k8s to sloth-k8s¶
juju integrate cos-config:sloth sloth:sloth
cos-configuration-k8s will sync the repository and forward all SLO files it finds under
slos_path to sloth-k8s. To force an immediate sync without waiting for the next
update-status hook, run:
juju run cos-config/0 sync-now
Option 2: Integrate via the SlothProvider library¶
Use this approach when your charm generates or parametrizes SLO specs at runtime (for example, based on charm config or relation data).
1. Add the SLO relation endpoint¶
In the charm’s charmcraft.yaml, under provides, add:
provides:
slos:
optional: true
interface: sloth
description: |
Sends SLOs (Service Level Objective) specifications to a Sloth charm.
2. Install the Sloth library¶
Add the library to the charm’s dependencies. For example, with uv:
uv add charmlibs-interfaces-sloth
Then update the lockfile (e.g. tox -e lock, just lock, or poetry lock).
3. Set up the SlothProvider¶
Import SlothProvider and wire it up in the charm:
import ops
from charmlibs.interfaces.sloth import SlothProvider
SLOS_RELATION_NAME = "slos"
class MyCharm(ops.CharmBase):
def __init__(self, *args):
super().__init__(*args)
self.sloth = SlothProvider(self, SLOS_RELATION_NAME)
# Observe relation changes to send SLOs. Add further observations
# (e.g. config-changed) if _get_slo_spec depends on charm config.
# If the charm has a reconciler, call _send_slos from there instead.
self.framework.observe(self.on.slos_relation_changed, self._send_slos)
def _get_slo_spec(self) -> str:
"""Return the SLO specification as a YAML string in Sloth format."""
# OPTION A: Let the operator paste a raw Sloth spec as a config option.
if spec := self.config["slos"]:
return spec
# OPTION B: Read a static spec bundled with the charm.
with open("./slos/slo_spec.yaml", "r") as f:
return f.read()
# OPTION C: Pick a preset based on charm config.
preset = self.config["preset"]
with open(f"./slos/{preset}_slo_spec.yaml", "r") as f:
return f.read()
# OPTION D: Fill in a template with charm config values.
with open("./slos/slo_spec_template.yaml", "r") as f:
template = f.read()
return template.replace("__OBJECTIVE__", self.config["objective"])
def _send_slos(self, event: ops.RelationChangedEvent):
self.sloth.provide_slos(self._get_slo_spec())
The crucial design decision is how to implement _get_slo_spec. SLO objectives often vary
significantly across deployments (the same database can be mission-critical or best-effort
depending on context). We recommend always providing a fallback that accepts a raw Sloth YAML
string as a config option, and optionally adding opinionated presets or a template for operators
who prefer not to write the spec from scratch.
Add any config options to charmcraft.yaml accordingly:
options:
slos:
description: |
SLO specifications to send to Sloth, as a YAML string in Sloth format.
See https://pkg.go.dev/github.com/slok/sloth/pkg/prometheus/api/v1.
type: string
default: ""
4. Create SLO specification files¶
Write the YAML spec file(s) in the format expected by Sloth. See the
Sloth documentation for the
full specification. Save them under ./src/slos and ensure the path matches the one used in
_get_slo_spec.
5. Testing¶
At a minimum:
Write a unit test that verifies
_get_slo_specreturns a valid non-empty string.Write state-transition tests (using
ops.testing) that verify:When
relation-changedfires, the output state’sslosdatabag contains the expected spec.When relevant config parameters change, the relation databag updates accordingly.
For end-to-end coverage, consider an integration test that deploys the charm together with
sloth-k8s, prometheus-k8s, and grafana-k8s, and verifies that the SLOs are correctly
received, processed, and forwarded.