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.
Files changed (165) hide show
  1. cloudfall/__init__.py +19 -0
  2. cloudfall/__main__.py +5 -0
  3. cloudfall/_bundled/schemas/v1/alert-rule.schema.json +63 -0
  4. cloudfall/_bundled/schemas/v1/application.schema.json +60 -0
  5. cloudfall/_bundled/schemas/v1/artifact.schema.json +71 -0
  6. cloudfall/_bundled/schemas/v1/backup-receipt.schema.json +58 -0
  7. cloudfall/_bundled/schemas/v1/common.schema.json +115 -0
  8. cloudfall/_bundled/schemas/v1/component.schema.json +234 -0
  9. cloudfall/_bundled/schemas/v1/deployment-receipt.schema.json +53 -0
  10. cloudfall/_bundled/schemas/v1/domain.schema.json +208 -0
  11. cloudfall/_bundled/schemas/v1/environment-receipt.schema.json +55 -0
  12. cloudfall/_bundled/schemas/v1/logging-stack.schema.json +473 -0
  13. cloudfall/_bundled/schemas/v1/observed-domain.schema.json +158 -0
  14. cloudfall/_bundled/schemas/v1/observed-server.schema.json +627 -0
  15. cloudfall/_bundled/schemas/v1/operator-policy.schema.json +97 -0
  16. cloudfall/_bundled/schemas/v1/operator-proposal.schema.json +253 -0
  17. cloudfall/_bundled/schemas/v1/release-receipt.schema.json +66 -0
  18. cloudfall/_bundled/schemas/v1/server-type.schema.json +289 -0
  19. cloudfall/_bundled/schemas/v1/server.schema.json +146 -0
  20. cloudfall/_bundled/schemas/v1/service.schema.json +222 -0
  21. cloudfall/_bundled/schemas/v1/ssh-public-key.schema.json +55 -0
  22. cloudfall/agent_tools.py +868 -0
  23. cloudfall/arguments.py +92 -0
  24. cloudfall/audit.py +905 -0
  25. cloudfall/authoring.py +341 -0
  26. cloudfall/cli.py +1748 -0
  27. cloudfall/commands.py +257 -0
  28. cloudfall/cutover.py +370 -0
  29. cloudfall/dashboard.py +407 -0
  30. cloudfall/dashboard_server.py +201 -0
  31. cloudfall/domain.py +1157 -0
  32. cloudfall/importer.py +906 -0
  33. cloudfall/inventory.py +2116 -0
  34. cloudfall/lifecycle.py +874 -0
  35. cloudfall/mcp_server.py +704 -0
  36. cloudfall/migrate.py +730 -0
  37. cloudfall/observation.py +143 -0
  38. cloudfall/operations.py +1228 -0
  39. cloudfall/operator.py +1099 -0
  40. cloudfall/project.py +841 -0
  41. cloudfall/py.typed +0 -0
  42. cloudfall/render_api.py +338 -0
  43. cloudfall/resources.py +67 -0
  44. cloudfall/secrets.py +262 -0
  45. cloudfall/service_evidence.py +659 -0
  46. cloudfall/validation.py +1340 -0
  47. cloudfall-0.2.0.dist-info/METADATA +408 -0
  48. cloudfall-0.2.0.dist-info/RECORD +165 -0
  49. cloudfall-0.2.0.dist-info/WHEEL +4 -0
  50. cloudfall-0.2.0.dist-info/entry_points.txt +4 -0
  51. cloudfall-0.2.0.dist-info/licenses/LICENSE +661 -0
  52. cloudfall_engine/__init__.py +5 -0
  53. cloudfall_engine/__main__.py +5 -0
  54. cloudfall_engine/_bundled/ansible/ansible.cfg +7 -0
  55. cloudfall_engine/_bundled/ansible/playbooks/backup.yml +86 -0
  56. cloudfall_engine/_bundled/ansible/playbooks/baseline.yml +35 -0
  57. cloudfall_engine/_bundled/ansible/playbooks/bootstrap.yml +24 -0
  58. cloudfall_engine/_bundled/ansible/playbooks/data.yml +28 -0
  59. cloudfall_engine/_bundled/ansible/playbooks/deploy.yml +35 -0
  60. cloudfall_engine/_bundled/ansible/playbooks/domains.yml +21 -0
  61. cloudfall_engine/_bundled/ansible/playbooks/health.yml +31 -0
  62. cloudfall_engine/_bundled/ansible/playbooks/inspect.yml +15 -0
  63. cloudfall_engine/_bundled/ansible/playbooks/logging.yml +35 -0
  64. cloudfall_engine/_bundled/ansible/playbooks/restart.yml +31 -0
  65. cloudfall_engine/_bundled/ansible/playbooks/rollback.yml +33 -0
  66. cloudfall_engine/_bundled/ansible/playbooks/services.yml +37 -0
  67. cloudfall_engine/_bundled/ansible/playbooks/time.yml +9 -0
  68. cloudfall_engine/_bundled/ansible/roles/cloudfall_access/defaults/main.yml +2 -0
  69. cloudfall_engine/_bundled/ansible/roles/cloudfall_access/handlers/main.yml +5 -0
  70. cloudfall_engine/_bundled/ansible/roles/cloudfall_access/meta/argument_specs.yml +34 -0
  71. cloudfall_engine/_bundled/ansible/roles/cloudfall_access/meta/main.yml +13 -0
  72. cloudfall_engine/_bundled/ansible/roles/cloudfall_access/tasks/main.yml +59 -0
  73. cloudfall_engine/_bundled/ansible/roles/cloudfall_bootstrap/defaults/main.yml +11 -0
  74. cloudfall_engine/_bundled/ansible/roles/cloudfall_bootstrap/meta/argument_specs.yml +52 -0
  75. cloudfall_engine/_bundled/ansible/roles/cloudfall_bootstrap/meta/main.yml +13 -0
  76. cloudfall_engine/_bundled/ansible/roles/cloudfall_bootstrap/tasks/main.yml +99 -0
  77. cloudfall_engine/_bundled/ansible/roles/cloudfall_data_migration/defaults/main.yml +15 -0
  78. cloudfall_engine/_bundled/ansible/roles/cloudfall_data_migration/meta/argument_specs.yml +36 -0
  79. cloudfall_engine/_bundled/ansible/roles/cloudfall_data_migration/meta/main.yml +13 -0
  80. cloudfall_engine/_bundled/ansible/roles/cloudfall_data_migration/tasks/main.yml +201 -0
  81. cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/defaults/main.yml +10 -0
  82. cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/meta/argument_specs.yml +125 -0
  83. cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/meta/main.yml +13 -0
  84. cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/health.yml +3 -0
  85. cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/health_gate.yml +37 -0
  86. cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/main.yml +285 -0
  87. cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/restart.yml +17 -0
  88. cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/rollback.yml +52 -0
  89. cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/templates/component.service.j2 +24 -0
  90. cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/defaults/main.yml +2 -0
  91. cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/handlers/main.yml +5 -0
  92. cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/meta/argument_specs.yml +33 -0
  93. cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/meta/main.yml +13 -0
  94. cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/tasks/main.yml +51 -0
  95. cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/templates/nftables.conf.j2 +31 -0
  96. cloudfall_engine/_bundled/ansible/roles/cloudfall_inspect/defaults/main.yml +6 -0
  97. cloudfall_engine/_bundled/ansible/roles/cloudfall_inspect/meta/argument_specs.yml +24 -0
  98. cloudfall_engine/_bundled/ansible/roles/cloudfall_inspect/meta/main.yml +13 -0
  99. cloudfall_engine/_bundled/ansible/roles/cloudfall_inspect/tasks/main.yml +643 -0
  100. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/defaults/main.yml +6 -0
  101. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/handlers/main.yml +29 -0
  102. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/meta/argument_specs.yml +227 -0
  103. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/meta/main.yml +11 -0
  104. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/tasks/main.yml +487 -0
  105. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/alertmanager-defaults.j2 +2 -0
  106. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/alertmanager.yaml.j2 +33 -0
  107. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/grafana.ini.j2 +20 -0
  108. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/loki-datasource.yml.j2 +11 -0
  109. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/loki-systemd-override.conf.j2 +3 -0
  110. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/loki.yaml.j2 +49 -0
  111. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/nginx-loki-gateway.conf.j2 +45 -0
  112. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/prometheus-datasource.yml.j2 +11 -0
  113. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/prometheus-defaults.j2 +2 -0
  114. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/prometheus-rules.yaml.j2 +20 -0
  115. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/prometheus.yaml.j2 +24 -0
  116. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/defaults/main.yml +7 -0
  117. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/handlers/main.yml +6 -0
  118. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/meta/argument_specs.yml +122 -0
  119. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/meta/main.yml +11 -0
  120. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/tasks/main.yml +224 -0
  121. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/templates/alloy-systemd-override.conf.j2 +3 -0
  122. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/templates/config.alloy.j2 +127 -0
  123. cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/defaults/main.yml +4 -0
  124. cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/handlers/main.yml +5 -0
  125. cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/meta/argument_specs.yml +69 -0
  126. cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/meta/main.yml +13 -0
  127. cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/tasks/main.yml +212 -0
  128. cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/templates/domain-site.conf.j2 +54 -0
  129. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/defaults/main.yml +6 -0
  130. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/handlers/main.yml +9 -0
  131. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/meta/argument_specs.yml +91 -0
  132. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/meta/main.yml +13 -0
  133. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/tasks/main.yml +295 -0
  134. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/backup.service.j2 +11 -0
  135. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/backup.sh.j2 +17 -0
  136. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/backup.timer.j2 +10 -0
  137. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/cloudfall.conf.j2 +3 -0
  138. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/restore-check.service.j2 +11 -0
  139. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/restore-check.sh.j2 +27 -0
  140. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/restore-check.timer.j2 +10 -0
  141. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/defaults/main.yml +2 -0
  142. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/handlers/main.yml +6 -0
  143. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/meta/argument_specs.yml +68 -0
  144. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/meta/main.yml +13 -0
  145. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/tasks/main.yml +148 -0
  146. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/backup.service.j2 +11 -0
  147. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/backup.sh.j2 +14 -0
  148. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/backup.timer.j2 +10 -0
  149. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/cloudfall.conf.j2 +6 -0
  150. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/restore-check.service.j2 +11 -0
  151. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/restore-check.sh.j2 +15 -0
  152. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/restore-check.timer.j2 +10 -0
  153. cloudfall_engine/_bundled/ansible/roles/cloudfall_time/defaults/main.yml +2 -0
  154. cloudfall_engine/_bundled/ansible/roles/cloudfall_time/meta/argument_specs.yml +10 -0
  155. cloudfall_engine/_bundled/ansible/roles/cloudfall_time/meta/main.yml +13 -0
  156. cloudfall_engine/_bundled/ansible/roles/cloudfall_time/tasks/main.yml +119 -0
  157. cloudfall_engine/_bundled/ansible/roles/cloudfall_unattended_upgrades/defaults/main.yml +3 -0
  158. cloudfall_engine/_bundled/ansible/roles/cloudfall_unattended_upgrades/meta/argument_specs.yml +8 -0
  159. cloudfall_engine/_bundled/ansible/roles/cloudfall_unattended_upgrades/meta/main.yml +13 -0
  160. cloudfall_engine/_bundled/ansible/roles/cloudfall_unattended_upgrades/tasks/main.yml +37 -0
  161. cloudfall_engine/ansible_inventory.py +253 -0
  162. cloudfall_engine/artifact.py +258 -0
  163. cloudfall_engine/cli.py +273 -0
  164. cloudfall_engine/playbook.py +175 -0
  165. cloudfall_engine/py.typed +0 -0
@@ -0,0 +1,99 @@
1
+ ---
2
+ - name: Validate bootstrap target
3
+ ansible.builtin.assert:
4
+ that:
5
+ - ansible_facts["distribution"] == "Debian"
6
+ - ansible_facts["service_mgr"] == "systemd"
7
+ fail_msg: Cloudfall bootstrap supports Debian hosts running systemd only
8
+
9
+ - name: Install baseline operating-system packages
10
+ ansible.builtin.apt:
11
+ name: "{{ cloudfall_bootstrap_packages }}"
12
+ state: present
13
+ update_cache: true
14
+ cache_valid_time: 3600
15
+
16
+ - name: Create application root
17
+ ansible.builtin.file:
18
+ path: "{{ cloudfall_bootstrap_apps_root }}"
19
+ state: directory
20
+ owner: root
21
+ group: root
22
+ mode: "0755"
23
+
24
+ - name: Create application groups
25
+ ansible.builtin.group:
26
+ name: "{{ item.user }}"
27
+ state: present
28
+ system: true
29
+ loop: "{{ cloudfall_bootstrap_applications }}"
30
+ loop_control:
31
+ label: "{{ item.id }}"
32
+
33
+ - name: Create application service accounts
34
+ ansible.builtin.user:
35
+ name: "{{ item.user }}"
36
+ group: "{{ item.user }}"
37
+ home: "{{ cloudfall_bootstrap_apps_root }}/{{ item.id }}"
38
+ shell: /usr/sbin/nologin
39
+ create_home: false
40
+ system: true
41
+ state: present
42
+ loop: "{{ cloudfall_bootstrap_applications }}"
43
+ loop_control:
44
+ label: "{{ item.id }}"
45
+
46
+ - name: Create application roots
47
+ ansible.builtin.file:
48
+ path: "{{ cloudfall_bootstrap_apps_root }}/{{ item.id }}"
49
+ state: directory
50
+ owner: "{{ item.user }}"
51
+ group: "{{ item.user }}"
52
+ mode: "0750"
53
+ loop: "{{ cloudfall_bootstrap_applications }}"
54
+ loop_control:
55
+ label: "{{ item.id }}"
56
+
57
+ - name: Create application shared directories
58
+ ansible.builtin.file:
59
+ path: "{{ cloudfall_bootstrap_apps_root }}/{{ item.id }}/shared"
60
+ state: directory
61
+ owner: "{{ item.user }}"
62
+ group: "{{ item.user }}"
63
+ mode: "0750"
64
+ loop: "{{ cloudfall_bootstrap_applications }}"
65
+ loop_control:
66
+ label: "{{ item.id }}"
67
+
68
+ - name: Create component roots
69
+ ansible.builtin.file:
70
+ path: "{{ item.install_root }}"
71
+ state: directory
72
+ owner: "{{ item.application_user }}"
73
+ group: "{{ item.application_user }}"
74
+ mode: "0750"
75
+ loop: "{{ cloudfall_bootstrap_components }}"
76
+ loop_control:
77
+ label: "{{ item.id }}"
78
+
79
+ - name: Create component release directories
80
+ ansible.builtin.file:
81
+ path: "{{ item.install_root }}/releases"
82
+ state: directory
83
+ owner: "{{ item.application_user }}"
84
+ group: "{{ item.application_user }}"
85
+ mode: "0750"
86
+ loop: "{{ cloudfall_bootstrap_components }}"
87
+ loop_control:
88
+ label: "{{ item.id }}"
89
+
90
+ - name: Create component shared directories
91
+ ansible.builtin.file:
92
+ path: "{{ item.install_root }}/shared"
93
+ state: directory
94
+ owner: "{{ item.application_user }}"
95
+ group: "{{ item.application_user }}"
96
+ mode: "0750"
97
+ loop: "{{ cloudfall_bootstrap_components }}"
98
+ loop_control:
99
+ label: "{{ item.id }}"
@@ -0,0 +1,15 @@
1
+ ---
2
+ cloudfall_data_migration_receipt_directory: ""
3
+ cloudfall_data_migration_url_path: /root/.cloudfall-data-source-url
4
+ cloudfall_data_migration_dump_path: /var/tmp/cloudfall-data-migration.dump
5
+ # Exact per-table row counts in one deterministic query, so source and
6
+ # target evidence can be compared verbatim.
7
+ cloudfall_data_migration_count_query: >-
8
+ SELECT table_name,
9
+ (xpath('/row/c/text()',
10
+ query_to_xml(format('SELECT count(*) AS c FROM %I.%I',
11
+ table_schema, table_name),
12
+ false, true, '')))[1]::text::bigint AS rows
13
+ FROM information_schema.tables
14
+ WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
15
+ ORDER BY table_name
@@ -0,0 +1,36 @@
1
+ ---
2
+ argument_specs:
3
+ main:
4
+ short_description: Migrate one external PostgreSQL database into a declared service
5
+ options:
6
+ cloudfall_data_migration_service:
7
+ type: dict
8
+ required: true
9
+ description: The rendered declared PostgreSQL service receiving the data
10
+ cloudfall_data_migration_database:
11
+ type: str
12
+ required: true
13
+ description: Declared database name inside the service to restore into
14
+ cloudfall_data_migration_source_url_file:
15
+ type: path
16
+ required: true
17
+ description: >-
18
+ Controller-side file whose only content is the source database
19
+ connection URL; the URL itself never crosses a command line
20
+ cloudfall_data_migration_receipt_directory:
21
+ type: str
22
+ required: false
23
+ default: ""
24
+ description: Controller directory receiving the migration receipt
25
+ cloudfall_data_migration_url_path:
26
+ type: path
27
+ required: false
28
+ default: /root/.cloudfall-data-source-url
29
+ cloudfall_data_migration_dump_path:
30
+ type: path
31
+ required: false
32
+ default: /var/tmp/cloudfall-data-migration.dump
33
+ cloudfall_data_migration_count_query:
34
+ type: str
35
+ required: false
36
+ description: Deterministic per-table row-count query used on both sides
@@ -0,0 +1,13 @@
1
+ ---
2
+ galaxy_info:
3
+ role_name: cloudfall_data_migration
4
+ author: Cloudfall
5
+ description: Dump an external PostgreSQL database and restore it locally with verification
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,201 @@
1
+ ---
2
+ - name: Validate the migration target service
3
+ ansible.builtin.assert:
4
+ that:
5
+ - cloudfall_data_migration_service.serviceKind == "postgresql"
6
+ fail_msg: Data migration supports declared PostgreSQL services only
7
+
8
+ - name: Select the declared target database
9
+ ansible.builtin.set_fact:
10
+ cloudfall_data_migration_entries: >-
11
+ {{
12
+ cloudfall_data_migration_service.postgresql.databases
13
+ | selectattr('name', 'equalto', cloudfall_data_migration_database)
14
+ | list
15
+ }}
16
+
17
+ - name: Require exactly one declared database entry
18
+ ansible.builtin.assert:
19
+ that:
20
+ - cloudfall_data_migration_entries | length == 1
21
+ fail_msg: >-
22
+ Database {{ cloudfall_data_migration_database }} is not declared on
23
+ service {{ cloudfall_data_migration_service.id }}; declare it in the
24
+ config before migrating data into it
25
+
26
+ - name: Record the declared database owner
27
+ ansible.builtin.set_fact:
28
+ cloudfall_data_migration_owner: >-
29
+ {{ cloudfall_data_migration_entries[0].owner }}
30
+
31
+ - name: Migrate and verify the declared database
32
+ block:
33
+ - name: Stage the source connection URL on the target host
34
+ ansible.builtin.copy:
35
+ src: "{{ cloudfall_data_migration_source_url_file }}"
36
+ dest: "{{ cloudfall_data_migration_url_path }}"
37
+ owner: root
38
+ group: root
39
+ mode: "0600"
40
+ no_log: true
41
+
42
+ - name: Count existing tables in the target database
43
+ ansible.builtin.command:
44
+ argv:
45
+ - /usr/bin/psql
46
+ - --dbname
47
+ - "{{ cloudfall_data_migration_database }}"
48
+ - --tuples-only
49
+ - --no-align
50
+ - --command
51
+ - >-
52
+ SELECT count(*) FROM information_schema.tables
53
+ WHERE table_schema = 'public'
54
+ become: true
55
+ become_user: postgres
56
+ register: cloudfall_data_migration_target_tables
57
+ changed_when: false
58
+
59
+ - name: Refuse to restore into a non-empty database
60
+ ansible.builtin.assert:
61
+ that:
62
+ - cloudfall_data_migration_target_tables.stdout | trim == "0"
63
+ fail_msg: >-
64
+ Database {{ cloudfall_data_migration_database }} already contains
65
+ {{ cloudfall_data_migration_target_tables.stdout | trim }}
66
+ tables; data migration refuses to overwrite existing data
67
+
68
+ - name: Dump the source database on the target host
69
+ ansible.builtin.shell:
70
+ cmd: >-
71
+ pg_dump
72
+ --dbname "$(cat {{ cloudfall_data_migration_url_path | quote }})"
73
+ --no-owner --no-privileges --format custom
74
+ --file {{ cloudfall_data_migration_dump_path | quote }}
75
+ register: cloudfall_data_migration_dump
76
+ retries: 3
77
+ delay: 10
78
+ until: cloudfall_data_migration_dump.rc == 0
79
+ changed_when: true
80
+
81
+ - name: Restrict the staged dump to the postgres user
82
+ ansible.builtin.file:
83
+ path: "{{ cloudfall_data_migration_dump_path }}"
84
+ owner: postgres
85
+ group: postgres
86
+ mode: "0600"
87
+
88
+ - name: Read source table row counts
89
+ ansible.builtin.shell:
90
+ cmd: >-
91
+ psql
92
+ --dbname "$(cat {{ cloudfall_data_migration_url_path | quote }})"
93
+ --csv --tuples-only
94
+ --command {{ cloudfall_data_migration_count_query | quote }}
95
+ register: cloudfall_data_migration_source_counts
96
+ changed_when: false
97
+
98
+ - name: Require at least one source table
99
+ ansible.builtin.assert:
100
+ that:
101
+ - cloudfall_data_migration_source_counts.stdout_lines | length > 0
102
+ fail_msg: >-
103
+ The source database contains no tables in schema public; there is
104
+ nothing to migrate
105
+
106
+ - name: Restore the dump into the declared database
107
+ ansible.builtin.command:
108
+ argv:
109
+ - /usr/bin/pg_restore
110
+ - --no-owner
111
+ - --no-privileges
112
+ - --role
113
+ - "{{ cloudfall_data_migration_owner }}"
114
+ - --dbname
115
+ - "{{ cloudfall_data_migration_database }}"
116
+ - "{{ cloudfall_data_migration_dump_path }}"
117
+ become: true
118
+ become_user: postgres
119
+ changed_when: true
120
+
121
+ - name: Read restored table row counts
122
+ ansible.builtin.command:
123
+ argv:
124
+ - /usr/bin/psql
125
+ - --dbname
126
+ - "{{ cloudfall_data_migration_database }}"
127
+ - --csv
128
+ - --tuples-only
129
+ - --command
130
+ - "{{ cloudfall_data_migration_count_query }}"
131
+ become: true
132
+ become_user: postgres
133
+ register: cloudfall_data_migration_restored_counts
134
+ changed_when: false
135
+
136
+ - name: Require identical per-table row counts
137
+ ansible.builtin.assert:
138
+ that:
139
+ - >-
140
+ cloudfall_data_migration_source_counts.stdout_lines
141
+ == cloudfall_data_migration_restored_counts.stdout_lines
142
+ fail_msg: >-
143
+ Restored row counts do not match the source; source
144
+ {{ cloudfall_data_migration_source_counts.stdout_lines }}
145
+ versus restored
146
+ {{ cloudfall_data_migration_restored_counts.stdout_lines }}
147
+ always:
148
+ - name: Remove the staged source connection URL
149
+ ansible.builtin.file:
150
+ path: "{{ cloudfall_data_migration_url_path }}"
151
+ state: absent
152
+
153
+ - name: Remove the staged dump
154
+ ansible.builtin.file:
155
+ path: "{{ cloudfall_data_migration_dump_path }}"
156
+ state: absent
157
+
158
+ - name: Ensure the controller receipt directory exists
159
+ ansible.builtin.file:
160
+ path: "{{ cloudfall_data_migration_receipt_directory }}"
161
+ state: directory
162
+ mode: "0755"
163
+ delegate_to: localhost
164
+ become: false
165
+ when: cloudfall_data_migration_receipt_directory | length > 0
166
+
167
+ - name: Write the data-migration receipt on the controller
168
+ ansible.builtin.copy:
169
+ content: >-
170
+ {{
171
+ {
172
+ 'apiVersion': 'cloudfall/v1',
173
+ 'kind': 'DataMigrationReceipt',
174
+ 'metadata': {
175
+ 'id': cloudfall_data_migration_service.id
176
+ ~ '-' ~ cloudfall_data_migration_database,
177
+ 'description':
178
+ 'Data-migration receipt written by cloudfall_data_migration',
179
+ },
180
+ 'spec': {
181
+ 'service': cloudfall_data_migration_service.id,
182
+ 'database': cloudfall_data_migration_database,
183
+ 'owner': cloudfall_data_migration_owner,
184
+ 'server': cloudfall_server_id,
185
+ 'migratedAt': ansible_facts['date_time']['iso8601'],
186
+ 'tables': cloudfall_data_migration_source_counts.stdout_lines
187
+ | map('regex_replace', '^(.*),([0-9]+)$',
188
+ '{"table": "\1", "rows": \2}')
189
+ | map('from_json') | list,
190
+ 'verification': 'per-table row counts matched',
191
+ },
192
+ } | to_nice_json
193
+ }}
194
+ dest: >-
195
+ {{ cloudfall_data_migration_receipt_directory }}/{{
196
+ cloudfall_data_migration_service.id
197
+ }}-{{ cloudfall_data_migration_database }}.json
198
+ mode: "0644"
199
+ delegate_to: localhost
200
+ become: false
201
+ when: cloudfall_data_migration_receipt_directory | length > 0
@@ -0,0 +1,10 @@
1
+ ---
2
+ cloudfall_deploy_environment_file: ""
3
+ cloudfall_deploy_receipt_directory: ""
4
+ cloudfall_deploy_uv_version: "0.12.10"
5
+ cloudfall_deploy_uv_sha256: >-
6
+ 173d95a0c32d18c896c46ba6fafbf3cf9c14ab74b033f81b76c883ef492a976b
7
+ cloudfall_deploy_uv_url: >-
8
+ https://github.com/astral-sh/uv/releases/download/{{
9
+ cloudfall_deploy_uv_version
10
+ }}/uv-x86_64-unknown-linux-gnu.tar.gz
@@ -0,0 +1,125 @@
1
+ ---
2
+ argument_specs:
3
+ main:
4
+ short_description: Deploy one verified release of a declared component
5
+ options:
6
+ cloudfall_deploy_component:
7
+ type: dict
8
+ required: true
9
+ options:
10
+ id:
11
+ type: str
12
+ required: true
13
+ application:
14
+ type: str
15
+ required: true
16
+ application_user:
17
+ type: str
18
+ required: true
19
+ install_root:
20
+ type: path
21
+ required: true
22
+ repository:
23
+ type: dict
24
+ required: true
25
+ runtime:
26
+ type: dict
27
+ required: true
28
+ options:
29
+ type:
30
+ type: str
31
+ required: true
32
+ version:
33
+ type: str
34
+ required: true
35
+ packageManager:
36
+ type: str
37
+ required: true
38
+ service:
39
+ type: dict
40
+ required: true
41
+ options:
42
+ manager:
43
+ type: str
44
+ choices:
45
+ - systemd
46
+ required: true
47
+ name:
48
+ type: str
49
+ required: true
50
+ command:
51
+ type: list
52
+ elements: str
53
+ required: true
54
+ healthCheck:
55
+ type: dict
56
+ required: true
57
+ options:
58
+ type:
59
+ type: str
60
+ choices:
61
+ - http
62
+ - none
63
+ required: true
64
+ scheme:
65
+ type: str
66
+ choices:
67
+ - http
68
+ - https
69
+ port:
70
+ type: int
71
+ path:
72
+ type: str
73
+ expectedStatuses:
74
+ type: list
75
+ elements: int
76
+ timeoutSeconds:
77
+ type: int
78
+ attempts:
79
+ type: int
80
+ retainUntilCleanup:
81
+ type: bool
82
+ required: true
83
+ cloudfall_deploy_release:
84
+ type: str
85
+ required: true
86
+ cloudfall_deploy_artifact_archive:
87
+ type: str
88
+ required: true
89
+ cloudfall_deploy_artifact_sha256:
90
+ type: str
91
+ required: true
92
+ cloudfall_deploy_environment_file:
93
+ type: str
94
+ default: ""
95
+ cloudfall_deploy_receipt_directory:
96
+ type: str
97
+ default: ""
98
+ cloudfall_deploy_uv_version:
99
+ type: str
100
+ default: "0.12.10"
101
+ cloudfall_deploy_uv_sha256:
102
+ type: str
103
+ cloudfall_deploy_uv_url:
104
+ type: str
105
+ rollback:
106
+ short_description: Roll one component back to an existing release
107
+ options:
108
+ cloudfall_deploy_component:
109
+ type: dict
110
+ required: true
111
+ cloudfall_deploy_release:
112
+ type: str
113
+ required: true
114
+ restart:
115
+ short_description: Restart one component behind its health check
116
+ options:
117
+ cloudfall_deploy_component:
118
+ type: dict
119
+ required: true
120
+ health:
121
+ short_description: Probe one component's declared health check
122
+ options:
123
+ cloudfall_deploy_component:
124
+ type: dict
125
+ required: true
@@ -0,0 +1,13 @@
1
+ ---
2
+ galaxy_info:
3
+ role_name: cloudfall_deploy
4
+ author: Cloudfall
5
+ description: Deploy one verified component release with a health-gated switch
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,3 @@
1
+ ---
2
+ - name: Probe the component's declared health check
3
+ ansible.builtin.include_tasks: health_gate.yml
@@ -0,0 +1,37 @@
1
+ ---
2
+ - name: Gate the component on its declared HTTP health check
3
+ ansible.builtin.uri:
4
+ url: >-
5
+ {{ cloudfall_deploy_component.healthCheck.scheme }}://127.0.0.1:{{
6
+ cloudfall_deploy_component.healthCheck.port
7
+ }}{{ cloudfall_deploy_component.healthCheck.path }}
8
+ status_code: >-
9
+ {{ cloudfall_deploy_component.healthCheck.expectedStatuses }}
10
+ timeout: "{{ cloudfall_deploy_component.healthCheck.timeoutSeconds }}"
11
+ return_content: false
12
+ register: cloudfall_deploy_gate
13
+ retries: >-
14
+ {{ cloudfall_deploy_component.healthCheck.attempts | int - 1 }}
15
+ delay: 3
16
+ until: >-
17
+ cloudfall_deploy_gate.status
18
+ in cloudfall_deploy_component.healthCheck.expectedStatuses
19
+ when:
20
+ - not ansible_check_mode
21
+ - cloudfall_deploy_component.healthCheck.type == "http"
22
+
23
+ - name: Gate the component on an active service unit
24
+ ansible.builtin.command:
25
+ argv:
26
+ - /usr/bin/systemctl
27
+ - is-active
28
+ - --quiet
29
+ - "{{ cloudfall_deploy_component.service.name }}.service"
30
+ register: cloudfall_deploy_unit_gate
31
+ changed_when: false
32
+ retries: 4
33
+ delay: 3
34
+ until: cloudfall_deploy_unit_gate.rc == 0
35
+ when:
36
+ - not ansible_check_mode
37
+ - cloudfall_deploy_component.healthCheck.type == "none"