mirror of
https://github.com/containers/ansible-podman-collections.git
synced 2026-02-04 07:11:49 +00:00
added podman_prune module (#538)
* added podman-prune module --------- Signed-off-by: Roberto Alfieri <ralfieri@redhat.com> Signed-off-by: Roberto Alfieri <me@rebtoor.com>
This commit is contained in:
parent
66f2706464
commit
7d1d016fe9
4 changed files with 498 additions and 0 deletions
107
.github/workflows/podman_prune.yml
vendored
Normal file
107
.github/workflows/podman_prune.yml
vendored
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
name: Podman prune
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/podman_prune.yml'
|
||||
- 'ci/*.yml'
|
||||
- 'ci/run_containers_tests.sh'
|
||||
- 'ci/playbooks/containers/podman_prune.yml'
|
||||
- 'plugins/modules/podman_prune.py'
|
||||
- 'tests/integration/targets/podman_prune/**'
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/podman_prune.yml'
|
||||
- 'ci/*.yml'
|
||||
- 'ci/run_containers_tests.sh'
|
||||
- 'ci/playbooks/containers/podman_prune.yml'
|
||||
- 'plugins/modules/podman_prune.py'
|
||||
- 'tests/integration/targets/podman_prune/**'
|
||||
schedule:
|
||||
- cron: 4 0 * * * # Run daily at 0:03 UTC
|
||||
|
||||
jobs:
|
||||
|
||||
test_podman_prune:
|
||||
name: Podman prune ${{ matrix.ansible-version }}-${{ matrix.os || 'ubuntu-22.04' }}
|
||||
runs-on: ${{ matrix.os || 'ubuntu-22.04' }}
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ansible-version:
|
||||
- ansible<2.10
|
||||
# - git+https://github.com/ansible/ansible.git@stable-2.11
|
||||
- git+https://github.com/ansible/ansible.git@devel
|
||||
os:
|
||||
- ubuntu-22.04
|
||||
python-version:
|
||||
- 3.9
|
||||
|
||||
steps:
|
||||
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Upgrade pip and display Python and PIP versions
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y python*-wheel python*-yaml
|
||||
python -m pip install --upgrade pip
|
||||
python -V
|
||||
pip --version
|
||||
|
||||
- name: Set up pip cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ github.ref }}-units-VMs
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Install Ansible ${{ matrix.ansible-version }}
|
||||
run: python3 -m pip install --user --force-reinstall --upgrade '${{ matrix.ansible-version }}'
|
||||
|
||||
- name: Build and install the collection tarball
|
||||
run: |
|
||||
rm -rf /tmp/just_new_collection
|
||||
~/.local/bin/ansible-galaxy collection build --output-path /tmp/just_new_collection --force
|
||||
~/.local/bin/ansible-galaxy collection install -vvv --force /tmp/just_new_collection/*.tar.gz
|
||||
|
||||
- name: Run collection tests for podman prune
|
||||
run: |
|
||||
export PATH=~/.local/bin:$PATH
|
||||
|
||||
echo "Run ansible version"
|
||||
command -v ansible
|
||||
ansible --version
|
||||
|
||||
export ANSIBLE_CONFIG=$(pwd)/ci/ansible-dev.cfg
|
||||
if [[ '${{ matrix.ansible-version }}' == 'ansible<2.10' ]]; then
|
||||
export ANSIBLE_CONFIG=$(pwd)/ci/ansible-2.9.cfg
|
||||
fi
|
||||
|
||||
echo $ANSIBLE_CONFIG
|
||||
command -v ansible-playbook
|
||||
pip --version
|
||||
python --version
|
||||
ansible-playbook --version
|
||||
|
||||
ansible-playbook -vv ci/playbooks/pre.yml \
|
||||
-e host=localhost \
|
||||
-i localhost, \
|
||||
-e ansible_connection=local \
|
||||
-e setup_python=false
|
||||
|
||||
TEST2RUN=podman_prune ./ci/run_containers_tests.sh
|
||||
shell: bash
|
||||
8
ci/playbooks/containers/podman_prune.yml
Normal file
8
ci/playbooks/containers/podman_prune.yml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
- hosts: all
|
||||
gather_facts: true
|
||||
tasks:
|
||||
- include_role:
|
||||
name: podman_prune
|
||||
vars:
|
||||
ansible_python_interpreter: "/usr/bin/python"
|
||||
252
plugins/modules/podman_prune.py
Normal file
252
plugins/modules/podman_prune.py
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# Copyright (c) 2023, Roberto Alfieri <ralfieri@redhat.com>
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
module: podman_prune
|
||||
author:
|
||||
- 'Roberto Alfieri (@rebtoor)'
|
||||
version_added: '1.10.0'
|
||||
short_description: Allows to prune various podman objects
|
||||
notes: []
|
||||
description:
|
||||
- Allows to run C(podman container prune), C(podman image prune), C(podman network prune),
|
||||
C(podman volume prune) and C(podman system prune)
|
||||
requirements:
|
||||
- 'Podman installed on host'
|
||||
options:
|
||||
executable:
|
||||
description:
|
||||
- Podman binary.
|
||||
type: str
|
||||
default: podman
|
||||
container:
|
||||
description:
|
||||
- Whether to prune containers.
|
||||
type: bool
|
||||
default: false
|
||||
container_filters:
|
||||
description:
|
||||
- A dictionary of filter values used for selecting containers to delete.
|
||||
- 'For example, C(until: 24h).'
|
||||
- See L(the podman documentation,
|
||||
https://docs.podman.io/en/latest/markdown/podman-container-prune.1.html#filter-filters)
|
||||
for more information on possible filters.
|
||||
type: dict
|
||||
image:
|
||||
description:
|
||||
- Whether to prune images.
|
||||
type: bool
|
||||
default: false
|
||||
image_filters:
|
||||
description:
|
||||
- A dictionary of filter values used for selecting images to delete.
|
||||
- 'You can also use C(dangling_only: false) to delete dangling and non-dangling images or C(external: true)
|
||||
to delete images even when they are used by external containers.'
|
||||
- See L(the podman documentation,
|
||||
https://docs.podman.io/en/latest/markdown/podman-image-prune.1.html#filter-filters)
|
||||
for more information on possible filters.
|
||||
type: dict
|
||||
network:
|
||||
description:
|
||||
- Whether to prune networks.
|
||||
type: bool
|
||||
default: false
|
||||
network_filters:
|
||||
description:
|
||||
- A dictionary of filter values used for selecting networks to delete.
|
||||
- See L(the podman documentation,
|
||||
https://docs.podman.io/en/latest/markdown/podman-network-prune.1.html#filter)
|
||||
for more information on possible filters.
|
||||
type: dict
|
||||
system:
|
||||
description:
|
||||
- Wheter to prune unused pods, containers, image, networks and volume data
|
||||
type: bool
|
||||
default: false
|
||||
system_all:
|
||||
description:
|
||||
- Wheter to prune all unused images, not only dangling images.
|
||||
type: bool
|
||||
default: false
|
||||
system_volumes:
|
||||
description:
|
||||
- Wheter to prune volumes currently unused by any container.
|
||||
type: bool
|
||||
default: false
|
||||
volume:
|
||||
description:
|
||||
- Whether to prune volumes.
|
||||
type: bool
|
||||
default: false
|
||||
volume_filters:
|
||||
description:
|
||||
- A dictionary of filter values used for selecting volumes to delete.
|
||||
- See L(the podman documentation,
|
||||
https://docs.podman.io/en/latest/markdown/podman-volume-prune.1.html#filter)
|
||||
for more information on possible filters.
|
||||
type: dict
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Prune containers older than 24h
|
||||
containers.podman.podman_prune:
|
||||
containers: true
|
||||
containers_filters:
|
||||
# only consider containers created more than 24 hours ago
|
||||
until: 24h
|
||||
|
||||
- name: Prune everything
|
||||
containers.podman.podman_prune:
|
||||
system: true
|
||||
|
||||
- name: Prune everything (including non-dangling images)
|
||||
containers.podman.podman_prune:
|
||||
system: true
|
||||
system_all: true
|
||||
system_volumes: true
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
# containers
|
||||
containers:
|
||||
description:
|
||||
- List of IDs of deleted containers.
|
||||
returned: I(containers) is C(true)
|
||||
type: list
|
||||
elements: str
|
||||
sample: []
|
||||
|
||||
# images
|
||||
images:
|
||||
description:
|
||||
- List of IDs of deleted images.
|
||||
returned: I(images) is C(true)
|
||||
type: list
|
||||
elements: str
|
||||
sample: []
|
||||
|
||||
# networks
|
||||
networks:
|
||||
description:
|
||||
- List of IDs of deleted networks.
|
||||
returned: I(networks) is C(true)
|
||||
type: list
|
||||
elements: str
|
||||
sample: []
|
||||
|
||||
# volumes
|
||||
volumes:
|
||||
description:
|
||||
- List of IDs of deleted volumes.
|
||||
returned: I(volumes) is C(true)
|
||||
type: list
|
||||
elements: str
|
||||
sample: []
|
||||
|
||||
# system
|
||||
system:
|
||||
description:
|
||||
- List of ID of deleted containers, volumes, images, network and total reclaimed space
|
||||
returned: I(system) is C(true)
|
||||
type: list
|
||||
elements: str
|
||||
sample: []
|
||||
'''
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def filtersPrepare(target, filters):
|
||||
filter_out = []
|
||||
if target == 'system':
|
||||
for system_filter in filters:
|
||||
filter_out.append(filters[system_filter])
|
||||
else:
|
||||
for common_filter in filters:
|
||||
if isinstance(filters[common_filter], dict):
|
||||
dict_filters = filters[common_filter]
|
||||
for single_filter in dict_filters:
|
||||
filter_out.append('--filter={label}={key}={value}'.format(label=common_filter, key=single_filter,
|
||||
value=dict_filters[single_filter]))
|
||||
else:
|
||||
if target == 'image' and (common_filter in ('dangling_only', 'external')):
|
||||
if common_filter == 'dangling_only' and not filters['dangling_only']:
|
||||
filter_out.append('-a')
|
||||
if common_filter == 'external' and filters['external']:
|
||||
filter_out.append('--external')
|
||||
else:
|
||||
filter_out.append('--filter={label}={value}'.format(label=common_filter,
|
||||
value=filters[common_filter]))
|
||||
|
||||
return filter_out
|
||||
|
||||
|
||||
def podmanExec(module, target, filters, executable):
|
||||
command = [executable, target, 'prune', '--force']
|
||||
if filters is not None:
|
||||
command.extend(filtersPrepare(target, filters))
|
||||
rc, out, err = module.run_command(command)
|
||||
changed = bool(out)
|
||||
|
||||
if rc != 0:
|
||||
module.fail_json(
|
||||
msg='Error executing prune on {target}: {err}'.format(target=target, err=err))
|
||||
|
||||
return {
|
||||
"changed": changed,
|
||||
target: list(filter(None, out.split('\n'))),
|
||||
"errors": err
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
results = dict()
|
||||
module_args = dict(
|
||||
container=dict(type='bool', default=False),
|
||||
container_filters=dict(type='dict'),
|
||||
image=dict(type='bool', default=False),
|
||||
image_filters=dict(type='dict'),
|
||||
network=dict(type='bool', default=False),
|
||||
network_filters=dict(type='dict'),
|
||||
volume=dict(type='bool', default=False),
|
||||
volume_filters=dict(type='dict'),
|
||||
system=dict(type='bool', default=False),
|
||||
system_all=dict(type='bool', default=False),
|
||||
system_volumes=dict(type='bool', default=False),
|
||||
executable=dict(type='str', default='podman')
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=module_args
|
||||
)
|
||||
|
||||
executable = module.get_bin_path(
|
||||
module.params['executable'], required=True)
|
||||
|
||||
for target, filters in (
|
||||
('container', 'container_filters'), ('image', 'image_filters'), ('network', 'network_filters'),
|
||||
('volume', 'volume_filters')):
|
||||
if module.params[target]:
|
||||
results[target] = podmanExec(module, target, module.params[filters], executable)
|
||||
|
||||
if module.params['system']:
|
||||
target = 'system'
|
||||
system_filters = {}
|
||||
if module.params['system_all']:
|
||||
system_filters['system_all'] = '--all'
|
||||
if module.params['system_volumes']:
|
||||
system_filters['system_volumes'] = '--volumes'
|
||||
results[target] = podmanExec(module, target, system_filters, executable)
|
||||
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
131
tests/integration/targets/podman_prune/tasks/main.yml
Normal file
131
tests/integration/targets/podman_prune/tasks/main.yml
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
- name: Create random names
|
||||
ansible.builtin.set_fact:
|
||||
cname: "{{ 'ansible-container-%0x' % ((2**32) | random) }}"
|
||||
nname: "{{ 'ansible-network-%0x' % ((2**32) | random) }}"
|
||||
vname: "{{ 'ansible-volume-%0x' % ((2**32) | random) }}"
|
||||
|
||||
- name: Test podman_prune module
|
||||
block:
|
||||
# Create objects to be pruned
|
||||
- name: Create container
|
||||
containers.podman.podman_container:
|
||||
name: "{{ cname }}"
|
||||
image: quay.io/podman/hello:latest
|
||||
state: present
|
||||
register: container
|
||||
|
||||
- name: Create network
|
||||
containers.podman.podman_network:
|
||||
name: "{{ nname }}"
|
||||
state: present
|
||||
register: network
|
||||
|
||||
- name: Create volume
|
||||
containers.podman.podman_volume:
|
||||
name: "{{ vname }}"
|
||||
state: present
|
||||
register: volume
|
||||
|
||||
# Prune objects
|
||||
- name: Prune objects
|
||||
containers.podman.podman_prune:
|
||||
container: true
|
||||
network: true
|
||||
volume: true
|
||||
|
||||
- name: Check if container exists
|
||||
containers.podman.podman_container_info:
|
||||
register: container_exists
|
||||
|
||||
- name: Check if podman network exists
|
||||
containers.podman.podman_network_info:
|
||||
register: network_exists
|
||||
|
||||
- name: Check if podman volume exists
|
||||
containers.podman.podman_volume_info:
|
||||
register: volume_exists
|
||||
|
||||
- name: Verify assertions for network, container and volume
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
# containers
|
||||
- container.container.Id not in container_exists.containers | map(attribute='Name') | list | flatten
|
||||
# networks
|
||||
- network.network.name not in network_exists.networks | map(attribute='id') | list | flatten
|
||||
# volumes
|
||||
- volume.volume.Name not in volume_exists.volumes | map(attribute='Name') | list | flatten
|
||||
|
||||
# Test with filters
|
||||
- name: Prune objects with filters
|
||||
containers.podman.podman_prune:
|
||||
image: true
|
||||
image_filters:
|
||||
dangling_only: false
|
||||
external: true
|
||||
|
||||
- name: Check if image exists
|
||||
containers.podman.podman_image_info:
|
||||
register: image_exists
|
||||
|
||||
- name: Verify assertions for image (with filters)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- image_exists.images | length == 0
|
||||
|
||||
- name: Create container
|
||||
containers.podman.podman_container:
|
||||
name: "{{ cname }}"
|
||||
image: quay.io/podman/hello:latest
|
||||
state: present
|
||||
register: container_system
|
||||
|
||||
- name: Create network
|
||||
containers.podman.podman_network:
|
||||
name: "{{ nname }}"
|
||||
state: present
|
||||
register: network_system
|
||||
|
||||
- name: Create volume
|
||||
containers.podman.podman_volume:
|
||||
name: "{{ vname }}"
|
||||
state: present
|
||||
register: volume_system
|
||||
|
||||
- name: System prune
|
||||
containers.podman.podman_prune:
|
||||
system: true
|
||||
system_all: true
|
||||
system_volumes: true
|
||||
|
||||
- name: Check if container exists
|
||||
containers.podman.podman_container_info:
|
||||
register: container_system_exists
|
||||
|
||||
- name: Check if podman network exists
|
||||
containers.podman.podman_network_info:
|
||||
register: network_system_exists
|
||||
|
||||
- name: Check if podman volume exists
|
||||
containers.podman.podman_volume_info:
|
||||
register: volume_system_exists
|
||||
|
||||
- name: Check if image exists
|
||||
containers.podman.podman_image_info:
|
||||
register: image_system_exists
|
||||
|
||||
- name: Verify assertions for system
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
# container
|
||||
- container_system.container.Id not in container_system_exists.containers | map(attribute='Name') | list | flatten
|
||||
# networks
|
||||
- network_system.network.name not in network_system_exists.networks | map(attribute='id') | list | flatten
|
||||
# volumes
|
||||
- volume_system.volume.Name not in volume_system_exists.volumes | map(attribute='Name') | list | flatten
|
||||
# images
|
||||
- image_system_exists.images | length == 0
|
||||
|
||||
always:
|
||||
- name: Cleanup
|
||||
ansible.builtin.command: podman system prune -a -f --volumes
|
||||
ignore_errors: true
|
||||
Loading…
Add table
Add a link
Reference in a new issue