Skip to content

v0.7.5

Choose a tag to compare

@github-actions github-actions released this 22 May 20:00

What's new in v0.7.5

v0.7.5 is a bug fix on the Kepler integration that shipped in v0.7.4. The scraper targeted metric names and a label name that no released Kepler version ever published, so on any real Kepler v2 cluster every scrape returned HTTP 200 with zero matching samples, the kepler_ebpf co2.model tag never lit up, and carbon attribution silently fell through to the proxy chain. Cross-checked against sustainable-computing.io/kepler/design/metrics/ and the Kepler repo docs/user/metrics.md. The Redfish BMC integration is unchanged, the DMTF schema we target stayed stable. No CLI surface change, no daemon wire protocol change, no report JSON schema change.

Kepler scraper aligned to the Kepler v2 series

The container variant now reads kepler_container_cpu_joules_total (was kepler_container_joules_total) keyed by container_name (unchanged). The new Process variant reads kepler_process_cpu_joules_total keyed by comm (the kernel command-name label, was the non-existent command).

Removed metric_kind variants

metric_kind = "process_package" and metric_kind = "process_dram" are removed. Both targeted metrics that Kepler never published at any granularity. Operators with these values in .perf-sentinel.toml get a config-load error pointing at the replacement:

[green.kepler] metric_kind 'process_package' was removed in v0.7.5.
Kepler v2 only exposes per-process CPU joules, use 'process' instead.

The new accepted set is "container" (default) and "process", matched case-insensitively for parity with parse_daemon_environment on the same TOML surface.

Defensive hardening

So the same class of bug cannot ship silently again, the rest of the diff adds layered validation:

  • Config-load pre-validation. load_from_str now parses metric_kind before the lossy Config::from conversion, mirroring the daemon-environment pattern. Invalid values surface as ConfigError::Validation at startup instead of a buried tracing::error followed by silent Kepler disable.
  • TASK_COMM_LEN cap. When metric_kind = "process", service_mappings label values are capped at 15 bytes to match the Linux kernel's comm truncation. A label longer than 15 bytes can never match a real sample on the wire, so rejecting at config load saves a debugging round.
  • Cardinality cap. service_mappings is capped at 1024 entries, mirroring MAX_SERVICE_REGIONS. Bounds the config-load memory footprint against fat-finger or hostile configs.
  • Control-character rejection. parse_kepler_metric_kind and validate_kepler_service_mappings reject C0/C1 control characters in the operator-supplied strings before any error message interpolates them. Closes a low-severity ANSI-injection vector where a hostile .perf-sentinel.toml could embed escape sequences and have them rendered through stderr the first time an operator ran the binary on the file.

Zero-sample diagnostic

A new warn-once log fires after three consecutive HTTP-200 scrapes that yield zero matching samples, the exact failure mode of running v0.7.5 against a Kepler version older than v0.10:

Kepler endpoint replied HTTP 200 but no samples matched the configured
metric across the last 3 ticks. Most common cause: the cluster runs a
Kepler exporter older than v0.10 (legacy metric names without the
'_cpu_' infix). Other causes: metric_kind mismatched with the deployment
topology, or service_mappings label values that do not exist on the wire.

perf_sentinel_kepler_last_scrape_age_seconds keeps its existing semantics, it resets to 0 on every HTTP-200. The new docs note in docs/METRICS.md "Kepler scrape counters" calls out that alerts driven only by the gauge will not catch the zero-sample case and should be paired with rate(perf_sentinel_kepler_scrape_total{status="success"}[5m]) and the daemon-side co2.model tag presence.

Documentation

  • docs/CONFIGURATION.md and its FR mirror: metric_kind reference table reduced to container / process, examples updated.
  • docs/LIMITATIONS.md and its FR mirror: "Kepler precision bounds" updated, metric_kind references corrected.
  • docs/METRICS.md: new sections "Kepler scrape counters" and "Redfish scrape counters" mirroring the existing Scaphandre section, with the zero-sample staleness note.
  • crates/sentinel-core/src/score/kepler/config.rs: KeplerMetricKind enum doc warns explicitly that Kepler v1 / pre-0.10 deployments are not supported.
  • crates/sentinel-core/src/score/kepler/parser.rs: module doc refreshed to reflect the Kepler v2 series.
  • examples/perf-sentinel.toml: commented [green.kepler] block updated.

Tests

The diff adds 11 unit tests against the new code paths and renames the existing Kepler fixtures (26 hardcoded Prometheus literals) to the v2 series:

  • parse_kepler_metric_kind case-insensitive matching, empty-string rejection, raw value preservation in error messages, control-character rejection, legacy migration round-trip through load_from_str.
  • validate_kepler_service_mappings 15-byte boundary for Process, cardinality cap.
  • convert_kepler_section_with_env Process happy path.
  • track_zero_sample_streak four edges: under threshold, at threshold, latch persistence over a longer streak, reset on non-empty scrape.

Why this is a patch and not a minor

The bug fix removes two metric_kind enum values, which is technically a config-input breaking change, but neither value ever produced a working Kepler scraper in v0.7.4. No operator could be running v0.7.4 with these values and getting useful telemetry, so the surface area of compat impact is empty. The renamed container metric (kepler_container_joules_totalkepler_container_cpu_joules_total) is internal to the scraper, the operator-facing metric_kind = "container" config value is unchanged. The CLI subcommand surface, daemon HTTP routes, and report JSON schema are byte-for-byte identical to v0.7.4. The co2.model enum gains no new value, the existing kepler_ebpf tag is preserved.

Verifying this release

# Binary integrity via SLSA Build L3 attestation
gh attestation verify perf-sentinel-linux-amd64 \
  --owner robintra --repo perf-sentinel

# A periodic disclosure produced by this binary
perf-sentinel verify-hash --report perf-sentinel-report.json \
  --expected-identity "https://github.com/robintra/perf-sentinel/.github/workflows/release.yml@refs/tags/v0.7.5" \
  --expected-issuer "https://token.actions.githubusercontent.com"

gh CLI 2.49 or newer required for gh attestation verify (unchanged from v0.7.2).

Full Changelog: v0.7.4...v0.7.5