mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-09-08 11:17:17 +02:00
Compare commits
11 Commits
140572a0a4
...
c9a7830953
Author | SHA1 | Date | |
---|---|---|---|
c9a7830953 | |||
53e5c563ae | |||
0b3b3a810a | |||
6d14f16dfd | |||
632d922977 | |||
26b29debc0 | |||
0c4cd283c4 | |||
5d36a806ff | |||
84de85d905 | |||
457f3659fa | |||
4c7ee0441e |
@@ -7,8 +7,8 @@
|
||||
- name: "For '{{ application_id }}': Load database variables"
|
||||
include_vars: "{{ item }}"
|
||||
loop:
|
||||
- "{{ DOCKER_VARS_FILE }}" # Important to load docker variables first so that database can use them
|
||||
- "{{ cmp_db_docker_vars_file_db }}" # Important to load them before docker role so that backup can use them
|
||||
- "{{ DOCKER_VARS_FILE }}" # Important to load docker variables first so that database can use them
|
||||
- "{{ DATABASE_VARS_FILE }}" # Important to load them before docker role so that backup can use them
|
||||
|
||||
- name: "For '{{ application_id }}': Load central RDBMS"
|
||||
include_role:
|
||||
|
@@ -1 +1 @@
|
||||
cmp_db_docker_vars_file_db: "{{ playbook_dir }}/roles/cmp-rdbms/vars/database.yml"
|
||||
DATABASE_VARS_FILE: "{{ playbook_dir }}/roles/cmp-rdbms/vars/database.yml"
|
@@ -16,6 +16,10 @@
|
||||
login_password: "{{ applications | get_app_conf(application_id, 'credentials.postgres_password', True) }}"
|
||||
login_host: "{{ postgres_local_host }}"
|
||||
login_port: "{{ postgres_port }}"
|
||||
register: postgresql_result
|
||||
until: postgresql_result is succeeded
|
||||
retries: "{{ postgres_retry_retries }}"
|
||||
delay: "{{ postgres_retry_delay }}"
|
||||
|
||||
# 2) Create the database user (with password)
|
||||
- name: "Create database user: {{ database_username }}"
|
||||
@@ -28,6 +32,10 @@
|
||||
login_password: "{{ applications | get_app_conf(application_id, 'credentials.postgres_password', True) }}"
|
||||
login_host: "{{ postgres_local_host }}"
|
||||
login_port: "{{ postgres_port }}"
|
||||
register: postgresql_result
|
||||
until: postgresql_result is succeeded
|
||||
retries: "{{ postgres_retry_retries }}"
|
||||
delay: "{{ postgres_retry_delay }}"
|
||||
|
||||
# 3) Enable LOGIN for the role (removes NOLOGIN)
|
||||
- name: "Enable login for role {{ database_username }}"
|
||||
@@ -40,12 +48,16 @@
|
||||
query: |
|
||||
ALTER ROLE "{{ database_username }}"
|
||||
WITH LOGIN;
|
||||
register: postgresql_result
|
||||
until: postgresql_result is succeeded
|
||||
retries: "{{ postgres_retry_retries }}"
|
||||
delay: "{{ postgres_retry_delay }}"
|
||||
|
||||
# 4) Grant ALL privileges on all tables in the public schema
|
||||
- name: "Grant ALL privileges on tables in public schema to {{ database_username }}"
|
||||
community.postgresql.postgresql_privs:
|
||||
db: "{{ database_name }}"
|
||||
role: "{{ database_username }}"
|
||||
roles: "{{ database_username }}"
|
||||
objs: ALL_IN_SCHEMA
|
||||
privs: ALL
|
||||
type: table
|
||||
@@ -55,12 +67,16 @@
|
||||
login_password: "{{ applications | get_app_conf(application_id, 'credentials.postgres_password', True) }}"
|
||||
login_host: "{{ postgres_local_host }}"
|
||||
login_port: "{{ postgres_port }}"
|
||||
register: postgresql_result
|
||||
until: postgresql_result is succeeded
|
||||
retries: "{{ postgres_retry_retries }}"
|
||||
delay: "{{ postgres_retry_delay }}"
|
||||
|
||||
# 5) Grant ALL privileges at the database level
|
||||
- name: "Grant all privileges on database {{ database_name }} to {{ database_username }}"
|
||||
community.postgresql.postgresql_privs:
|
||||
db: "{{ database_name }}"
|
||||
role: "{{ database_username }}"
|
||||
roles: "{{ database_username }}"
|
||||
type: database
|
||||
privs: ALL
|
||||
state: present
|
||||
@@ -68,6 +84,10 @@
|
||||
login_password: "{{ applications | get_app_conf(application_id, 'credentials.postgres_password', True) }}"
|
||||
login_host: "{{ postgres_local_host }}"
|
||||
login_port: "{{ postgres_port }}"
|
||||
register: postgresql_result
|
||||
until: postgresql_result is succeeded
|
||||
retries: "{{ postgres_retry_retries }}"
|
||||
delay: "{{ postgres_retry_delay }}"
|
||||
|
||||
# 6) Grant USAGE/CREATE on schema and set default privileges
|
||||
- name: "Set comprehensive schema privileges for {{ database_username }}"
|
||||
@@ -82,6 +102,10 @@
|
||||
GRANT CREATE ON SCHEMA public TO "{{ database_username }}";
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||||
GRANT ALL PRIVILEGES ON TABLES TO "{{ database_username }}";
|
||||
register: postgresql_result
|
||||
until: postgresql_result is succeeded
|
||||
retries: "{{ postgres_retry_retries }}"
|
||||
delay: "{{ postgres_retry_delay }}"
|
||||
|
||||
# 7) Ensure PostGIS and related extensions are installed (if enabled)
|
||||
- name: "Ensure PostGIS-related extensions are installed"
|
||||
@@ -98,6 +122,10 @@
|
||||
- pg_trgm
|
||||
- unaccent
|
||||
when: postgres_gis_enabled | bool
|
||||
register: postgresql_result
|
||||
until: postgresql_result is succeeded
|
||||
retries: "{{ postgres_retry_retries }}"
|
||||
delay: "{{ postgres_retry_delay }}"
|
||||
|
||||
# 8) Ensure pgvector (vector) extension is installed (for Discourse‑AI, pgvector, …)
|
||||
- name: "Ensure pgvector (vector) extension is installed"
|
||||
@@ -109,3 +137,7 @@
|
||||
login_password: "{{ applications | get_app_conf(application_id, 'credentials.postgres_password', True) }}"
|
||||
login_host: "{{ postgres_local_host }}"
|
||||
login_port: "{{ postgres_port }}"
|
||||
register: postgresql_result
|
||||
until: postgresql_result is succeeded
|
||||
retries: "{{ postgres_retry_retries }}"
|
||||
delay: "{{ postgres_retry_delay }}"
|
||||
|
@@ -10,4 +10,4 @@
|
||||
|
||||
- name: "Initialize database for '{{ database_name }}'"
|
||||
include_tasks: 02_init.yml
|
||||
when: "{{ postgres_init }}"
|
||||
when: postgres_init | bool
|
@@ -20,4 +20,6 @@ postgres_init: "{{ database_username is defined and database_pa
|
||||
postgres_expose_local: True # Exposes the db to localhost, almost everytime neccessary
|
||||
postgres_custom_image_name: "postgres_custom"
|
||||
postgres_local_host: "127.0.0.1"
|
||||
postgres_pg_vector_enabled: True # Required by discourse, propably in a later step it makes sense to define this as a configuration option in config/main.yml
|
||||
postgres_pg_vector_enabled: True # Required by discourse, propably in a later step it makes sense to define this as a configuration option in config/main.yml
|
||||
postgres_retry_retries: 5
|
||||
postgres_retry_delay: 2
|
@@ -2,7 +2,7 @@
|
||||
application_id: "svc-prx-openresty"
|
||||
|
||||
# Deactivate Database for openresty
|
||||
database_enabled: false
|
||||
database_type: ""
|
||||
|
||||
# Openresty
|
||||
openresty_image: "openresty/openresty"
|
||||
|
@@ -1 +0,0 @@
|
||||
database_enabled: "{{ database_type | default('') | bool }}" # Enables the database backup
|
@@ -1,12 +1,12 @@
|
||||
- block:
|
||||
- name: "pkgmgr install {{ bkp_docker_to_local_pkg }}"
|
||||
- name: "pkgmgr install {{ bkp_docker_2_loc_pkg }}"
|
||||
include_role:
|
||||
name: pkgmgr-install
|
||||
vars:
|
||||
package_name: "{{ bkp_docker_to_local_pkg }}"
|
||||
package_name: "{{ bkp_docker_2_loc_pkg }}"
|
||||
|
||||
- name: "Retrieve {{ bkp_docker_to_local_pkg }} path from pkgmgr"
|
||||
command: "pkgmgr path {{ bkp_docker_to_local_pkg }}"
|
||||
- name: "Retrieve {{ bkp_docker_2_loc_pkg }} path from pkgmgr"
|
||||
command: "pkgmgr path {{ bkp_docker_2_loc_pkg }}"
|
||||
register: pkgmgr_output
|
||||
changed_when: false
|
||||
|
||||
@@ -16,4 +16,4 @@
|
||||
changed_when: false
|
||||
when: backup_docker_to_local_folder is not defined
|
||||
vars:
|
||||
bkp_docker_to_local_pkg: backup-docker-to-local
|
||||
bkp_docker_2_loc_pkg: backup-docker-to-local
|
||||
|
@@ -52,10 +52,10 @@
|
||||
database_name is defined and
|
||||
database_username is defined and
|
||||
database_password is defined) and
|
||||
run_once_bkp_docker_to_local_file_permission is not defined
|
||||
run_once_bkp_docker_2_loc_file_permission is not defined
|
||||
register: file_permission_result
|
||||
|
||||
- name: run the backup_docker_to_local_file_permission tasks once
|
||||
set_fact:
|
||||
run_once_bkp_docker_to_local_file_permission: true
|
||||
when: run_once_bkp_docker_to_local_file_permission is not defined and file_permission_result is defined and file_permission_result.changed
|
||||
run_once_bkp_docker_2_loc_file_permission: true
|
||||
when: run_once_bkp_docker_2_loc_file_permission is not defined and file_permission_result is defined and file_permission_result.changed
|
@@ -3,9 +3,8 @@
|
||||
- include_tasks: utils/run_once.yml
|
||||
when:
|
||||
- run_once_sys_bkp_docker_2_loc is not defined
|
||||
- database_enabled | bool
|
||||
|
||||
- name: "include 04_seed-database-to-backup.yml"
|
||||
include_tasks: 04_seed-database-to-backup.yml
|
||||
when:
|
||||
- database_enabled | bool
|
||||
- bkp_docker_2_loc_db_enabled | bool
|
||||
|
@@ -5,5 +5,5 @@ OnFailure=sys-alm-compose.infinito@%n.service sys-cln-faild-bkps.infinito.servic
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStartPre=/bin/sh -c '/usr/bin/python {{ path_system_lock_script }} {{ system_maintenance_services | join(' ') }} --ignore {{ system_maintenance_backup_services | reject('equalto', 'sys-bkp-docker-2-loc') | join(' ') }} --timeout "{{system_maintenance_lock_timeout_backup_services}}"'
|
||||
ExecStart=/bin/sh -c '{{ bkp_docker_to_local_exec }} --everything'
|
||||
ExecStart=/bin/sh -c '{{ bkp_docker_2_loc_exec }} --everything'
|
||||
ExecStartPost=/bin/sh -c '/bin/systemctl start sys-rpr-docker-soft.infinito.service &'
|
@@ -5,5 +5,5 @@ OnFailure=sys-alm-compose.infinito@%n.service sys-cln-faild-bkps.infinito.servic
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStartPre=/bin/sh -c '/usr/bin/python {{ path_system_lock_script }} {{ system_maintenance_services | join(' ') }} --ignore {{ system_maintenance_backup_services | reject('equalto', 'sys-bkp-docker-2-loc-everything') | join(' ') }} --timeout "{{system_maintenance_lock_timeout_backup_services}}"'
|
||||
ExecStart=/bin/sh -c '{{ bkp_docker_to_local_exec }}'
|
||||
ExecStart=/bin/sh -c '{{ bkp_docker_2_loc_exec }}'
|
||||
ExecStartPost=/bin/sh -c '/bin/systemctl start sys-rpr-docker-soft.infinito.service &'
|
@@ -1,43 +1,46 @@
|
||||
# Mapping logic for backup-docker-to-local CLI arguments
|
||||
#
|
||||
# - bkp_docker_to_local_database_routine: All service names where backup.database_routine is set (for --database-containers)
|
||||
# - bkp_docker_to_local_no_stop_required: All images where backup.no_stop_required is set (for --images-no-stop-required)
|
||||
# - bkp_docker_to_local_disabled: All images where backup.disabled is set (for --images-no-backup-required)
|
||||
# - bkp_docker_2_loc_database_routine: All service names where backup.database_routine is set (for --database-containers)
|
||||
# - bkp_docker_2_loc_no_stop_required: All images where backup.no_stop_required is set (for --images-no-stop-required)
|
||||
# - bkp_docker_2_loc_disabled: All images where backup.disabled is set (for --images-no-backup-required)
|
||||
# CLI-ready variables render these lists as argument strings.
|
||||
|
||||
# Verify if DB is enabled
|
||||
bkp_docker_2_loc_db_enabled: "{{ database_type | default('') | bool }}"
|
||||
|
||||
# Gather mapped values as lists
|
||||
bkp_docker_to_local_database_routine: >-
|
||||
bkp_docker_2_loc_database_routine: >-
|
||||
{{ applications | find_dock_val_by_bkp_entr('database_routine', 'name') | list }}
|
||||
|
||||
bkp_docker_to_local_no_stop_required: >-
|
||||
bkp_docker_2_loc_no_stop_required: >-
|
||||
{{ applications | find_dock_val_by_bkp_entr('no_stop_required', 'image') | list }}
|
||||
|
||||
bkp_docker_to_local_disabled: >-
|
||||
bkp_docker_2_loc_disabled: >-
|
||||
{{ applications | find_dock_val_by_bkp_entr('disabled', 'image') | list }}
|
||||
|
||||
# CLI argument strings (only set if list not empty)
|
||||
bkp_docker_to_local_database_routine_cli: >-
|
||||
{% if bkp_docker_to_local_database_routine | length > 0 -%}
|
||||
--database-containers {{ bkp_docker_to_local_database_routine | join(' ') }}
|
||||
bkp_docker_2_loc_database_routine_cli: >-
|
||||
{% if bkp_docker_2_loc_database_routine | length > 0 -%}
|
||||
--database-containers {{ bkp_docker_2_loc_database_routine | join(' ') }}
|
||||
{%- endif %}
|
||||
|
||||
bkp_docker_to_local_no_stop_required_cli: >-
|
||||
{% if bkp_docker_to_local_no_stop_required | length > 0 -%}
|
||||
--images-no-stop-required {{ bkp_docker_to_local_no_stop_required | join(' ') }}
|
||||
bkp_docker_2_loc_no_stop_required_cli: >-
|
||||
{% if bkp_docker_2_loc_no_stop_required | length > 0 -%}
|
||||
--images-no-stop-required {{ bkp_docker_2_loc_no_stop_required | join(' ') }}
|
||||
{%- endif %}
|
||||
|
||||
bkp_docker_to_local_disabled_cli: >-
|
||||
{% if bkp_docker_to_local_disabled | length > 0 -%}
|
||||
--images-no-backup-required {{ bkp_docker_to_local_disabled | join(' ') }}
|
||||
bkp_docker_2_loc_disabled_cli: >-
|
||||
{% if bkp_docker_2_loc_disabled | length > 0 -%}
|
||||
--images-no-backup-required {{ bkp_docker_2_loc_disabled | join(' ') }}
|
||||
{%- endif %}
|
||||
|
||||
# List of CLI args for convenience (e.g. for looping or joining)
|
||||
bkp_docker_to_local_cli_args_list:
|
||||
- "{{ bkp_docker_to_local_database_routine_cli }}"
|
||||
- "{{ bkp_docker_to_local_no_stop_required_cli }}"
|
||||
- "{{ bkp_docker_to_local_disabled_cli }}"
|
||||
bkp_docker_2_loc_cli_args_list:
|
||||
- "{{ bkp_docker_2_loc_database_routine_cli }}"
|
||||
- "{{ bkp_docker_2_loc_no_stop_required_cli }}"
|
||||
- "{{ bkp_docker_2_loc_disabled_cli }}"
|
||||
|
||||
bkp_docker_to_local_exec: >-
|
||||
bkp_docker_2_loc_exec: >-
|
||||
/usr/bin/python {{ backup_docker_to_local_folder }}backup-docker-to-local.py
|
||||
--compose-dir {{ path_docker_compose_instances }}
|
||||
{{ bkp_docker_to_local_cli_args_list | select('string') | join(' ') }}
|
||||
{{ bkp_docker_2_loc_cli_args_list | select('string') | join(' ') }}
|
@@ -1,23 +1,24 @@
|
||||
|
||||
- name: "reset (if enabled)"
|
||||
include_tasks: reset.yml
|
||||
include_tasks: 01_reset.yml
|
||||
when: mode_reset | bool and run_once_sys_timer is not defined
|
||||
|
||||
- name: create {{service_name}}.infinito.timer
|
||||
template:
|
||||
src: dummy.timer.j2
|
||||
dest: "/etc/systemd/system/{{service_name}}.infinito.timer"
|
||||
register: dummy_timer
|
||||
|
||||
- name: "restart timer"
|
||||
systemd:
|
||||
daemon_reload: yes
|
||||
name: "{{service_name}}.infinito.timer"
|
||||
state: restarted
|
||||
enabled: yes
|
||||
when: dummy_timer.changed or activate_all_timers | bool
|
||||
|
||||
- name: run {{ role_name }} once
|
||||
set_fact:
|
||||
run_once_sys_timer: true
|
||||
when: run_once_sys_timer is not defined
|
||||
|
||||
- name: create {{ sys_timer_file }}
|
||||
template:
|
||||
src: dummy.timer.j2
|
||||
dest: "/etc/systemd/system/{{ sys_timer_file }}"
|
||||
register: dummy_timer
|
||||
|
||||
- name: "restart timer"
|
||||
systemd:
|
||||
daemon_reload: yes
|
||||
name: "{{ sys_timer_file }}"
|
||||
state: restarted
|
||||
enabled: yes
|
||||
when: dummy_timer.changed or activate_all_timers | bool
|
||||
|
||||
|
1
roles/sys-timer/vars/main.yml
Normal file
1
roles/sys-timer/vars/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
sys_timer_file: "{{service_name}}.infinito.timer"
|
@@ -16,8 +16,8 @@ docker:
|
||||
image: "baserow/baserow"
|
||||
version: "latest"
|
||||
name: "baserow"
|
||||
volumes:
|
||||
data: "baserow_data"
|
||||
volumes:
|
||||
data: "baserow_data"
|
||||
server:
|
||||
domains:
|
||||
canonical:
|
||||
|
@@ -1,5 +1,5 @@
|
||||
# Public URL
|
||||
BASEROW_PUBLIC_URL=https://{{ domain }}
|
||||
BASEROW_PUBLIC_URL={{ domains | get_url(application_id, WEB_PROTOCOL) }}
|
||||
|
||||
# Email Server Configuration
|
||||
EMAIL_SMTP={{ system_email.smtp | upper }}
|
||||
@@ -12,7 +12,7 @@ EMAIL_SMTP_USE_TLS={{ system_email.tls | upper }}
|
||||
DATABASE_USER={{ database_username }}
|
||||
DATABASE_NAME={{ database_name }}
|
||||
DATABASE_HOST={{ database_host }}
|
||||
DATABASE_PORT={{database_port}}
|
||||
DATABASE_PORT={{ database_port }}
|
||||
DATABASE_PASSWORD={{ database_password }}
|
||||
|
||||
REDIS_URL=redis://redis:6379
|
||||
|
@@ -15,4 +15,4 @@ discourse_redis_host: "{{ application_id |get_entity_name }}-r
|
||||
|
||||
# General Docker Configuration
|
||||
docker_repository_directory : "{{ docker_compose.directories.services}}{{applications | get_app_conf( application_id, 'repository') }}/"
|
||||
docker_compose_flush_handlers: false
|
||||
docker_compose_flush_handlers: true
|
@@ -3,7 +3,7 @@ database_type: "postgres"
|
||||
|
||||
container_port: "{{ applications | get_app_conf(application_id, 'docker.services.listmonk.port', True) }}"
|
||||
|
||||
# Docker specific
|
||||
# Docker
|
||||
docker_compose_flush_handlers: false
|
||||
|
||||
# Listmonk Specific
|
||||
|
@@ -1,5 +1,8 @@
|
||||
# General
|
||||
application_id: "web-app-mastodon"
|
||||
database_type: "postgres"
|
||||
|
||||
# Mastodon Specific
|
||||
mastodon_version: "{{ applications | get_app_conf(application_id, 'docker.services.mastodon.version', True) }}"
|
||||
mastodon_image: "{{ applications | get_app_conf(application_id, 'docker.services.mastodon.image', True) }}"
|
||||
mastodon_name: "{{ applications | get_app_conf(application_id, 'docker.services.mastodon.name', True) }}"
|
||||
|
@@ -4,7 +4,7 @@ application_id: "web-app-matrix"
|
||||
database_type: "postgres"
|
||||
registration_file_folder: "/data/"
|
||||
|
||||
# Matrix specific
|
||||
# Matrix
|
||||
matrix_synapse_version: "{{ applications | get_app_conf(application_id, 'docker.services.synapse.version', True) }}"
|
||||
matrix_synapse_image: "{{ applications | get_app_conf(application_id, 'docker.services.synapse.image', True) }}"
|
||||
matrix_synapse_name: "{{ applications | get_app_conf(application_id, 'docker.services.synapse.name', True) }}"
|
||||
@@ -17,4 +17,7 @@ matrix_project: "{{ application_id | get_entity_name }}"
|
||||
# Webserver
|
||||
well_known_directory: "{{nginx.directories.data.well_known}}/matrix/"
|
||||
location_upload: "~ ^/_matrix/media/v3/"
|
||||
client_max_body_size: "{{ applications | get_app_conf(application_id, 'server.client_max_body_size') }}"
|
||||
client_max_body_size: "{{ applications | get_app_conf(application_id, 'server.client_max_body_size') }}"
|
||||
|
||||
# Docker
|
||||
docker_compose_flush_handlers: false
|
@@ -36,4 +36,11 @@ server:
|
||||
- "mig.{{ primary_domain }}"
|
||||
aliases:
|
||||
- "meta-infinite-graph.{{ primary_domain }}"
|
||||
build_data: true # Enables the building of the meta data which the graph requiers
|
||||
|
||||
build_data:
|
||||
# This shouldn't be relevant anymore, because the data is anyhow build async in background
|
||||
# Enables the building of the meta data which the graph requiers
|
||||
enabled: true
|
||||
|
||||
# Recommended to set this to false in inventory to optimize speed
|
||||
wait_for: true
|
||||
|
@@ -20,12 +20,6 @@
|
||||
include_role:
|
||||
name: cmp-docker-proxy
|
||||
|
||||
- name: Create tree
|
||||
command: "infinito build tree --no-signal --alarm-timeout 0 -s {{ mig_roles_meta_volume }}"
|
||||
when:
|
||||
- mig_build_data
|
||||
|
||||
- name: Create roles list
|
||||
command: "infinito build roles_list --no-signal --alarm-timeout 0 -o {{ mig_roles_meta_list }}"
|
||||
when:
|
||||
- mig_build_data
|
||||
- name: Build data (single async task)
|
||||
include_tasks: 02_build_data.yml
|
||||
when: mig_build_data | bool
|
||||
|
38
roles/web-app-mig/tasks/02_build_data.yml
Normal file
38
roles/web-app-mig/tasks/02_build_data.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
- name: Build data (single async task)
|
||||
shell: |
|
||||
set -euo pipefail
|
||||
infinito build tree --no-signal --alarm-timeout 0 -s {{ mig_roles_meta_volume }}
|
||||
infinito build roles_list --no-signal --alarm-timeout 0 -o {{ mig_roles_meta_list }}
|
||||
async: 3600
|
||||
poll: 0
|
||||
register: mig_build_job
|
||||
|
||||
- name: Fail if MIG build job did not start
|
||||
fail:
|
||||
msg: >
|
||||
MIG build job failed to start. No job ID returned.
|
||||
when: mig_build_job.ansible_job_id is not defined
|
||||
|
||||
- name: Debug MIG build job ID
|
||||
debug:
|
||||
msg: "MIG build job started with ID: {{ mig_build_job.ansible_job_id }}"
|
||||
when: enable_debug | bool
|
||||
|
||||
- debug:
|
||||
msg: "Waiting for MIG build job to finish. Set 'build_data.wait_for=false' in the application config to skip waiting and improve performance."
|
||||
when: mig_wait_for_build | bool
|
||||
|
||||
- name: Wait for MIG build job to finish (enforce failure)
|
||||
async_status:
|
||||
jid: "{{ mig_build_job.ansible_job_id }}"
|
||||
register: mig_build_result
|
||||
until: mig_build_result.finished
|
||||
retries: 360
|
||||
delay: 10
|
||||
when:
|
||||
- mig_wait_for_build | bool
|
||||
failed_when:
|
||||
- mig_build_result.result is defined
|
||||
- mig_build_result.result.rc is defined
|
||||
- mig_build_result.result.rc != 0
|
||||
|
@@ -1,12 +1,13 @@
|
||||
# General
|
||||
application_id: web-app-mig # ID of the application, should be the name of the role folder
|
||||
application_id: web-app-mig
|
||||
|
||||
# Docker
|
||||
docker_compose_flush_handlers: true
|
||||
docker_pull_git_repository: true
|
||||
docker_repository_address: "https://github.com/kevinveenbirkenbach/meta-infinite-graph"
|
||||
docker_compose_flush_handlers: true
|
||||
docker_pull_git_repository: true
|
||||
docker_repository_address: "https://github.com/kevinveenbirkenbach/meta-infinite-graph"
|
||||
|
||||
# Helper variables
|
||||
mig_image: "mig:latest"
|
||||
mig_container: "mig"
|
||||
mig_build_data: "{{ applications | get_app_conf(application_id, 'build_data') }}"
|
||||
mig_image: "mig:latest"
|
||||
mig_container: "mig"
|
||||
mig_build_data: "{{ applications | get_app_conf(application_id, 'build_data.enabled') }}"
|
||||
mig_wait_for_build: "{{ applications | get_app_conf(application_id, 'build_data.wait_for') }}"
|
@@ -1,9 +1,15 @@
|
||||
# General
|
||||
application_id: web-app-mobilizon
|
||||
container_port: 4000
|
||||
|
||||
# Database
|
||||
database_type: "postgres"
|
||||
postgres_gis_enabled: true
|
||||
|
||||
container_port: 4000
|
||||
# Docker
|
||||
docker_compose_flush_handlers: false
|
||||
|
||||
# Mobilizon
|
||||
mobilizon_host_conf_exs_file: "{{docker_compose.directories.config}}config.exs"
|
||||
mobilizon_version: "{{ applications | get_app_conf(application_id, 'docker.services.mobilizon.version', True) }}"
|
||||
mobilizon_image: "{{ applications | get_app_conf(application_id, 'docker.services.mobilizon.image', True) }}"
|
||||
|
@@ -2,7 +2,7 @@
|
||||
application_id: "web-app-peertube"
|
||||
database_type: "postgres"
|
||||
|
||||
# Docker Specific
|
||||
# Docker
|
||||
docker_compose_flush_handlers: true
|
||||
|
||||
# Role variables
|
||||
|
@@ -1,5 +1,5 @@
|
||||
- name: "load variables from {{ cmp_db_docker_vars_file_db }}"
|
||||
include_vars: "{{ cmp_db_docker_vars_file_db }}"
|
||||
- name: "load variables from {{ DATABASE_VARS_FILE }}"
|
||||
include_vars: "{{ DATABASE_VARS_FILE }}"
|
||||
|
||||
- name: "loading database configuration variables"
|
||||
include_vars:
|
||||
|
@@ -9,11 +9,10 @@
|
||||
include_role:
|
||||
name: srv-web-7-6-composer
|
||||
vars:
|
||||
domain: "{{ domains | get_domain(application_id) }}"
|
||||
http_port: "{{ ports.localhost.http[application_id] }}"
|
||||
|
||||
- name: "generate {{domains | get_domain(application_id)}}.conf"
|
||||
- name: "generate '{{ CDN_NGINX_FILE }}'"
|
||||
template:
|
||||
src: "nginx.conf.j2"
|
||||
dest: "{{ nginx.directories.http.servers }}{{ domains | get_domain(application_id) }}.conf"
|
||||
dest: "{{ nginx.directories.http.servers }}{{ CDN_NGINX_FILE }}"
|
||||
notify: restart openresty
|
@@ -1,6 +1,6 @@
|
||||
server
|
||||
{
|
||||
server_name {{domains | get_domain(application_id)}};
|
||||
server_name {{ domains | get_domain(application_id) }};
|
||||
|
||||
{% include 'roles/srv-web-7-7-letsencrypt/templates/ssl_header.j2' %}
|
||||
|
||||
|
@@ -1,2 +1,6 @@
|
||||
# General
|
||||
application_id: "web-svc-cdn"
|
||||
domain: "{{ domains | get_domain(application_id) }}"
|
||||
|
||||
# CDN
|
||||
CDN_NGINX_FILE: "{{ domain }}.conf"
|
||||
|
114
tests/integration/test_handler_names_static.py
Normal file
114
tests/integration/test_handler_names_static.py
Normal file
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Integration test: ensure no Jinja variables are used in handler *names*.
|
||||
|
||||
Why this policy?
|
||||
- Handler identifiers should be stable strings. If you ever notify by handler
|
||||
name (instead of a dedicated `listen:` key), a templated name can fail to
|
||||
resolve or silently not match what `notify` referenced.
|
||||
- Even when `listen:` is used (recommended), variable-laden names make logs and
|
||||
tooling brittle and can trigger undefined-variable errors at parse/run time.
|
||||
- Keeping handler names static improves reliability, debuggability, and
|
||||
compatibility with analysis tools.
|
||||
|
||||
Allowed:
|
||||
- You may still template other fields or use `listen:` for dynamic trigger
|
||||
routing; just keep the handler’s `name` static text.
|
||||
|
||||
This test scans: roles/*/handlers/main.yml
|
||||
"""
|
||||
|
||||
import os
|
||||
import glob
|
||||
import re
|
||||
import unittest
|
||||
|
||||
try:
|
||||
import yaml # PyYAML
|
||||
except ImportError as exc:
|
||||
raise SystemExit(
|
||||
"PyYAML is required to run this test. Install with: pip install pyyaml"
|
||||
) from exc
|
||||
|
||||
|
||||
JINJA_VAR_PATTERN = re.compile(r"{{.*?}}") # minimal check for any templating
|
||||
|
||||
|
||||
def _iter_tasks(node):
|
||||
"""
|
||||
Yield all task-like dicts from a loaded YAML node, descending into common
|
||||
task containers (`block`, `rescue`, `always`), just in case.
|
||||
"""
|
||||
if isinstance(node, dict):
|
||||
# If this dict looks like a task (has 'name' or a module key), yield it.
|
||||
if any(k in node for k in ("name", "action")):
|
||||
yield node
|
||||
|
||||
# Dive into known task containers (handlers can include blocks too).
|
||||
for key in ("block", "rescue", "always"):
|
||||
if key in node and isinstance(node[key], list):
|
||||
for item in node[key]:
|
||||
yield from _iter_tasks(item)
|
||||
|
||||
elif isinstance(node, list):
|
||||
for item in node:
|
||||
yield from _iter_tasks(item)
|
||||
|
||||
|
||||
class StaticHandlerNamesTest(unittest.TestCase):
|
||||
"""
|
||||
Ensures handler names are static strings (no Jinja variables like {{ ... }}).
|
||||
"""
|
||||
|
||||
def test_no_templated_names_in_handlers(self):
|
||||
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
|
||||
pattern = os.path.join(project_root, "roles", "*", "handlers", "main.yml")
|
||||
|
||||
violations = []
|
||||
|
||||
for handler_path in sorted(glob.glob(pattern)):
|
||||
# Load possibly multi-document YAML safely
|
||||
try:
|
||||
with open(handler_path, "r", encoding="utf-8") as f:
|
||||
docs = list(yaml.safe_load_all(f))
|
||||
except FileNotFoundError:
|
||||
continue
|
||||
except yaml.YAMLError as e:
|
||||
violations.append(
|
||||
f"{handler_path} -> YAML parse error: {e}"
|
||||
)
|
||||
continue
|
||||
|
||||
for doc in docs:
|
||||
for task in _iter_tasks(doc):
|
||||
name = task.get("name")
|
||||
if not isinstance(name, str):
|
||||
# ignore unnamed or non-string names
|
||||
continue
|
||||
if JINJA_VAR_PATTERN.search(name):
|
||||
# Compose a clear, actionable message
|
||||
listen = task.get("listen")
|
||||
listen_hint = (
|
||||
""
|
||||
if listen
|
||||
else " Consider using a static handler name and, if you need flexible triggers, add a static `listen:` key that your tasks `notify`."
|
||||
)
|
||||
violations.append(
|
||||
f"{handler_path} -> Handler name contains variables: {name!r}\n"
|
||||
"Reason: Handler names must be static. Using Jinja variables in the name "
|
||||
"can break handler resolution (when notified by name), produces unstable logs, "
|
||||
"and may cause undefined-variable errors. Keep the handler `name` constant."
|
||||
f"{listen_hint}"
|
||||
)
|
||||
|
||||
if violations:
|
||||
self.fail(
|
||||
"Templated handler names are not allowed.\n\n"
|
||||
+ "\n\n".join(violations)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
Reference in New Issue
Block a user