1
0
mirror of https://github.com/ansible/awx.git synced 2024-11-02 01:21:21 +03:00

Prevent user from selecting an invalid JT when adding/editing a wfjt node

This commit is contained in:
mabashian 2018-04-18 18:12:48 -04:00
parent cbe3bc3f2a
commit a918539e23
8 changed files with 66 additions and 229 deletions

View File

@ -89,6 +89,10 @@ function TemplatesStrings (BaseString) {
ns.warnings = {
WORKFLOW_RESTRICTED_COPY: t.s('You do not have access to all resources used by this workflow. Resources that you don\'t have access to will not be copied and will result in an incomplete workflow.')
};
ns.workflows = {
INVALID_JOB_TEMPLATE: t.s('This Job Template is missing a default inventory or project. This must be addressed in the Job Template form before this node can be saved.')
};
}
TemplatesStrings.$inject = ['BaseStringService'];

View File

@ -18,7 +18,6 @@ import workflowService from './workflows/workflow.service';
import WorkflowForm from './workflows.form';
import InventorySourcesList from './inventory-sources.list';
import TemplateList from './templates.list';
import TemplatesStrings from './templates.strings';
import listRoute from '~features/templates/routes/templatesList.route.js';
import templateCompletedJobsRoute from '~features/jobs/routes/templateCompletedJobs.route.js';
@ -32,7 +31,6 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
// TODO: currently being kept arround for rbac selection, templates within projects and orgs, etc.
.factory('TemplateList', TemplateList)
.value('InventorySourcesList', InventorySourcesList)
.service('TemplatesStrings', TemplatesStrings)
.config(['$stateProvider', 'stateDefinitionsProvider', '$stateExtenderProvider',
function($stateProvider, stateDefinitionsProvider, $stateExtenderProvider) {
let stateTree, addJobTemplate, editJobTemplate, addWorkflow, editWorkflow,

View File

@ -1,7 +0,0 @@
function TemplatesStrings (BaseString) {
BaseString.call(this, 'templates');
}
TemplatesStrings.$inject = ['BaseStringService'];
export default TemplatesStrings;

View File

@ -1,11 +1,9 @@
import workflowMaker from './workflow-maker.directive';
import WorkflowMakerController from './workflow-maker.controller';
import WorkflowMakerForm from './workflow-maker.form';
export default
angular.module('templates.workflowMaker', [])
// In order to test this controller I had to expose it at the module level
// like so. Is this correct? Is there a better pattern for doing this?
.controller('WorkflowMakerController', WorkflowMakerController)
.factory('WorkflowMakerForm', WorkflowMakerForm)
.directive('workflowMaker', workflowMaker);

View File

@ -275,6 +275,10 @@
height: 100%;
overflow: hidden;
}
.WorkflowMaker-invalidJobTemplateWarning {
margin-bottom: 5px;
color: @default-err;
}
.Key-list {
margin: 0;

View File

@ -5,15 +5,16 @@
*************************************************/
export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
'$state', 'ProcessErrors', 'CreateSelect2', 'WorkflowMakerForm', '$q', 'JobTemplateModel',
'Empty', 'PromptService', 'Rest',
function($scope, WorkflowService, GetBasePath, TemplatesService, $state,
ProcessErrors, CreateSelect2, WorkflowMakerForm, $q, JobTemplate,
Empty, PromptService, Rest) {
'$state', 'ProcessErrors', 'CreateSelect2', '$q', 'JobTemplateModel',
'Empty', 'PromptService', 'Rest', 'TemplatesStrings',
function($scope, WorkflowService, GetBasePath, TemplatesService,
$state, ProcessErrors, CreateSelect2, $q, JobTemplate,
Empty, PromptService, Rest, TemplatesStrings) {
let form = WorkflowMakerForm();
let promptWatcher, surveyQuestionWatcher;
$scope.strings = TemplatesStrings;
$scope.workflowMakerFormConfig = {
nodeMode: "idle",
activeTab: "jobs",
@ -184,7 +185,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
params.node.isNew = false;
continueRecursing(data.data.id);
}, function(error) {
ProcessErrors($scope, error.data, error.status, form, {
ProcessErrors($scope, error.data, error.status, null, {
hdr: 'Error!',
msg: 'Failed to add workflow node. ' +
'POST returned status: ' +
@ -403,7 +404,11 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
$q.all(associatePromises.concat(credentialPromises))
.then(function() {
$scope.closeDialog();
}).catch(({data, status}) => {
ProcessErrors($scope, data, status, null);
});
}).catch(({data, status}) => {
ProcessErrors($scope, data, status, null);
});
};
@ -552,6 +557,8 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
}
$scope.promptData = null;
$scope.selectedTemplateInvalid = false;
$scope.showPromptButton = false;
// Reset the edgeConflict flag
resetEdgeConflict();
@ -647,6 +654,12 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
prompts.credentials.value = workflowNodeCredentials.concat(defaultCredsWithoutOverrides);
if ((!$scope.nodeBeingEdited.unifiedJobTemplate.inventory && !launchConf.ask_inventory_on_launch) || !$scope.nodeBeingEdited.unifiedJobTemplate.project) {
$scope.selectedTemplateInvalid = true;
} else {
$scope.selectedTemplateInvalid = false;
}
if (!launchConf.survey_enabled &&
!launchConf.ask_inventory_on_launch &&
!launchConf.ask_credential_on_launch &&
@ -658,7 +671,6 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
!launchConf.ask_diff_mode_on_launch &&
!launchConf.survey_enabled &&
!launchConf.credential_needed_to_start &&
!launchConf.inventory_needed_to_start &&
launchConf.passwords_needed_to_start.length === 0 &&
launchConf.variables_needed_to_start.length === 0) {
$scope.showPromptButton = false;
@ -794,7 +806,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
$scope.nodeBeingEdited.unifiedJobTemplate = _.clone(data.data.results[0]);
finishConfiguringEdit();
}, function(error) {
ProcessErrors($scope, error.data, error.status, form, {
ProcessErrors($scope, error.data, error.status, null, {
hdr: 'Error!',
msg: 'Failed to get unified job template. GET returned ' +
'status: ' + error.status
@ -946,8 +958,6 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
$scope.templateManuallySelected = function(selectedTemplate) {
$scope.selectedTemplate = angular.copy(selectedTemplate);
if (selectedTemplate.type === "job_template") {
let jobTemplate = new JobTemplate();
@ -955,6 +965,14 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
.then((responses) => {
let launchConf = responses[1].data;
if ((!selectedTemplate.inventory && !launchConf.ask_inventory_on_launch) || !selectedTemplate.project) {
$scope.selectedTemplateInvalid = true;
} else {
$scope.selectedTemplateInvalid = false;
}
$scope.selectedTemplate = angular.copy(selectedTemplate);
if (!launchConf.survey_enabled &&
!launchConf.ask_inventory_on_launch &&
!launchConf.ask_credential_on_launch &&
@ -966,7 +984,6 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
!launchConf.ask_diff_mode_on_launch &&
!launchConf.survey_enabled &&
!launchConf.credential_needed_to_start &&
!launchConf.inventory_needed_to_start &&
launchConf.passwords_needed_to_start.length === 0 &&
launchConf.variables_needed_to_start.length === 0) {
$scope.showPromptButton = false;
@ -1028,6 +1045,8 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
});
} else {
// TODO - clear out prompt data?
$scope.selectedTemplate = angular.copy(selectedTemplate);
$scope.selectedTemplateInvalid = false;
$scope.showPromptButton = false;
}
};
@ -1114,7 +1133,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
buildTreeFromNodes();
}
}, function(error){
ProcessErrors($scope, error.data, error.status, form, {
ProcessErrors($scope, error.data, error.status, null, {
hdr: 'Error!',
msg: 'Failed to get workflow job template nodes. GET returned ' +
'status: ' + error.status

View File

@ -1,183 +0,0 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
/**
* @ngdoc function
* @name forms.function:JobTemplate
* @description This form is for adding/editing a Job Template
*/
export default ['NotificationsList', 'i18n', '$rootScope', function(NotificationsList, i18n, $rootScope) {
return function() {
var WorkflowMakerFormObject = {
addTitle: '',
editTitle: '',
name: 'workflow_maker',
basePath: 'job_templates',
tabs: false,
cancelButton: false,
showHeader: false,
fields: {
edgeType: {
label: i18n._('Type'),
type: 'radio_group',
ngShow: 'selectedTemplate && edgeFlags.showTypeOptions',
ngDisabled: '!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)',
options: [
{
label: i18n._('On Success'),
value: 'success',
ngShow: '!edgeFlags.typeRestriction || edgeFlags.typeRestriction === "successFailure"'
},
{
label: i18n._('On Failure'),
value: 'failure',
ngShow: '!edgeFlags.typeRestriction || edgeFlags.typeRestriction === "successFailure"'
},
{
label: i18n._('Always'),
value: 'always',
ngShow: '!edgeFlags.typeRestriction || edgeFlags.typeRestriction === "always"'
}
],
awRequiredWhen: {
reqExpression: 'edgeFlags.showTypeOptions'
}
},
credential: {
label: i18n._('Credential'),
type: 'lookup',
sourceModel: 'credential',
sourceField: 'name',
ngClick: 'lookUpCredential()',
requiredErrorMsg: i18n._("Please select a Credential."),
class: 'Form-formGroup--fullWidth',
awPopOver: "<p>" + i18n._("Select the credential you want the job to use when accessing the remote hosts. Choose the credential containing " +
" the username and SSH key or password that Ansible will need to log into the remote hosts.") + "</p>",
dataTitle: i18n._('Credential'),
dataPlacement: 'right',
dataContainer: "body",
ngShow: "selectedTemplate.ask_credential_on_launch",
ngDisabled: '!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)',
awRequiredWhen: {
reqExpression: 'selectedTemplate && selectedTemplate.ask_credential_on_launch'
}
},
inventory: {
label: i18n._('Inventory'),
type: 'lookup',
sourceModel: 'inventory',
sourceField: 'name',
list: 'OrganizationList',
basePath: 'organization',
ngClick: 'lookUpInventory()',
requiredErrorMsg: i18n._("Please select an Inventory."),
class: 'Form-formGroup--fullWidth',
awPopOver: "<p>" + i18n._("Select the inventory containing the hosts you want this job to manage.") + "</p>",
dataTitle: i18n._('Inventory'),
dataPlacement: 'right',
dataContainer: "body",
ngShow: "selectedTemplate.ask_inventory_on_launch",
ngDisabled: '!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)',
awRequiredWhen: {
reqExpression: 'selectedTemplate && selectedTemplate.ask_inventory_on_launch'
}
},
job_type: {
label: i18n._('Job Type'),
type: 'select',
ngOptions: 'type.label for type in job_type_options track by type.value',
"default": 0,
class: 'Form-formGroup--fullWidth',
awPopOver: "<p>" + i18n.sprintf(i18n._("When this template is submitted as a job, setting the type to %s will execute the playbook, running tasks " +
" on the selected hosts."), "<em>run</em>") + "</p> <p>" +
i18n.sprintf(i18n._("Setting the type to %s will not execute the playbook. Instead, %s will check playbook " +
" syntax, test environment setup and report problems."), "<em>check</em>", "<code>ansible</code>") + "</p> <p>" +
i18n.sprintf(i18n._("Setting the type to %s will execute the playbook and store any " +
" scanned facts for use with " + $rootScope.BRAND_NAME + "'s System Tracking feature."), "<em>scan</em>") + "</p>",
dataTitle: i18n._('Job Type'),
dataPlacement: 'right',
dataContainer: "body",
ngShow: "selectedTemplate.ask_job_type_on_launch",
ngDisabled: '!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)',
awRequiredWhen: {
reqExpression: 'selectedTemplate && selectedTemplate.ask_job_type_on_launch'
}
},
limit: {
label: i18n._('Limit'),
type: 'text',
class: 'Form-formGroup--fullWidth',
awPopOver: "<p>" + i18n.sprintf(i18n._("Provide a host pattern to further constrain the list of hosts that will be managed or affected by the playbook. " +
"Multiple patterns can be separated by %s %s or %s"), "&#59;", "&#58;", "&#44;") + "</p><p>" +
i18n.sprintf(i18n._("For more information and examples see " +
"%sthe Patterns topic at docs.ansible.com%s."), "<a href=\"http://docs.ansible.com/intro_patterns.html\" target=\"_blank\">", "</a>") + "</p>",
dataTitle: i18n._('Limit'),
dataPlacement: 'right',
dataContainer: "body",
ngShow: "selectedTemplate.ask_limit_on_launch",
ngDisabled: '!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
},
job_tags: {
label: i18n._('Job Tags'),
type: 'textarea',
rows: 5,
'elementClass': 'Form-textInput',
class: 'Form-formGroup--fullWidth',
awPopOver: "<p>" + i18n._("Provide a comma separated list of tags.") + "</p>\n" +
"<p>" + i18n._("Tags are useful when you have a large playbook, and you want to run a specific part of a play or task.") + "</p>" +
"<p>" + i18n._("Consult the Ansible documentation for further details on the usage of tags.") + "</p>",
dataTitle: i18n._("Job Tags"),
dataPlacement: "right",
dataContainer: "body",
ngShow: "selectedTemplate.ask_tags_on_launch",
ngDisabled: '!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
},
skip_tags: {
label: i18n._('Skip Tags'),
type: 'textarea',
rows: 5,
'elementClass': 'Form-textInput',
class: 'Form-formGroup--fullWidth',
awPopOver: "<p>" + i18n._("Provide a comma separated list of tags.") + "</p>\n" +
"<p>" + i18n._("Skip tags are useful when you have a large playbook, and you want to skip specific parts of a play or task.") + "</p>" +
"<p>" + i18n._("Consult the Ansible documentation for further details on the usage of tags.") + "</p>",
dataTitle: i18n._("Skip Tags"),
dataPlacement: "right",
dataContainer: "body",
ngShow: "selectedTemplate.ask_skip_tags_on_launch",
ngDisabled: '!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
}
},
buttons: {
cancel: {
ngClick: 'cancelNodeForm()',
ngShow: '(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
},
close: {
ngClick: 'cancelNodeForm()',
ngShow: '!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
},
select: {
ngClick: 'saveNodeForm()',
ngDisabled: "workflow_maker_form.$invalid || !selectedTemplate",
ngShow: '(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
}
}
};
var itm;
for (itm in WorkflowMakerFormObject.related) {
if (WorkflowMakerFormObject.related[itm].include === "NotificationsList") {
WorkflowMakerFormObject.related[itm] = NotificationsList;
WorkflowMakerFormObject.related[itm].generateList = true; // tell form generator to call list generator and inject a list
}
}
return WorkflowMakerFormObject;
};
}];

View File

@ -93,31 +93,35 @@
<div id="workflow-project-sync-list" ui-view="projectSyncList" ng-show="workflowMakerFormConfig.activeTab === 'project_sync'"></div>
<div id="workflow-inventory-sync-list" ui-view="inventorySyncList" ng-show="workflowMakerFormConfig.activeTab === 'inventory_sync'"></div>
</div>
<div ng-show="selectedTemplate">
<div class="form-group Form-formGroup Form-formGroup--singleColumn">
<label for="verbosity" class="Form-inputLabelContainer">
<span class="Form-requiredAsterisk">*</span>
<span class="Form-inputLabel">RUN</span>
</label>
<div>
<select
id="workflow_node_edge"
ng-options="v as v.label for v in edgeTypeOptions track by v.value"
ng-model="edgeType"
class="form-control Form-dropDown"
name="edgeType"
tabindex="-1"
aria-hidden="true">
</select>
</div>
<div ng-if="selectedTemplate && selectedTemplateInvalid">
<div class="WorkflowMaker-invalidJobTemplateWarning">
<span class="fa fa-warning"></span>
<span>{{:: strings.get('workflows.INVALID_JOB_TEMPLATE') }}</span>
</div>
<div class="buttons Form-buttons" id="workflow_maker_controls">
<button type="button" class="btn btn-sm Form-primaryButton Form-primaryButton--noMargin" id="workflow_maker_prompt_btn" ng-show="showPromptButton" ng-click="openPromptModal()"> Prompt</button>
<button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_cancel_btn" ng-show="(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)" ng-click="cancelNodeForm()"> Cancel</button>
<button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_close_btn" ng-show="!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)" ng-click="cancelNodeForm()"> Close</button>
<button type="button" class="btn btn-sm Form-saveButton" id="workflow_maker_select_btn" ng-show="(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)" ng-click="confirmNodeForm()" ng-disabled="workflow_maker_form.$invalid || !selectedTemplate || promptModalMissingReqFields" disabled="disabled"> Select</button>
</div>
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-show="selectedTemplate && !selectedTemplateInvalid">
<label for="verbosity" class="Form-inputLabelContainer">
<span class="Form-requiredAsterisk">*</span>
<span class="Form-inputLabel">RUN</span>
</label>
<div>
<select
id="workflow_node_edge"
ng-options="v as v.label for v in edgeTypeOptions track by v.value"
ng-model="edgeType"
class="form-control Form-dropDown"
name="edgeType"
tabindex="-1"
aria-hidden="true">
</select>
</div>
</div>
<div class="buttons Form-buttons" id="workflow_maker_controls">
<button type="button" class="btn btn-sm Form-primaryButton Form-primaryButton--noMargin" id="workflow_maker_prompt_btn" ng-show="showPromptButton" ng-click="openPromptModal()"> Prompt</button>
<button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_cancel_btn" ng-show="(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)" ng-click="cancelNodeForm()"> Cancel</button>
<button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_close_btn" ng-show="!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)" ng-click="cancelNodeForm()"> Close</button>
<button type="button" class="btn btn-sm Form-saveButton" id="workflow_maker_select_btn" ng-show="(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate) && !selectedTemplateInvalid" ng-click="confirmNodeForm()" ng-disabled="!selectedTemplate || promptModalMissingReqFields"> Select</button>
</div>
</div>
</div>
</div>