Skip to content

Changelog

v0.1.0 (unreleased)

Initial release of the bnerd-keycloak-operator.

Features

  • KeycloakInstance CRD (namespaced, kci): single CR = one Keycloak deployment, reconciled as raw resources (StatefulSet, Services, Ingresses, Secrets) — no Flux/Helm indirection, unlike bnerd-gitlab-operator. Spec covers version, replicas, hosts, ingress (class/TLS via cert-manager), Postgres backend, admin bootstrap credentials, provider JAR injection, image-pull secrets, realm import, resource requests/limits, placement, and a raw env escape hatch.

  • KeycloakVersionMap CRD (cluster-scoped, kcvm): maps Keycloak version strings to container images, with alias support (latest, stable, "26").

  • Reference-faithful StatefulSet: rolling one-by-one updates (protects Infinispan in-memory session state), headless discovery Service for JGroups DNS_PING, the full KC_* env contract (proxy headers, hostname/strict, health/metrics, ispn/kubernetes cache stack, Postgres DB vars), and startup/readiness/liveness probes on the management port (9000).

  • First-class provider (JAR) injection: spec.providers renders one initContainer per entry, copying *.jar from the provider image into a shared /opt/keycloak/providers volume — covers themes, SPI providers, and custom functions with a single mechanism.

  • Capability-gated managed Postgres: spec.postgres.managed: true applies a PerconaPGCluster (<name>-pg) via server-side-apply, gated on the pgv2.percona.com CRD being present — Failed/PerconaCRDMissing with no silent fallback when it is not. Its generated credentials Secret (<name>-pg-db-credentials) is consumed directly as the DB credentials Secret.

  • BYO Postgres: managed: false reads host/dbname/user/password (+ optional port) from spec.postgres.credentialsSecret.

  • Admin bootstrap credentials always via Secret (KC_BOOTSTRAP_ADMIN_* via secretKeyRef), never inline values. spec.adminSecret set = BYO Secret used verbatim; empty = the operator generates and tracks <name>-admin-credentials (32-char random password, generated once, never rotated).

  • Optional realm import: spec.realmImport.secretName mounts the realm JSON Secret at /opt/keycloak/data/import and adds --import-realm.

  • Status synthesis: phase (Pending / Provisioning / Deploying / Ready / Failed), host, observedVersion, observedGeneration, conditions (Validated / BackendsReady / Progressing / Ready / UpgradeInProgress / MetricsExporterReady) with a stable reason vocabulary — see docs/CRD-reference.md. A Kubernetes Event (reason: Phase<NewPhase>) is emitted on every status.phase transition.

  • Finalizer GC: removes operator-owned Secrets (tracked in status.secrets) on deletion. The managed PerconaPGCluster is intentionally retained (no owner reference) — same data-loss-prevention policy as bnerd-gitlab-operator.

  • Version resolution: transient (VersionResolutionPending, 30s requeue) rather than permanently Failed when KeycloakVersionMap/default is missing or the requested version/alias is not yet present in it.

  • Operator Helm chart (charts/bnerd-keycloak-operator): ClusterRole + ClusterRoleBinding, namespaced leader-election Role/RoleBinding, Deployment, ServiceAccount, CRDs in crds/, and an optional default KeycloakVersionMap (versionMap.installDefault: true).

  • HA / production hardening: an owned PodDisruptionBudget (maxUnavailable: 1) when desired replicas >= 2 — removed on scale-to-1 and while a cross-minor upgrade drains; soft pod anti-affinity across nodes; pod/container security context (runAsNonRoot, seccompProfile: RuntimeDefault, allowPrivilegeEscalation: false, capabilities.drop: [ALL] on the Keycloak container and every provider initContainer; runAsUser deliberately unset); explicit RollingUpdate StatefulSet strategy; terminationGracePeriodSeconds: 60; and a management (9000) port on the client Service for metrics scraping.

  • spec.postgres.nodes: overrides the managed Postgres instance replica count derived from topology (0 = topology default; > 0 wins). Managed-only and >= 0, enforced at validation.

  • Optional ServiceMonitor (spec.metrics.serviceMonitor: true): capability-gated on the monitoring.coreos.com ServiceMonitor CRD. Present → an owned ServiceMonitor scraping the management port /metrics every 30s; absent → MetricsExporterReady=False/ServiceMonitorCRDMissing warning without failing the instance.

  • Upgrade orchestration: same-minor changes roll one-by-one in place; cross-minor/major changes are drain-first recreates (UpgradeInProgressUpgradeCompleted); downgrades are refused (VersionDowngradeBlocked) unless the k8s.bnerd.com/allow-downgrade break-glass annotation is set, in which case a cross-minor downgrade is also a drain-first recreate. Readiness is gated on the StatefulSet rollout converging onto the new revision, so Ready / observedVersion never advance while old-revision pods still serve.

  • envtest suite: controller tests covering validation, version resolution, BYO + managed Postgres paths (including invalid-Secret requeue + recovery), child-resource rendering, status phases + rollout-convergence readiness, phase event de-duplication, deletion GC, upgrade orchestration (patch rolling, cross-minor recreate, no-downgrade guard + break-glass), PDB lifecycle, and the ServiceMonitor capability gate (present, absent, and toggle-off).

  • Live kind e2e: a full integration suite (6 tests) runs the operator in-cluster against real Keycloak + Postgres pods — happy path, BYO + managed backends, deletion, patch rolling upgrade (watch-verified never scaled to zero), cross-minor recreate + no-downgrade guard + break-glass, and provider-JAR injection. Driven by make kind-up && make kind-test && make kind-down.

Known limitations

See Known Limitations for the full list.