Commit 0bd13b8c authored by John Westcott IV's avatar John Westcott IV

Additional Fixes for PR

Resolved SCM credentials for projects

Export and import extra_credentials in job_templates

Resolve credentials for inventory source

Now checks notification templates before asset import
parent 3cc21618
......@@ -74,7 +74,6 @@ def resolve_asset_dependencies(an_asset, asset_type):
# Multiple credentials on things like job templates come through as:
# vault_credential
# machine_credential
# extra_credential
if relation.endswith("credential"):
model_type = "credential"
......@@ -214,6 +213,14 @@ def extract_inventory_relations(asset, relation_type):
relation['source_script'], relation_type, e
new_relation['source_script'] = script['name']
if 'credential' in relation and relation['credential']:
credential = tower_cli.get_resource('credential').get(relation['credential'])
except TowerCLIError as e:
raise TowerCLIError("Unable to get inventory credential {} for {} : {}".format(
relation['credential'], relation_type, e
new_relation['credential'] = credential['name']
del new_relation['inventory']
......@@ -353,3 +360,15 @@ def get_assets_from_input(all=False, asset_input=None):
raise TowerCLIError("Nothing assets were specified")
return return_assets
def extract_extra_credentials(asset):
return_credentials = []
name_to_id_map = {}
extra_credentials = load_all_assets(asset['related']['extra_credentials'])
for a_credential in extra_credentials['results']:
name_to_id_map[a_credential['name']] = a_credential['id']
return {'items': return_credentials, 'existing_name_to_id_map': name_to_id_map}
......@@ -96,6 +96,10 @@ class Receiver:
exported_asset[common.ASSET_RELATION_KEY][notification_type] = \
common.extract_notifications(asset, notification_type)
elif relation == 'extra_credentials':
exported_asset[common.ASSET_RELATION_KEY][relation] =\
# Finally add the object to the list of objects that are being exported
......@@ -15,6 +15,7 @@ class Sender(LoggingCommand):
my_user = None
secret_management = 'default'
columns = None
sorted_assets = {}
def __init__(self, no_color):
self.no_color = no_color
......@@ -30,18 +31,19 @@ class Sender(LoggingCommand):
raise TowerCLIError("Unable to get assets from input")
# Next we will sort all of the assets by their type again, will raise if there are errors
sorted_assets = self.prep_and_sort_all_assets(import_json, prevent)
# This is setting sorted_assets
self.prep_and_sort_all_assets(import_json, prevent)
for asset_type in common.SEND_ORDER:
# If we don't have any of this asset type we can move on
if asset_type not in sorted_assets or len(sorted_assets[asset_type]) == 0:
if asset_type not in self.sorted_assets or len(self.sorted_assets[asset_type]) == 0:
identifier = common.get_identity(asset_type)
resource = tower_cli.get_resource(asset_type)
post_options = common.get_api_options(asset_type)
for an_asset in sorted_assets[asset_type]:
for an_asset in self.sorted_assets[asset_type]:
asset_name = an_asset[identifier]
self.print_header_row(asset_type, asset_name)
......@@ -276,6 +278,8 @@ class Sender(LoggingCommand):
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])
# Checking for post update actions on the different objects
if asset_changed:
......@@ -446,21 +450,39 @@ class Sender(LoggingCommand):
group['hosts'] = hosts
elif relation in common.NOTIFICATION_TYPES:
# You can't really do anything with notifications because they are just names
# We could try and resolve but if we are importing the notification we need
# we wouldn't be able to resolve the name beforehand.
for notification_name in an_asset[common.ASSET_RELATION_KEY][relation]:
if 'notification_template' in self.sorted_assets and\
notification_name in self.sorted_assets['notification_template']:
tower_cli.get_resource('notification_template').get(**{'name': notification_name})
except TowerCLIError:
self.log_error("Unable to resolve {} {}".format(relation, notification_name))
post_check_succeeded = False
elif relation == 'inventory_source':
# Someday we could go and try to resolve inventory projects or scripts
elif relation == 'extra_credentials':
for credential in an_asset[common.ASSET_RELATION_KEY][relation]:
if 'credential' in self.sorted_assets and credential in self.sorted_assets['credential']:
tower_cli.get_resource('credential').get(**{'name': credential})
except TowerCLIError:
self.log_error("Unable to resolve extra_credential {}".format(credential))
post_check_succeeded = False
self.log_warn("WARNING: Relation {} is not checked".format(relation))
return post_check_succeeded
def prep_and_sort_all_assets(self, import_json, prevent):
sorted_assets = {}
self.sorted_assets = {}
had_sort_issues = False
for asset in import_json:
if common.ASSET_TYPE_KEY not in asset:
......@@ -493,16 +515,14 @@ class Sender(LoggingCommand):
del asset[common.ASSET_TYPE_KEY]
# We made it here so the asset should be ok to try and import so add it to a list to import
if asset_type not in sorted_assets:
sorted_assets[asset_type] = []
if asset_type not in self.sorted_assets:
self.sorted_assets[asset_type] = []
# If we got any errors when sorting the assets raise an exception
if had_sort_issues:
raise TowerCLIError("One or more errors encountered in provided assets, stopping import")
return sorted_assets
def get_all_objects(self, source):
all_objects = []
......@@ -815,6 +835,38 @@ class Sender(LoggingCommand):
del a_node['unified_job_type']
del a_node['unified_job_name']
def import_extra_credentials(self, existing_object, new_creds):
existing_creds_data = common.extract_extra_credentials(existing_object)
existing_creds = existing_creds_data['items']
existing_name_to_id = existing_creds_data['existing_name_to_id_map']
if existing_creds == new_creds:
self.log_ok("All extra creds are up to date")
# Creds to remove is the difference between new_creds and existing_creds
for cred in list(set(existing_creds).difference(new_creds)):
existing_object['id'], existing_name_to_id[cred]
self.log_change("Removed extra credential {}".format(cred))
except TowerCLIError as e:
self.log_error("Unable to remove extra credential {} : {}".format(cred, e))
# Creds to add is the difference between existing_creds and extra_creds
for cred in list(set(new_creds).difference(existing_creds)):
new_credential = tower_cli.get_resource('credential').get(**{'name': cred})
except TowerCLIError as e:
self.log_error("Unable to resolve extra credential {} : {}".format(cred, e))
tower_cli.get_resource('job_template').associate_credential(existing_object['id'], new_credential['id'])
self.log_change("Added extra credential {}".format(cred))
except TowerCLIError as e:
self.log_error("Unable to add extra credential {} : ".format(cred, e))
def import_inventory_groups(self, existing_object, groups):
existing_groups_data = common.extract_inventory_groups(existing_object)
existing_groups = existing_groups_data['items']
......@@ -1059,6 +1111,17 @@ class Sender(LoggingCommand):
if 'credential' in item:
credential = tower_cli.get_resource('credential').get(**{'name': item['credential']})
item['credential'] = credential['id']
except TowerCLIError as e:
"Unable to resolve credential {} as credential for inventory source {} : {}".format(
item['credential'], item['name'], e
if len(resolve_errors) > 0:
raise TowerCLIError("\n".join(resolve_errors))
......@@ -28,7 +28,7 @@ class Resource(models.SurveyResource):
cli_help = 'Manage job templates.'
endpoint = '/job_templates/'
dependencies = ['inventory', 'credential', 'project', 'vault_credential']
related = ['survey_spec', 'notification_templates']
related = ['survey_spec', 'notification_templates', 'extra_credentials']
name = models.Field(unique=True)
description = models.Field(required=False, display=False)
......@@ -26,7 +26,7 @@ class Resource(models.Resource, models.MonitorableResource):
cli_help = 'Manage projects within Ansible Tower.'
endpoint = '/projects/'
unified_job_type = '/project_updates/'
dependencies = ['organization']
dependencies = ['organization', 'credential']
related = ['notification_templates']
name = models.Field(unique=True)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment