Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

---

## [0.4.2] - 2026-03-18

### Fixed
- **`scaffold sre` empty `slos: []` when `--slo-type error_rate`** — `generate_slo_manifest()` in
`cli/scaffold_sre.py` had no branch for the `error_rate` SLO type, leaving `slo.yaml` with an
empty `slos: []` list. Sloth/OpenSLO tooling therefore received no SLO objectives. A dedicated
`error_rate` SLO entry (with matching SLI queries and alerting config) is now generated when
`--slo-type error_rate` is specified.

---

## [0.4.1] - 2026-03-17

### Changed
Expand Down
2 changes: 1 addition & 1 deletion cli/__version__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Single source of truth for the devopsos package version."""

__version__ = "0.4.1"
__version__ = "0.4.2"
22 changes: 22 additions & 0 deletions cli/scaffold_sre.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,28 @@ def generate_slo_manifest(args):
},
})

if args.slo_type in ("error_rate", "all"):
slos.append({
"name": "error_rate",
"description": f"{name} error rate SLO — error rate stays below {round(100 - args.slo_target, 4)}%",
"objective": args.slo_target,
"sli": {
"events": {
"error_query": f"rate(http_requests_total{{job=\"{name}\",status=~\"(5..)\"}}[{{{{.window}}}}])",
"total_query": f"rate(http_requests_total{{job=\"{name}\"}}[{{{{.window}}}}])",
}
},
"alerting": {
"name": f"{name.title()}ErrorRateSLO",
"labels": {"team": args.team},
"annotations": {
"runbook": f"https://wiki.example.com/runbooks/{name}/error-rate",
},
"page_alert": {"labels": {"severity": "critical"}},
"ticket_alert": {"labels": {"severity": "warning"}},
},
})

if args.slo_type in ("latency", "all"):
slos.append({
"name": "latency",
Expand Down
15 changes: 4 additions & 11 deletions tests/test_comprehensive.py
Original file line number Diff line number Diff line change
Expand Up @@ -854,21 +854,11 @@ def test_slo_manifest_latency_entry(self):
slo_names = [s["name"] for s in manifest["slos"]]
assert "latency" in slo_names

# BUG-2: error_rate SLO type produces empty slos list in manifest
@pytest.mark.xfail(
strict=True,
reason=(
"BUG-2: generate_slo_manifest() only checks for 'availability' and "
"'latency' slo_type values. When slo_type='error_rate', neither "
"condition matches so the slos list is empty. An error_rate SLO "
"entry should be generated for slo_type='error_rate'."
),
)
def test_slo_manifest_error_rate_bug(self):
"""
BUG-2: When slo_type='error_rate', generate_slo_manifest() should
return at least one SLO entry capturing the error rate objective,
but currently returns an empty slos list.
and that entry should have name == 'error_rate'.
"""
args = _sre_args(slo_type="error_rate")
manifest = scaffold_sre.generate_slo_manifest(args)
Expand All @@ -877,13 +867,16 @@ def test_slo_manifest_error_rate_bug(self):
"Expected at least one SLO entry for error_rate type, "
"got empty list."
)
slo_names = [s["name"] for s in manifest["slos"]]
assert "error_rate" in slo_names

def test_slo_manifest_all_type_has_both_slos(self):
args = _sre_args(slo_type="all")
manifest = scaffold_sre.generate_slo_manifest(args)
slo_names = [s["name"] for s in manifest["slos"]]
assert "availability" in slo_names
assert "latency" in slo_names
assert "error_rate" in slo_names

def test_alertmanager_config_slack_receiver(self):
args = _sre_args(slack_channel="#platform-alerts")
Expand Down
Loading