Skip to content

Commit 7d1d016

Browse files
authored
added podman_prune module (#538)
* added podman-prune module --------- Signed-off-by: Roberto Alfieri <[email protected]> Signed-off-by: Roberto Alfieri <[email protected]>
1 parent 66f2706 commit 7d1d016

File tree

4 files changed

+498
-0
lines changed

4 files changed

+498
-0
lines changed

.github/workflows/podman_prune.yml

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
name: Podman prune
2+
3+
on:
4+
push:
5+
paths:
6+
- '.github/workflows/podman_prune.yml'
7+
- 'ci/*.yml'
8+
- 'ci/run_containers_tests.sh'
9+
- 'ci/playbooks/containers/podman_prune.yml'
10+
- 'plugins/modules/podman_prune.py'
11+
- 'tests/integration/targets/podman_prune/**'
12+
branches:
13+
- master
14+
pull_request:
15+
paths:
16+
- '.github/workflows/podman_prune.yml'
17+
- 'ci/*.yml'
18+
- 'ci/run_containers_tests.sh'
19+
- 'ci/playbooks/containers/podman_prune.yml'
20+
- 'plugins/modules/podman_prune.py'
21+
- 'tests/integration/targets/podman_prune/**'
22+
schedule:
23+
- cron: 4 0 * * * # Run daily at 0:03 UTC
24+
25+
jobs:
26+
27+
test_podman_prune:
28+
name: Podman prune ${{ matrix.ansible-version }}-${{ matrix.os || 'ubuntu-22.04' }}
29+
runs-on: ${{ matrix.os || 'ubuntu-22.04' }}
30+
defaults:
31+
run:
32+
shell: bash
33+
strategy:
34+
fail-fast: false
35+
matrix:
36+
ansible-version:
37+
- ansible<2.10
38+
# - git+https://github.com/ansible/[email protected]
39+
- git+https://github.com/ansible/ansible.git@devel
40+
os:
41+
- ubuntu-22.04
42+
python-version:
43+
- 3.9
44+
45+
steps:
46+
47+
- name: Check out repository
48+
uses: actions/checkout@v3
49+
50+
- name: Set up Python ${{ matrix.python-version }}
51+
uses: actions/setup-python@v4
52+
with:
53+
python-version: ${{ matrix.python-version }}
54+
55+
- name: Upgrade pip and display Python and PIP versions
56+
run: |
57+
sudo apt-get update
58+
sudo apt-get install -y python*-wheel python*-yaml
59+
python -m pip install --upgrade pip
60+
python -V
61+
pip --version
62+
63+
- name: Set up pip cache
64+
uses: actions/cache@v3
65+
with:
66+
path: ~/.cache/pip
67+
key: ${{ runner.os }}-pip-${{ github.ref }}-units-VMs
68+
restore-keys: |
69+
${{ runner.os }}-pip-
70+
${{ runner.os }}-
71+
72+
- name: Install Ansible ${{ matrix.ansible-version }}
73+
run: python3 -m pip install --user --force-reinstall --upgrade '${{ matrix.ansible-version }}'
74+
75+
- name: Build and install the collection tarball
76+
run: |
77+
rm -rf /tmp/just_new_collection
78+
~/.local/bin/ansible-galaxy collection build --output-path /tmp/just_new_collection --force
79+
~/.local/bin/ansible-galaxy collection install -vvv --force /tmp/just_new_collection/*.tar.gz
80+
81+
- name: Run collection tests for podman prune
82+
run: |
83+
export PATH=~/.local/bin:$PATH
84+
85+
echo "Run ansible version"
86+
command -v ansible
87+
ansible --version
88+
89+
export ANSIBLE_CONFIG=$(pwd)/ci/ansible-dev.cfg
90+
if [[ '${{ matrix.ansible-version }}' == 'ansible<2.10' ]]; then
91+
export ANSIBLE_CONFIG=$(pwd)/ci/ansible-2.9.cfg
92+
fi
93+
94+
echo $ANSIBLE_CONFIG
95+
command -v ansible-playbook
96+
pip --version
97+
python --version
98+
ansible-playbook --version
99+
100+
ansible-playbook -vv ci/playbooks/pre.yml \
101+
-e host=localhost \
102+
-i localhost, \
103+
-e ansible_connection=local \
104+
-e setup_python=false
105+
106+
TEST2RUN=podman_prune ./ci/run_containers_tests.sh
107+
shell: bash
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
- hosts: all
3+
gather_facts: true
4+
tasks:
5+
- include_role:
6+
name: podman_prune
7+
vars:
8+
ansible_python_interpreter: "/usr/bin/python"

plugins/modules/podman_prune.py

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
5+
# Copyright (c) 2023, Roberto Alfieri <[email protected]>
6+
7+
from __future__ import absolute_import, division, print_function
8+
__metaclass__ = type
9+
10+
DOCUMENTATION = r'''
11+
module: podman_prune
12+
author:
13+
- 'Roberto Alfieri (@rebtoor)'
14+
version_added: '1.10.0'
15+
short_description: Allows to prune various podman objects
16+
notes: []
17+
description:
18+
- Allows to run C(podman container prune), C(podman image prune), C(podman network prune),
19+
C(podman volume prune) and C(podman system prune)
20+
requirements:
21+
- 'Podman installed on host'
22+
options:
23+
executable:
24+
description:
25+
- Podman binary.
26+
type: str
27+
default: podman
28+
container:
29+
description:
30+
- Whether to prune containers.
31+
type: bool
32+
default: false
33+
container_filters:
34+
description:
35+
- A dictionary of filter values used for selecting containers to delete.
36+
- 'For example, C(until: 24h).'
37+
- See L(the podman documentation,
38+
https://docs.podman.io/en/latest/markdown/podman-container-prune.1.html#filter-filters)
39+
for more information on possible filters.
40+
type: dict
41+
image:
42+
description:
43+
- Whether to prune images.
44+
type: bool
45+
default: false
46+
image_filters:
47+
description:
48+
- A dictionary of filter values used for selecting images to delete.
49+
- 'You can also use C(dangling_only: false) to delete dangling and non-dangling images or C(external: true)
50+
to delete images even when they are used by external containers.'
51+
- See L(the podman documentation,
52+
https://docs.podman.io/en/latest/markdown/podman-image-prune.1.html#filter-filters)
53+
for more information on possible filters.
54+
type: dict
55+
network:
56+
description:
57+
- Whether to prune networks.
58+
type: bool
59+
default: false
60+
network_filters:
61+
description:
62+
- A dictionary of filter values used for selecting networks to delete.
63+
- See L(the podman documentation,
64+
https://docs.podman.io/en/latest/markdown/podman-network-prune.1.html#filter)
65+
for more information on possible filters.
66+
type: dict
67+
system:
68+
description:
69+
- Wheter to prune unused pods, containers, image, networks and volume data
70+
type: bool
71+
default: false
72+
system_all:
73+
description:
74+
- Wheter to prune all unused images, not only dangling images.
75+
type: bool
76+
default: false
77+
system_volumes:
78+
description:
79+
- Wheter to prune volumes currently unused by any container.
80+
type: bool
81+
default: false
82+
volume:
83+
description:
84+
- Whether to prune volumes.
85+
type: bool
86+
default: false
87+
volume_filters:
88+
description:
89+
- A dictionary of filter values used for selecting volumes to delete.
90+
- See L(the podman documentation,
91+
https://docs.podman.io/en/latest/markdown/podman-volume-prune.1.html#filter)
92+
for more information on possible filters.
93+
type: dict
94+
'''
95+
96+
EXAMPLES = r'''
97+
- name: Prune containers older than 24h
98+
containers.podman.podman_prune:
99+
containers: true
100+
containers_filters:
101+
# only consider containers created more than 24 hours ago
102+
until: 24h
103+
104+
- name: Prune everything
105+
containers.podman.podman_prune:
106+
system: true
107+
108+
- name: Prune everything (including non-dangling images)
109+
containers.podman.podman_prune:
110+
system: true
111+
system_all: true
112+
system_volumes: true
113+
'''
114+
115+
RETURN = r'''
116+
# containers
117+
containers:
118+
description:
119+
- List of IDs of deleted containers.
120+
returned: I(containers) is C(true)
121+
type: list
122+
elements: str
123+
sample: []
124+
125+
# images
126+
images:
127+
description:
128+
- List of IDs of deleted images.
129+
returned: I(images) is C(true)
130+
type: list
131+
elements: str
132+
sample: []
133+
134+
# networks
135+
networks:
136+
description:
137+
- List of IDs of deleted networks.
138+
returned: I(networks) is C(true)
139+
type: list
140+
elements: str
141+
sample: []
142+
143+
# volumes
144+
volumes:
145+
description:
146+
- List of IDs of deleted volumes.
147+
returned: I(volumes) is C(true)
148+
type: list
149+
elements: str
150+
sample: []
151+
152+
# system
153+
system:
154+
description:
155+
- List of ID of deleted containers, volumes, images, network and total reclaimed space
156+
returned: I(system) is C(true)
157+
type: list
158+
elements: str
159+
sample: []
160+
'''
161+
162+
163+
from ansible.module_utils.basic import AnsibleModule
164+
165+
166+
def filtersPrepare(target, filters):
167+
filter_out = []
168+
if target == 'system':
169+
for system_filter in filters:
170+
filter_out.append(filters[system_filter])
171+
else:
172+
for common_filter in filters:
173+
if isinstance(filters[common_filter], dict):
174+
dict_filters = filters[common_filter]
175+
for single_filter in dict_filters:
176+
filter_out.append('--filter={label}={key}={value}'.format(label=common_filter, key=single_filter,
177+
value=dict_filters[single_filter]))
178+
else:
179+
if target == 'image' and (common_filter in ('dangling_only', 'external')):
180+
if common_filter == 'dangling_only' and not filters['dangling_only']:
181+
filter_out.append('-a')
182+
if common_filter == 'external' and filters['external']:
183+
filter_out.append('--external')
184+
else:
185+
filter_out.append('--filter={label}={value}'.format(label=common_filter,
186+
value=filters[common_filter]))
187+
188+
return filter_out
189+
190+
191+
def podmanExec(module, target, filters, executable):
192+
command = [executable, target, 'prune', '--force']
193+
if filters is not None:
194+
command.extend(filtersPrepare(target, filters))
195+
rc, out, err = module.run_command(command)
196+
changed = bool(out)
197+
198+
if rc != 0:
199+
module.fail_json(
200+
msg='Error executing prune on {target}: {err}'.format(target=target, err=err))
201+
202+
return {
203+
"changed": changed,
204+
target: list(filter(None, out.split('\n'))),
205+
"errors": err
206+
}
207+
208+
209+
def main():
210+
results = dict()
211+
module_args = dict(
212+
container=dict(type='bool', default=False),
213+
container_filters=dict(type='dict'),
214+
image=dict(type='bool', default=False),
215+
image_filters=dict(type='dict'),
216+
network=dict(type='bool', default=False),
217+
network_filters=dict(type='dict'),
218+
volume=dict(type='bool', default=False),
219+
volume_filters=dict(type='dict'),
220+
system=dict(type='bool', default=False),
221+
system_all=dict(type='bool', default=False),
222+
system_volumes=dict(type='bool', default=False),
223+
executable=dict(type='str', default='podman')
224+
)
225+
226+
module = AnsibleModule(
227+
argument_spec=module_args
228+
)
229+
230+
executable = module.get_bin_path(
231+
module.params['executable'], required=True)
232+
233+
for target, filters in (
234+
('container', 'container_filters'), ('image', 'image_filters'), ('network', 'network_filters'),
235+
('volume', 'volume_filters')):
236+
if module.params[target]:
237+
results[target] = podmanExec(module, target, module.params[filters], executable)
238+
239+
if module.params['system']:
240+
target = 'system'
241+
system_filters = {}
242+
if module.params['system_all']:
243+
system_filters['system_all'] = '--all'
244+
if module.params['system_volumes']:
245+
system_filters['system_volumes'] = '--volumes'
246+
results[target] = podmanExec(module, target, system_filters, executable)
247+
248+
module.exit_json(**results)
249+
250+
251+
if __name__ == '__main__':
252+
main()

0 commit comments

Comments
 (0)