--- - name: Validate compose_service parameters ansible.builtin.assert: that: - compose_service_name | length > 0 - (compose_service_compose_content | length > 0) != (compose_service_compose_template | length > 0) fail_msg: >- Задайте compose_service_name и ровно один из compose_service_compose_content / compose_service_compose_template. - name: Ensure the service root directory exists ansible.builtin.file: path: "{{ compose_service_root }}" state: directory owner: "{{ compose_service_owner }}" group: "{{ compose_service_group }}" mode: "{{ compose_service_root_mode }}" - name: Ensure the service data directories exist ansible.builtin.file: path: "{{ item.path }}" state: directory owner: "{{ item.owner | default(compose_service_owner) }}" group: "{{ item.group | default(compose_service_group) }}" mode: "{{ item.mode | default('0750') }}" loop: "{{ compose_service_directories }}" loop_control: label: "{{ item.path }}" # --- Файл окружения -------------------------------------------------------- # Создаётся один раз под umask 077. Секреты никогда не попадают в вывод: # задача целиком под no_log. - name: Generate the environment file once ansible.builtin.shell: | set -eu umask 077 if [ -e {{ compose_service_env_path | quote }} ]; then exit 0 fi : > {{ compose_service_env_path | quote }} {% for key, value in compose_service_env_static.items() %} printf '%s\n' {{ (key ~ '=' ~ value) | quote }} >> {{ compose_service_env_path | quote }} {% endfor %} {% for key in compose_service_env_generated %} printf '%s=%s\n' {{ key | quote }} "$(openssl rand -hex {{ compose_service_env_secret_bytes }})" >> {{ compose_service_env_path | quote }} {% endfor %} printf created args: executable: /bin/sh register: compose_service_env_result changed_when: compose_service_env_result.stdout == 'created' no_log: true when: >- compose_service_env_static | length > 0 or compose_service_env_generated | length > 0 - name: Enforce the environment file permissions ansible.builtin.file: path: "{{ compose_service_env_path }}" owner: "{{ compose_service_owner }}" group: "{{ compose_service_group }}" mode: "{{ compose_service_env_mode }}" when: >- compose_service_env_static | length > 0 or compose_service_env_generated | length > 0 - name: Install the environment example ansible.builtin.copy: dest: "{{ compose_service_env_path }}.example" owner: "{{ compose_service_owner }}" group: "{{ compose_service_group }}" mode: "{{ compose_service_env_example_mode }}" content: "{{ compose_service_env_example }}" when: compose_service_env_example | length > 0 # --- compose.yml ----------------------------------------------------------- - name: Install the Compose configuration from inline content ansible.builtin.copy: dest: "{{ compose_service_root }}/{{ compose_service_compose_file }}" owner: "{{ compose_service_owner }}" group: "{{ compose_service_group }}" mode: "{{ compose_service_compose_mode }}" content: "{{ compose_service_compose_content }}" when: compose_service_compose_content | length > 0 register: compose_service_compose_inline - name: Install the Compose configuration from a template ansible.builtin.template: src: "{{ compose_service_compose_template }}" dest: "{{ compose_service_root }}/{{ compose_service_compose_file }}" owner: "{{ compose_service_owner }}" group: "{{ compose_service_group }}" mode: "{{ compose_service_compose_mode }}" when: compose_service_compose_template | length > 0 register: compose_service_compose_templated - name: Record whether the Compose configuration changed ansible.builtin.set_fact: compose_service_compose_changed: >- {{ (compose_service_compose_inline.changed | default(false)) or (compose_service_compose_templated.changed | default(false)) }} # --- systemd --------------------------------------------------------------- - name: Install the systemd unit ansible.builtin.copy: dest: "{{ compose_service_unit_path }}" owner: root group: root mode: "0644" content: | [Unit] Description={{ compose_service_description }} Wants=network-online.target After=network-online.target docker.service{{ (' ' ~ compose_service_after | join(' ')) if compose_service_after else '' }} Requires=docker.service{{ (' ' ~ compose_service_requires | join(' ')) if compose_service_requires else '' }} [Service] Type=oneshot RemainAfterExit=yes WorkingDirectory={{ compose_service_root }} ExecStart={{ compose_service_docker_binary }} compose -f {{ compose_service_root }}/{{ compose_service_compose_file }} up -d --remove-orphans ExecStop={{ compose_service_docker_binary }} compose -f {{ compose_service_root }}/{{ compose_service_compose_file }} down [Install] WantedBy=multi-user.target register: compose_service_unit notify: compose_service_daemon_reload - name: Apply the pending systemd daemon reload ansible.builtin.meta: flush_handlers # --- Валидация ------------------------------------------------------------- # no_log: сообщение об ошибке `docker compose config` подставляет значения # переменных из .env. - name: Validate the Compose configuration ansible.builtin.command: argv: - "{{ compose_service_docker_binary }}" - compose - -f - "{{ compose_service_root }}/{{ compose_service_compose_file }}" - config - --quiet args: chdir: "{{ compose_service_root }}" changed_when: false no_log: true when: compose_service_validate # --- Запуск ---------------------------------------------------------------- - name: Enable and start the service ansible.builtin.systemd: name: "{{ compose_service_name }}" enabled: "{{ compose_service_enabled }}" state: "{{ 'restarted' if compose_service_needs_restart else 'started' }}" vars: compose_service_needs_restart: >- {{ (compose_service_compose_changed | bool) or (compose_service_unit.changed | default(false)) or (compose_service_restart_triggers | select | list | length > 0) }} - name: Wait for the service health endpoint ansible.builtin.uri: url: "{{ compose_service_health_url }}" status_code: "{{ compose_service_health_status }}" follow_redirects: "{{ compose_service_health_follow_redirects }}" return_content: false register: compose_service_health retries: "{{ compose_service_health_retries }}" delay: "{{ compose_service_health_delay }}" until: compose_service_health.status in (compose_service_health_status | map('int') | list) when: compose_service_health_url | length > 0