ADR-059: Multi-Version Keycloak Support with Single Canonical Model¶
Category: architecture Provenance: guided-ai
Decision¶
The operator supports Keycloak 24.0.0 and higher using a Single Canonical Model + Adapter architecture. A single set of Pydantic models is generated from the canonical Keycloak version (currently 26.5.2). All operator logic (Reconcilers, Admin Client, Validation) is written against this canonical model. Version-specific adapters handle field conversions, validation, and incompatibilities at runtime. This approach eliminates code duplication while providing clear error messages when using features unavailable in older versions.
Rationale¶
Keycloak introduces breaking changes in minor and major versions (e.g., removing fields, changing types). The Single Canonical Model approach provides several benefits: 1. Type safety - All code is written against strongly-typed Pydantic models 2. Maintainability - Only one model file to maintain, not 20+ version-specific files 3. Clear errors - Version incompatibilities are caught early with clear error messages 4. Reduced complexity - No need for complex model selection or conversion logic in reconcilers 5. CI/CD simplicity - Only test against canonical version; adapters handle other versions The adapter pattern keeps version-specific logic isolated and testable.
Agent Instructions¶
Using the Multi-Version Architecture¶
When modifying the KeycloakAdminClient or working with Keycloak APIs:
1. Always import models from keycloak_operator.models.keycloak_api (the canonical models).
2. Do NOT create version-specific model files - there is only ONE model file.
3. For version-specific behavior, add logic to the appropriate adapter in keycloak_operator/compatibility/adapters.py.
4. Use adapter.validate_for_version(spec) to check spec compatibility before reconciliation.
5. Use adapter.convert_to_keycloak(data, model_name) to convert outbound requests.
6. Use adapter.convert_from_keycloak(data, model_name) to convert inbound responses.
7. Check adapter.get_status_conditions() after validation to get warnings/errors for CR status.
8. Breaking changes between versions should be handled in adapter _apply_outbound_conversions() and _apply_inbound_conversions().
Adding Support for a New Keycloak Major Version¶
When a new Keycloak major version is released (e.g., 27.0.0):
- Check for breaking changes: Review Keycloak release notes and OpenAPI spec diff
- Update constants: Add version to
MINIMUM_KEYCLOAK_VERSIONcheck inconstants.pyif needed - Create adapter (if needed): Add new adapter class in
compatibility/adapters.pyfor version-specific conversions - Register adapter: Update
get_adapter()factory function to return appropriate adapter - Add unit tests: Test adapter conversions in
tests/unit/test_compatibility.py - Validate manually: Run full integration test suite with
KEYCLOAK_VERSION=27.0.0 task test:all - Document validation: Update
scripts/keycloak_versions.yamlwith validation date and commit
Updating the Canonical Model Version¶
When updating the canonical model version (e.g., from 26.5.2 to 27.0.0):
- Download new spec:
curl -o keycloak-api-spec.yaml https://www.keycloak.org/docs-api/27.0.0/rest-api/openapi.yaml - Regenerate models:
task keycloak:models - Review changes:
git diff src/keycloak_operator/models/keycloak_api.py - Update constants: Set
CANONICAL_MODEL_VERSION = "27.0.0"inconstants.py - Update keycloak_versions.yaml: Change
canonical_version.versionto new version - Fix breaking changes: Update adapters for any removed/changed fields
- Run full test suite:
task test:allto ensure all tests pass - Document in ADR notes: Add breaking changes to the rationale section of this ADR
Validating a Keycloak Version¶
To validate support for a specific Keycloak version:
- Set environment variable:
export KEYCLOAK_VERSION=24.0.0 - Run full test suite:
task test:all - If tests pass, document in
scripts/keycloak_versions.yamlundervalidated_versions - Include: version, date, commit hash, and pass/fail status
Port Behavior¶
Use helpers from keycloak_operator.utils.validation:
- supports_management_port(image, version_override): Returns True for 25.x+
- get_health_port(image, version_override): Returns 9000 for 25.x+, 8080 for 24.x
Only the canonical Keycloak version is tested in the CI/CD pipeline. Support for other versions is verified through manual validation runs documented in scripts/keycloak_versions.yaml.
Known breaking changes handled by adapters: - 26.4.0: Removed oAuth2DeviceCodeLifespan and oAuth2DevicePollingInterval fields - 26.3.0: Changed ClientPolicyCondition/Executor.configuration from list[Any] to dict[str, Any] - 25.x and earlier: Organizations feature not available (requires 26.0.0+) - 24.x: No management port (KC_HTTP_MANAGEMENT_PORT), health checks use port 8080
Rejected Alternatives¶
Single Version Support (Status Status Quo)¶
Users cannot use the operator with older supported Red Hat Build of Keycloak versions (e.g., 24.x).
Multiple Model Files (One per Version)¶
Generates 20+ model files (v24.py, v25.py, v26_0.py, v26_1.py, etc.), massive code duplication, complex import logic, and maintenance burden.
Multiple Dispatch / Versioned Reconcilers¶
Writing separate reconciliation logic for every version would lead to massive code duplication and maintenance burden.
Manual Model Maintenance¶
Too error-prone and labor-intensive given the size of the Keycloak API (~250 models). Automated generation is safer.