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,212 @@
1
+ ---
2
+ - name: Validate domain target and declaration
3
+ ansible.builtin.assert:
4
+ that:
5
+ - ansible_facts["distribution"] == "Debian"
6
+ - ansible_facts["service_mgr"] == "systemd"
7
+ - cloudfall_nginx_site_domain.proxy.server == cloudfall_server_id
8
+ - cloudfall_nginx_site_domain.proxy.upstream is defined
9
+ - cloudfall_nginx_site_domain.tls.mode in ["disabled", "required"]
10
+ fail_msg: >-
11
+ Domain deployment requires its declared Debian proxy server and a
12
+ declared upstream endpoint
13
+
14
+ - name: Install Nginx and Certbot
15
+ ansible.builtin.apt:
16
+ name:
17
+ - nginx
18
+ - certbot
19
+ state: present
20
+ update_cache: true
21
+ cache_valid_time: 3600
22
+
23
+ - name: Create the ACME challenge webroot
24
+ ansible.builtin.file:
25
+ path: "{{ cloudfall_nginx_site_acme_webroot }}"
26
+ state: directory
27
+ owner: root
28
+ group: root
29
+ mode: "0755"
30
+
31
+ - name: Inspect existing certificate material
32
+ ansible.builtin.stat:
33
+ path: >-
34
+ /etc/letsencrypt/live/{{
35
+ cloudfall_nginx_site_domain.primaryName
36
+ }}/fullchain.pem
37
+ follow: true
38
+ get_checksum: false
39
+ register: cloudfall_nginx_site_certificate
40
+
41
+ # The TLS server block references certificate files, so on a host without
42
+ # them the vhost first goes live HTTP-only. That serves the ACME challenge
43
+ # location, which is exactly what webroot issuance needs; the vhost is
44
+ # re-rendered with TLS after the certificate exists.
45
+ - name: Determine whether TLS can be enabled before issuance
46
+ ansible.builtin.set_fact:
47
+ cloudfall_nginx_site_tls_active: >-
48
+ {{
49
+ cloudfall_nginx_site_domain.tls.mode == "required"
50
+ and cloudfall_nginx_site_certificate.stat.exists
51
+ }}
52
+
53
+ - name: Render the managed virtual host
54
+ ansible.builtin.template:
55
+ src: domain-site.conf.j2
56
+ dest: "{{ cloudfall_nginx_site_domain.proxy.configurationPath }}"
57
+ owner: root
58
+ group: root
59
+ mode: "0644"
60
+ notify: Reload Nginx
61
+
62
+ - name: Enable the managed virtual host
63
+ ansible.builtin.file:
64
+ src: "{{ cloudfall_nginx_site_domain.proxy.configurationPath }}"
65
+ dest: >-
66
+ /etc/nginx/sites-enabled/{{
67
+ cloudfall_nginx_site_domain.proxy.configurationPath | basename
68
+ }}
69
+ state: link
70
+ notify: Reload Nginx
71
+ when: >-
72
+ cloudfall_nginx_site_domain.proxy.configurationPath.startswith(
73
+ '/etc/nginx/sites-available/'
74
+ )
75
+
76
+ - name: Enable Nginx
77
+ ansible.builtin.systemd_service:
78
+ name: nginx.service
79
+ enabled: true
80
+ state: started
81
+
82
+ - name: Validate the Nginx configuration before issuance
83
+ ansible.builtin.command:
84
+ argv:
85
+ - /usr/sbin/nginx
86
+ - -t
87
+ changed_when: false
88
+ when: not ansible_check_mode
89
+
90
+ - name: Serve the ACME challenge location before issuance
91
+ ansible.builtin.meta: flush_handlers
92
+
93
+ - name: Issue the declared certificate
94
+ ansible.builtin.command:
95
+ argv: >-
96
+ {{
97
+ [
98
+ '/usr/bin/certbot', 'certonly', '--webroot',
99
+ '--webroot-path', cloudfall_nginx_site_acme_webroot,
100
+ '--non-interactive', '--agree-tos',
101
+ '--register-unsafely-without-email',
102
+ '--cert-name', cloudfall_nginx_site_domain.primaryName,
103
+ ]
104
+ + (
105
+ [cloudfall_nginx_site_domain.primaryName]
106
+ + cloudfall_nginx_site_domain.aliases
107
+ )
108
+ | map('regex_replace', '^', '--domain=')
109
+ | list
110
+ }}
111
+ changed_when: true
112
+ when:
113
+ - not ansible_check_mode
114
+ - cloudfall_nginx_site_domain.tls.mode == "required"
115
+ - cloudfall_nginx_site_issue_certificate
116
+ - not cloudfall_nginx_site_certificate.stat.exists
117
+
118
+ - name: Re-inspect certificate material after issuance
119
+ ansible.builtin.stat:
120
+ path: >-
121
+ /etc/letsencrypt/live/{{
122
+ cloudfall_nginx_site_domain.primaryName
123
+ }}/fullchain.pem
124
+ follow: true
125
+ get_checksum: false
126
+ register: cloudfall_nginx_site_certificate
127
+
128
+ - name: Determine whether TLS can be enabled
129
+ ansible.builtin.set_fact:
130
+ cloudfall_nginx_site_tls_active: >-
131
+ {{
132
+ cloudfall_nginx_site_domain.tls.mode == "required"
133
+ and cloudfall_nginx_site_certificate.stat.exists
134
+ }}
135
+
136
+ - name: Render the managed virtual host with issued material
137
+ ansible.builtin.template:
138
+ src: domain-site.conf.j2
139
+ dest: "{{ cloudfall_nginx_site_domain.proxy.configurationPath }}"
140
+ owner: root
141
+ group: root
142
+ mode: "0644"
143
+ notify: Reload Nginx
144
+
145
+ - name: Enable the certificate renewal timer
146
+ ansible.builtin.systemd_service:
147
+ name: certbot.timer
148
+ enabled: true
149
+ state: started
150
+ when: cloudfall_nginx_site_domain.tls.mode == "required"
151
+
152
+ - name: Validate the complete Nginx configuration
153
+ ansible.builtin.command:
154
+ argv:
155
+ - /usr/sbin/nginx
156
+ - -t
157
+ changed_when: false
158
+ when: not ansible_check_mode
159
+
160
+ - name: Apply the validated virtual host
161
+ ansible.builtin.meta: flush_handlers
162
+
163
+ - name: Hash the applied configuration for the receipt
164
+ ansible.builtin.stat:
165
+ path: "{{ cloudfall_nginx_site_domain.proxy.configurationPath }}"
166
+ checksum_algorithm: sha256
167
+ register: cloudfall_nginx_site_applied
168
+ when:
169
+ - not ansible_check_mode
170
+ - cloudfall_nginx_site_receipt_directory | length > 0
171
+
172
+ - name: Ensure the controller receipt directory exists
173
+ ansible.builtin.file:
174
+ path: "{{ cloudfall_nginx_site_receipt_directory }}"
175
+ state: directory
176
+ mode: "0755"
177
+ delegate_to: localhost
178
+ become: false
179
+ when: cloudfall_nginx_site_receipt_directory | length > 0
180
+
181
+ - name: Write the deployment receipt on the controller
182
+ ansible.builtin.copy:
183
+ content: >-
184
+ {{
185
+ {
186
+ 'apiVersion': 'cloudfall/v1',
187
+ 'kind': 'DeploymentReceipt',
188
+ 'metadata': {
189
+ 'id': cloudfall_nginx_site_domain.id,
190
+ 'description': 'Deployment receipt written by cloudfall_nginx_site',
191
+ },
192
+ 'spec': {
193
+ 'domain': cloudfall_nginx_site_domain.id,
194
+ 'server': cloudfall_server_id,
195
+ 'deployedAt': ansible_facts['date_time']['iso8601'],
196
+ 'configurationPath':
197
+ cloudfall_nginx_site_domain.proxy.configurationPath,
198
+ 'configurationSha256':
199
+ cloudfall_nginx_site_applied.stat.checksum,
200
+ },
201
+ } | to_nice_json
202
+ }}
203
+ dest: >-
204
+ {{ cloudfall_nginx_site_receipt_directory }}/{{
205
+ cloudfall_nginx_site_domain.id
206
+ }}.json
207
+ mode: "0644"
208
+ delegate_to: localhost
209
+ become: false
210
+ when:
211
+ - not ansible_check_mode
212
+ - cloudfall_nginx_site_receipt_directory | length > 0
@@ -0,0 +1,54 @@
1
+ # Managed by Cloudfall. Local changes will be overwritten.
2
+ {% set names = (
3
+ [cloudfall_nginx_site_domain.primaryName]
4
+ + cloudfall_nginx_site_domain.aliases
5
+ ) | join(' ') %}
6
+ {% set upstream_address = cloudfall_nginx_site_domain.proxy.upstream.address %}
7
+ {% set upstream_host = '[' ~ upstream_address ~ ']'
8
+ if ':' in upstream_address else upstream_address %}
9
+ {% macro proxy_location() %}
10
+ location / {
11
+ proxy_http_version 1.1;
12
+ proxy_pass http://{{ upstream_host }}:{{ cloudfall_nginx_site_domain.proxy.upstream.port }};
13
+ proxy_pass_request_headers on;
14
+ proxy_set_header Host $host;
15
+ proxy_set_header X-Real-IP $remote_addr;
16
+ proxy_set_header X-Forwarded-For $remote_addr;
17
+ proxy_set_header X-Forwarded-Proto $scheme;
18
+ proxy_set_header X-Forwarded-Host $host;
19
+ proxy_set_header X-Forwarded-Port $server_port;
20
+ }
21
+ {% endmacro %}
22
+ server {
23
+ listen 80;
24
+ listen [::]:80;
25
+ server_name {{ names }};
26
+
27
+ location /.well-known/acme-challenge/ {
28
+ root {{ cloudfall_nginx_site_acme_webroot }};
29
+ }
30
+
31
+ {% if cloudfall_nginx_site_tls_active %}
32
+ location / {
33
+ return 301 https://$host$request_uri;
34
+ }
35
+ {% else %}
36
+ {{ proxy_location() -}}
37
+ {% endif %}
38
+ }
39
+ {% if cloudfall_nginx_site_tls_active %}
40
+
41
+ server {
42
+ listen 443 ssl;
43
+ listen [::]:443 ssl;
44
+ http2 on;
45
+ server_name {{ names }};
46
+
47
+ ssl_certificate /etc/letsencrypt/live/{{ cloudfall_nginx_site_domain.primaryName }}/fullchain.pem;
48
+ ssl_certificate_key /etc/letsencrypt/live/{{ cloudfall_nginx_site_domain.primaryName }}/privkey.pem;
49
+ ssl_protocols TLSv1.2 TLSv1.3;
50
+ ssl_session_tickets off;
51
+
52
+ {{ proxy_location() -}}
53
+ }
54
+ {% endif %}
@@ -0,0 +1,6 @@
1
+ ---
2
+ cloudfall_postgresql_metrics_role: alloy
3
+ cloudfall_postgresql_repository_key_url: >-
4
+ https://www.postgresql.org/media/keys/ACCC4CF8.asc
5
+ cloudfall_postgresql_repository_key_path: /etc/apt/keyrings/postgresql.asc
6
+ cloudfall_postgresql_repository_url: https://apt.postgresql.org/pub/repos/apt
@@ -0,0 +1,9 @@
1
+ ---
2
+ - name: Restart PostgreSQL
3
+ ansible.builtin.systemd_service:
4
+ name: >-
5
+ postgresql@{{
6
+ cloudfall_postgresql_service.postgresql.majorVersion
7
+ }}-main.service
8
+ state: restarted
9
+ daemon_reload: true
@@ -0,0 +1,91 @@
1
+ ---
2
+ argument_specs:
3
+ main:
4
+ short_description: Converge one declared PostgreSQL service
5
+ options:
6
+ cloudfall_postgresql_service:
7
+ type: dict
8
+ required: true
9
+ options:
10
+ id:
11
+ type: str
12
+ required: true
13
+ serviceKind:
14
+ type: str
15
+ choices:
16
+ - postgresql
17
+ required: true
18
+ environment:
19
+ type: str
20
+ required: true
21
+ server:
22
+ type: str
23
+ required: true
24
+ bind:
25
+ type: dict
26
+ required: true
27
+ options:
28
+ address:
29
+ type: str
30
+ choices:
31
+ - 127.0.0.1
32
+ required: true
33
+ port:
34
+ type: int
35
+ required: true
36
+ postgresql:
37
+ type: dict
38
+ required: true
39
+ options:
40
+ majorVersion:
41
+ type: str
42
+ required: true
43
+ packageVersion:
44
+ type: str
45
+ databases:
46
+ type: list
47
+ elements: dict
48
+ required: true
49
+ options:
50
+ name:
51
+ type: str
52
+ required: true
53
+ application:
54
+ type: str
55
+ required: true
56
+ owner:
57
+ type: str
58
+ required: true
59
+ backup:
60
+ type: dict
61
+ required: true
62
+ options:
63
+ directory:
64
+ type: path
65
+ required: true
66
+ onCalendar:
67
+ type: str
68
+ required: true
69
+ restoreCheckOnCalendar:
70
+ type: str
71
+ retentionDays:
72
+ type: int
73
+ required: true
74
+ metrics:
75
+ type: dict
76
+ options:
77
+ enabled:
78
+ type: bool
79
+ required: true
80
+ cloudfall_postgresql_metrics_role:
81
+ type: str
82
+ default: alloy
83
+ cloudfall_postgresql_repository_key_url:
84
+ type: str
85
+ default: https://www.postgresql.org/media/keys/ACCC4CF8.asc
86
+ cloudfall_postgresql_repository_key_path:
87
+ type: path
88
+ default: /etc/apt/keyrings/postgresql.asc
89
+ cloudfall_postgresql_repository_url:
90
+ type: str
91
+ default: https://apt.postgresql.org/pub/repos/apt
@@ -0,0 +1,13 @@
1
+ ---
2
+ galaxy_info:
3
+ role_name: cloudfall_postgresql
4
+ author: Cloudfall
5
+ description: Install a declared loopback PostgreSQL service with backups
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,295 @@
1
+ ---
2
+ - name: Validate PostgreSQL service target and declaration
3
+ ansible.builtin.assert:
4
+ that:
5
+ - ansible_facts["distribution"] == "Debian"
6
+ - ansible_facts["service_mgr"] == "systemd"
7
+ - cloudfall_postgresql_service.serviceKind == "postgresql"
8
+ - cloudfall_postgresql_service.server == cloudfall_server_id
9
+ - cloudfall_postgresql_service.bind.address == "127.0.0.1"
10
+ - cloudfall_postgresql_service.postgresql.databases | length > 0
11
+ fail_msg: >-
12
+ PostgreSQL deployment requires its declared Debian server and a
13
+ loopback-only bind address
14
+
15
+ - name: Validate the declared backup calendar expression
16
+ ansible.builtin.command:
17
+ argv:
18
+ - /usr/bin/systemd-analyze
19
+ - calendar
20
+ - "{{ cloudfall_postgresql_service.backup.onCalendar }}"
21
+ changed_when: false
22
+
23
+ - name: Install PostgreSQL repository prerequisites
24
+ ansible.builtin.apt:
25
+ name:
26
+ - ca-certificates
27
+ - gnupg
28
+ - python3-debian
29
+ state: present
30
+ update_cache: true
31
+ cache_valid_time: 3600
32
+
33
+ - name: Create APT keyring directory
34
+ ansible.builtin.file:
35
+ path: /etc/apt/keyrings
36
+ state: directory
37
+ owner: root
38
+ group: root
39
+ mode: "0755"
40
+
41
+ - name: Install PostgreSQL repository signing key
42
+ ansible.builtin.get_url:
43
+ url: "{{ cloudfall_postgresql_repository_key_url }}"
44
+ dest: "{{ cloudfall_postgresql_repository_key_path }}"
45
+ owner: root
46
+ group: root
47
+ mode: "0644"
48
+
49
+ - name: Configure the PGDG package repository
50
+ ansible.builtin.deb822_repository:
51
+ name: postgresql
52
+ types:
53
+ - deb
54
+ uris:
55
+ - "{{ cloudfall_postgresql_repository_url }}"
56
+ suites:
57
+ - "{{ ansible_facts['distribution_release'] }}-pgdg"
58
+ components:
59
+ - main
60
+ signed_by: "{{ cloudfall_postgresql_repository_key_path }}"
61
+ state: present
62
+
63
+ - name: Install the pinned PostgreSQL server
64
+ ansible.builtin.apt:
65
+ name: >-
66
+ {{
67
+ 'postgresql-' ~ cloudfall_postgresql_service.postgresql.majorVersion
68
+ ~ '=' ~ cloudfall_postgresql_service.postgresql.packageVersion
69
+ if 'packageVersion' in cloudfall_postgresql_service.postgresql
70
+ else 'postgresql-'
71
+ ~ cloudfall_postgresql_service.postgresql.majorVersion
72
+ }}
73
+ state: present
74
+ update_cache: true
75
+ cache_valid_time: 3600
76
+ policy_rc_d: 101
77
+
78
+ - name: Install the managed cluster configuration
79
+ ansible.builtin.template:
80
+ src: cloudfall.conf.j2
81
+ dest: >-
82
+ /etc/postgresql/{{
83
+ cloudfall_postgresql_service.postgresql.majorVersion
84
+ }}/main/conf.d/cloudfall.conf
85
+ owner: root
86
+ group: postgres
87
+ mode: "0640"
88
+ notify: Restart PostgreSQL
89
+
90
+ - name: Enable and start the PostgreSQL cluster
91
+ ansible.builtin.systemd_service:
92
+ name: >-
93
+ postgresql@{{
94
+ cloudfall_postgresql_service.postgresql.majorVersion
95
+ }}-main.service
96
+ enabled: true
97
+ state: started
98
+ daemon_reload: true
99
+
100
+ - name: Apply the validated cluster configuration
101
+ ansible.builtin.meta: flush_handlers
102
+
103
+ - name: Wait for PostgreSQL readiness
104
+ ansible.builtin.command:
105
+ argv:
106
+ - /usr/bin/pg_isready
107
+ - --quiet
108
+ - --host
109
+ - "{{ cloudfall_postgresql_service.bind.address }}"
110
+ - --port
111
+ - "{{ cloudfall_postgresql_service.bind.port }}"
112
+ register: cloudfall_postgresql_readiness
113
+ changed_when: false
114
+ retries: 12
115
+ delay: 5
116
+ until: cloudfall_postgresql_readiness.rc == 0
117
+ when: not ansible_check_mode
118
+
119
+ - name: Read existing PostgreSQL roles
120
+ ansible.builtin.command:
121
+ argv:
122
+ - /usr/bin/psql
123
+ - --port
124
+ - "{{ cloudfall_postgresql_service.bind.port }}"
125
+ - --tuples-only
126
+ - --no-align
127
+ - --command
128
+ - SELECT rolname FROM pg_roles
129
+ become: true
130
+ become_user: postgres
131
+ register: cloudfall_postgresql_existing_roles
132
+ changed_when: false
133
+ when: not ansible_check_mode
134
+
135
+ - name: Create missing application login roles
136
+ ansible.builtin.command:
137
+ argv:
138
+ - /usr/bin/psql
139
+ - --port
140
+ - "{{ cloudfall_postgresql_service.bind.port }}"
141
+ - --command
142
+ - CREATE ROLE "{{ item }}" LOGIN
143
+ become: true
144
+ become_user: postgres
145
+ loop: >-
146
+ {{
147
+ cloudfall_postgresql_service.postgresql.databases
148
+ | map(attribute='owner')
149
+ | unique
150
+ | list
151
+ }}
152
+ when:
153
+ - not ansible_check_mode
154
+ - item not in cloudfall_postgresql_existing_roles.stdout_lines
155
+ changed_when: true
156
+
157
+ - name: Create the metrics monitoring role
158
+ ansible.builtin.command:
159
+ argv:
160
+ - /usr/bin/psql
161
+ - --port
162
+ - "{{ cloudfall_postgresql_service.bind.port }}"
163
+ - --command
164
+ - CREATE ROLE "{{ cloudfall_postgresql_metrics_role }}" LOGIN
165
+ become: true
166
+ become_user: postgres
167
+ when:
168
+ - not ansible_check_mode
169
+ - "'metrics' in cloudfall_postgresql_service"
170
+ - >-
171
+ cloudfall_postgresql_metrics_role
172
+ not in cloudfall_postgresql_existing_roles.stdout_lines
173
+ changed_when: true
174
+
175
+ - name: Grant pg_monitor to the metrics monitoring role
176
+ ansible.builtin.command:
177
+ argv:
178
+ - /usr/bin/psql
179
+ - --port
180
+ - "{{ cloudfall_postgresql_service.bind.port }}"
181
+ - --command
182
+ - GRANT pg_monitor TO "{{ cloudfall_postgresql_metrics_role }}"
183
+ become: true
184
+ become_user: postgres
185
+ when:
186
+ - not ansible_check_mode
187
+ - "'metrics' in cloudfall_postgresql_service"
188
+ changed_when: false
189
+
190
+ - name: Read existing PostgreSQL databases
191
+ ansible.builtin.command:
192
+ argv:
193
+ - /usr/bin/psql
194
+ - --port
195
+ - "{{ cloudfall_postgresql_service.bind.port }}"
196
+ - --tuples-only
197
+ - --no-align
198
+ - --command
199
+ - SELECT datname FROM pg_database
200
+ become: true
201
+ become_user: postgres
202
+ register: cloudfall_postgresql_existing_databases
203
+ changed_when: false
204
+ when: not ansible_check_mode
205
+
206
+ - name: Create missing application databases
207
+ ansible.builtin.command:
208
+ argv:
209
+ - /usr/bin/psql
210
+ - --port
211
+ - "{{ cloudfall_postgresql_service.bind.port }}"
212
+ - --command
213
+ - CREATE DATABASE "{{ item.name }}" OWNER "{{ item.owner }}"
214
+ become: true
215
+ become_user: postgres
216
+ loop: "{{ cloudfall_postgresql_service.postgresql.databases }}"
217
+ loop_control:
218
+ label: "{{ item.name }}"
219
+ when:
220
+ - not ansible_check_mode
221
+ - item.name not in cloudfall_postgresql_existing_databases.stdout_lines
222
+ changed_when: true
223
+
224
+ - name: Create the backup directory
225
+ ansible.builtin.file:
226
+ path: "{{ cloudfall_postgresql_service.backup.directory }}"
227
+ state: directory
228
+ owner: postgres
229
+ group: postgres
230
+ mode: "0700"
231
+
232
+ - name: Install the backup script
233
+ ansible.builtin.template:
234
+ src: backup.sh.j2
235
+ dest: /usr/local/sbin/cloudfall-postgresql-backup
236
+ owner: root
237
+ group: root
238
+ mode: "0755"
239
+
240
+ - name: Install the restore-check script
241
+ ansible.builtin.template:
242
+ src: restore-check.sh.j2
243
+ dest: /usr/local/sbin/cloudfall-postgresql-restore-check
244
+ owner: root
245
+ group: root
246
+ mode: "0755"
247
+
248
+ - name: Install the backup service unit
249
+ ansible.builtin.template:
250
+ src: backup.service.j2
251
+ dest: /etc/systemd/system/cloudfall-postgresql-backup.service
252
+ owner: root
253
+ group: root
254
+ mode: "0644"
255
+
256
+ - name: Install the backup timer unit
257
+ ansible.builtin.template:
258
+ src: backup.timer.j2
259
+ dest: /etc/systemd/system/cloudfall-postgresql-backup.timer
260
+ owner: root
261
+ group: root
262
+ mode: "0644"
263
+
264
+ - name: Enable the backup timer
265
+ ansible.builtin.systemd_service:
266
+ name: cloudfall-postgresql-backup.timer
267
+ enabled: true
268
+ state: started
269
+ daemon_reload: true
270
+
271
+ - name: Install the restore-drill service unit
272
+ ansible.builtin.template:
273
+ src: restore-check.service.j2
274
+ dest: /etc/systemd/system/cloudfall-postgresql-restore-check.service
275
+ owner: root
276
+ group: root
277
+ mode: "0644"
278
+ when: cloudfall_postgresql_service.backup.restoreCheckOnCalendar is defined
279
+
280
+ - name: Install the restore-drill timer unit
281
+ ansible.builtin.template:
282
+ src: restore-check.timer.j2
283
+ dest: /etc/systemd/system/cloudfall-postgresql-restore-check.timer
284
+ owner: root
285
+ group: root
286
+ mode: "0644"
287
+ when: cloudfall_postgresql_service.backup.restoreCheckOnCalendar is defined
288
+
289
+ - name: Enable the restore-drill timer
290
+ ansible.builtin.systemd_service:
291
+ name: cloudfall-postgresql-restore-check.timer
292
+ enabled: true
293
+ state: started
294
+ daemon_reload: true
295
+ when: cloudfall_postgresql_service.backup.restoreCheckOnCalendar is defined
@@ -0,0 +1,11 @@
1
+ # Managed by Cloudfall. Local changes will be overwritten.
2
+ [Unit]
3
+ Description=Cloudfall PostgreSQL backup for {{ cloudfall_postgresql_service.id }}
4
+ After=postgresql@{{ cloudfall_postgresql_service.postgresql.majorVersion }}-main.service
5
+ Requires=postgresql@{{ cloudfall_postgresql_service.postgresql.majorVersion }}-main.service
6
+
7
+ [Service]
8
+ Type=oneshot
9
+ User=postgres
10
+ Group=postgres
11
+ ExecStart=/usr/local/sbin/cloudfall-postgresql-backup