Files
simple-private-codecollection/codebundles/gcp-bucket-crossplane-health
stewartshea fe0946a146 Fix Crossplane Bucket generation rule to use plural resource name
Revert the earlier typo that changed the resourceTypes entry from
`buckets.storage.gcp.upbound.io` to `bucket.storage.gcp.upbound.io`
under the mistaken rationale of "consistency with preferred API
version". Kubernetes resource type specs in generation rules use the
API plural name (as reported by `kubectl api-resources`), which for
this CRD is `buckets`.

Confirmed against the live cluster:
    kubectl api-resources --api-group=storage.gcp.upbound.io
        NAME=buckets  NAMESPACED=false  KIND=Bucket

Symptom of the typo (from workspace-builder logs, image
`806-merge-343c86e3`):
    Trying custom resource bucket.storage.gcp.upbound.io
    Error scanning for custom resource instances; error: (403)

The 403 was misleading — it isn't an RBAC problem (the ClusterRole
correctly grants get/list/watch on `buckets`); it's the API server
rejecting a call for a resource type that doesn't exist. The
scope-aware discovery from runwhen-local PR #806 is working; it
just had the wrong plural to look up.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-30 22:53:57 -04:00
..

gcp-bucket-crossplane-health

A generation-rule-only codebundle. It has no sli.robot, no runbook.robot, and no meta.yaml. It ships:

File Role
.runwhen/generation-rules/gcp-bucket-crossplane-health.yaml Tells workspace-builder to discover every Crossplane Bucket (storage.gcp.upbound.io/v1beta1) in the cluster and generate one SLX per bucket.
.runwhen/templates/gcp-bucket-crossplane-health-slx.yaml Jinja template for the ServiceLevelX resource.
.runwhen/templates/gcp-bucket-crossplane-health-sli.yaml Jinja template for the ServiceLevelIndicator. Points at rw-generic-codecollection/k8s-kubectl-cmd/sli.robot.
.runwhen/templates/gcp-bucket-crossplane-health-taskset.yaml Jinja template for the Runbook (TaskSet). Points at rw-generic-codecollection/k8s-kubectl-cmd/runbook.robot in JSON-issue mode.

How it works

  1. workspace-builder loads this codecollection and reads the generation rule.
  2. The rule declares resourceTypes: [buckets.storage.gcp.upbound.io/v1beta1]. RunWhen Local's Kubernetes indexer expands that spec into plural=buckets, group=storage.gcp.upbound.io, version=v1beta1, calls list_cluster_custom_object, and stores each Bucket in the registry.
  3. The matchRules predicate matches every Bucket by name (regex .+).
  4. For each match, the rule emits an SLX with three output items (slx, sli, runbook). Each output item is rendered from the corresponding Jinja template.
  5. The SLI and Runbook templates set codeBundle.repoUrl to the internal airgap URL for rw-generic-codecollection, and pathToRobot to k8s-kubectl-cmd/{sli,runbook}.robot. That's where the actual runtime lives — this codecollection ships only the discovery and templating logic.

Health scoring

  • SLI — the KUBECTL_COMMAND runs kubectl get bucket ... -o json, pipes through jq to compute the fraction of .status.conditions[] whose status == "True", and pushes that number (0.0 to 1.0) as the metric. A healthy Crossplane Bucket has Ready=True and Synced=True, producing an SLI of 1.0.
  • TaskSet — the same generic bundle in JSON-issue mode. The jq expression emits { issuesIdentified: true|false, issues: [...] }. Each condition whose status is not True becomes one issue with a severity (higher for Ready), a reproduce_hint, and human-readable next_steps.

Why this pattern is useful

  • Reuse over rewrite: no need to fork or maintain another robot file.
  • Small blast radius: a private repo like this one holds just the organization-specific discovery logic and messaging.
  • Fast iteration: change the jq filter and the workspace-builder picks it up on the next reconcile — no runner image rebuild required.