Skip to content

Commit 8c19c25

Browse files
committed
fix(infra): keep local keycloak Argo app manual until ESO runtime creds
1 parent 721f69d commit 8c19c25

13 files changed

Lines changed: 172 additions & 12 deletions

File tree

AGENTS.decisions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
- Core ESO reconciliation is mandatory; there is no feature toggle to disable `auth-reconcile-eso-runtime-secrets`.
7878
- Keycloak is a mandatory runtime identity baseline:
7979
- ArgoCD overlays always include environment-specific `infra/gitops/argocd/core/<env>/keycloak.yaml` applications.
80+
- Local profile keeps Keycloak Argo application sync manual by default (`infra/gitops/argocd/core|overlays/local/keycloak.yaml`) so local-lite smoke remains stable until runtime credentials are reconciled.
8081
- Keycloak deploys in namespace `security` and consumes ESO-issued `security/keycloak-runtime-credentials`.
8182
- Keycloak realm model is module-scoped (`iap`, `workflows`, `langfuse`) so each auth consumer has a dedicated realm contract.
8283
- STACKIT profiles always provision a dedicated managed PostgreSQL instance for Keycloak (no toggle); local profiles always use the shared local Postgres instance.

docs/platform/consumer/quickstart.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ For local live execution, the blueprint prefers the `docker-desktop` Kubernetes
102102
Set `LOCAL_KUBE_CONTEXT` before running `infra-provision-deploy` if you want to override that default.
103103
Use `make auth-reconcile-eso-runtime-secrets` whenever you need an explicit runtime credential
104104
source-to-target ESO reconciliation pass (including readiness and target-secret verification).
105+
For local profiles, Keycloak Argo sync is manual by default; after a successful reconcile run,
106+
sync `platform-keycloak-local` explicitly from ArgoCD UI/CLI when you want to activate browser login.
105107
See [Runtime Credentials (ESO)](runtime_credentials_eso.md) for local seeding and managed-store wiring.
106108

107109
Before publishing hosts or API routes, review [Endpoint Exposure Model](endpoint_exposure_model.md)

docs/platform/consumer/runtime_credentials_eso.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ make auth-reconcile-eso-runtime-secrets
6565
- `infra/gitops/argocd/core/<env>/keycloak.yaml`
6666
- Keycloak admin and DB bootstrap credentials come from ESO target:
6767
- `security/keycloak-runtime-credentials`
68+
- Local overlay keeps Keycloak Argo sync manual by default (`infra/gitops/argocd/overlays/local/keycloak.yaml`)
69+
so local-lite smoke does not fail before runtime credentials are available.
6870
- Module-scoped realms are used by default:
6971
- IAP realm: `KEYCLOAK_REALM_IAP` (default `iap`)
7072
- Workflows realm: `KEYCLOAK_REALM_WORKFLOWS` (default `workflows`)
@@ -98,6 +100,11 @@ make auth-reconcile-eso-runtime-secrets
98100
Resulting state artifact:
99101
- `artifacts/infra/runtime_credentials_eso_reconcile.env`
100102

103+
After local runtime credentials are ready, manually sync the local Keycloak Argo application (UI or CLI):
104+
```bash
105+
argocd app sync platform-keycloak-local
106+
```
107+
101108
## Managed Profile Flow
102109

103110
The ESO source->target contract stays the same across profiles; only the source-store/provider wiring changes.

infra/gitops/argocd/core/local/keycloak.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,6 @@ spec:
8080
existingSecretKey: KEYCLOAK_DATABASE_PASSWORD
8181
extraManifests: []
8282
syncPolicy:
83-
automated:
84-
prune: true
85-
selfHeal: true
8683
syncOptions:
8784
- CreateNamespace=true
8885
- PrunePropagationPolicy=foreground

infra/gitops/argocd/overlays/local/keycloak.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,6 @@ spec:
8080
existingSecretKey: KEYCLOAK_DATABASE_PASSWORD
8181
extraManifests: []
8282
syncPolicy:
83-
automated:
84-
prune: true
85-
selfHeal: true
8683
syncOptions:
8784
- CreateNamespace=true
8885
- PrunePropagationPolicy=foreground

scripts/bin/blueprint/validate_contract.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,31 @@ def _kustomization_resources(path: Path) -> set[str]:
185185
return resources
186186

187187

188+
def _manifest_sync_policy_has_automated(content: str) -> bool:
189+
lines = content.splitlines()
190+
for idx, line in enumerate(lines):
191+
if line.strip() != "syncPolicy:":
192+
continue
193+
194+
parent_indent = len(line) - len(line.lstrip(" "))
195+
cursor = idx + 1
196+
while cursor < len(lines):
197+
candidate = lines[cursor]
198+
stripped = candidate.strip()
199+
if not stripped or stripped.startswith("#"):
200+
cursor += 1
201+
continue
202+
203+
candidate_indent = len(candidate) - len(candidate.lstrip(" "))
204+
if candidate_indent <= parent_indent:
205+
break
206+
if stripped.startswith("automated:"):
207+
return True
208+
cursor += 1
209+
return False
210+
return False
211+
212+
188213
def _validate_core_chart_values_contract(repo_root: Path) -> list[str]:
189214
errors: list[str] = []
190215

@@ -257,9 +282,11 @@ def _validate_runtime_credentials_contract(repo_root: Path) -> list[str]:
257282
"infra/gitops/argocd/core/dev/keycloak.yaml",
258283
"infra/gitops/argocd/core/stage/keycloak.yaml",
259284
"infra/gitops/argocd/core/prod/keycloak.yaml",
285+
"infra/gitops/argocd/overlays/local/keycloak.yaml",
260286
"scripts/bin/platform/auth/reconcile_eso_runtime_secrets.sh",
261287
"scripts/templates/blueprint/bootstrap/docs/platform/consumer/runtime_credentials_eso.md",
262288
"scripts/templates/infra/bootstrap/infra/gitops/argocd/core/keycloak.application.yaml.tmpl",
289+
"scripts/templates/infra/bootstrap/infra/gitops/argocd/overlays/local/keycloak.yaml",
263290
"scripts/templates/blueprint/bootstrap/blueprint/runtime_identity_contract.yaml",
264291
"scripts/templates/infra/bootstrap/infra/gitops/platform/base/security/runtime-external-secrets-core.yaml",
265292
)
@@ -331,6 +358,30 @@ def _validate_runtime_credentials_contract(repo_root: Path) -> list[str]:
331358
f"{resource_path}"
332359
)
333360

361+
keycloak_sync_policy_contract = {
362+
"infra/gitops/argocd/core/local/keycloak.yaml": False,
363+
"infra/gitops/argocd/overlays/local/keycloak.yaml": False,
364+
"infra/gitops/argocd/core/dev/keycloak.yaml": True,
365+
"infra/gitops/argocd/core/stage/keycloak.yaml": True,
366+
"infra/gitops/argocd/core/prod/keycloak.yaml": True,
367+
}
368+
for relative_path, expected_automated in keycloak_sync_policy_contract.items():
369+
manifest_path = repo_root / relative_path
370+
if not manifest_path.is_file():
371+
continue
372+
has_automated = _manifest_sync_policy_has_automated(manifest_path.read_text(encoding="utf-8"))
373+
if has_automated == expected_automated:
374+
continue
375+
if expected_automated:
376+
errors.append(
377+
f"{relative_path} must keep syncPolicy.automated enabled for managed profile convergence"
378+
)
379+
else:
380+
errors.append(
381+
f"{relative_path} must keep syncPolicy manual (syncPolicy.automated absent) "
382+
"until runtime credentials are reconciled"
383+
)
384+
334385
for consumer_path, dependency_path in RUNTIME_DEPENDENCY_EDGES:
335386
consumer_file = repo_root / consumer_path
336387
if not consumer_file.is_file():

scripts/bin/infra/bootstrap.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,9 @@ bootstrap_optional_manifest() {
480480
keycloak)
481481
keycloak_seed_env_defaults
482482
local keycloak_extra_manifests
483+
local keycloak_sync_automated_block
483484
keycloak_extra_manifests="$(keycloak_extra_manifests_block)"
485+
keycloak_sync_automated_block="$(keycloak_sync_automated_block "$env")"
484486
# Keycloak is a mandatory identity baseline; render under argocd/core even
485487
# though other modules continue using argocd/optional.
486488
ensure_file_from_rendered_template \
@@ -499,6 +501,7 @@ bootstrap_optional_manifest() {
499501
"KEYCLOAK_GATEWAY_NAME=$KEYCLOAK_GATEWAY_NAME" \
500502
"KEYCLOAK_GATEWAY_CLASS_NAME=$KEYCLOAK_GATEWAY_CLASS_NAME" \
501503
"KEYCLOAK_TLS_SECRET_NAME=$KEYCLOAK_TLS_SECRET_NAME" \
504+
"KEYCLOAK_SYNC_AUTOMATED_BLOCK=$keycloak_sync_automated_block" \
502505
"KEYCLOAK_EXTRA_MANIFESTS_BLOCK=$keycloak_extra_manifests"
503506
if [[ "$env" == "local" ]]; then
504507
local keycloak_core_manifest="$ROOT_DIR/infra/gitops/argocd/core/local/keycloak.yaml"

scripts/lib/infra/keycloak.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,23 @@ keycloak_public_endpoints_enabled() {
5050
[[ "$(normalize_bool "${PUBLIC_ENDPOINTS_ENABLED:-false}")" == "true" ]]
5151
}
5252

53+
keycloak_sync_automated_block() {
54+
local environment="${1:-$(profile_environment)}"
55+
56+
if [[ "$environment" == "local" ]]; then
57+
# Keep local Keycloak manual by default until runtime credentials are
58+
# reconciled, avoiding local-lite smoke regressions from missing secrets.
59+
printf ''
60+
return 0
61+
fi
62+
63+
cat <<'EOF'
64+
automated:
65+
prune: true
66+
selfHeal: true
67+
EOF
68+
}
69+
5370
keycloak_extra_manifests_block() {
5471
if ! keycloak_public_endpoints_enabled; then
5572
printf '%s' " extraManifests: []"

scripts/templates/blueprint/bootstrap/docs/platform/consumer/quickstart.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ For local live execution, the blueprint prefers the `docker-desktop` Kubernetes
102102
Set `LOCAL_KUBE_CONTEXT` before running `infra-provision-deploy` if you want to override that default.
103103
Use `make auth-reconcile-eso-runtime-secrets` whenever you need an explicit runtime credential
104104
source-to-target ESO reconciliation pass (including readiness and target-secret verification).
105+
For local profiles, Keycloak Argo sync is manual by default; after a successful reconcile run,
106+
sync `platform-keycloak-local` explicitly from ArgoCD UI/CLI when you want to activate browser login.
105107
See [Runtime Credentials (ESO)](runtime_credentials_eso.md) for local seeding and managed-store wiring.
106108

107109
Before publishing hosts or API routes, review [Endpoint Exposure Model](endpoint_exposure_model.md)

scripts/templates/blueprint/bootstrap/docs/platform/consumer/runtime_credentials_eso.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ make auth-reconcile-eso-runtime-secrets
6565
- `infra/gitops/argocd/core/<env>/keycloak.yaml`
6666
- Keycloak admin and DB bootstrap credentials come from ESO target:
6767
- `security/keycloak-runtime-credentials`
68+
- Local overlay keeps Keycloak Argo sync manual by default (`infra/gitops/argocd/overlays/local/keycloak.yaml`)
69+
so local-lite smoke does not fail before runtime credentials are available.
6870
- Module-scoped realms are used by default:
6971
- IAP realm: `KEYCLOAK_REALM_IAP` (default `iap`)
7072
- Workflows realm: `KEYCLOAK_REALM_WORKFLOWS` (default `workflows`)
@@ -98,6 +100,11 @@ make auth-reconcile-eso-runtime-secrets
98100
Resulting state artifact:
99101
- `artifacts/infra/runtime_credentials_eso_reconcile.env`
100102

103+
After local runtime credentials are ready, manually sync the local Keycloak Argo application (UI or CLI):
104+
```bash
105+
argocd app sync platform-keycloak-local
106+
```
107+
101108
## Managed Profile Flow
102109

103110
The ESO source->target contract stays the same across profiles; only the source-store/provider wiring changes.

0 commit comments

Comments
 (0)