cloudfall 0.7.0rc5__tar.gz → 0.8.0rc2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/CHANGELOG.md +35 -1
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/PKG-INFO +2 -1
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/README.md +1 -0
- cloudfall-0.8.0rc2/changelog.d/89-ci-deploy-guide.md +3 -0
- cloudfall-0.8.0rc2/changelog.d/96-healthcheck-none.md +3 -0
- cloudfall-0.8.0rc2/changelog.d/97-stale-proposals.md +3 -0
- cloudfall-0.8.0rc2/docs/ci-deploy-guide.md +385 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/getting-started.md +3 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/specs/001-stale-proposals.md +13 -1
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_deploy/defaults/main.yml +3 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_deploy/meta/argument_specs.yml +3 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_deploy/tasks/main.yml +80 -1
- cloudfall-0.8.0rc2/engine/tests/test_deploy_role.py +203 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/pyproject.toml +1 -1
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/app.py +70 -10
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/decision.py +108 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/why.py +43 -9
- cloudfall-0.8.0rc2/sdk/tests/test_stale.py +384 -0
- cloudfall-0.7.0rc5/changelog.d/76-changed-from-play-stats.md +0 -10
- cloudfall-0.7.0rc5/changelog.d/78-why-agent-turn.md +0 -13
- cloudfall-0.7.0rc5/changelog.d/81-database-claims.md +0 -8
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/.ansible-lint +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/.gitignore +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/ARCHITECTURE.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/CODE_OF_CONDUCT.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/CONTRIBUTING.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/LICENSE +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/MANIFESTO.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/ROADMAP.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/SECURITY.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/Taskfile.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/changelog.d/README.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/README.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/examples/alert-rules/postgresql-down.yaml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/examples/applications/crm.yaml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/examples/components/crm-backend.yaml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/examples/domains/crm-site.yaml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/examples/logging-stacks/operations.yaml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/examples/operator-policies/production.yaml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/examples/server-types/debian-application.yaml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/examples/servers/h1.yaml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/examples/servers/h2.yaml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/examples/services/postgresql-main.yaml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/examples/services/redis-cache.yaml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/examples/ssh-public-keys/example-admin.yaml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/agent-investigation.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/alert-rule.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/application.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/artifact.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/backup-receipt.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/common.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/component.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/deployment-receipt.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/domain.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/environment-receipt.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/logging-stack.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/observed-domain.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/observed-server.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/operation-decision.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/operation.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/operator-policy.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/operator-proposal.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/release-receipt.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/server-type.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/server.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/service.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/schemas/v1/ssh-public-key.schema.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/tests/invalid/missing-server/applications/crm.yaml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/tests/invalid/missing-server/components/crm-backend.yaml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/tests/invalid/missing-server/server-types/debian-application.yaml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/tests/invalid/missing-server/servers/h1.yaml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/tests/invalid/schema/components/component-unknown-field.yaml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/tests/observed/compliant/h1.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/config/tests/observed/compliant/h2.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/README.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/.gitignore +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/ansible.cfg +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/inventories/production/hosts.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/operations/converge.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/operations/disk-report.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/operations/journal-vacuum.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/operations/postgresql-reinit.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/operations/postgresql-restart.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/operations/service-logs.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/operations/shop-logrotate.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/playbooks/disk-report.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/playbooks/journal-vacuum.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/playbooks/postgresql-reinit.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/playbooks/postgresql-restart.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/playbooks/postgresql.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/playbooks/service-logs.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/playbooks/shop-logrotate.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/playbooks/shop.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/disk-report-20261008170744.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/disk-report-20261008170744.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/journal-vacuum-20261008171016.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/journal-vacuum-20261008171016.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/postgresql-reinit-20261008171010.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/postgresql-reinit-20261008171010.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/postgresql-restart-20261008170624.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/postgresql-restart-20261008170624.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/service-logs-20261008170612.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/service-logs-20261008170612.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/service-logs-20261008170740.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/service-logs-20261008170740.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/service-logs-20261008170742.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/service-logs-20261008170742.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/service-logs-20261008170838.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/service-logs-20261008170838.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/service-logs-20261008170920.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/service-logs-20261008170920.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/shop-logrotate-20261008170752.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full/shop-logrotate-20261008170752.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full-approved/disk-report-20261008204348.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full-approved/disk-report-20261008204348.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full-approved/disk-report-20261008204421.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full-approved/disk-report-20261008204421.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full-approved/postgresql-restart-20261008204358.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full-approved/postgresql-restart-20261008204358.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full-approved/shop-logrotate-20261008204351.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/recordings/disk-full-approved/shop-logrotate-20261008204351.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/scenario/break.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/incident/site.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/pyproject.toml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/src/cloudfall_demo/__init__.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/src/cloudfall_demo/server.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/src/cloudfall_demo/static/evaluation.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/src/cloudfall_demo/static/index.html +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/tests/test_demo.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/demo/uv.lock +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/ansible-context.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/assets/logo.svg +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/availability-design.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/brownfield-design.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/decisions/2026-09-14-no-ansible-replacement.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/decisions/2026-09-14-no-nixos-base.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/decisions/2026-09-14-no-rust-rewrite.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/decisions.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/fleet-goals.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/hybrid-storage-design.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/images/demo.png +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/landscape.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/logging-service-guide.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/neon-migration-guide.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/new-server-storage-guide.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/operator-guide.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/proving-runs/2026-09-08-debian-container-m1.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/proving-runs/2026-09-08-dns-cutover.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/proving-runs/2026-09-08-guided-data-migration.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/proving-runs/2026-09-08-hetzner-m1.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/proving-runs/2026-09-08-hetzner-m2-m3.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/proving-runs/2026-09-08-hetzner-m4-m5.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/proving-runs/2026-09-08-render-cutover.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/proving-runs/2026-09-10-hetzner-m7.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/proving-runs/2026-09-10-hetzner-m8.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/proving-runs/2026-09-10-hetzner-m9.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/proving-runs/2026-09-11-hetzner-m10.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/proving-runs/2026-10-08-disk-full-agent.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/render-migration-guide.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/docs/secrets-guide.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/README.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/ansible.cfg +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/playbooks/backup.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/playbooks/baseline.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/playbooks/bootstrap.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/playbooks/data.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/playbooks/deploy.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/playbooks/domains.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/playbooks/health.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/playbooks/inspect.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/playbooks/logging.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/playbooks/restart.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/playbooks/rollback.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/playbooks/services.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/playbooks/time.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_access/defaults/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_access/handlers/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_access/meta/argument_specs.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_access/meta/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_access/tasks/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_bootstrap/defaults/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_bootstrap/meta/argument_specs.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_bootstrap/meta/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_bootstrap/tasks/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_data_migration/defaults/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_data_migration/meta/argument_specs.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_data_migration/meta/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_data_migration/tasks/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_deploy/meta/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_deploy/tasks/health.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_deploy/tasks/health_gate.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_deploy/tasks/restart.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_deploy/tasks/rollback.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_deploy/templates/component.service.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_firewall/defaults/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_firewall/handlers/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_firewall/meta/argument_specs.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_firewall/meta/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_firewall/tasks/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_firewall/templates/nftables.conf.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_inspect/defaults/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_inspect/meta/argument_specs.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_inspect/meta/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_inspect/tasks/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_backend/defaults/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_backend/handlers/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_backend/meta/argument_specs.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_backend/meta/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_backend/tasks/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/alertmanager-defaults.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/alertmanager.yaml.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/grafana.ini.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/loki-datasource.yml.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/loki-systemd-override.conf.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/loki.yaml.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/nginx-loki-gateway.conf.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/prometheus-datasource.yml.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/prometheus-defaults.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/prometheus-rules.yaml.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/prometheus.yaml.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_collector/defaults/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_collector/handlers/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_collector/meta/argument_specs.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_collector/meta/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_collector/tasks/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_collector/templates/alloy-systemd-override.conf.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_logging_collector/templates/config.alloy.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_nginx_site/defaults/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_nginx_site/handlers/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_nginx_site/meta/argument_specs.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_nginx_site/meta/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_nginx_site/tasks/apply_virtual_host.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_nginx_site/tasks/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_nginx_site/templates/domain-site.conf.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_postgresql/defaults/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_postgresql/handlers/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_postgresql/meta/argument_specs.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_postgresql/meta/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_postgresql/tasks/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_postgresql/templates/backup.service.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_postgresql/templates/backup.sh.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_postgresql/templates/backup.timer.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_postgresql/templates/cloudfall.conf.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_postgresql/templates/restore-check.service.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_postgresql/templates/restore-check.sh.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_postgresql/templates/restore-check.timer.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_redis/defaults/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_redis/handlers/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_redis/meta/argument_specs.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_redis/meta/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_redis/tasks/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_redis/templates/backup.service.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_redis/templates/backup.sh.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_redis/templates/backup.timer.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_redis/templates/cloudfall.conf.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_redis/templates/restore-check.service.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_redis/templates/restore-check.sh.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_redis/templates/restore-check.timer.j2 +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_time/defaults/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_time/meta/argument_specs.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_time/meta/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_time/tasks/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_unattended_upgrades/defaults/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_unattended_upgrades/meta/argument_specs.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_unattended_upgrades/meta/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_unattended_upgrades/tasks/main.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/src/cloudfall_engine/__init__.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/src/cloudfall_engine/__main__.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/src/cloudfall_engine/ansible_inventory.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/src/cloudfall_engine/artifact.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/src/cloudfall_engine/cli.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/src/cloudfall_engine/playbook.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/src/cloudfall_engine/py.typed +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/tests/test_ansible_inventory.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/tests/test_artifact_builder.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/tests/test_deploy_templates.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/tests/test_firewall_templates.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/tests/test_logging_templates.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/tests/test_nginx_site_role.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/tests/test_nginx_site_templates.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/tests/test_playbook.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/tests/test_postgresql_templates.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/tests/test_time_role.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/.gitignore +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/README.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/ansible.cfg +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/inventories/production/hosts.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/operations/converge.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/operations/disk-report.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/operations/journal-vacuum.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/operations/postgresql-reinit.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/operations/postgresql-restart.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/operations/service-logs.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/operations/shop-logrotate.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/playbooks/disk-report.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/playbooks/journal-vacuum.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/playbooks/postgresql-reinit.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/playbooks/postgresql-restart.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/playbooks/postgresql.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/playbooks/service-logs.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/playbooks/shop-logrotate.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/playbooks/shop.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/disk-report-20261008170744.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/disk-report-20261008170744.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/journal-vacuum-20261008171016.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/journal-vacuum-20261008171016.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/postgresql-reinit-20261008171010.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/postgresql-reinit-20261008171010.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/postgresql-restart-20261008170624.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/postgresql-restart-20261008170624.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/service-logs-20261008170612.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/service-logs-20261008170612.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/service-logs-20261008170740.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/service-logs-20261008170740.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/service-logs-20261008170742.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/service-logs-20261008170742.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/service-logs-20261008170838.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/service-logs-20261008170838.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/service-logs-20261008170920.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/service-logs-20261008170920.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/shop-logrotate-20261008170752.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full/shop-logrotate-20261008170752.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full-approved/disk-report-20261008204348.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full-approved/disk-report-20261008204348.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full-approved/disk-report-20261008204421.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full-approved/disk-report-20261008204421.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full-approved/postgresql-restart-20261008204358.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full-approved/postgresql-restart-20261008204358.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full-approved/shop-logrotate-20261008204351.diff +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/recordings/disk-full-approved/shop-logrotate-20261008204351.json +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/scenario/break.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/disk-full-incident/site.yml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/render-demo/README.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/render-demo/pyproject.toml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/render-demo/render.yaml +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/render-demo/src/demo/__init__.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/render-demo/src/demo/asgi.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/examples/render-demo/uv.lock +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/README.md +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/__init__.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/__main__.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/agent_server.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/agent_tools.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/ansible_api.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/ansible_reader.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/audit.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/authoring.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/callback_plugins/__init__.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/callback_plugins/cloudfall_host_stats.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/catalog.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/cli.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/commands.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/cutover.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/dashboard.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/dashboard_server.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/domain.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/importer.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/inventory.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/investigation.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/lifecycle.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/migrate.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/observation.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/observe.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/operations.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/operator.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/project.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/py.typed +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/recording.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/render_api.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/resources.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/secrets.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/service_evidence.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/src/cloudfall/validation.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_agent_server.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_ansible_reader.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_app.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_arguments.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_audit.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_authoring.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_catalog.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_commands.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_cutover.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_dashboard_server.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_data_migration.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_decision.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_host_stats.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_importer.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_inventory.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_investigation.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_lifecycle.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_migrate.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_observe.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_operations_dashboard.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_operator.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_project.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_render_api.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_resources.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_secrets.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_service_evidence.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_validation.py +0 -0
- {cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/sdk/tests/test_why.py +0 -0
|
@@ -6,6 +6,39 @@ Notable changes to Cloudfall. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.7.0] - 2026-10-19
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- A decision records a host `changed` when any task of the play changed it,
|
|
14
|
+
not only the last one. The check preview, the execution and the verify run
|
|
15
|
+
read each host's counts from the play recap through a callback the SDK
|
|
16
|
+
ships (`cloudfall_host_stats`), in place of Ansible's `tree` callback,
|
|
17
|
+
which keeps only the last task's result: a run whose recap said
|
|
18
|
+
`changed=2` was recorded `changed: []`, and a verify run that changed a
|
|
19
|
+
host was recorded `verified`. The callback is added ahead of the callback
|
|
20
|
+
directories the team's `ansible.cfg` names, not in place of them (#76)
|
|
21
|
+
- The Neon guide says each database belongs to an application Cloudfall
|
|
22
|
+
deploys on the same server, and no longer suggests an app on another host
|
|
23
|
+
can reach it; the brownfield design's operation example uses a target
|
|
24
|
+
scope the schema accepts (`host`); the site no longer offers a PostgreSQL
|
|
25
|
+
on its own, and its navigation and footer links are easier to tap on a
|
|
26
|
+
phone (#81)
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- `cloudfall why` tells which agent turn asked for a decision. Each step of
|
|
31
|
+
an investigation record now keeps the `turn` that made its call (optional
|
|
32
|
+
in `agent-investigation.schema.json`; older records have none). `why`
|
|
33
|
+
reads the investigation records (`--investigations`, default
|
|
34
|
+
`investigations`, served to MCP clients from the same place) and adds to a
|
|
35
|
+
decision's story the model, the endpoint, the alert, the turn, and that
|
|
36
|
+
turn's reasoning with the endpoint's response and request ids; the answer
|
|
37
|
+
carries the same as `agent`. A decision a later call only repeated keeps
|
|
38
|
+
the turn that asked first. An investigation record that is not valid JSON
|
|
39
|
+
or does not match its schema stops `why` with `RECORD_INVALID`
|
|
40
|
+
(`why_investigation_invalid`) (#78)
|
|
41
|
+
|
|
9
42
|
## [0.6.0] - 2026-10-10
|
|
10
43
|
|
|
11
44
|
### Added
|
|
@@ -828,7 +861,8 @@ on disposable Hetzner Cloud Debian 13 servers (see
|
|
|
828
861
|
exposed freely, every server-changing tool gated behind a two-step
|
|
829
862
|
confirmation handshake
|
|
830
863
|
|
|
831
|
-
[Unreleased]: https://github.com/cloudfall-dev/cloudfall/compare/v0.
|
|
864
|
+
[Unreleased]: https://github.com/cloudfall-dev/cloudfall/compare/v0.7.0...HEAD
|
|
865
|
+
[0.7.0]: https://github.com/cloudfall-dev/cloudfall/compare/v0.6.0...v0.7.0
|
|
832
866
|
[0.6.0]: https://github.com/cloudfall-dev/cloudfall/compare/v0.5.1...v0.6.0
|
|
833
867
|
[0.5.1]: https://github.com/cloudfall-dev/cloudfall/compare/v0.5.0...v0.5.1
|
|
834
868
|
[0.5.0]: https://github.com/cloudfall-dev/cloudfall/compare/v0.4.0...v0.5.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: cloudfall
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0rc2
|
|
4
4
|
Summary: The operator's record for self-hosted servers run by an AI agent: Ansible executes, Cloudfall gates, verifies and keeps the receipts
|
|
5
5
|
Project-URL: Homepage, https://cloudfall.dev
|
|
6
6
|
Project-URL: Repository, https://github.com/cloudfall-dev/cloudfall
|
|
@@ -266,6 +266,7 @@ per-table row counts and writes a receipt. See the
|
|
|
266
266
|
| [Operator](docs/operator-guide.md) | The always-on operator, proposals, approvals, autonomy policy |
|
|
267
267
|
| [Disk-full incident](examples/disk-full-incident) | `agent investigate` with Nemotron on a real broken host, live or replayed |
|
|
268
268
|
| [Secrets](docs/secrets-guide.md) | sops/age secrets rendered into per-component env files |
|
|
269
|
+
| [CI deploy](docs/ci-deploy-guide.md) | Deploying from GitHub Actions with per-environment keys and required reviewers |
|
|
269
270
|
| [Logging](docs/logging-service-guide.md) | Loki, Grafana and Alloy behind an mTLS gateway |
|
|
270
271
|
| [Storage](docs/new-server-storage-guide.md) | Destructive, new-server-only RAID provisioning ([design](docs/hybrid-storage-design.md)) |
|
|
271
272
|
| [Architecture](ARCHITECTURE.md) | Module boundaries and the long-term fleet vision |
|
|
@@ -238,6 +238,7 @@ per-table row counts and writes a receipt. See the
|
|
|
238
238
|
| [Operator](docs/operator-guide.md) | The always-on operator, proposals, approvals, autonomy policy |
|
|
239
239
|
| [Disk-full incident](examples/disk-full-incident) | `agent investigate` with Nemotron on a real broken host, live or replayed |
|
|
240
240
|
| [Secrets](docs/secrets-guide.md) | sops/age secrets rendered into per-component env files |
|
|
241
|
+
| [CI deploy](docs/ci-deploy-guide.md) | Deploying from GitHub Actions with per-environment keys and required reviewers |
|
|
241
242
|
| [Logging](docs/logging-service-guide.md) | Loki, Grafana and Alloy behind an mTLS gateway |
|
|
242
243
|
| [Storage](docs/new-server-storage-guide.md) | Destructive, new-server-only RAID provisioning ([design](docs/hybrid-storage-design.md)) |
|
|
243
244
|
| [Architecture](ARCHITECTURE.md) | Module boundaries and the long-term fleet vision |
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
### Added
|
|
2
|
+
|
|
3
|
+
- A CI deploy guide (`docs/ci-deploy-guide.md`): one GitHub Actions workflow that renders secrets, builds the artifact with `cloudfall-engine artifact build`, deploys with `cloudfall deploy --yes` and uploads the receipts, with one SSH key and one age key per environment and required reviewers standing in for `approval: manual`, which `deploy` does not enforce; not yet run against a real server (#89)
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
### Fixed
|
|
2
|
+
|
|
3
|
+
- A deploy of a component with `healthCheck: {type: none}` no longer fails its health gate on the missing HTTP fields and stops the release: the gate now requires the systemd unit to be active right after the restart and still active, with no automatic restart in between, 10 seconds later, so a worker that crashes or crash-loops on start still fails and rolls back (#96)
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
### Added
|
|
2
|
+
|
|
3
|
+
- An open proposal whose basis hosts have been observed again since is now reported as stale: `decisions list`, `decisions show` and `cloudfall why` add `stale` and `staleHosts` to each `proposed` decision, `--stale` on `decisions list` and `why` keeps only those, and `decisions approve` names each stale host and its newer observation time before asking for the id. Worked out on read from the observation files; the record and its schema do not change (#26)
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# CI deploy guide
|
|
2
|
+
|
|
3
|
+
Every other guide runs Cloudfall from an operator's machine: your age key in
|
|
4
|
+
`~/.config/cloudfall/age.key`, your SSH agent, your shell. This guide moves
|
|
5
|
+
the same deploy into a GitHub Actions workflow, so a merge to `main` updates
|
|
6
|
+
staging and a production deploy waits for a person's approval.
|
|
7
|
+
|
|
8
|
+
The record does not change. The runner renders the secrets, builds the
|
|
9
|
+
artifact and deploys it with the same commands you run by hand, and the
|
|
10
|
+
receipts they write are uploaded with the run so they outlive the runner.
|
|
11
|
+
|
|
12
|
+
> **Status:** not proven live. The workflow below is written from the code
|
|
13
|
+
> and checked against the flags of Cloudfall 0.7.0 (`--help` and the
|
|
14
|
+
> source), but it has not had a run against a real server yet. Treat the
|
|
15
|
+
> first run on staging as the proving run, and open an issue for anything
|
|
16
|
+
> that differs.
|
|
17
|
+
|
|
18
|
+
## What the runner needs
|
|
19
|
+
|
|
20
|
+
The runner is a controller like your laptop. It needs:
|
|
21
|
+
|
|
22
|
+
- The project checked out: the repository `cloudfall init` created, with its
|
|
23
|
+
`pyproject.toml` and `uv.lock` committed
|
|
24
|
+
- `uv`, which installs Python 3.14, Cloudfall and `ansible-core` from the
|
|
25
|
+
project's lock file
|
|
26
|
+
- `ansible-playbook` 2.21.0 or later: it comes with Cloudfall
|
|
27
|
+
(`ansible-core>=2.21,<2.22`), so `uv run` finds it in the project's
|
|
28
|
+
virtualenv
|
|
29
|
+
- `git` 2.24.0 or later, which `artifact build` uses to clone the
|
|
30
|
+
component's repository; GitHub's Ubuntu runners ship a newer one
|
|
31
|
+
- `sops` 3.8.0 or later, which `secrets render` runs to decrypt fragments;
|
|
32
|
+
`age` itself is not needed, because sops decrypts age files on its own
|
|
33
|
+
- An SSH key the servers accept, and their host keys in `known_hosts`
|
|
34
|
+
- The age key that decrypts this environment's secrets
|
|
35
|
+
|
|
36
|
+
These are the versions the commands check before they run (their
|
|
37
|
+
`required_tools`); a missing or older tool stops the command with an error
|
|
38
|
+
naming it. `uv run cloudfall doctor` checks all of them at once, so the
|
|
39
|
+
workflow runs it first.
|
|
40
|
+
|
|
41
|
+
## One key per environment
|
|
42
|
+
|
|
43
|
+
A runner should hold the keys of one environment and nothing else. Cloudfall
|
|
44
|
+
already scopes two of them.
|
|
45
|
+
|
|
46
|
+
**SSH keys.** Each `SshPublicKey` names an environment, and the baseline
|
|
47
|
+
installs a server's active keys only when their environment matches the
|
|
48
|
+
server's. Make a key for the staging runner and declare it for staging:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
ssh-keygen -t ed25519 -N "" -C "ci staging" -f ci-staging
|
|
52
|
+
uv run cloudfall add ssh-key ci-staging.pub --owner ci --id ci-staging --environment staging
|
|
53
|
+
uv run cloudfall-engine inventory render --output tmp/ansible-inventory.json
|
|
54
|
+
uv run cloudfall-engine playbook run baseline --inventory tmp/ansible-inventory.json
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The baseline writes the key into root's `authorized_keys` on staging servers
|
|
58
|
+
only, so the staging runner cannot log in to production. A server whose
|
|
59
|
+
`ssh.user` is not `root` needs the key installed for that user by hand.
|
|
60
|
+
|
|
61
|
+
**Age keys.** This one needs a change to your project. `cloudfall init`
|
|
62
|
+
writes a `.sops.yaml` with one rule that encrypts every fragment under
|
|
63
|
+
`secrets/` to one key. A runner holding that key can decrypt everything.
|
|
64
|
+
Split the rule by environment directory, and list your own key in each rule
|
|
65
|
+
so you can still edit every file:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
age-keygen -o staging.key
|
|
69
|
+
age-keygen -o production.key
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```yaml
|
|
73
|
+
creation_rules:
|
|
74
|
+
- path_regex: secrets/staging/.*\.env$
|
|
75
|
+
age: age1staging...,age1yourown...
|
|
76
|
+
- path_regex: secrets/production/.*\.env$
|
|
77
|
+
age: age1production...,age1yourown...
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
sops uses the first rule that matches a path. Files encrypted before the
|
|
81
|
+
change keep their old recipients until you re-encrypt them:
|
|
82
|
+
|
|
83
|
+
```sh
|
|
84
|
+
sops updatekeys secrets/staging/crm.env
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Why this stops a staging runner: a secret reference
|
|
88
|
+
`{environment: production, path: /crm}` resolves from
|
|
89
|
+
`secrets/production/crm.env`, and `secrets render` decrypts only the files
|
|
90
|
+
the component's references name. If a component on the staging runner
|
|
91
|
+
references a production fragment, sops has no matching key, and the render
|
|
92
|
+
fails with `secrets_decrypt_failed` before anything is written. Cloudfall
|
|
93
|
+
does not itself check that a component's references name the environment of
|
|
94
|
+
its servers; the key the runner holds is what enforces it.
|
|
95
|
+
|
|
96
|
+
Store `staging.key` and `production.key` in a password manager, then delete
|
|
97
|
+
the local copies once they are in GitHub.
|
|
98
|
+
|
|
99
|
+
## GitHub environments and secrets
|
|
100
|
+
|
|
101
|
+
Create one GitHub environment per Cloudfall environment, with the same name
|
|
102
|
+
(`staging`, `production`), and put three secrets in each:
|
|
103
|
+
|
|
104
|
+
| Secret | Content |
|
|
105
|
+
| --- | --- |
|
|
106
|
+
| `CLOUDFALL_SSH_KEY` | The private key of that environment's runner key, such as `ci-staging` |
|
|
107
|
+
| `CLOUDFALL_KNOWN_HOSTS` | The `known_hosts` lines of that environment's servers |
|
|
108
|
+
| `SOPS_AGE_KEY` | The age key file of that environment, such as `staging.key` |
|
|
109
|
+
|
|
110
|
+
```sh
|
|
111
|
+
gh secret set CLOUDFALL_SSH_KEY --env staging < ci-staging
|
|
112
|
+
gh secret set SOPS_AGE_KEY --env staging < staging.key
|
|
113
|
+
ssh-keygen -F 203.0.113.20 > staging-known-hosts
|
|
114
|
+
gh secret set CLOUDFALL_KNOWN_HOSTS --env staging < staging-known-hosts
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`ssh-keygen -F` reads the host key your own machine already trusts, which is
|
|
118
|
+
safer than scanning it from the runner. For a server on another port, look it
|
|
119
|
+
up as `[203.0.113.20]:2222`.
|
|
120
|
+
|
|
121
|
+
In the repository's settings, under Environments:
|
|
122
|
+
|
|
123
|
+
- `production`: add required reviewers and turn on "Prevent self-review"
|
|
124
|
+
- Both: limit deployment branches to `main`, so a job on another branch never
|
|
125
|
+
sees the secrets
|
|
126
|
+
|
|
127
|
+
## The workflow
|
|
128
|
+
|
|
129
|
+
Save this as `.github/workflows/deploy.yml` in the project repository.
|
|
130
|
+
Replace `crm-backend-staging` with your staging component.
|
|
131
|
+
|
|
132
|
+
```yaml
|
|
133
|
+
name: deploy
|
|
134
|
+
|
|
135
|
+
on:
|
|
136
|
+
push:
|
|
137
|
+
branches: [main]
|
|
138
|
+
workflow_dispatch:
|
|
139
|
+
inputs:
|
|
140
|
+
environment:
|
|
141
|
+
description: Cloudfall environment, and the GitHub environment of the same name
|
|
142
|
+
type: choice
|
|
143
|
+
options: [staging, production]
|
|
144
|
+
default: staging
|
|
145
|
+
component:
|
|
146
|
+
description: Component to deploy
|
|
147
|
+
required: true
|
|
148
|
+
default: crm-backend-staging
|
|
149
|
+
ref:
|
|
150
|
+
description: Git ref of the component's repository to build
|
|
151
|
+
required: true
|
|
152
|
+
default: main
|
|
153
|
+
repository_dispatch:
|
|
154
|
+
types: [deploy]
|
|
155
|
+
|
|
156
|
+
concurrency:
|
|
157
|
+
group: deploy-${{ inputs.environment || github.event.client_payload.environment || 'staging' }}
|
|
158
|
+
cancel-in-progress: false
|
|
159
|
+
|
|
160
|
+
permissions:
|
|
161
|
+
contents: read
|
|
162
|
+
|
|
163
|
+
jobs:
|
|
164
|
+
deploy:
|
|
165
|
+
runs-on: ubuntu-24.04
|
|
166
|
+
environment: ${{ inputs.environment || github.event.client_payload.environment || 'staging' }}
|
|
167
|
+
env:
|
|
168
|
+
COMPONENT: ${{ inputs.component || github.event.client_payload.component || 'crm-backend-staging' }}
|
|
169
|
+
REF: ${{ inputs.ref || github.event.client_payload.ref || 'main' }}
|
|
170
|
+
SOPS_VERSION: v3.13.3
|
|
171
|
+
steps:
|
|
172
|
+
- uses: actions/checkout@v7
|
|
173
|
+
|
|
174
|
+
- uses: astral-sh/setup-uv@v10.1.0
|
|
175
|
+
|
|
176
|
+
- name: Install Cloudfall from the lock file
|
|
177
|
+
run: uv sync --locked
|
|
178
|
+
|
|
179
|
+
- name: Install sops
|
|
180
|
+
run: |
|
|
181
|
+
base="https://github.com/getsops/sops/releases/download/$SOPS_VERSION"
|
|
182
|
+
curl -fsSLO "$base/sops-$SOPS_VERSION.linux.amd64"
|
|
183
|
+
curl -fsSLO "$base/sops-$SOPS_VERSION.checksums.txt"
|
|
184
|
+
sha256sum --check --ignore-missing "sops-$SOPS_VERSION.checksums.txt"
|
|
185
|
+
sudo install -m 0755 "sops-$SOPS_VERSION.linux.amd64" /usr/local/bin/sops
|
|
186
|
+
|
|
187
|
+
- name: Load the SSH key
|
|
188
|
+
env:
|
|
189
|
+
CLOUDFALL_SSH_KEY: ${{ secrets.CLOUDFALL_SSH_KEY }}
|
|
190
|
+
CLOUDFALL_KNOWN_HOSTS: ${{ secrets.CLOUDFALL_KNOWN_HOSTS }}
|
|
191
|
+
run: |
|
|
192
|
+
install -m 0700 -d ~/.ssh
|
|
193
|
+
printf '%s\n' "$CLOUDFALL_KNOWN_HOSTS" > ~/.ssh/known_hosts
|
|
194
|
+
eval "$(ssh-agent -s)"
|
|
195
|
+
printf '%s\n' "$CLOUDFALL_SSH_KEY" | ssh-add -
|
|
196
|
+
echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK" >> "$GITHUB_ENV"
|
|
197
|
+
|
|
198
|
+
- name: Write the age key
|
|
199
|
+
env:
|
|
200
|
+
SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}
|
|
201
|
+
run: |
|
|
202
|
+
install -m 0600 /dev/null "$RUNNER_TEMP/age.key"
|
|
203
|
+
printf '%s\n' "$SOPS_AGE_KEY" > "$RUNNER_TEMP/age.key"
|
|
204
|
+
echo "SOPS_AGE_KEY_FILE=$RUNNER_TEMP/age.key" >> "$GITHUB_ENV"
|
|
205
|
+
|
|
206
|
+
- name: Check the toolchain
|
|
207
|
+
run: uv run cloudfall doctor
|
|
208
|
+
|
|
209
|
+
- name: Validate the config
|
|
210
|
+
run: uv run cloudfall config validate
|
|
211
|
+
|
|
212
|
+
- name: Render secrets
|
|
213
|
+
run: uv run cloudfall secrets render "$COMPONENT"
|
|
214
|
+
|
|
215
|
+
- name: Build the artifact
|
|
216
|
+
id: build
|
|
217
|
+
run: |
|
|
218
|
+
uv run cloudfall-engine artifact build "$COMPONENT" --ref "$REF" --format json > "$RUNNER_TEMP/build.json"
|
|
219
|
+
echo "release=$(jq -r '.data.release' "$RUNNER_TEMP/build.json")" >> "$GITHUB_OUTPUT"
|
|
220
|
+
|
|
221
|
+
- name: Show the plan
|
|
222
|
+
env:
|
|
223
|
+
RELEASE: ${{ steps.build.outputs.release }}
|
|
224
|
+
run: uv run cloudfall deploy "$COMPONENT" --release "$RELEASE"
|
|
225
|
+
|
|
226
|
+
- name: Deploy
|
|
227
|
+
env:
|
|
228
|
+
RELEASE: ${{ steps.build.outputs.release }}
|
|
229
|
+
run: uv run cloudfall deploy "$COMPONENT" --release "$RELEASE" --env-file "tmp/env/$COMPONENT.env" --yes
|
|
230
|
+
|
|
231
|
+
- name: Remove the decrypted files
|
|
232
|
+
if: always()
|
|
233
|
+
run: rm -f "tmp/env/$COMPONENT.env" "$RUNNER_TEMP/age.key"
|
|
234
|
+
|
|
235
|
+
- name: Upload the receipts
|
|
236
|
+
if: always()
|
|
237
|
+
uses: actions/upload-artifact@v7
|
|
238
|
+
with:
|
|
239
|
+
name: cloudfall-${{ env.COMPONENT }}-${{ github.run_id }}
|
|
240
|
+
path: |
|
|
241
|
+
tmp/artifacts/**/*.json
|
|
242
|
+
tmp/releases/
|
|
243
|
+
tmp/env-receipts/
|
|
244
|
+
retention-days: 90
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
What each step does:
|
|
248
|
+
|
|
249
|
+
- **Install Cloudfall from the lock file.** `uv sync --locked` installs the
|
|
250
|
+
Cloudfall release the project pins, and fails if `uv.lock` is out of date
|
|
251
|
+
rather than resolving a different one
|
|
252
|
+
- **Install sops.** The binary is checked against the release's checksum
|
|
253
|
+
file before it is installed
|
|
254
|
+
- **Load the SSH key.** The key goes into an `ssh-agent`, never onto disk;
|
|
255
|
+
Ansible reaches the agent through `SSH_AUTH_SOCK`
|
|
256
|
+
- **Write the age key.** The file is created `0600` before the key is
|
|
257
|
+
written into it, and `SOPS_AGE_KEY_FILE` points sops at it, as on your own
|
|
258
|
+
machine
|
|
259
|
+
- **Render secrets.** Writes `tmp/env/<component>.env` (`0600`) and the
|
|
260
|
+
receipt `tmp/env-receipts/<component>.json`; the log shows key names and a
|
|
261
|
+
hash, never a value
|
|
262
|
+
- **Build the artifact.** Clones the component's repository at `REF` and
|
|
263
|
+
packages it under `tmp/artifacts/<component>/`
|
|
264
|
+
- **Show the plan.** `deploy` without `--yes` validates the release and
|
|
265
|
+
prints which servers it would change, and changes nothing
|
|
266
|
+
- **Deploy.** Ships the release behind the component's health check, and
|
|
267
|
+
writes the release receipt `tmp/releases/<component>.json`
|
|
268
|
+
- **Remove the decrypted files.** GitHub-hosted runners are thrown away
|
|
269
|
+
after the job; on a self-hosted runner this step is what keeps the values
|
|
270
|
+
off its disk
|
|
271
|
+
|
|
272
|
+
If the project is not at the root of the repository, set `CLOUDFALL_PROJECT`
|
|
273
|
+
to its directory in the job's `env`.
|
|
274
|
+
|
|
275
|
+
## Passing the release id
|
|
276
|
+
|
|
277
|
+
`cloudfall deploy` takes the release to ship as `--release`, and only
|
|
278
|
+
`artifact build` produces one. Two things trip people up:
|
|
279
|
+
|
|
280
|
+
- `artifact build` is a `cloudfall-engine` command, not a `cloudfall` one.
|
|
281
|
+
Through an agent it is the `build_artifact` tool of `cloudfall mcp serve`
|
|
282
|
+
- The release id is in the build's JSON answer at `data.release`, such as
|
|
283
|
+
`20260101T000000Z-abcdef0` (the build time and the short commit). The
|
|
284
|
+
workflow reads it with `jq` and passes it on as a step output
|
|
285
|
+
|
|
286
|
+
Off a terminal both CLIs answer in JSON by default; the workflow passes
|
|
287
|
+
`--format json` so the build step does not depend on that.
|
|
288
|
+
|
|
289
|
+
The artifact is a file on the runner. `deploy` verifies it from
|
|
290
|
+
`tmp/artifacts` before shipping it, so build and deploy must run in the same
|
|
291
|
+
job. Splitting them across jobs means uploading `tmp/artifacts/` from one and
|
|
292
|
+
downloading it into the other.
|
|
293
|
+
|
|
294
|
+
`REF` is a ref of the component's repository (its `repository.url`), not of
|
|
295
|
+
the project repository. A push to the project's `main` therefore rebuilds the
|
|
296
|
+
component's `main`. To ship to production exactly the commit staging ran,
|
|
297
|
+
dispatch the production deploy with that commit's full hash as `ref`; the
|
|
298
|
+
staging receipt records it as `gitCommit`. The build packages the same
|
|
299
|
+
commit again, which gives a new release id and archive.
|
|
300
|
+
|
|
301
|
+
## Triggering it from an application repository
|
|
302
|
+
|
|
303
|
+
An application repository can start a deploy when its own `main` changes,
|
|
304
|
+
with a `repository_dispatch` event on the project repository:
|
|
305
|
+
|
|
306
|
+
```sh
|
|
307
|
+
gh api repos/acme/fleet/dispatches \
|
|
308
|
+
-f event_type=deploy \
|
|
309
|
+
-f 'client_payload[component]=crm-backend-staging' \
|
|
310
|
+
-f 'client_payload[environment]=staging' \
|
|
311
|
+
-f "client_payload[ref]=$GITHUB_SHA"
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Run it as a step in the application's workflow, authenticated with a token
|
|
315
|
+
that may write the project repository's contents (a fine-grained token or a
|
|
316
|
+
GitHub App token, stored as a secret of the application repository). The
|
|
317
|
+
event runs the workflow from the project's default branch, and a dispatch
|
|
318
|
+
naming `production` still waits for its reviewers.
|
|
319
|
+
|
|
320
|
+
## Approval: who presses the button
|
|
321
|
+
|
|
322
|
+
Every `Application` declares `spec.approval: manual` or `automatic`.
|
|
323
|
+
Cloudfall validates the field and writes it into the rendered inventory, but
|
|
324
|
+
no command acts on it today: `cloudfall deploy --yes` deploys an application
|
|
325
|
+
marked `manual` exactly as it deploys one marked `automatic`. The only gate
|
|
326
|
+
inside `deploy` is `--yes`, and a workflow that passes `--yes` has approved
|
|
327
|
+
its own deploy.
|
|
328
|
+
|
|
329
|
+
So in CI, the approval comes from GitHub, not from Cloudfall:
|
|
330
|
+
|
|
331
|
+
- Give every environment that holds an application marked `manual` required
|
|
332
|
+
reviewers, and turn on "Prevent self-review", so the person who pushed the
|
|
333
|
+
change cannot approve its deploy
|
|
334
|
+
- The job waits before its first step until a reviewer approves, so it never
|
|
335
|
+
sees the SSH key or the age key of that environment without approval
|
|
336
|
+
- Leave `staging` without reviewers if its applications are `automatic`
|
|
337
|
+
|
|
338
|
+
Read `approval: manual` as your declaration that such a gate must exist; the
|
|
339
|
+
environment's reviewers are what enforce it. The approval itself lands in
|
|
340
|
+
GitHub's deployment history, not in Cloudfall's record.
|
|
341
|
+
|
|
342
|
+
## Keeping the receipts
|
|
343
|
+
|
|
344
|
+
`tmp/` is ignored by git, and the runner disappears with the job. The upload
|
|
345
|
+
step keeps, for 90 days:
|
|
346
|
+
|
|
347
|
+
- `tmp/artifacts/<component>/<release>.json`: the artifact metadata, with the
|
|
348
|
+
commit and the archive's SHA-256
|
|
349
|
+
- `tmp/releases/<component>.json`: the release receipt from `deploy`
|
|
350
|
+
- `tmp/env-receipts/<component>.json`: the key names and hash of the
|
|
351
|
+
rendered environment file, never the values
|
|
352
|
+
|
|
353
|
+
It never uploads `tmp/env/`, which holds the decrypted values. Add
|
|
354
|
+
`tmp/artifacts/` whole if you want the archives kept as well.
|
|
355
|
+
|
|
356
|
+
If you want the receipts in git, copy them to a tracked directory and commit
|
|
357
|
+
them back from the job. Give the job `contents: write` and add a step after
|
|
358
|
+
the deploy:
|
|
359
|
+
|
|
360
|
+
```yaml
|
|
361
|
+
- name: Commit the receipt
|
|
362
|
+
env:
|
|
363
|
+
RELEASE: ${{ steps.build.outputs.release }}
|
|
364
|
+
run: |
|
|
365
|
+
mkdir -p receipts/releases
|
|
366
|
+
cp "tmp/releases/$COMPONENT.json" "receipts/releases/$COMPONENT-$RELEASE.json"
|
|
367
|
+
git config user.name "github-actions[bot]"
|
|
368
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
369
|
+
git add receipts/releases
|
|
370
|
+
git commit -m "Record $COMPONENT $RELEASE"
|
|
371
|
+
git push
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Only the kind directories are read as resources, so `receipts/` is safe in
|
|
375
|
+
the project. A push made with the workflow's own token does not start
|
|
376
|
+
another workflow run, so this does not loop. A branch protection rule on
|
|
377
|
+
`main` will refuse the push unless it allows the workflow to bypass it.
|
|
378
|
+
|
|
379
|
+
## What this guide does not cover
|
|
380
|
+
|
|
381
|
+
- Moving `artifact build` into the `cloudfall` CLI, so CI installs and pins
|
|
382
|
+
one tool
|
|
383
|
+
- A published reusable action
|
|
384
|
+
- Converging the baseline, services or domains from CI; the same pattern
|
|
385
|
+
applies, with the matching `cloudfall-engine playbook run` command
|
|
@@ -71,6 +71,9 @@ shows them); project playbooks by path, with `--roles` directories searched
|
|
|
71
71
|
before the bundled roles. To move a project to a newer Cloudfall commit, bump
|
|
72
72
|
`rev` and run `uv sync`.
|
|
73
73
|
|
|
74
|
+
To deploy from a pipeline instead of your own machine, so a merge to `main`
|
|
75
|
+
updates staging, see the [CI deploy guide](ci-deploy-guide.md).
|
|
76
|
+
|
|
74
77
|
## The agent contract
|
|
75
78
|
|
|
76
79
|
Humans and AI agents both edit the resources and both run the CLI, with a
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# S-001: Stale proposals
|
|
2
2
|
|
|
3
|
-
status:
|
|
3
|
+
status: built
|
|
4
4
|
|
|
5
5
|
## Problem
|
|
6
6
|
|
|
@@ -66,4 +66,16 @@ them.
|
|
|
66
66
|
|
|
67
67
|
## Issues
|
|
68
68
|
|
|
69
|
+
- cloudfall-dev/cloudfall#97: S-001-1, S-001-2, S-001-3, S-001-4, S-001-5, S-001-6, S-001-7, S-001-8, S-001-9
|
|
70
|
+
|
|
69
71
|
## Verification
|
|
72
|
+
|
|
73
|
+
- S-001-1: proposed `deploy` on a tmp project with `hz1` observed at 09:00, rewrote `hz1.json` to 09:30, ran `cloudfall decisions list`; and `test_s001_1_a_newer_observation_makes_the_proposal_stale`, the proposal read `"stale": true, "staleHosts": ["hz1"]`
|
|
74
|
+
- S-001-2: `cloudfall decisions list` on the same project before the rewrite, plus `test_s001_2_the_same_missing_or_no_observation_is_not_stale`, `test_s001_2_a_basis_that_names_no_host_is_never_stale` and `test_s001_2_times_compare_as_moments_not_as_text`, each read `"stale": false` with no `staleHosts`
|
|
75
|
+
- S-001-3: `decisions list`, `decisions show` and `cloudfall why` on the tmp project's `ran` facts record, plus `test_s001_3_a_decision_that_is_not_proposed_carries_no_staleness` over verified, ran and superseded records, carried neither key
|
|
76
|
+
- S-001-4: `cloudfall decisions list --stale` and `--stale --status proposed` on the tmp project, plus `test_s001_4_the_stale_filter_keeps_only_the_stale_and_combines_with_status`, returned only the stale proposal, the same set both ways
|
|
77
|
+
- S-001-5: `cloudfall why --stale` on the tmp project, plus `test_s001_5_why_stale_keeps_only_the_stale_and_reports_them`, returned only the stale proposal with `stale` and `staleHosts` identical to `decisions list`
|
|
78
|
+
- S-001-6: `cloudfall decisions show deploy-20261020094012` on the tmp project, plus `test_s001_6_show_reports_a_stale_proposal`, reported `"stale": true, "staleHosts": ["hz1"]`
|
|
79
|
+
- S-001-7: `git hash-object` of both tmp records before and after `decisions list`, `decisions show` and `why`, plus `test_s001_7_working_out_staleness_writes_nothing` (byte and mtime compare), the bytes were unchanged
|
|
80
|
+
- S-001-8: `test_s001_8_approve_names_the_stale_hosts_and_still_runs` runs `decisions approve` at a simulated terminal against a real `ansible-playbook`; the prompt named `web-1 was observed again at 2026-09-21T16:02:10Z` and the decision ended `verified` (no terminal was available to run it by hand)
|
|
81
|
+
- S-001-9: wrote invalid JSON to the tmp project's `hz1.json` and ran `cloudfall decisions list`, plus `test_s001_9_an_observation_file_that_is_not_json_is_record_invalid`, it exited `RECORD_INVALID` (86) naming `tmp/s001/tmp/observed/hz1.json`
|
{cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_deploy/defaults/main.yml
RENAMED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
cloudfall_deploy_environment_file: ""
|
|
3
3
|
cloudfall_deploy_receipt_directory: ""
|
|
4
|
+
# A component without an HTTP health check passes its gate when its unit is
|
|
5
|
+
# active right after the restart and still active this many seconds later.
|
|
6
|
+
cloudfall_deploy_unit_settle_seconds: 10
|
|
4
7
|
cloudfall_deploy_uv_version: "0.12.10"
|
|
5
8
|
cloudfall_deploy_uv_sha256: >-
|
|
6
9
|
173d95a0c32d18c896c46ba6fafbf3cf9c14ab74b033f81b76c883ef492a976b
|
{cloudfall-0.7.0rc5 → cloudfall-0.8.0rc2}/engine/ansible/roles/cloudfall_deploy/tasks/main.yml
RENAMED
|
@@ -203,7 +203,86 @@
|
|
|
203
203
|
until: >-
|
|
204
204
|
cloudfall_deploy_health.status
|
|
205
205
|
in cloudfall_deploy_component.healthCheck.expectedStatuses
|
|
206
|
-
when:
|
|
206
|
+
when:
|
|
207
|
+
- not ansible_check_mode
|
|
208
|
+
- cloudfall_deploy_component.healthCheck.type == "http"
|
|
209
|
+
|
|
210
|
+
- name: Gate the release on an active service unit
|
|
211
|
+
ansible.builtin.command:
|
|
212
|
+
argv:
|
|
213
|
+
- /usr/bin/systemctl
|
|
214
|
+
- is-active
|
|
215
|
+
- --quiet
|
|
216
|
+
- "{{ cloudfall_deploy_component.service.name }}.service"
|
|
217
|
+
register: cloudfall_deploy_unit_started
|
|
218
|
+
changed_when: false
|
|
219
|
+
when:
|
|
220
|
+
- not ansible_check_mode
|
|
221
|
+
- cloudfall_deploy_component.healthCheck.type == "none"
|
|
222
|
+
|
|
223
|
+
- name: Read the service unit restart count before settling
|
|
224
|
+
ansible.builtin.command:
|
|
225
|
+
argv:
|
|
226
|
+
- /usr/bin/systemctl
|
|
227
|
+
- show
|
|
228
|
+
- --property=NRestarts
|
|
229
|
+
- --value
|
|
230
|
+
- "{{ cloudfall_deploy_component.service.name }}.service"
|
|
231
|
+
register: cloudfall_deploy_unit_restarts_before
|
|
232
|
+
changed_when: false
|
|
233
|
+
when:
|
|
234
|
+
- not ansible_check_mode
|
|
235
|
+
- cloudfall_deploy_component.healthCheck.type == "none"
|
|
236
|
+
|
|
237
|
+
- name: Let the service unit settle
|
|
238
|
+
ansible.builtin.pause:
|
|
239
|
+
seconds: "{{ cloudfall_deploy_unit_settle_seconds }}"
|
|
240
|
+
when:
|
|
241
|
+
- not ansible_check_mode
|
|
242
|
+
- cloudfall_deploy_component.healthCheck.type == "none"
|
|
243
|
+
|
|
244
|
+
- name: Gate the release on a service unit still active after settling
|
|
245
|
+
ansible.builtin.command:
|
|
246
|
+
argv:
|
|
247
|
+
- /usr/bin/systemctl
|
|
248
|
+
- is-active
|
|
249
|
+
- --quiet
|
|
250
|
+
- "{{ cloudfall_deploy_component.service.name }}.service"
|
|
251
|
+
register: cloudfall_deploy_unit_settled
|
|
252
|
+
changed_when: false
|
|
253
|
+
when:
|
|
254
|
+
- not ansible_check_mode
|
|
255
|
+
- cloudfall_deploy_component.healthCheck.type == "none"
|
|
256
|
+
|
|
257
|
+
- name: Read the service unit restart count after settling
|
|
258
|
+
ansible.builtin.command:
|
|
259
|
+
argv:
|
|
260
|
+
- /usr/bin/systemctl
|
|
261
|
+
- show
|
|
262
|
+
- --property=NRestarts
|
|
263
|
+
- --value
|
|
264
|
+
- "{{ cloudfall_deploy_component.service.name }}.service"
|
|
265
|
+
register: cloudfall_deploy_unit_restarts_after
|
|
266
|
+
changed_when: false
|
|
267
|
+
when:
|
|
268
|
+
- not ansible_check_mode
|
|
269
|
+
- cloudfall_deploy_component.healthCheck.type == "none"
|
|
270
|
+
|
|
271
|
+
- name: Gate the release on a service unit that did not restart while settling
|
|
272
|
+
ansible.builtin.assert:
|
|
273
|
+
that:
|
|
274
|
+
- cloudfall_deploy_unit_restarts_before.stdout is match('^[0-9]+$')
|
|
275
|
+
- cloudfall_deploy_unit_restarts_after.stdout is match('^[0-9]+$')
|
|
276
|
+
- cloudfall_deploy_unit_restarts_after.stdout | int
|
|
277
|
+
== cloudfall_deploy_unit_restarts_before.stdout | int
|
|
278
|
+
fail_msg: >-
|
|
279
|
+
{{ cloudfall_deploy_component.service.name }}.service restarted
|
|
280
|
+
while settling (NRestarts {{
|
|
281
|
+
cloudfall_deploy_unit_restarts_before.stdout
|
|
282
|
+
}} to {{ cloudfall_deploy_unit_restarts_after.stdout }})
|
|
283
|
+
when:
|
|
284
|
+
- not ansible_check_mode
|
|
285
|
+
- cloudfall_deploy_component.healthCheck.type == "none"
|
|
207
286
|
rescue:
|
|
208
287
|
- name: Restore the previous release symlink
|
|
209
288
|
ansible.builtin.file:
|