Using podman containers¶
Note
This example demonstrates the use of an ansible-native configuration.
This example demonstrates testing with Podman containers using standard Ansible inventory and playbooks for complete control over the testing environment.
Overview¶
This scenario uses:
- Inventory management: Standard Ansible inventory files for container definitions
- Container lifecycle: Managed through Ansible playbooks with full customization
- Flexible configuration: Container settings defined in inventory for easy modification
When you run molecule test --scenario-name podman, Molecule executes the complete test sequence including create, converge, verify, cleanup, and destroy steps using your custom playbooks.
Configuration¶
The configuration uses standard Ansible patterns:
---
ansible:
cfg:
defaults:
deprecation_warnings: false
executor:
args:
ansible_playbook:
- --inventory=inventory/
dependency:
name: galaxy
options:
requirements-file: ${MOLECULE_SCENARIO_DIRECTORY}/requirements.yml
scenario:
test_sequence:
- dependency
- destroy
- create
- converge
- idempotence
- verify
- cleanup
- destroy
Key points: The ansible section configures inventory location and executor arguments. The default driver indicates pure Ansible automation without Molecule-specific drivers.
Inventory Structure¶
Container definitions are managed through standard Ansible inventory:
---
all:
children:
molecule:
hosts:
molecule-fedora:
ansible_connection: containers.podman.podman
container_image: ghcr.io/ansible/community-ansible-dev-tools:latest
container_command: sleep 1d
container_privileged: false
Key points: Containers are defined as inventory hosts with connection and configuration details. The molecule group contains test targets.
---
# Molecule group variables for container configuration
container_log_driver: json-file
container_systemd: false
Key points: Group variables provide shared configuration for container settings.
Lifecycle Playbooks¶
Create Playbook¶
---
- name: Create container instances
hosts: localhost
gather_facts: false
tasks:
- name: Create containers from inventory
containers.podman.podman_container:
hostname: "{{ item }}"
name: "{{ item }}"
image: "{{ hostvars[item]['container_image'] }}"
command: "{{ hostvars[item]['container_command'] | default('sleep 1d') }}"
privileged: "{{ hostvars[item]['container_privileged'] | default(false) }}"
volumes: "{{ hostvars[item]['container_volumes'] | default(omit) }}"
capabilities: "{{ hostvars[item]['container_capabilities'] | default(omit) }}"
systemd: "{{ hostvars[item]['container_systemd'] | default(false) }}"
log_driver: "{{ hostvars[item]['container_log_driver'] | default('json-file') }}"
state: started
register: result
loop: "{{ groups['molecule'] }}"
- name: Verify containers are running
ansible.builtin.include_tasks:
file: tasks/create-fail.yml
when: >
item.container.State.ExitCode != 0 or
not item.container.State.Running
loop: "{{ result.results }}"
loop_control:
label: "{{ item.container.Name }}"
- name: Wait for containers to be ready
ansible.builtin.wait_for_connection:
timeout: 30
delegate_to: "{{ item }}"
loop: "{{ groups['molecule'] }}"
Key points: Creates containers based on inventory definitions using hostvars for configuration. Includes error handling and connection verification.
---
- name: Retrieve container log
ansible.builtin.command:
cmd: podman logs {{ item.container.Name }}
changed_when: false
register: logfile_cmd
- name: Display container log and fail
ansible.builtin.fail:
msg: |
Container {{ item.container.Name }} failed to start properly.
Exit Code: {{ item.container.State.ExitCode }}
Running: {{ item.container.State.Running }}
Log output: {{ logfile_cmd.stdout | default('No logs available') }}
Key points: Error handling task that displays container logs when creation fails.
Converge Playbook¶
---
- name: Converge
hosts: molecule
gather_facts: true
tasks:
- name: Read OS release information
ansible.builtin.slurp:
src: /etc/os-release
register: os_release
- name: Write OS info to file
ansible.builtin.copy:
content: "{{ os_release.content | b64decode }}"
dest: /tmp/molecule_os_info.txt
mode: "0644"
Key points: Validates inventory structure, gathers facts from containers, reads OS information, and writes data to a temporary file for verification. Uses proper Ansible modules instead of raw commands thanks to the Python-enabled container image.
Verify Playbook¶
---
- name: Verify
hosts: molecule
gather_facts: true
tasks:
- name: Read OS info file created during converge
ansible.builtin.slurp:
src: /tmp/molecule_os_info.txt
register: os_info_file
- name: Verify OS is Fedora-based
ansible.builtin.assert:
that:
- os_correct == true
- os_family_correct == true
fail_msg: "Expected Fedora Linux-based OS, but got: {{ os_info }} and {{ ansible_facts['os_family'] }}"
success_msg: "Successfully verified Fedora Linux-based container"
vars:
os: Fedora Linux
os_family: RedHat
os_info: "{{ os_info_file.content | b64decode }}"
os_correct: "{{ os in os_info }}"
os_family_correct: "{{ os_family == ansible_facts['os_family'] }}"
- name: Verify the dev tools environment variable
ansible.builtin.assert:
that:
- env_var_value == expected
fail_msg: "Expected {{ env_var }} to be {{ expected }}, but got: {{ env_var_value }}"
success_msg: "Successfully verified {{ env_var }}"
vars:
env_var: ANSIBLE_DEV_TOOLS_CONTAINER
env_var_value: "{{ ansible_env['ANSIBLE_DEV_TOOLS_CONTAINER'] }}"
expected: "1"
Key points: Reads the temporary file created during converge, verifies the container OS is Fedora-based, and validates the dev tools environment variable. Demonstrates comprehensive container testing and validation.
Cleanup Playbook¶
---
- name: Cleanup container instances
hosts: molecule
gather_facts: false
tasks:
- name: Check if container is running
containers.podman.podman_container_info:
name: "{{ inventory_hostname }}"
register: container_info
delegate_to: localhost
- name: Remove temporary files from running containers
ansible.builtin.file:
path: /tmp/molecule_os_info.txt
state: absent
when:
- container_info.containers | length > 0
- container_info.containers[0].State.Running
failed_when: false
Key points: Intelligently removes temporary files created during testing, but only when containers exist and are running. Includes robust error handling for edge cases.
Destroy Playbook¶
---
- name: Destroy container instances
hosts: localhost
gather_facts: false
tasks:
- name: Get info for all containers
containers.podman.podman_container_info:
name: "{{ item }}"
loop: "{{ groups['molecule'] }}"
register: podman_infos
- name: Kill container if running
containers.podman.podman_container:
name: "{{ item.item }}"
state: stopped
timeout: 2
loop: "{{ podman_infos.results }}"
loop_control:
label: "{{ item.item }}"
when:
- item.containers | length > 0
- item.containers[0].State.Status == "running"
Key points: Cleanly removes containers using inventory host definitions with error handling.
Benefits¶
- Standard practices: Uses familiar Ansible inventory and playbook patterns
- Full control: Complete customization of container lifecycle and configuration
- Modern tooling: Leverages Python-enabled container images for full Ansible module support
- Comprehensive testing: Includes converge, verify, and cleanup phases for thorough validation
- Reusable: Inventory can be shared across multiple scenarios
- Maintainable: Clear separation of concerns between configuration and automation
- Flexible: Easy to extend with additional container settings, networking, or testing logic
- Robust: Intelligent error handling and conditional execution for reliable testing
This approach provides the foundation for more complex testing scenarios while maintaining simplicity and demonstrating modern ansible-native practices with comprehensive lifecycle management.