Skip to content

fix(k8s): gate deploy health on a serving CNPG cluster instead of a fully-ready one - #2985

Draft
devantler wants to merge 3 commits into
mainfrom
claude/cnpg-serving-health-gate-2639
Draft

fix(k8s): gate deploy health on a serving CNPG cluster instead of a fully-ready one#2985
devantler wants to merge 3 commits into
mainfrom
claude/cnpg-serving-health-gate-2639

Conversation

@devantler

@devantler devantler commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

🤖 Generated with Claude Code in an interactive session with @devantler.

Why

A single database losing one replica has now evicted every PR from the merge queue four times:
wedding-db 2026-06-19, coroot-db 2026-07-14, and umami-db twice on 2026-08-05. Each time the
database was serving perfectly well — primary healthy, backups green — and each time the merge lane
was blocked for hours.

The reason it keeps recurring is that the trigger is routine, not exceptional. CloudNativePG
restarts every instance whenever the operator, the barman-cloud sidecar, or the PostgreSQL image
changes, and Renovate ships those bumps continuously. #2971 (barman-cloud plugin v0.13.0 → v0.14.0)
merged earlier today and rolled every instance of all four clusters; three sat at 2 of 3 ready for
about eight minutes. Any merge_group run during that window fails its deploy and its PR is evicted.

Every mitigation so far aimed at preventing a replica from degrading (synchronous replication,
force/prune disabled). Those are worth having, but they cannot fix this: umami-db already carried
the synchronous-replication mitigation and was stranded anyway, and no mitigation stops a rolling
update from happening at all. The gate itself is what needs to change.

What

Health-check a CNPG Cluster on "the database is serving" — a primary is elected and at least one
instance is ready — instead of demanding every instance be ready. Applied to the three Kustomizations
that own a Cluster: apps (umami-db, backstage-db), infrastructure (coroot-db), and the tenant
RGD's two Kustomization templates, which covers wedding-db and every future tenant.

A cluster with no ready instance, or no elected primary, matches no current expression, stays
InProgress, and still fails the deploy — so a genuinely broken database is still caught.

This deliberately moves "one replica is down" from blocking to paging, which is direction #1 on
#2639. The paging half is #2979, whose cnpg-degraded-alert CronJob is by its own description
"deliberately independent of any Flux health gate, so relaxing that gate cannot silently remove this
coverage" — the two are designed to compose. Worth landing #2979 alongside or before this, so the
alerting path is live when the blocking path stops being the de-facto detector.

Verification

  • ksail workload validate and ksail --config ksail.prod.yaml workload validate — both clean, 554
    files each; all three changed files explicitly validated rather than skipped.

  • The CEL expression was compiled and evaluated against every cluster state observed on prod
    today, alongside a control expression reproducing current kstatus behaviour. The two diverge in
    exactly the degraded cases — confirming the change is not a no-op — and agree on the failure cases:

    State new gate current behaviour
    healthy 3/3 pass pass
    umami-db, stranded replica (2/3) pass fail ← the wedge
    rolling update from chore(deps): update helm release plugin-barman-cloud to v0.7.1 #2971 (2/3) pass fail ← the wedge
    total outage 0/3 fail fail
    no primary elected fail fail
    fresh apply, empty status fail (waits) fail (waits)
  • Field references checked against all four live clusters: status.readyInstances and
    status.currentPrimary are present on every one.

One honest edge: if status is absent entirely the expression errors rather than returning
false. Flux's own documented examples don't guard against this either, and the docs frame the
consequence as waiting until timeout — the same outcome kstatus already gives an uninitialised
resource. The realistic fresh-apply state (status: {}) returns false cleanly.

Fixes #2639

Important

Merge-order gate: #2985 and #3004 both re-approve the same expectedRenderedSurfaceSHA from the same base value. Whichever merges second will conflict on that line and must re-measure and re-approve against the new main — the second PR's hash is not valid once the first lands.

…ully-ready one

kstatus marks a CNPG Cluster NotReady for the whole of any rolling update,
and CNPG rolls every instance whenever the operator, the barman-cloud plugin
sidecar, or the PostgreSQL image changes. Those are routine Renovate bumps:
plugin v0.13.0 -> v0.14.0 (#2971) restarted every instance of all four
clusters on 2026-08-05.

A database that is merely rolling, or serving on 2 of 3 instances, therefore
failed the apps/infrastructure health check, timed out the deploy, and evicted
every PR from the merge queue - wedding-db 2026-06-19, coroot-db 2026-07-14,
umami-db twice on 2026-08-05.

Gate on 'the database is serving' instead: a primary is elected and at least
one instance is ready. A cluster with no ready instance matches no current
expression, stays InProgress, and still fails the deploy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

⚠️MegaLinter analysis: Success with warnings

⚠️ BASH / bash-exec - 5 errors
Results of bash-exec linter (version 5.3.9)
See documentation on https://megalinter.io/9.6.0/descriptors/bash_bash_exec/
-----------------------------------------------

✅ [SUCCESS] scripts/dr-rebuild-supersession-guard.sh
❌ [ERROR] scripts/ghcr-auth-lib.sh
    Error: File:[scripts/ghcr-auth-lib.sh] is not executable

✅ [SUCCESS] scripts/guard-cilium-homogeneous-device-rollout.sh
✅ [SUCCESS] scripts/megalinter-scan-counts.sh
✅ [SUCCESS] scripts/normalize-sarif-paths.sh
❌ [ERROR] scripts/refresh-flux-ghcr-auth-safety.sh
    Error: File:[scripts/refresh-flux-ghcr-auth-safety.sh] is not executable

✅ [SUCCESS] scripts/refresh-flux-ghcr-auth.sh
✅ [SUCCESS] scripts/report-cilium-rollout-gate-suppression.sh
✅ [SUCCESS] scripts/run-ksail-prod-with-pull-auth.sh
✅ [SUCCESS] scripts/summarize-sarif-findings.sh
✅ [SUCCESS] scripts/tests/test-cilium-bandwidth-manager-component.sh
✅ [SUCCESS] scripts/tests/test-cilium-homogeneous-devices-activation.sh
✅ [SUCCESS] scripts/tests/test-cilium-homogeneous-devices-autoscaler-gate.sh
✅ [SUCCESS] scripts/tests/test-cilium-homogeneous-devices-flux-wait.sh
✅ [SUCCESS] scripts/tests/test-cilium-mutual-auth-policy-regressions.sh
✅ [SUCCESS] scripts/tests/test-cilium-mutual-auth-policy.sh
✅ [SUCCESS] scripts/tests/test-cilium-rollout-gate-suppression-signal.sh
✅ [SUCCESS] scripts/tests/test-dr-rebuild-supersession-guard.sh
✅ [SUCCESS] scripts/tests/test-kyverno-admission-vpa.sh
❌ [ERROR] scripts/tests/test-normalize-sarif-paths.sh
    Error: File:[scripts/tests/test-normalize-sarif-paths.sh] is not executable

✅ [SUCCESS] scripts/tests/test-openbao-oidc-role.sh
❌ [ERROR] scripts/tests/test-opencost-usage-scraper.sh
    Error: File:[scripts/tests/test-opencost-usage-scraper.sh] is not executable

❌ [ERROR] scripts/tests/test-refresh-flux-ghcr-auth-safety.sh
    Error: File:[scripts/tests/test-refresh-flux-ghcr-auth-safety.sh] is not executable

✅ [SUCCESS] scripts/tests/test-restrict-tenant-secret-stores.sh
✅ [SUCCESS] scripts/tests/test-summarize-sarif-findings.sh
✅ [SUCCESS] scripts/tests/test-verify-published-evidence.sh
✅ [SUCCESS] scripts/update-vendored-operators.sh
✅ [SUCCESS] scripts/validate-alert-coverage.sh
✅ [SUCCESS] scripts/verify-published-evidence.sh
✅ [SUCCESS] scripts/wait-for-platform-flux-revision.sh
⚠️ REPOSITORY / checkov - 15 errors
ernetes-policy-index/bc-k8s-33

		Code lines for this resource are too many. Please use IDE of your choice to review the file.
Check: CKV_K8S_25: "Minimize the admission of containers with added capability"
	FAILED for resource: Deployment.kube-system.coredns
	File: /k8s/providers/docker/infrastructure/controllers/coredns/deployment.yaml:2-146
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-24

		Code lines for this resource are too many. Please use IDE of your choice to review the file.
Check: CKV_K8S_11: "CPU limits should be set"
	FAILED for resource: Deployment.kube-system.coredns
	File: /k8s/providers/docker/infrastructure/controllers/coredns/deployment.yaml:2-146
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-10

		Code lines for this resource are too many. Please use IDE of your choice to review the file.
Check: CKV_K8S_40: "Containers should run as a high UID to avoid host conflict"
	FAILED for resource: Job.userns-longhorn-smoke.userns-longhorn-smoke
	File: /k8s/providers/hetzner/apps/userns-longhorn-smoke/job.yaml:5-154
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-37

		Code lines for this resource are too many. Please use IDE of your choice to review the file.
Check: CKV_K8S_9: "Readiness Probe Should be Configured"
	FAILED for resource: PodTemplate.overprovisioning.overprovisioning
	File: /k8s/providers/hetzner/infrastructure/overprovisioning/pod-template.yaml:29-86
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-8

		Code lines for this resource are too many. Please use IDE of your choice to review the file.
Check: CKV_K8S_8: "Liveness Probe Should be Configured"
	FAILED for resource: PodTemplate.overprovisioning.overprovisioning
	File: /k8s/providers/hetzner/infrastructure/overprovisioning/pod-template.yaml:29-86
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-7

		Code lines for this resource are too many. Please use IDE of your choice to review the file.
Check: CKV_K8S_49: "Minimize wildcard use in Roles and ClusterRoles"
	FAILED for resource: Role.github-config.github-config-managed-resources
	File: /k8s/bases/apps/github-config/role.yaml:20-55
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/ensure-minimized-wildcard-use-in-roles-and-clusterroles

		20 | apiVersion: rbac.authorization.k8s.io/v1
		21 | kind: Role
		22 | metadata:
		23 |   name: github-config-managed-resources
		24 |   namespace: github-config
		25 |   labels:
		26 |     app.kubernetes.io/managed-by: ksail
		27 | rules:
		28 |   - apiGroups:
		29 |       - repo.github.m.upbound.io
		30 |       - team.github.m.upbound.io
		31 |       - actions.github.m.upbound.io
		32 |       - enterprise.github.m.upbound.io
		33 |       - github.m.upbound.io
		34 |     resources:
		35 |       - "*"
		36 |     verbs:
		37 |       - get
		38 |       - list
		39 |       - watch
		40 |       - create
		41 |       - update
		42 |       - patch
		43 |       - delete
		44 |   - apiGroups:
		45 |       - external-secrets.io
		46 |     resources:
		47 |       - externalsecrets
		48 |     verbs:
		49 |       - get
		50 |       - list
		51 |       - watch
		52 |       - create
		53 |       - update
		54 |       - patch
		55 |       - delete

Check: CKV_K8S_35: "Prefer using secrets as files over secrets as environment variables"
	FAILED for resource: CronJob.umami.umami-provision-tenants
	File: /k8s/bases/apps/umami/cron-job.yaml:44-318
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-33

		Code lines for this resource are too many. Please use IDE of your choice to review the file.
Check: CKV_K8S_22: "Use read-only filesystem for containers where possible"
	FAILED for resource: CronJob.umami.umami-provision-tenants
	File: /k8s/bases/apps/umami/cron-job.yaml:44-318
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-21

		Code lines for this resource are too many. Please use IDE of your choice to review the file.
Check: CKV_K8S_40: "Containers should run as a high UID to avoid host conflict"
	FAILED for resource: Job.openbao.vault-config
	File: /k8s/bases/infrastructure/vault-config/job.yaml:36-1200
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-37

		Code lines for this resource are too many. Please use IDE of your choice to review the file.
Check: CKV_K8S_40: "Containers should run as a high UID to avoid host conflict"
	FAILED for resource: CronJob.openbao.vault-snapshot
	File: /k8s/bases/infrastructure/vault-backup/cron-job.yaml:23-197
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-37

		Code lines for this resource are too many. Please use IDE of your choice to review the file.
Check: CKV_K8S_40: "Containers should run as a high UID to avoid host conflict"
	FAILED for resource: Job.openbao.vault-snapshot-init
	File: /k8s/bases/infrastructure/vault-backup/job.yaml:23-191
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-37

		Code lines for this resource are too many. Please use IDE of your choice to review the file.
Check: CKV_K8S_9: "Readiness Probe Should be Configured"
	FAILED for resource: Deployment.opencost.opencost-usage-scraper
	File: /k8s/bases/infrastructure/opencost/components/usage-scraper/deployment.yaml:1-78
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-8

		Code lines for this resource are too many. Please use IDE of your choice to review the file.
Check: CKV_K8S_8: "Liveness Probe Should be Configured"
	FAILED for resource: Deployment.opencost.opencost-usage-scraper
	File: /k8s/bases/infrastructure/opencost/components/usage-scraper/deployment.yaml:1-78
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-7

		Code lines for this resource are too many. Please use IDE of your choice to review the file.
github_actions scan results:

Passed checks: 100, Failed checks: 0, Skipped checks: 0

(Truncated to last 6666 characters out of 7904)
⚠️ SPELL / cspell - 2998 errors
"cyclonedx",
        "daemonset",
        "datapath",
        "datreeio",
        "dbname",
        "dedupe",
        "deletecollection",
        "deploymentruntimeconfigs",
        "descheduler",
        "descheduling",
        "desynchronising",
        "devantler",
        "diffable",
        "dispositioned",
        "distroless",
        "dockerconfigjson",
        "docstrings",
        "dorny",
        "dpkg",
        "drainable",
        "emptydir",
        "endgroup",
        "entrys",
        "envsubst",
        "esac",
        "etcdctl",
        "evictable",
        "extensionless",
        "externalsecret",
        "externalsecrets",
        "fanout",
        "featureflagsource",
        "fleetdm",
        "fromdateiso",
        "gatewayapi",
        "generatable",
        "generatingpolicies",
        "genkey",
        "gethomepage",
        "gocritic's",
        "golangci",
        "gosec",
        "grjtvs",
        "growfs",
        "growpart",
        "healthchecks",
        "healthz",
        "helmrelease",
        "helmreleases",
        "helmv",
        "homelab",
        "hostnames",
        "httproute",
        "idempotently",
        "imagetools",
        "imagevalidatingpolicy",
        "imranismail",
        "injective",
        "inspectable",
        "iscsi",
        "italicise",
        "ivpol",
        "keylessly",
        "keypair",
        "kprobes",
        "kptr",
        "krew",
        "ksail",
        "ksail's",
        "ksailcd",
        "kubeconfig",
        "kubeconform",
        "kubeconform's",
        "kubelet",
        "kubelet's",
        "kubelets",
        "kubelogin",
        "kubescape",
        "kubespan",
        "kubevirt",
        "kubevuln",
        "kustomization",
        "kustomizations",
        "kyverno",
        "letsencrypt",
        "libc",
        "libgnutls",
        "lintable",
        "livez",
        "loadtester",
        "locationless",
        "luks",
        "lycheeignore",
        "machineconfig",
        "materialise",
        "materialised",
        "materialises",
        "maxage",
        "maxbackup",
        "maxsize",
        "maxx",
        "microtime",
        "misconfig",
        "misconfigs",
        "misordered",
        "mktemp",
        "mlock",
        "mutatingpolicies",
        "mutatingwebhookconfigurations",
        "myapp",
        "najsk",
        "neighbour",
        "nenv",
        "netlink",
        "netpol",
        "netpols",
        "neutralises",
        "neutralising",
        "nextjs",
        "nftables",
        "nilnil",
        "nobuckets",
        "nodepod",
        "nodeport",
        "nolint",
        "nonroot",
        "normalisation",
        "normalised",
        "normalises",
        "normalising",
        "nosec",
        "nullglob",
        "ocirepository",
        "openbao",
        "opencost",
        "openfeature",
        "overprovisioning",
        "parallelised",
        "pasteable",
        "permissioning",
        "pipefail",
        "policyignore",
        "policyreports",
        "portforward",
        "preemptible",
        "preservingly",
        "prioritisable",
        "prioritisation",
        "prioritised",
        "privesc",
        "providerconfigs",
        "pseudonymization",
        "pseudonymized",
        "pseudonymizes",
        "publishapp",
        "publishprovider",
        "pushsecret",
        "pushsecrets",
        "pycache",
        "randomises",
        "rdqwpktr",
        "readyz",
        "reassertions",
        "recognisably",
        "recognise",
        "recognised",
        "recolour",
        "reconverges",
        "refreshfluxghcrauth",
        "regenerable",
        "releaserc",
        "rematerialise",
        "rematerialised",
        "replicaset",
        "repoint",
        "repointed",
        "repoints",
        "repositoryrulesets",
        "resizer",
        "restrictor",
        "retabbed",
        "retarget",
        "rmem",
        "rolebindings",
        "rollouts",
        "rshared",
        "sanitised",
        "sanitiser",
        "sanitising",
        "sarif",
        "scheckov",
        "schedulability",
        "schedulable",
        "scopeable",
        "scopeless",
        "seccomp",
        "secretbox",
        "secretstore",
        "seedable",
        "serialised",
        "serverside",
        "serviceaccount",
        "serviceaccounts",
        "sgdisk",
        "shellcheck",
        "shfmt",
        "shopt",
        "siderolabs",
        "siderolink",
        "sigstore",
        "skmde",
        "slurpfile",
        "sngle",
        "specnull",
        "spiffe",
        "srole",
        "startswith",
        "statefulset",
        "statemanager",
        "stdlib",
        "storageclass",
        "subtest",
        "summarised",
        "surfaceless",
        "syft",
        "syscall",
        "sysctls",
        "talosconfig",
        "talosctl",
        "tanzu",
        "templatesyncignore",
        "thresholded",
        "tlsv",
        "tmpl",
        "tostring",
        "tracepoints",
        "trixie",
        "trueish",
        "trustd",
        "ture",
        "uids",
        "umami",
        "umami's",
        "unablated",
        "unclickable",
        "uncompilable",
        "uncordon",
        "uncordoned",
        "undecoded",
        "undercounts",
        "undispositioned",
        "unevidenced",
        "unexcepted",
        "unfiled",
        "ungated",
        "ungenerated",
        "unifi",
        "uninvoked",
        "unmarshalling",
        "unmarshals",
        "unmodelled",
        "unparseable",
        "unprovisioned",
        "unrecognisable",
        "unrecognised",
        "unrepresentable",
        "unreviewed",
        "unroutable",
        "unrun",
        "unshippable",
        "unskipped",
        "untrackable",
        "unwired",
        "upbound",
        "updatekeys",
        "upjet",
        "upstreaming",
        "urlencode",
        "userinfo",
        "userns",
        "ushfn",
        "validatable",
        "validatealertcoverage",
        "validatingwebhookconfigurations",
        "vcunav",
        "velero",
        "virt",
        "volumesnapshot",
        "vpas",
        "vulnerabilitymanifests",
        "vulnerabilitymanifestsummary",
        "vulns",
        "vxlan",
        "wffc",
        "wgpolicyk",
        "wildcarded",
        "wlid",
        "wmem",
        "workloadconfigurationscans",
        "workloadconfigurationscansummary",
        "worktrees",
        "yannh",
        "yubikey",
        "yzwvjjmcyfnl",
        "zizmor"
    ]
}


You can also copy-paste megalinter-reports/.cspell.json at the root of your repository

(Truncated to last 6666 characters out of 532272)
⚠️ COPYPASTE / jscpd - 181 errors
[285:3 - 292:37]
Clone found (go)
 - scripts/tests/refresh-flux-ghcr-auth/rollout_safety_test.go [370:69 - 379:5] (10 lines, 57 tokens)
   scripts/tests/refresh-flux-ghcr-auth/rollout_safety_test.go [384:83 - 393:5]
Clone found (go)
 - scripts/tests/refresh-flux-ghcr-auth/rollout_safety_test.go [422:68 - 430:5] (9 lines, 51 tokens)
   scripts/tests/refresh-flux-ghcr-auth/rollout_safety_test.go [435:83 - 443:5]
Clone found (go)
 - scripts/tests/refresh-flux-ghcr-auth/rollout_safety_test.go [447:52 - 464:5] (18 lines, 123 tokens)
   scripts/tests/refresh-flux-ghcr-auth/rollout_safety_test.go [468:57 - 485:5]
Clone found (go)
 - scripts/tests/refresh-flux-ghcr-auth/rollout_safety_test.go [492:77 - 508:11] (17 lines, 118 tokens)
   scripts/tests/refresh-flux-ghcr-auth/rollout_safety_test.go [519:110 - 535:11]
Clone found (bash)
 - scripts/tests/test-cilium-bandwidth-manager-component.sh [9:1 - 54:2] (46 lines, 202 tokens)
   scripts/tests/test-cilium-homogeneous-devices-activation.sh [9:1 - 54:2]
Clone found (bash)
 - scripts/tests/test-cilium-bandwidth-manager-component.sh [52:5 - 72:2] (21 lines, 67 tokens)
   scripts/tests/test-cilium-homogeneous-devices-activation.sh [80:23 - 100:2]
Clone found (bash)
 - scripts/tests/test-cilium-bandwidth-manager-component.sh [54:1 - 72:2] (19 lines, 65 tokens)
   scripts/tests/test-opencost-usage-scraper.sh [15:1 - 33:2]
Clone found (bash)
 - scripts/tests/test-cilium-mutual-auth-policy.sh [28:29 - 41:10] (14 lines, 78 tokens)
   scripts/tests/test-cilium-mutual-auth-policy.sh [79:33 - 92:10]
Clone found (bash)
 - scripts/tests/test-cilium-mutual-auth-policy.sh [98:33 - 107:54] (10 lines, 57 tokens)
   scripts/tests/test-cilium-mutual-auth-policy.sh [118:32 - 127:54]
Clone found (bash)
 - scripts/tests/test-refresh-flux-ghcr-auth-safety.sh [177:5 - 185:51] (9 lines, 76 tokens)
   scripts/tests/test-refresh-flux-ghcr-auth-safety.sh [186:5 - 194:51]
Clone found (python)
 - scripts/tests/test_validate_homepage_bookmarks.py [46:57 - 54:54] (9 lines, 58 tokens)
   scripts/tests/test_validate_homepage_bookmarks.py [100:53 - 109:54]
Clone found (go)
 - scripts/validate-dr-signing/main.go [920:21 - 952:5] (33 lines, 145 tokens)
   scripts/validate-merge-group-heal/main.go [77:40 - 109:5]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [244:50 - 249:24] (6 lines, 103 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [756:31 - 761:24]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [276:14 - 282:4] (7 lines, 115 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [603:43 - 609:4]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [302:13 - 311:7] (10 lines, 158 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [828:18 - 837:7]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [311:1 - 316:8] (6 lines, 93 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [838:1 - 843:8]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [388:5 - 393:2] (6 lines, 82 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [574:8 - 579:9]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [389:1 - 399:23] (11 lines, 220 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [666:60 - 677:3]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [393:15 - 404:2] (12 lines, 185 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [494:17 - 505:2]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [440:15 - 451:12] (12 lines, 264 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [649:1 - 662:3]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [503:2 - 509:19] (7 lines, 89 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [688:7 - 694:19]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [503:2 - 509:33] (7 lines, 103 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [1013:58 - 1019:33]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [503:2 - 509:4] (7 lines, 74 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [1126:5 - 1132:4]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [596:30 - 601:8] (6 lines, 50 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [623:44 - 628:8]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [710:22 - 716:2] (7 lines, 135 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [1212:22 - 1218:2]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [951:33 - 959:11] (9 lines, 118 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [963:130 - 971:11]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [1158:47 - 1163:2] (6 lines, 166 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [1238:46 - 1243:2]
Clone found (python)
 - scripts/validate-naming.py [126:52 - 132:25] (7 lines, 53 tokens)
   scripts/validate-naming.py [171:82 - 177:29]
┌────────┬────────────────┬─────────────┬──────────────┬──────────────┬──────────────────┬───────────────────┐
│ Format │ Files analyzed │ Total lines │ Total tokens │ Clones found │ Duplicated lines │ Duplicated tokens │
├────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ bash   │ 33             │ 10713       │ 42086        │ 19           │ 233 (2.17%)      │ 1459 (3.47%)      │
├────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ go     │ 34             │ 27587       │ 172172       │ 96           │ 784 (2.84%)      │ 7524 (4.37%)      │
├────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ python │ 4              │ 807         │ 5606         │ 2            │ 14 (1.73%)       │ 111 (1.98%)       │
├────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ txt    │ 57             │ 3378        │ 109125       │ 64           │ 2417 (71.55%)    │ 80219 (73.51%)    │
├────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ Total: │ 128            │ 42485       │ 328989       │ 181          │ 3448 (8.12%)     │ 89313 (27.15%)    │
└────────┴────────────────┴─────────────┴──────────────┴──────────────┴──────────────────┴───────────────────┘
Found 181 clones.
HTML report saved to megalinter-reports/copy-paste/jscpd-report.html
ERROR: jscpd found too many duplicates (8.1%) over threshold (0.0%)
time: 1.32s

(Truncated to last 6666 characters out of 38626)
⚠️ MARKDOWN / markdownlint - 63 errors
ould have a language specified [Context: "```"]
AGENTS.md:101:401 error MD013/line-length Line length [Expected: 400; Actual: 1769]
AGENTS.md:105:401 error MD013/line-length Line length [Expected: 400; Actual: 1126]
AGENTS.md:106:401 error MD013/line-length Line length [Expected: 400; Actual: 1447]
AGENTS.md:159:401 error MD013/line-length Line length [Expected: 400; Actual: 649]
AGENTS.md:161:401 error MD013/line-length Line length [Expected: 400; Actual: 971]
AGENTS.md:188:401 error MD013/line-length Line length [Expected: 400; Actual: 970]
AGENTS.md:191:401 error MD013/line-length Line length [Expected: 400; Actual: 453]
AGENTS.md:210:401 error MD013/line-length Line length [Expected: 400; Actual: 1360]
AGENTS.md:279:401 error MD013/line-length Line length [Expected: 400; Actual: 491]
AGENTS.md:280:401 error MD013/line-length Line length [Expected: 400; Actual: 468]
AGENTS.md:286:401 error MD013/line-length Line length [Expected: 400; Actual: 532]
AGENTS.md:288:401 error MD013/line-length Line length [Expected: 400; Actual: 523]
AGENTS.md:291:401 error MD013/line-length Line length [Expected: 400; Actual: 613]
AGENTS.md:292:401 error MD013/line-length Line length [Expected: 400; Actual: 714]
AGENTS.md:296:401 error MD013/line-length Line length [Expected: 400; Actual: 502]
AGENTS.md:300:401 error MD013/line-length Line length [Expected: 400; Actual: 441]
AGENTS.md:305:401 error MD013/line-length Line length [Expected: 400; Actual: 427]
AGENTS.md:408:401 error MD013/line-length Line length [Expected: 400; Actual: 1139]
AGENTS.md:410:401 error MD013/line-length Line length [Expected: 400; Actual: 1240]
AGENTS.md:415:401 error MD013/line-length Line length [Expected: 400; Actual: 430]
AGENTS.md:426:401 error MD013/line-length Line length [Expected: 400; Actual: 1137]
AGENTS.md:437:401 error MD013/line-length Line length [Expected: 400; Actual: 790]
AGENTS.md:442:401 error MD013/line-length Line length [Expected: 400; Actual: 515]
CLAUDE.md:1 error MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "@AGENTS.md"]
docs/dr/alerting.md:213:28 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/crypto-custody.md:22:389 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/crypto-custody.md:23:264 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/crypto-custody.md:27:35 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/crypto-custody.md:27:161 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/crypto-custody.md:27:239 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/crypto-custody.md:114 error MD024/no-duplicate-heading Multiple headings with the same content [Context: "Custody recommendations"]
docs/dr/crypto-custody.md:245 error MD024/no-duplicate-heading Multiple headings with the same content [Context: "Custody recommendations"]
docs/dr/crypto-custody.md:251 error MD024/no-duplicate-heading Multiple headings with the same content [Context: "What to do if it leaks"]
docs/dr/crypto-custody.md:258 error MD024/no-duplicate-heading Multiple headings with the same content [Context: "What to do if it is *lost* (no..."]
docs/dr/restore-drill.md:42 error MD028/no-blanks-blockquote Blank line inside blockquote
docs/dr/runbook.md:23:102 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/runbook.md:23:487 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/runbook.md:34 error MD028/no-blanks-blockquote Blank line inside blockquote
docs/dr/runbook.md:41 error MD028/no-blanks-blockquote Blank line inside blockquote
docs/dr/runbook.md:50 error MD028/no-blanks-blockquote Blank line inside blockquote
docs/dr/runbook.md:489:92 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/spire-server-ha.md:93 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/dr/velero-cnpg.md:11 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/dr/velero-cnpg.md:56:78 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/velero-cnpg.md:56:166 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/velero-cnpg.md:57:78 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/velero-cnpg.md:57:227 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/velero-cnpg.md:58:78 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/velero-cnpg.md:58:166 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/github-management.md:38:401 error MD013/line-length Line length [Expected: 400; Actual: 419]
docs/github-management.md:40:401 error MD013/line-length Line length [Expected: 400; Actual: 522]
docs/node-autoscaling.md:14 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/oidc-kubectl.md:95 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/runtime-security.md:114 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/rwx-storage.md:9 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/unifi-management.md:14 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/unifi-management.md:62 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
README.md:116:401 error MD013/line-length Line length [Expected: 400; Actual: 540]
README.md:237:32 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
README.md:237:36 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]

(Truncated to last 6666 characters out of 6993)
⚠️ REPOSITORY / trivy - 1 error
er/infrastructure/vertical-pod-autoscalers/spire-agent.yaml:9-13
────────────────────────────────────────
   9 ┌ spec:
  10 │   targetRef:
  11 │     apiVersion: apps/v1
  12 │     kind: DaemonSet
  13 └     name: spire-agent
────────────────────────────────────────



k8s/providers/hetzner/infrastructure/vertical-pod-autoscalers/spire-server.yaml (kubernetes)
============================================================================================
Tests: 118 (SUCCESSES: 116, FAILURES: 2)
Failures: 2 (UNKNOWN: 0, LOW: 1, MEDIUM: 1, HIGH: 0, CRITICAL: 0)

KSV-0037 (MEDIUM): VerticalPodAutoscaler 'spire-server' should not be set with 'kube-system' namespace
════════════════════════════════════════
ensure that user resources are not placed in kube-system namespace

See https://avd.aquasec.com/misconfig/ksv-0037
────────────────────────────────────────
 k8s/providers/hetzner/infrastructure/vertical-pod-autoscalers/spire-server.yaml:7-11
────────────────────────────────────────
   7 ┌ spec:
   8 │   targetRef:
   9 │     apiVersion: apps/v1
  10 │     kind: StatefulSet
  11 └     name: spire-server
────────────────────────────────────────


KSV-0039 (LOW): A LimitRange policy with a default requests and limits for each container should be configured
════════════════════════════════════════
Ensure that a LimitRange policy is configured to limit resource usage for namespaces or nodes

See https://avd.aquasec.com/misconfig/ksv-0039
────────────────────────────────────────
 k8s/providers/hetzner/infrastructure/vertical-pod-autoscalers/spire-server.yaml:7-11
────────────────────────────────────────
   7 ┌ spec:
   8 │   targetRef:
   9 │     apiVersion: apps/v1
  10 │     kind: StatefulSet
  11 └     name: spire-server
────────────────────────────────────────



k8s/providers/hetzner/infrastructure/vertical-pod-autoscalers/tetragon-operator.yaml (kubernetes)
=================================================================================================
Tests: 118 (SUCCESSES: 116, FAILURES: 2)
Failures: 2 (UNKNOWN: 0, LOW: 1, MEDIUM: 1, HIGH: 0, CRITICAL: 0)

KSV-0037 (MEDIUM): VerticalPodAutoscaler 'tetragon-operator' should not be set with 'kube-system' namespace
════════════════════════════════════════
ensure that user resources are not placed in kube-system namespace

See https://avd.aquasec.com/misconfig/ksv-0037
────────────────────────────────────────
 k8s/providers/hetzner/infrastructure/vertical-pod-autoscalers/tetragon-operator.yaml:7-11
────────────────────────────────────────
   7 ┌ spec:
   8 │   targetRef:
   9 │     apiVersion: apps/v1
  10 │     kind: Deployment
  11 └     name: tetragon-operator
────────────────────────────────────────


KSV-0039 (LOW): A LimitRange policy with a default requests and limits for each container should be configured
════════════════════════════════════════
Ensure that a LimitRange policy is configured to limit resource usage for namespaces or nodes

See https://avd.aquasec.com/misconfig/ksv-0039
────────────────────────────────────────
 k8s/providers/hetzner/infrastructure/vertical-pod-autoscalers/tetragon-operator.yaml:7-11
────────────────────────────────────────
   7 ┌ spec:
   8 │   targetRef:
   9 │     apiVersion: apps/v1
  10 │     kind: Deployment
  11 └     name: tetragon-operator
────────────────────────────────────────



k8s/providers/hetzner/infrastructure/vertical-pod-autoscalers/tetragon.yaml (kubernetes)
========================================================================================
Tests: 118 (SUCCESSES: 116, FAILURES: 2)
Failures: 2 (UNKNOWN: 0, LOW: 1, MEDIUM: 1, HIGH: 0, CRITICAL: 0)

KSV-0037 (MEDIUM): VerticalPodAutoscaler 'tetragon' should not be set with 'kube-system' namespace
════════════════════════════════════════
ensure that user resources are not placed in kube-system namespace

See https://avd.aquasec.com/misconfig/ksv-0037
────────────────────────────────────────
 k8s/providers/hetzner/infrastructure/vertical-pod-autoscalers/tetragon.yaml:9-13
────────────────────────────────────────
   9 ┌ spec:
  10 │   targetRef:
  11 │     apiVersion: apps/v1
  12 │     kind: DaemonSet
  13 └     name: tetragon
────────────────────────────────────────


KSV-0039 (LOW): A LimitRange policy with a default requests and limits for each container should be configured
════════════════════════════════════════
Ensure that a LimitRange policy is configured to limit resource usage for namespaces or nodes

See https://avd.aquasec.com/misconfig/ksv-0039
────────────────────────────────────────
 k8s/providers/hetzner/infrastructure/vertical-pod-autoscalers/tetragon.yaml:9-13
────────────────────────────────────────
   9 ┌ spec:
  10 │   targetRef:
  11 │     apiVersion: apps/v1
  12 │     kind: DaemonSet
  13 └     name: tetragon
────────────────────────────────────────



ksail.prod.yaml (kubernetes)
============================
Tests: 118 (SUCCESSES: 117, FAILURES: 1)
Failures: 1 (UNKNOWN: 0, LOW: 1, MEDIUM: 0, HIGH: 0, CRITICAL: 0)

KSV-0039 (LOW): A LimitRange policy with a default requests and limits for each container should be configured
════════════════════════════════════════
Ensure that a LimitRange policy is configured to limit resource usage for namespaces or nodes

See https://avd.aquasec.com/misconfig/ksv-0039
────────────────────────────────────────
 ksail.prod.yaml:7-270
────────────────────────────────────────
   7 ┌   name: prod
   8 │ spec:
   9 │   cluster:
  10 │     distributionConfig: talos
  11 │     connection:
  12 │       context: admin@prod
  13 │       timeout: 30m
  14 │     distribution: Talos
  15 └     provider: Hetzner
  ..   
────────────────────────────────────────



ksail.yaml (kubernetes)
=======================
Tests: 118 (SUCCESSES: 117, FAILURES: 1)
Failures: 1 (UNKNOWN: 0, LOW: 1, MEDIUM: 0, HIGH: 0, CRITICAL: 0)

KSV-0039 (LOW): A LimitRange policy with a default requests and limits for each container should be configured
════════════════════════════════════════
Ensure that a LimitRange policy is configured to limit resource usage for namespaces or nodes

See https://avd.aquasec.com/misconfig/ksv-0039
────────────────────────────────────────
 ksail.yaml:7-55
────────────────────────────────────────
   7 ┌   name: local
   8 │ spec:
   9 │   cluster:
  10 │     distributionConfig: talos-local
  11 │     connection:
  12 │       context: admin@local
  13 │       timeout: 40m
  14 │     distribution: Talos
  15 └     provider: Docker
  ..   
────────────────────────────────────────



📣 Notices:
  - Version 0.73.0 of Trivy is now available, current version is 0.71.2

To suppress version checks, run Trivy scans with the --skip-version-check flag

(Truncated to last 6666 characters out of 792645)

✅ Linters with no issues

actionlint, betterleaks, git_diff, golangci-lint, grype, jsonlint, lychee, osv-scanner, prettier, prettier, revive, secretlint, shellcheck, shfmt, syft, trivy-sbom, trufflehog, v8r, v8r, yamllint, zizmor

Notices

📣 MegaLinter 9.5.0 is out! Discover the new features and security recommendations in the release announcement. (Skip this info by defining SECURITY_SUGGESTIONS: false)

See detailed reports in MegaLinter artifacts

MegaLinter is graciously provided by OX Security
Show us your support by starring ⭐ the repository

devantler and others added 2 commits August 6, 2026 23:26
The three resources this PR edits are individually pinned in the EKS CI
authorization contract, so adding `healthCheckExprs` to them moved their
fingerprints and the aggregate surface hash, failing `Validate EKS
Authorization` and its `CI - Required Checks` rollup.

Re-approved after running the measurement the constant's own note requires,
against main 72fe791: 520 rendered documents on both sides with membership
identical (zero added, removed or renamed by apiVersion|kind|namespace|name
set difference), and exactly three entries moved -- precisely the three
re-approved here. The complete rendered delta is 19 lines, all ADDED and all
part of a `healthCheckExprs` entry; none removed or modified. All 67
grant-bearing documents (Role/ClusterRole/RoleBinding/ClusterRoleBinding/
ServiceAccount) are byte-identical, as are all 116 `aws`-bearing lines, so
nothing granted to the aws/aws service account is touched.

Gate verified still enforcing: widening one verb on the pinned
`kro-tenant-rgd` ClusterRole makes the validator report that ClusterRole and
the aggregate surface as unapproved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

Validation (run locally — GitHub Actions is in a major outage)

GitHub Actions has been in a major_outage since 2026-08-06T16:33:31Z (githubstatus.com), so this
repo's own workflows are queued and not starting; only the GitHub-managed CodeQL checks report. The
head therefore carries no Validate EKS Authorization result yet. Everything below was run locally
against the exact pushed head 1f2f2d3b, with the renderer pinned to the versions the validator
requires (kubectl v1.36.2 / kustomize v5.8.1).

RED → GREEN. Before: Validate EKS Authorization failed with three unapproved rendered
fingerprints plus the aggregate surface. After: EKS CI role authorization contract passed.
go test ./scripts/validate-eks-ci-role-policy/...ok. Both overlays build; gofmt/go vet clean.

The 35 unresolved Flux substitution lines in the CI log are not 35 defects. They are diagnostic
output the validator emits only alongside an aggregate-surface mismatch, to explain a moved hash —
the code says so explicitly, and promoting them to errors was measured to fail the approved tree. They
disappear with the surface re-approval. There were exactly four real failures.

Membership measurement (the discipline the constant's own note requires), branch vs main 72fe791:

Check Result
Documents rendered 520 both sides
Membership by apiVersion|kind|namespace|name identical — 0 added, 0 removed, 0 renamed
Entries moved exactly 3, precisely the 3 re-approved here
Complete rendered delta 19 lines, all ADDED, every one part of a healthCheckExprs entry; none removed or modified
Grant-bearing docs (Role/ClusterRole/RoleBinding/ClusterRoleBinding/ServiceAccount) 67 both sides, all byte-identical
aws-bearing lines 116 both sides, byte-identical; zero in the delta

Gate proven still enforcing (ablation). Widening one verb on the pinned kro-tenant-rgd
ClusterRole made the validator report that ClusterRole and the aggregate surface as unapproved —
so this re-approval did not disable the control. Reverted byte-exact.

⚠️ Merge-order gate: #3004 re-approves the same expectedRenderedSurfaceSHA from the same base
value. Whichever of the two merges second will conflict on that line and must re-measure and
re-approve against the new main — the second PR's hash is not valid after the first lands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 🫴 Ready

Development

Successfully merging this pull request may close these issues.

fix(observability): a single stuck coroot-db replica blocks every platform merge

1 participant