From 2f6b8591b181d881d655c80648e91c2b48e4ddb4 Mon Sep 17 00:00:00 2001 From: Meecr0b Date: Fri, 7 Sep 2018 19:41:09 +0200 Subject: [PATCH] tower_credential: expect ssh_key_data to be a string instead of path (#45158) * expect ssh_key_data to be a string instead of path ssh_key_data should be a string filled with the private key the old behavior can be archived with a lookup Fixes #45119 * clarifies ssh_key_data description, adds newline --- .../tower_credential_ssh_key_data.yaml | 3 + .../rst/porting_guides/porting_guide_2.8.rst | 3 + .../ansible_tower/tower_credential.py | 35 ++++++-- .../targets/tower_credential/tasks/main.yml | 84 +++++++++++-------- 4 files changed, 81 insertions(+), 44 deletions(-) create mode 100644 changelogs/fragments/tower_credential_ssh_key_data.yaml diff --git a/changelogs/fragments/tower_credential_ssh_key_data.yaml b/changelogs/fragments/tower_credential_ssh_key_data.yaml new file mode 100644 index 0000000000..578e92ef93 --- /dev/null +++ b/changelogs/fragments/tower_credential_ssh_key_data.yaml @@ -0,0 +1,3 @@ +--- +minor_changes: +- tower_credential - Expect ssh_key_data to be the content of a ssh_key file instead of the path to the file (https://github.com/ansible/ansible/pull/45158) diff --git a/docs/docsite/rst/porting_guides/porting_guide_2.8.rst b/docs/docsite/rst/porting_guides/porting_guide_2.8.rst index 1f2c4b576a..1094e905c6 100644 --- a/docs/docsite/rst/porting_guides/porting_guide_2.8.rst +++ b/docs/docsite/rst/porting_guides/porting_guide_2.8.rst @@ -51,6 +51,9 @@ The following modules will be removed in Ansible 2.12. Please update your playbo Noteworthy module changes ------------------------- +* The ``tower_credential`` module originally required the ``ssh_key_data`` to be the path to a ssh_key_file. + In order to work like Tower/AWX, ``ssh_key_data`` now contains the content of the file. + The previous behavior can be achieved with ``lookup('file', '/path/to/file')``. Plugins ======= diff --git a/lib/ansible/modules/web_infrastructure/ansible_tower/tower_credential.py b/lib/ansible/modules/web_infrastructure/ansible_tower/tower_credential.py index d9c7614177..80fb76e227 100644 --- a/lib/ansible/modules/web_infrastructure/ansible_tower/tower_credential.py +++ b/lib/ansible/modules/web_infrastructure/ansible_tower/tower_credential.py @@ -58,7 +58,8 @@ options: - Password for this credential. Use ASK for prompting. secret_key for AWS. api_key for RAX. ssh_key_data: description: - - Path to SSH private key. + - SSH private key content. To extract the content from a file path, use the lookup function (see examples). + required: False ssh_key_unlock: description: - Unlock password for ssh_key. Use ASK for prompting. @@ -123,6 +124,17 @@ EXAMPLES = ''' organization: test-org state: present tower_config_file: "~/tower_cli.cfg" + +- name: Create a valid SCM credential from a private_key file + tower_credential: + name: SCM Credential + organization: Default + state: present + kind: scm + username: joe + password: secret + ssh_key_data: "{{ lookup('file', '/tmp/id_rsa') }}" + ssh_key_unlock: "passphrase" ''' import os @@ -187,7 +199,7 @@ def main(): host=dict(), username=dict(), password=dict(no_log=True), - ssh_key_data=dict(no_log=True, type='path'), + ssh_key_data=dict(no_log=True, type='str'), ssh_key_unlock=dict(no_log=True), authorize=dict(type='bool', default=False), authorize_password=dict(no_log=True), @@ -254,13 +266,18 @@ def main(): params['team'] = team['id'] if module.params.get('ssh_key_data'): - filename = module.params.get('ssh_key_data') - if not os.path.exists(filename): - module.fail_json(msg='file not found: %s' % filename) - if os.path.isdir(filename): - module.fail_json(msg='attempted to read contents of directory: %s' % filename) - with open(filename, 'rb') as f: - module.params['ssh_key_data'] = to_text(f.read()) + data = module.params.get('ssh_key_data') + if os.path.exists(data): + module.deprecate( + msg='ssh_key_data should be a string, not a path to a file. Use lookup(\'file\', \'/path/to/file\') instead', + version="2.12" + ) + if os.path.isdir(data): + module.fail_json(msg='attempted to read contents of directory: %s' % data) + with open(data, 'rb') as f: + module.params['ssh_key_data'] = to_text(f.read()) + else: + module.params['ssh_key_data'] = data for key in ('authorize', 'authorize_password', 'client', 'security_token', 'secret', 'tenant', 'subscription', diff --git a/test/integration/targets/tower_credential/tasks/main.yml b/test/integration/targets/tower_credential/tasks/main.yml index 77abf5a5ca..9af78d9577 100644 --- a/test/integration/targets/tower_credential/tasks/main.yml +++ b/test/integration/targets/tower_credential/tasks/main.yml @@ -5,6 +5,10 @@ - name: Generate a local SSH key local_action: "shell ssh-keygen -b 2048 -t rsa -f {{ tempdir.stdout }}/id_rsa -q -N 'passphrase'" +- name: Read the generated key + set_fact: + ssh_key_data: "{{ lookup('file', tempdir.stdout + '/id_rsa') }}" + - name: Create a User-specific credential tower_credential: name: SSH Credential @@ -43,6 +47,46 @@ 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: Create a valid SSH credential from lookup source + tower_credential: + name: SSH Credential from lookup source + organization: Default + state: present + kind: ssh + description: An example SSH credential from lookup source + username: joe + password: secret + become_method: sudo + become_username: superuser + become_password: supersecret + ssh_key_data: "{{ lookup('file', tempdir.stdout + '/id_rsa') }}" + ssh_key_unlock: "passphrase" + register: result + +- assert: + that: + - "result is changed" + +- name: Create a valid SSH credential from file source + tower_credential: + name: SSH Credential from file source + organization: Default + state: present + kind: ssh + description: An example SSH credential from file source + username: joe + password: secret + become_method: sudo + become_username: superuser + become_password: supersecret ssh_key_data: "{{ tempdir.stdout }}/id_rsa" ssh_key_unlock: "passphrase" register: result @@ -50,6 +94,8 @@ - assert: that: - "result is changed" + - "result is not failed" + - "'ssh_key_data should be a string, not a path to a file.' in result.deprecations[0].msg" - name: Create an invalid SSH credential (passphrase required) tower_credential: @@ -58,7 +104,7 @@ state: present kind: ssh username: joe - ssh_key_data: "{{ tempdir.stdout }}/id_rsa" + ssh_key_data: "{{ ssh_key_data }}" ignore_errors: yes register: result @@ -67,38 +113,6 @@ - "result is failed" - "'must be set when SSH key is encrypted' in result.module_stderr" -- name: Create an invalid SSH credential (ssh_key_data file is missing) - tower_credential: - name: SSH Credential - organization: Default - state: present - kind: ssh - username: joe - ssh_key_data: "{{ tempdir.stdout }}/not_a_valid_file" - ignore_errors: yes - register: result - -- assert: - that: - - "result is failed" - - "'file not found' in result.msg" - -- name: Create an invalid SSH credential (ssh_key_data is a directory) - tower_credential: - name: SSH Credential - organization: Default - state: present - kind: ssh - username: joe - ssh_key_data: "{{ tempdir.stdout }}" - ignore_errors: yes - register: result - -- assert: - that: - - "result is failed" - - "'attempted to read contents of directory' in result.msg" - - name: Create an invalid SSH credential (Organization not found) tower_credential: name: SSH Credential @@ -202,7 +216,7 @@ kind: scm username: joe password: secret - ssh_key_data: "{{ tempdir.stdout }}/id_rsa" + ssh_key_data: "{{ ssh_key_data }}" ssh_key_unlock: "passphrase" register: result @@ -338,7 +352,7 @@ kind: gce username: joe project: ABC123 - ssh_key_data: "{{ tempdir.stdout }}/id_rsa" + ssh_key_data: "{{ ssh_key_data }}" register: result - assert: