From bb9026bb6ae304fe117a94b5acabe222234d1ddb Mon Sep 17 00:00:00 2001 From: uumas Date: Mon, 21 Sep 2026 05:50:50 +0300 Subject: [PATCH] service: Template file improvements --- roles/service/meta/argument_specs.yaml | 5 +- roles/service/tasks/host_mounts.yaml | 27 ++-- roles/service/vars/main/mounts.yaml | 118 +++++++++--------- roles/service/vars/main/secrets.yaml | 58 ++++----- .../service/vars/main/template_validate.yaml | 24 ++++ 5 files changed, 126 insertions(+), 106 deletions(-) create mode 100644 roles/service/vars/main/template_validate.yaml diff --git a/roles/service/meta/argument_specs.yaml b/roles/service/meta/argument_specs.yaml index ab3d361..ea1b725 100644 --- a/roles/service/meta/argument_specs.yaml +++ b/roles/service/meta/argument_specs.yaml @@ -163,9 +163,7 @@ argument_specs: - Mount source. - If mount type is volume, name of the volume. - If mount type is bind, host path to bind mount inside the container. - - >- - If mount type is template and template_directory is false, the name of the template file, must end in .j2. - If template_directory is true, this is the destination directory + - If mount type is template, the name of the template file, must end in .j2. - If mount type is copy, name of the file or directory to copy. Directory name must end in /. type: str required: true @@ -337,6 +335,7 @@ argument_specs: host on the default port. - The database user will be - The password will be accessible as secret at /run/secrets/ + - A postgres url is accessible as secret at /run/secrets/postgres-url - > The password will also be available as the service_podman_secrets['-'] variable. diff --git a/roles/service/tasks/host_mounts.yaml b/roles/service/tasks/host_mounts.yaml index df4abab..6b3e8e9 100644 --- a/roles/service/tasks/host_mounts.yaml +++ b/roles/service/tasks/host_mounts.yaml @@ -13,28 +13,29 @@ - name: Create service template mount directories ansible.builtin.file: - path: "{{ _service_host_directory }}/mounts/{{ item }}" + path: "{{ item }}" state: directory mode: "0700" - loop: "{{ _service_all_template_mount_directories }}" + loop: "{{ _service_all_template_mount_host_files | map('dirname') | unique }}" - name: Template files for template mounts ansible.builtin.template: - src: "{{ item[0].source }}" - dest: "{{ item[1] }}" - mode: "{{ item[0].mode | default('0644') }}" - validate: "{{ validate if item[0].template_validate_command is defined else omit }}" + src: "{{ item.source }}" + dest: "{{ item.hostfile }}" + mode: "{{ item.mode | default('0644') }}" + validate: "{{ validate if item.template_validate_command is defined else omit }}" notify: >- {{ 'Reload' if service_container_reload_method != 'none' else 'Restart' }} container service {{ service_name }} - loop: "{{ _service_all_template_mounts | zip(_service_all_template_mount_host_files) }}" + loop: "{{ _service_all_template_mounts_full }}" vars: - validate: >- - podman run --rm - -v %s:{{ item[0].destination }}:ro - --entrypoint {{ item[0].template_validate_command.split(' ', 1)[0] }} - {{ service_container_image }} - {{ item[0].template_validate_command.split(' ', 1)[1] }} + validate: "{{ _service_template_validate_command }}" + +- name: Ensure no legacy templated files present + ansible.builtin.file: + path: "{{ _service_host_directory }}/mounts/{{ item.source | regex_replace('\\.j2$', '') }}" + state: absent + loop: "{{ _service_all_template_mounts_full }}" - name: Copy files for copy mounts ansible.builtin.copy: diff --git a/roles/service/vars/main/mounts.yaml b/roles/service/vars/main/mounts.yaml index c6fe041..bd3dbd4 100644 --- a/roles/service/vars/main/mounts.yaml +++ b/roles/service/vars/main/mounts.yaml @@ -26,39 +26,54 @@ _service_container_bind_mounts: >- ([ _service_container_socket_mount ] if _service_native_socket else []) }} -_service_template_mounts_withdefaults: >- +_service_template_mounts_hostfiles: >- + {{ + _service_template_mounts + | map(attribute='destination') + | map('dirname') + | map('replace', '/', '_') + | map('regex_replace', '^', _service_host_directory ~ '/mounts/') + | zip( + _service_template_mounts + | map(attribute='destination') + | map('basename') + ) + | map('path_join') + | map('community.general.dict_kv', 'hostfile') + }} +_service_template_mounts_full: >- {{ ([{'readonly': true, 'template_directory': false}] * _service_template_mounts | length) - | zip(_service_template_mounts) + | zip( + _service_template_mounts, + _service_template_mounts_hostfiles + ) | map('combine') }} -_service_template_mounts_plain: "{{ _service_template_mounts_withdefaults | rejectattr('template_directory') }}" +_service_template_mounts_plain: "{{ _service_template_mounts_full | rejectattr('template_directory') }}" _service_container_template_mounts_plain: >- {{ _service_template_mounts_plain - | community.general.remove_keys(['mode', 'template_validate_command', 'template_directory']) + | community.general.remove_keys(['mode', 'template_validate_command', 'template_directory', 'hostfile']) | zip( _service_template_mounts_plain - | map(attribute='source') - | map('regex_replace', '\.j2$', '') - | map('regex_replace', '^', _service_host_directory ~ '/mounts/') + | map(attribute='hostfile') | map('community.general.dict_kv', 'source'), - ([{'type': 'bind'}] * _service_template_mounts | length) + ([{'type': 'bind'}] * _service_template_mounts_plain | length) ) | map('combine') }} -_service_template_mounts_directory: "{{ _service_template_mounts_withdefaults | selectattr('template_directory') }}" +_service_template_mounts_directory: "{{ _service_template_mounts_full | selectattr('template_directory') }}" _service_container_template_mounts_directory: >- {{ _service_template_mounts_directory - | community.general.remove_keys(['mode', 'template_validate_command', 'template_directory']) + | community.general.remove_keys(['mode', 'template_validate_command', 'template_directory', 'hostfile']) | zip( _service_template_mounts_directory - | map(attribute='destination') - | map('replace', '/', '_') - | map('regex_replace', '^', _service_host_directory ~ '/mounts/') + | map(attribute='hostfile') + | map('dirname') | map('community.general.dict_kv', 'source'), - ([{'type': 'bind'}] * _service_template_mounts | length) + ([{'type': 'bind'}] * _service_template_mounts_directory | length) ) | map('combine') | unique @@ -93,57 +108,38 @@ _service_container_mounts: >- _service_all_template_mounts: >- {{ ( - _service_template_mounts + - ( - _service_additional_containers | - map(attribute='mounts', default=[]) | - flatten + _service_template_mounts + + ( + _service_additional_containers + | map(attribute='mounts', default=[]) + | flatten ) - ) | - selectattr('type', '==', 'template') | - unique - }} -_service_all_template_mounts_withdefaults: >- - {{ - ([{'readonly': true, 'template_directory': false}] * _service_all_template_mounts | length) - | zip(_service_all_template_mounts) - | map('combine') - }} -_service_all_template_mounts_plain: "{{ _service_all_template_mounts_withdefaults | rejectattr('template_directory') }}" -_service_all_template_mounts_directory: "{{ _service_all_template_mounts_withdefaults | selectattr('template_directory') }}" -_service_all_template_mount_directories: >- - {{ - ( - _service_all_template_mounts_plain - | map(attribute='source') - | map('dirname') - | unique - | select('!=', '') - ) + ( - _service_all_template_mounts_directory - | map(attribute='destination') - | map('replace', '/', '_') ) + | selectattr('type', '==', 'template') + | unique }} _service_all_template_mount_host_files: >- {{ - ( - _service_all_template_mounts_plain - | map(attribute='source') - | map('regex_replace', '\.j2$', '') - | map('regex_replace', '^', _service_host_directory ~ '/mounts/') - ) + ( - _service_all_template_mounts_directory + _service_all_template_mounts + | map(attribute='destination') + | map('dirname') + | map('replace', '/', '_') + | map('regex_replace', '^', _service_host_directory ~ '/mounts/') + | zip( + _service_all_template_mounts | map(attribute='destination') - | map('replace', '/', '_') - | map('regex_replace', '^', _service_host_directory ~ '/mounts/') - | zip( - _service_all_template_mounts_directory - | map(attribute='source') - | map('regex_replace', '\.j2$', '') - ) | map('path_join') + | map('basename') ) - + | map('path_join') + }} +_service_all_template_mounts_full: >- + {{ + ([{'readonly': true, 'template_directory': false}] * _service_all_template_mounts | length) + | zip( + _service_all_template_mounts, + _service_all_template_mount_host_files | map('community.general.dict_kv', 'hostfile') + ) + | map('combine') }} _service_all_copy_mounts: >- @@ -161,7 +157,7 @@ _service_all_copy_mounts: >- }} _service_all_copy_mount_host_files: >- {{ - _service_all_copy_mounts | - map(attribute='source') | - map('regex_replace', '^', _service_host_directory ~ '/mounts/') + _service_all_copy_mounts + | map(attribute='source') + | map('regex_replace', '^', _service_host_directory ~ '/mounts/') }} diff --git a/roles/service/vars/main/secrets.yaml b/roles/service/vars/main/secrets.yaml index ad9d141..8fb0f11 100644 --- a/roles/service/vars/main/secrets.yaml +++ b/roles/service/vars/main/secrets.yaml @@ -2,34 +2,34 @@ _service_container_secrets: >- {{ service_container_secrets + | map(attribute='name') + | map('community.general.dict_kv', 'target') + | zip( + service_container_secrets, + service_container_secrets | map(attribute='name') - | map('community.general.dict_kv', 'target') - | zip( - service_container_secrets, - service_container_secrets - | map(attribute='name') - | map('regex_replace', '^', service_name ~ '-') - | map('community.general.dict_kv', 'name') - ) - | map('combine') - + ( - [{ - 'name': _service_database_name, - 'type': service_database_secret_type, - 'target': service_database_secret_target - }] if _service_setup_database else [] - ) - + ( - [{ - 'name': _service_database_name ~ '-url', - 'value': - 'postgres://' - ~ service_name | replace('-', '_') - ~ ':' ~ service_podman_secrets[service_name ~ '-postgres'] - ~ '@postgres/' ~ service_name | replace('-', '_') - ~ '?sslmode=disable', - 'type': service_database_secret_type, - 'target': service_database_secret_target ~ '-url' - }] if service_podman_secrets[service_name ~ '-postgres'] is defined else [] - ) + | map('regex_replace', '^', service_name ~ '-') + | map('community.general.dict_kv', 'name') + ) + | map('combine') + + ( + [{ + 'name': _service_database_name, + 'type': service_database_secret_type, + 'target': service_database_secret_target + }] if _service_setup_database else [] + ) + + ( + [{ + 'name': _service_database_name ~ '-url', + 'value': + 'postgres://' + ~ service_name | replace('-', '_') + ~ ':' ~ service_podman_secrets[service_name ~ '-postgres'] + ~ '@postgres/' ~ service_name | replace('-', '_') + ~ '?sslmode=disable', + 'type': service_database_secret_type, + 'target': service_database_secret_target ~ '-url' + }] if service_podman_secrets[service_name ~ '-postgres'] is defined else [] + ) }} diff --git a/roles/service/vars/main/template_validate.yaml b/roles/service/vars/main/template_validate.yaml new file mode 100644 index 0000000..c2395da --- /dev/null +++ b/roles/service/vars/main/template_validate.yaml @@ -0,0 +1,24 @@ +--- +_service_template_validate_secrets: >- + {{ + _service_container_secrets + | map(attribute='name') + | zip( + _service_container_secrets + | community.general.remove_keys(['name', 'value', 'length']) + | map('items') + | map('map', 'join', '=') + | map('join', ',') + ) + | map('join', ',') + | map('regex_replace', '^', '--secret ') + | join(' ') + }} + +_service_template_validate_command: >- + podman run --rm + -v %s:{{ item.destination }}:ro + --entrypoint {{ item.template_validate_command.split(' ', 1)[0] }} + {{ _service_template_validate_secrets }} + {{ service_container_image }} + {{ item.template_validate_command.split(' ', 1)[1] }}