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 += "";
}
}
}
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