Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 88 additions & 26 deletions .github/workflows/configurator-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ jobs:
node --test ../infra/ci/prepare-release.test.mjs
node --test ../infra/ci/buildpack-source.test.mjs
node --test ../infra/ci/image-staging.test.mjs
node --test ../infra/ci/release-lifecycle.test.mjs
- name: Test first preparations and stateful platform Plan/Apply broker
run: npm run test:platform-database
- name: Test database migrations and tenant isolation
Expand All @@ -96,10 +97,6 @@ jobs:
go run . -check
go run . -variables-source ../../../src/variables.tf -output ../../docs/accelerator-inputs.json -check
go run . -variables-source ../../../src/variables.tf -output ../../app/packages/domain/src/accelerator-inputs.json -check
- name: Verify browser flow before packaging
run: |
npx playwright install --with-deps chromium
npm run test:e2e
- name: Package production application and pinned Node runtime
run: |
mkdir -p ../.local/release/apps/api ../.local/release/packages/contracts ../.local/release/packages/domain ../.local/release/public ../.local/release/runtime
Expand All @@ -120,13 +117,14 @@ jobs:
with:
tofu_version: '1.12.6'
tofu_wrapper: false
- name: Test isolated native Plan hold and interruption
run: LZC_TEST_TOFU_BIN="$(command -v tofu)" npx vitest run tests/worker-recovery-hold.test.ts
- name: Test real OpenTofu contract and package isolated runner
- name: Verify browser flow and package native runner in parallel
run: |
node ../infra/ci/run-parallel.mjs browser 'npx playwright install --with-deps chromium && npm run test:e2e' native '
LZC_TEST_TOFU_BIN="$(command -v tofu)" npx vitest run tests/worker-recovery-hold.test.ts
npm run test:common-contract
LZC_TEST_TOFU_BIN="$(command -v tofu)" npm run test:plan
LZC_HOSTED_EXECUTION_PACKAGE=true LZC_PACKAGE_APPLICATION_ROOT=true LZC_PACKAGE_PLATFORM_UPGRADE_ROOT=true bash ../deploy/runner/package.sh
'
cp -R ../.local/runner ../.local/release/inspection
tar --exclude='./inspection/accelerator/firewall-image.qcow2' -czf ../.local/release.tar.gz -C ../.local/release .
(cd ../.local && sha256sum release.tar.gz > release.sha256)
Expand Down Expand Up @@ -264,6 +262,14 @@ jobs:
github-token: ${{ github.token }}
merge-multiple: true
path: landing-zone-configurator/.local
- name: Detect source-bound parallel release support
id: lifecycle
run: |
if [ -f infra/ci/stage-release-application.sh ] && [ -f infra/ci/package-migration.mjs ]; then
echo 'parallel=true' >> "$GITHUB_OUTPUT"
else
echo 'parallel=false' >> "$GITHUB_OUTPUT"
fi
- name: Unpack application
env:
LZC_RELEASE_REVISION: ${{ steps.source.outputs.release_revision }}
Expand All @@ -279,6 +285,9 @@ jobs:
mkdir -p .local/runner
tar -xzf .local/runner.tar.gz -C .local/runner
diff -qr .local/release/inspection .local/runner
if [ "${{ steps.lifecycle.outputs.parallel }}" = true ]; then
node infra/ci/package-migration.mjs .local/release .local/migration
fi
- name: Prepare encrypted state read access
run: node infra/ci/prepare-release.mjs backend
env:
Expand Down Expand Up @@ -314,8 +323,54 @@ jobs:
LZC_DEPLOYMENT_ARTIFACT_KEY: ${{ secrets.LZC_DEPLOYMENT_ARTIFACT_KEY }}
LZC_GITHUB_CLIENT_ID: ${{ secrets.LZC_GITHUB_CLIENT_ID }}
LZC_GITHUB_CLIENT_SECRET: ${{ secrets.LZC_GITHUB_CLIENT_SECRET }}
- name: Stage isolated runner template in its own organisation
- name: CF login and target
if: steps.lifecycle.outputs.parallel == 'true'
run: |
cf version
cf api "$CF_API_URL"
cf auth
cf target -o lzc-dev -s configurator
test "$(cf space configurator --guid)" = "$LZC_CF_SPACE_ID"
- name: Stage runner, migration and application in parallel
if: steps.lifecycle.outputs.parallel == 'true'
id: stage_runner
run: |
rm -f "$LZC_PRIVATE/app-stage.json"
node infra/ci/run-parallel.mjs runner 'bash infra/ci/stage-release-runner.sh' migration 'bash infra/ci/stage-release-migration.sh' application 'bash infra/ci/stage-release-application.sh'
- name: Runner staging failure diagnostics
if: failure() && steps.stage_runner.outcome == 'failure'
run: |
export CF_HOME="${LZC_PRIVATE:-/tmp}/cf-runner"
app_id="$(cf app lzc-plan-template --guid)"
build="$(cf curl "/v3/builds?app_guids=$app_id&order_by=-created_at&per_page=1" | jq -ce '.resources | if length == 1 then .[0] else error("Expected one latest build") end')"
printf '%s\n' "$build" | jq '{guid, state, error, created_at, updated_at, staging_disk_in_mb, staging_memory_in_mb}'
droplet_id="$(printf '%s\n' "$build" | jq -r '.droplet.guid // empty')"
if [ -n "$droplet_id" ]; then
cf curl "/v3/droplets/$droplet_id" | jq '{guid, state, error, created_at, updated_at}'
else
echo "Latest build has no associated droplet; no droplet diagnostics available." >&2
fi
cf logs lzc-plan-template --recent
- name: Sanitized application staging failure diagnostics
if: failure() && steps.stage_runner.outcome == 'failure'
run: |
app_id="$(cf app landing-zone-configurator --guid)"
build="$(cf curl "/v3/builds?app_guids=$app_id&order_by=-created_at&per_page=1" | jq -ce '.resources | if length == 1 then .[0] else error("Expected one latest build") end')"
printf '%s\n' "$build" | jq '{guid, state, error, created_at, updated_at, staging_disk_in_mb, staging_memory_in_mb}'
droplet_id="$(printf '%s\n' "$build" | jq -r '.droplet.guid // empty')"
if [ -n "$droplet_id" ]; then
cf curl "/v3/droplets/$droplet_id" | jq '{guid, state, error, created_at, updated_at}'
else
echo "Latest build has no associated droplet; no droplet diagnostics available." >&2
fi
cf logs landing-zone-configurator --recent | awk '/\[STG\/[0-9]+\]/ { print }'
- name: Activate application after migration and runner validation
if: steps.lifecycle.outputs.parallel == 'true'
id: push
run: node infra/ci/activate-release.mjs
- name: Legacy source — Stage isolated runner template in its own organisation
if: steps.lifecycle.outputs.parallel != 'true'
id: legacy_stage_runner
run: |
cf api "$CF_API_URL"
CF_USERNAME="$LZC_RUNNER_CF_USERNAME" CF_PASSWORD="$LZC_RUNNER_CF_PASSWORD" cf auth
Expand All @@ -329,8 +384,8 @@ jobs:
cf run-task lzc-plan-template --command "./runtime/bin/node --input-type=module -e 'import { platformFirewallImage } from \"@lzc/contracts\"; console.log(platformFirewallImage.sha256 + \" accelerator/firewall-image.qcow2\");' | sha256sum --check" --name "image-$GITHUB_RUN_ID" -m 128M -k 4096M --wait
cf run-task lzc-plan-template --command 'TF_CLI_CONFIG_FILE=/home/vcap/app/runner.tfrc ./tools/tofu -chdir=accelerator init -backend=false -input=false -lockfile=readonly -no-color && ./tools/tofu -chdir=accelerator validate -no-color' --name "engine-$GITHUB_RUN_ID" -m 1024M -k 4096M --wait
cf run-task lzc-plan-template --command 'TF_CLI_CONFIG_FILE=/home/vcap/app/runner.tfrc ./tools/tofu -chdir=application-src/application init -backend=false -input=false -lockfile=readonly -no-color && ./tools/tofu -chdir=application-src/application validate -no-color' --name "application-engine-$GITHUB_RUN_ID" -m 1024M -k 4096M --wait
- name: Runner staging failure diagnostics
if: failure() && steps.stage_runner.outcome == 'failure'
- name: Legacy source — Runner staging failure diagnostics
if: steps.lifecycle.outputs.parallel != 'true' && failure() && steps.legacy_stage_runner.outcome == 'failure'
run: |
app_id="$(cf app lzc-plan-template --guid)"
build="$(cf curl "/v3/builds?app_guids=$app_id&order_by=-created_at&per_page=1" | jq -ce '.resources | if length == 1 then .[0] else error("Expected one latest build") end')"
Expand All @@ -342,22 +397,25 @@ jobs:
echo "Latest build has no associated droplet; no droplet diagnostics available." >&2
fi
cf logs lzc-plan-template --recent
- name: CF login and target
- name: Legacy source — CF login and target
if: steps.lifecycle.outputs.parallel != 'true'
run: |
cf version
cf api "$CF_API_URL"
cf auth
cf target -o lzc-dev -s configurator
test "$(cf space configurator --guid)" = "$LZC_CF_SPACE_ID"
- name: Stage isolated database migration task
id: migration
- name: Legacy source — Stage isolated database migration task
if: steps.lifecycle.outputs.parallel != 'true'
id: legacy_migration
run: cf push --task -f deploy/cloud-foundry/migration-manifest.yml -p .local/release --vars-file "$LZC_PRIVATE/migration-vars.json" --redact-env
- name: Apply versioned database migrations
id: database_migrations
- name: Legacy source — Apply versioned database migrations
if: steps.lifecycle.outputs.parallel != 'true'
id: legacy_database_migrations
timeout-minutes: 5
run: cf run-task landing-zone-configurator-migrate --command "./runtime/bin/node apps/api/dist/migrate.js $GITHUB_RUN_ID" --name "migrate-$GITHUB_RUN_ID" -m 128M -k 4096M --wait
- name: Sanitized database migration diagnostics
if: always() && (steps.database_migrations.outcome == 'success' || steps.database_migrations.outcome == 'failure')
- name: Legacy source — Sanitized database migration diagnostics
if: steps.lifecycle.outputs.parallel != 'true' && always() && (steps.legacy_database_migrations.outcome == 'success' || steps.legacy_database_migrations.outcome == 'failure')
run: |
for attempt in 1 2 3; do
if cf logs landing-zone-configurator-migrate --recent | node infra/ci/migration-evidence.mjs "$GITHUB_RUN_ID" > "$LZC_PRIVATE/migration-evidence.json"; then
Expand All @@ -371,13 +429,15 @@ jobs:
if [ "$attempt" -lt 3 ]; then sleep 5; fi
done
exit 1
- name: Remove privileged migration application
- name: Legacy source — Remove privileged migration application
if: steps.lifecycle.outputs.parallel != 'true'
run: cf delete landing-zone-configurator-migrate -f
- name: CF push application
id: push
- name: Legacy source — CF push application
if: steps.lifecycle.outputs.parallel != 'true'
id: legacy_push
run: cf push -f deploy/cloud-foundry/manifest.yml -p .local/release --vars-file "$LZC_PRIVATE/app-vars.json" --redact-env
- name: Sanitized application staging failure diagnostics
if: failure() && steps.push.outcome == 'failure'
- name: Legacy source — Sanitized application staging failure diagnostics
if: steps.lifecycle.outputs.parallel != 'true' && failure() && steps.legacy_push.outcome == 'failure'
run: |
app_id="$(cf app landing-zone-configurator --guid)"
build="$(cf curl "/v3/builds?app_guids=$app_id&order_by=-created_at&per_page=1" | jq -ce '.resources | if length == 1 then .[0] else error("Expected one latest build") end')"
Expand Down Expand Up @@ -407,10 +467,10 @@ jobs:
exit 1
# Router logs may contain OAuth callback codes. Do not export raw logs to CI.
- name: CF application status
if: always() && steps.push.outcome == 'success'
if: always() && (steps.push.outcome == 'success' || steps.legacy_push.outcome == 'success')
run: cf app landing-zone-configurator
- name: Public route checks
if: always() && steps.push.outcome == 'success'
if: always() && (steps.push.outcome == 'success' || steps.legacy_push.outcome == 'success')
env:
LZC_URL: https://lzc-dev-configurator.apps.01.cf.eu01.stackit.cloud
LZC_AUTH_ENABLED: ${{ vars.LZC_AUTH_ENABLED }}
Expand Down Expand Up @@ -441,8 +501,10 @@ jobs:
cf unmap-route landing-zone-configurator apps.01.cf.eu01.stackit.cloud --hostname lzc-dev-configurator-7dbff805
fi
- name: Clean up migration application after failure or cancellation
if: (failure() || cancelled()) && (steps.migration.outcome == 'success' || steps.migration.outcome == 'failure' || steps.migration.outcome == 'cancelled')
run: cf delete landing-zone-configurator-migrate -f
if: failure() || cancelled()
run: |
if [ "${{ steps.lifecycle.outputs.parallel }}" = true ]; then export CF_HOME="$LZC_PRIVATE/cf-migration"; fi
cf delete landing-zone-configurator-migrate -f
- name: Remove private deployment inputs
if: always()
run: rm -rf -- "$RUNNER_TEMP/lzc-release"
2 changes: 2 additions & 0 deletions .github/workflows/validate-configurator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ jobs:
run: npm run check
- name: Check release configuration with the built API validators
run: node --test ../infra/ci/prepare-release.test.mjs
- name: Check parallel release lifecycle and slim migration payload
run: node --test ../infra/ci/release-lifecycle.test.mjs
- name: Check release configuration under the explicit Dev exception
run: node --test ../infra/ci/prepare-release.test.mjs
env:
Expand Down
7 changes: 7 additions & 0 deletions docs/workflows.de.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,13 @@ Pflichtprüfung, ohne sicherzustellen, dass er für jeden relevanten PR ein Erge
| Configurator Runtime | `root=runtime`; standardmäßig `apply=false`. Benötigt den obigen Laufzeit-Operatorschalter; Apply verwendet `lzc-dev-runtime-apply`. |
| Configurator Release | Standardmäßig `release_mode=qualified` und `build_only=false`; leere `release_run_id` bedeutet einen neuen Build im gewählten Modus. Setzen Sie `build_only=true` für einen Build ohne CF-Bereitstellung; die Veröffentlichung des geschützten Tags erfolgt trotzdem; echte Modellaufrufe laufen im Modus `qualified`. Um einen bestehenden qualifizierten Build zu übernehmen, lassen Sie Build-only deaktiviert und geben Sie dessen numerische Run-ID an; das aufbewahrte Artefakt/Tag muss weiterhin gültig sein. |

Browser-Abnahme und native Paketierung laufen im Release-Build parallel; die
Bereitstellung bereitet Runner, minimales Migrationspaket und Web/API mit getrennten
CF-Homes parallel vor. Die App-Aktivierung wartet auf alle Prüfungen und die
vollständige Runner-Bindung. Der Release bleibt eine unveränderliche Quelle;
Komponenten-Wiederverwendung folgt später in
[Issue #167](https://github.com/stackitcloud/stackit-landing-zone/issues/167).

Für die normale Configurator-Bereitstellung: Wählen Sie **Configurator Release**,
**runner_pool_size=1** für einen vorbereiteten Einmal-Runner in Dev
(`0` deaktiviert/baut den Pool ab; Standard `0`, Maximum `4`, 1 GiB RAM je Reserve),
Expand Down
6 changes: 6 additions & 0 deletions docs/workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ it reports a result for every relevant PR.
| Configurator Runtime | `root=runtime`; `apply=false` by default. Requires the runtime operator switch above; Apply uses `lzc-dev-runtime-apply`. |
| Configurator Release | `release_mode=qualified` and `build_only=false` by default; `release_run_id` empty means a fresh build under the selected mode. Set `build_only=true` to build without CF deployment; protected tag publication still occurs; live-model calls run in `qualified` mode. To promote an existing qualified build, leave build-only unchecked and enter its numeric run ID; the retained artifact/tag must remain valid. |

Release build browser acceptance and native packaging run concurrently; deployment
stages Runner, the minimal migration payload and Web/API concurrently with isolated
CF homes. App activation waits for all checks and the completed Runner binding.
The release remains one immutable source; component reuse is deferred to
[issue #167](https://github.com/stackitcloud/stackit-landing-zone/issues/167).

For the normal Configurator deployment: select **Configurator Release**, `main`,
choose **runner_pool_size=1** to maintain one prepared one-shot Dev Runner
(`0` disables/drains the pool; default `0`, maximum `4`, 1 GiB RAM per reserve),
Expand Down
40 changes: 34 additions & 6 deletions landing-zone-configurator/deploy/cloud-foundry/README.de.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,40 @@ Release in `landing-zone-configurator-migrate`, einer separaten Task-only-App oh
Route. Nur diese kurzlebige App erhält die Datenbank-Owner-Zugangsdaten; sie wird
nach der Migration und bei Fehlern gelöscht. Migrationsprüfsummen und ein Advisory Lock
sichern wiederholte Ausführung ab. Die Web-App verwendet ihre eingeschränkte Runtime-Rolle und RLS.
Migrations-App und ihr Task benötigen beide 4096 MB Festplattenquota, weil sie
den vollständigen unveränderlichen Web-Release einschließlich der gepinnten Inspektionsruntime entpacken.
Das frühere Limit von 512 MB scheiterte beim Extrahieren des Containers, bevor Node oder irgendein
Migrationscode startete. Der Task behält sein Speicherlimit von 128 MB.
Der exakte qualifizierte Release enthält allein 505.65 MiB Nutzlast regulärer Dateien;
Dateisystemblöcke, Verzeichnisse und Container verursachen zusätzlichen Overhead.
Das Migrationspaket wird auf dem Deployment-Runner aus dem per Prüfsumme verifizierten
unveränderlichen Release abgeleitet. Es enthält nur die gepinnte Node-Binärdatei samt
Lizenz, den Migrationseinstieg, Datenbankkonfiguration/Migrations-/Fehlerhilfen, das
vollständige SQL-Verzeichnis und die installierten `pg`-Abhängigkeiten. Es findet keine
neue Abhängigkeitsauflösung statt. Web-Assets, weitere API-Abhängigkeiten, Provider und
Inspektions-Images sind ausgeschlossen. Der Task behält seine Limits von 128 MB
Speicher und 4096 MB Festplatte; Letzteres ist eine Obergrenze und kein Grund, das
vollständige Web-/Inspektionspaket hochzuladen.

Browser-Abnahme und native Vertragsprüfungen/Runner-Paketierung laufen nach dem
normalen Anwendungs-Build parallel. Beide müssen erfolgreich sein, bevor das
Release-Archiv zusammengestellt wird. Bei der Bereitstellung laufen Runner-Staging
und -Validierung, der kleine Migrationstask und Web/API-Staging parallel; Runner- und
Migrationsidentität verwenden getrennte CF-Homes. Der Anwendungszweig verwendet eine
private Kopie mit deaktivierter Ausführung und ohne Runner-Bindung. `cf push --no-start`
lädt hoch und wendet das Manifest an; `cf stage-package` erstellt aus dem exakt
bestimmten bereiten Paket ein Droplet, ohne es zuzuweisen oder einen neuen
Anwendungsprozess zu starten. Bestehende Prozesse behalten ihre bisherige Umgebung.
Erst nach Erfolg aller drei Zweige wendet die Aktivierung die vollständige Runner-Bindung
an, prüft die protokollierte Paket-/Build-/Droplet-Zuordnung, stoppt die alte App,
weist das neue Droplet zu und startet es. Es bleibt eine Bereitstellung mit einer
Instanz und kurzer Aktivierungsunterbrechung, kein Rolling Release. Ein fehlgeschlagener
Zweig beendet die übrigen und verhindert die Aktivierung; die privilegierte Migrations-App
wird bei Erfolg und Fehler gelöscht, ergänzt durch Workflow-Bereinigung bei Abbruch.
Die Laufzeiten der Zweige werden protokolliert.

Ältere qualifizierte Artefakte, deren exakte Quelle die neuen Skripte noch nicht enthält,
behalten bei einer Promotion den bisherigen sequenziellen Ablauf. Das Artefakt wird
weder neu gebaut noch stillschweigend mit einem anderen Quellbaum ausgeführt.

Alle Komponenten gehören weiterhin zu einem quellgebundenen Release. Ein älterer
Runner wird nicht wiederverwendet; Promotion, Qualifikation, Prüfsummen und Freigaben
bleiben erhalten. Getrennte Komponenten-Releases verfolgt
[Issue #167](https://github.com/stackitcloud/stackit-landing-zone/issues/167).
Die nachfolgenden Tasks für Netzwerk, Dienstverbindung und Runner-Isolation
entpacken dieses Droplet ebenfalls und verwenden dieselbe Festplattenquota von 4096 MB.
Diagnosen des Migrationstasks melden nur Release-Run-ID, Verbindungs-/Migrations-
Expand Down
Loading
Loading