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, unlikebnerd-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/kubernetescache stack, Postgres DB vars), and startup/readiness/liveness probes on the management port (9000). -
First-class provider (JAR) injection:
spec.providersrenders one initContainer per entry, copying*.jarfrom the provider image into a shared/opt/keycloak/providersvolume — covers themes, SPI providers, and custom functions with a single mechanism. -
Capability-gated managed Postgres:
spec.postgres.managed: trueapplies aPerconaPGCluster(<name>-pg) via server-side-apply, gated on thepgv2.percona.comCRD being present —Failed/PerconaCRDMissingwith 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: falsereadshost/dbname/user/password(+ optionalport) fromspec.postgres.credentialsSecret. -
Admin bootstrap credentials always via Secret (
KC_BOOTSTRAP_ADMIN_*viasecretKeyRef), never inline values.spec.adminSecretset = 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.secretNamemounts the realm JSON Secret at/opt/keycloak/data/importand 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 — seedocs/CRD-reference.md. A Kubernetes Event (reason: Phase<NewPhase>) is emitted on everystatus.phasetransition. -
Finalizer GC: removes operator-owned Secrets (tracked in
status.secrets) on deletion. The managedPerconaPGClusteris intentionally retained (no owner reference) — same data-loss-prevention policy asbnerd-gitlab-operator. -
Version resolution: transient (
VersionResolutionPending, 30s requeue) rather than permanentlyFailedwhenKeycloakVersionMap/defaultis 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 incrds/, and an optional defaultKeycloakVersionMap(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;runAsUserdeliberately unset); explicitRollingUpdateStatefulSet strategy;terminationGracePeriodSeconds: 60; and amanagement(9000) port on the client Service for metrics scraping. -
spec.postgres.nodes: overrides the managed Postgres instance replica count derived fromtopology(0= topology default;> 0wins). Managed-only and>= 0, enforced at validation. -
Optional ServiceMonitor (
spec.metrics.serviceMonitor: true): capability-gated on themonitoring.coreos.comServiceMonitorCRD. Present → an ownedServiceMonitorscraping themanagementport/metricsevery 30s; absent →MetricsExporterReady=False/ServiceMonitorCRDMissingwarning without failing the instance. -
Upgrade orchestration: same-minor changes roll one-by-one in place; cross-minor/major changes are drain-first recreates (
UpgradeInProgress→UpgradeCompleted); downgrades are refused (VersionDowngradeBlocked) unless thek8s.bnerd.com/allow-downgradebreak-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, soReady/observedVersionnever 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.