apiVersion: runwhen.com/v1 kind: Runbook metadata: name: {{slx_name}} labels: {% include "common-labels.yaml" %} annotations: {% include "common-annotations.yaml" %} spec: location: {{default_location}} # See the SLI template for the rationale on hardcoding the generic # codecollection URL here. codeBundle: repoUrl: http://rw-airgap-cc-catalog-svc.runwhen-env-airgap:8080/git/rw-generic-codecollection.git ref: main pathToRobot: codebundles/k8s-kubectl-cmd/runbook.robot configProvided: - name: TASK_TITLE value: 'Inspect Crossplane GCP Bucket {{match_resource.resource.metadata.name}} conditions' - name: TIMEOUT_SECONDS value: '300' # Enable JSON-issue mode in the generic runbook: it will parse the # stdout JSON, check the trigger key, and emit one RunWhen issue per # element in the issues array. - name: ISSUE_JSON_QUERY_ENABLED value: 'true' - name: ISSUE_JSON_TRIGGER_KEY value: issuesIdentified - name: ISSUE_JSON_TRIGGER_VALUE value: 'true' - name: ISSUE_JSON_ISSUES_KEY value: issues # The `jq` expression below emits the JSON envelope that the generic # runbook expects: # { issuesIdentified: , issues: [ {title,severity,...}, ... ] } # One issue per condition whose status is not "True". Severity 2 for # Ready failures (data-plane visible), 3 for others (e.g. Synced). - name: KUBECTL_COMMAND value: | kubectl get buckets.storage.gcp.upbound.io {{match_resource.resource.metadata.name}} -o json | jq -c '(.status.conditions // []) as $c | { issuesIdentified: (($c|length)==0 or ([$c[]|select(.status!="True")]|length > 0)), issues: ( if ($c|length)==0 then [ { title: "Crossplane Bucket {{match_resource.resource.metadata.name}} has no status conditions yet", severity: 3, expected: "Bucket should have Ready and Synced conditions populated by the Crossplane provider", actual: "status.conditions is empty or missing", reproduce_hint: "kubectl get buckets.storage.gcp.upbound.io {{match_resource.resource.metadata.name}} -o yaml", next_steps: "1. Check that the crossplane-system provider pod for provider-gcp-storage is Running.\n2. kubectl describe buckets.storage.gcp.upbound.io {{match_resource.resource.metadata.name}} to see reconciliation events.\n3. Verify the referenced ProviderConfig exists and is Healthy.\n4. Wait one reconcile interval and re-check.", details: (.status | tostring) } ] else [ $c[] | select(.status!="True") | { title: ("Crossplane Bucket " + .type + " condition is not True: {{match_resource.resource.metadata.name}}"), severity: (if .type=="Ready" then 2 else 3 end), expected: (.type + " condition should be True"), actual: (.type + "=" + .status + " reason=" + (.reason // "n/a") + " message=" + (.message // "n/a")), reproduce_hint: "kubectl get buckets.storage.gcp.upbound.io {{match_resource.resource.metadata.name}} -o yaml", next_steps: "1. kubectl describe buckets.storage.gcp.upbound.io {{match_resource.resource.metadata.name}}\n2. Inspect the crossplane-system provider-gcp-storage pod logs for reconcile errors.\n3. Verify the ProviderConfig references a Secret with valid GCP credentials.\n4. Check GCP IAM permissions on the target bucket / project.\n5. Look for spec drift and wait for the next reconcile.", details: (. | tostring) } ] end ) }' secretsProvided: {% if wb_version %} {% include "kubernetes-auth.yaml" ignore missing %} {% else %} - name: kubeconfig workspaceKey: {{custom.kubeconfig_secret_name}} {% endif %}