Initial conversion of tower_credential

This commit is contained in:
John Westcott IV
2020-03-31 00:07:46 -04:00
committed by beeankha
parent f04e7067e8
commit fd24918ba8
4 changed files with 351 additions and 213 deletions

View File

@@ -52,6 +52,7 @@ The following notes are changes that may require changes to playbooks:
- Some return values (e.g., `credential_type`) have been removed. Use of `id` is recommended. - Some return values (e.g., `credential_type`) have been removed. Use of `id` is recommended.
- `tower_job_template` no longer supports the deprecated `extra_vars_path` parameter, please use `extra_vars` with the lookup plugin to replace this functionality. - `tower_job_template` no longer supports the deprecated `extra_vars_path` parameter, please use `extra_vars` with the lookup plugin to replace this functionality.
- The `notification_configuration` parameter of `tower_notification` has changed from a string to a dict. Please use the `lookup` plugin to read an existing file into a dict. - The `notification_configuration` parameter of `tower_notification` has changed from a string to a dict. Please use the `lookup` plugin to read an existing file into a dict.
- `tower_credential` no longer supports passing a file name to ssh_key_data.
## Running Unit Tests ## Running Unit Tests

View File

@@ -265,7 +265,14 @@ class TowerModule(AnsibleModule):
def resolve_name_to_id(self, endpoint, name_or_id): def resolve_name_to_id(self, endpoint, name_or_id):
# Try to resolve the object by name # Try to resolve the object by name
response = self.get_endpoint(endpoint, **{'data': {'name': name_or_id}}) name_field = 'name'
if endpoint == 'users':
name_field = 'username'
response = self.get_endpoint(endpoint, **{'data': {name_field: name_or_id}})
if response['status_code'] == 400:
self.fail_json(msg="Unable to try and resolve {0} for {1} : {2}".format(endpoint, name_or_id, response['json']['detail']))
if response['json']['count'] == 1: if response['json']['count'] == 1:
return response['json']['results'][0]['id'] return response['json']['results'][0]['id']
elif response['json']['count'] == 0: elif response['json']['count'] == 0:
@@ -567,6 +574,23 @@ class TowerModule(AnsibleModule):
else: else:
self.exit_json(**self.json_output) self.exit_json(**self.json_output)
# We need to be able to recursevly step through fields in the case of inputs for credentials.
# They are dicts and we can't just compare them at the top level because the dict returned from tower_cli will have fields like $encrypted$
def compare_fields(self, new_item, existing_item):
needs_update = False
for field in new_item:
existing_field = existing_item.get(field, None)
new_field = new_item.get(field, None)
if type(existing_field) == dict and type(new_field) == dict:
needs_update = needs_update or self.compare_fields(new_field, existing_field)
# If the two items don't match and we are not comparing '' to None
# In the case of extra_vars in a job template, we have to pass in {} for nothing ('' is not valid)
# But when its returned from the API, instead of {} we get back ''.
elif existing_field != new_field and not (existing_field in (None, '') and new_field in ('', {})):
# Something doesn't match so let's update it
needs_update = True
return needs_update
def update_if_needed(self, existing_item, new_item, on_update=None, associations=None): def update_if_needed(self, existing_item, new_item, on_update=None, associations=None):
# This will exit from the module on its own # This will exit from the module on its own
# If the method successfully updates an item and on_update param is defined, # If the method successfully updates an item and on_update param is defined,
@@ -594,15 +618,7 @@ class TowerModule(AnsibleModule):
self.fail_json(msg="Unable to process update of item due to missing data {0}".format(ke)) self.fail_json(msg="Unable to process update of item due to missing data {0}".format(ke))
# Check to see if anything within the item requires the item to be updated # Check to see if anything within the item requires the item to be updated
needs_update = False needs_update = self.compare_fields(new_item, existing_item)
for field in new_item:
existing_field = existing_item.get(field, None)
new_field = new_item.get(field, None)
# If the two items don't match and we are not comparing '' to None
if existing_field != new_field and not (existing_field in (None, '') and new_field == ''):
# Something doesn't match so let's update it
needs_update = True
break
# If we decided the item needs to be updated, update it # If we decided the item needs to be updated, update it
self.json_output['id'] = item_id self.json_output['id'] = item_id

View File

@@ -28,37 +28,24 @@ options:
- The name to use for the credential. - The name to use for the credential.
required: True required: True
type: str type: str
new_name:
description:
- Setting this option will change the existing name (looked up via the name field.
required: True
type: str
description: description:
description: description:
- The description to use for the credential. - The description to use for the credential.
type: str type: str
user:
description:
- User that should own this credential.
type: str
team:
description:
- Team that should own this credential.
type: str
project:
description:
- Project that should use this credential.
type: str
organization: organization:
description: description:
- Organization that should own the credential. - Organization that should own the credential.
required: True
type: str
kind:
description:
- Type of credential being added.
- The ssh choice refers to a Tower Machine credential.
required: False required: False
type: str type: str
choices: ["ssh", "vault", "net", "scm", "aws", "vmware", "satellite6", "cloudforms", "gce", "azure_rm", "openstack", "rhv", "insights", "tower"]
credential_type: credential_type:
description: description:
- Name of credential type. - Name of credential type.
- Will be prefered over kind
required: False required: False
version_added: "2.10" version_added: "2.10"
type: str type: str
@@ -67,91 +54,132 @@ options:
- >- - >-
Credential inputs where the keys are var names used in templating. Credential inputs where the keys are var names used in templating.
Refer to the Ansible Tower documentation for example syntax. Refer to the Ansible Tower documentation for example syntax.
- Any fields in this dict will take prescedence over any fields mentioned below (i.e. host, username, etc)
required: False required: False
version_added: "2.9" version_added: "2.9"
type: dict type: dict
user:
description:
- User that should own this credential.
type: str
team:
description:
- Team that should own this credential.
type: str
kind:
description:
- Type of credential being added.
- The ssh choice refers to a Tower Machine credential.
- Deprecated, please use credential_type
required: False
type: str
choices: ["ssh", "vault", "net", "scm", "aws", "vmware", "satellite6", "cloudforms", "gce", "azure_rm", "openstack", "rhv", "insights", "tower"]
host: host:
description: description:
- Host for this credential. - Host for this credential.
- Deprecated, will be removed in a future release
type: str type: str
username: username:
description: description:
- Username for this credential. ``access_key`` for AWS. - Username for this credential. ``access_key`` for AWS.
- Deprecated, please use inputs
type: str type: str
password: password:
description: description:
- Password for this credential. ``secret_key`` for AWS. ``api_key`` for RAX. - Password for this credential. ``secret_key`` for AWS. ``api_key`` for RAX.
- Use "ASK" and launch in Tower to be prompted. - Use "ASK" and launch in Tower to be prompted.
- Deprecated, please use inputs
type: str
project:
description:
- Project that should use this credential for GCP.
- Deprecated, will be removed in a future release
type: str type: str
ssh_key_data: ssh_key_data:
description: description:
- SSH private key content. To extract the content from a file path, use the lookup function (see examples). - SSH private key content. To extract the content from a file path, use the lookup function (see examples).
- Deprecated, please use inputs
required: False required: False
type: str type: str
ssh_key_unlock: ssh_key_unlock:
description: description:
- Unlock password for ssh_key. - Unlock password for ssh_key.
- Use "ASK" and launch in Tower to be prompted. - Use "ASK" and launch in Tower to be prompted.
- Deprecated, please use inputs
type: str type: str
authorize: authorize:
description: description:
- Should use authorize for net type. - Should use authorize for net type.
- Deprecated, please use inputs
type: bool type: bool
default: 'no' default: 'no'
authorize_password: authorize_password:
description: description:
- Password for net credentials that require authorize. - Password for net credentials that require authorize.
- Deprecated, please use inputs
type: str type: str
client: client:
description: description:
- Client or application ID for azure_rm type. - Client or application ID for azure_rm type.
- Deprecated, please use inputs
type: str type: str
security_token: security_token:
description: description:
- STS token for aws type. - STS token for aws type.
- Deprecated, please use inputs
version_added: "2.6" version_added: "2.6"
type: str type: str
secret: secret:
description: description:
- Secret token for azure_rm type. - Secret token for azure_rm type.
- Deprecated, please use inputs
type: str type: str
subscription: subscription:
description: description:
- Subscription ID for azure_rm type. - Subscription ID for azure_rm type.
- Deprecated, please use inputs
type: str type: str
tenant: tenant:
description: description:
- Tenant ID for azure_rm type. - Tenant ID for azure_rm type.
- Deprecated, please use inputs
type: str type: str
domain: domain:
description: description:
- Domain for openstack type. - Domain for openstack type.
- Deprecated, please use inputs
type: str type: str
become_method: become_method:
description: description:
- Become method to use for privilege escalation. - Become method to use for privilege escalation.
- Some examples are "None", "sudo", "su", "pbrun" - Some examples are "None", "sudo", "su", "pbrun"
- Due to become plugins, these can be arbitrary - Due to become plugins, these can be arbitrary
- Deprecated, please use inputs
type: str type: str
become_username: become_username:
description: description:
- Become username. - Become username.
- Use "ASK" and launch in Tower to be prompted. - Use "ASK" and launch in Tower to be prompted.
- Deprecated, please use inputs
type: str type: str
become_password: become_password:
description: description:
- Become password. - Become password.
- Use "ASK" and launch in Tower to be prompted. - Use "ASK" and launch in Tower to be prompted.
- Deprecated, please use inputs
type: str type: str
vault_password: vault_password:
description: description:
- Vault password. - Vault password.
- Use "ASK" and launch in Tower to be prompted. - Use "ASK" and launch in Tower to be prompted.
- Deprecated, please use inputs
type: str type: str
vault_id: vault_id:
description: description:
- Vault identifier. - Vault identifier.
- This parameter is only valid if C(kind) is specified as C(vault). - This parameter is only valid if C(kind) is specified as C(vault).
- Deprecated, please use inputs
type: str type: str
version_added: "2.8" version_added: "2.8"
state: state:
@@ -160,21 +188,23 @@ options:
choices: ["present", "absent"] choices: ["present", "absent"]
default: "present" default: "present"
type: str type: str
tower_oauthtoken:
requirements: description:
- ansible-tower-cli >= 3.0.2 - The Tower OAuth token to use.
required: False
type: str
version_added: "3.7"
extends_documentation_fragment: awx.awx.auth extends_documentation_fragment: awx.awx.auth
''' '''
EXAMPLES = ''' EXAMPLES = '''
- name: Add tower credential - name: Add tower machine credential
tower_credential: tower_credential:
name: Team Name name: Team Name
description: Team Description description: Team Description
organization: test-org organization: test-org
kind: ssh credential_type: Machine
state: present state: present
tower_config_file: "~/tower_cli.cfg" tower_config_file: "~/tower_cli.cfg"
@@ -183,7 +213,8 @@ EXAMPLES = '''
name: SCM Credential name: SCM Credential
organization: Default organization: Default
state: present state: present
kind: scm credential_type: Source Control
inputs:
username: joe username: joe
password: secret password: secret
ssh_key_data: "{{ lookup('file', '/tmp/id_rsa') }}" ssh_key_data: "{{ lookup('file', '/tmp/id_rsa') }}"
@@ -196,12 +227,10 @@ EXAMPLES = '''
- name: Add Credential Into Tower - name: Add Credential Into Tower
tower_credential: tower_credential:
name: Workshop Credential name: Workshop Credential
ssh_key_data: "{{ aws_ssh_key['content'] | b64decode }}" credential_type: Machine
kind: ssh
organization: Default organization: Default
tower_username: admin inputs:
tower_password: ansible ssh_key_data: "{{ aws_ssh_key['content'] | b64decode }}"
tower_host: https://localhost
run_once: true run_once: true
delegate_to: localhost delegate_to: localhost
@@ -215,23 +244,11 @@ EXAMPLES = '''
tower_host: https://localhost tower_host: https://localhost
''' '''
import os from ..module_utils.tower_api import TowerModule
from ansible.module_utils._text import to_text
from ..module_utils.ansible_tower import TowerModule, tower_auth_config, tower_check_mode
try:
import tower_cli
import tower_cli.exceptions as exc
from tower_cli.conf import settings
except ImportError:
pass
KIND_CHOICES = { KIND_CHOICES = {
'ssh': 'Machine', 'ssh': 'Machine',
'vault': 'Ansible Vault', 'vault': 'Vault',
'net': 'Network', 'net': 'Network',
'scm': 'Source Control', 'scm': 'Source Control',
'aws': 'Amazon Web Services', 'aws': 'Amazon Web Services',
@@ -257,162 +274,117 @@ OLD_INPUT_NAMES = (
) )
def credential_type_for_kind(params):
credential_type_res = tower_cli.get_resource('credential_type')
kind = params.get('kind')
arguments = {'managed_by_tower': True}
if kind == 'ssh':
if params.get('vault_password'):
arguments['kind'] = 'vault'
else:
arguments['kind'] = 'ssh'
elif kind in ('net', 'scm', 'insights', 'vault'):
arguments['kind'] = kind
elif kind in KIND_CHOICES:
arguments.update(dict(
kind='cloud',
name=KIND_CHOICES[kind]
))
return credential_type_res.get(**arguments)
def main(): def main():
# Any additional arguments that are not fields of the item can be added here
argument_spec = dict( argument_spec = dict(
name=dict(required=True), name=dict(required=True),
user=dict(), new_name=dict(),
team=dict(), description=dict(),
kind=dict(choices=list(KIND_CHOICES.keys())), organization=dict(),
credential_type=dict(), credential_type=dict(),
inputs=dict(type='dict'), inputs=dict(type='dict'),
user=dict(),
team=dict(),
# These are for backwards compatability
kind=dict(choices=list(KIND_CHOICES.keys())),
host=dict(), host=dict(),
username=dict(), username=dict(),
password=dict(no_log=True), password=dict(no_log=True),
ssh_key_data=dict(no_log=True, type='str'), project=dict(),
ssh_key_data=dict(no_log=True),
ssh_key_unlock=dict(no_log=True), ssh_key_unlock=dict(no_log=True),
authorize=dict(type='bool', default=False), authorize=dict(type='bool'),
authorize_password=dict(no_log=True), authorize_password=dict(no_log=True),
client=dict(), client=dict(),
security_token=dict(), security_token=dict(),
secret=dict(), secret=dict(no_log=True),
tenant=dict(),
subscription=dict(), subscription=dict(),
tenant=dict(),
domain=dict(), domain=dict(),
become_method=dict(), become_method=dict(),
become_username=dict(), become_username=dict(),
become_password=dict(no_log=True), become_password=dict(no_log=True),
vault_password=dict(no_log=True), vault_password=dict(no_log=True),
description=dict(),
organization=dict(required=True),
project=dict(),
state=dict(choices=['present', 'absent'], default='present'),
vault_id=dict(), vault_id=dict(),
# End backwards compatability
state=dict(choices=['present', 'absent'], default='present'),
) )
mutually_exclusive = [ # Create a module for ourselves
('kind', 'credential_type') module = TowerModule(argument_spec=argument_spec, supports_check_mode=True, required_one_of=[['kind', 'credential_type']])
]
for input_name in OLD_INPUT_NAMES:
mutually_exclusive.append(('inputs', input_name))
module = TowerModule(argument_spec=argument_spec, supports_check_mode=True,
mutually_exclusive=mutually_exclusive)
# Extract our parameters
name = module.params.get('name') name = module.params.get('name')
new_name = module.params.get('new_name')
description = module.params.get('description')
organization = module.params.get('organization') organization = module.params.get('organization')
credential_type = module.params.get('credential_type')
inputs = module.params.get('inputs')
user = module.params.get('user')
team = module.params.get('team')
# The legacy arguments are put into a hash down below
kind = module.params.get('kind')
# End backwards compatability
state = module.params.get('state') state = module.params.get('state')
json_output = {'credential': name, 'state': state} # Attempt to look up the related items the user specified (these will fail the module if not found)
tower_auth = tower_auth_config(module)
with settings.runtime_values(**tower_auth):
tower_check_mode(module)
credential = tower_cli.get_resource('credential')
try:
params = {}
params['create_on_missing'] = True
params['name'] = name
if organization: if organization:
org_res = tower_cli.get_resource('organization') org_id = module.resolve_name_to_id('organizations', organization)
org = org_res.get(name=organization) if user:
params['organization'] = org['id'] user_id = module.resolve_name_to_id('users', user)
if team:
team_id = module.resolve_name_to_id('teams', team)
try: if kind:
tower_cli.get_resource('credential_type') module.deprecate(msg='The kind parameter has been depricated, please use credential_type instead', version="3.6")
except (ImportError, AttributeError):
# /api/v1/ backwards compat
# older versions of tower-cli don't *have* a credential_type
# resource
params['kind'] = module.params.get('kind')
else:
if module.params.get('credential_type'):
credential_type_res = tower_cli.get_resource('credential_type')
try:
credential_type = credential_type_res.get(name=module.params['credential_type'])
except (exc.NotFound) as excinfo:
module.fail_json(msg=(
'Failed to update credential, credential_type not found: {0}'
).format(excinfo), changed=False)
params['credential_type'] = credential_type['id']
if module.params.get('inputs'): cred_type_id = module.resolve_name_to_id('credential_types', credential_type if credential_type else KIND_CHOICES[kind])
params['inputs'] = module.params.get('inputs')
elif module.params.get('kind'): # Attempt to look up the object based on the provided name and inventory ID
credential_type = credential_type_for_kind(module.params) credential = module.get_one('credentials', **{
params['credential_type'] = credential_type['id'] 'data': {
else: 'name': name,
module.fail_json(msg='must either specify credential_type or kind', changed=False) 'credential_type': cred_type_id,
}
})
if module.params.get('description'): # Create credential input from legacy inputs
params['description'] = module.params.get('description') credential_inputs = {}
for legacy_input in OLD_INPUT_NAMES:
if module.params.get(legacy_input) is not None:
module.deprecate(msg='{0} parameter has been depricated, please use inputs instead'.format(legacy_input), version="3.6")
credential_inputs[legacy_input] = module.params.get(legacy_input)
if inputs:
credential_inputs.update(inputs)
if module.params.get('user'): # Create the data that gets sent for create and update
user_res = tower_cli.get_resource('user') credential_fields = {
user = user_res.get(username=module.params.get('user')) 'name': new_name if new_name else name,
params['user'] = user['id'] 'credential_type': cred_type_id,
'inputs': credential_inputs,
}
if description:
credential_fields['description'] = description
if organization:
credential_fields['organization'] = org_id
if module.params.get('team'): # If we don't already have a credential (and we are creating one) we can add user/team
team_res = tower_cli.get_resource('team') # The API does not appear to do anything with these after creation anyway
team = team_res.get(name=module.params.get('team')) # NOTE: We can't just add these on a modification because they are never returned from a GET so it would always cause a changed=True
params['team'] = team['id'] if not credential:
if user:
credential_fields['user'] = user_id
if team:
credential_fields['team'] = team_id
if module.params.get('ssh_key_data'): if state == 'absent':
data = module.params.get('ssh_key_data') # If the state was absent we can let the module delete it if needed, the module will handle exiting from this
if os.path.exists(data): module.delete_if_needed(credential)
module.deprecate( elif state == 'present':
msg='ssh_key_data should be a string, not a path to a file.', # If the state was present we can let the module build or update the existing group, this will return on its own
version="2.12" module.create_or_update_if_needed(
credential, credential_fields, endpoint='credentials', item_type='credential'
) )
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
if module.params.get('vault_id', None) and module.params.get('kind') != 'vault':
module.fail_json(msg="Parameter 'vault_id' is only valid if parameter 'kind' is specified as 'vault'")
for key in OLD_INPUT_NAMES:
if 'kind' in params:
params[key] = module.params.get(key)
elif module.params.get(key):
params.setdefault('inputs', {})[key] = module.params.get(key)
if state == 'present':
result = credential.modify(**params)
json_output['id'] = result['id']
elif state == 'absent':
result = credential.delete(**params)
except (exc.NotFound) as excinfo:
module.fail_json(msg='Failed to update credential, organization not found: {0}'.format(excinfo), changed=False)
except (exc.ConnectionError, exc.BadRequest, exc.AuthError) as excinfo:
module.fail_json(msg='Failed to update credential: {0}'.format(excinfo), changed=False)
json_output['changed'] = result['changed']
module.exit_json(**json_output)
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -1,24 +1,29 @@
--- ---
- name: Generate a random string for test
set_fact:
test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
when: test_id is not defined
- name: Generate names - name: Generate names
set_fact: set_fact:
ssh_cred_name1: "AWX-Collection-tests-tower_credential-ssh-cred1-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" ssh_cred_name1: "AWX-Collection-tests-tower_credential-ssh-cred1-{{ test_id }}"
ssh_cred_name2: "AWX-Collection-tests-tower_credential-ssh-cred2-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" ssh_cred_name2: "AWX-Collection-tests-tower_credential-ssh-cred2-{{ test_id }}"
ssh_cred_name3: "AWX-Collection-tests-tower_credential-ssh-cred-lookup-source-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" ssh_cred_name3: "AWX-Collection-tests-tower_credential-ssh-cred-lookup-source-{{ test_id }}"
ssh_cred_name4: "AWX-Collection-tests-tower_credential-ssh-cred-file-source-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" ssh_cred_name4: "AWX-Collection-tests-tower_credential-ssh-cred-file-source-{{ test_id }}"
vault_cred_name1: "AWX-Collection-tests-tower_credential-vault-cred1-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" vault_cred_name1: "AWX-Collection-tests-tower_credential-vault-cred1-{{ test_id }}"
vault_cred_name2: "AWX-Collection-tests-tower_credential-vault-ssh-cred1-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" vault_cred_name2: "AWX-Collection-tests-tower_credential-vault-ssh-cred1-{{ test_id }}"
net_cred_name1: "AWX-Collection-tests-tower_credential-net-cred1-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" net_cred_name1: "AWX-Collection-tests-tower_credential-net-cred1-{{ test_id }}"
scm_cred_name1: "AWX-Collection-tests-tower_credential-scm-cred1-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" scm_cred_name1: "AWX-Collection-tests-tower_credential-scm-cred1-{{ test_id }}"
aws_cred_name1: "AWX-Collection-tests-tower_credential-aws-cred1-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" aws_cred_name1: "AWX-Collection-tests-tower_credential-aws-cred1-{{ test_id }}"
vmware_cred_name1: "AWX-Collection-tests-tower_credential-vmware-cred1-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" vmware_cred_name1: "AWX-Collection-tests-tower_credential-vmware-cred1-{{ test_id }}"
sat6_cred_name1: "AWX-Collection-tests-tower_credential-sat6-cred1-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" sat6_cred_name1: "AWX-Collection-tests-tower_credential-sat6-cred1-{{ test_id }}"
cf_cred_name1: "AWX-Collection-tests-tower_credential-cf-cred1-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" cf_cred_name1: "AWX-Collection-tests-tower_credential-cf-cred1-{{ test_id }}"
gce_cred_name1: "AWX-Collection-tests-tower_credential-gce-cred1-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" gce_cred_name1: "AWX-Collection-tests-tower_credential-gce-cred1-{{ test_id }}"
azurerm_cred_name1: "AWX-Collection-tests-tower_credential-azurerm-cred1-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" azurerm_cred_name1: "AWX-Collection-tests-tower_credential-azurerm-cred1-{{ test_id }}"
openstack_cred_name1: "AWX-Collection-tests-tower_credential-openstack-cred1-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" openstack_cred_name1: "AWX-Collection-tests-tower_credential-openstack-cred1-{{ test_id }}"
rhv_cred_name1: "AWX-Collection-tests-tower_credential-rhv-cred1-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" rhv_cred_name1: "AWX-Collection-tests-tower_credential-rhv-cred1-{{ test_id }}"
insights_cred_name1: "AWX-Collection-tests-tower_credential-insights-cred1-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" insights_cred_name1: "AWX-Collection-tests-tower_credential-insights-cred1-{{ test_id }}"
tower_cred_name1: "AWX-Collection-tests-tower_credential-tower-cred1-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" tower_cred_name1: "AWX-Collection-tests-tower_credential-tower-cred1-{{ test_id }}"
- name: create a tempdir for an SSH key - name: create a tempdir for an SSH key
local_action: shell mktemp -d local_action: shell mktemp -d
@@ -31,7 +36,42 @@
set_fact: set_fact:
ssh_key_data: "{{ lookup('file', tempdir.stdout + '/id_rsa') }}" ssh_key_data: "{{ lookup('file', tempdir.stdout + '/id_rsa') }}"
- name: Create a User-specific credential - name: Test deprication warnings
tower_credential:
name: "{{ ssh_cred_name1 }}"
organization: Default
user: admin
kind: ssh
authorize: False
authorize_password: 'test'
client: 'test'
security_token: 'test'
secret: 'test'
tenant: 'test'
subscription: 'test'
domain: 'test'
become_method: 'test'
become_username: 'test'
become_password: 'test'
vault_password: 'test'
project: 'test'
host: 'test'
username: 'test'
password: 'test'
ssh_key_data: 'test'
vault_id: 'test'
ssh_key_unlock: 'test'
state: absent
ignore_errors: True
register: result
- assert:
that:
- "'deprecations' in result"
# The 20 comes from the length of OLD_INPUT_NAMES + 1 for kind
- result['deprecations'] | length() == 20
- name: Create a User-specific credential (old school)
tower_credential: tower_credential:
name: "{{ ssh_cred_name1 }}" name: "{{ ssh_cred_name1 }}"
organization: Default organization: Default
@@ -44,6 +84,44 @@
that: that:
- "result is changed" - "result is changed"
- name: Re-create the User-specific credential (new school)
tower_credential:
name: "{{ ssh_cred_name1 }}"
organization: Default
user: admin
credential_type: 'Machine'
state: present
register: result
- assert:
that:
- "result is not changed"
- name: Delete a User-specific credential
tower_credential:
name: "{{ ssh_cred_name1 }}"
organization: Default
user: admin
state: absent
kind: ssh
register: result
- assert:
that:
- "result is changed"
- name: Create the User-specific credential tied to a user, no org
tower_credential:
name: "{{ ssh_cred_name1 }}"
user: admin
credential_type: 'Machine'
state: present
register: result
- assert:
that:
- "result is changed"
- name: Delete a User-specific credential - name: Delete a User-specific credential
tower_credential: tower_credential:
name: "{{ ssh_cred_name1 }}" name: "{{ ssh_cred_name1 }}"
@@ -57,7 +135,7 @@
that: that:
- "result is changed" - "result is changed"
- name: Create a valid SSH credential - name: Create a valid SSH credential (old school)
tower_credential: tower_credential:
name: "{{ ssh_cred_name2 }}" name: "{{ ssh_cred_name2 }}"
organization: Default organization: Default
@@ -77,7 +155,48 @@
that: that:
- "result is changed" - "result is changed"
- name: Create a valid SSH credential from lookup source - name: Create a valid SSH credential (new school)
tower_credential:
name: "{{ ssh_cred_name2 }}"
organization: Default
state: present
credential_type: Machine
description: An example SSH 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
# This will be changed because we are setting ssh_key_data and ssh_key_unlock.
# These will come out as $encrypted$ which will always compare false to the values.
- assert:
that:
- result is changed
- name: Create a valid SSH credential (new school) (no change)
tower_credential:
name: "{{ ssh_cred_name2 }}"
organization: Default
state: present
credential_type: Machine
description: An example SSH credential
inputs:
username: joe
become_method: sudo
become_username: superuser
register: result
# This should no longer be changed because we aren't passing any secure fields
- assert:
that:
- result is not changed
- name: Create a valid SSH credential from lookup source (old school)
tower_credential: tower_credential:
name: "{{ ssh_cred_name3 }}" name: "{{ ssh_cred_name3 }}"
organization: Default organization: Default
@@ -97,7 +216,29 @@
that: that:
- "result is changed" - "result is changed"
- name: Create a valid SSH credential from file source - name: Create a valid SSH credential from lookup source (new school)
tower_credential:
name: "{{ ssh_cred_name3 }}"
organization: Default
state: present
credential_type: Machine
description: An example SSH credential from lookup source
inputs:
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
# This will be changed because we are passing in ssh_key_data and password
- assert:
that:
- result is changed
- name: Fail to create an SSH credential from a file source (old school format)
tower_credential: tower_credential:
name: "{{ ssh_cred_name4 }}" name: "{{ ssh_cred_name4 }}"
organization: Default organization: Default
@@ -112,12 +253,13 @@
ssh_key_data: "{{ tempdir.stdout }}/id_rsa" ssh_key_data: "{{ tempdir.stdout }}/id_rsa"
ssh_key_unlock: "passphrase" ssh_key_unlock: "passphrase"
register: result register: result
ignore_errors: True
- assert: - assert:
that: that:
- "result is changed" - result is failed
- "result is not failed" - "'Unable to create credential {{ ssh_cred_name4 }}' in result.msg"
- "'ssh_key_data should be a string, not a path to a file.' in result.deprecations[0].msg" - "'Invalid certificate or key' in result.msg"
- name: Create an invalid SSH credential (passphrase required) - name: Create an invalid SSH credential (passphrase required)
tower_credential: tower_credential:
@@ -148,7 +290,7 @@
- assert: - assert:
that: that:
- "result is failed" - "result is failed"
- "'The requested object could not be found' in result.msg" - "'The organizations Missing Organization was not found on the Tower server' in result.msg"
- name: Delete an SSH credential - name: Delete an SSH credential
tower_credential: tower_credential:
@@ -182,9 +324,10 @@
kind: ssh kind: ssh
register: result register: result
# This one was never really created so it shouldn't be deleted
- assert: - assert:
that: that:
- "result is changed" - "result is not changed"
- name: Create a valid Vault credential - name: Create a valid Vault credential
tower_credential: tower_credential:
@@ -201,7 +344,7 @@
- "result is changed" - "result is changed"
# We should decide when to delete this test # We should decide when to delete this test
- name: Create a valid Vault credential w/ kind=ssh (deprecated) - name: Create a valid Vault credential w/ kind=ssh (deprecated, will now fail)
tower_credential: tower_credential:
name: "{{ vault_cred_name2 }}" name: "{{ vault_cred_name2 }}"
organization: Default organization: Default
@@ -210,10 +353,14 @@
description: An example Vault credential description: An example Vault credential
vault_password: secret-vault vault_password: secret-vault
register: result register: result
ignore_errors: True
- assert: - assert:
that: that:
- "result is changed" - result is failed
- "'Unable to create credential {{ vault_cred_name2 }}' in result.msg"
- "'Additional properties are not allowed' in result.msg"
- "'\\'vault_password\\' was unexpected' in result.msg"
- name: Delete a Vault credential - name: Delete a Vault credential
tower_credential: tower_credential:
@@ -235,9 +382,10 @@
kind: vault kind: vault
register: result register: result
# The creation of vault_cred_name2 never worked so we shouldn't actually need to delete it
- assert: - assert:
that: that:
- "result is changed" - "result is not changed"
- name: Create a valid Network credential - name: Create a valid Network credential
tower_credential: tower_credential:
@@ -594,4 +742,5 @@
- assert: - assert:
that: that:
- "result.msg =='Failed to update credential, organization not found: The requested object could not be found.'" - result is failed
- "result.msg =='The organizations test-non-existing-org was not found on the Tower server'"