diff --git a/plugins/modules/podman_secret.py b/plugins/modules/podman_secret.py index 16f00531..76b10ad3 100644 --- a/plugins/modules/podman_secret.py +++ b/plugins/modules/podman_secret.py @@ -21,6 +21,7 @@ data: description: - The value of the secret. Required when C(state) is C(present). + Mutually exclusive with C(env) and C(path). type: str driver: description: @@ -31,6 +32,11 @@ description: - Driver-specific key-value options. type: dict + env: + description: + - The name of the environment variable that contains the secret. + Mutually exclusive with C(data) and C(path). + type: str executable: description: - Path to C(podman) executable if it is not in the C($PATH) on the @@ -53,6 +59,11 @@ - The name of the secret. required: True type: str + path: + description: + - Path to the file that contains the secret. + Mutually exclusive with C(data) and C(env). + type: path state: description: - Whether to create or remove the named secret. @@ -67,7 +78,7 @@ type: dict debug: description: - - Enable debug mode for module. + - Enable debug mode for module. It prints secrets diff. type: bool default: False ''' @@ -99,6 +110,8 @@ name: mysecret """ +import os + 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 @@ -116,7 +129,7 @@ def podman_secret_exists(module, executable, name, version): return rc == 0 -def need_update(module, executable, name, data, skip, driver, driver_opts, debug, labels): +def need_update(module, executable, name, data, path, env, skip, driver, driver_opts, debug, labels): cmd = [executable, 'secret', 'inspect', '--showsecret', name] rc, out, err = module.run_command(cmd) if rc != 0: @@ -132,10 +145,37 @@ def need_update(module, executable, name, data, skip, driver, driver_opts, debug 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 data: + if secret['SecretData'] != data: + if debug: + diff['after'] = data + diff['before'] = secret['SecretData'] + else: + diff['after'] = "" + diff['before'] = "" + return True + if path: + with open(path, 'rb') as f: + text = f.read().decode('utf-8') + if secret['SecretData'] != text: + if debug: + diff['after'] = text + diff['before'] = secret['SecretData'] + else: + diff['after'] = "" + diff['before'] = "" + return True + if env: + env_data = os.environ.get(env) + if secret['SecretData'] != env_data: + if debug: + diff['after'] = env_data + diff['before'] = secret['SecretData'] + else: + diff['after'] = "" + diff['before'] = "" + return True + if driver_opts: for k, v in driver_opts.items(): if secret['Spec']['Driver']['Options'].get(k) != v: @@ -155,13 +195,13 @@ def need_update(module, executable, name, data, skip, driver, driver_opts, debug return False -def podman_secret_create(module, executable, name, data, force, skip, +def podman_secret_create(module, executable, name, data, path, env, force, skip, driver, driver_opts, debug, labels): 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 need_update(module, executable, name, data, skip, driver, driver_opts, debug, labels): + if need_update(module, executable, name, data, path, env, skip, driver, driver_opts, debug, labels): podman_secret_remove(module, executable, name) else: return {"changed": False} @@ -183,9 +223,20 @@ def podman_secret_create(module, executable, name, data, force, skip, cmd.append('--label') cmd.append("=".join([k, v])) cmd.append(name) - cmd.append('-') + if data: + cmd.append('-') + elif path: + cmd.append(path) + elif env: + if os.environ.get(env) is None: + module.fail_json(msg="Environment variable %s is not set" % env) + cmd.append("--env") + cmd.append(env) - rc, out, err = module.run_command(cmd, data=data, binary_data=True) + if data: + rc, out, err = module.run_command(cmd, data=data, binary_data=True) + else: + rc, out, err = module.run_command(cmd) if rc != 0: module.fail_json(msg="Unable to create secret: %s" % err) @@ -220,6 +271,8 @@ def main(): state=dict(type='str', default='present', choices=['absent', 'present']), name=dict(type='str', required=True), data=dict(type='str', no_log=True), + env=dict(type='str'), + path=dict(type='path'), force=dict(type='bool', default=False), skip_existing=dict(type='bool', default=False), driver=dict(type='str'), @@ -227,6 +280,8 @@ def main(): labels=dict(type='dict'), debug=dict(type='bool', default=False), ), + required_if=[('state', 'present', ['path', 'env', 'data'], True)], + mutually_exclusive=[['path', 'env', 'data']], ) state = module.params['state'] @@ -235,16 +290,16 @@ def main(): if state == 'present': data = module.params['data'] - if data is None: - raise Exception("'data' is required when 'state' is 'present'") force = module.params['force'] skip = module.params['skip_existing'] driver = module.params['driver'] driver_opts = module.params['driver_opts'] debug = module.params['debug'] labels = module.params['labels'] + path = module.params['path'] + env = module.params['env'] results = podman_secret_create(module, executable, - name, data, force, skip, + name, data, path, env, force, skip, driver, driver_opts, debug, labels) 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 dba9ef73..c47ebb4b 100644 --- a/tests/integration/targets/podman_secret/tasks/main.yml +++ b/tests/integration/targets/podman_secret/tasks/main.yml @@ -17,7 +17,10 @@ containers.podman.podman_secret: executable: "{{ test_executable | default('podman') }}" state: absent - name: mysecret + name: "{{ item }}" + loop: + - mysecret + - mysecret2 - name: Create secret containers.podman.podman_secret: @@ -283,7 +286,10 @@ containers.podman.podman_secret: executable: "{{ test_executable | default('podman') }}" state: absent - name: mysecret + name: "{{ item }}" + loop: + - mysecret + - mysecret2 - name: Create secret if not exists and skip existing containers.podman.podman_secret: @@ -323,6 +329,116 @@ state: absent name: mysecret2 + + - when: podman_version_gt470 + block: + + - name: Create a file with secret data + copy: + content: "secret content 1" + dest: ~/mysecret-1 + + - name: Create secret from file + containers.podman.podman_secret: + executable: "{{ test_executable | default('podman') }}" + name: mysecret2 + path: ~/mysecret-1 + state: present + register: secret1 + + - name: Create secret again + containers.podman.podman_secret: + executable: "{{ test_executable | default('podman') }}" + name: mysecret2 + path: ~/mysecret-1 + state: present + register: secret2 + + - name: Check outputs + assert: + that: + - secret1 is changed + - secret2 is not changed + + - name: Create another secret in other file + copy: + content: "secret content 2" + dest: ~/mysecret-2 + + - name: Create secret from other file + containers.podman.podman_secret: + executable: "{{ test_executable | default('podman') }}" + name: mysecret3 + path: ~/mysecret-2 + state: present + debug: true + register: secret3 + + - name: Check outputs + assert: + that: + - secret3 is changed + + - name: Create a secret from non existing file + containers.podman.podman_secret: + executable: "{{ test_executable | default('podman') }}" + name: mysecret4 + path: ~/mysecret-3 + state: present + debug: true + register: secret4 + ignore_errors: true + + - name: Check outputs + assert: + that: + - secret4 is failed + + - name: Create a secret from non-existing environment variable + containers.podman.podman_secret: + executable: "{{ test_executable | default('podman') }}" + name: mysecret5 + env: NON_EXISTING_ENV + state: present + register: secret5 + ignore_errors: true + + - name: Check outputs + assert: + that: + - secret5 is failed + - "'Environment variable NON_EXISTING_ENV is not set' in secret5.msg" + + - name: Create a secret from existing environment variable + containers.podman.podman_secret: + executable: "{{ test_executable | default('podman') }}" + name: mysecret5 + env: EXISTING_ENV + state: present + environment: + EXISTING_ENV: "secret env content" + register: secret6 + + - name: Show secret6 + containers.podman.podman_secret_info: + executable: "{{ test_executable | default('podman') }}" + name: mysecret5 + showsecret: true + register: secret6_info + + - name: Check outputs + assert: + that: + - secret6 is changed + - secret6_info is success + - secret6_info.secrets.0.SecretData == "secret env content" + + - name: Remove secret + containers.podman.podman_secret: + executable: "{{ test_executable | default('podman') }}" + state: absent + name: mysecret5 + always: - name: Remove container that uses secret containers.podman.podman_container: