Skip to content

Commit bc3670f

Browse files
authored
Add podman_container_exec module (#627)
* Add podman_container_exec Signed-off-by: nishipy <[email protected]> * Update podman_container_exec Signed-off-by: nishipy <[email protected]> * Add tests for podman_container_exec Signed-off-by: nishipy <[email protected]> * Update examples for podman_container_exec Signed-off-by: nishipy <[email protected]> * Fix CI errors Signed-off-by: nishipy <[email protected]> * Fix CI errors Signed-off-by: nishipy <[email protected]> * Fix typo Signed-off-by: nishipy <[email protected]> * Add workflow for podman_container_exec Signed-off-by: nishipy <[email protected]> * Fix python version in the workflow for podman_container_exec Signed-off-by: nishipy <[email protected]> * Fix python version in the workflow for podman_container_exec Signed-off-by: nishipy <[email protected]> --------- Signed-off-by: nishipy <[email protected]>
1 parent c6ae4b9 commit bc3670f

File tree

4 files changed

+418
-0
lines changed

4 files changed

+418
-0
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
name: Podman container exec
2+
3+
on:
4+
push:
5+
paths:
6+
- '.github/workflows/podman_container_exec.yml'
7+
- 'ci/*.yml'
8+
- 'ci/run_containers_tests.sh'
9+
- 'ci/playbooks/containers/podman_container_exec.yml'
10+
- 'plugins/modules/podman_container_exec.py'
11+
- 'tests/integration/targets/podman_container_exec/**'
12+
branches:
13+
- master
14+
pull_request:
15+
paths:
16+
- '.github/workflows/podman_container_exec.yml'
17+
- 'ci/*.yml'
18+
- 'ci/run_containers_tests.sh'
19+
- 'ci/playbooks/containers/podman_container_exec.yml'
20+
- 'plugins/modules/podman_container_exec.py'
21+
- 'tests/integration/targets/podman_container_exec/**'
22+
schedule:
23+
- cron: 4 0 * * * # Run daily at 0:03 UTC
24+
25+
jobs:
26+
27+
test_podman_container_exec:
28+
name: Podman container exec ${{ 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.10"
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+
- name: Set up pip cache
63+
uses: actions/cache@v3
64+
with:
65+
path: ~/.cache/pip
66+
key: ${{ runner.os }}-pip-${{ github.ref }}-units-VMs
67+
restore-keys: |
68+
${{ runner.os }}-pip-
69+
${{ runner.os }}-
70+
- name: Install Ansible ${{ matrix.ansible-version }}
71+
run: python3 -m pip install --user --force-reinstall --upgrade '${{ matrix.ansible-version }}'
72+
73+
- name: Build and install the collection tarball
74+
run: |
75+
rm -rf /tmp/just_new_collection
76+
~/.local/bin/ansible-galaxy collection build --output-path /tmp/just_new_collection --force
77+
~/.local/bin/ansible-galaxy collection install -vvv --force /tmp/just_new_collection/*.tar.gz
78+
- name: Run collection tests for Podman container exec
79+
run: |
80+
export PATH=~/.local/bin:$PATH
81+
echo "Run ansible version"
82+
command -v ansible
83+
ansible --version
84+
export ANSIBLE_CONFIG=$(pwd)/ci/ansible-dev.cfg
85+
if [[ '${{ matrix.ansible-version }}' == 'ansible<2.10' ]]; then
86+
export ANSIBLE_CONFIG=$(pwd)/ci/ansible-2.9.cfg
87+
fi
88+
echo $ANSIBLE_CONFIG
89+
command -v ansible-playbook
90+
pip --version
91+
python --version
92+
ansible-playbook --version
93+
ansible-playbook -vv ci/playbooks/pre.yml \
94+
-e host=localhost \
95+
-i localhost, \
96+
-e ansible_connection=local \
97+
-e setup_python=false
98+
TEST2RUN=podman_container_exec ./ci/run_containers_tests.sh
99+
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_container_exec
7+
vars:
8+
ansible_python_interpreter: "{{ _ansible_python_interpreter }}"
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
#!/usr/bin/python
2+
# coding: utf-8 -*-
3+
4+
# Copyright (c) 2023, Takuya Nishimura <@nishipy>
5+
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6+
from __future__ import absolute_import, division, print_function
7+
__metaclass__ = type
8+
9+
DOCUMENTATION = r'''
10+
module: podman_container_exec
11+
author:
12+
- Takuya Nishimura (@nishipy)
13+
short_description: Executes a command in a running container.
14+
description:
15+
- Executes a command in a running container.
16+
options:
17+
name:
18+
description:
19+
- Name of the container where the command is executed.
20+
type: str
21+
required: true
22+
command:
23+
description:
24+
- The command to run in the container.
25+
- One of the I(command) or I(args) is required.
26+
type: str
27+
argv:
28+
description:
29+
- Passes the command as a list rather than a string.
30+
- One of the I(command) or I(args) is required.
31+
type: list
32+
elements: str
33+
detach:
34+
description:
35+
- If true, the command runs in the background.
36+
- The exec session is automatically removed when it completes.
37+
type: bool
38+
default: false
39+
env:
40+
description:
41+
- Set environment variables.
42+
type: dict
43+
privileged:
44+
description:
45+
- Give extended privileges to the container.
46+
type: bool
47+
default: false
48+
tty:
49+
description:
50+
- Allocate a pseudo-TTY.
51+
type: bool
52+
default: false
53+
user:
54+
description:
55+
- The username or UID used and, optionally, the groupname or GID for the specified command.
56+
- Both user and group may be symbolic or numeric.
57+
type: str
58+
workdir:
59+
description:
60+
- Working directory inside the container.
61+
type: str
62+
requirements:
63+
- podman
64+
notes:
65+
- See L(the Podman documentation,https://docs.podman.io/en/latest/markdown/podman-exec.1.html) for details of podman-exec(1).
66+
'''
67+
68+
EXAMPLES = r'''
69+
- name: Execute a command with workdir
70+
containers.podman.podman_container_exec:
71+
name: ubi8
72+
command: "cat redhat-release"
73+
workdir: /etc
74+
75+
- name: Execute a command with a list of args and enviroment variables
76+
containers.podman.podman_container_exec:
77+
name: test_container
78+
argv:
79+
- /bin/sh
80+
- -c
81+
- echo $HELLO $BYE
82+
env:
83+
HELLO: hello world
84+
BYE: goodbye world
85+
86+
- name: Execute command in background by using detach
87+
containers.podman.podman_container_exec:
88+
name: detach_container
89+
command: "cat redhat-release"
90+
detach: true
91+
'''
92+
93+
RETURN = r'''
94+
stdout:
95+
type: str
96+
returned: success
97+
description:
98+
- The standard output of the command executed in the container.
99+
stderr:
100+
type: str
101+
returned: success
102+
description:
103+
- The standard output of the command executed in the container.
104+
rc:
105+
type: int
106+
returned: success
107+
sample: 0
108+
description:
109+
- The exit code of the command executed in the container.
110+
exec_id:
111+
type: str
112+
returned: success and I(detach=true)
113+
sample: f99002e34c1087fd1aa08d5027e455bf7c2d6b74f019069acf6462a96ddf2a47
114+
description:
115+
- The ID of the exec session.
116+
'''
117+
118+
119+
import shlex
120+
from ansible.module_utils.six import string_types
121+
from ansible.module_utils._text import to_text
122+
from ansible.module_utils.basic import AnsibleModule
123+
from ansible_collections.containers.podman.plugins.module_utils.podman.common import run_podman_command
124+
125+
126+
def run_container_exec(module: AnsibleModule) -> dict:
127+
'''
128+
Execute podman-container-exec for the given options
129+
'''
130+
exec_with_args = ['container', 'exec']
131+
# podman_container_exec always returns changed=true
132+
changed = True
133+
exec_options = []
134+
135+
name = module.params['name']
136+
argv = module.params['argv']
137+
command = module.params['command']
138+
detach = module.params['detach']
139+
env = module.params['env']
140+
privileged = module.params['privileged']
141+
tty = module.params['tty']
142+
user = module.params['user']
143+
workdir = module.params['workdir']
144+
145+
if command is not None:
146+
argv = shlex.split(command)
147+
148+
if detach:
149+
exec_options.append('--detach')
150+
151+
if env is not None:
152+
for key, value in env.items():
153+
if not isinstance(value, string_types):
154+
module.fail_json(
155+
msg="Specify string value %s on the env field" % (value))
156+
157+
to_text(value, errors='surrogate_or_strict')
158+
exec_options += ['--env',
159+
'%s="%s"' % (key, value)]
160+
161+
if privileged:
162+
exec_options.append('--privileged')
163+
164+
if tty:
165+
exec_options.append('--tty')
166+
167+
if user is not None:
168+
exec_options += ['--user',
169+
to_text(user, errors='surrogate_or_strict')]
170+
171+
if workdir is not None:
172+
exec_options += ['--workdir',
173+
to_text(workdir, errors='surrogate_or_strict')]
174+
175+
exec_options.append(name)
176+
exec_options.extend(argv)
177+
178+
exec_with_args.extend(exec_options)
179+
180+
rc, stdout, stderr = run_podman_command(
181+
module=module, executable='podman', args=exec_with_args)
182+
183+
result = {
184+
'changed': changed,
185+
'podman_command': exec_options,
186+
'rc': rc,
187+
'stdout': stdout,
188+
'stderr': stderr,
189+
}
190+
191+
if detach:
192+
result['exec_id'] = stdout.replace('\n', '')
193+
194+
return result
195+
196+
197+
def main():
198+
argument_spec = {
199+
'name': {
200+
'type': 'str',
201+
'required': True,
202+
},
203+
'command': {
204+
'type': 'str',
205+
},
206+
'argv': {
207+
'type': 'list',
208+
'elements': 'str',
209+
},
210+
'detach': {
211+
'type': 'bool',
212+
'default': False,
213+
},
214+
'env': {
215+
'type': 'dict',
216+
},
217+
'privileged': {
218+
'type': 'bool',
219+
'default': False,
220+
},
221+
'tty': {
222+
'type': 'bool',
223+
'default': False,
224+
},
225+
'user': {
226+
'type': 'str',
227+
},
228+
'workdir': {
229+
'type': 'str',
230+
},
231+
}
232+
233+
module = AnsibleModule(
234+
argument_spec=argument_spec,
235+
supports_check_mode=True,
236+
required_one_of=[('argv', 'command')],
237+
)
238+
239+
result = run_container_exec(module)
240+
module.exit_json(**result)
241+
242+
243+
if __name__ == '__main__':
244+
main()

0 commit comments

Comments
 (0)