Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: add podman_container_copy module #813

Merged
merged 1 commit into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions .github/workflows/podman_container_copy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: Podman Container Copy module

on:
push:
paths:
- '.github/workflows/podman_container_copy.yml'
- 'ci/*.yml'
- 'ci/run_containers_tests.sh'
- 'ci/playbooks/containers/podman_container_copy.yml'
- 'plugins/modules/podman_container_copy.py'
- 'tests/integration/targets/podman_container_copy/**'
branches:
- master
pull_request:
paths:
- '.github/workflows/podman_container_copy.yml'
- 'ci/*.yml'
- 'ci/run_containers_tests.sh'
- 'ci/playbooks/containers/podman_container_copy.yml'
- 'plugins/modules/podman_container_copy.py'
- 'tests/integration/targets/podman_container_copy/**'
schedule:
- cron: 4 0 * * * # Run daily at 0:03 UTC

jobs:

test_podman_container_copy:
name: Podman Container Copy ${{ 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/[email protected]
- git+https://github.com/ansible/ansible.git@devel
os:
- ubuntu-22.04
python-version:
- 3.11

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 Container Copy module
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_container_copy ./ci/run_containers_tests.sh
shell: bash
8 changes: 8 additions & 0 deletions ci/playbooks/containers/podman_container_copy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
- hosts: all
gather_facts: true
tasks:
- include_role:
name: podman_container_copy
vars:
ansible_python_interpreter: "{{ _ansible_python_interpreter }}"
131 changes: 131 additions & 0 deletions plugins/modules/podman_container_copy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#!/usr/bin/python
# Copyright (c) 2024 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function
__metaclass__ = type


DOCUMENTATION = r'''
module: podman_container_copy
author:
- Alessandro Rossi (@kubealex)
short_description: Copy file to/from a container
notes:
- Podman may required elevated privileges in order to run properly.
description:
- Copy file or folder from the host to a container and vice-versa.
options:
executable:
description:
- Path to C(podman) executable if it is not in the C($PATH) on the machine running C(podman)
default: 'podman'
type: str
src:
description:
- Path of the file/folder to copy from/to the container
type: str
required: True
dest:
description:
- Path of the destination file/folder to copy from/to the container
required: True
type: str
container:
description:
- Name/ID of the container to copy from/to
required: True
type: str
from_container:
description:
- Specify whether or not the file must be copied from the container to the host
required: False
default: False
type: bool
archive:
description:
- Chown copied files to the primary uid/gid of the destination container.
required: False
default: True
type: bool
overwrite:
description:
- Allow to overwrite directories with non-directories and vice versa
required: False
default: False
type: bool
'''

EXAMPLES = r"""
- name: Copy file "test.yml" on the host to the "apache" container's root folder
containers.podman.podman_search:
src: test.yml
dest: /
container: apache
- name: Copy file "test.yml" in the "apache" container's root folder to the playbook's folder
containers.podman.podman_search:
src: /test.yml
dest: ./
container: apache
from_container: True
"""

from ansible.module_utils.basic import AnsibleModule


def copy_file(module, executable, src, dest, container, from_container, archive, overwrite):
if from_container:
command = [executable, 'cp', '{0}:{1}'.format(container, src), dest]
else:
command = [executable, 'cp', src, '{0}:{1}'.format(container, dest)]

if not archive:
command.append('--archive=False')

if overwrite:
command.append('--overwrite')

rc, out, err = module.run_command(command)

if rc != 0:
module.fail_json(msg='Unable to copy file to/from container - {out}'.format(out=err))
else:
changed = True
return changed, out, err


def main():
module = AnsibleModule(
argument_spec=dict(
executable=dict(type='str', default='podman'),
src=dict(type='str', required=True),
dest=dict(type='str', required=True),
container=dict(type='str', required=True),
from_container=dict(type='bool', required=False, default=False),
archive=dict(type='bool', required=False, default=True),
overwrite=dict(type='bool', required=False, default=False)
),
supports_check_mode=False,
)

executable = module.params['executable']
src = module.params['src']
dest = module.params['dest']
container = module.params['container']
from_container = module.params['from_container']
archive = module.params['archive']
overwrite = module.params['overwrite']

executable = module.get_bin_path(executable, required=True)

changed, out, err = copy_file(module, executable, src, dest, container, from_container, archive, overwrite)

results = dict(
changed=changed
)

module.exit_json(**results)


if __name__ == '__main__':
main()
104 changes: 104 additions & 0 deletions tests/integration/targets/podman_container_copy/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
---
- name: Test podman container copy host-container
block:
- name: Generate random value for container name
ansible.builtin.set_fact:
container_name: "{{ 'ansible-test-podman-%0x' % ((2**32) | random) }}"
file_name: sample_file

- name: Start container
containers.podman.podman_container:
executable: "{{ test_executable | default('podman') }}"
name: "{{ container_name }}"
image: alpine:3.7
state: started
command: sleep 1d

- name: Create local file to copy
ansible.builtin.copy:
content: |
This file tests if host -> container copy is working
dest: "{{ playbook_dir }}/{{ file_name }}"
mode: "0755"

- name: Copy local file to containers root folder
containers.podman.podman_container_copy:
executable: "{{ test_executable | default('podman') }}"
src: "{{ playbook_dir }}/{{ file_name }}"
dest: "/{{ file_name }}"
container: "{{ container_name }}"

- name: Verify that the file exists in the container
containers.podman.podman_container_exec:
executable: "{{ test_executable | default('podman') }}"
name: "{{ container_name }}"
command: "cat /{{ file_name }}"

always:
- name: Remove container
containers.podman.podman_container:
executable: "{{ test_executable | default('podman') }}"
name: "{{ container_name }}"
state: absent

- name: Remove local file
ansible.builtin.file:
path: "{{ playbook_dir }}/{{ file_name }}"
state: absent

- name: Test podman container copy container-host
block:
- name: Generate random value for container name
ansible.builtin.set_fact:
container_name: "{{ 'ansible-test-podman-%0x' % ((2**32) | random) }}"
file_name: sample_file

- name: Start container
containers.podman.podman_container:
executable: "{{ test_executable | default('podman') }}"
name: "{{ container_name }}"
image: alpine:3.7
state: started
command: sleep 1d

- name: Create file in the container for further copy
containers.podman.podman_container_exec:
executable: "{{ test_executable | default('podman') }}"
name: "{{ container_name }}"
command: sh -c 'echo "This file tests if container -> host copy is working" > /{{ file_name }}'

- name: Verify that the file exists in the container
containers.podman.podman_container_exec:
executable: "{{ test_executable | default('podman') }}"
name: "{{ container_name }}"
command: "cat /{{ file_name }}"

- name: Copy local file to containers root folder
containers.podman.podman_container_copy:
executable: "{{ test_executable | default('podman') }}"
src: "/{{ file_name }}"
dest: "{{ playbook_dir }}/{{ file_name }}"
container: "{{ container_name }}"
from_container: true

- name: Check file
ansible.builtin.stat:
path: "{{ playbook_dir }}/{{ file_name }}"
register: copied_file

- name: Check it's present
ansible.builtin.assert:
that:
- copied_file.stat.exists

always:
- name: Remove container
containers.podman.podman_container:
executable: "{{ test_executable | default('podman') }}"
name: "{{ container_name }}"
state: absent

- name: Remove local file
ansible.builtin.file:
path: "{{ playbook_dir }}/{{ file_name }}"
state: absent
Loading