Compare commits

..

8 Commits

Author SHA1 Message Date
uumas
8b55af2d06 synapse: Fix signing key path 2025-07-05 16:37:00 +03:00
uumas
e38c283825 service: Fix additional container mounts to use service prefix 2025-07-05 16:36:33 +03:00
uumas
2182b821f4 service: Use properly prefixed loop var for additional containers 2025-07-05 16:35:57 +03:00
uumas
6437c78a94 service: Move mount variable definitions form set_fact to vars 2025-07-05 16:33:29 +03:00
uumas
c31fbf0833 service: Support specifying container command 2025-07-05 16:24:26 +03:00
uumas
84036653fe container: Set exit code 143 as success
Some applications return 143 when exiting due to SIGTERM
2025-07-05 16:20:34 +03:00
uumas
a862606df2 volume: Support device-based volumes
Also support those options in container and service roles
2025-07-05 16:19:14 +03:00
uumas
faa68bfe83 service: Support setting type and target for db password secret 2025-06-28 13:46:07 +03:00
17 changed files with 275 additions and 82 deletions

View File

@@ -48,7 +48,7 @@ argument_specs:
elements: dict elements: dict
options: options:
type: type:
description: Type of volume description: Type of mount
type: str type: str
required: true required: true
choices: choices:
@@ -80,6 +80,28 @@ argument_specs:
type: str type: str
required: false required: false
default: "" default: ""
volume_device:
description: >-
The path of a device which is mounted for the volume.
Only applicable if mount type is volume.
type: str
required: false
default: ""
volume_type:
description: >-
The filesystem type of device as used by the mount commands -t option
Only applicable if mount type is volume.
type: str
required: false
default: ""
volume_mount_options:
description: >-
The mount options to use for a filesystem as used by the mount command -o option
Only applicable if mount type is volume.
type: list
elements: str
required: false
default: []
container_publish_ports: container_publish_ports:
description: "A list of published ports in docker format (<host listen address>:<host port>:<container port>)" description: "A list of published ports in docker format (<host listen address>:<host port>:<container port>)"

View File

@@ -27,6 +27,9 @@
volume_name: "{{ volume.source }}" volume_name: "{{ volume.source }}"
volume_uid: "{{ volume.user | default('') }}" volume_uid: "{{ volume.user | default('') }}"
volume_gid: "{{ volume.group | default('') }}" volume_gid: "{{ volume.group | default('') }}"
volume_type: "{{ volume.volume_type | default('') }}"
volume_device: "{{ volume.volume_device | default('') }}"
volume_mount_options: "{{ volume.volume_mount_options | default([]) }}"
loop: "{{ _container_volumes }}" loop: "{{ _container_volumes }}"
loop_control: loop_control:
loop_var: volume loop_var: volume

View File

@@ -18,7 +18,7 @@ _container_mounts: >-
{{ {{
container_mounts | selectattr('type', '!=', 'volume') + container_mounts | selectattr('type', '!=', 'volume') +
container_mounts | selectattr('type', '==', 'volume') container_mounts | selectattr('type', '==', 'volume')
| community.general.remove_keys(['user', 'group']) | community.general.keep_keys(['type', 'source', 'destination', 'readonly'])
| zip(_container_volume_mount_sources) | map('combine') | zip(_container_volume_mount_sources) | map('combine')
}} }}
@@ -53,6 +53,8 @@ _container_quadlet_unit_options: |
{% for want in container_wants %} {% for want in container_wants %}
Wants={{ want }} Wants={{ want }}
{% endfor %} {% endfor %}
[Service]
SuccessExitStatus=0 143
_container_quadlet_auto_start_options: | _container_quadlet_auto_start_options: |
[Service] [Service]
Restart=always Restart=always

View File

@@ -1,4 +1,6 @@
--- ---
service_container_command: []
service_domains: [] service_domains: []
service_vhost_locations: [] service_vhost_locations: []
service_proxy_pass_host_header: true service_proxy_pass_host_header: true
@@ -11,6 +13,8 @@ service_container_secrets: []
service_container_env: {} service_container_env: {}
service_database_type: none service_database_type: none
service_database_secret_type: mount
service_database_secret_target: "{{ service_database_type }}"
service_postgres_image: docker.io/library/postgres service_postgres_image: docker.io/library/postgres
service_redis: false service_redis: false

View File

@@ -8,6 +8,13 @@ argument_specs:
type: str type: str
required: true required: true
service_container_command:
description: Command to start the service container with.
type: list
required: false
default: []
elements: str
service_domains: service_domains:
description: A list of domains which should be proxied to the main service container description: A list of domains which should be proxied to the main service container
type: list type: list
@@ -77,7 +84,7 @@ argument_specs:
elements: dict elements: dict
options: options:
type: type:
description: Type of volume description: Type of mount
type: str type: str
required: true required: true
choices: choices:
@@ -112,6 +119,28 @@ argument_specs:
type: str type: str
required: false required: false
default: "" default: ""
volume_device:
description: >-
The path of a device which is mounted for the volume.
Only applicable if mount type is volume.
type: str
required: false
default: ""
volume_type:
description: >-
The filesystem type of device as used by the mount commands -t option
Only applicable if mount type is volume.
type: str
required: false
default: ""
volume_mount_options:
description: >-
The mount options to use for a filesystem as used by the mount command -o option
Only applicable if mount type is volume.
type: list
elements: str
required: false
default: []
service_container_secrets: service_container_secrets:
description: description:
- > - >
@@ -177,11 +206,24 @@ argument_specs:
The password will also be available as the The password will also be available as the
service_podman_secrets['{{ service_name }}-{{ service_database_type }}'] variable. service_podman_secrets['{{ service_name }}-{{ service_database_type }}'] variable.
type: str type: str
required: false
choices: choices:
- postgres - postgres
- none - none
required: false
default: none default: none
service_database_secret_type:
description: Secret type for database secret for service container
type: str
choices:
- mount
- env
required: false
default: mount
service_database_secret_target:
description: Secret target for database secret for service container.
type: str
required: false
default: "{{ service_database_type }}"
service_postgres_image: service_postgres_image:
description: Postgresql image to use. description: Postgresql image to use.
type: str type: str
@@ -207,8 +249,8 @@ argument_specs:
description: description:
- List of additional containers for the service. - List of additional containers for the service.
- > - >
Will inherit most options from main service container, except for publish_ports. Will inherit most options from main service container. All options can be overridden
All options can be overridden per-container. per-container.
type: list type: list
required: false required: false
default: [] default: []
@@ -228,6 +270,12 @@ argument_specs:
type: str type: str
required: false required: false
default: "{{ service_container_image }}" default: "{{ service_container_image }}"
command:
description: Command to start the container with.
type: list
required: false
default: "[]"
elements: str
mounts: mounts:
description: List of bind mounts or volumes to be mounted inside the main service container. description: List of bind mounts or volumes to be mounted inside the main service container.
type: list type: list
@@ -236,7 +284,7 @@ argument_specs:
elements: dict elements: dict
options: options:
type: type:
description: Type of volume description: Type of mount
type: str type: str
required: true required: true
choices: choices:
@@ -261,6 +309,28 @@ argument_specs:
- Defaults to false for volume and bind, true for template - Defaults to false for volume and bind, true for template
type: bool type: bool
required: false required: false
volume_device:
description: >-
The path of a device which is mounted for the volume.
Only applicable if mount type is volume.
type: str
required: false
default: ""
volume_type:
description: >-
The filesystem type of device as used by the mount commands -t option
Only applicable if mount type is volume.
type: str
required: false
default: ""
volume_mount_options:
description: >-
The mount options to use for a filesystem as used by the mount command -o option
Only applicable if mount type is volume.
type: list
elements: str
required: false
default: []
publish_ports: publish_ports:
description: "A list of published ports in docker format (<host listen address>:<host port>:<container port>)" description: "A list of published ports in docker format (<host listen address>:<host port>:<container port>)"
type: list type: list

View File

@@ -3,18 +3,60 @@
ansible.builtin.include_role: ansible.builtin.include_role:
name: container name: container
vars: vars:
container_name: "{{ container.name }}" _service_additional_volume_mounts: "{{ _service_additional_container.mounts | selectattr('type', '==', 'volume') }}"
container_image: "{{ container.image | default(service_container_image) }}" _service_additional_template_mounts: "{{ _service_additional_container.mounts | selectattr('type', '==', 'template') }}"
_service_additional_host_directory: "/srv/{{ service_name }}"
_service_additional_container_volume_mounts: >-
{{
_service_additional_volume_mounts |
zip(
_service_additional_volume_mounts |
map(attribute='source') |
map('regex_replace', '^', service_name ~ '-') |
map('community.general.dict_kv', 'source')
) |
map('combine')
}}
_service_additional_container_bind_mounts: "{{ _service_additional_container.mounts | selectattr('type', '==', 'bind') }}"
_service_additional_container_template_mounts: >-
{{
([{'readonly': true}] * _service_additional_template_mounts | length) |
zip(
_service_additional_template_mounts,
_service_additional_template_mounts |
map(attribute='source') |
map('regex_replace', '\.j2$', '') |
map('regex_replace', '^', _service_host_directory ~ '/mounts/') |
map('community.general.dict_kv', 'source'),
([{'type': 'bind'}] * _service_additional_template_mounts | length)
) |
map('combine')
}}
_service_additional_container_mounts: >-
{{
_service_additional_container_volume_mounts +
_service_additional_container_bind_mounts +
_service_additional_container_template_mounts
if _service_additional_container.mounts is defined
else
_service_container_mounts
}}
container_name: "{{ _service_additional_container.name }}"
container_image: "{{ _service_additional_container.image | default(service_container_image) }}"
container_image_creds: "{{ service_container_image_creds }}" container_image_creds: "{{ service_container_image_creds }}"
container_command: "{{ _service_additional_container.command | default([]) }}"
container_user: "{{ service_container_user }}" container_user: "{{ service_container_user }}"
container_mounts: "{{ container.mounts | default(_service_container_mounts) }}" container_mounts: "{{ _service_additional_container_mounts }}"
container_publish_ports: "{{ container.publish_ports | default([]) }}" container_publish_ports: "{{ _service_additional_container.publish_ports | default([]) }}"
container_networks: "{{ _service_container_networks }}" container_networks: "{{ _service_container_networks }}"
container_secrets: "{{ container.secrets | default(_service_container_secrets) }}" container_secrets: "{{ _service_additional_container.secrets | default(_service_container_secrets) }}"
container_env: "{{ container.env | default(service_container_env) }}" container_env: "{{ _service_additional_container.env | default(service_container_env) }}"
container_requires: "{{ _service_container_requires }}" container_requires: "{{ _service_container_requires }}"
container_wants: "{{ service_wants }}" container_wants: "{{ service_wants }}"
container_auto_update: "{{ service_auto_update }}" container_auto_update: "{{ service_auto_update }}"
loop: "{{ _service_additional_containers }}" loop: "{{ _service_additional_containers }}"
loop_control: loop_control:
loop_var: container loop_var: _service_additional_container

View File

@@ -2,10 +2,6 @@
- name: Validate inputs - name: Validate inputs
ansible.builtin.import_tasks: validation.yaml ansible.builtin.import_tasks: validation.yaml
- name: Initialize variables
ansible.builtin.set_fact:
_service_container_mounts: []
- name: Database for {{ service_name }} - name: Database for {{ service_name }}
ansible.builtin.include_tasks: database.yaml ansible.builtin.include_tasks: database.yaml
when: _service_setup_database when: _service_setup_database
@@ -18,9 +14,9 @@
ansible.builtin.include_tasks: secrets.yaml ansible.builtin.include_tasks: secrets.yaml
when: _service_container_secrets | length > 0 when: _service_container_secrets | length > 0
- name: Mounts for {{ service_name }} - name: Template mounts for {{ service_name }}
ansible.builtin.include_tasks: mounts.yaml ansible.builtin.include_tasks: templates.yaml
when: service_container_mounts | length > 0 when: _service_template_mounts | length > 0
- name: Additional containers for {{ service_name }} - name: Additional containers for {{ service_name }}
ansible.builtin.include_tasks: additional.yaml ansible.builtin.include_tasks: additional.yaml
@@ -33,6 +29,7 @@
container_name: "{{ service_name }}" container_name: "{{ service_name }}"
container_image: "{{ service_container_image }}" container_image: "{{ service_container_image }}"
container_image_creds: "{{ service_container_image_creds }}" container_image_creds: "{{ service_container_image_creds }}"
container_command: "{{ service_container_command }}"
container_user: "{{ service_container_user }}" container_user: "{{ service_container_user }}"
container_mounts: "{{ _service_container_mounts }}" container_mounts: "{{ _service_container_mounts }}"
container_publish_ports: "{{ service_container_publish_ports }}" container_publish_ports: "{{ service_container_publish_ports }}"

View File

@@ -1,32 +0,0 @@
---
- name: Set container named mounts
ansible.builtin.set_fact:
_service_container_mounts: >
{{ _service_container_mounts +
[mount | combine({'source': service_name + '-' + mount.source})] }}
when: mount.type == 'volume'
- name: Set container named mounts
ansible.builtin.set_fact:
_service_container_mounts: "{{ _service_container_mounts + [mount] }}"
when: mount.type == 'bind'
- name: Template mounts
when: mount.type == 'template'
block:
- name: Set template host path
ansible.builtin.set_fact:
_service_template_host_path: "{{ _service_host_directory }}/mounts/{{ (mount.source | split('.'))[0:-1] | join('.') }}" # Strip .j2 extension
- name: Template files for template mounts
ansible.builtin.template:
src: "{{ mount.source }}"
dest: "{{ _service_template_host_path }}"
mode: "0644"
notify: "Restart container service {{ service_name }}"
- name: Set container template mounts
ansible.builtin.set_fact:
_service_container_mounts: >
{{ _service_container_mounts +
[{'readonly': true} | combine(mount) | combine({'type': 'bind', 'source': _service_template_host_path})] }}

View File

@@ -1,21 +0,0 @@
---
- name: Create template mount directories under /srv
when: _service_template_mounts | length > 0
block:
- name: Create directory {{ _service_host_directory }}
ansible.builtin.file:
path: "{{ _service_host_directory }}"
state: directory
mode: "0755"
- name: Create directory {{ _service_host_directory + '/mounts' }}
ansible.builtin.file:
path: "{{ _service_host_directory }}/mounts"
state: directory
mode: "0700"
- name: Set mount definitions for {{ service_name }}
ansible.builtin.include_tasks: mount.yaml
loop: "{{ service_container_mounts }}"
loop_control:
loop_var: mount

View File

@@ -0,0 +1,20 @@
---
- name: Create directory {{ _service_host_directory }}
ansible.builtin.file:
path: "{{ _service_host_directory }}"
state: directory
mode: "0755"
- name: Create directory {{ _service_host_directory + '/mounts' }}
ansible.builtin.file:
path: "{{ _service_host_directory }}/mounts"
state: directory
mode: "0700"
- name: Template files for template mounts
ansible.builtin.template:
src: "{{ item[0].source }}"
dest: "{{ item[1].source }}"
mode: "0644"
notify: "Restart container service {{ service_name }}"
loop: "{{ _service_template_mounts | zip(_service_container_template_mounts) }}"

View File

@@ -1,7 +1,42 @@
--- ---
_service_template_mounts: "{{ service_container_mounts | selectattr('type', '==', 'template') | list }}" _service_volume_mounts: "{{ service_container_mounts | selectattr('type', '==', 'volume') }}"
_service_template_mounts: "{{ service_container_mounts | selectattr('type', '==', 'template') }}"
_service_host_directory: "/srv/{{ service_name }}" _service_host_directory: "/srv/{{ service_name }}"
_service_container_volume_mounts: >-
{{
_service_volume_mounts |
zip(
_service_volume_mounts |
map(attribute='source') |
map('regex_replace', '^', service_name ~ '-') |
map('community.general.dict_kv', 'source')
) |
map('combine')
}}
_service_container_bind_mounts: "{{ service_container_mounts | selectattr('type', '==', 'bind') }}"
_service_container_template_mounts: >-
{{
([{'readonly': true}] * _service_template_mounts | length) |
zip(
_service_template_mounts,
_service_template_mounts |
map(attribute='source') |
map('regex_replace', '\.j2$', '') |
map('regex_replace', '^', _service_host_directory ~ '/mounts/') |
map('community.general.dict_kv', 'source'),
([{'type': 'bind'}] * _service_template_mounts | length)
) |
map('combine')
}}
_service_container_mounts: >-
{{
_service_container_volume_mounts +
_service_container_bind_mounts +
_service_container_template_mounts
}}
_service_setup_database: "{{ service_database_type != 'none' }}" _service_setup_database: "{{ service_database_type != 'none' }}"
_service_database_name: "{{ service_name }}-{{ service_database_type }}" _service_database_name: "{{ service_name }}-{{ service_database_type }}"
@@ -20,7 +55,13 @@ _service_container_secrets: >-
| map('community.general.dict_kv', 'name') | map('community.general.dict_kv', 'name')
) )
| map('combine') | map('combine')
+ ([{'name': _service_database_name, 'target': service_database_type }] if _service_setup_database else []) + (
[{
'name': _service_database_name,
'type': service_database_secret_type,
'target': service_database_secret_target
}] if _service_setup_database else []
)
}} }}
_service_additional_containers: >- _service_additional_containers: >-

View File

@@ -2,7 +2,7 @@
# vim:ft=yaml # vim:ft=yaml
# {{ ansible_managed }} # {{ ansible_managed }}
signing_key_path: /run/secrets/synapse-signing-key signing_key_path: /run/secrets/signing-key
media_store_path: /data/media media_store_path: /data/media
log_config: /data/log.yaml log_config: /data/log.yaml

View File

@@ -1,3 +1,6 @@
--- ---
volume_uid: "" volume_uid: ""
volume_gid: "" volume_gid: ""
volume_type: ""
volume_device: ""
volume_mount_options: []

View File

@@ -0,0 +1,7 @@
---
- name: "Restart volume service {{ volume_name }}"
ansible.builtin.systemd_service:
name: "{{ volume_name }}-volume.service"
state: restarted
daemon_reload: true
ignore_errors: '{{ ansible_check_mode }}'

View File

@@ -17,3 +17,19 @@ argument_specs:
type: str type: str
required: false required: false
default: "" default: ""
volume_device:
description: The path of a device which is mounted for the volume.
type: str
required: false
default: ""
volume_type:
description: The filesystem type of device as used by the mount commands -t option
type: str
required: false
default: ""
volume_mount_options:
description: The mount options to use for a filesystem as used by the mount command -o option
type: list
elements: str
required: false
default: []

View File

@@ -5,7 +5,8 @@
- name: Create container volume service {{ volume_name }} - name: Create container volume service {{ volume_name }}
containers.podman.podman_volume: containers.podman.podman_volume:
name: "{{ volume_name }}" name: "{{ volume_name }}"
options: "{{ volume_options }}" options: "{{ _volume_options }}"
state: quadlet state: quadlet
quadlet_file_mode: "0644" quadlet_file_mode: "0644"
notify: Reload systemd daemon quadlet_options: "{{ _volume_quadlet_options }}"
notify: Restart volume service {{ volume_name }}

View File

@@ -1,6 +1,24 @@
--- ---
volume_mount_options_incl_empty: _volume_mount_options_incl_empty:
- "{{ 'uid=' ~ volume_uid if volume_uid | length > 0 else '' }}" - "{{ 'uid=' ~ volume_uid if volume_uid | length > 0 else '' }}"
- "{{ 'gid=' ~ volume_gid if volume_gid | length > 0 else '' }}" - "{{ 'gid=' ~ volume_gid if volume_gid | length > 0 else '' }}"
volume_mount_options: "{{ volume_mount_options_incl_empty | select('!=', '') | list }}" _volume_mount_options: >-
volume_options: "{{ ['o=' ~ volume_mount_options | join(',')] if volume_mount_options | length > 0 else [] }}" {{
_volume_mount_options_incl_empty
| select('!=', '')
+ volume_mount_options
}}
_volume_options: >-
{{
(['o=' ~ _volume_mount_options | join(',')] if _volume_mount_options | length > 0 else [])
+ (['type=' ~ volume_type] if volume_type | length > 0 else [])
+ (['device=' ~ volume_device] if volume_device | length > 0 else [])
}}
_volume_device_quadlet_options: |
[Service]
ExecStartPost=/usr/bin/podman volume mount {{ volume_name }}
ExecStop=/usr/bin/podman volume unmount {{ volume_name }}
ExecStop=/usr/bin/podman volume rm {{ volume_name }}
_volume_quadlet_options: >-
{{ [_volume_device_quadlet_options] if volume_device | length > 0 else [] }}