# 04 — Generation rule walkthrough ## What you'll do Read the generation rule at [`codebundles/gcp-bucket-crossplane-health/.runwhen/generation-rules/gcp-bucket-crossplane-health.yaml`](../codebundles/gcp-bucket-crossplane-health/.runwhen/generation-rules/gcp-bucket-crossplane-health.yaml) line by line and understand each block. ## The full file ```yaml apiVersion: runwhen.com/v1 kind: GenerationRules spec: platform: kubernetes generationRules: - resourceTypes: - buckets.storage.gcp.upbound.io matchRules: - type: pattern pattern: ".+" properties: [name] mode: substring slxs: - baseName: xp-bkt-hlth qualifiers: ["resource", "cluster"] baseTemplateName: gcp-bucket-crossplane-health levelOfDetail: detailed outputItems: - type: slx - type: sli - type: runbook templateName: gcp-bucket-crossplane-health-taskset.yaml ``` ## Block-by-block ### Envelope ```yaml apiVersion: runwhen.com/v1 kind: GenerationRules # note the plural — the loader rejects "GenerationRule" spec: platform: kubernetes # per-file default; can be overridden per rule generationRules: [ ... ] # one or more rules ``` - `kind` must be exactly `GenerationRules`. RunWhen Local's loader compares this to a schema constant. - `spec.platform` sets the default platform for every rule in the file. - `spec.generationRules` is a list — you can define many rules per file. ### `resourceTypes` ```yaml resourceTypes: - buckets.storage.gcp.upbound.io ``` Omitting `/v1beta2` (or `/v1beta1`) tells the indexer to use the API server's preferred version, which survives CRD schema upgrades. Pin the version only when you rely on a specific schema shape. This is the *selective discovery* signal. Because this string appears in a loaded rule, the Kubernetes indexer knows to enumerate that CRD. Without any rule mentioning a CRD, it would not be listed. You can list multiple types in one rule if you want the same SLX shape for each. Splitting into separate rules is usually cleaner though. ### `matchRules` ```yaml matchRules: - type: pattern pattern: ".+" properties: [name] mode: substring ``` Match predicates are ANDed together. The pattern predicate here says "the resource's `name` property must match the regex `.+` (i.e. non-empty) as a substring". Since every Bucket has a name, every Bucket matches. You can filter more aggressively if you want. Examples: ```yaml # Only buckets whose name starts with "prod-" - type: pattern pattern: "^prod-" properties: [name] mode: substring ``` ```yaml # Only buckets that carry the label env=production - type: exists path: "resource/metadata/labels/env" ``` ```yaml # Combine — buckets in the prod GCP project label AND with a Ready condition entry - type: and matches: - type: pattern pattern: "^runwhen-prod-.*" properties: [name] mode: substring - type: exists path: "resource/status/conditions" ``` ### `slxs` ```yaml slxs: - baseName: xp-bkt-hlth qualifiers: ["resource", "cluster"] baseTemplateName: gcp-bucket-crossplane-health levelOfDetail: detailed outputItems: - type: slx - type: sli - type: runbook templateName: gcp-bucket-crossplane-health-taskset.yaml ``` - `baseName` — short identifier appended to the qualifier prefix to form the final `slx_name`. Keep it under 15 characters (`xp-bkt-hlth` is 11). - `qualifiers` — determines both the **generated SLX name** and how many SLXs are produced. - Including `resource` means "one SLX per matching Bucket". - Including `cluster` prefixes the name with the cluster name so it is stable across multi-cluster workspaces. - **Not including `namespace`** — Crossplane managed resources are cluster-scoped; they have no namespace. - `baseTemplateName` — the file-name stem for the templates. The output items resolve to `-.yaml` unless overridden. So `type: slx` looks up `gcp-bucket-crossplane-health-slx.yaml`, `type: sli` → `-sli.yaml`, etc. - `levelOfDetail` — one of `none | basic | detailed` (or `0|1|2`). This gates whether the SLX is emitted based on the platform's LOD filter. Since our airgap `defaultLOD` is `detailed`, we set the SLX to `detailed` so it always renders. - `outputItems` — one entry per artifact we want. `type: runbook` specifies an explicit `templateName` because we want to point at a file named `-taskset.yaml`, not `-runbook.yaml`, purely for convention matching what other RunWhen bundles do. ## What resource identity ends up in the SLX Given a Bucket named `runwhen-nonprod-shared-litellm-logging` on cluster `shared-cluster`, the rule will produce an SLX whose name is roughly: ``` shared-cluster-runwhen-nonprod-shared-litellm-logging-xp-bkt-hlth ``` RunWhen Local will *also* shorten and hash-suffix pieces as needed for length. The exact rendering happens in [`generation_rules.py::make_slx_name`](https://github.com/runwhen-contrib/runwhen-local/blob/main/src/enrichers/generation_rules.py). ## Screenshot placeholders - `images/04-generation-rule-annotated.png` — the YAML above with arrows pointing to the resource type, match rule, and output items. ## Next Continue to [05 — SLX and templates](05-slx-and-templates.md).