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
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "otel-as-code",
"version": "0.8.18",
"version": "0.8.19",
"description": "OpenTelemetry instrumentation and observability-as-code for any stack",
"author": {
"name": "radra.gwent",
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ jobs:
run: bash tests/hooks/go-instrument-wiring.test.sh
- name: Run PHP refusal test (detected, in scope, refused with a reason)
run: bash tests/hooks/php-refusal.test.sh
- name: Run Grafana SLO pattern contract (skill text names every block the validated golden carries; #165)
run: bash tests/hooks/grafana-slo-pattern.test.sh
- name: Run zero-services guard contract (every command answers an empty scan the same way; #164)
run: bash tests/hooks/zero-services-guard.test.sh
- name: Run collector exporter-auth contract (--exporter-header; --force keeps exporter headers; #171)
Expand Down
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
> of the same shape, and every concrete line item that isn't done yet has a linked tracking issue —
> comment there rather than only in this file if you want to weigh in.

Current release: **0.8.18**. Node.js, Python, Java, .NET, Go, Ruby, and Rust (axum) instrumentation, Terraform
Current release: **0.8.19**. Node.js, Python, Java, .NET, Go, Ruby, and Rust (axum) instrumentation, Terraform
for Grafana, Datadog, New Relic, and Dash0, semconv pinned at 1.44.0. Since 0.2.0 the command set
has grown past generation: `/otel-uninstrument` rolls instrumentation back behind an ownership
marker, `/otel-remediate` proposes a deployment-config diff for services that have no application
Expand Down
2 changes: 1 addition & 1 deletion agents/terraform-gen.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ Rules:
## Grafana main.tf

Generate using patterns from the `terraform-patterns` skill (grafana section).
Include: provider block, grafana_folder, grafana_dashboard (request rate + error rate + p99 latency panels **plus one panel per confirmed `businessAttrs` entry** — see "Business-attribute panels" above), grafana_rule_group (error rate > 5% for 5m, p99 > 500ms for 5m), grafana_slo (availability SLO at 99.9% over 30d).
Include: provider block, grafana_folder, grafana_dashboard (request rate + error rate + p99 latency panels **plus one panel per confirmed `businessAttrs` entry** — see "Business-attribute panels" above), grafana_rule_group (error rate > 5% for 5m, p99 > 500ms for 5m), grafana_slo (availability SLO at 99.9% over 30d, with its three required blocks: `query`, `objectives` and at least one `destination_datasource { uid = var.prometheus_datasource_uid }`; leaving the last out fails `terraform validate`, #165).

## Datadog main.tf

Expand Down
40 changes: 39 additions & 1 deletion skills/terraform-patterns/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,45 @@ variable "grafana_service_account_token" {
1. `grafana_folder` — create a folder to scope all generated resources
2. `grafana_dashboard` — uses `config_json` with a JSON-encoded dashboard model
3. `grafana_rule_group` — unified alerting (NOT the deprecated `grafana_alert_notification`)
4. `grafana_slo` — Grafana Cloud SLOs (requires `grafana_slo` resource, available on Cloud plans)
4. `grafana_slo` — Grafana Cloud SLOs (requires `grafana_slo` resource, available on Cloud plans).
**It has required blocks, and omitting one fails `terraform validate`** (#165): at least one
`destination_datasource` (without it: `Insufficient destination_datasource blocks … At least 1
"destination_datasource" blocks are required`), plus `query` and `objectives`. Follow the shape
in "SLO resource" below.

### SLO resource
Shares the `prometheus_datasource_uid` variable with the dashboard panels and alert rules, so there
is one place to point all of them at the Prometheus/Mimir datasource:
```hcl
variable "prometheus_datasource_uid" {
description = "UID of the Prometheus/Mimir datasource backing these queries"
type = string
default = "grafanacloud-prom"
}

resource "grafana_slo" "otel" {
name = "${var.service_name} availability"
description = "99.9% of requests to ${var.service_name} are non-5xx over 30d."

query { # required
type = "freeform"
freeform {
query = "<PromQL ratio: good / total, filtered by job — see the gotcha below>"
}
}

objectives { # required
value = 0.999
window = "30d"
}

destination_datasource { # required: at least one
uid = var.prometheus_datasource_uid
}
}
```
`tests/snapshots/grafana/main.tf.snap` is this shape and is `terraform validate`d in CI; keep the
two in step.

### Key gotchas
- `grafana_folder.uid` is auto-generated on create; reference it via `grafana_folder.<resource_label>.uid` (e.g. `grafana_folder.otel_folder.uid`)
Expand Down
37 changes: 37 additions & 0 deletions tests/hooks/grafana-slo-pattern.test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
# tests/hooks/grafana-slo-pattern.test.sh
# Locks the #165 contract: the Grafana guidance the generator reads must name every block the
# validated golden grafana_slo carries. The golden has always had destination_datasource, so CI's
# terraform validate was green, while the skill and generator text never mentioned it: whether a
# generated module validated depended on the model going beyond its instructions.
set -uo pipefail
cd "$(dirname "$0")/../.."
pass=0; fail=0
check() { if eval "$2"; then echo "PASS: $1"; pass=$((pass+1)); else echo "FAIL: $1"; fail=$((fail+1)); fi; }

SKILL="skills/terraform-patterns/SKILL.md"
GEN="agents/terraform-gen.md"
GOLD="tests/snapshots/grafana/main.tf.snap"

# Nested block names directly inside the golden's grafana_slo resource.
BLOCKS=$(awk '/^resource "grafana_slo"/{f=1;next} f&&/^}/{f=0} f&&/^ [a-z_]+ *\{/{gsub(/[ {]/,""); print}' "$GOLD")
echo "golden grafana_slo blocks: $(echo $BLOCKS)"

check "the golden grafana_slo has the three blocks this contract is about" \
'grep -qx query <<<"$BLOCKS" && grep -qx objectives <<<"$BLOCKS" && grep -qx destination_datasource <<<"$BLOCKS"'

SLO_SECTION=$(sed -n '/^### SLO resource/,/^### Key gotchas/p' "$SKILL")
for b in $BLOCKS; do
check "terraform-patterns SLO pattern includes the golden's \`$b\` block" \
'grep -qE "^ $b *\{" <<<"$SLO_SECTION"'
done
check "terraform-patterns says omitting a required block fails validate, with the real error text" \
'grep -qF "Insufficient destination_datasource blocks" "$SKILL"'
check "terraform-patterns defines the shared prometheus_datasource_uid variable the SLO uses" \
'grep -qF "variable \"prometheus_datasource_uid\"" <<<"$SLO_SECTION" && grep -qF "uid = var.prometheus_datasource_uid" <<<"$SLO_SECTION"'
check "terraform-gen names destination_datasource in its Grafana instructions" \
'grep -qF "destination_datasource { uid = var.prometheus_datasource_uid }" "$GEN"'

echo ""
echo "Results: $pass passed, $fail failed"
[ "$fail" -eq 0 ]
Loading