From 76f03b9adc075203b16b565c793a2cd2cc363530 Mon Sep 17 00:00:00 2001 From: matt Date: Mon, 20 Mar 2023 09:59:24 -0600 Subject: [PATCH 1/5] add `exists` to awx.awx.credential --- awx_collection/plugins/modules/credential.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/awx_collection/plugins/modules/credential.py b/awx_collection/plugins/modules/credential.py index 4e7f02e558..75f3a69470 100644 --- a/awx_collection/plugins/modules/credential.py +++ b/awx_collection/plugins/modules/credential.py @@ -87,7 +87,7 @@ options: update_secrets: description: - C(true) will always update encrypted values. - - C(false) will only updated encrypted values if a change is absolutely known to be needed. + - C(false) will only update encrypted values if a change is absolutely known to be needed. type: bool default: true user: @@ -100,8 +100,8 @@ options: type: str state: description: - - Desired state of the resource. - choices: ["present", "absent"] + - Desired state of the resource. C(exists) will not modify the resource if it is present. + choices: ["present", "absent", "exists"] default: "present" type: str @@ -216,7 +216,7 @@ def main(): update_secrets=dict(type='bool', default=True, no_log=False), user=dict(), team=dict(), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present', 'absent', 'exists'], default='present'), ) # Create a module for ourselves @@ -265,6 +265,10 @@ def main(): # If the state was absent we can let the module delete it if needed, the module will handle exiting from this module.delete_if_needed(credential) + if state == 'exists' and credential is not None: + # If credential exists and state is exists, we're done here. + module.create_if_needed(credential, credential, endpoint='credentials', item_type='credential') + # Attempt to look up the related items the user specified (these will fail the module if not found) if user: user_id = module.resolve_name_to_id('users', user) From 7c4aedf7164d085eabd63bd92eed4cf5cd00cc79 Mon Sep 17 00:00:00 2001 From: matt Date: Mon, 20 Mar 2023 13:36:24 -0600 Subject: [PATCH 2/5] exit from module --- awx_collection/plugins/modules/credential.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx_collection/plugins/modules/credential.py b/awx_collection/plugins/modules/credential.py index 75f3a69470..2f037f68dc 100644 --- a/awx_collection/plugins/modules/credential.py +++ b/awx_collection/plugins/modules/credential.py @@ -267,7 +267,7 @@ def main(): if state == 'exists' and credential is not None: # If credential exists and state is exists, we're done here. - module.create_if_needed(credential, credential, endpoint='credentials', item_type='credential') + module.exit_json(**module.json_output) # Attempt to look up the related items the user specified (these will fail the module if not found) if user: From b0c416334fb3abc5e820667d65167fb374164c39 Mon Sep 17 00:00:00 2001 From: matt Date: Thu, 23 Mar 2023 15:44:00 -0600 Subject: [PATCH 3/5] add test coverage --- awx_collection/test/awx/test_credential.py | 17 ++++++ .../targets/credential/tasks/main.yml | 54 +++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/awx_collection/test/awx/test_credential.py b/awx_collection/test/awx/test_credential.py index f12594bee8..65e68538f1 100644 --- a/awx_collection/test/awx/test_credential.py +++ b/awx_collection/test/awx/test_credential.py @@ -132,3 +132,20 @@ def test_secret_field_write_twice(run_module, organization, admin_user, cred_typ else: assert result.get('changed') is False, result assert Credential.objects.get(id=result['id']).get_input('token') == val1 + +@pytest.mark.django_db +@pytest.mark.parametrize('state', ('present', 'absent', 'exists')) +def test_credential_state(run_module, organization, admin_user, cred_type, state): + for state in ('present', 'absent', 'exists'): + result = run_module( + 'credential', + dict( + name='Galaxy Token for Steve', + organization=organization.name, + credential_type=cred_type.name, + inputs={'token': '7rEZK38DJl58A7RxA6EC7lLvUHbBQ1'}, + state=state, + ), + admin_user, + ) + assert not result.get('failed', False), result.get('msg', result) diff --git a/awx_collection/tests/integration/targets/credential/tasks/main.yml b/awx_collection/tests/integration/targets/credential/tasks/main.yml index 57b2168f25..42d22d8bc7 100644 --- a/awx_collection/tests/integration/targets/credential/tasks/main.yml +++ b/awx_collection/tests/integration/targets/credential/tasks/main.yml @@ -178,6 +178,60 @@ that: - result is changed +- name: Delete an SSH credential + credential: + name: "{{ ssh_cred_name2 }}" + organization: Default + state: absent + credential_type: Machine + register: result + +- assert: + that: + - "result is changed" + +- name: Ensure existence of SSH credential + credential: + name: "{{ ssh_cred_name2 }}" + organization: Default + state: exists + credential_type: Machine + description: An example SSH awx.awx.credential + inputs: + username: joe + password: secret + become_method: sudo + become_username: superuser + become_password: supersecret + ssh_key_data: "{{ ssh_key_data }}" + ssh_key_unlock: "passphrase" + register: result + +- assert: + that: + - result is changed + +- name: Ensure existence of SSH credential, not updating any inputs + credential: + name: "{{ ssh_cred_name2 }}" + organization: Default + state: exists + credential_type: Machine + description: An example SSH awx.awx.credential + inputs: + username: joe + password: no-update-secret + become_method: sudo + become_username: some-other-superuser + become_password: some-other-secret + ssh_key_data: "{{ ssh_key_data }}" + ssh_key_unlock: "another-pass-phrase" + register: result + +- assert: + that: + - result is not changed + - name: Create an invalid SSH credential (passphrase required) credential: name: SSH Credential From 4a3d437b32e6f638e36c57e905b2ff5e12b596a6 Mon Sep 17 00:00:00 2001 From: matt Date: Tue, 11 Apr 2023 11:35:36 -0600 Subject: [PATCH 4/5] spaces for pep8 --- awx_collection/test/awx/test_credential.py | 1 + 1 file changed, 1 insertion(+) diff --git a/awx_collection/test/awx/test_credential.py b/awx_collection/test/awx/test_credential.py index 65e68538f1..e68a233f61 100644 --- a/awx_collection/test/awx/test_credential.py +++ b/awx_collection/test/awx/test_credential.py @@ -133,6 +133,7 @@ def test_secret_field_write_twice(run_module, organization, admin_user, cred_typ assert result.get('changed') is False, result assert Credential.objects.get(id=result['id']).get_input('token') == val1 + @pytest.mark.django_db @pytest.mark.parametrize('state', ('present', 'absent', 'exists')) def test_credential_state(run_module, organization, admin_user, cred_type, state): From 93e6f974f6994e1d1bcdc7c8fad8c5fa4220a544 Mon Sep 17 00:00:00 2001 From: matt Date: Tue, 18 Apr 2023 09:51:20 -0600 Subject: [PATCH 5/5] remove redundant loop --- awx_collection/test/awx/test_credential.py | 25 +++++++++++----------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/awx_collection/test/awx/test_credential.py b/awx_collection/test/awx/test_credential.py index e68a233f61..c67953197e 100644 --- a/awx_collection/test/awx/test_credential.py +++ b/awx_collection/test/awx/test_credential.py @@ -137,16 +137,15 @@ def test_secret_field_write_twice(run_module, organization, admin_user, cred_typ @pytest.mark.django_db @pytest.mark.parametrize('state', ('present', 'absent', 'exists')) def test_credential_state(run_module, organization, admin_user, cred_type, state): - for state in ('present', 'absent', 'exists'): - result = run_module( - 'credential', - dict( - name='Galaxy Token for Steve', - organization=organization.name, - credential_type=cred_type.name, - inputs={'token': '7rEZK38DJl58A7RxA6EC7lLvUHbBQ1'}, - state=state, - ), - admin_user, - ) - assert not result.get('failed', False), result.get('msg', result) + result = run_module( + 'credential', + dict( + name='Galaxy Token for Steve', + organization=organization.name, + credential_type=cred_type.name, + inputs={'token': '7rEZK38DJl58A7RxA6EC7lLvUHbBQ1'}, + state=state, + ), + admin_user, + ) + assert not result.get('failed', False), result.get('msg', result)