cloudfall 0.2.0__py3-none-any.whl
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/__init__.py +19 -0
- cloudfall/__main__.py +5 -0
- cloudfall/_bundled/schemas/v1/alert-rule.schema.json +63 -0
- cloudfall/_bundled/schemas/v1/application.schema.json +60 -0
- cloudfall/_bundled/schemas/v1/artifact.schema.json +71 -0
- cloudfall/_bundled/schemas/v1/backup-receipt.schema.json +58 -0
- cloudfall/_bundled/schemas/v1/common.schema.json +115 -0
- cloudfall/_bundled/schemas/v1/component.schema.json +234 -0
- cloudfall/_bundled/schemas/v1/deployment-receipt.schema.json +53 -0
- cloudfall/_bundled/schemas/v1/domain.schema.json +208 -0
- cloudfall/_bundled/schemas/v1/environment-receipt.schema.json +55 -0
- cloudfall/_bundled/schemas/v1/logging-stack.schema.json +473 -0
- cloudfall/_bundled/schemas/v1/observed-domain.schema.json +158 -0
- cloudfall/_bundled/schemas/v1/observed-server.schema.json +627 -0
- cloudfall/_bundled/schemas/v1/operator-policy.schema.json +97 -0
- cloudfall/_bundled/schemas/v1/operator-proposal.schema.json +253 -0
- cloudfall/_bundled/schemas/v1/release-receipt.schema.json +66 -0
- cloudfall/_bundled/schemas/v1/server-type.schema.json +289 -0
- cloudfall/_bundled/schemas/v1/server.schema.json +146 -0
- cloudfall/_bundled/schemas/v1/service.schema.json +222 -0
- cloudfall/_bundled/schemas/v1/ssh-public-key.schema.json +55 -0
- cloudfall/agent_tools.py +868 -0
- cloudfall/arguments.py +92 -0
- cloudfall/audit.py +905 -0
- cloudfall/authoring.py +341 -0
- cloudfall/cli.py +1748 -0
- cloudfall/commands.py +257 -0
- cloudfall/cutover.py +370 -0
- cloudfall/dashboard.py +407 -0
- cloudfall/dashboard_server.py +201 -0
- cloudfall/domain.py +1157 -0
- cloudfall/importer.py +906 -0
- cloudfall/inventory.py +2116 -0
- cloudfall/lifecycle.py +874 -0
- cloudfall/mcp_server.py +704 -0
- cloudfall/migrate.py +730 -0
- cloudfall/observation.py +143 -0
- cloudfall/operations.py +1228 -0
- cloudfall/operator.py +1099 -0
- cloudfall/project.py +841 -0
- cloudfall/py.typed +0 -0
- cloudfall/render_api.py +338 -0
- cloudfall/resources.py +67 -0
- cloudfall/secrets.py +262 -0
- cloudfall/service_evidence.py +659 -0
- cloudfall/validation.py +1340 -0
- cloudfall-0.2.0.dist-info/METADATA +408 -0
- cloudfall-0.2.0.dist-info/RECORD +165 -0
- cloudfall-0.2.0.dist-info/WHEEL +4 -0
- cloudfall-0.2.0.dist-info/entry_points.txt +4 -0
- cloudfall-0.2.0.dist-info/licenses/LICENSE +661 -0
- cloudfall_engine/__init__.py +5 -0
- cloudfall_engine/__main__.py +5 -0
- cloudfall_engine/_bundled/ansible/ansible.cfg +7 -0
- cloudfall_engine/_bundled/ansible/playbooks/backup.yml +86 -0
- cloudfall_engine/_bundled/ansible/playbooks/baseline.yml +35 -0
- cloudfall_engine/_bundled/ansible/playbooks/bootstrap.yml +24 -0
- cloudfall_engine/_bundled/ansible/playbooks/data.yml +28 -0
- cloudfall_engine/_bundled/ansible/playbooks/deploy.yml +35 -0
- cloudfall_engine/_bundled/ansible/playbooks/domains.yml +21 -0
- cloudfall_engine/_bundled/ansible/playbooks/health.yml +31 -0
- cloudfall_engine/_bundled/ansible/playbooks/inspect.yml +15 -0
- cloudfall_engine/_bundled/ansible/playbooks/logging.yml +35 -0
- cloudfall_engine/_bundled/ansible/playbooks/restart.yml +31 -0
- cloudfall_engine/_bundled/ansible/playbooks/rollback.yml +33 -0
- cloudfall_engine/_bundled/ansible/playbooks/services.yml +37 -0
- cloudfall_engine/_bundled/ansible/playbooks/time.yml +9 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_access/defaults/main.yml +2 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_access/handlers/main.yml +5 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_access/meta/argument_specs.yml +34 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_access/meta/main.yml +13 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_access/tasks/main.yml +59 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_bootstrap/defaults/main.yml +11 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_bootstrap/meta/argument_specs.yml +52 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_bootstrap/meta/main.yml +13 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_bootstrap/tasks/main.yml +99 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_data_migration/defaults/main.yml +15 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_data_migration/meta/argument_specs.yml +36 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_data_migration/meta/main.yml +13 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_data_migration/tasks/main.yml +201 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/defaults/main.yml +10 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/meta/argument_specs.yml +125 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/meta/main.yml +13 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/health.yml +3 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/health_gate.yml +37 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/main.yml +285 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/restart.yml +17 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/rollback.yml +52 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/templates/component.service.j2 +24 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/defaults/main.yml +2 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/handlers/main.yml +5 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/meta/argument_specs.yml +33 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/meta/main.yml +13 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/tasks/main.yml +51 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/templates/nftables.conf.j2 +31 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_inspect/defaults/main.yml +6 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_inspect/meta/argument_specs.yml +24 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_inspect/meta/main.yml +13 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_inspect/tasks/main.yml +643 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/defaults/main.yml +6 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/handlers/main.yml +29 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/meta/argument_specs.yml +227 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/meta/main.yml +11 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/tasks/main.yml +487 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/alertmanager-defaults.j2 +2 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/alertmanager.yaml.j2 +33 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/grafana.ini.j2 +20 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/loki-datasource.yml.j2 +11 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/loki-systemd-override.conf.j2 +3 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/loki.yaml.j2 +49 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/nginx-loki-gateway.conf.j2 +45 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/prometheus-datasource.yml.j2 +11 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/prometheus-defaults.j2 +2 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/prometheus-rules.yaml.j2 +20 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/prometheus.yaml.j2 +24 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/defaults/main.yml +7 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/handlers/main.yml +6 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/meta/argument_specs.yml +122 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/meta/main.yml +11 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/tasks/main.yml +224 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/templates/alloy-systemd-override.conf.j2 +3 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/templates/config.alloy.j2 +127 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/defaults/main.yml +4 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/handlers/main.yml +5 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/meta/argument_specs.yml +69 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/meta/main.yml +13 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/tasks/main.yml +212 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/templates/domain-site.conf.j2 +54 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/defaults/main.yml +6 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/handlers/main.yml +9 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/meta/argument_specs.yml +91 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/meta/main.yml +13 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/tasks/main.yml +295 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/backup.service.j2 +11 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/backup.sh.j2 +17 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/backup.timer.j2 +10 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/cloudfall.conf.j2 +3 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/restore-check.service.j2 +11 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/restore-check.sh.j2 +27 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/restore-check.timer.j2 +10 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/defaults/main.yml +2 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/handlers/main.yml +6 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/meta/argument_specs.yml +68 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/meta/main.yml +13 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/tasks/main.yml +148 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/backup.service.j2 +11 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/backup.sh.j2 +14 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/backup.timer.j2 +10 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/cloudfall.conf.j2 +6 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/restore-check.service.j2 +11 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/restore-check.sh.j2 +15 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/restore-check.timer.j2 +10 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_time/defaults/main.yml +2 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_time/meta/argument_specs.yml +10 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_time/meta/main.yml +13 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_time/tasks/main.yml +119 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_unattended_upgrades/defaults/main.yml +3 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_unattended_upgrades/meta/argument_specs.yml +8 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_unattended_upgrades/meta/main.yml +13 -0
- cloudfall_engine/_bundled/ansible/roles/cloudfall_unattended_upgrades/tasks/main.yml +37 -0
- cloudfall_engine/ansible_inventory.py +253 -0
- cloudfall_engine/artifact.py +258 -0
- cloudfall_engine/cli.py +273 -0
- cloudfall_engine/playbook.py +175 -0
- cloudfall_engine/py.typed +0 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
---
|
|
2
|
+
- name: Validate deployment target and declaration
|
|
3
|
+
ansible.builtin.assert:
|
|
4
|
+
that:
|
|
5
|
+
- ansible_facts["distribution"] == "Debian"
|
|
6
|
+
- ansible_facts["service_mgr"] == "systemd"
|
|
7
|
+
- ansible_facts["architecture"] == "x86_64"
|
|
8
|
+
- cloudfall_deploy_component.service.manager == "systemd"
|
|
9
|
+
- cloudfall_deploy_component.runtime.type == "python"
|
|
10
|
+
- cloudfall_deploy_component.runtime.packageManager == "uv"
|
|
11
|
+
- cloudfall_deploy_release | length > 0
|
|
12
|
+
- cloudfall_deploy_artifact_sha256 | length == 64
|
|
13
|
+
fail_msg: >-
|
|
14
|
+
The first deploy slice supports Debian x86_64 hosts and Python
|
|
15
|
+
components managed by uv under systemd
|
|
16
|
+
|
|
17
|
+
- name: Compute release paths
|
|
18
|
+
ansible.builtin.set_fact:
|
|
19
|
+
cloudfall_deploy_release_directory: >-
|
|
20
|
+
{{ cloudfall_deploy_component.install_root }}/releases/{{
|
|
21
|
+
cloudfall_deploy_release
|
|
22
|
+
}}
|
|
23
|
+
cloudfall_deploy_current_link: >-
|
|
24
|
+
{{ cloudfall_deploy_component.install_root }}/current
|
|
25
|
+
cloudfall_deploy_shared_directory: >-
|
|
26
|
+
{{ cloudfall_deploy_component.install_root }}/shared
|
|
27
|
+
cloudfall_deploy_staging_archive: >-
|
|
28
|
+
/tmp/cloudfall-{{ cloudfall_deploy_component.id }}-{{
|
|
29
|
+
cloudfall_deploy_release
|
|
30
|
+
}}.tar.gz
|
|
31
|
+
|
|
32
|
+
- name: Check the installed uv version
|
|
33
|
+
ansible.builtin.command:
|
|
34
|
+
argv:
|
|
35
|
+
- /usr/local/bin/uv
|
|
36
|
+
- --version
|
|
37
|
+
register: cloudfall_deploy_uv_installed
|
|
38
|
+
changed_when: false
|
|
39
|
+
failed_when: false
|
|
40
|
+
|
|
41
|
+
- name: Install the pinned uv release
|
|
42
|
+
when: >-
|
|
43
|
+
cloudfall_deploy_uv_installed.rc != 0
|
|
44
|
+
or cloudfall_deploy_uv_version not in cloudfall_deploy_uv_installed.stdout
|
|
45
|
+
block:
|
|
46
|
+
- name: Download the pinned uv archive
|
|
47
|
+
ansible.builtin.get_url:
|
|
48
|
+
url: "{{ cloudfall_deploy_uv_url }}"
|
|
49
|
+
dest: /tmp/cloudfall-uv.tar.gz
|
|
50
|
+
checksum: "sha256:{{ cloudfall_deploy_uv_sha256 }}"
|
|
51
|
+
mode: "0644"
|
|
52
|
+
|
|
53
|
+
- name: Unpack the uv archive
|
|
54
|
+
ansible.builtin.unarchive:
|
|
55
|
+
src: /tmp/cloudfall-uv.tar.gz
|
|
56
|
+
dest: /tmp
|
|
57
|
+
remote_src: true
|
|
58
|
+
|
|
59
|
+
- name: Install the uv binary
|
|
60
|
+
ansible.builtin.copy:
|
|
61
|
+
src: /tmp/uv-x86_64-unknown-linux-gnu/uv
|
|
62
|
+
dest: /usr/local/bin/uv
|
|
63
|
+
remote_src: true
|
|
64
|
+
owner: root
|
|
65
|
+
group: root
|
|
66
|
+
mode: "0755"
|
|
67
|
+
|
|
68
|
+
- name: Create release and shared directories
|
|
69
|
+
ansible.builtin.file:
|
|
70
|
+
path: "{{ item }}"
|
|
71
|
+
state: directory
|
|
72
|
+
owner: "{{ cloudfall_deploy_component.application_user }}"
|
|
73
|
+
group: "{{ cloudfall_deploy_component.application_user }}"
|
|
74
|
+
mode: "0755"
|
|
75
|
+
loop:
|
|
76
|
+
- "{{ cloudfall_deploy_component.install_root }}/releases"
|
|
77
|
+
- "{{ cloudfall_deploy_release_directory }}"
|
|
78
|
+
- "{{ cloudfall_deploy_shared_directory }}"
|
|
79
|
+
- "{{ cloudfall_deploy_shared_directory }}/env"
|
|
80
|
+
- "{{ cloudfall_deploy_shared_directory }}/uv"
|
|
81
|
+
|
|
82
|
+
- name: Transfer the release artifact
|
|
83
|
+
ansible.builtin.copy:
|
|
84
|
+
src: "{{ cloudfall_deploy_artifact_archive }}"
|
|
85
|
+
dest: "{{ cloudfall_deploy_staging_archive }}"
|
|
86
|
+
owner: root
|
|
87
|
+
group: root
|
|
88
|
+
mode: "0644"
|
|
89
|
+
|
|
90
|
+
- name: Verify the transferred artifact digest
|
|
91
|
+
ansible.builtin.stat:
|
|
92
|
+
path: "{{ cloudfall_deploy_staging_archive }}"
|
|
93
|
+
checksum_algorithm: sha256
|
|
94
|
+
register: cloudfall_deploy_staged
|
|
95
|
+
|
|
96
|
+
- name: Refuse a release whose digest does not match its metadata
|
|
97
|
+
ansible.builtin.assert:
|
|
98
|
+
that:
|
|
99
|
+
- cloudfall_deploy_staged.stat.checksum
|
|
100
|
+
== cloudfall_deploy_artifact_sha256
|
|
101
|
+
fail_msg: >-
|
|
102
|
+
Transferred artifact digest does not match the declared
|
|
103
|
+
artifact metadata
|
|
104
|
+
|
|
105
|
+
- name: Unpack the verified release
|
|
106
|
+
ansible.builtin.unarchive:
|
|
107
|
+
src: "{{ cloudfall_deploy_staging_archive }}"
|
|
108
|
+
dest: "{{ cloudfall_deploy_release_directory }}"
|
|
109
|
+
remote_src: true
|
|
110
|
+
owner: "{{ cloudfall_deploy_component.application_user }}"
|
|
111
|
+
group: "{{ cloudfall_deploy_component.application_user }}"
|
|
112
|
+
|
|
113
|
+
- name: Remove the staged archive
|
|
114
|
+
ansible.builtin.file:
|
|
115
|
+
path: "{{ cloudfall_deploy_staging_archive }}"
|
|
116
|
+
state: absent
|
|
117
|
+
|
|
118
|
+
- name: Install the component environment file
|
|
119
|
+
ansible.builtin.copy:
|
|
120
|
+
src: "{{ cloudfall_deploy_environment_file }}"
|
|
121
|
+
dest: >-
|
|
122
|
+
{{ cloudfall_deploy_shared_directory }}/env/{{
|
|
123
|
+
cloudfall_deploy_component.id
|
|
124
|
+
}}.env
|
|
125
|
+
owner: root
|
|
126
|
+
group: "{{ cloudfall_deploy_component.application_user }}"
|
|
127
|
+
mode: "0640"
|
|
128
|
+
when: cloudfall_deploy_environment_file | length > 0
|
|
129
|
+
|
|
130
|
+
- name: Materialize locked dependencies for the release
|
|
131
|
+
ansible.builtin.command:
|
|
132
|
+
argv:
|
|
133
|
+
- /usr/local/bin/uv
|
|
134
|
+
- sync
|
|
135
|
+
- --frozen
|
|
136
|
+
- --no-dev
|
|
137
|
+
chdir: "{{ cloudfall_deploy_release_directory }}"
|
|
138
|
+
become: true
|
|
139
|
+
become_user: "{{ cloudfall_deploy_component.application_user }}"
|
|
140
|
+
environment:
|
|
141
|
+
HOME: "{{ cloudfall_deploy_shared_directory }}"
|
|
142
|
+
UV_CACHE_DIR: "{{ cloudfall_deploy_shared_directory }}/uv/cache"
|
|
143
|
+
UV_PYTHON_INSTALL_DIR: "{{ cloudfall_deploy_shared_directory }}/uv/python"
|
|
144
|
+
changed_when: true
|
|
145
|
+
|
|
146
|
+
- name: Inspect the currently active release
|
|
147
|
+
ansible.builtin.stat:
|
|
148
|
+
path: "{{ cloudfall_deploy_current_link }}"
|
|
149
|
+
follow: false
|
|
150
|
+
get_checksum: false
|
|
151
|
+
register: cloudfall_deploy_current
|
|
152
|
+
|
|
153
|
+
- name: Record the rollback candidate
|
|
154
|
+
ansible.builtin.set_fact:
|
|
155
|
+
cloudfall_deploy_previous_directory: >-
|
|
156
|
+
{{
|
|
157
|
+
cloudfall_deploy_current.stat.lnk_source
|
|
158
|
+
if cloudfall_deploy_current.stat.exists
|
|
159
|
+
and cloudfall_deploy_current.stat.islnk
|
|
160
|
+
else ""
|
|
161
|
+
}}
|
|
162
|
+
|
|
163
|
+
- name: Install the managed service unit
|
|
164
|
+
ansible.builtin.template:
|
|
165
|
+
src: component.service.j2
|
|
166
|
+
dest: >-
|
|
167
|
+
/etc/systemd/system/{{ cloudfall_deploy_component.service.name }}.service
|
|
168
|
+
owner: root
|
|
169
|
+
group: root
|
|
170
|
+
mode: "0644"
|
|
171
|
+
|
|
172
|
+
- name: Activate the release behind the health gate
|
|
173
|
+
block:
|
|
174
|
+
- name: Switch the current release symlink
|
|
175
|
+
ansible.builtin.file:
|
|
176
|
+
src: "{{ cloudfall_deploy_release_directory }}"
|
|
177
|
+
dest: "{{ cloudfall_deploy_current_link }}"
|
|
178
|
+
state: link
|
|
179
|
+
force: true
|
|
180
|
+
follow: false
|
|
181
|
+
|
|
182
|
+
- name: Restart the component service
|
|
183
|
+
ansible.builtin.systemd_service:
|
|
184
|
+
name: "{{ cloudfall_deploy_component.service.name }}.service"
|
|
185
|
+
enabled: true
|
|
186
|
+
state: restarted
|
|
187
|
+
daemon_reload: true
|
|
188
|
+
|
|
189
|
+
- name: Gate the release on its declared health check
|
|
190
|
+
ansible.builtin.uri:
|
|
191
|
+
url: >-
|
|
192
|
+
{{ cloudfall_deploy_component.healthCheck.scheme }}://127.0.0.1:{{
|
|
193
|
+
cloudfall_deploy_component.healthCheck.port
|
|
194
|
+
}}{{ cloudfall_deploy_component.healthCheck.path }}
|
|
195
|
+
status_code: >-
|
|
196
|
+
{{ cloudfall_deploy_component.healthCheck.expectedStatuses }}
|
|
197
|
+
timeout: "{{ cloudfall_deploy_component.healthCheck.timeoutSeconds }}"
|
|
198
|
+
return_content: false
|
|
199
|
+
register: cloudfall_deploy_health
|
|
200
|
+
retries: >-
|
|
201
|
+
{{ cloudfall_deploy_component.healthCheck.attempts | int - 1 }}
|
|
202
|
+
delay: 3
|
|
203
|
+
until: >-
|
|
204
|
+
cloudfall_deploy_health.status
|
|
205
|
+
in cloudfall_deploy_component.healthCheck.expectedStatuses
|
|
206
|
+
when: not ansible_check_mode
|
|
207
|
+
rescue:
|
|
208
|
+
- name: Restore the previous release symlink
|
|
209
|
+
ansible.builtin.file:
|
|
210
|
+
src: "{{ cloudfall_deploy_previous_directory }}"
|
|
211
|
+
dest: "{{ cloudfall_deploy_current_link }}"
|
|
212
|
+
state: link
|
|
213
|
+
force: true
|
|
214
|
+
follow: false
|
|
215
|
+
when: cloudfall_deploy_previous_directory | length > 0
|
|
216
|
+
|
|
217
|
+
- name: Restart the restored release
|
|
218
|
+
ansible.builtin.systemd_service:
|
|
219
|
+
name: "{{ cloudfall_deploy_component.service.name }}.service"
|
|
220
|
+
state: restarted
|
|
221
|
+
daemon_reload: true
|
|
222
|
+
when: cloudfall_deploy_previous_directory | length > 0
|
|
223
|
+
|
|
224
|
+
- name: Stop the failed first release
|
|
225
|
+
ansible.builtin.systemd_service:
|
|
226
|
+
name: "{{ cloudfall_deploy_component.service.name }}.service"
|
|
227
|
+
state: stopped
|
|
228
|
+
when: cloudfall_deploy_previous_directory | length == 0
|
|
229
|
+
|
|
230
|
+
- name: Fail the deployment after rolling back
|
|
231
|
+
ansible.builtin.fail:
|
|
232
|
+
msg: >-
|
|
233
|
+
Release {{ cloudfall_deploy_release }} failed its health check and
|
|
234
|
+
was {{
|
|
235
|
+
'rolled back to '
|
|
236
|
+
~ (cloudfall_deploy_previous_directory | basename)
|
|
237
|
+
if cloudfall_deploy_previous_directory | length > 0
|
|
238
|
+
else 'stopped because no previous release exists'
|
|
239
|
+
}}
|
|
240
|
+
|
|
241
|
+
- name: Ensure the controller receipt directory exists
|
|
242
|
+
ansible.builtin.file:
|
|
243
|
+
path: "{{ cloudfall_deploy_receipt_directory }}"
|
|
244
|
+
state: directory
|
|
245
|
+
mode: "0755"
|
|
246
|
+
delegate_to: localhost
|
|
247
|
+
become: false
|
|
248
|
+
when: cloudfall_deploy_receipt_directory | length > 0
|
|
249
|
+
|
|
250
|
+
- name: Write the release receipt on the controller
|
|
251
|
+
ansible.builtin.copy:
|
|
252
|
+
content: >-
|
|
253
|
+
{{
|
|
254
|
+
{
|
|
255
|
+
'apiVersion': 'cloudfall/v1',
|
|
256
|
+
'kind': 'ReleaseReceipt',
|
|
257
|
+
'metadata': {
|
|
258
|
+
'id': cloudfall_deploy_component.id,
|
|
259
|
+
'description': 'Release receipt written by cloudfall_deploy',
|
|
260
|
+
},
|
|
261
|
+
'spec': {
|
|
262
|
+
'component': cloudfall_deploy_component.id,
|
|
263
|
+
'server': cloudfall_server_id,
|
|
264
|
+
'release': cloudfall_deploy_release,
|
|
265
|
+
'artifactSha256': cloudfall_deploy_artifact_sha256,
|
|
266
|
+
'deployedAt': ansible_facts['date_time']['iso8601'],
|
|
267
|
+
'previousRelease': (
|
|
268
|
+
(cloudfall_deploy_previous_directory | basename)
|
|
269
|
+
if cloudfall_deploy_previous_directory | length > 0
|
|
270
|
+
else none
|
|
271
|
+
),
|
|
272
|
+
'healthCheckPassed': true,
|
|
273
|
+
},
|
|
274
|
+
} | to_nice_json
|
|
275
|
+
}}
|
|
276
|
+
dest: >-
|
|
277
|
+
{{ cloudfall_deploy_receipt_directory }}/{{
|
|
278
|
+
cloudfall_deploy_component.id
|
|
279
|
+
}}.json
|
|
280
|
+
mode: "0644"
|
|
281
|
+
delegate_to: localhost
|
|
282
|
+
become: false
|
|
283
|
+
when:
|
|
284
|
+
- not ansible_check_mode
|
|
285
|
+
- cloudfall_deploy_receipt_directory | length > 0
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
- name: Validate restart target and declaration
|
|
3
|
+
ansible.builtin.assert:
|
|
4
|
+
that:
|
|
5
|
+
- ansible_facts["distribution"] == "Debian"
|
|
6
|
+
- ansible_facts["service_mgr"] == "systemd"
|
|
7
|
+
- cloudfall_deploy_component.service.manager == "systemd"
|
|
8
|
+
fail_msg: Restart requires a Debian systemd host
|
|
9
|
+
|
|
10
|
+
- name: Restart the component service
|
|
11
|
+
ansible.builtin.systemd_service:
|
|
12
|
+
name: "{{ cloudfall_deploy_component.service.name }}.service"
|
|
13
|
+
state: restarted
|
|
14
|
+
daemon_reload: true
|
|
15
|
+
|
|
16
|
+
- name: Require the restarted component to pass its health check
|
|
17
|
+
ansible.builtin.include_tasks: health_gate.yml
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
- name: Validate rollback target and declaration
|
|
3
|
+
ansible.builtin.assert:
|
|
4
|
+
that:
|
|
5
|
+
- ansible_facts["distribution"] == "Debian"
|
|
6
|
+
- ansible_facts["service_mgr"] == "systemd"
|
|
7
|
+
- cloudfall_deploy_component.service.manager == "systemd"
|
|
8
|
+
- cloudfall_deploy_release | length > 0
|
|
9
|
+
fail_msg: >-
|
|
10
|
+
Rollback requires a Debian systemd host and an explicit release id
|
|
11
|
+
|
|
12
|
+
- name: Compute rollback paths
|
|
13
|
+
ansible.builtin.set_fact:
|
|
14
|
+
cloudfall_deploy_release_directory: >-
|
|
15
|
+
{{ cloudfall_deploy_component.install_root }}/releases/{{
|
|
16
|
+
cloudfall_deploy_release
|
|
17
|
+
}}
|
|
18
|
+
cloudfall_deploy_current_link: >-
|
|
19
|
+
{{ cloudfall_deploy_component.install_root }}/current
|
|
20
|
+
|
|
21
|
+
- name: Inspect the rollback target release
|
|
22
|
+
ansible.builtin.stat:
|
|
23
|
+
path: "{{ cloudfall_deploy_release_directory }}"
|
|
24
|
+
get_checksum: false
|
|
25
|
+
register: cloudfall_deploy_rollback_target
|
|
26
|
+
|
|
27
|
+
- name: Require the rollback target on the server
|
|
28
|
+
ansible.builtin.assert:
|
|
29
|
+
that:
|
|
30
|
+
- cloudfall_deploy_rollback_target.stat.exists
|
|
31
|
+
- cloudfall_deploy_rollback_target.stat.isdir
|
|
32
|
+
fail_msg: >-
|
|
33
|
+
Release {{ cloudfall_deploy_release }} does not exist under
|
|
34
|
+
{{ cloudfall_deploy_component.install_root }}/releases
|
|
35
|
+
|
|
36
|
+
- name: Switch the current release symlink to the rollback target
|
|
37
|
+
ansible.builtin.file:
|
|
38
|
+
src: "{{ cloudfall_deploy_release_directory }}"
|
|
39
|
+
dest: "{{ cloudfall_deploy_current_link }}"
|
|
40
|
+
state: link
|
|
41
|
+
force: true
|
|
42
|
+
follow: false
|
|
43
|
+
|
|
44
|
+
- name: Restart the component service
|
|
45
|
+
ansible.builtin.systemd_service:
|
|
46
|
+
name: "{{ cloudfall_deploy_component.service.name }}.service"
|
|
47
|
+
enabled: true
|
|
48
|
+
state: restarted
|
|
49
|
+
daemon_reload: true
|
|
50
|
+
|
|
51
|
+
- name: Require the rolled-back release to pass its health check
|
|
52
|
+
ansible.builtin.include_tasks: health_gate.yml
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Managed by Cloudfall. Local changes will be overwritten.
|
|
2
|
+
{% set command = cloudfall_deploy_component.service.command %}
|
|
3
|
+
{% set executable = command[0]
|
|
4
|
+
if command[0].startswith('/')
|
|
5
|
+
else cloudfall_deploy_component.install_root ~ '/current/' ~ command[0] %}
|
|
6
|
+
[Unit]
|
|
7
|
+
Description=Cloudfall component {{ cloudfall_deploy_component.id }}
|
|
8
|
+
After=network.target
|
|
9
|
+
|
|
10
|
+
[Service]
|
|
11
|
+
Type=exec
|
|
12
|
+
User={{ cloudfall_deploy_component.application_user }}
|
|
13
|
+
Group={{ cloudfall_deploy_component.application_user }}
|
|
14
|
+
WorkingDirectory={{ cloudfall_deploy_component.install_root }}/current
|
|
15
|
+
EnvironmentFile=-{{ cloudfall_deploy_component.install_root }}/shared/env/{{ cloudfall_deploy_component.id }}.env
|
|
16
|
+
ExecStart={{ executable }}{% for argument in command[1:] %} "{{ argument }}"{% endfor %}
|
|
17
|
+
|
|
18
|
+
Restart=on-failure
|
|
19
|
+
RestartSec=2
|
|
20
|
+
NoNewPrivileges=true
|
|
21
|
+
PrivateTmp=true
|
|
22
|
+
|
|
23
|
+
[Install]
|
|
24
|
+
WantedBy=multi-user.target
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
argument_specs:
|
|
3
|
+
main:
|
|
4
|
+
short_description: Enforce the declared Cloudfall inbound firewall
|
|
5
|
+
options:
|
|
6
|
+
cloudfall_firewall_ruleset:
|
|
7
|
+
type: dict
|
|
8
|
+
required: true
|
|
9
|
+
options:
|
|
10
|
+
policy:
|
|
11
|
+
type: str
|
|
12
|
+
choices:
|
|
13
|
+
- default-deny
|
|
14
|
+
required: true
|
|
15
|
+
allowedInbound:
|
|
16
|
+
type: list
|
|
17
|
+
elements: dict
|
|
18
|
+
required: true
|
|
19
|
+
options:
|
|
20
|
+
port:
|
|
21
|
+
type: int
|
|
22
|
+
required: true
|
|
23
|
+
protocol:
|
|
24
|
+
type: str
|
|
25
|
+
choices:
|
|
26
|
+
- tcp
|
|
27
|
+
- udp
|
|
28
|
+
required: true
|
|
29
|
+
description:
|
|
30
|
+
type: str
|
|
31
|
+
cloudfall_firewall_configuration_path:
|
|
32
|
+
type: path
|
|
33
|
+
default: /etc/nftables.conf
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
galaxy_info:
|
|
3
|
+
role_name: cloudfall_firewall
|
|
4
|
+
author: Cloudfall
|
|
5
|
+
description: Enforce a declared default-deny nftables inbound policy
|
|
6
|
+
license: AGPL-3.0-or-later
|
|
7
|
+
min_ansible_version: "2.21"
|
|
8
|
+
platforms:
|
|
9
|
+
- name: Debian
|
|
10
|
+
versions:
|
|
11
|
+
- bookworm
|
|
12
|
+
- trixie
|
|
13
|
+
dependencies: []
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
- name: Validate firewall target and declared ruleset
|
|
3
|
+
ansible.builtin.assert:
|
|
4
|
+
that:
|
|
5
|
+
- ansible_facts["distribution"] == "Debian"
|
|
6
|
+
- ansible_facts["service_mgr"] == "systemd"
|
|
7
|
+
- cloudfall_firewall_ruleset.policy == "default-deny"
|
|
8
|
+
- cloudfall_firewall_ruleset.allowedInbound | length > 0
|
|
9
|
+
fail_msg: >-
|
|
10
|
+
Cloudfall firewall management requires Debian, systemd, and a declared
|
|
11
|
+
default-deny ruleset with at least one allowed inbound rule
|
|
12
|
+
|
|
13
|
+
- name: Refuse a ruleset that would block the control connection
|
|
14
|
+
ansible.builtin.assert:
|
|
15
|
+
that:
|
|
16
|
+
- >-
|
|
17
|
+
ansible_port | default(22) | int in (
|
|
18
|
+
cloudfall_firewall_ruleset.allowedInbound
|
|
19
|
+
| selectattr('protocol', 'equalto', 'tcp')
|
|
20
|
+
| map(attribute='port')
|
|
21
|
+
| map('int')
|
|
22
|
+
| list
|
|
23
|
+
)
|
|
24
|
+
fail_msg: >-
|
|
25
|
+
Refusing to apply a firewall that does not allow the SSH control port
|
|
26
|
+
|
|
27
|
+
- name: Install nftables
|
|
28
|
+
ansible.builtin.apt:
|
|
29
|
+
name: nftables
|
|
30
|
+
state: present
|
|
31
|
+
update_cache: true
|
|
32
|
+
cache_valid_time: 3600
|
|
33
|
+
|
|
34
|
+
- name: Render the managed nftables configuration
|
|
35
|
+
ansible.builtin.template:
|
|
36
|
+
src: nftables.conf.j2
|
|
37
|
+
dest: "{{ cloudfall_firewall_configuration_path }}"
|
|
38
|
+
owner: root
|
|
39
|
+
group: root
|
|
40
|
+
mode: "0644"
|
|
41
|
+
validate: /usr/sbin/nft --check --file %s
|
|
42
|
+
notify: Reload nftables
|
|
43
|
+
|
|
44
|
+
- name: Enable and start nftables
|
|
45
|
+
ansible.builtin.systemd_service:
|
|
46
|
+
name: nftables.service
|
|
47
|
+
enabled: true
|
|
48
|
+
state: started
|
|
49
|
+
|
|
50
|
+
- name: Apply the validated nftables configuration
|
|
51
|
+
ansible.builtin.meta: flush_handlers
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/sbin/nft -f
|
|
2
|
+
# Managed by Cloudfall. Local changes will be overwritten.
|
|
3
|
+
|
|
4
|
+
flush ruleset
|
|
5
|
+
|
|
6
|
+
table inet cloudfall {
|
|
7
|
+
chain input {
|
|
8
|
+
type filter hook input priority 0; policy drop;
|
|
9
|
+
|
|
10
|
+
iif "lo" accept
|
|
11
|
+
ct state established,related accept
|
|
12
|
+
ct state invalid drop
|
|
13
|
+
ip protocol icmp accept
|
|
14
|
+
meta l4proto ipv6-icmp accept
|
|
15
|
+
{% for rule in cloudfall_firewall_ruleset.allowedInbound %}
|
|
16
|
+
{% if rule.description is defined %}
|
|
17
|
+
{{ rule.protocol }} dport {{ rule.port }} accept comment "{{ rule.description }}"
|
|
18
|
+
{% else %}
|
|
19
|
+
{{ rule.protocol }} dport {{ rule.port }} accept
|
|
20
|
+
{% endif %}
|
|
21
|
+
{% endfor %}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
chain forward {
|
|
25
|
+
type filter hook forward priority 0; policy drop;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
chain output {
|
|
29
|
+
type filter hook output priority 0; policy accept;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
argument_specs:
|
|
3
|
+
main:
|
|
4
|
+
short_description: Collect read-only Debian host evidence
|
|
5
|
+
options:
|
|
6
|
+
cloudfall_inspect_collector_version:
|
|
7
|
+
type: str
|
|
8
|
+
default: "0.2.0"
|
|
9
|
+
cloudfall_inspect_output_directory:
|
|
10
|
+
type: path
|
|
11
|
+
required: true
|
|
12
|
+
cloudfall_inspect_server_type:
|
|
13
|
+
type: dict
|
|
14
|
+
required: true
|
|
15
|
+
cloudfall_inspect_server_id:
|
|
16
|
+
type: str
|
|
17
|
+
required: true
|
|
18
|
+
cloudfall_inspect_logging_backend:
|
|
19
|
+
type: dict
|
|
20
|
+
default: {}
|
|
21
|
+
cloudfall_inspect_components:
|
|
22
|
+
type: list
|
|
23
|
+
elements: dict
|
|
24
|
+
default: []
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
galaxy_info:
|
|
3
|
+
role_name: cloudfall_inspect
|
|
4
|
+
author: Cloudfall
|
|
5
|
+
description: Collect read-only Debian host evidence for Cloudfall audits
|
|
6
|
+
license: AGPL-3.0-or-later
|
|
7
|
+
min_ansible_version: "2.21"
|
|
8
|
+
platforms:
|
|
9
|
+
- name: Debian
|
|
10
|
+
versions:
|
|
11
|
+
- bookworm
|
|
12
|
+
- trixie
|
|
13
|
+
dependencies: []
|