diff --git a/README.rst b/README.rst index 655c7235..feda9d62 100644 --- a/README.rst +++ b/README.rst @@ -10,6 +10,10 @@ It is also what the Ansible `tower_*` modules use under the hood. Such as: https://docs.ansible.com/ansible/latest/modules/tower_organization_module.html +These modules are now vendored as part of the AWX collection at: + +https://galaxy.ansible.com/awx/awx + Supporting correct operation of the modules is the maintenance aim of this package. diff --git a/docs/source/HISTORY.rst b/docs/source/HISTORY.rst index 6e51b43a..3e0725e6 100644 --- a/docs/source/HISTORY.rst +++ b/docs/source/HISTORY.rst @@ -1,6 +1,12 @@ Release History =============== +3.3.7 (2019-10-25) +------------------ + +- Job template associate_credential now uses "credentials" endpoint +- Include related credentials to job templates in send and receive commands + 3.3.6 (2019-07-19) ------------------ diff --git a/tests/test_resources_job_template.py b/tests/test_resources_job_template.py index b5e088d0..612e5a32 100644 --- a/tests/test_resources_job_template.py +++ b/tests/test_resources_job_template.py @@ -41,7 +41,7 @@ def test_create(self): t.register_json(endpoint, {'changed': True, 'id': 42}, method='POST') self.res.create(name='bar', job_type='run', inventory=1, - project=1, playbook='foobar.yml', credential=1) + project=1, playbook='foobar.yml') self.assertEqual(t.requests[0].method, 'GET') self.assertEqual(t.requests[1].method, 'POST') self.assertEqual(len(t.requests), 2) @@ -55,7 +55,7 @@ def test_create(self): t.register_json(endpoint, {'changed': True, 'id': 42}, method='POST') self.res.create(name='bar', inventory=1, project=1, - playbook='foobar.yml', credential=1) + playbook='foobar.yml') req_body = json.loads(t.requests[1].body) self.assertIn('job_type', req_body) self.assertEqual(req_body['job_type'], 'run') @@ -74,13 +74,13 @@ def test_job_template_create_with_echo(self): 'playbook': 'foobar.yml', 'credential': 1}, method='POST') self.res.create(name='bar', job_type='run', inventory=1, - project=1, playbook='foobar.yml', credential=1) + project=1, playbook='foobar.yml') f = ResSubcommand(self.res)._echo_method(self.res.create) with mock.patch.object(click, 'secho'): with settings.runtime_values(format='human'): f(name='bar', job_type='run', inventory=1, - project=1, playbook='foobar.yml', credential=1) + project=1, playbook='foobar.yml') def test_create_w_extra_vars(self): """Establish that a job template can be created @@ -94,7 +94,7 @@ def test_create_w_extra_vars(self): t.register_json(endpoint, {'changed': True, 'id': 42}, method='POST') self.res.create(name='bar', job_type='run', inventory=1, - project=1, playbook='foobar.yml', credential=1, + project=1, playbook='foobar.yml', extra_vars=['foo: bar']) self.assertEqual(t.requests[0].method, 'GET') self.assertEqual(t.requests[1].method, 'POST') @@ -137,9 +137,9 @@ def test_associate_credential(self): that we expect. """ with client.test_mode as t: - t.register_json('/job_templates/42/extra_credentials/?id=84', + t.register_json('/job_templates/42/credentials/?id=84', {'count': 0, 'results': []}) - t.register_json('/job_templates/42/extra_credentials/', {}, method='POST') + t.register_json('/job_templates/42/credentials/', {}, method='POST') self.res.associate_credential(42, 84) self.assertEqual(t.requests[1].body, json.dumps({'associate': True, 'id': 84})) @@ -149,10 +149,10 @@ def test_disassociate_credential(self): that we expect. """ with client.test_mode as t: - t.register_json('/job_templates/42/extra_credentials/?id=84', + t.register_json('/job_templates/42/credentials/?id=84', {'count': 1, 'results': [{'id': 84}], 'next': None, 'previous': None}) - t.register_json('/job_templates/42/extra_credentials/', {}, method='POST') + t.register_json('/job_templates/42/credentials/', {}, method='POST') self.res.disassociate_credential(42, 84) self.assertEqual(t.requests[1].body, json.dumps({'disassociate': True, 'id': 84})) diff --git a/tower_cli/cli/transfer/common.py b/tower_cli/cli/transfer/common.py index d651b9f3..acff3d28 100644 --- a/tower_cli/cli/transfer/common.py +++ b/tower_cli/cli/transfer/common.py @@ -96,6 +96,9 @@ def map_node_to_post_options(post_options, source_node, target_node): if 'required' in post_options[option] and post_options[option]["required"]: target_node[option] = source_node[option] elif option in source_node and source_node[option] != default: + # work-around AWX bug in 8.0.0 where webhook_service OPTIONS not right + if option == 'webhook_service' and source_node[option] == '': + continue target_node[option] = source_node[option] @@ -419,12 +422,12 @@ def get_assets_from_input(all=False, asset_input=None): return return_assets -def extract_extra_credentials(asset): +def extract_credentials(asset): return_credentials = [] name_to_id_map = {} - extra_credentials = load_all_assets(asset['related']['extra_credentials']) - for a_credential in extra_credentials['results']: + credentials = load_all_assets(asset['related']['credentials']) + for a_credential in credentials['results']: name_to_id_map[a_credential['name']] = a_credential['id'] return_credentials.append(a_credential['name']) diff --git a/tower_cli/cli/transfer/receive.py b/tower_cli/cli/transfer/receive.py index 6a4bfd05..0a918078 100644 --- a/tower_cli/cli/transfer/receive.py +++ b/tower_cli/cli/transfer/receive.py @@ -97,9 +97,9 @@ def export_assets(self, all, asset_input): exported_asset[common.ASSET_RELATION_KEY][notification_type] = \ common.extract_notifications(asset, notification_type) - elif relation == 'extra_credentials': + elif relation == 'credentials': exported_asset[common.ASSET_RELATION_KEY][relation] =\ - common.extract_extra_credentials(asset)['items'] + common.extract_credentials(asset)['items'] elif relation == 'schedules': exported_asset[common.ASSET_RELATION_KEY][relation] =\ diff --git a/tower_cli/cli/transfer/send.py b/tower_cli/cli/transfer/send.py index 65470216..3696f2f8 100644 --- a/tower_cli/cli/transfer/send.py +++ b/tower_cli/cli/transfer/send.py @@ -1,5 +1,7 @@ import tower_cli import json +import six +import re from tower_cli.exceptions import TowerCLIError, CannotStartJob, JobFailure import tower_cli.cli.transfer.common as common from tower_cli.cli.transfer.logging_command import LoggingCommand @@ -96,7 +98,7 @@ def send(self, source, prevent, exclude, secret_management): # First use the API to get the user from tower_cli.api import Client api_client = Client() - me_response = api_client.request('GET', 'me') + me_response = api_client.request('GET', 'me/') response_json = me_response.json() if 'results' not in response_json or 'id' not in response_json['results'][0]: raise TowerCLIError("Unable to get user information from Tower") @@ -287,8 +289,8 @@ def send(self, source, prevent, exclude, secret_management): self.import_inventory_groups(existing_object, relations[a_relation]) elif a_relation in common.NOTIFICATION_TYPES: self.import_notification_relations(existing_object, relations[a_relation], a_relation) - elif a_relation == 'extra_credentials': - self.import_extra_credentials(existing_object, relations[a_relation]) + elif a_relation == 'credentials': + self.import_credentials(existing_object, relations[a_relation]) elif a_relation == 'schedules': schedules_to_import.append(relations[a_relation]) elif a_relation == 'roles': @@ -396,20 +398,33 @@ def can_object_post(self, asset_type, an_asset, post_options): # Choices is an array like [ [ value, label ], [value, label], ... ] valid_options = [] for a_choice in post_options[option]["choices"]: - value = a_choice[0] - label = a_choice[1] + if isinstance(a_choice, six.string_types): + m = re.match(r"^\('(?P.*)',\s'(?P