diff --git a/plugins/module_utils/podman/common.py b/plugins/module_utils/podman/common.py index 2eefcb2d..d890043e 100644 --- a/plugins/module_utils/podman/common.py +++ b/plugins/module_utils/podman/common.py @@ -306,3 +306,15 @@ def normalize_signal(signal_name_or_number): if signal_name not in _signal_map: raise RuntimeError("Unknown signal '{0}'".format(signal_name_or_number)) return str(_signal_map[signal_name]) + + +def get_podman_version(module, fail=True): + executable = module.params['executable'] if module.params['executable'] else 'podman' + rc, out, err = module.run_command( + [executable, b'--version']) + if rc != 0 or not out or "version" not in out: + if fail: + module.fail_json(msg="'%s --version' run failed! Error: %s" % + (executable, err)) + return None + return out.split("version")[1].strip() diff --git a/plugins/modules/podman_secret.py b/plugins/modules/podman_secret.py index fc8ec1f1..a9a5bb69 100644 --- a/plugins/modules/podman_secret.py +++ b/plugins/modules/podman_secret.py @@ -61,6 +61,11 @@ choices: - absent - present + debug: + description: + - Enable debug mode for module. + type: bool + default: False ''' EXAMPLES = r""" @@ -91,19 +96,68 @@ """ from ansible.module_utils.basic import AnsibleModule +from ansible_collections.containers.podman.plugins.module_utils.podman.common import LooseVersion +from ansible_collections.containers.podman.plugins.module_utils.podman.common import get_podman_version +diff = {"before": '', "after": ''} -def podman_secret_create(module, executable, name, data, force, skip, - driver, driver_opts): - if force: - module.run_command([executable, 'secret', 'rm', name]) - if skip: + +def podman_secret_exists(module, executable, name, version): + if version is None or LooseVersion(version) < LooseVersion('4.5.0'): rc, out, err = module.run_command( [executable, 'secret', 'ls', "--format", "{{.Name}}"]) - if name in [i.strip() for i in out.splitlines()]: - return { - "changed": False, - } + return name in [i.strip() for i in out.splitlines()] + rc, out, err = module.run_command( + [executable, 'secret', 'exists', name]) + return rc == 0 + + +def need_update(module, executable, name, data, driver, driver_opts, debug): + + cmd = [executable, 'secret', 'inspect', '--showsecret', name] + rc, out, err = module.run_command(cmd) + if rc != 0: + if debug: + module.log("PODMAN-SECRET-DEBUG: Unable to get secret info: %s" % err) + return True + try: + secret = module.from_json(out)[0] + # We support only file driver for now + if (driver and driver != 'file') or secret['Spec']['Driver']['Name'] != 'file': + if debug: + module.log("PODMAN-SECRET-DEBUG: Idempotency of driver %s is not supported" % driver) + return True + if secret['SecretData'] != data: + diff['after'] = "" + diff['before'] = "" + return True + if driver_opts: + for k, v in driver_opts.items(): + if secret['Spec']['Driver']['Options'].get(k) != v: + diff['after'] = "=".join([k, v]) + diff['before'] = "=".join( + [k, secret['Spec']['Driver']['Options'].get(k)]) + return True + except Exception: + return True + return False + + +def podman_secret_create(module, executable, name, data, force, skip, + driver, driver_opts, debug): + podman_version = get_podman_version(module, fail=False) + if (podman_version is not None and + LooseVersion(podman_version) >= LooseVersion('4.7.0') + and (driver is None or driver == 'file')): + if not skip and need_update(module, executable, name, data, driver, driver_opts, debug): + podman_secret_remove(module, executable, name) + else: + return {"changed": False} + else: + if force: + podman_secret_remove(module, executable, name) + if skip and podman_secret_exists(module, executable, name, podman_version): + return {"changed": False} cmd = [executable, 'secret', 'create'] if driver: @@ -121,6 +175,7 @@ def podman_secret_create(module, executable, name, data, force, skip, return { "changed": True, + "diff": diff, } @@ -150,6 +205,7 @@ def main(): skip_existing=dict(type='bool', default=False), driver=dict(type='str'), driver_opts=dict(type='dict'), + debug=dict(type='bool', default=False), ), ) @@ -165,9 +221,10 @@ def main(): skip = module.params['skip_existing'] driver = module.params['driver'] driver_opts = module.params['driver_opts'] + debug = module.params['debug'] results = podman_secret_create(module, executable, name, data, force, skip, - driver, driver_opts) + driver, driver_opts, debug) else: results = podman_secret_remove(module, executable, name) diff --git a/tests/integration/targets/podman_secret/tasks/main.yml b/tests/integration/targets/podman_secret/tasks/main.yml index c6c90de9..c8c562fe 100644 --- a/tests/integration/targets/podman_secret/tasks/main.yml +++ b/tests/integration/targets/podman_secret/tasks/main.yml @@ -1,6 +1,18 @@ - name: Test podman_secret block: + - name: Discover podman version + shell: podman version | grep "^Version:" | awk {'print $2'} + register: podman_v + + - name: Set podman version fact + set_fact: + podman_version: "{{ podman_v.stdout | string }}" + + - name: Set podman version fact to gt than 4.7.0 if so + set_fact: + podman_version_gt470: "{{ podman_version is version('4.7.0', '>=') }}" + - name: Make sure secret doesn't exist containers.podman.podman_secret: executable: "{{ test_executable | default('podman') }}" @@ -11,6 +23,7 @@ containers.podman.podman_secret: executable: "{{ test_executable | default('podman') }}" name: mysecret + debug: true data: secret content - name: Recreate secret @@ -27,13 +40,33 @@ name: mysecret data: super secret content skip_existing: true + debug: true register: skipped + - name: Force secret to same + containers.podman.podman_secret: + executable: "{{ test_executable | default('podman') }}" + name: mysecret + data: super secret content + force: true + debug: true + register: forced2 + - name: Check assertions assert: that: - forced is changed - skipped is not changed + - forced2 is changed + when: not podman_version_gt470 + + - name: Check assertions for podman >= 4.7.0 + assert: + that: + - forced is changed + - skipped is not changed + - forced2 is not changed + when: podman_version_gt470 - name: Create container that uses secret containers.podman.podman_container: @@ -103,6 +136,85 @@ a: b c: d + - when: podman_version_gt470 + block: + + - name: Create secret with file driver and different options + containers.podman.podman_secret: + executable: "{{ test_executable | default('podman') }}" + name: mysecret + data: secret content + driver: file + driver_opts: + a: b + c: e + register: opts_changed + + - name: Create secret with file driver and different options again + containers.podman.podman_secret: + executable: "{{ test_executable | default('podman') }}" + name: mysecret + data: secret content + driver: file + driver_opts: + a: b + c: e + register: opts_changed2 + + - name: Create secret with different content + containers.podman.podman_secret: + executable: "{{ test_executable | default('podman') }}" + name: mysecret + data: other secret content + driver_opts: + a: b + c: e + register: secret_changed + + - name: Create secret with different content again + containers.podman.podman_secret: + executable: "{{ test_executable | default('podman') }}" + name: mysecret + data: other secret content + driver_opts: + a: b + c: e + register: secret_changed2 + + - name: Create secret with different content but skipped + containers.podman.podman_secret: + executable: "{{ test_executable | default('podman') }}" + name: mysecret + data: other secret content and skipped + skip_existing: true + debug: true + driver_opts: + a: b + c: e + register: skip_secret_changed + + - name: Create secret with different content but forced + containers.podman.podman_secret: + executable: "{{ test_executable | default('podman') }}" + name: mysecret + data: other secret content and skipped + force: true + debug: true + driver_opts: + a: b + c: e + register: force_secret_changed + + - name: Check opts changes + assert: + that: + - opts_changed is changed + - opts_changed2 is not changed + - secret_changed is changed + - secret_changed2 is not changed + - skip_secret_changed is not changed + - force_secret_changed is changed + - name: Remove secret containers.podman.podman_secret: executable: "{{ test_executable | default('podman') }}"