From 17fea0e02beb9941c465619b72c105788fa8b277 Mon Sep 17 00:00:00 2001 From: Daniel Akulenok Date: Sat, 10 Jan 2026 23:21:34 +0100 Subject: [PATCH] podman --- README.md | 178 +++++++++----------- defaults/main.yml | 46 ++++- handlers/main.yml | 58 +++++-- tasks/containers.yml | 334 +++++++++++++++++++------------------ tasks/host_directories.yml | 18 ++ tasks/main.yml | 7 + tasks/networks.yml | 43 +++-- tasks/pods.yml | 121 +++++++------- tasks/volumes.yml | 27 +-- 9 files changed, 452 insertions(+), 380 deletions(-) create mode 100644 tasks/host_directories.yml diff --git a/README.md b/README.md index e140dfa..97978bd 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ ``` ### 2. Run Your First Container + ```yaml - hosts: servers roles: @@ -20,7 +21,6 @@ podman_containers: - name: nginx image: nginx:latest - state: started ports: - "80:80" ``` @@ -28,11 +28,11 @@ ### 3. Common Patterns **Web application with database:** + ```yaml podman_containers: - name: webapp image: myapp:latest - state: started ports: - "8080:8080" env: @@ -40,7 +40,6 @@ podman_containers: - name: database image: postgres:15 - state: started volumes: - "db-data:/var/lib/postgresql/data" env: @@ -63,127 +62,99 @@ podman_volumes: --- -## 🔧 Complete Feature Reference +## 🔧 Configuration Guide -### Container Management +### Management Modes (Quadlet vs Systemd) + +This role automatically selects the best management engine based on your operating system version: + +- **Quadlet** (Default for Debian 13+): Uses Podman's native systemd generator via `.container` files. This is the modern, preferred method. +- **Systemd** (Default for Debian < 13): Uses legacy `podman generate systemd` to create service units. + +The determination is controlled by the logic in `defaults/main.yml`: + +```yaml +# Auto-detected. True for Debian 13+, False otherwise. +podman_use_quadlet: "{{ ... }}" + +# Sets default state to 'quadlet' or 'started'/'present' accordingly +podman_mode: ... +``` + +**Recommendation:** Do **not** set `state` explicitly in your variables (e.g., `podman_containers`) unless you have a specific reason. The role's defaults will ensure the correct state is applied for your OS version. + +Users can still manually control defaults if needed: + +```yaml +# Force Quadlet usage on older systems (if supported) +podman_use_quadlet: true + +# Or customize default options +podman_container_defaults: + quadlet_options: + - "AutoUpdate=registry" + - | + [Install] + WantedBy=default.target +``` + +If you prefer the standard imperative approach (similar to `docker run`) regardless of OS, you can override the defaults or set `state: started` on individual items. + +### Resource Definition + +The variables `podman_containers`, `podman_networks`, `podman_volumes`, and `podman_pods` accept standard parameters from the [containers.podman](https://docs.ansible.com/ansible/latest/collections/containers/podman/index.html) collection. + +#### Containers ```yaml podman_containers: - - name: my-app + - name: nginx image: nginx:latest - state: started # started|stopped|present|absent - ports: - - "8080:80" - - "443:443" + ports: ["80:80"] volumes: - - "/host/path:/container/path" - - "volume-name:/data" + - "html_vol:/usr/share/nginx/html" + - "./local_conf:/etc/nginx/conf.d:ro" env: - ENV_VAR: value - networks: - - app-network - restart_policy: always # no|always|on-failure|unless-stopped - user: "1000:1000" - labels: - app: web - environment: prod - memory: "1g" - cpu_shares: 1024 - device: - - "/dev/sda:/dev/xvda:rwm" - security_opt: - - "seccomp=unconfined" - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost"] - interval: 30s - timeout: 10s - retries: 3 - # Systemd service generation (optional) - generate_systemd: - path: "/etc/systemd/system" - restart_policy: always - after: ["network.target"] - wants: ["network-online.target"] + NGINX_HOST: example.com + # Quadlet-specific options can be added as a list + quadlet_options: + - "AutoUpdate=registry" ``` -### Network Management +#### Networks ```yaml podman_networks: - - name: app-network - state: present - driver: bridge # bridge|macvlan|ipvlan - subnet: "172.20.0.0/16" - gateway: "172.20.0.1" - internal: false # true for isolated networks - dns: - - "8.8.8.8" - - "1.1.1.1" - options: - mtu: 1500 - vlan: 100 - - # Advanced networking - - name: macvlan-net - driver: macvlan - macvlan: "eth0" # Parent interface - subnet: "192.168.1.0/24" - - - name: ipv6-net - driver: bridge - subnet: "fd00::/64" - ipv6: true + - name: app_net + subnet: "10.0.0.0/24" + gateway: "10.0.0.1" + dns: ["8.8.8.8"] ``` -### Volume Management +#### Volumes ```yaml +# Toggle automatic creation of host directories for bind mounts +podman_create_volumes: true + podman_volumes: - - name: app-data - state: present - driver: local # local|tmpfs - labels: - backup: daily - environment: prod - options: - - "device=/dev/sdb1" - - "type=ext4" - - "o=rw" - - - name: tmpfs-volume - driver: tmpfs - options: - - "tmpfs-size=100m" - - "tmpfs-mode=1777" + - name: db_data + # state defaults to 'quadlet' ``` -### Pod Management +#### Pods ```yaml podman_pods: - - name: webapp-pod - state: started - ports: - - "8080:80" - networks: - - frontend - hostname: webapp - dns: - - "8.8.8.8" - labels: - app: webapp - volumes: - - "webapp-data:/data" - memory: "2g" - cpu_shares: 1024 - share: "net,ipc" # Shared namespaces - infra: true # Use infra container - infra_image: "registry.k8s.io/pause:3.9" + - name: app_pod + ports: ["8080:80"] + share: "net,ipc" ``` ### Advanced Configuration #### Registry & Security Policy + ```yaml # Basic registry setup (development) podman_policy_default_type: "insecureAcceptAnything" @@ -206,6 +177,7 @@ podman_policy_trusted_registries: ``` #### Systemd Service Generation + ```yaml # Global systemd settings podman_generate_systemd: true @@ -219,6 +191,7 @@ podman_systemd_options: ``` #### Resource Cleanup + ```yaml # Auto-cleanup unused resources podman_prune_enabled: true @@ -231,6 +204,7 @@ podman_prune_options: ``` #### Storage Configuration + ```yaml podman_configure_storage: true podman_storage_driver: overlay @@ -239,6 +213,7 @@ podman_storage_runroot: /run/containers/storage ``` #### API & Socket Services + ```yaml podman_enable_socket: true # Enable Podman socket podman_enable_api_service: true # Enable REST API @@ -265,6 +240,7 @@ ansible-playbook -t podman-networks playbook.yml ``` **Available tags:** + - `podman` - Run everything - `podman-install` - Package installation - `podman-configure` - Configuration files @@ -281,6 +257,7 @@ ansible-playbook -t podman-networks playbook.yml ## 📚 Example Playbooks ### Development Environment + ```yaml - hosts: dev-servers roles: @@ -293,7 +270,6 @@ ansible-playbook -t podman-networks playbook.yml podman_containers: - name: dev-web image: nginx:latest - state: started ports: - "8080:80" volumes: @@ -301,6 +277,7 @@ ansible-playbook -t podman-networks playbook.yml ``` ### Production Environment + ```yaml - hosts: prod-servers roles: @@ -317,7 +294,6 @@ ansible-playbook -t podman-networks playbook.yml podman_containers: - name: prod-app image: registry.company.com/app:v1.2.3 - state: started restart_policy: always memory: "2g" cpu_shares: 2048 @@ -329,6 +305,7 @@ ansible-playbook -t podman-networks playbook.yml ``` ### Multi-Service Application + ```yaml - hosts: app-servers roles: @@ -347,7 +324,6 @@ ansible-playbook -t podman-networks playbook.yml # Database - name: postgres image: postgres:15 - state: started networks: - app-network volumes: @@ -359,7 +335,6 @@ ansible-playbook -t podman-networks playbook.yml # Cache - name: redis image: redis:7-alpine - state: started networks: - app-network volumes: @@ -368,7 +343,6 @@ ansible-playbook -t podman-networks playbook.yml # Application - name: app image: myapp:latest - state: started networks: - app-network ports: diff --git a/defaults/main.yml b/defaults/main.yml index 586a645..7bdd496 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -166,14 +166,27 @@ podman_policy_trusted_registries: podman_enable_socket: false podman_enable_api_service: false +# Determine if Quadlet should be used (Debian 13+ or other distros) +podman_use_quadlet: "{{ not (ansible_distribution | default('Debian') == 'Debian' and ansible_distribution_major_version | default('13') | int < 13) }}" +podman_mode: "{{ 'quadlet' if podman_use_quadlet else 'started' }}" + # Container management +podman_container_defaults: + state: "{{ podman_mode }}" + pull: newer + quadlet_options: + - "AutoUpdate=registry" + - "Pull=newer" + - | + [Install] + WantedBy=default.target + podman_containers: [] # Example container configurations: # podman_containers: # # Basic web server container # - name: nginx # image: docker.io/nginx:latest -# state: started # ports: # - "8080:80" # volumes: @@ -192,7 +205,6 @@ podman_containers: [] # # Advanced container with comprehensive configuration # - name: app-server # image: registry.example.com/myapp:v1.2.3 -# state: started # # Basic runtime options # command: ["/app/start.sh", "--config", "/etc/app/config.yaml"] # entrypoint: "/entrypoint.sh" @@ -473,6 +485,13 @@ podman_containers: [] # - "no-new-privileges=true" # Network management +podman_network_defaults: + state: "{{ 'quadlet' if podman_use_quadlet else 'present' }}" + quadlet_options: + - | + [Install] + WantedBy=default.target + podman_networks: [] # Example network configuration: # podman_networks: @@ -480,7 +499,6 @@ podman_networks: [] # driver: bridge # subnet: "10.89.0.0/24" # gateway: "10.89.0.1" -# state: present # - name: frontend-network # driver: bridge # subnet: "172.20.0.0/16" @@ -508,14 +526,20 @@ podman_networks: [] # - "10.10.0.0/16,192.168.1.1" # Volume management +podman_create_volumes: true +podman_volume_defaults: + state: "{{ 'quadlet' if podman_use_quadlet else 'present' }}" + quadlet_options: + - | + [Install] + WantedBy=default.target + podman_volumes: [] # Example volume configuration: # podman_volumes: # - name: app-data -# state: present # driver: local # - name: database-volume -# state: present # driver: local # labels: # environment: production @@ -525,7 +549,6 @@ podman_volumes: [] # - "type=ext4" # - "o=rw" # - name: tmpfs-volume -# state: present # driver: tmpfs # options: # - "tmpfs-size=100m" @@ -542,11 +565,17 @@ podman_volumes: [] # debug: false # Pod management +podman_pod_defaults: + state: "{{ podman_mode }}" + quadlet_options: + - | + [Install] + WantedBy=default.target + podman_pods: [] # Example pod configuration: # podman_pods: # - name: webapp-pod -# state: started # ports: # - "8080:80" # - "3306:3306" @@ -576,7 +605,6 @@ podman_pods: [] # share: "net,ipc" # userns: "auto" # - name: monitoring-pod -# state: started # ports: # - "9090:9090" # device: @@ -614,7 +642,7 @@ podman_pods: [] podman_auto_remove: true # Systemd service generation configuration -podman_generate_systemd: true +podman_generate_systemd: "{{ not podman_use_quadlet }}" podman_systemd_dir: /etc/systemd/system podman_systemd_options: new: true diff --git a/handlers/main.yml b/handlers/main.yml index e079a66..5e3dc2c 100644 --- a/handlers/main.yml +++ b/handlers/main.yml @@ -11,30 +11,52 @@ ansible.builtin.systemd: daemon_reload: true -- name: Start Podman pods +- name: Restart Podman pods ansible.builtin.systemd: - name: "{{ podman_systemd_options.pod_prefix }}{{ item.name }}" - enabled: true - state: started - daemon_reload: true + name: "{{ (podman_systemd_options.pod_prefix if podman_generate_systemd | bool else '') ~ item ~ ('-pod' if not podman_generate_systemd | bool else '') }}" + state: restarted listen: Reload systemd - loop: "{{ podman_pods }}" + loop: "{{ podman_pod_output.results | selectattr('changed', 'equalto', true) | map(attribute='item.name') | list }}" loop_control: - label: "{{ item.name }}" + label: "{{ item }}" when: - - podman_pods is defined - - podman_pods | length > 0 + - podman_pod_output is defined + - podman_pod_output.results | length > 0 -- name: Start Podman podless containers +- name: Restart Podman containers ansible.builtin.systemd: - name: "{{ podman_systemd_options.container_prefix }}{{ item.name }}" - enabled: true - state: started - daemon_reload: true + name: "{{ (podman_systemd_options.container_prefix if podman_generate_systemd | bool else '') ~ item }}" + state: restarted listen: Reload systemd - loop: "{{ podman_containers | rejectattr('pod', 'defined') | list }}" + loop: "{{ podman_container_output.results | selectattr('changed', 'equalto', true) | map(attribute='item.name') | list }}" loop_control: - label: "{{ item.name }}" + label: "{{ item }}" when: - - podman_containers is defined - - podman_containers | length > 0 + - podman_container_output is defined + - podman_container_output.results | length > 0 + +- name: Restart Podman networks + ansible.builtin.systemd: + name: "{{ item }}-network" + state: restarted + listen: Reload systemd + loop: "{{ podman_network_output.results | selectattr('changed', 'equalto', true) | map(attribute='item.name') | list }}" + loop_control: + label: "{{ item }}" + when: + - podman_network_output is defined + - podman_network_output.results | length > 0 + - not podman_generate_systemd | bool + +- name: Restart Podman volumes + ansible.builtin.systemd: + name: "{{ item }}-volume" + state: restarted + listen: Reload systemd + loop: "{{ podman_volume_output.results | selectattr('changed', 'equalto', true) | map(attribute='item.name') | list }}" + loop_control: + label: "{{ item }}" + when: + - podman_volume_output is defined + - podman_volume_output.results | length > 0 + - not podman_generate_systemd | bool diff --git a/tasks/containers.yml b/tasks/containers.yml index 88e5f2f..7fc0bd4 100644 --- a/tasks/containers.yml +++ b/tasks/containers.yml @@ -2,226 +2,230 @@ # Manage Podman containers - name: Manage Podman containers + vars: + container_item: "{{ podman_container_defaults | default({}) | combine(item) }}" containers.podman.podman_container: - name: "{{ item.name }}" - image: "{{ item.image }}" - state: "{{ item.state | default('present') }}" + name: "{{ container_item.name }}" + image: "{{ container_item.image }}" + state: "{{ container_item.state | default('present') }}" # Basic container configuration - command: "{{ item.command | default(omit) }}" - entrypoint: "{{ item.entrypoint | default(omit) }}" - user: "{{ item.user | default(omit) }}" - workdir: "{{ item.working_dir | default(omit) }}" - hostname: "{{ item.hostname | default(omit) }}" + command: "{{ container_item.command | default(omit) }}" + entrypoint: "{{ container_item.entrypoint | default(omit) }}" + user: "{{ container_item.user | default(omit) }}" + workdir: "{{ container_item.working_dir | default(omit) }}" + hostname: "{{ container_item.hostname | default(omit) }}" # Container runtime options - detach: "{{ item.detach | bool | default(omit) }}" - interactive: "{{ item.interactive | bool | default(omit) }}" - tty: "{{ item.tty | bool | default(omit) }}" - rm: "{{ item.auto_remove | bool | default(podman_auto_remove) }}" - rmi: "{{ item.remove_image | bool | default(omit) }}" - privileged: "{{ item.privileged | bool | default(omit) }}" - read_only: "{{ item.read_only | bool | default(omit) }}" - read_only_tmpfs: "{{ item.read_only_tmpfs | bool | default(omit) }}" - init: "{{ item.init | bool | default(omit) }}" - init_path: "{{ item.init_path | default(omit) }}" + detach: "{{ container_item.detach | bool | default(omit) }}" + interactive: "{{ container_item.interactive | bool | default(omit) }}" + tty: "{{ container_item.tty | bool | default(omit) }}" + rm: "{{ container_item.auto_remove | bool | default(podman_auto_remove) }}" + rmi: "{{ container_item.remove_image | bool | default(omit) }}" + privileged: "{{ container_item.privileged | bool | default(omit) }}" + read_only: "{{ container_item.read_only | bool | default(omit) }}" + read_only_tmpfs: "{{ container_item.read_only_tmpfs | bool | default(omit) }}" + init: "{{ container_item.init | bool | default(omit) }}" + init_path: "{{ container_item.init_path | default(omit) }}" # Networking - network: "{{ item.networks | default(omit) }}" - publish: "{{ item.ports | default(omit) }}" - publish_all: "{{ item.publish_all | bool | default(omit) }}" - expose: "{{ item.expose | default(omit) }}" - ip: "{{ item.ip | default(omit) }}" - ip6: "{{ item.ip6 | default(omit) }}" - mac_address: "{{ item.mac_address | default(omit) }}" - network_aliases: "{{ item.network_aliases | default(omit) }}" - dns: "{{ item.dns | default(omit) }}" - dns_option: "{{ item.dns_options | default(omit) }}" - dns_search: "{{ item.dns_search | default(omit) }}" - etc_hosts: "{{ item.add_hosts | default(omit) }}" - no_hosts: "{{ item.no_hosts | bool | default(omit) }}" + network: "{{ container_item.networks | default(omit) }}" + publish: "{{ container_item.ports | default(omit) }}" + publish_all: "{{ container_item.publish_all | bool | default(omit) }}" + expose: "{{ container_item.expose | default(omit) }}" + ip: "{{ container_item.ip | default(omit) }}" + ip6: "{{ container_item.ip6 | default(omit) }}" + mac_address: "{{ container_item.mac_address | default(omit) }}" + network_aliases: "{{ container_item.network_aliases | default(omit) }}" + dns: "{{ container_item.dns | default(omit) }}" + dns_option: "{{ container_item.dns_options | default(omit) }}" + dns_search: "{{ container_item.dns_search | default(omit) }}" + etc_hosts: "{{ container_item.add_hosts | default(omit) }}" + no_hosts: "{{ container_item.no_hosts | bool | default(omit) }}" # Storage and volumes - volume: "{{ item.volumes | default(omit) }}" - volumes_from: "{{ item.volumes_from | default(omit) }}" - mount: "{{ item.mounts | default(omit) }}" - tmpfs: "{{ item.tmpfs | default(omit) }}" - image_volume: "{{ item.image_volume | default(omit) }}" + volume: "{{ container_item.volumes | default(omit) }}" + volumes_from: "{{ container_item.volumes_from | default(omit) }}" + mount: "{{ container_item.mounts | default(omit) }}" + tmpfs: "{{ container_item.tmpfs | default(omit) }}" + image_volume: "{{ container_item.image_volume | default(omit) }}" # Environment variables - env: "{{ item.env | default(omit) }}" - env_file: "{{ item.env_files | default(omit) }}" - env_host: "{{ item.env_host | bool | default(omit) }}" - env_merge: "{{ item.env_merge | default(omit) }}" - unsetenv: "{{ item.unsetenv | default(omit) }}" - unsetenv_all: "{{ item.unsetenv_all | bool | default(omit) }}" + env: "{{ container_item.env | default(omit) }}" + env_file: "{{ container_item.env_files | default(omit) }}" + env_host: "{{ container_item.env_host | bool | default(omit) }}" + env_merge: "{{ container_item.env_merge | default(omit) }}" + unsetenv: "{{ container_item.unsetenv | default(omit) }}" + unsetenv_all: "{{ container_item.unsetenv_all | bool | default(omit) }}" # Resource constraints - memory: "{{ item.memory | default(omit) }}" - memory_reservation: "{{ item.memory_reservation | default(omit) }}" - memory_swap: "{{ item.memory_swap | default(omit) }}" - memory_swappiness: "{{ item.memory_swappiness | default(omit) }}" - kernel_memory: "{{ item.kernel_memory | default(omit) }}" - cpus: "{{ item.cpus | default(omit) }}" - cpu_shares: "{{ item.cpu_shares | default(omit) }}" - cpu_period: "{{ item.cpu_period | default(omit) }}" - cpu_quota: "{{ item.cpu_quota | default(omit) }}" - cpu_rt_period: "{{ item.cpu_rt_period | default(omit) }}" - cpu_rt_runtime: "{{ item.cpu_rt_runtime | default(omit) }}" - cpuset_cpus: "{{ item.cpuset_cpus | default(omit) }}" - cpuset_mems: "{{ item.cpuset_mems | default(omit) }}" - blkio_weight: "{{ item.blkio_weight | default(omit) }}" - blkio_weight_device: "{{ item.blkio_weight_device | default(omit) }}" - oom_kill_disable: "{{ item.oom_kill_disable | bool | default(omit) }}" - oom_score_adj: "{{ item.oom_score_adj | default(omit) }}" - pids_limit: "{{ item.pids_limit | default(omit) }}" + memory: "{{ container_item.memory | default(omit) }}" + memory_reservation: "{{ container_item.memory_reservation | default(omit) }}" + memory_swap: "{{ container_item.memory_swap | default(omit) }}" + memory_swappiness: "{{ container_item.memory_swappiness | default(omit) }}" + kernel_memory: "{{ container_item.kernel_memory | default(omit) }}" + cpus: "{{ container_item.cpus | default(omit) }}" + cpu_shares: "{{ container_item.cpu_shares | default(omit) }}" + cpu_period: "{{ container_item.cpu_period | default(omit) }}" + cpu_quota: "{{ container_item.cpu_quota | default(omit) }}" + cpu_rt_period: "{{ container_item.cpu_rt_period | default(omit) }}" + cpu_rt_runtime: "{{ container_item.cpu_rt_runtime | default(omit) }}" + cpuset_cpus: "{{ container_item.cpuset_cpus | default(omit) }}" + cpuset_mems: "{{ container_item.cpuset_mems | default(omit) }}" + blkio_weight: "{{ container_item.blkio_weight | default(omit) }}" + blkio_weight_device: "{{ container_item.blkio_weight_device | default(omit) }}" + oom_kill_disable: "{{ container_item.oom_kill_disable | bool | default(omit) }}" + oom_score_adj: "{{ container_item.oom_score_adj | default(omit) }}" + pids_limit: "{{ container_item.pids_limit | default(omit) }}" # Device access - device: "{{ item.devices | default(omit) }}" - device_cgroup_rule: "{{ item.device_cgroup_rule | default(omit) }}" - device_read_bps: "{{ item.device_read_bps | default(omit) }}" - device_read_iops: "{{ item.device_read_iops | default(omit) }}" - device_write_bps: "{{ item.device_write_bps | default(omit) }}" - device_write_iops: "{{ item.device_write_iops | default(omit) }}" - gpus: "{{ item.gpus | default(omit) }}" + device: "{{ container_item.devices | default(omit) }}" + device_cgroup_rule: "{{ container_item.device_cgroup_rule | default(omit) }}" + device_read_bps: "{{ container_item.device_read_bps | default(omit) }}" + device_read_iops: "{{ container_item.device_read_iops | default(omit) }}" + device_write_bps: "{{ container_item.device_write_bps | default(omit) }}" + device_write_iops: "{{ container_item.device_write_iops | default(omit) }}" + gpus: "{{ container_item.gpus | default(omit) }}" # Security options - security_opt: "{{ item.security_opt | default(omit) }}" - cap_add: "{{ item.cap_add | default(omit) }}" - cap_drop: "{{ item.cap_drop | default(omit) }}" - seccomp_policy: "{{ item.seccomp_policy | default(omit) }}" - userns: "{{ item.userns | default(omit) }}" - uidmap: "{{ item.uidmap | default(omit) }}" - gidmap: "{{ item.gidmap | default(omit) }}" - subuidname: "{{ item.subuidname | default(omit) }}" - subgidname: "{{ item.subgidname | default(omit) }}" - group_add: "{{ item.groups | default(omit) }}" - group_entry: "{{ item.group_entry | default(omit) }}" - passwd: "{{ item.passwd | default(omit) }}" - passwd_entry: "{{ item.passwd_entry | default(omit) }}" + security_opt: "{{ container_item.security_opt | default(omit) }}" + cap_add: "{{ container_item.cap_add | default(omit) }}" + cap_drop: "{{ container_item.cap_drop | default(omit) }}" + seccomp_policy: "{{ container_item.seccomp_policy | default(omit) }}" + userns: "{{ container_item.userns | default(omit) }}" + uidmap: "{{ container_item.uidmap | default(omit) }}" + gidmap: "{{ container_item.gidmap | default(omit) }}" + subuidname: "{{ container_item.subuidname | default(omit) }}" + subgidname: "{{ container_item.subgidname | default(omit) }}" + group_add: "{{ container_item.groups | default(omit) }}" + group_entry: "{{ container_item.group_entry | default(omit) }}" + passwd: "{{ container_item.passwd | default(omit) }}" + passwd_entry: "{{ container_item.passwd_entry | default(omit) }}" # Namespaces - ipc: "{{ item.ipc_mode | default(omit) }}" - pid: "{{ item.pid_mode | default(omit) }}" - uts: "{{ item.uts | default(omit) }}" - cgroupns: "{{ item.cgroupns | default(omit) }}" + ipc: "{{ container_item.ipc_mode | default(omit) }}" + pid: "{{ container_item.pid_mode | default(omit) }}" + uts: "{{ container_item.uts | default(omit) }}" + cgroupns: "{{ container_item.cgroupns | default(omit) }}" # Cgroups - cgroups: "{{ item.cgroups | default(omit) }}" - cgroup_parent: "{{ item.cgroup_parent | default(omit) }}" - cgroup_conf: "{{ item.cgroup_conf | default(omit) }}" + cgroups: "{{ container_item.cgroups | default(omit) }}" + cgroup_parent: "{{ container_item.cgroup_parent | default(omit) }}" + cgroup_conf: "{{ container_item.cgroup_conf | default(omit) }}" # System configuration - sysctl: "{{ item.sysctl | default(omit) }}" - systemd: "{{ item.systemd | default(omit) }}" - ulimit: "{{ item.ulimits | default(omit) }}" - umask: "{{ item.umask | default(omit) }}" + sysctl: "{{ container_item.sysctl | default(omit) }}" + systemd: "{{ container_item.systemd | default(omit) }}" + ulimit: "{{ container_item.ulimits | default(omit) }}" + umask: "{{ container_item.umask | default(omit) }}" # Shared memory and tmpfs - shm_size: "{{ item.shm_size | default(omit) }}" - shm_size_systemd: "{{ item.shm_size_systemd | default(omit) }}" + shm_size: "{{ container_item.shm_size | default(omit) }}" + shm_size_systemd: "{{ container_item.shm_size_systemd | default(omit) }}" # Pods - pod: "{{ item.pod | default(omit) }}" - pod_id_file: "{{ item.pod_id_file | default(omit) }}" + pod: "{{ container_item.pod | default(omit) }}" + pod_id_file: "{{ container_item.pod_id_file | default(omit) }}" # Logging - log_driver: "{{ item.log_driver | default(omit) }}" - log_opt: "{{ item.log_options | default(omit) }}" - log_level: "{{ item.log_level | default(omit) }}" + log_driver: "{{ container_item.log_driver | default(omit) }}" + log_opt: "{{ container_item.log_options | default(omit) }}" + log_level: "{{ container_item.log_level | default(omit) }}" # Health checks - healthcheck: "{{ item.healthcheck | default(omit) }}" - healthcheck_interval: "{{ item.healthcheck_interval | default(omit) }}" - healthcheck_timeout: "{{ item.healthcheck_timeout | default(omit) }}" - healthcheck_start_period: "{{ item.healthcheck_start_period | default(omit) }}" - healthcheck_retries: "{{ item.healthcheck_retries | default(omit) }}" - healthcheck_failure_action: "{{ item.healthcheck_failure_action | default(omit) }}" - no_healthcheck: "{{ item.no_healthcheck | bool | default(omit) }}" + healthcheck: "{{ container_item.healthcheck | default(omit) }}" + healthcheck_interval: "{{ container_item.healthcheck_interval | default(omit) }}" + healthcheck_timeout: "{{ container_item.healthcheck_timeout | default(omit) }}" + healthcheck_start_period: "{{ container_item.healthcheck_start_period | default(omit) }}" + healthcheck_retries: "{{ container_item.healthcheck_retries | default(omit) }}" + healthcheck_failure_action: "{{ container_item.healthcheck_failure_action | default(omit) }}" + no_healthcheck: "{{ container_item.no_healthcheck | bool | default(omit) }}" # Startup health checks - health_startup_cmd: "{{ item.health_startup_cmd | default(omit) }}" - health_startup_interval: "{{ item.health_startup_interval | default(omit) }}" - health_startup_timeout: "{{ item.health_startup_timeout | default(omit) }}" - health_startup_retries: "{{ item.health_startup_retries | default(omit) }}" - health_startup_success: "{{ item.health_startup_success | default(omit) }}" + health_startup_cmd: "{{ container_item.health_startup_cmd | default(omit) }}" + health_startup_interval: "{{ container_item.health_startup_interval | default(omit) }}" + health_startup_timeout: "{{ container_item.health_startup_timeout | default(omit) }}" + health_startup_retries: "{{ container_item.health_startup_retries | default(omit) }}" + health_startup_success: "{{ container_item.health_startup_success | default(omit) }}" # Metadata and labels - label: "{{ item.labels | default(omit) }}" - label_file: "{{ item.label_file | default(omit) }}" - annotation: "{{ item.annotations | default(omit) }}" + label: "{{ container_item.labels | default(omit) }}" + label_file: "{{ container_item.label_file | default(omit) }}" + annotation: "{{ container_item.annotations | default(omit) }}" # Container lifecycle - restart_policy: "{{ item.restart_policy | default(omit) }}" - restart_time: "{{ item.restart_time | default(omit) }}" - stop_signal: "{{ item.stop_signal | default(omit) }}" - stop_time: "{{ item.stop_time | default(omit) }}" - stop_timeout: "{{ item.stop_timeout | default(omit) }}" - timeout: "{{ item.timeout | default(omit) }}" + restart_policy: "{{ container_item.restart_policy | default(omit) }}" + restart_time: "{{ container_item.restart_time | default(omit) }}" + stop_signal: "{{ container_item.stop_signal | default(omit) }}" + stop_time: "{{ container_item.stop_time | default(omit) }}" + stop_timeout: "{{ container_item.stop_timeout | default(omit) }}" + timeout: "{{ container_item.timeout | default(omit) }}" # Pull and image options - pull: "{{ item.pull | default(omit) }}" - image_strict: "{{ item.image_strict | bool | default(omit) }}" - arch: "{{ item.arch | default(omit) }}" - os: "{{ item.os | default(omit) }}" - platform: "{{ item.platform | default(omit) }}" - variant: "{{ item.variant | default(omit) }}" + pull: "{{ container_item.pull | default(omit) }}" + image_strict: "{{ container_item.image_strict | bool | default(omit) }}" + arch: "{{ container_item.arch | default(omit) }}" + os: "{{ container_item.os | default(omit) }}" + platform: "{{ container_item.platform | default(omit) }}" + variant: "{{ container_item.variant | default(omit) }}" # Registry and authentication - authfile: "{{ item.authfile | default(omit) }}" - tls_verify: "{{ item.tls_verify | default(omit) }}" - decryption_key: "{{ item.decryption_key | default(omit) }}" + authfile: "{{ container_item.authfile | default(omit) }}" + tls_verify: "{{ container_item.tls_verify | default(omit) }}" + decryption_key: "{{ container_item.decryption_key | default(omit) }}" # File operations - cidfile: "{{ item.cidfile | default(omit) }}" - conmon_pidfile: "{{ item.conmon_pidfile | default(omit) }}" - pid_file: "{{ item.pid_file | default(omit) }}" + cidfile: "{{ container_item.cidfile | default(omit) }}" + conmon_pidfile: "{{ container_item.conmon_pidfile | default(omit) }}" + pid_file: "{{ container_item.pid_file | default(omit) }}" # Special options - attach: "{{ item.attach | default(omit) }}" - detach_keys: "{{ item.detach_keys | default(omit) }}" - sig_proxy: "{{ item.sig_proxy | bool | default(omit) }}" - http_proxy: "{{ item.http_proxy | bool | default(omit) }}" + attach: "{{ container_item.attach | default(omit) }}" + detach_keys: "{{ container_item.detach_keys | default(omit) }}" + sig_proxy: "{{ container_item.sig_proxy | bool | default(omit) }}" + http_proxy: "{{ container_item.http_proxy | bool | default(omit) }}" # Advanced options - chrootdirs: "{{ item.chrootdirs | default(omit) }}" - hooks_dir: "{{ item.hooks_dir | default(omit) }}" - hostuser: "{{ item.hostuser | default(omit) }}" - init_ctr: "{{ item.init_ctr | default(omit) }}" - personality: "{{ item.personality | default(omit) }}" - preserve_fd: "{{ item.preserve_fd | default(omit) }}" - preserve_fds: "{{ item.preserve_fds | default(omit) }}" - rdt_class: "{{ item.rdt_class | default(omit) }}" - requires: "{{ item.requires | default(omit) }}" - rootfs: "{{ item.rootfs | bool | default(omit) }}" - sdnotify: "{{ item.sdnotify | default(omit) }}" - secrets: "{{ item.secrets | default(omit) }}" - timezone: "{{ item.timezone | default(omit) }}" + chrootdirs: "{{ container_item.chrootdirs | default(omit) }}" + hooks_dir: "{{ container_item.hooks_dir | default(omit) }}" + hostuser: "{{ container_item.hostuser | default(omit) }}" + init_ctr: "{{ container_item.init_ctr | default(omit) }}" + personality: "{{ container_item.personality | default(omit) }}" + preserve_fd: "{{ container_item.preserve_fd | default(omit) }}" + preserve_fds: "{{ container_item.preserve_fds | default(omit) }}" + rdt_class: "{{ container_item.rdt_class | default(omit) }}" + requires: "{{ container_item.requires | default(omit) }}" + rootfs: "{{ container_item.rootfs | bool | default(omit) }}" + sdnotify: "{{ container_item.sdnotify | default(omit) }}" + secrets: "{{ container_item.secrets | default(omit) }}" + timezone: "{{ container_item.timezone | default(omit) }}" # Retry options - retry: "{{ item.retry | default(omit) }}" - retry_delay: "{{ item.retry_delay | default(omit) }}" + retry: "{{ container_item.retry | default(omit) }}" + retry_delay: "{{ container_item.retry_delay | default(omit) }}" # Systemd generation - generate_systemd: "{{ item.generate_systemd | default(omit) }}" + generate_systemd: "{{ container_item.generate_systemd | default(omit) }}" # Quadlet options - quadlet_dir: "{{ item.quadlet_dir | default(omit) }}" - quadlet_filename: "{{ item.quadlet_filename | default(omit) }}" - quadlet_file_mode: "{{ item.quadlet_file_mode | default(omit) }}" - quadlet_options: "{{ item.quadlet_options | default(omit) }}" + quadlet_dir: "{{ container_item.quadlet_dir | default(omit) }}" + quadlet_filename: "{{ container_item.quadlet_filename | default(omit) }}" + quadlet_file_mode: "{{ container_item.quadlet_file_mode | default(omit) }}" + quadlet_options: "{{ container_item.quadlet_options | default(omit) }}" # Control options - cmd_args: "{{ item.cmd_args | default(omit) }}" - executable: "{{ item.executable | default('podman') }}" - recreate: "{{ item.recreate | bool | default(omit) }}" - force_restart: "{{ item.force_restart | bool | default(omit) }}" - force_delete: "{{ item.force_delete | bool | default(omit) }}" - delete_depend: "{{ item.delete_depend | bool | default(omit) }}" - delete_time: "{{ item.delete_time | default(omit) }}" - delete_volumes: "{{ item.delete_volumes | bool | default(omit) }}" - debug: "{{ item.debug | bool | default(omit) }}" + cmd_args: "{{ container_item.cmd_args | default(omit) }}" + executable: "{{ container_item.executable | default('podman') }}" + recreate: "{{ container_item.recreate | bool | default(omit) }}" + force_restart: "{{ container_item.force_restart | bool | default(omit) }}" + force_delete: "{{ container_item.force_delete | bool | default(omit) }}" + delete_depend: "{{ container_item.delete_depend | bool | default(omit) }}" + delete_time: "{{ container_item.delete_time | default(omit) }}" + delete_volumes: "{{ container_item.delete_volumes | bool | default(omit) }}" + debug: "{{ container_item.debug | bool | default(omit) }}" loop: "{{ podman_containers }}" loop_control: label: "{{ item.name }}" - register: podman_container_results + register: podman_container_output + notify: + - Reload systemd diff --git a/tasks/host_directories.yml b/tasks/host_directories.yml new file mode 100644 index 0000000..a760d8d --- /dev/null +++ b/tasks/host_directories.yml @@ -0,0 +1,18 @@ +--- +# Ensure volumes are created + +- name: Create bind mount directories from containers + ansible.builtin.file: + path: "{{ item.1.split(':')[0] }}" + state: directory + mode: '0755' + owner: root + group: root + loop: "{{ podman_containers | subelements('volumes', skip_missing=True) }}" + when: + - podman_create_volumes | bool + - item.1 is string + - item.1.split(':')[0] | dirname | length > 0 + - item.1.split(':')[0].startswith('/') or item.1.split(':')[0].startswith('./') + loop_control: + label: "{{ item.0.name }} -> {{ item.1.split(':')[0] }}" diff --git a/tasks/main.yml b/tasks/main.yml index bd561a2..4bf6364 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -26,6 +26,13 @@ - podman - podman-networks +- name: Create volume directories + ansible.builtin.include_tasks: host_directories.yml + when: podman_create_volumes | bool + tags: + - podman + - podman-volumes + - name: Manage Podman volumes ansible.builtin.include_tasks: volumes.yml when: podman_volumes | length > 0 diff --git a/tasks/networks.yml b/tasks/networks.yml index ec42a5c..687fce9 100644 --- a/tasks/networks.yml +++ b/tasks/networks.yml @@ -2,24 +2,33 @@ # Manage Podman networks - name: Manage Podman networks + vars: + network_item: "{{ podman_network_defaults | default({}) | combine(item) }}" containers.podman.podman_network: - name: "{{ item.name }}" - state: "{{ item.state | default('present') }}" - driver: "{{ item.driver | default('bridge') }}" - subnet: "{{ item.subnet | default(omit) }}" - gateway: "{{ item.gateway | default(omit) }}" - ip_range: "{{ item.ip_range | default(omit) }}" - disable_dns: "{{ item.disable_dns | bool | default(omit) }}" - internal: "{{ item.internal | bool | default(omit) }}" - opt: "{{ item.options | default(omit) }}" - dns: "{{ item.dns | default(omit) }}" - interface_name: "{{ item.interface_name | default(omit) }}" - ipam_driver: "{{ item.ipam_driver | default(omit) }}" - ipv6: "{{ item.ipv6 | bool | default(omit) }}" - macvlan: "{{ item.macvlan | default(omit) }}" - net_config: "{{ item.net_config | default(omit) }}" - route: "{{ item.route | default(omit) }}" - recreate: "{{ item.recreate | bool | default(omit) }}" + name: "{{ network_item.name }}" + state: "{{ network_item.state | default('present') }}" + driver: "{{ network_item.driver | default('bridge') }}" + subnet: "{{ network_item.subnet | default(omit) }}" + gateway: "{{ network_item.gateway | default(omit) }}" + ip_range: "{{ network_item.ip_range | default(omit) }}" + disable_dns: "{{ network_item.disable_dns | bool | default(omit) }}" + internal: "{{ network_item.internal | bool | default(omit) }}" + opt: "{{ network_item.options | default(omit) }}" + dns: "{{ network_item.dns | default(omit) }}" + interface_name: "{{ network_item.interface_name | default(omit) }}" + ipam_driver: "{{ network_item.ipam_driver | default(omit) }}" + ipv6: "{{ network_item.ipv6 | bool | default(omit) }}" + macvlan: "{{ network_item.macvlan | default(omit) }}" + net_config: "{{ network_item.net_config | default(omit) }}" + route: "{{ network_item.route | default(omit) }}" + recreate: "{{ network_item.recreate | bool | default(omit) }}" + quadlet_dir: "{{ network_item.quadlet_dir | default(omit) }}" + quadlet_filename: "{{ network_item.quadlet_filename | default(omit) }}" + quadlet_file_mode: "{{ network_item.quadlet_file_mode | default(omit) }}" + quadlet_options: "{{ network_item.quadlet_options | default(omit) }}" loop: "{{ podman_networks }}" loop_control: label: "{{ item.name }}" + register: podman_network_output + notify: + - Reload systemd diff --git a/tasks/pods.yml b/tasks/pods.yml index 107f09d..e39756d 100644 --- a/tasks/pods.yml +++ b/tasks/pods.yml @@ -2,65 +2,70 @@ # Manage Podman pods - name: Manage Podman pods + vars: + pod_item: "{{ podman_pod_defaults | default({}) | combine(item) }}" containers.podman.podman_pod: - name: "{{ item.name }}" - state: "{{ item.state | default('created') }}" - publish: "{{ item.ports | default(omit) }}" - network: "{{ item.networks | default(omit) }}" - volume: "{{ item.volumes | default(omit) }}" - label: "{{ item.labels | default(omit) }}" - hostname: "{{ item.hostname | default(omit) }}" - infra: "{{ item.infra | default(omit) }}" - infra_image: "{{ item.infra_image | default(omit) }}" - infra_command: "{{ item.infra_command | default(omit) }}" - infra_name: "{{ item.infra_name | default(omit) }}" - add_host: "{{ item.add_host | default(omit) }}" - dns: "{{ item.dns | default(omit) }}" - dns_opt: "{{ item.dns_opt | default(omit) }}" - dns_search: "{{ item.dns_search | default(omit) }}" - ip: "{{ item.ip | default(omit) }}" - ip6: "{{ item.ip6 | default(omit) }}" - mac_address: "{{ item.mac_address | default(omit) }}" - no_hosts: "{{ item.no_hosts | default(omit) }}" - share: "{{ item.share | default(omit) }}" - share_parent: "{{ item.share_parent | default(omit) }}" - userns: "{{ item.userns | default(omit) }}" - uidmap: "{{ item.uidmap | default(omit) }}" - gidmap: "{{ item.gidmap | default(omit) }}" - subuidname: "{{ item.subuidname | default(omit) }}" - subgidname: "{{ item.subgidname | default(omit) }}" - security_opt: "{{ item.security_opt | default(omit) }}" - memory: "{{ item.memory | default(omit) }}" - memory_swap: "{{ item.memory_swap | default(omit) }}" - cpu_shares: "{{ item.cpu_shares | default(omit) }}" - cpus: "{{ item.cpus | default(omit) }}" - cpuset_cpus: "{{ item.cpuset_cpus | default(omit) }}" - cpuset_mems: "{{ item.cpuset_mems | default(omit) }}" - blkio_weight: "{{ item.blkio_weight | default(omit) }}" - blkio_weight_device: "{{ item.blkio_weight_device | default(omit) }}" - device: "{{ item.device | default(omit) }}" - device_read_bps: "{{ item.device_read_bps | default(omit) }}" - device_write_bps: "{{ item.device_write_bps | default(omit) }}" - shm_size: "{{ item.shm_size | default(omit) }}" - shm_size_systemd: "{{ item.shm_size_systemd | default(omit) }}" - sysctl: "{{ item.sysctl | default(omit) }}" - cgroup_parent: "{{ item.cgroup_parent | default(omit) }}" - pid: "{{ item.pid | default(omit) }}" - uts: "{{ item.uts | default(omit) }}" - network_alias: "{{ item.network_alias | default(omit) }}" - volumes_from: "{{ item.volumes_from | default(omit) }}" - exit_policy: "{{ item.exit_policy | default(omit) }}" - restart_policy: "{{ item.restart_policy | default(omit) }}" - pod_id_file: "{{ item.pod_id_file | default(omit) }}" - label_file: "{{ item.label_file | default(omit) }}" - gpus: "{{ item.gpus | default(omit) }}" - generate_systemd: "{{ item.generate_systemd | default(omit) }}" - quadlet_dir: "{{ item.quadlet_dir | default(omit) }}" - quadlet_filename: "{{ item.quadlet_filename | default(omit) }}" - quadlet_file_mode: "{{ item.quadlet_file_mode | default(omit) }}" - quadlet_options: "{{ item.quadlet_options | default(omit) }}" - recreate: "{{ item.recreate | default(omit) }}" - debug: "{{ item.debug | default(omit) }}" + name: "{{ pod_item.name }}" + state: "{{ pod_item.state | default('created') }}" + publish: "{{ pod_item.ports | default(omit) }}" + network: "{{ pod_item.networks | default(omit) }}" + volume: "{{ pod_item.volumes | default(omit) }}" + label: "{{ pod_item.labels | default(omit) }}" + hostname: "{{ pod_item.hostname | default(omit) }}" + infra: "{{ pod_item.infra | default(omit) }}" + infra_image: "{{ pod_item.infra_image | default(omit) }}" + infra_command: "{{ pod_item.infra_command | default(omit) }}" + infra_name: "{{ pod_item.infra_name | default(omit) }}" + add_host: "{{ pod_item.add_host | default(omit) }}" + dns: "{{ pod_item.dns | default(omit) }}" + dns_opt: "{{ pod_item.dns_opt | default(omit) }}" + dns_search: "{{ pod_item.dns_search | default(omit) }}" + ip: "{{ pod_item.ip | default(omit) }}" + ip6: "{{ pod_item.ip6 | default(omit) }}" + mac_address: "{{ pod_item.mac_address | default(omit) }}" + no_hosts: "{{ pod_item.no_hosts | default(omit) }}" + share: "{{ pod_item.share | default(omit) }}" + share_parent: "{{ pod_item.share_parent | default(omit) }}" + userns: "{{ pod_item.userns | default(omit) }}" + uidmap: "{{ pod_item.uidmap | default(omit) }}" + gidmap: "{{ pod_item.gidmap | default(omit) }}" + subuidname: "{{ pod_item.subuidname | default(omit) }}" + subgidname: "{{ pod_item.subgidname | default(omit) }}" + security_opt: "{{ pod_item.security_opt | default(omit) }}" + memory: "{{ pod_item.memory | default(omit) }}" + memory_swap: "{{ pod_item.memory_swap | default(omit) }}" + cpu_shares: "{{ pod_item.cpu_shares | default(omit) }}" + cpus: "{{ pod_item.cpus | default(omit) }}" + cpuset_cpus: "{{ pod_item.cpuset_cpus | default(omit) }}" + cpuset_mems: "{{ pod_item.cpuset_mems | default(omit) }}" + blkio_weight: "{{ pod_item.blkio_weight | default(omit) }}" + blkio_weight_device: "{{ pod_item.blkio_weight_device | default(omit) }}" + device: "{{ pod_item.device | default(omit) }}" + device_read_bps: "{{ pod_item.device_read_bps | default(omit) }}" + device_write_bps: "{{ pod_item.device_write_bps | default(omit) }}" + shm_size: "{{ pod_item.shm_size | default(omit) }}" + shm_size_systemd: "{{ pod_item.shm_size_systemd | default(omit) }}" + sysctl: "{{ pod_item.sysctl | default(omit) }}" + cgroup_parent: "{{ pod_item.cgroup_parent | default(omit) }}" + pid: "{{ pod_item.pid | default(omit) }}" + uts: "{{ pod_item.uts | default(omit) }}" + network_alias: "{{ pod_item.network_alias | default(omit) }}" + volumes_from: "{{ pod_item.volumes_from | default(omit) }}" + exit_policy: "{{ pod_item.exit_policy | default(omit) }}" + restart_policy: "{{ pod_item.restart_policy | default(omit) }}" + pod_id_file: "{{ pod_item.pod_id_file | default(omit) }}" + label_file: "{{ pod_item.label_file | default(omit) }}" + gpus: "{{ pod_item.gpus | default(omit) }}" + generate_systemd: "{{ pod_item.generate_systemd | default(omit) }}" + quadlet_dir: "{{ pod_item.quadlet_dir | default(omit) }}" + quadlet_filename: "{{ pod_item.quadlet_filename | default(omit) }}" + quadlet_file_mode: "{{ pod_item.quadlet_file_mode | default(omit) }}" + quadlet_options: "{{ pod_item.quadlet_options | default(omit) }}" + recreate: "{{ pod_item.recreate | default(omit) }}" + debug: "{{ pod_item.debug | default(omit) }}" loop: "{{ podman_pods }}" loop_control: label: "{{ item.name }}" + register: podman_pod_output + notify: + - Reload systemd diff --git a/tasks/volumes.yml b/tasks/volumes.yml index 656343c..b50745b 100644 --- a/tasks/volumes.yml +++ b/tasks/volumes.yml @@ -2,18 +2,23 @@ # Manage Podman volumes - name: Manage Podman volumes + vars: + volume_item: "{{ podman_volume_defaults | default({}) | combine(item) }}" containers.podman.podman_volume: - name: "{{ item.name }}" - state: "{{ item.state | default('present') }}" - driver: "{{ item.driver | default('local') }}" - options: "{{ item.options | default(omit) }}" - label: "{{ item.labels | default(omit) }}" - debug: "{{ item.debug | bool | default(omit) }}" - recreate: "{{ item.recreate | bool | default(omit) }}" - quadlet_dir: "{{ item.quadlet_dir | default(omit) }}" - quadlet_filename: "{{ item.quadlet_filename | default(omit) }}" - quadlet_file_mode: "{{ item.quadlet_file_mode | default(omit) }}" - quadlet_options: "{{ item.quadlet_options | default(omit) }}" + name: "{{ volume_item.name }}" + state: "{{ volume_item.state | default('present') }}" + driver: "{{ volume_item.driver | default('local') }}" + options: "{{ volume_item.options | default(omit) }}" + label: "{{ volume_item.labels | default(omit) }}" + debug: "{{ volume_item.debug | bool | default(omit) }}" + recreate: "{{ volume_item.recreate | bool | default(omit) }}" + quadlet_dir: "{{ volume_item.quadlet_dir | default(omit) }}" + quadlet_filename: "{{ volume_item.quadlet_filename | default(omit) }}" + quadlet_file_mode: "{{ volume_item.quadlet_file_mode | default(omit) }}" + quadlet_options: "{{ volume_item.quadlet_options | default(omit) }}" loop: "{{ podman_volumes }}" loop_control: label: "{{ item.name }}" + register: podman_volume_output + notify: + - Reload systemd