Files
home-network-k3s-impl/decisions/2026-10-30-renovate-track-keycloak-operator.md
jakman85andClaude Opus 5.5 8ed4e30201
gate / gate (pull_request) Successful in 2m57s
gate / conformance (pull_request) Has been skipped
docs(decisions): handoff for feat/renovate-track-keycloak-operator
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 03:06:56 -05:00

7.3 KiB

date, branch, title
date branch title
2026-10-30 feat/renovate-track-keycloak-operator Renovate tracks the Keycloak operator (the real Keycloak version), never auto-merged; the catalog's phantom image is pinned as documentation and disabled

What changed

  • platform/keycloak/kustomization.yaml: each of the three keycloak-k8s-resources/26.0.6 release URLs (two CRDs and kubernetes.yml) now has a # renovate: datasource=github-tags depName=keycloak/keycloak-k8s-resources marker on the line above it. That is the format the existing custom.regex marker manager parses (the kubevirt pattern). A comment explains that these URLs are the Keycloak version. The Keycloak CR has no spec.image, so the operator's RELATED_IMAGE_KEYCLOAK picks the server image.

  • renovate.json5 has two new packageRules:

    • keycloak/keycloak-k8s-resources: groupName: keycloak-operator, automerge: false, addLabels: [needs-review, blast-radius]. A comment explains why. The operator, the CRDs and the server are released in lockstep. The IdP gates SSO for every app and for kubectl. And passkey-reconciler.yaml encodes Keycloak 26.0.6 Admin API shapes that the static gate never exercises. Majors still go through the existing dashboard-approval rule.
    • quay.io/keycloak/keycloak: enabled: false. Its only occurrences are in files that deploy nothing.

    The "STILL CANNOT FIX" comment also gains a one-line correction for keycloak.

  • catalog/keycloak.yaml: source.image changed from quay.io/keycloak/keycloak (no tag) to quay.io/keycloak/keycloak:26.0.6. A comment says the image is DOCUMENTATION-ONLY and that the operator chooses the real one. apps/wave-1/keycloak.yaml was re-rendered, which changes one line.

  • platform/keycloak/README.md:

    • Corrects the false claim "Renovate maintains the version". Until now Renovate could not see the version.
    • Adds a new section, "Upgrading Keycloak: the operator IS the version". It lists the 26.0.6 assumptions in the passkey reconciler to diff against a new tag, and the post-merge checks.

The phantom: pin and disable, not delete. apps/wave-1/keycloak.yaml is not listed in apps/wave-1/kustomization.yaml, and render-golden excludes it (RENDER_GOLDEN_NOT_ONBOARDED). Deleting it would still not be a no-op:

  • It is the fixture for the keycloak cases in apps/wave-1/tests/app-slis.test.yaml (the StatefulSet-based keycloak-down rule). Those cases are discovered by the every-PrometheusRule-in-apps/wave-1 convention.
  • Removing it means moving that rule and its tests to platform/keycloak. That is a separate observability change, not a Renovate one.

Pinning plus enabled: false changes one line in a file nothing applies. It cannot affect the cluster, and render-golden stays green.

What I verified

  • Live state, prod, read-only kubectl, 2026-10-30.
    • Keycloak CR keycloak has an empty spec.image.
    • The operator Deployment runs quay.io/keycloak/keycloak-operator:26.0.6, imageID sha256:8e3a1a56346f…, with RELATED_IMAGE_KEYCLOAK=quay.io/keycloak/keycloak:26.0.6.
    • keycloak-0 and keycloak-1 both run quay.io/keycloak/keycloak:26.0.6, imageID sha256:a93d22e13b86….
    • There is no PrometheusRule containing "keycloak" in any namespace. This confirms the phantom deploys nothing, including its keycloak-down rule (see the next section).
  • renovate-config-validator on the pinned ghcr.io/renovatebot/renovate:44.167.0: Config validated successfully. The only migration note is the pre-existing docker block.
  • Local dry run (RENOVATE_PLATFORM=local, RENOVATE_DRY_RUN=full, debug, the same pinned image, this worktree, with a GitHub token passed by environment variable; the token string occurs 0 times in the log):
    • The regex manager went to 21 files / 24 deps.
    • keycloak/keycloak-k8s-resources was extracted three times from platform/keycloak/kustomization.yaml, each at currentValue 26.0.6, datasource github-tags, versioning semver.
    • Each extraction has one update, 26.8.1, updateType minor, and all three share one branchName: renovate/keycloak-operator.
    • quay.io/keycloak/keycloak was extracted from catalog/keycloak.yaml (regex) and from apps/wave-1/keycloak.yaml (kubernetes) at 26.0.6, with skipReason: disabled and no updates.
  • Rule effects. --platform=local does not print each branch's merged flags. So I ran Renovate's own applyPackageRules (from dist/util/package-rules in the same 44.167.0 image) over this renovate.json5 for each case:
    Update Result
    operator minor 26.0.6 → 26.8.1 automerge:false, groupName:keycloak-operator, labels dependencies, automated-update, needs-review, blast-radius
    operator patch same as the minor
    operator major automerge:false, dependencyDashboardApproval:true, labels add breaking
    quay.io/keycloak/keycloak, catalog and rendered enabled:false
    control: sonarr patch automerge:true, so the rule did not disable auto-merge globally
  • render-golden, host: render.py --selftest passed, and all 10 onboarded service golden(s) reproduce. Before the pin, the committed phantom was byte-identical to the canonical render. I re-rendered it after the pin.
  • Toolbox gate (commit 24db269, worktree with the ENGINE shim mounting .git and the spec repo, DEV_KUBECONFIG=/dev/null): rc 3.
    • The only stage that did not run is gitea-settings.
    • spec-immutability, decisions-log, lint, l0, render-golden, l1 and approval-policy all passed.
    • L2/L3 conformance scaffold-skipped, as expected.
    • Not green.
  • Sensitive paths: none touched (checked against infra/gitea/approval-policy.json), so no /approve-override is needed.

What I did NOT verify

  • A real Renovate run. No PR was opened by Renovate. The labels and automerge:false are proven by Renovate's own matcher, not by an opened PR.
  • That 26.8.1 is safe to adopt. I did not diff 26.0.6 against 26.8.1. That includes the reconciler's assumptions: recurseExecutions displayName, updateExecutions, the RpEntityName guard, requiredActions REPLACE, and config aliases. The README section lists them for whoever reviews the first bump PR. It also says to take or confirm a fresh CNPG backup first, because a server minor may migrate the schema.
  • FINDING, not fixed: Keycloak has no deployed "IdP down" alert. The keycloak-down SLI rule exists only inside the undeployed apps/wave-1/keycloak.yaml. promtool tests it, but it is not on the cluster: there is no keycloak PrometheusRule live. Separately, because that file exists, render.py --emit-waves would treat keycloak as onboarded. It would then add a Deployment named keycloak beside the operator's StatefulSet (the known --emit-waves hazard). The proper fix is a separate PR: move the rule and its tests into platform/keycloak, then delete the phantom.
  • The catalog tag can drift. Nothing ties catalog/keycloak.yaml's documentation tag to the operator URLs. It must be updated by hand after an operator bump, and the README says so.
  • Concurrent branches fix/renovate-hold-auth-and-self and feat/pin-pihole-jackett-plex also edit renovate.json5. Neither had merged when I checked. A rebase may be needed.