From e4cd2c449385b35b6dba2a7f0bdb20db239fe6ab Mon Sep 17 00:00:00 2001 From: DilasserT <49373066+DilasserT@users.noreply.github.com> Date: Fri, 11 Aug 2023 15:29:22 +0200 Subject: [PATCH] Adding volume import and export option (#617) * Adding volume import and export option adding volume import and volume export to podman_import and podman_export Updating integration tests Signed-off-by: DilasserT * Fixes and linting Signed-off-by: DilasserT --------- Signed-off-by: DilasserT --- plugins/modules/podman_export.py | 33 ++++++-- plugins/modules/podman_import.py | 41 +++++++++- .../targets/podman_export/tasks/main.yml | 82 ++++++++++++++++++- .../targets/podman_import/tasks/main.yml | 77 +++++++++++++++++ 4 files changed, 224 insertions(+), 9 deletions(-) diff --git a/plugins/modules/podman_export.py b/plugins/modules/podman_export.py index e2bb196..dda0099 100644 --- a/plugins/modules/podman_export.py +++ b/plugins/modules/podman_export.py @@ -24,7 +24,10 @@ options: description: - Container to export. type: str - required: true + volume: + description: + - Volume to export. + type: str force: description: - Force saving to file even if it exists. @@ -48,6 +51,9 @@ EXAMPLES = ''' - containers.podman.podman_export: dest: /path/to/tar/file container: container-name +- containers.podman.podman_export: + dest: /path/to/tar/file + volume: volume-name ''' import os # noqa: E402 @@ -57,8 +63,16 @@ from ..module_utils.podman.common import remove_file_or_dir # noqa: E402 def export(module, executable): changed = False - command = [executable, 'export'] - command += ['-o=%s' % module.params['dest'], module.params['container']] + export_type = '' + command = [] + if module.params['container']: + export_type = 'container' + command = [executable, 'export'] + else: + export_type = 'volume' + command = [executable, 'volume', 'export'] + + command += ['-o=%s' % module.params['dest'], module.params[export_type]] if module.params['force']: dest = module.params['dest'] if os.path.exists(dest): @@ -75,8 +89,8 @@ def export(module, executable): return changed, '', '' rc, out, err = module.run_command(command) if rc != 0: - module.fail_json(msg="Error exporting container %s: %s" % ( - module.params['container'], err)) + module.fail_json(msg="Error exporting %s %s: %s" % (export_type, + module.params['container'], err)) return changed, out, err @@ -84,11 +98,18 @@ def main(): module = AnsibleModule( argument_spec=dict( dest=dict(type='str', required=True), - container=dict(type='str', required=True), + container=dict(type='str'), + volume=dict(type='str'), force=dict(type='bool', default=True), executable=dict(type='str', default='podman') ), supports_check_mode=True, + mutually_exclusive=[ + ('container', 'volume'), + ], + required_one_of=[ + ('container', 'volume'), + ], ) executable = module.get_bin_path(module.params['executable'], required=True) diff --git a/plugins/modules/podman_import.py b/plugins/modules/podman_import.py index 5090b17..6a408c0 100644 --- a/plugins/modules/podman_import.py +++ b/plugins/modules/podman_import.py @@ -29,6 +29,10 @@ options: - Set changes as list of key-value pairs, see example. type: list elements: dict + volume: + description: + - Volume to import, cannot be used with change and commit_message + type: str executable: description: - Path to C(podman) executable if it is not in the C($PATH) on the @@ -95,6 +99,9 @@ EXAMPLES = ''' - "CMD": /bin/bash - "User": root commit_message: "Importing image" +- containers.podman.podman_import: + src: /path/to/tar/file + volume: myvolume ''' import json # noqa: E402 @@ -128,25 +135,55 @@ def load(module, executable): return changed, out, err, info, command +def volume_load(module, executable): + changed = True + command = [executable, 'volume', 'import', module.params['volume'], module.params['src']] + src = module.params['src'] + if module.check_mode: + return changed, '', '', '', command + rc, out, err = module.run_command(command) + if rc != 0: + module.fail_json(msg="Error importing volume %s: %s" % (src, err)) + rc, out2, err2 = module.run_command([executable, 'volume', 'inspect', module.params['volume']]) + if rc != 0: + module.fail_json(msg="Volume %s inspection failed: %s" % (module.params['volume'], err2)) + try: + info = json.loads(out2)[0] + except Exception as e: + module.fail_json(msg="Could not parse JSON from volume %s: %s" % (module.params['volume'], e)) + return changed, out, err, info, command + + def main(): module = AnsibleModule( argument_spec=dict( src=dict(type='str', required=True), commit_message=dict(type='str'), change=dict(type='list', elements='dict'), - executable=dict(type='str', default='podman') + executable=dict(type='str', default='podman'), + volume=dict(type='str', required=False), ), + mutually_exclusive=[ + ('volume', 'commit_message'), + ('volume', 'change'), + ], supports_check_mode=True, ) executable = module.get_bin_path(module.params['executable'], required=True) - changed, out, err, image_info, command = load(module, executable) + volume_info = '' + image_info = '' + if module.params['volume']: + changed, out, err, volume_info, command = volume_load(module, executable) + else: + changed, out, err, image_info, command = load(module, executable) results = { "changed": changed, "stdout": out, "stderr": err, "image": image_info, + "volume": volume_info, "podman_command": " ".join(command) } diff --git a/tests/integration/targets/podman_export/tasks/main.yml b/tests/integration/targets/podman_export/tasks/main.yml index 70f8d38..39ac61f 100644 --- a/tests/integration/targets/podman_export/tasks/main.yml +++ b/tests/integration/targets/podman_export/tasks/main.yml @@ -1,5 +1,5 @@ --- -- name: Test podman export +- name: Test podman container export block: - name: Start container containers.podman.podman_container: @@ -69,3 +69,83 @@ executable: "{{ test_executable | default('podman') }}" name: container state: absent + +- name: Test podman volume export + block: + - name: Start container + containers.podman.podman_container: + executable: "{{ test_executable | default('podman') }}" + name: container + image: alpine:3.7 + state: started + volume: + - "volume:/test" + command: sleep 1d + + - name: Export volume + containers.podman.podman_export: + executable: "{{ test_executable | default('podman') }}" + volume: volume + dest: /tmp/volume + + - name: Check file + stat: + path: /tmp/volume + register: vlm + + - name: Check it's exported + assert: + that: + - vlm.stat.exists + + - name: Import volume + containers.podman.podman_import: + executable: "{{ test_executable | default('podman') }}" + src: /tmp/volume + volume: "volume" + register: volume + + - name: Check it's imported + assert: + that: + - volume is success + + - name: Export volume without force + containers.podman.podman_export: + executable: "{{ test_executable | default('podman') }}" + volume: volume + dest: /tmp/volume + force: false + register: volume2 + + - name: Check it's exported + assert: + that: + - volume2 is success + - volume2 is not changed + + - name: Export volume with force + containers.podman.podman_export: + executable: "{{ test_executable | default('podman') }}" + volume: volume + dest: /tmp/volume + force: true + register: volume3 + + - name: Check it's not exported + assert: + that: + - volume3 is changed + + always: + - name: Remove container + containers.podman.podman_container: + executable: "{{ test_executable | default('podman') }}" + name: container + state: absent + + - name: Remove volume + containers.podman.podman_volume: + executable: "{{ test_executable | default('podman') }}" + name: test_volume + state: absent diff --git a/tests/integration/targets/podman_import/tasks/main.yml b/tests/integration/targets/podman_import/tasks/main.yml index db8254e..9ec1133 100644 --- a/tests/integration/targets/podman_import/tasks/main.yml +++ b/tests/integration/targets/podman_import/tasks/main.yml @@ -64,9 +64,86 @@ - test2.image.User == 'someuser' - test2.image["Config"]["Cmd"][2] == "/bin/nonsh" +- name: Test podman volume import + block: + - name: Start container + containers.podman.podman_container: + executable: "{{ test_executable | default('podman') }}" + name: container + image: alpine:3.7 + state: started + volume: + - "volume:/test" + command: touch /test/test_file + + - name: Export volume + containers.podman.podman_export: + executable: "{{ test_executable | default('podman') }}" + volume: volume + dest: /tmp/volume + + - name: Check file + stat: + path: /tmp/volume + register: vlm + + - name: Check it's exported + assert: + that: + - vlm.stat.exists + + - name: delete container + containers.podman.podman_container: + state: absent + name: container + + - name: delete volume + containers.podman.podman_volume: + state: absent + name: volume + + # podman needs a volume to exist before import + - name: creating volume before importing + containers.podman.podman_volume: + name: volume + state: present + + - name: Import volume + containers.podman.podman_import: + executable: "{{ test_executable | default('podman') }}" + src: /tmp/volume + volume: "volume" + register: volume + + - name: Check it's imported + assert: + that: + - volume is success + + - name: Check file is there + containers.podman.podman_container: + executable: "{{ test_executable | default('podman') }}" + name: container + image: alpine:3.7 + state: started + volume: + - "volume:/test" + command: ls /test/test_file + register: ls + + - name: Check it's imported + assert: + that: + - ls is success + always: - name: Remove container containers.podman.podman_container: executable: "{{ test_executable | default('podman') }}" name: container state: absent + - name: Remove volume + containers.podman.podman_volume: + executable: "{{ test_executable | default('podman') }}" + name: volume + state: absent