diff --git a/awx/main/managers.py b/awx/main/managers.py index 1683b38c4e..8748a71c5b 100644 --- a/awx/main/managers.py +++ b/awx/main/managers.py @@ -87,7 +87,11 @@ class InstanceManager(models.Manager): return node[0] raise RuntimeError("No instance found with the current cluster host id") - def register(self, uuid=settings.SYSTEM_UUID, hostname=settings.CLUSTER_HOST_ID): + def register(self, uuid=None, hostname=None): + if not uuid: + uuid = settings.SYSTEM_UUID + if not hostname: + hostname = settings.CLUSTER_HOST_ID with advisory_lock('instance_registration_%s' % hostname): instance = self.filter(hostname=hostname) if instance.exists(): diff --git a/awx/main/tasks.py b/awx/main/tasks.py index ccef351626..2c5c1afa7e 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -55,7 +55,7 @@ from awx.main.queue import CallbackQueueDispatcher from awx.main.expect import run, isolated_manager from awx.main.utils import (get_ansible_version, get_ssh_version, decrypt_field, update_scm_url, check_proot_installed, build_proot_temp_dir, get_licenser, - wrap_args_with_proot, OutputEventFilter, ignore_inventory_computed_fields, + wrap_args_with_proot, OutputEventFilter, OutputVerboseFilter, ignore_inventory_computed_fields, ignore_inventory_group_removal, get_type_for_model, extract_ansible_vars) from awx.main.utils.reload import restart_local_services, stop_local_services from awx.main.utils.pglock import advisory_lock @@ -821,19 +821,26 @@ class BaseTask(LogErrorsTask): def get_stdout_handle(self, instance): ''' - Return an virtual file object for capturing stdout and events. + Return an virtual file object for capturing stdout and/or events. ''' dispatcher = CallbackQueueDispatcher() - def event_callback(event_data): - event_data.setdefault(self.event_data_key, instance.id) - if 'uuid' in event_data: - cache_event = cache.get('ev-{}'.format(event_data['uuid']), None) - if cache_event is not None: - event_data.update(cache_event) - dispatcher.dispatch(event_data) + if isinstance(instance, (Job, AdHocCommand, ProjectUpdate)): + def event_callback(event_data): + event_data.setdefault(self.event_data_key, instance.id) + if 'uuid' in event_data: + cache_event = cache.get('ev-{}'.format(event_data['uuid']), None) + if cache_event is not None: + event_data.update(cache_event) + dispatcher.dispatch(event_data) - return OutputEventFilter(event_callback) + return OutputEventFilter(event_callback) + else: + def event_callback(event_data): + event_data.setdefault(self.event_data_key, instance.id) + dispatcher.dispatch(event_data) + + return OutputVerboseFilter(event_callback) def pre_run_hook(self, instance, **kwargs): ''' diff --git a/awx/main/tests/unit/utils/test_event_filter.py b/awx/main/tests/unit/utils/test_event_filter.py index 85ecc609d0..fb8f4fa144 100644 --- a/awx/main/tests/unit/utils/test_event_filter.py +++ b/awx/main/tests/unit/utils/test_event_filter.py @@ -5,7 +5,7 @@ from StringIO import StringIO from six.moves import xrange -from awx.main.utils import OutputEventFilter +from awx.main.utils import OutputEventFilter, OutputVerboseFilter MAX_WIDTH = 78 EXAMPLE_UUID = '890773f5-fe6d-4091-8faf-bdc8021d65dd' @@ -145,3 +145,55 @@ def test_large_stdout_blob(): f = OutputEventFilter(_callback) for x in range(1024 * 10): f.write('x' * 1024) + + +def test_verbose_line_buffering(): + events = [] + + def _callback(event_data): + events.append(event_data) + + f = OutputVerboseFilter(_callback) + f.write('one two\r\n\r\n') + + assert len(events) == 2 + assert events[0]['start_line'] == 0 + assert events[0]['end_line'] == 1 + assert events[0]['stdout'] == 'one two' + + assert events[1]['start_line'] == 1 + assert events[1]['end_line'] == 2 + assert events[1]['stdout'] == '' + + f.write('three') + assert len(events) == 2 + f.write('\r\nfou') + + # three is not pushed to buffer until its line completes + assert len(events) == 3 + assert events[2]['start_line'] == 2 + assert events[2]['end_line'] == 3 + assert events[2]['stdout'] == 'three' + + f.write('r\r') + f.write('\nfi') + + assert events[3]['start_line'] == 3 + assert events[3]['end_line'] == 4 + assert events[3]['stdout'] == 'four' + + f.write('ve') + f.write('\r\n') + + assert len(events) == 5 + assert events[4]['start_line'] == 4 + assert events[4]['end_line'] == 5 + assert events[4]['stdout'] == 'five' + + f.close() + + from pprint import pprint + pprint(events) + assert len(events) == 6 + + assert events[5]['event'] == 'EOF' diff --git a/awx/main/utils/common.py b/awx/main/utils/common.py index ba3413a133..be531d7e17 100644 --- a/awx/main/utils/common.py +++ b/awx/main/utils/common.py @@ -48,7 +48,7 @@ __all__ = ['get_object_or_400', 'get_object_or_403', 'camelcase_to_underscore', 'copy_m2m_relationships', 'prefetch_page_capabilities', 'to_python_boolean', 'ignore_inventory_computed_fields', 'ignore_inventory_group_removal', '_inventory_updates', 'get_pk_from_dict', 'getattrd', 'NoDefaultProvided', - 'get_current_apps', 'set_current_apps', 'OutputEventFilter', + 'get_current_apps', 'set_current_apps', 'OutputEventFilter', 'OutputVerboseFilter', 'extract_ansible_vars', 'get_search_fields', 'get_system_task_capacity', 'get_cpu_capacity', 'get_mem_capacity', 'wrap_args_with_proot', 'build_proot_temp_dir', 'check_proot_installed', 'model_to_dict', 'model_instance_diff', 'timestamp_apiformat', 'parse_yaml_or_json', 'RequireDebugTrueOrTest', @@ -1009,6 +1009,32 @@ class OutputEventFilter(object): self._current_event_data = None +class OutputVerboseFilter(OutputEventFilter): + ''' + File-like object that dispatches stdout data. + Does not search for encoded job event data. + Use for unified job types that do not encode job event data. + ''' + def write(self, data): + self._buffer.write(data) + + # if the current chunk contains a line break + if data and '\n' in data: + # emit events for all complete lines we know about + lines = self._buffer.getvalue().splitlines(True) # keep ends + remainder = None + # if last line is not a complete line, then exclude it + if '\n' not in lines[-1]: + remainder = lines.pop() + # emit all complete lines + for line in lines: + self._emit_event(line) + self._buffer = StringIO() + # put final partial line back on buffer + if remainder: + self._buffer.write(remainder) + + def is_ansible_variable(key): return key.startswith('ansible_') diff --git a/awx/sso/conf.py b/awx/sso/conf.py index 504b7724d4..fef093e62d 100644 --- a/awx/sso/conf.py +++ b/awx/sso/conf.py @@ -301,8 +301,8 @@ def _register_ldap(append=None): register( 'AUTH_LDAP{}_GROUP_TYPE_PARAMS'.format(append_str), field_class=fields.LDAPGroupTypeParamsField, - label=_('LDAP Group Type'), - help_text=_('Parameters to send the chosen group type.'), + label=_('LDAP Group Type Parameters'), + help_text=_('Key value parameters to send the chosen group type init method.'), category=_('LDAP'), category_slug='ldap', default=collections.OrderedDict([ diff --git a/awx/ui/client/src/inventories-hosts/inventories/inventory.list.js b/awx/ui/client/src/inventories-hosts/inventories/inventory.list.js index c075662883..a8edfa27eb 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/inventory.list.js +++ b/awx/ui/client/src/inventories-hosts/inventories/inventory.list.js @@ -113,7 +113,7 @@ export default ['i18n', function(i18n) { "class": 'btn-danger btn-xs', awToolTip: i18n._('Copy inventory'), dataPlacement: 'top', - ngShow: 'inventory.summary_fields.user_capabilities.edit' + ngShow: '!inventory.pending_deletion && inventory.summary_fields.user_capabilities.edit' }, view: { label: i18n._('View'), diff --git a/awx/ui/client/src/organizations/organizations.form.js b/awx/ui/client/src/organizations/organizations.form.js index 14883a3428..f33dd00526 100644 --- a/awx/ui/client/src/organizations/organizations.form.js +++ b/awx/ui/client/src/organizations/organizations.form.js @@ -52,7 +52,8 @@ export default ['NotificationsList', 'i18n', dataTitle: i18n._('Ansible Environment'), dataContainer: 'body', dataPlacement: 'right', - ngDisabled: '!(organization_obj.summary_fields.user_capabilities.edit || canAdd)' + ngDisabled: '!(organization_obj.summary_fields.user_capabilities.edit || canAdd)', + ngShow: 'custom_virtualenvs_options.length > 0' } }, diff --git a/awx/ui/client/src/projects/projects.form.js b/awx/ui/client/src/projects/projects.form.js index 38c151215d..baa204bcee 100644 --- a/awx/ui/client/src/projects/projects.form.js +++ b/awx/ui/client/src/projects/projects.form.js @@ -52,17 +52,6 @@ export default ['i18n', 'NotificationsList', 'TemplateList', ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd) || !canEditOrg', awLookupWhen: '(project_obj.summary_fields.user_capabilities.edit || canAdd) && canEditOrg' }, - custom_virtualenv: { - label: i18n._('Ansible Environment'), - type: 'select', - defaultText: i18n._('Select Ansible Environment'), - ngOptions: 'venv for venv in custom_virtualenvs_options track by venv', - awPopOver: "

" + i18n._("Select the custom Python virtual environment for this project to run on.") + "

", - dataTitle: i18n._('Ansible Environment'), - dataContainer: 'body', - dataPlacement: 'right', - ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)' - }, scm_type: { label: i18n._('SCM Type'), type: 'select', @@ -211,8 +200,21 @@ export default ['i18n', 'NotificationsList', 'TemplateList', dataTitle: i18n._('Cache Timeout'), dataPlacement: 'right', dataContainer: "body", - ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)' - } + ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)', + subForm: 'sourceSubForm' + }, + custom_virtualenv: { + label: i18n._('Ansible Environment'), + type: 'select', + defaultText: i18n._('Select Ansible Environment'), + ngOptions: 'venv for venv in custom_virtualenvs_options track by venv', + awPopOver: "

" + i18n._("Select the custom Python virtual environment for this project to run on.") + "

", + dataTitle: i18n._('Ansible Environment'), + dataContainer: 'body', + dataPlacement: 'right', + ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)', + ngShow: 'custom_virtualenvs_options.length > 0' + }, }, buttons: { diff --git a/awx/ui/client/src/shared/list-generator/list-generator.factory.js b/awx/ui/client/src/shared/list-generator/list-generator.factory.js index 2df8eec05a..4e1189095e 100644 --- a/awx/ui/client/src/shared/list-generator/list-generator.factory.js +++ b/awx/ui/client/src/shared/list-generator/list-generator.factory.js @@ -395,61 +395,66 @@ export default ['$compile', 'Attr', 'Icon', } if (field_action === 'pending_deletion') { innerTable += `Pending Delete`; - } - // Plug in Dropdown Component - if (field_action === 'submit') { + } else if (field_action === 'submit') { innerTable += ``; } else { - fAction = list.fieldActions[field_action]; - innerTable += ""; } - //html += (fAction.label) ? " " + list.fieldActions[field_action].label + - // "" : ""; - innerTable += ""; } } } diff --git a/awx/ui/client/src/shared/upgrade/upgrade.block.less b/awx/ui/client/src/shared/upgrade/upgrade.block.less index 1f7ecbe25f..4d0203fa32 100644 --- a/awx/ui/client/src/shared/upgrade/upgrade.block.less +++ b/awx/ui/client/src/shared/upgrade/upgrade.block.less @@ -1,15 +1,17 @@ .at-Upgrade--panel { align-items: center; background-color: @at-color-body-background-light; + border-radius: 10px; color: @at-color-body-text; display: flex; flex-direction: column; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: @at-font-size-jumbotron-text; + height: ~"calc(100vh - 40px)"; justify-content: center; - margin-top: @at-space-10x; + margin: @at-space-4x; padding: @at-space-10x; - } +} .at-Upgrade--header { display: flex; diff --git a/awx/ui/client/src/templates/job_templates/job-template.form.js b/awx/ui/client/src/templates/job_templates/job-template.form.js index fc24852e2d..cd2824b8a3 100644 --- a/awx/ui/client/src/templates/job_templates/job-template.form.js +++ b/awx/ui/client/src/templates/job_templates/job-template.form.js @@ -240,7 +240,8 @@ function(NotificationsList, i18n) { dataTitle: i18n._('Ansible Environment'), dataContainer: 'body', dataPlacement: 'right', - ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)' + ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)', + ngShow: 'custom_virtualenvs_options.length > 0' }, instance_groups: { label: i18n._('Instance Groups'), diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js index a86b38dc2e..5037f15644 100644 --- a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js +++ b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js @@ -74,12 +74,13 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', let buildSendableNodeData = function() { // Create the node let sendableNodeData = { - unified_job_template: params.node.unifiedJobTemplate.id + unified_job_template: params.node.unifiedJobTemplate.id, + credential: _.get(params, 'node.originalNodeObj.credential') || null }; - if(_.has(params, 'node.promptData.extraVars')) { - if(_.get(params, 'node.promptData.launchConf.defaults.extra_vars')) { - if(!sendableNodeData.extra_data) { + if (_.has(params, 'node.promptData.extraVars')) { + if (_.get(params, 'node.promptData.launchConf.defaults.extra_vars')) { + if (!sendableNodeData.extra_data) { sendableNodeData.extra_data = {}; } @@ -87,15 +88,15 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', // Only include extra vars that differ from the template default vars _.forOwn(params.node.promptData.extraVars, (value, key) => { - if(!defaultVars[key] || defaultVars[key] !== value) { + if (!defaultVars[key] || defaultVars[key] !== value) { sendableNodeData.extra_data[key] = value; } }); - if(_.isEmpty(sendableNodeData.extra_data)) { + if (_.isEmpty(sendableNodeData.extra_data)) { delete sendableNodeData.extra_data; } } else { - if(_.has(params, 'node.promptData.extraVars') && !_.isEmpty(params.node.promptData.extraVars)) { + if (_.has(params, 'node.promptData.extraVars') && !_.isEmpty(params.node.promptData.extraVars)) { sendableNodeData.extra_data = params.node.promptData.extraVars; } } @@ -104,7 +105,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', // Check to see if the user has provided any prompt values that are different // from the defaults in the job template - if(params.node.unifiedJobTemplate.type === "job_template" && params.node.promptData) { + if (params.node.unifiedJobTemplate.type === "job_template" && params.node.promptData) { sendableNodeData = PromptService.bundlePromptDataForSaving({ promptData: params.node.promptData, dataToSave: sendableNodeData @@ -117,26 +118,23 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', let continueRecursing = function(parentId) { $scope.totalIteratedNodes++; - if($scope.totalIteratedNodes === $scope.treeData.data.totalNodes) { + if ($scope.totalIteratedNodes === $scope.treeData.data.totalNodes) { // We're done recursing, lets move on completionCallback(); - } - else { - if(params.node.children && params.node.children.length > 0) { + } else { + if (params.node.children && params.node.children.length > 0) { _.forEach(params.node.children, function(child) { - if(child.edgeType === "success") { + if (child.edgeType === "success") { recursiveNodeUpdates({ parentId: parentId, node: child }, completionCallback); - } - else if(child.edgeType === "failure") { + } else if (child.edgeType === "failure") { recursiveNodeUpdates({ parentId: parentId, node: child }, completionCallback); - } - else if(child.edgeType === "always") { + } else if (child.edgeType === "always") { recursiveNodeUpdates({ parentId: parentId, node: child @@ -147,7 +145,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', } }; - if(params.node.isNew) { + if (params.node.isNew) { TemplatesService.addWorkflowNode({ url: $scope.treeData.workflow_job_template_obj.related.workflow_nodes, @@ -155,7 +153,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', }) .then(function(data) { - if(!params.node.isRoot) { + if (!params.node.isRoot) { associateRequests.push({ parentId: params.parentId, nodeId: data.data.id, @@ -163,7 +161,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', }); } - if(_.get(params, 'node.promptData.launchConf.ask_credential_on_launch')){ + if (_.get(params, 'node.promptData.launchConf.ask_credential_on_launch')){ // This finds the credentials that were selected in the prompt but don't occur // in the template defaults let credentialsToPost = params.node.promptData.prompts.credentials.value.filter(function(credFromPrompt) { @@ -193,18 +191,17 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', error.status }); }); - } - else { - if(params.node.edited || !params.node.originalParentId || (params.node.originalParentId && params.parentId !== params.node.originalParentId)) { + } else { + if (params.node.edited || !params.node.originalParentId || (params.node.originalParentId && params.parentId !== params.node.originalParentId)) { - if(params.node.edited) { + if (params.node.edited) { editRequests.push({ id: params.node.nodeId, data: buildSendableNodeData() }); - if(_.get(params, 'node.promptData.launchConf.ask_credential_on_launch')){ + if (_.get(params, 'node.promptData.launchConf.ask_credential_on_launch')){ let credentialsNotInPriorCredentials = params.node.promptData.prompts.credentials.value.filter(function(credFromPrompt) { let defaultCreds = params.node.promptData.launchConf.defaults.credentials ? params.node.promptData.launchConf.defaults.credentials : []; return !defaultCreds.some(function(defaultCred) { @@ -243,20 +240,19 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', }); }); } - } - if((params.node.originalParentId && params.parentId !== params.node.originalParentId) || params.node.originalEdge !== params.node.edgeType) {//beep + if ((params.node.originalParentId && params.parentId !== params.node.originalParentId) || params.node.originalEdge !== params.node.edgeType) {//beep let parentIsDeleted = false; _.forEach($scope.treeData.data.deletedNodes, function(deletedNode) { - if(deletedNode === params.node.originalParentId) { + if (deletedNode === params.node.originalParentId) { parentIsDeleted = true; } }); - if(!parentIsDeleted) { + if (!parentIsDeleted) { disassociateRequests.push({ parentId: params.node.originalParentId, nodeId: params.node.nodeId, @@ -267,7 +263,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', // Can only associate if we have a parent. // If we don't have a parent then this is a root node // and the act of disassociating will make it a root node - if(params.parentId) { + if (params.parentId) { associateRequests.push({ parentId: params.parentId, nodeId: params.node.nodeId, @@ -275,8 +271,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', }); } - } - else if(!params.node.originalParentId && params.parentId) { + } else if (!params.node.originalParentId && params.parentId) { // This used to be a root node but is now not a root node associateRequests.push({ parentId: params.parentId, @@ -293,7 +288,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', let updateEdgeDropdownOptions = (optionsToInclude) => { // Not passing optionsToInclude will include all by default - if(!optionsToInclude) { + if (!optionsToInclude) { $scope.edgeTypeOptions = [ { label: 'Always', @@ -312,17 +307,17 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.edgeTypeOptions = []; optionsToInclude.forEach((optionToInclude) => { - if(optionToInclude === "always") { + if (optionToInclude === "always") { $scope.edgeTypeOptions.push({ label: 'Always', value: 'always' }); - } else if(optionToInclude === "success") { + } else if (optionToInclude === "success") { $scope.edgeTypeOptions.push({ label: 'On Success', value: 'success' }); - } else if(optionToInclude === "failure") { + } else if (optionToInclude === "failure") { $scope.edgeTypeOptions.push({ label: 'On Failure', value: 'failure' @@ -346,9 +341,9 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', promptWatcher = $scope.$watchGroup(promptDataToWatch, function() { let missingPromptValue = false; - if($scope.missingSurveyValue) { + if ($scope.missingSurveyValue) { missingPromptValue = true; - } else if(!$scope.promptData.prompts.inventory.value || !$scope.promptData.prompts.inventory.value.id) { + } else if (!$scope.promptData.prompts.inventory.value || !$scope.promptData.prompts.inventory.value.id) { missingPromptValue = true; } $scope.promptModalMissingReqFields = missingPromptValue; @@ -365,7 +360,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.totalIteratedNodes = 0; - if($scope.treeData && $scope.treeData.data && $scope.treeData.data.children && $scope.treeData.data.children.length > 0) { + if ($scope.treeData && $scope.treeData.data && $scope.treeData.data.children && $scope.treeData.data.children.length > 0) { let completionCallback = function() { let disassociatePromises = disassociateRequests.map(function(request) { @@ -376,13 +371,6 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', }); }); - let credentialPromises = credentialRequests.map(function(request) { - return TemplatesService.postWorkflowNodeCredential({ - id: request.id, - data: request.data - }); - }); - let editNodePromises = editRequests.map(function(request) { return TemplatesService.editWorkflowNode({ id: request.id, @@ -394,9 +382,16 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', return TemplatesService.deleteWorkflowJobTemplateNode(nodeId); }); - $q.all(disassociatePromises.concat(editNodePromises, deletePromises, credentialPromises)) + $q.all(disassociatePromises.concat(editNodePromises, deletePromises)) .then(function() { + let credentialPromises = credentialRequests.map(function(request) { + return TemplatesService.postWorkflowNodeCredential({ + id: request.id, + data: request.data + }); + }); + let associatePromises = associateRequests.map(function(request) { return TemplatesService.associateWorkflowNode({ parentId: request.parentId, @@ -405,7 +400,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', }); }); - $q.all(associatePromises) + $q.all(associatePromises.concat(credentialPromises)) .then(function() { $scope.closeDialog(); }); @@ -417,8 +412,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', node: child }, completionCallback); }); - } - else { + } else { let deletePromises = $scope.treeData.data.deletedNodes.map(function(nodeId) { return TemplatesService.deleteWorkflowJobTemplateNode(nodeId); @@ -522,11 +516,11 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', } } - if(promptWatcher) { + if (promptWatcher) { promptWatcher(); } - if(surveyQuestionWatcher) { + if (surveyQuestionWatcher) { surveyQuestionWatcher(); } @@ -549,11 +543,11 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.nodeBeingEdited.isActiveEdit = false; } - if(promptWatcher) { + if (promptWatcher) { promptWatcher(); } - if(surveyQuestionWatcher) { + if (surveyQuestionWatcher) { surveyQuestionWatcher(); } @@ -601,12 +595,12 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', let jobTemplate = new JobTemplate(); - if(!_.isEmpty($scope.nodeBeingEdited.promptData)) { + if (!_.isEmpty($scope.nodeBeingEdited.promptData)) { $scope.promptData = _.cloneDeep($scope.nodeBeingEdited.promptData); - } else if($scope.nodeBeingEdited.unifiedJobTemplate){ + } else if ($scope.nodeBeingEdited.unifiedJobTemplate){ let promises = [jobTemplate.optionsLaunch($scope.nodeBeingEdited.unifiedJobTemplate.id), jobTemplate.getLaunch($scope.nodeBeingEdited.unifiedJobTemplate.id)]; - if(_.has($scope, 'nodeBeingEdited.originalNodeObj.related.credentials')) { + if (_.has($scope, 'nodeBeingEdited.originalNodeObj.related.credentials')) { Rest.setUrl($scope.nodeBeingEdited.originalNodeObj.related.credentials); promises.push(Rest.get()); } @@ -630,8 +624,8 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', const credentialHasScheduleOverride = (templateDefaultCred) => { let credentialHasOverride = false; workflowNodeCredentials.forEach((scheduleCred) => { - if(templateDefaultCred.credential_type === scheduleCred.credential_type) { - if( + if (templateDefaultCred.credential_type === scheduleCred.credential_type) { + if ( (!templateDefaultCred.vault_id && !scheduleCred.inputs.vault_id) || (templateDefaultCred.vault_id && scheduleCred.inputs.vault_id && templateDefaultCred.vault_id === scheduleCred.inputs.vault_id) ) { @@ -643,9 +637,9 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', return credentialHasOverride; }; - if(_.has(launchConf, 'defaults.credentials')) { + if (_.has(launchConf, 'defaults.credentials')) { launchConf.defaults.credentials.forEach((defaultCred) => { - if(!credentialHasScheduleOverride(defaultCred)) { + if (!credentialHasScheduleOverride(defaultCred)) { defaultCredsWithoutOverrides.push(defaultCred); } }); @@ -653,7 +647,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', prompts.credentials.value = workflowNodeCredentials.concat(defaultCredsWithoutOverrides); - if(!launchConf.survey_enabled && + if (!launchConf.survey_enabled && !launchConf.ask_inventory_on_launch && !launchConf.ask_credential_on_launch && !launchConf.ask_verbosity_on_launch && @@ -671,11 +665,11 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', } else { $scope.showPromptButton = true; - if(launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'nodeBeingEdited.originalNodeObj.summary_fields.inventory')) { + if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'nodeBeingEdited.originalNodeObj.summary_fields.inventory')) { $scope.promptModalMissingReqFields = true; } - if(responses[1].data.survey_enabled) { + if (responses[1].data.survey_enabled) { // go out and get the survey questions jobTemplate.getSurveyQuestions($scope.nodeBeingEdited.unifiedJobTemplate.id) .then((surveyQuestionRes) => { @@ -700,7 +694,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', surveyQuestionWatcher = $scope.$watch('promptData.surveyQuestions', () => { let missingSurveyValue = false; _.each($scope.promptData.surveyQuestions, (question) => { - if(question.required && (Empty(question.model) || question.model === [])) { + if (question.required && (Empty(question.model) || question.model === [])) { missingSurveyValue = true; } }); @@ -709,8 +703,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', watchForPromptChanges(); }); - } - else { + } else { $scope.nodeBeingEdited.promptData = $scope.promptData = { launchConf: launchConf, launchOptions: launchOptions, @@ -729,7 +722,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.selectedTemplate = $scope.nodeBeingEdited.unifiedJobTemplate; - if($scope.selectedTemplate.unified_job_type) { + if ($scope.selectedTemplate.unified_job_type) { switch ($scope.selectedTemplate.unified_job_type) { case "job": $scope.workflowMakerFormConfig.activeTab = "jobs"; @@ -741,8 +734,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.workflowMakerFormConfig.activeTab = "inventory_sync"; break; } - } - else if($scope.selectedTemplate.type) { + } else if ($scope.selectedTemplate.type) { switch ($scope.selectedTemplate.type) { case "job_template": $scope.workflowMakerFormConfig.activeTab = "jobs"; @@ -767,19 +759,19 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', switch($scope.nodeBeingEdited.edgeType) { case "always": $scope.edgeType = {label: "Always", value: "always"}; - if(siblingConnectionTypes.length === 1 && _.includes(siblingConnectionTypes, "always")) { + if (siblingConnectionTypes.length === 1 && _.includes(siblingConnectionTypes, "always")) { edgeDropdownOptions = ["always"]; } break; case "success": $scope.edgeType = {label: "On Success", value: "success"}; - if(siblingConnectionTypes.length !== 0 && (!_.includes(siblingConnectionTypes, "always"))) { + if (siblingConnectionTypes.length !== 0 && (!_.includes(siblingConnectionTypes, "always"))) { edgeDropdownOptions = ["success", "failure"]; } break; case "failure": $scope.edgeType = {label: "On Failure", value: "failure"}; - if(siblingConnectionTypes.length !== 0 && (!_.includes(siblingConnectionTypes, "always"))) { + if (siblingConnectionTypes.length !== 0 && (!_.includes(siblingConnectionTypes, "always"))) { edgeDropdownOptions = ["success", "failure"]; } break; @@ -857,13 +849,12 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.$broadcast("refreshWorkflowChart"); - if($scope.placeholderNode) { + if ($scope.placeholderNode) { let edgeType = {label: "On Success", value: "success"}; - if($scope.placeholderNode.isRoot) { + if ($scope.placeholderNode.isRoot) { updateEdgeDropdownOptions(["always"]); edgeType = {label: "Always", value: "always"}; - } - else { + } else { // we need to update the possible edges based on any new siblings let siblingConnectionTypes = WorkflowService.getSiblingConnectionTypes({ tree: $scope.treeData.data, @@ -889,8 +880,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', } $scope.edgeType = edgeType; - } - else if($scope.nodeBeingEdited) { + } else if ($scope.nodeBeingEdited) { let siblingConnectionTypes = WorkflowService.getSiblingConnectionTypes({ tree: $scope.treeData.data, parentId: $scope.nodeBeingEdited.parent.id, @@ -958,14 +948,14 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.selectedTemplate = angular.copy(selectedTemplate); - if(selectedTemplate.type === "job_template") { + if (selectedTemplate.type === "job_template") { let jobTemplate = new JobTemplate(); $q.all([jobTemplate.optionsLaunch(selectedTemplate.id), jobTemplate.getLaunch(selectedTemplate.id)]) .then((responses) => { let launchConf = responses[1].data; - if(!launchConf.survey_enabled && + if (!launchConf.survey_enabled && !launchConf.ask_inventory_on_launch && !launchConf.ask_credential_on_launch && !launchConf.ask_verbosity_on_launch && @@ -983,11 +973,11 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', } else { $scope.showPromptButton = true; - if(launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory')) { + if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory')) { $scope.promptModalMissingReqFields = true; } - if(launchConf.survey_enabled) { + if (launchConf.survey_enabled) { // go out and get the survey questions jobTemplate.getSurveyQuestions(selectedTemplate.id) .then((surveyQuestionRes) => { @@ -1012,7 +1002,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', surveyQuestionWatcher = $scope.$watch('promptData.surveyQuestions', () => { let missingSurveyValue = false; _.each($scope.promptData.surveyQuestions, (question) => { - if(question.required && (Empty(question.model) || question.model === [])) { + if (question.required && (Empty(question.model) || question.model === [])) { missingSurveyValue = true; } }); @@ -1021,8 +1011,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', watchForPromptChanges(); }); - } - else { + } else { $scope.promptData = { launchConf: responses[1].data, launchOptions: responses[0].data, @@ -1098,7 +1087,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', // TODO: I think that the workflow chart directive (and eventually d3) is meddling with // this treeData object and removing the children object for some reason (?) // This happens on occasion and I think is a race condition (?) - if(!$scope.treeData.data.children) { + if (!$scope.treeData.data.children) { $scope.treeData.data.children = []; } @@ -1116,12 +1105,11 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', for(var i=0; i