From 2e858790dbac7a86b93e55a20a7c0598b781fe95 Mon Sep 17 00:00:00 2001
From: mabashian
Date: Tue, 20 Mar 2018 15:53:12 -0400
Subject: [PATCH] Propagate launch/relaunch logic across the app. Removed some
old launch related factories.
---
.../templates/list-templates.controller.js | 135 ++++----
.../relaunchButton.component.js | 199 ++++++++++--
.../relaunchButton.partial.html | 3 +-
awx/ui/client/lib/models/AdHocCommand.js | 44 +++
awx/ui/client/lib/models/InventorySource.js | 28 +-
awx/ui/client/lib/models/Job.js | 31 +-
awx/ui/client/lib/models/Project.js | 26 +-
awx/ui/client/lib/models/WorkflowJob.js | 17 +-
.../client/lib/models/WorkflowJobTemplate.js | 68 +++-
awx/ui/client/lib/models/index.js | 2 +
.../job-templates-list.directive.js | 146 ++++++++-
.../job-templates-list.partial.html | 1 +
.../inventories/adhoc/adhoc.controller.js | 38 +--
.../completed-jobs/completed-jobs.list.js | 6 +-
.../src/job-results/job-results.controller.js | 4 -
.../src/job-results/job-results.route.js | 2 +-
.../src/job-results/job-results.service.js | 8 +-
.../inventory-update.factory.js | 15 +-
.../project-update.factory.js | 15 +-
awx/ui/client/src/jobs/all-jobs.list.js | 6 +-
.../factories/job-status-tool-tip.factory.js | 31 --
.../factories/jobs-list-update.factory.js | 49 ---
.../jobs/factories/relaunch-adhoc.factory.js | 11 -
.../factories/relaunch-inventory.factory.js | 26 --
.../jobs/factories/relaunch-job.factory.js | 28 --
.../factories/relaunch-playbook.factory.js | 12 -
.../jobs/factories/relaunch-scm.factory.js | 11 -
.../client/src/jobs/jobs-list.controller.js | 46 +--
awx/ui/client/src/jobs/main.js | 14 -
.../organizations-job-templates.controller.js | 40 +--
.../portal-mode-job-templates.controller.js | 78 ++++-
.../src/portal-mode/portal-mode.route.js | 2 +-
.../factories/schedule-post.factory.js | 60 +---
.../adhoc/standard-out-adhoc.partial.html | 4 +-
.../adhoc/standard-out-adhoc.route.js | 9 +
.../standard-out-inventory-sync.partial.html | 2 +-
.../standard-out-inventory-sync.route.js | 9 +
.../standard-out-management-jobs.route.js | 9 +
.../standard-out-scm-update.partial.html | 2 +-
.../standard-out-scm-update.route.js | 9 +
.../src/standard-out/standard-out.block.less | 4 +
.../standard-out/standard-out.controller.js | 298 +++++++++---------
.../src/templates/completed-jobs.list.js | 9 +-
.../job-template-edit.controller.js | 49 +--
.../src/templates/prompt/prompt.controller.js | 47 ++-
.../src/templates/prompt/prompt.service.js | 161 +++++++++-
.../preview/prompt-preview.controller.js | 2 +-
.../steps/preview/prompt-preview.partial.html | 8 +-
.../workflow-maker.controller.js | 79 ++---
.../workflow-results.service.js | 11 +-
.../job-results.controller-test.js | 13 -
.../workflow-results.controller-test.js | 2 +-
.../workflow-results.service-test.js | 2 +-
53 files changed, 1123 insertions(+), 808 deletions(-)
create mode 100644 awx/ui/client/lib/models/AdHocCommand.js
delete mode 100644 awx/ui/client/src/jobs/factories/job-status-tool-tip.factory.js
delete mode 100644 awx/ui/client/src/jobs/factories/jobs-list-update.factory.js
delete mode 100644 awx/ui/client/src/jobs/factories/relaunch-adhoc.factory.js
delete mode 100644 awx/ui/client/src/jobs/factories/relaunch-inventory.factory.js
delete mode 100644 awx/ui/client/src/jobs/factories/relaunch-job.factory.js
delete mode 100644 awx/ui/client/src/jobs/factories/relaunch-playbook.factory.js
delete mode 100644 awx/ui/client/src/jobs/factories/relaunch-scm.factory.js
diff --git a/awx/ui/client/features/templates/list-templates.controller.js b/awx/ui/client/features/templates/list-templates.controller.js
index f6f9123272..550a6fbe9c 100644
--- a/awx/ui/client/features/templates/list-templates.controller.js
+++ b/awx/ui/client/features/templates/list-templates.controller.js
@@ -16,14 +16,12 @@ function ListTemplatesController(
$state,
Alert,
Dataset,
- InitiatePlaybookRun,
ProcessErrors,
Prompt,
PromptService,
resolvedModels,
strings,
- Wait,
- Empty
+ Wait
) {
const vm = this || {};
const [jobTemplate, workflowTemplate] = resolvedModels;
@@ -42,7 +40,7 @@ function ListTemplatesController(
position: 'right',
arrowHeight: 15
}
- }
+ };
$scope.canAddJobTemplate = jobTemplate.options('actions.POST');
$scope.canAddWorkflowJobTemplate = workflowTemplate.options('actions.POST');
@@ -347,6 +345,7 @@ function ListTemplatesController(
launchConf: launchData.data,
launchOptions: launchOptions.data,
template: template.id,
+ templateType: template.type,
prompts: PromptService.processPromptValues({
launchConf: launchData.data,
launchOptions: launchOptions.data
@@ -368,79 +367,75 @@ function ListTemplatesController(
}
function runWorkflowTemplate(template) {
- InitiatePlaybookRun({ scope: $scope, id: template.id, job_type: 'workflow_job_template' });
+ const selectedWorkflowJobTemplate = workflowTemplate.create();
+ const preLaunchPromises = [
+ selectedWorkflowJobTemplate.getLaunch(template.id),
+ selectedWorkflowJobTemplate.optionsLaunch(template.id),
+ ];
+
+ Promise.all(preLaunchPromises)
+ .then(([launchData, launchOptions]) => {
+ if (selectedWorkflowJobTemplate.canLaunchWithoutPrompt()) {
+ return selectedWorkflowJobTemplate
+ .postLaunch({ id: template.id })
+ .then(({ data }) => {
+ $state.go('workflowResults', { id: data.workflow_job }, { reload: true });
+ });
+ }
+
+ const promptData = {
+ launchConf: launchData.data,
+ launchOptions: launchOptions.data,
+ template: template.id,
+ templateType: template.type,
+ prompts: PromptService.processPromptValues({
+ launchConf: launchData.data,
+ launchOptions: launchOptions.data
+ }),
+ triggerModalOpen: true,
+ };
+
+ if (launchData.data.survey_enabled) {
+ selectedWorkflowJobTemplate.getSurveyQuestions(template.id)
+ .then(({ data }) => {
+ const processed = PromptService.processSurveyQuestions({ surveyQuestions: data.spec });
+ promptData.surveyQuestions = processed.surveyQuestions;
+ $scope.promptData = promptData;
+ });
+ } else {
+ $scope.promptData = promptData;
+ }
+ });
}
$scope.launchJob = () => {
- const jobLaunchData = {
- extra_vars: $scope.promptData.extraVars
- };
-
- if ($scope.promptData.launchConf.ask_tags_on_launch){
- jobLaunchData.job_tags = $scope.promptData.prompts.tags.value.map(a => a.value).join();
- }
-
- if ($scope.promptData.launchConf.ask_skip_tags_on_launch){
- jobLaunchData.skip_tags = $scope.promptData.prompts.skipTags.value.map(a => a.value).join();
- }
-
- if ($scope.promptData.launchConf.ask_limit_on_launch && _.has($scope, 'promptData.prompts.limit.value')){
- jobLaunchData.limit = $scope.promptData.prompts.limit.value;
- }
-
- if ($scope.promptData.launchConf.ask_job_type_on_launch && _.has($scope, 'promptData.prompts.jobType.value.value')) {
- jobLaunchData.job_type = $scope.promptData.prompts.jobType.value.value;
- }
-
- if ($scope.promptData.launchConf.ask_verbosity_on_launch && _.has($scope, 'promptData.prompts.verbosity.value.value')) {
- jobLaunchData.verbosity = $scope.promptData.prompts.verbosity.value.value;
- }
-
- if ($scope.promptData.launchConf.ask_inventory_on_launch && !Empty($scope.promptData.prompts.inventory.value.id)){
- jobLaunchData.inventory_id = $scope.promptData.prompts.inventory.value.id;
- }
-
- if ($scope.promptData.launchConf.ask_credential_on_launch){
- jobLaunchData.credentials = [];
- $scope.promptData.prompts.credentials.value.forEach((credential) => {
- jobLaunchData.credentials.push(credential.id);
- });
- }
-
- if ($scope.promptData.launchConf.ask_diff_mode_on_launch && _.has($scope, 'promptData.prompts.diffMode.value')) {
- jobLaunchData.diff_mode = $scope.promptData.prompts.diffMode.value;
- }
-
- if ($scope.promptData.prompts.credentials.passwords) {
- _.forOwn($scope.promptData.prompts.credentials.passwords, (val, key) => {
- if (!jobLaunchData.credential_passwords) {
- jobLaunchData.credential_passwords = {};
- }
- if (key === "ssh_key_unlock") {
- jobLaunchData.credential_passwords.ssh_key_unlock = val.value;
- } else if (key !== "vault") {
- jobLaunchData.credential_passwords[`${key}_password`] = val.value;
- } else {
- _.each(val, (vaultCred) => {
- jobLaunchData.credential_passwords[vaultCred.vault_id ? `${key}_password.${vaultCred.vault_id}` : `${key}_password`] = vaultCred.value;
- });
- }
- });
- }
+ const jobLaunchData = PromptService.bundlePromptDataForLaunch($scope.promptData);
// If the extra_vars dict is empty, we don't want to include it if we didn't prompt for anything.
if(_.isEmpty(jobLaunchData.extra_vars) && !($scope.promptData.launchConf.ask_variables_on_launch && $scope.promptData.launchConf.survey_enabled && $scope.promptData.surveyQuestions.length > 0)){
delete jobLaunchData.extra_vars;
}
- jobTemplate.create().postLaunch({
- id: $scope.promptData.template,
- launchData: jobLaunchData
- })
- .then((launchRes) => {
- $state.go('jobResult', { id: launchRes.data.job }, { reload: true });
- })
- .catch(createErrorHandler('launch job template', 'POST'));
+ if($scope.promptData.templateType === 'job_template') {
+ jobTemplate.create().postLaunch({
+ id: $scope.promptData.template,
+ launchData: jobLaunchData
+ })
+ .then((launchRes) => {
+ $state.go('jobResult', { id: launchRes.data.job }, { reload: true });
+ })
+ .catch(createErrorHandler('launch job template', 'POST'));
+ } else if($scope.promptData.templateType === 'workflow_job_template') {
+ workflowTemplate.create().postLaunch({
+ id: $scope.promptData.template,
+ launchData: jobLaunchData
+ })
+ .then((launchRes) => {
+ $state.go('workflowResults', { id: launchRes.data.workflow_job }, { reload: true });
+ })
+ .catch(createErrorHandler('launch workflow job template', 'POST'));
+ }
+
};
}
@@ -450,14 +445,12 @@ ListTemplatesController.$inject = [
'$state',
'Alert',
'Dataset',
- 'InitiatePlaybookRun',
'ProcessErrors',
'Prompt',
'PromptService',
'resolvedModels',
'TemplatesStrings',
- 'Wait',
- 'Empty'
+ 'Wait'
];
export default ListTemplatesController;
diff --git a/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js b/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js
index 465404c10e..dc69ef47c4 100644
--- a/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js
+++ b/awx/ui/client/lib/components/relaunchButton/relaunchButton.component.js
@@ -5,17 +5,108 @@ const atRelaunch = {
bindings: {
state: '<'
},
- controller: ['RelaunchJob', 'InitiatePlaybookRun', 'ComponentsStrings', '$scope', atRelaunchCtrl],
+ controller: ['ProcessErrors', 'AdhocRun', 'ComponentsStrings',
+ 'ProjectModel', 'InventorySourceModel', 'WorkflowJobModel', 'Alert',
+ 'AdHocCommandModel', 'JobModel', 'JobTemplateModel', 'PromptService',
+ 'GetBasePath', '$state', '$q', '$scope', atRelaunchCtrl
+ ],
controllerAs: 'vm'
};
-function atRelaunchCtrl (RelaunchJob, InitiatePlaybookRun, strings, $scope) {
+function atRelaunchCtrl (
+ ProcessErrors, AdhocRun, strings,
+ Project, InventorySource, WorkflowJob, Alert,
+ AdHocCommand, Job, JobTemplate, PromptService,
+ GetBasePath, $state, $q, $scope
+) {
const vm = this;
const scope = $scope.$parent;
const job = _.get(scope, 'job') || _.get(scope, 'completed_job');
+ const jobObj = new Job();
+ const jobTemplate = new JobTemplate();
+
+ const checkRelaunchPlaybook = (option) => {
+ jobObj.getRelaunch({
+ id: job.id
+ }).then((getRelaunchRes) => {
+ if (
+ getRelaunchRes.data.passwords_needed_to_start &&
+ getRelaunchRes.data.passwords_needed_to_start.length > 0
+ ) {
+ const jobPromises = [
+ jobObj.request('get', job.id),
+ jobTemplate.optionsLaunch(job.unified_job_template)
+ ];
+
+ $q.all(jobPromises)
+ .then(([jobRes, launchOptions]) => {
+ const populatedJob = jobRes.data;
+ const jobTypeChoices = _.get(
+ launchOptions,
+ 'data.actions.POST.job_type.choices',
+ []
+ ).map(c => ({ label: c[1], value: c[0] }));
+ const verbosityChoices = _.get(
+ launchOptions,
+ 'data.actions.POST.verbosity.choices',
+ []
+ ).map(c => ({ label: c[1], value: c[0] }));
+
+ vm.promptData = {
+ launchConf: {
+ passwords_needed_to_start: getRelaunchRes.data.passwords_needed_to_start
+ },
+ launchOptions: launchOptions.data,
+ job: job.id,
+ relaunchHostType: option ? (option.name).toLowerCase() : null,
+ prompts: {
+ credentials: {
+ value: populatedJob.summary_fields.credentials ? populatedJob.summary_fields.credentials : []
+ },
+ variables: {
+ value: populatedJob.extra_vars
+ },
+ inventory: {
+ value: populatedJob.summary_fields.inventory ? populatedJob.summary_fields.inventory : null
+ },
+ verbosity: {
+ value: _.find(verbosityChoices, item => item.value === populatedJob.verbosity),
+ choices: verbosityChoices
+ },
+ jobType: {
+ value: _.find(jobTypeChoices, item => item.value === populatedJob.job_type),
+ choices: jobTypeChoices
+ },
+ limit: {
+ value: populatedJob.limit
+ },
+ tags: {
+ value: populatedJob.job_tags
+ },
+ skipTags: {
+ value: populatedJob.skip_tags
+ },
+ diffMode: {
+ value: populatedJob.diff_mode
+ }
+ },
+ triggerModalOpen: true
+ };
+ });
+ } else {
+ jobObj.postRelaunch({
+ id: job.id
+ }).then((launchRes) => {
+ if (!$state.includes('jobs')) {
+ $state.go('jobResult', { id: launchRes.data.id }, { reload: true });
+ }
+ });
+ }
+ });
+ };
vm.$onInit = () => {
- vm.showRelaunch = !(job.type === 'system_job') && job.summary_fields.user_capabilities.start;
+ vm.showRelaunch = job.type !== 'system_job' && job.summary_fields.user_capabilities.start;
vm.showDropdown = job.type === 'job' && job.failed === true;
vm.createDropdown();
@@ -46,27 +137,97 @@ function atRelaunchCtrl (RelaunchJob, InitiatePlaybookRun, strings, $scope) {
};
vm.relaunchJob = () => {
- let typeId;
-
if (job.type === 'inventory_update') {
- typeId = job.inventory_source;
- } else if (job.type === 'project_update') {
- typeId = job.project;
- } else if (job.type === 'job' || job.type === 'system_job'
- || job.type === 'ad_hoc_command' || job.type === 'workflow_job') {
- typeId = job.id;
- }
+ const inventorySource = new InventorySource();
- RelaunchJob({ scope, id: typeId, type: job.type, name: job.name });
+ inventorySource.getUpdate(job.inventory_source)
+ .then((getUpdateRes) => {
+ if (getUpdateRes.data.can_update) {
+ inventorySource.postUpdate(job.inventory_source)
+ .then((postUpdateRes) => {
+ if (!$state.includes('jobs')) {
+ $state.go('inventorySyncStdout', { id: postUpdateRes.data.id }, { reload: true });
+ }
+ });
+ } else {
+ Alert(
+ 'Permission Denied', 'You do not have permission to sync this inventory source. Please contact your system administrator.',
+ 'alert-danger'
+ );
+ }
+ });
+ } else if (job.type === 'project_update') {
+ const project = new Project();
+
+ project.getUpdate(job.project)
+ .then((getUpdateRes) => {
+ if (getUpdateRes.data.can_update) {
+ project.postUpdate(job.project)
+ .then((postUpdateRes) => {
+ if (!$state.includes('jobs')) {
+ $state.go('scmUpdateStdout', { id: postUpdateRes.data.id }, { reload: true });
+ }
+ });
+ } else {
+ Alert(
+ 'Permission Denied', 'You do not have access to update this project. Please contact your system administrator.',
+ 'alert-danger'
+ );
+ }
+ });
+ } else if (job.type === 'workflow_job') {
+ const workflowJob = new WorkflowJob();
+
+ workflowJob.postRelaunch({
+ id: job.id
+ }).then((launchRes) => {
+ if (!$state.includes('jobs')) {
+ $state.go('workflowResults', { id: launchRes.data.id }, { reload: true });
+ }
+ });
+ } else if (job.type === 'ad_hoc_command') {
+ const adHocCommand = new AdHocCommand();
+
+ adHocCommand.getRelaunch({
+ id: job.id
+ }).then((getRelaunchRes) => {
+ if (
+ getRelaunchRes.data.passwords_needed_to_start &&
+ getRelaunchRes.data.passwords_needed_to_start.length > 0
+ ) {
+ AdhocRun({ scope, project_id: job.id, relaunch: true });
+ } else {
+ adHocCommand.postRelaunch({
+ id: job.id
+ }).then((launchRes) => {
+ if (!$state.includes('jobs')) {
+ $state.go('adHocJobStdout', { id: launchRes.data.id }, { reload: true });
+ }
+ });
+ }
+ });
+ } else if (job.type === 'job') {
+ checkRelaunchPlaybook();
+ }
};
vm.relaunchOn = (option) => {
- InitiatePlaybookRun({
- scope,
- id: job.id,
- relaunch: true,
- job_type: job.type,
- host_type: (option.name).toLowerCase()
+ checkRelaunchPlaybook(option);
+ };
+
+ vm.relaunchJobWithPassword = () => {
+ jobObj.postRelaunch({
+ id: vm.promptData.job,
+ relaunchData: PromptService.bundlePromptDataForRelaunch(vm.promptData)
+ }).then((launchRes) => {
+ if (!$state.includes('jobs')) {
+ $state.go('jobResult', { id: launchRes.data.job }, { reload: true });
+ }
+ }).catch(({ data, status }) => {
+ ProcessErrors($scope, data, status, null, {
+ hdr: 'Error!',
+ msg: `Error relaunching job. POST returned status: ${status}`
+ });
});
};
}
diff --git a/awx/ui/client/lib/components/relaunchButton/relaunchButton.partial.html b/awx/ui/client/lib/components/relaunchButton/relaunchButton.partial.html
index 5ce4c45c17..69b3eea711 100644
--- a/awx/ui/client/lib/components/relaunchButton/relaunchButton.partial.html
+++ b/awx/ui/client/lib/components/relaunchButton/relaunchButton.partial.html
@@ -30,4 +30,5 @@
ng-if="!vm.showDropdown">
-
\ No newline at end of file
+
+
diff --git a/awx/ui/client/lib/models/AdHocCommand.js b/awx/ui/client/lib/models/AdHocCommand.js
new file mode 100644
index 0000000000..c398219531
--- /dev/null
+++ b/awx/ui/client/lib/models/AdHocCommand.js
@@ -0,0 +1,44 @@
+let Base;
+let $http;
+
+function getRelaunch (params) {
+ const req = {
+ method: 'GET',
+ url: `${this.path}${params.id}/relaunch/`
+ };
+
+ return $http(req);
+}
+
+function postRelaunch (params) {
+ const req = {
+ method: 'POST',
+ url: `${this.path}${params.id}/relaunch/`
+ };
+
+ return $http(req);
+}
+
+function AdHocCommandModel (method, resource, config) {
+ Base.call(this, 'ad_hoc_commands');
+
+ this.Constructor = AdHocCommandModel;
+ this.postRelaunch = postRelaunch.bind(this);
+ this.getRelaunch = getRelaunch.bind(this);
+
+ return this.create(method, resource, config);
+}
+
+function AdHocCommandModelLoader (BaseModel, _$http_) {
+ Base = BaseModel;
+ $http = _$http_;
+
+ return AdHocCommandModel;
+}
+
+AdHocCommandModelLoader.$inject = [
+ 'BaseModel',
+ '$http'
+];
+
+export default AdHocCommandModelLoader;
diff --git a/awx/ui/client/lib/models/InventorySource.js b/awx/ui/client/lib/models/InventorySource.js
index 27ec05b6e9..0d68a11780 100644
--- a/awx/ui/client/lib/models/InventorySource.js
+++ b/awx/ui/client/lib/models/InventorySource.js
@@ -1,5 +1,6 @@
let Base;
let WorkflowJobTemplateNode;
+let $http;
function setDependentResources (id) {
this.dependentResources = [
@@ -12,28 +13,51 @@ function setDependentResources (id) {
];
}
+function getUpdate (id) {
+ const req = {
+ method: 'GET',
+ url: `${this.path}${id}/update/`
+ };
+
+ return $http(req);
+}
+
+function postUpdate (id) {
+ const req = {
+ method: 'POST',
+ url: `${this.path}${id}/update/`
+ };
+
+ return $http(req);
+}
+
function InventorySourceModel (method, resource, config) {
Base.call(this, 'inventory_sources');
this.Constructor = InventorySourceModel;
this.setDependentResources = setDependentResources.bind(this);
+ this.getUpdate = getUpdate.bind(this);
+ this.postUpdate = postUpdate.bind(this);
return this.create(method, resource, config);
}
function InventorySourceModelLoader (
BaseModel,
- WorkflowJobTemplateNodeModel
+ WorkflowJobTemplateNodeModel,
+ _$http_
) {
Base = BaseModel;
WorkflowJobTemplateNode = WorkflowJobTemplateNodeModel;
+ $http = _$http_;
return InventorySourceModel;
}
InventorySourceModelLoader.$inject = [
'BaseModel',
- 'WorkflowJobTemplateNodeModel'
+ 'WorkflowJobTemplateNodeModel',
+ '$http'
];
export default InventorySourceModelLoader;
diff --git a/awx/ui/client/lib/models/Job.js b/awx/ui/client/lib/models/Job.js
index 9be420b2f9..7d87f82330 100644
--- a/awx/ui/client/lib/models/Job.js
+++ b/awx/ui/client/lib/models/Job.js
@@ -1,21 +1,48 @@
let Base;
+let $http;
+
+function getRelaunch (params) {
+ const req = {
+ method: 'GET',
+ url: `${this.path}${params.id}/relaunch/`
+ };
+
+ return $http(req);
+}
+
+function postRelaunch (params) {
+ const req = {
+ method: 'POST',
+ url: `${this.path}${params.id}/relaunch/`
+ };
+
+ if (params.relaunchData) {
+ req.data = params.relaunchData;
+ }
+
+ return $http(req);
+}
function JobModel (method, resource, config) {
Base.call(this, 'jobs');
this.Constructor = JobModel;
+ this.postRelaunch = postRelaunch.bind(this);
+ this.getRelaunch = getRelaunch.bind(this);
return this.create(method, resource, config);
}
-function JobModelLoader (BaseModel) {
+function JobModelLoader (BaseModel, _$http_) {
Base = BaseModel;
+ $http = _$http_;
return JobModel;
}
JobModelLoader.$inject = [
- 'BaseModel'
+ 'BaseModel',
+ '$http'
];
export default JobModelLoader;
diff --git a/awx/ui/client/lib/models/Project.js b/awx/ui/client/lib/models/Project.js
index bb08c09179..02606c3dcd 100644
--- a/awx/ui/client/lib/models/Project.js
+++ b/awx/ui/client/lib/models/Project.js
@@ -2,6 +2,7 @@ let Base;
let JobTemplate;
let WorkflowJobTemplateNode;
let InventorySource;
+let $http;
function setDependentResources (id) {
this.dependentResources = [
@@ -26,11 +27,31 @@ function setDependentResources (id) {
];
}
+function getUpdate (id) {
+ const req = {
+ method: 'GET',
+ url: `${this.path}${id}/update/`
+ };
+
+ return $http(req);
+}
+
+function postUpdate (id) {
+ const req = {
+ method: 'POST',
+ url: `${this.path}${id}/update/`
+ };
+
+ return $http(req);
+}
+
function ProjectModel (method, resource, config) {
Base.call(this, 'projects');
this.Constructor = ProjectModel;
this.setDependentResources = setDependentResources.bind(this);
+ this.getUpdate = getUpdate.bind(this);
+ this.postUpdate = postUpdate.bind(this);
return this.create(method, resource, config);
}
@@ -40,11 +61,13 @@ function ProjectModelLoader (
JobTemplateModel,
WorkflowJobTemplateNodeModel,
InventorySourceModel,
+ _$http_
) {
Base = BaseModel;
JobTemplate = JobTemplateModel;
WorkflowJobTemplateNode = WorkflowJobTemplateNodeModel;
InventorySource = InventorySourceModel;
+ $http = _$http_;
return ProjectModel;
}
@@ -53,7 +76,8 @@ ProjectModelLoader.$inject = [
'BaseModel',
'JobTemplateModel',
'WorkflowJobTemplateNodeModel',
- 'InventorySourceModel'
+ 'InventorySourceModel',
+ '$http'
];
export default ProjectModelLoader;
diff --git a/awx/ui/client/lib/models/WorkflowJob.js b/awx/ui/client/lib/models/WorkflowJob.js
index 06ed451883..02ea3582cc 100644
--- a/awx/ui/client/lib/models/WorkflowJob.js
+++ b/awx/ui/client/lib/models/WorkflowJob.js
@@ -1,21 +1,34 @@
let Base;
+let $http;
+
+function postRelaunch (params) {
+ const req = {
+ method: 'POST',
+ url: `${this.path}${params.id}/relaunch/`
+ };
+
+ return $http(req);
+}
function WorkflowJobModel (method, resource, config) {
Base.call(this, 'workflow_jobs');
this.Constructor = WorkflowJobModel;
+ this.postRelaunch = postRelaunch.bind(this);
return this.create(method, resource, config);
}
-function WorkflowJobModelLoader (BaseModel) {
+function WorkflowJobModelLoader (BaseModel, _$http_) {
Base = BaseModel;
+ $http = _$http_;
return WorkflowJobModel;
}
WorkflowJobModelLoader.$inject = [
- 'BaseModel'
+ 'BaseModel',
+ '$http'
];
export default WorkflowJobModelLoader;
diff --git a/awx/ui/client/lib/models/WorkflowJobTemplate.js b/awx/ui/client/lib/models/WorkflowJobTemplate.js
index d0ff06fc78..79198bbe82 100644
--- a/awx/ui/client/lib/models/WorkflowJobTemplate.js
+++ b/awx/ui/client/lib/models/WorkflowJobTemplate.js
@@ -1,21 +1,85 @@
let Base;
+let $http;
+
+function optionsLaunch (id) {
+ const req = {
+ method: 'OPTIONS',
+ url: `${this.path}${id}/launch/`
+ };
+
+ return $http(req);
+}
+
+function getLaunch (id) {
+ const req = {
+ method: 'GET',
+ url: `${this.path}${id}/launch/`
+ };
+
+ return $http(req)
+ .then(res => {
+ this.model.launch.GET = res.data;
+
+ return res;
+ });
+}
+
+function postLaunch (params) {
+ const req = {
+ method: 'POST',
+ url: `${this.path}${params.id}/launch/`
+ };
+
+ if (params.launchData) {
+ req.data = params.launchData;
+ }
+
+ return $http(req);
+}
+
+function getSurveyQuestions (id) {
+ const req = {
+ method: 'GET',
+ url: `${this.path}${id}/survey_spec/`
+ };
+
+ return $http(req);
+}
+
+function canLaunchWithoutPrompt () {
+ const launchData = this.model.launch.GET;
+
+ return (
+ launchData.can_start_without_user_input &&
+ !launchData.survey_enabled
+ );
+}
function WorkflowJobTemplateModel (method, resource, config) {
Base.call(this, 'workflow_job_templates');
this.Constructor = WorkflowJobTemplateModel;
+ this.optionsLaunch = optionsLaunch.bind(this);
+ this.getLaunch = getLaunch.bind(this);
+ this.postLaunch = postLaunch.bind(this);
+ this.getSurveyQuestions = getSurveyQuestions.bind(this);
+ this.canLaunchWithoutPrompt = canLaunchWithoutPrompt.bind(this);
+
+ this.model.launch = {};
return this.create(method, resource, config);
}
-function WorkflowJobTemplateModelLoader (BaseModel) {
+function WorkflowJobTemplateModelLoader (BaseModel, _$http_) {
Base = BaseModel;
+ $http = _$http_;
return WorkflowJobTemplateModel;
}
WorkflowJobTemplateModelLoader.$inject = [
- 'BaseModel'
+ 'BaseModel',
+ '$http'
];
export default WorkflowJobTemplateModelLoader;
diff --git a/awx/ui/client/lib/models/index.js b/awx/ui/client/lib/models/index.js
index 9f3e57b3cd..3efc8d5299 100644
--- a/awx/ui/client/lib/models/index.js
+++ b/awx/ui/client/lib/models/index.js
@@ -1,6 +1,7 @@
import atLibServices from '~services';
import Application from '~models/Application';
+import AdHocCommand from '~models/AdHocCommand';
import Base from '~models/Base';
import Config from '~models/Config';
import Credential from '~models/Credential';
@@ -30,6 +31,7 @@ angular
atLibServices
])
.service('ApplicationModel', Application)
+ .service('AdHocCommandModel', AdHocCommand)
.service('BaseModel', Base)
.service('ConfigModel', Config)
.service('CredentialModel', Credential)
diff --git a/awx/ui/client/src/home/dashboard/lists/job-templates/job-templates-list.directive.js b/awx/ui/client/src/home/dashboard/lists/job-templates/job-templates-list.directive.js
index 1a34984a79..0456388539 100644
--- a/awx/ui/client/src/home/dashboard/lists/job-templates/job-templates-list.directive.js
+++ b/awx/ui/client/src/home/dashboard/lists/job-templates/job-templates-list.directive.js
@@ -1,10 +1,13 @@
/* jshint unused: vars */
export default
- [ 'InitiatePlaybookRun',
- 'templateUrl',
+ [ 'templateUrl',
'$state',
'Alert',
- function JobTemplatesList(InitiatePlaybookRun, templateUrl, $state, Alert) {
+ 'JobTemplateModel',
+ 'WorkflowJobTemplateModel',
+ 'PromptService',
+ 'ProcessErrors',
+ function JobTemplatesList(templateUrl, $state, Alert, JobTemplate, WorkflowJobTemplate, PromptService, ProcessErrors) {
return {
restrict: 'E',
link: link,
@@ -15,6 +18,9 @@ export default
};
function link(scope, element, attr) {
+ const jobTemplate = new JobTemplate();
+ const workflowTemplate = new WorkflowJobTemplate();
+
scope.$watch("data", function(data) {
if (data) {
if (data.length > 0) {
@@ -42,21 +48,131 @@ export default
scope.launchTemplate = function(template){
if(template) {
- if(template.type && (template.type === 'Job Template' || template.type === 'job_template')) {
- InitiatePlaybookRun({ scope: scope, id: template.id, job_type: 'job_template' });
- }
- else if(template.type && (template.type === 'Workflow Job Template' || template.type === 'workflow_job_template')) {
- InitiatePlaybookRun({ scope: scope, id: template.id, job_type: 'workflow_job_template' });
- }
- else {
- // Something went wrong - Let the user know that we're unable to launch because we don't know
- // what type of job template this is
- Alert('Error: Unable to determine template type', 'We were unable to determine this template\'s type while launching.');
- }
+ if(template.type && (template.type === 'Job Template' || template.type === 'job_template')) {
+ const selectedJobTemplate = jobTemplate.create();
+ const preLaunchPromises = [
+ selectedJobTemplate.getLaunch(template.id),
+ selectedJobTemplate.optionsLaunch(template.id),
+ ];
+
+ Promise.all(preLaunchPromises)
+ .then(([launchData, launchOptions]) => {
+ if (selectedJobTemplate.canLaunchWithoutPrompt()) {
+ return selectedJobTemplate
+ .postLaunch({ id: template.id })
+ .then(({ data }) => {
+ $state.go('jobResult', { id: data.job }, { reload: true });
+ });
+ }
+
+ const promptData = {
+ launchConf: launchData.data,
+ launchOptions: launchOptions.data,
+ template: template.id,
+ templateType: template.type,
+ prompts: PromptService.processPromptValues({
+ launchConf: launchData.data,
+ launchOptions: launchOptions.data
+ }),
+ triggerModalOpen: true,
+ };
+
+ if (launchData.data.survey_enabled) {
+ selectedJobTemplate.getSurveyQuestions(template.id)
+ .then(({ data }) => {
+ const processed = PromptService.processSurveyQuestions({ surveyQuestions: data.spec });
+ promptData.surveyQuestions = processed.surveyQuestions;
+ scope.promptData = promptData;
+ });
+ } else {
+ scope.promptData = promptData;
+ }
+ });
+ }
+ else if(template.type && (template.type === 'Workflow Job Template' || template.type === 'workflow_job_template')) {
+ const selectedWorkflowJobTemplate = workflowTemplate.create();
+ const preLaunchPromises = [
+ selectedWorkflowJobTemplate.getLaunch(template.id),
+ selectedWorkflowJobTemplate.optionsLaunch(template.id),
+ ];
+
+ Promise.all(preLaunchPromises)
+ .then(([launchData, launchOptions]) => {
+ if (selectedWorkflowJobTemplate.canLaunchWithoutPrompt()) {
+ return selectedWorkflowJobTemplate
+ .postLaunch({ id: template.id })
+ .then(({ data }) => {
+ $state.go('workflowResults', { id: data.workflow_job }, { reload: true });
+ });
+ }
+
+ const promptData = {
+ launchConf: launchData.data,
+ launchOptions: launchOptions.data,
+ template: template.id,
+ templateType: template.type,
+ prompts: PromptService.processPromptValues({
+ launchConf: launchData.data,
+ launchOptions: launchOptions.data
+ }),
+ triggerModalOpen: true,
+ };
+
+ if (launchData.data.survey_enabled) {
+ selectedWorkflowJobTemplate.getSurveyQuestions(template.id)
+ .then(({ data }) => {
+ const processed = PromptService.processSurveyQuestions({ surveyQuestions: data.spec });
+ promptData.surveyQuestions = processed.surveyQuestions;
+ scope.promptData = promptData;
+ });
+ } else {
+ scope.promptData = promptData;
+ }
+ });
}
else {
- Alert('Error: Unable to launch template', 'Template parameter is missing');
+ // Something went wrong - Let the user know that we're unable to launch because we don't know
+ // what type of job template this is
+ Alert('Error: Unable to determine template type', 'We were unable to determine this template\'s type while launching.');
}
+ }
+ else {
+ Alert('Error: Unable to launch template', 'Template parameter is missing');
+ }
+ };
+
+ scope.launchJob = () => {
+ const jobLaunchData = PromptService.bundlePromptDataForLaunch(scope.promptData);
+
+ // If the extra_vars dict is empty, we don't want to include it if we didn't prompt for anything.
+ if(_.isEmpty(jobLaunchData.extra_vars) && !(scope.promptData.launchConf.ask_variables_on_launch && scope.promptData.launchConf.survey_enabled && scope.promptData.surveyQuestions.length > 0)){
+ delete jobLaunchData.extra_vars;
+ }
+
+ if(scope.promptData.templateType === 'job_template') {
+ jobTemplate.create().postLaunch({
+ id: scope.promptData.template,
+ launchData: jobLaunchData
+ })
+ .then((launchRes) => {
+ $state.go('jobResult', { id: launchRes.data.job }, { reload: true });
+ })
+ .catch(({data, status}) => {
+ ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to launch job template: ' + status });
+ });
+ } else if(scope.promptData.templateType === 'workflow_job_template') {
+ workflowTemplate.create().postLaunch({
+ id: scope.promptData.template,
+ launchData: jobLaunchData
+ })
+ .then((launchRes) => {
+ $state.go('workflowResults', { id: launchRes.data.workflow_job }, { reload: true });
+ })
+ .catch(({data, status}) => {
+ ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to launch workflow job template: ' + status });
+ });
+ }
+
};
scope.editTemplate = function (template) {
diff --git a/awx/ui/client/src/home/dashboard/lists/job-templates/job-templates-list.partial.html b/awx/ui/client/src/home/dashboard/lists/job-templates/job-templates-list.partial.html
index 760f01a08d..67a1795f7d 100644
--- a/awx/ui/client/src/home/dashboard/lists/job-templates/job-templates-list.partial.html
+++ b/awx/ui/client/src/home/dashboard/lists/job-templates/job-templates-list.partial.html
@@ -56,3 +56,4 @@
You can create a job template here.
+
diff --git a/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.controller.js b/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.controller.js
index 0d3d79df37..5aa5938714 100644
--- a/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.controller.js
+++ b/awx/ui/client/src/inventories-hosts/inventories/adhoc/adhoc.controller.js
@@ -230,25 +230,25 @@ function adhocController($q, $scope, $stateParams,
$scope.removeStartAdhocRun();
}
$scope.removeStartAdhocRun = $scope.$on('StartAdhocRun', function() {
- var password;
- for (password in $scope.passwords) {
- data[$scope.passwords[password]] = $scope[
- $scope.passwords[password]
- ];
- }
- // Launch the adhoc job
- Rest.setUrl(GetBasePath('inventory') + id + '/ad_hoc_commands/');
- Rest.post(data)
- .then(({data}) => {
- Wait('stop');
- $state.go('adHocJobStdout', {id: data.id});
- })
- .catch(({data, status}) => {
- ProcessErrors($scope, data, status, adhocForm, {
- hdr: 'Error!',
- msg: 'Failed to launch adhoc command. POST ' +
- 'returned status: ' + status });
- });
+ var password;
+ for (password in $scope.passwords) {
+ data[$scope.passwords[password]] = $scope[
+ $scope.passwords[password]
+ ];
+ }
+ // Launch the adhoc job
+ Rest.setUrl(GetBasePath('inventory') + id + '/ad_hoc_commands/');
+ Rest.post(data)
+ .then(({data}) => {
+ Wait('stop');
+ $state.go('adHocJobStdout', {id: data.id});
+ })
+ .catch(({data, status}) => {
+ ProcessErrors($scope, data, status, adhocForm, {
+ hdr: 'Error!',
+ msg: 'Failed to launch adhoc command. POST ' +
+ 'returned status: ' + status });
+ });
});
if ($scope.removeCreateLaunchDialog) {
diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/completed-jobs/completed-jobs.list.js b/awx/ui/client/src/inventories-hosts/inventories/related/completed-jobs/completed-jobs.list.js
index 8580524392..5ab048c55e 100644
--- a/awx/ui/client/src/inventories-hosts/inventories/related/completed-jobs/completed-jobs.list.js
+++ b/awx/ui/client/src/inventories-hosts/inventories/related/completed-jobs/completed-jobs.list.js
@@ -71,12 +71,8 @@ export default ['i18n', function(i18n) {
columnClass: 'col-lg-2 col-md-2 col-sm-3 col-xs-4',
submit: {
- icon: 'icon-rocket',
- mode: 'all',
- ngClick: 'relaunchJob($event, completed_job.id)',
- awToolTip: i18n._('Relaunch using the same parameters'),
- dataPlacement: 'top',
ngShow: "!completed_job.type == 'system_job' || completed_job.summary_fields.user_capabilities.start",
+ // uses the at-relaunch directive
relaunch: true
},
"delete": {
diff --git a/awx/ui/client/src/job-results/job-results.controller.js b/awx/ui/client/src/job-results/job-results.controller.js
index b85bdbc8f9..e7e8f2c716 100644
--- a/awx/ui/client/src/job-results/job-results.controller.js
+++ b/awx/ui/client/src/job-results/job-results.controller.js
@@ -237,10 +237,6 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy
jobResultsService.cancelJob($scope.job);
};
- $scope.relaunchJob = function() {
- jobResultsService.relaunchJob($scope);
- };
-
$scope.lessLabels = false;
$scope.toggleLessLabels = function() {
if (!$scope.lessLabels) {
diff --git a/awx/ui/client/src/job-results/job-results.route.js b/awx/ui/client/src/job-results/job-results.route.js
index bbfd0282c0..60c06de7cd 100644
--- a/awx/ui/client/src/job-results/job-results.route.js
+++ b/awx/ui/client/src/job-results/job-results.route.js
@@ -47,7 +47,7 @@ export default {
return [preScope, eventOn];
}],
// the GET for the particular job
- jobData: ['Rest', 'GetBasePath', '$stateParams', '$q', '$state', 'Alert', 'jobResultsService', function(Rest, GetBasePath, $stateParams, $q, $state, Alert, jobResultsService) {
+ jobData: ['jobResultsService', '$stateParams', function(jobResultsService, $stateParams) {
return jobResultsService.getJobData($stateParams.id);
}],
Dataset: ['QuerySet', '$stateParams', 'jobData',
diff --git a/awx/ui/client/src/job-results/job-results.service.js b/awx/ui/client/src/job-results/job-results.service.js
index 5175aa373e..6b77575fff 100644
--- a/awx/ui/client/src/job-results/job-results.service.js
+++ b/awx/ui/client/src/job-results/job-results.service.js
@@ -5,8 +5,8 @@
*************************************************/
-export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors', 'InitiatePlaybookRun', 'GetBasePath', 'Alert', '$rootScope', 'i18n',
-function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, InitiatePlaybookRun, GetBasePath, Alert, $rootScope, i18n) {
+export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors', 'GetBasePath', 'Alert', '$rootScope', 'i18n',
+function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, GetBasePath, Alert, $rootScope, i18n) {
var val = {
// the playbook_on_stats event returns the count data in a weird format.
// format to what we need!
@@ -162,10 +162,6 @@ function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, InitiatePlaybo
actionText: i18n._('PROCEED')
});
},
- relaunchJob: function(scope) {
- InitiatePlaybookRun({ scope: scope, id: scope.job.id,
- relaunch: true });
- },
getJobData: function(id){
var val = $q.defer();
diff --git a/awx/ui/client/src/job-submission/job-submission-factories/inventory-update.factory.js b/awx/ui/client/src/job-submission/job-submission-factories/inventory-update.factory.js
index ddb1f67ab7..f218ab2767 100644
--- a/awx/ui/client/src/job-submission/job-submission-factories/inventory-update.factory.js
+++ b/awx/ui/client/src/job-submission/job-submission-factories/inventory-update.factory.js
@@ -24,13 +24,6 @@ export default
}
});
- if (scope.removePromptForPasswords) {
- scope.removePromptForPasswords();
- }
- scope.removePromptForPasswords = scope.$on('PromptForPasswords', function() {
- PromptForPasswords({ scope: scope, passwords: inventory_source.passwords_needed_to_update, callback: 'StartTheUpdate' });
- });
-
if (scope.removeStartTheUpdate) {
scope.removeStartTheUpdate();
}
@@ -49,13 +42,7 @@ export default
else {
inventory_source = data;
if (data.can_update) {
- if (data.passwords_needed_to_update) {
- Wait('stop');
- scope.$emit('PromptForPasswords');
- }
- else {
- scope.$emit('StartTheUpdate', {});
- }
+ scope.$emit('StartTheUpdate', {});
} else {
Wait('stop');
Alert('Error Launching Sync', 'Unable to execute the inventory sync. Please contact your system administrator.',
diff --git a/awx/ui/client/src/job-submission/job-submission-factories/project-update.factory.js b/awx/ui/client/src/job-submission/job-submission-factories/project-update.factory.js
index be732e0047..be7fe069fa 100644
--- a/awx/ui/client/src/job-submission/job-submission-factories/project-update.factory.js
+++ b/awx/ui/client/src/job-submission/job-submission-factories/project-update.factory.js
@@ -25,13 +25,6 @@ export default
}
});
- if (scope.removePromptForPasswords) {
- scope.removePromptForPasswords();
- }
- scope.removePromptForPasswords = scope.$on('PromptForPasswords', function() {
- PromptForPasswords({ scope: scope, passwords: project.passwords_needed_to_update, callback: 'StartTheUpdate' });
- });
-
if (scope.removeStartTheUpdate) {
scope.removeStartTheUpdate();
}
@@ -46,13 +39,7 @@ export default
.then(({data}) => {
project = data;
if (project.can_update) {
- if (project.passwords_needed_to_updated) {
- Wait('stop');
- scope.$emit('PromptForPasswords');
- }
- else {
- scope.$emit('StartTheUpdate', {});
- }
+ scope.$emit('StartTheUpdate', {});
}
else {
Alert('Permission Denied', 'You do not have access to update this project. Please contact your system administrator.',
diff --git a/awx/ui/client/src/jobs/all-jobs.list.js b/awx/ui/client/src/jobs/all-jobs.list.js
index f7737903a0..08cb3b99f7 100644
--- a/awx/ui/client/src/jobs/all-jobs.list.js
+++ b/awx/ui/client/src/jobs/all-jobs.list.js
@@ -92,12 +92,8 @@ export default ['i18n', function(i18n) {
dataPlacement: "top"
},
submit: {
- icon: 'icon-rocket',
- mode: 'all',
- ngClick: 'relaunchJob($event, job.id)',
- awToolTip: i18n._('Relaunch using the same parameters'),
- dataPlacement: 'top',
ngShow: "!(job.type == 'system_job') && job.summary_fields.user_capabilities.start",
+ // uses the at-relaunch directive
relaunch: true,
},
cancel: {
diff --git a/awx/ui/client/src/jobs/factories/job-status-tool-tip.factory.js b/awx/ui/client/src/jobs/factories/job-status-tool-tip.factory.js
deleted file mode 100644
index 126c9977d3..0000000000
--- a/awx/ui/client/src/jobs/factories/job-status-tool-tip.factory.js
+++ /dev/null
@@ -1,31 +0,0 @@
-export default
- function JobStatusToolTip() {
- return function(status) {
- var toolTip;
- switch (status) {
- case 'successful':
- case 'success':
- toolTip = 'There were no failed tasks.';
- break;
- case 'failed':
- toolTip = 'Some tasks encountered errors.';
- break;
- case 'canceled':
- toolTip = 'Stopped by user request.';
- break;
- case 'new':
- toolTip = 'In queue, waiting on task manager.';
- break;
- case 'waiting':
- toolTip = 'SCM Update or Inventory Update is executing.';
- break;
- case 'pending':
- toolTip = 'Not in queue, waiting on task manager.';
- break;
- case 'running':
- toolTip = 'Playbook tasks executing.';
- break;
- }
- return toolTip;
- };
- }
diff --git a/awx/ui/client/src/jobs/factories/jobs-list-update.factory.js b/awx/ui/client/src/jobs/factories/jobs-list-update.factory.js
deleted file mode 100644
index 5f950e458c..0000000000
--- a/awx/ui/client/src/jobs/factories/jobs-list-update.factory.js
+++ /dev/null
@@ -1,49 +0,0 @@
-export default
- function JobsListUpdate() {
- return function(params) {
- var scope = params.scope,
- parent_scope = params.parent_scope,
- list = params.list;
-
- scope[list.name].forEach(function(item, item_idx) {
- var fld, field,
- itm = scope[list.name][item_idx];
-
- //if (item.type === 'inventory_update') {
- // itm.name = itm.name.replace(/^.*?:/,'').replace(/^: /,'');
- //}
-
- // Set the item type label
- if (list.fields.type) {
- parent_scope.type_choices.forEach(function(choice) {
- if (choice.value === item.type) {
- itm.type_label = choice.label;
- }
- });
- }
- // Set the job status label
- parent_scope.status_choices.forEach(function(status) {
- if (status.value === item.status) {
- itm.status_label = status.label;
- }
- });
-
- if (list.name === 'completed_jobs' || list.name === 'running_jobs') {
- itm.status_tip = itm.status_label + '. Click for details.';
- }
- else if (list.name === 'queued_jobs') {
- itm.status_tip = 'Pending';
- }
-
- // Copy summary_field values
- for (field in list.fields) {
- fld = list.fields[field];
- if (fld.sourceModel) {
- if (itm.summary_fields[fld.sourceModel]) {
- itm[field] = itm.summary_fields[fld.sourceModel][fld.sourceField];
- }
- }
- }
- });
- };
- }
diff --git a/awx/ui/client/src/jobs/factories/relaunch-adhoc.factory.js b/awx/ui/client/src/jobs/factories/relaunch-adhoc.factory.js
deleted file mode 100644
index 462501f097..0000000000
--- a/awx/ui/client/src/jobs/factories/relaunch-adhoc.factory.js
+++ /dev/null
@@ -1,11 +0,0 @@
-export default
- function RelaunchAdhoc(AdhocRun) {
- return function(params) {
- var scope = params.scope,
- id = params.id;
- AdhocRun({ scope: scope, project_id: id, relaunch: true });
- };
- }
-
-RelaunchAdhoc.$inject =
- [ 'AdhocRun' ];
diff --git a/awx/ui/client/src/jobs/factories/relaunch-inventory.factory.js b/awx/ui/client/src/jobs/factories/relaunch-inventory.factory.js
deleted file mode 100644
index 3c9b3ecc4d..0000000000
--- a/awx/ui/client/src/jobs/factories/relaunch-inventory.factory.js
+++ /dev/null
@@ -1,26 +0,0 @@
-export default
- function RelaunchInventory(Wait, Rest, InventoryUpdate, ProcessErrors, GetBasePath) {
- return function(params) {
- var scope = params.scope,
- id = params.id,
- url = GetBasePath('inventory_sources') + id + '/';
- Wait('start');
- Rest.setUrl(url);
- Rest.get()
- .then(({data}) => {
- InventoryUpdate({
- scope: scope,
- url: data.related.update
- });
- })
- .catch(({data, status}) => {
- ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve inventory source: ' +
- url + ' GET returned: ' + status });
- });
- };
- }
-
-RelaunchInventory.$inject =
- [ 'Wait', 'Rest',
- 'InventoryUpdate', 'ProcessErrors', 'GetBasePath'
- ];
diff --git a/awx/ui/client/src/jobs/factories/relaunch-job.factory.js b/awx/ui/client/src/jobs/factories/relaunch-job.factory.js
deleted file mode 100644
index 0190cf9c79..0000000000
--- a/awx/ui/client/src/jobs/factories/relaunch-job.factory.js
+++ /dev/null
@@ -1,28 +0,0 @@
-export default
- function RelaunchJob(RelaunchInventory, RelaunchPlaybook, RelaunchSCM, RelaunchAdhoc) {
- return function(params) {
- var scope = params.scope,
- id = params.id,
- type = params.type,
- name = params.name;
- if (type === 'inventory_update') {
- RelaunchInventory({ scope: scope, id: id});
- }
- else if (type === 'ad_hoc_command') {
- RelaunchAdhoc({ scope: scope, id: id, name: name });
- }
- else if (type === 'job' || type === 'system_job' || type === 'workflow_job') {
- RelaunchPlaybook({ scope: scope, id: id, name: name, job_type: type });
- }
- else if (type === 'project_update') {
- RelaunchSCM({ scope: scope, id: id });
- }
- };
- }
-
-RelaunchJob.$inject =
- [ 'RelaunchInventory',
- 'RelaunchPlaybook',
- 'RelaunchSCM',
- 'RelaunchAdhoc'
- ];
diff --git a/awx/ui/client/src/jobs/factories/relaunch-playbook.factory.js b/awx/ui/client/src/jobs/factories/relaunch-playbook.factory.js
deleted file mode 100644
index 78431b17be..0000000000
--- a/awx/ui/client/src/jobs/factories/relaunch-playbook.factory.js
+++ /dev/null
@@ -1,12 +0,0 @@
-export default
- function RelaunchPlaybook(InitiatePlaybookRun) {
- return function(params) {
- var scope = params.scope,
- id = params.id,
- job_type = params.job_type;
- InitiatePlaybookRun({ scope: scope, id: id, relaunch: true, job_type: job_type });
- };
- }
-
-RelaunchPlaybook.$inject =
- [ 'InitiatePlaybookRun' ];
diff --git a/awx/ui/client/src/jobs/factories/relaunch-scm.factory.js b/awx/ui/client/src/jobs/factories/relaunch-scm.factory.js
deleted file mode 100644
index b37e90e9ca..0000000000
--- a/awx/ui/client/src/jobs/factories/relaunch-scm.factory.js
+++ /dev/null
@@ -1,11 +0,0 @@
-export default
- function RelaunchSCM(ProjectUpdate) {
- return function(params) {
- var scope = params.scope,
- id = params.id;
- ProjectUpdate({ scope: scope, project_id: id });
- };
- }
-
-RelaunchSCM.$inject =
- [ 'ProjectUpdate' ];
diff --git a/awx/ui/client/src/jobs/jobs-list.controller.js b/awx/ui/client/src/jobs/jobs-list.controller.js
index 7944e210f8..4ef6475023 100644
--- a/awx/ui/client/src/jobs/jobs-list.controller.js
+++ b/awx/ui/client/src/jobs/jobs-list.controller.js
@@ -11,11 +11,15 @@
*/
export default ['$state', '$rootScope', '$scope', '$stateParams',
- 'Find', 'DeleteJob', 'RelaunchJob',
+ 'Find', 'DeleteJob',
'GetBasePath', 'Dataset', 'QuerySet', 'ListDefinition', '$interpolate',
+ 'WorkflowJobModel', 'ProjectModel', 'Alert', 'InventorySourceModel',
+ 'AdHocCommandModel', 'JobModel',
function($state, $rootScope, $scope, $stateParams,
- Find, DeleteJob, RelaunchJob,
- GetBasePath, Dataset, qs, ListDefinition, $interpolate) {
+ Find, DeleteJob,
+ GetBasePath, Dataset, qs, ListDefinition, $interpolate,
+ WorkflowJob, Project, Alert, InventorySource,
+ AdHocCommand, Job) {
var list = ListDefinition;
@@ -95,42 +99,6 @@
DeleteJob({ scope: $scope, id: id });
};
- $scope.relaunchJob = function(event, id) {
- let job, typeId, jobs;
- try {
- $(event.target).tooltip('hide');
- } catch (e) {
- //ignore
- }
-
- if ($scope.completed_jobs) {
- jobs = $scope.completed_jobs;
- }
- else if ($scope.running_jobs) {
- jobs = $scope.running_jobs;
- }
- else if ($scope.queued_jobs) {
- jobs = $scope.queued_jobs;
- }
- else if ($scope.all_jobs) {
- jobs = $scope.all_jobs;
- }
- else if ($scope.jobs) {
- jobs = $scope.jobs;
- }
-
- job = Find({list: jobs, key: 'id', val: id });
-
- if (job.type === 'inventory_update') {
- typeId = job.inventory_source;
- } else if (job.type === 'project_update') {
- typeId = job.project;
- } else if (job.type === 'job' || job.type === "system_job" || job.type === 'ad_hoc_command' || job.type === 'workflow_job') {
- typeId = job.id;
- }
- RelaunchJob({ scope: $scope, id: typeId, type: job.type, name: job.name });
- };
-
$scope.viewjobResults = function(job) {
var goTojobResults = function(state) {
diff --git a/awx/ui/client/src/jobs/main.js b/awx/ui/client/src/jobs/main.js
index 2ee309875d..7aaf97035c 100644
--- a/awx/ui/client/src/jobs/main.js
+++ b/awx/ui/client/src/jobs/main.js
@@ -7,13 +7,6 @@
import jobsList from './jobs-list.controller';
import jobsRoute from './jobs.route';
import DeleteJob from './factories/delete-job.factory';
-import JobStatusToolTip from './factories/job-status-tool-tip.factory';
-import JobsListUpdate from './factories/jobs-list-update.factory';
-import RelaunchAdhoc from './factories/relaunch-adhoc.factory';
-import RelaunchInventory from './factories/relaunch-inventory.factory';
-import RelaunchJob from './factories/relaunch-job.factory';
-import RelaunchPlaybook from './factories/relaunch-playbook.factory';
-import RelaunchSCM from './factories/relaunch-scm.factory';
import AllJobsList from './all-jobs.list';
export default
@@ -23,11 +16,4 @@ export default
}])
.controller('JobsList', jobsList)
.factory('DeleteJob', DeleteJob)
- .factory('JobStatusToolTip', JobStatusToolTip)
- .factory('JobsListUpdate', JobsListUpdate)
- .factory('RelaunchAdhoc', RelaunchAdhoc)
- .factory('RelaunchInventory', RelaunchInventory)
- .factory('RelaunchJob', RelaunchJob)
- .factory('RelaunchPlaybook', RelaunchPlaybook)
- .factory('RelaunchSCM', RelaunchSCM)
.factory('AllJobsList', AllJobsList);
diff --git a/awx/ui/client/src/organizations/linkout/controllers/organizations-job-templates.controller.js b/awx/ui/client/src/organizations/linkout/controllers/organizations-job-templates.controller.js
index 8673874890..3caeb4425b 100644
--- a/awx/ui/client/src/organizations/linkout/controllers/organizations-job-templates.controller.js
+++ b/awx/ui/client/src/organizations/linkout/controllers/organizations-job-templates.controller.js
@@ -6,11 +6,11 @@
export default ['$scope', '$rootScope',
'$stateParams', 'Rest', 'ProcessErrors',
- 'GetBasePath', 'InitiatePlaybookRun', 'Wait', 'TemplateCopyService',
+ 'GetBasePath', 'InitiatePlaybookRun', 'Wait',
'$state', 'OrgJobTemplateList', 'OrgJobTemplateDataset', 'QuerySet',
function($scope, $rootScope,
$stateParams, Rest, ProcessErrors,
- GetBasePath, InitiatePlaybookRun, Wait, TemplateCopyService,
+ GetBasePath, InitiatePlaybookRun, Wait,
$state, OrgJobTemplateList, Dataset, qs) {
var list = OrgJobTemplateList,
@@ -81,24 +81,24 @@ export default ['$scope', '$rootScope',
$state.go('jobTemplateSchedules', { id: id });
};
- $scope.copyTemplate = function(id) {
- Wait('start');
- TemplateCopyService.get(id)
- .then((data) => {
- TemplateCopyService.set(data.results)
- .then((results) => {
- Wait('stop');
- if(results.type && results.type === 'job_template') {
- $state.go('templates.editJobTemplate', {job_template_id: results.id}, {reload: true});
- }
- });
- })
- .catch(({data, status}) => {
- ProcessErrors($rootScope, data, status, null, {hdr: 'Error!',
- msg: 'Call failed. Return status: '+ status});
- });
-
- };
+ // $scope.copyTemplate = function(id) {
+ // Wait('start');
+ // TemplateCopyService.get(id)
+ // .then((data) => {
+ // TemplateCopyService.set(data.results)
+ // .then((results) => {
+ // Wait('stop');
+ // if(results.type && results.type === 'job_template') {
+ // $state.go('templates.editJobTemplate', {job_template_id: results.id}, {reload: true});
+ // }
+ // });
+ // })
+ // .catch(({data, status}) => {
+ // ProcessErrors($rootScope, data, status, null, {hdr: 'Error!',
+ // msg: 'Call failed. Return status: '+ status});
+ // });
+ //
+ // };
}
];
diff --git a/awx/ui/client/src/portal-mode/portal-mode-job-templates.controller.js b/awx/ui/client/src/portal-mode/portal-mode-job-templates.controller.js
index 5453d9a248..65032896cd 100644
--- a/awx/ui/client/src/portal-mode/portal-mode-job-templates.controller.js
+++ b/awx/ui/client/src/portal-mode/portal-mode-job-templates.controller.js
@@ -4,23 +4,79 @@
* All Rights Reserved
*************************************************/
-export function PortalModeJobTemplatesController($scope, PortalJobTemplateList, InitiatePlaybookRun, Dataset) {
+export function PortalModeJobTemplatesController($scope, PortalJobTemplateList, Dataset, $state, PromptService, JobTemplate, ProcessErrors) {
var list = PortalJobTemplateList;
+ // search init
+ $scope.list = list;
+ $scope[`${list.iterator}_dataset`] = Dataset.data;
+ $scope[list.name] = $scope[`${list.iterator}_dataset`].results;
- init();
-
- function init() {
- // search init
- $scope.list = list;
- $scope[`${list.iterator}_dataset`] = Dataset.data;
- $scope[list.name] = $scope[`${list.iterator}_dataset`].results;
- }
+ const jobTemplate = new JobTemplate();
$scope.submitJob = function(id) {
- InitiatePlaybookRun({ scope: $scope, id: id, job_type: 'job_template' });
+ const selectedJobTemplate = jobTemplate.create();
+ const preLaunchPromises = [
+ selectedJobTemplate.getLaunch(id),
+ selectedJobTemplate.optionsLaunch(id),
+ ];
+
+ Promise.all(preLaunchPromises)
+ .then(([launchData, launchOptions]) => {
+ if (selectedJobTemplate.canLaunchWithoutPrompt()) {
+ return selectedJobTemplate
+ .postLaunch({ id: id })
+ .then(({ data }) => {
+ $state.go('jobResult', { id: data.job }, { reload: true });
+ });
+ }
+
+ const promptData = {
+ launchConf: launchData.data,
+ launchOptions: launchOptions.data,
+ template: id,
+ templateType: 'job_template',
+ prompts: PromptService.processPromptValues({
+ launchConf: launchData.data,
+ launchOptions: launchOptions.data
+ }),
+ triggerModalOpen: true,
+ };
+
+ if (launchData.data.survey_enabled) {
+ selectedJobTemplate.getSurveyQuestions(id)
+ .then(({ data }) => {
+ const processed = PromptService.processSurveyQuestions({ surveyQuestions: data.spec });
+ promptData.surveyQuestions = processed.surveyQuestions;
+ $scope.promptData = promptData;
+ });
+ } else {
+ $scope.promptData = promptData;
+ }
+ });
+ };
+
+ $scope.launchJob = () => {
+ const jobLaunchData = PromptService.bundlePromptDataForLaunch($scope.promptData);
+
+ // If the extra_vars dict is empty, we don't want to include it if we didn't prompt for anything.
+ if(_.isEmpty(jobLaunchData.extra_vars) && !($scope.promptData.launchConf.ask_variables_on_launch && $scope.promptData.launchConf.survey_enabled && $scope.promptData.surveyQuestions.length > 0)){
+ delete jobLaunchData.extra_vars;
+ }
+
+ jobTemplate.create().postLaunch({
+ id: $scope.promptData.template,
+ launchData: jobLaunchData
+ })
+ .then((launchRes) => {
+ $state.go('jobResult', { id: launchRes.data.job }, { reload: true });
+ })
+ .catch(({data, status}) => {
+ ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to launch job template: ' + status });
+ });
+
};
}
-PortalModeJobTemplatesController.$inject = ['$scope','PortalJobTemplateList', 'InitiatePlaybookRun', 'job_templatesDataset'];
+PortalModeJobTemplatesController.$inject = ['$scope','PortalJobTemplateList', 'job_templatesDataset', '$state', 'PromptService', 'JobTemplateModel', 'ProcessErrors'];
diff --git a/awx/ui/client/src/portal-mode/portal-mode.route.js b/awx/ui/client/src/portal-mode/portal-mode.route.js
index 024f6a7609..cb347821b3 100644
--- a/awx/ui/client/src/portal-mode/portal-mode.route.js
+++ b/awx/ui/client/src/portal-mode/portal-mode.route.js
@@ -50,7 +50,7 @@ export default {
list: PortalJobTemplateList,
mode: 'edit'
});
- return html;
+ return html + '';
},
controller: PortalModeJobTemplatesController
}
diff --git a/awx/ui/client/src/scheduler/factories/schedule-post.factory.js b/awx/ui/client/src/scheduler/factories/schedule-post.factory.js
index 0244f68068..bea921a8be 100644
--- a/awx/ui/client/src/scheduler/factories/schedule-post.factory.js
+++ b/awx/ui/client/src/scheduler/factories/schedule-post.factory.js
@@ -1,5 +1,5 @@
export default
- function SchedulePost(Rest, ProcessErrors, RRuleToAPI, Wait, $q, Schedule) {
+ function SchedulePost(Rest, ProcessErrors, RRuleToAPI, Wait, $q, Schedule, PromptService) {
return function(params) {
var scope = params.scope,
url = params.url,
@@ -36,57 +36,10 @@ export default
}
if(promptData) {
- if(promptData.launchConf.survey_enabled){
- for (var i=0; i < promptData.surveyQuestions.length; i++){
- var fld = promptData.surveyQuestions[i].variable;
- // grab all survey questions that have answers
- if(promptData.surveyQuestions[i].required || (promptData.surveyQuestions[i].required === false && promptData.surveyQuestions[i].model.toString()!=="")) {
- if(!scheduleData.extra_data) {
- scheduleData.extra_data = {};
- }
- scheduleData.extra_data[fld] = promptData.surveyQuestions[i].model;
- }
-
- if(promptData.surveyQuestions[i].required === false && _.isEmpty(promptData.surveyQuestions[i].model)) {
- switch (promptData.surveyQuestions[i].type) {
- // for optional text and text-areas, submit a blank string if min length is 0
- // -- this is confusing, for an explanation see:
- // http://docs.ansible.com/ansible-tower/latest/html/userguide/job_templates.html#optional-survey-questions
- //
- case "text":
- case "textarea":
- if (promptData.surveyQuestions[i].min === 0) {
- scheduleData.extra_data[fld] = "";
- }
- break;
- }
- }
- }
- }
-
- if(_.has(promptData, 'prompts.jobType.value.value') && _.get(promptData, 'launchConf.ask_job_type_on_launch')) {
- scheduleData.job_type = promptData.launchConf.defaults.job_type && promptData.launchConf.defaults.job_type === promptData.prompts.jobType.value.value ? null : promptData.prompts.jobType.value.value;
- }
- if(_.has(promptData, 'prompts.tags.value') && _.get(promptData, 'launchConf.ask_tags_on_launch')){
- const templateDefaultJobTags = promptData.launchConf.defaults.job_tags.split(',');
- scheduleData.job_tags = (_.isEqual(templateDefaultJobTags.sort(), promptData.prompts.tags.value.map(a => a.value).sort())) ? null : promptData.prompts.tags.value.map(a => a.value).join();
- }
- if(_.has(promptData, 'prompts.skipTags.value') && _.get(promptData, 'launchConf.ask_skip_tags_on_launch')){
- const templateDefaultSkipTags = promptData.launchConf.defaults.skip_tags.split(',');
- scheduleData.skip_tags = (_.isEqual(templateDefaultSkipTags.sort(), promptData.prompts.skipTags.value.map(a => a.value).sort())) ? null : promptData.prompts.skipTags.value.map(a => a.value).join();
- }
- if(_.has(promptData, 'prompts.limit.value') && _.get(promptData, 'launchConf.ask_limit_on_launch')){
- scheduleData.limit = promptData.launchConf.defaults.limit && promptData.launchConf.defaults.limit === promptData.prompts.limit.value ? null : promptData.prompts.limit.value;
- }
- if(_.has(promptData, 'prompts.verbosity.value.value') && _.get(promptData, 'launchConf.ask_verbosity_on_launch')){
- scheduleData.verbosity = promptData.launchConf.defaults.verbosity && promptData.launchConf.defaults.verbosity === promptData.prompts.verbosity.value.value ? null : promptData.prompts.verbosity.value.value;
- }
- if(_.has(promptData, 'prompts.inventory.value') && _.get(promptData, 'launchConf.ask_inventory_on_launch')){
- scheduleData.inventory = promptData.launchConf.defaults.inventory && promptData.launchConf.defaults.inventory.id === promptData.prompts.inventory.value.id ? null : promptData.prompts.inventory.value.id;
- }
- if(_.has(promptData, 'prompts.diffMode.value') && _.get(promptData, 'launchConf.ask_diff_mode_on_launch')){
- scheduleData.diff_mode = promptData.launchConf.defaults.diff_mode && promptData.launchConf.defaults.diff_mode === promptData.prompts.diffMode.value ? null : promptData.prompts.diffMode.value;
- }
+ scheduleData = PromptService.bundlePromptDataForSaving({
+ promptData: promptData,
+ dataToSave: scheduleData
+ });
}
Rest.setUrl(url);
@@ -212,5 +165,6 @@ SchedulePost.$inject =
'RRuleToAPI',
'Wait',
'$q',
- 'ScheduleModel'
+ 'ScheduleModel',
+ 'PromptService'
];
diff --git a/awx/ui/client/src/standard-out/adhoc/standard-out-adhoc.partial.html b/awx/ui/client/src/standard-out/adhoc/standard-out-adhoc.partial.html
index 2591deced8..7800872be2 100644
--- a/awx/ui/client/src/standard-out/adhoc/standard-out-adhoc.partial.html
+++ b/awx/ui/client/src/standard-out/adhoc/standard-out-adhoc.partial.html
@@ -8,7 +8,9 @@
RESULTS
diff --git a/awx/ui/client/src/standard-out/adhoc/standard-out-adhoc.route.js b/awx/ui/client/src/standard-out/adhoc/standard-out-adhoc.route.js
index e294116d78..899a98e9f7 100644
--- a/awx/ui/client/src/standard-out/adhoc/standard-out-adhoc.route.js
+++ b/awx/ui/client/src/standard-out/adhoc/standard-out-adhoc.route.js
@@ -23,5 +23,14 @@ export default {
"ad_hoc_command_events": []
}
}
+ },
+ resolve: {
+ jobData: ['Rest', 'GetBasePath', '$stateParams', function(Rest, GetBasePath, $stateParams) {
+ Rest.setUrl(GetBasePath('base') + 'ad_hoc_commands/' + $stateParams.id + '/');
+ return Rest.get()
+ .then(({data}) => {
+ return data;
+ });
+ }]
}
};
diff --git a/awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.partial.html b/awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.partial.html
index 4cd0ec3d82..2b2ebbadf0 100644
--- a/awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.partial.html
+++ b/awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.partial.html
@@ -8,7 +8,7 @@
RESULTS
diff --git a/awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.route.js b/awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.route.js
index b3931d345c..bdd1a9a2b1 100644
--- a/awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.route.js
+++ b/awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.route.js
@@ -25,5 +25,14 @@ export default {
}
},
jobType: 'inventory_updates'
+ },
+ resolve: {
+ jobData: ['Rest', 'GetBasePath', '$stateParams', function(Rest, GetBasePath, $stateParams) {
+ Rest.setUrl(GetBasePath('base') + 'inventory_updates/' + $stateParams.id + '/');
+ return Rest.get()
+ .then(({data}) => {
+ return data;
+ });
+ }]
}
};
diff --git a/awx/ui/client/src/standard-out/management-jobs/standard-out-management-jobs.route.js b/awx/ui/client/src/standard-out/management-jobs/standard-out-management-jobs.route.js
index 950801990c..e3a59e884d 100644
--- a/awx/ui/client/src/standard-out/management-jobs/standard-out-management-jobs.route.js
+++ b/awx/ui/client/src/standard-out/management-jobs/standard-out-management-jobs.route.js
@@ -23,5 +23,14 @@ export default {
"system_job_events": [],
}
}
+ },
+ resolve: {
+ jobData: ['Rest', 'GetBasePath', '$stateParams', function(Rest, GetBasePath, $stateParams) {
+ Rest.setUrl(GetBasePath('base') + 'system_jobs/' + $stateParams.id + '/');
+ return Rest.get()
+ .then(({data}) => {
+ return data;
+ });
+ }]
}
};
diff --git a/awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.partial.html b/awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.partial.html
index 008516a2ad..2921df86b4 100644
--- a/awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.partial.html
+++ b/awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.partial.html
@@ -8,7 +8,7 @@
RESULTS
diff --git a/awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.route.js b/awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.route.js
index c2e43be9ca..818509ccc7 100644
--- a/awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.route.js
+++ b/awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.route.js
@@ -25,5 +25,14 @@ export default {
"project_update_events": [],
}
},
+ },
+ resolve: {
+ jobData: ['Rest', 'GetBasePath', '$stateParams', function(Rest, GetBasePath, $stateParams) {
+ Rest.setUrl(GetBasePath('base') + 'project_updates/' + $stateParams.id + '/');
+ return Rest.get()
+ .then(({data}) => {
+ return data;
+ });
+ }]
}
};
diff --git a/awx/ui/client/src/standard-out/standard-out.block.less b/awx/ui/client/src/standard-out/standard-out.block.less
index bf0ca8727b..fe7e0d2757 100644
--- a/awx/ui/client/src/standard-out/standard-out.block.less
+++ b/awx/ui/client/src/standard-out/standard-out.block.less
@@ -131,6 +131,10 @@ standard-out-log {
font-size: 20px;
}
+.StandardOut-actions {
+ display: flex;
+}
+
.StandardOut-actionButton {
font-size: 16px;
height: 30px;
diff --git a/awx/ui/client/src/standard-out/standard-out.controller.js b/awx/ui/client/src/standard-out/standard-out.controller.js
index 220eead31c..1a707aa706 100644
--- a/awx/ui/client/src/standard-out/standard-out.controller.js
+++ b/awx/ui/client/src/standard-out/standard-out.controller.js
@@ -12,8 +12,9 @@
export function JobStdoutController ($rootScope, $scope, $state, $stateParams,
GetBasePath, Rest, ProcessErrors, Empty, GetChoices, LookUpName,
- ParseTypeChange, ParseVariableString, RelaunchJob, DeleteJob, Wait, i18n,
- fieldChoices, fieldLabels) {
+ ParseTypeChange, ParseVariableString, DeleteJob, Wait, i18n,
+ fieldChoices, fieldLabels, Project, Alert, InventorySource,
+ jobData) {
var job_id = $stateParams.id,
jobType = $state.current.data.jobType;
@@ -34,7 +35,12 @@ export function JobStdoutController ($rootScope, $scope, $state, $stateParams,
if (data.status === 'failed' || data.status === 'canceled' || data.status === 'error' || data.status === 'successful') {
// Go out and refresh the job details
- getjobResults();
+
+ Rest.setUrl(GetBasePath('base') + jobType + '/' + job_id + '/');
+ Rest.get()
+ .then(({data}) => {
+ updateJobObj(data);
+ });
}
});
@@ -72,149 +78,142 @@ export function JobStdoutController ($rootScope, $scope, $state, $stateParams,
// Set the parse type so that CodeMirror knows how to display extra params YAML/JSON
$scope.parseType = 'yaml';
- function getjobResults() {
+ function updateJobObj(updatedJobData) {
// Go out and get the job details based on the job type. jobType gets defined
// in the data block of the route declaration for each of the different types
// of stdout jobs.
- Rest.setUrl(GetBasePath('base') + jobType + '/' + job_id + '/');
- Rest.get()
- .then(({data}) => {
- $scope.job = data;
- $scope.job_template_name = data.name;
- $scope.created_by = data.summary_fields.created_by;
- $scope.project_name = (data.summary_fields.project) ? data.summary_fields.project.name : '';
- $scope.inventory_name = (data.summary_fields.inventory) ? data.summary_fields.inventory.name : '';
- $scope.job_template_url = '/#/templates/' + data.unified_job_template;
- if($scope.inventory_name && data.inventory && data.summary_fields.inventory && data.summary_fields.inventory.kind) {
- if(data.summary_fields.inventory.kind === '') {
- $scope.inventory_url = '/#/inventories/inventory' + data.inventory;
- }
- else if(data.summary_fields.inventory.kind === 'smart') {
- $scope.inventory_url = '/#/inventories/smart_inventory' + data.inventory;
- }
- }
- else {
- $scope.inventory_url = '';
- }
- $scope.project_url = ($scope.project_name && data.project) ? '/#/projects/' + data.project : '';
- $scope.credential_name = (data.summary_fields.credential) ? data.summary_fields.credential.name : '';
- $scope.credential_url = (data.credential) ? '/#/credentials/' + data.credential : '';
- $scope.cloud_credential_url = (data.cloud_credential) ? '/#/credentials/' + data.cloud_credential : '';
- if(data.summary_fields && data.summary_fields.source_workflow_job &&
- data.summary_fields.source_workflow_job.id){
- $scope.workflow_result_link = `/#/workflows/${data.summary_fields.source_workflow_job.id}`;
- }
- $scope.playbook = data.playbook;
- $scope.credential = data.credential;
- $scope.cloud_credential = data.cloud_credential;
- $scope.forks = data.forks;
- $scope.limit = data.limit;
- $scope.verbosity = data.verbosity;
- $scope.job_tags = data.job_tags;
- $scope.job.module_name = data.module_name;
- if (data.extra_vars) {
- $scope.variables = ParseVariableString(data.extra_vars);
- }
- $scope.$on('getInventorySource', function(e, d) {
- $scope.inv_manage_group_link = '/#/inventories/inventory/' + d.inventory + '/inventory_sources/edit/' + d.id;
+ $scope.job = updatedJobData;
+ $scope.job_template_name = updatedJobData.name;
+ $scope.created_by = updatedJobData.summary_fields.created_by;
+ $scope.project_name = (updatedJobData.summary_fields.project) ? updatedJobData.summary_fields.project.name : '';
+ $scope.inventory_name = (updatedJobData.summary_fields.inventory) ? updatedJobData.summary_fields.inventory.name : '';
+ $scope.job_template_url = '/#/templates/' + updatedJobData.unified_job_template;
+ if($scope.inventory_name && updatedJobData.inventory && updatedJobData.summary_fields.inventory && updatedJobData.summary_fields.inventory.kind) {
+ if(updatedJobData.summary_fields.inventory.kind === '') {
+ $scope.inventory_url = '/#/inventories/inventory' + updatedJobData.inventory;
+ }
+ else if(updatedJobData.summary_fields.inventory.kind === 'smart') {
+ $scope.inventory_url = '/#/inventories/smart_inventory' + updatedJobData.inventory;
+ }
+ }
+ else {
+ $scope.inventory_url = '';
+ }
+ $scope.project_url = ($scope.project_name && updatedJobData.project) ? '/#/projects/' + updatedJobData.project : '';
+ $scope.credential_name = (updatedJobData.summary_fields.credential) ? updatedJobData.summary_fields.credential.name : '';
+ $scope.credential_url = (updatedJobData.credential) ? '/#/credentials/' + updatedJobData.credential : '';
+ $scope.cloud_credential_url = (updatedJobData.cloud_credential) ? '/#/credentials/' + updatedJobData.cloud_credential : '';
+ if(updatedJobData.summary_fields && updatedJobData.summary_fields.source_workflow_job &&
+ updatedJobData.summary_fields.source_workflow_job.id){
+ $scope.workflow_result_link = `/#/workflows/${updatedJobData.summary_fields.source_workflow_job.id}`;
+ }
+ $scope.playbook = updatedJobData.playbook;
+ $scope.credential = updatedJobData.credential;
+ $scope.cloud_credential = updatedJobData.cloud_credential;
+ $scope.forks = updatedJobData.forks;
+ $scope.limit = updatedJobData.limit;
+ $scope.verbosity = updatedJobData.verbosity;
+ $scope.job_tags = updatedJobData.job_tags;
+ $scope.job.module_name = updatedJobData.module_name;
+ if (updatedJobData.extra_vars) {
+ $scope.variables = ParseVariableString(updatedJobData.extra_vars);
+ }
+
+ $scope.$on('getInventorySource', function(e, d) {
+ $scope.inv_manage_group_link = '/#/inventories/inventory/' + d.inventory + '/inventory_sources/edit/' + d.id;
+ });
+
+ // If we have a source then we have to go get the source choices from the server
+ if (!Empty(updatedJobData.source)) {
+ if ($scope.removeChoicesReady) {
+ $scope.removeChoicesReady();
+ }
+ $scope.removeChoicesReady = $scope.$on('ChoicesReady', function() {
+ $scope.source_choices.every(function(e) {
+ if (e.value === updatedJobData.source) {
+ $scope.source = e.label;
+ return false;
+ }
+ return true;
});
-
- // If we have a source then we have to go get the source choices from the server
- if (!Empty(data.source)) {
- if ($scope.removeChoicesReady) {
- $scope.removeChoicesReady();
- }
- $scope.removeChoicesReady = $scope.$on('ChoicesReady', function() {
- $scope.source_choices.every(function(e) {
- if (e.value === data.source) {
- $scope.source = e.label;
- return false;
- }
- return true;
- });
- });
- // GetChoices can be found in the helper: Utilities.js
- // It attaches the source choices to $scope.source_choices.
- // Then, when the callback is fired, $scope.source is bound
- // to the corresponding label.
- GetChoices({
- scope: $scope,
- url: GetBasePath('inventory_sources'),
- field: 'source',
- variable: 'source_choices',
- choice_name: 'choices',
- callback: 'ChoicesReady'
- });
- }
-
- // LookUpName can be found in the lookup-name.factory
- // It attaches the name that it gets (based on the url)
- // to the $scope variable defined by the attribute scope_var.
- if (!Empty(data.credential)) {
- LookUpName({
- scope: $scope,
- scope_var: 'credential',
- url: GetBasePath('credentials') + data.credential + '/',
- ignore_403: true
- });
- }
-
- if (!Empty(data.inventory)) {
- LookUpName({
- scope: $scope,
- scope_var: 'inventory',
- url: GetBasePath('inventory') + data.inventory + '/'
- });
- }
-
- if (!Empty(data.project)) {
- LookUpName({
- scope: $scope,
- scope_var: 'project',
- url: GetBasePath('projects') + data.project + '/'
- });
- }
-
- if (!Empty(data.cloud_credential)) {
- LookUpName({
- scope: $scope,
- scope_var: 'cloud_credential',
- url: GetBasePath('credentials') + data.cloud_credential + '/',
- ignore_403: true
- });
- }
-
- if (!Empty(data.inventory_source)) {
- LookUpName({
- scope: $scope,
- scope_var: 'inventory_source',
- url: GetBasePath('inventory_sources') + data.inventory_source + '/',
- callback: 'getInventorySource'
- });
- }
-
- if (data.extra_vars) {
- ParseTypeChange({
- scope: $scope,
- field_id: 'pre-formatted-variables',
- readOnly: true
- });
- }
-
- // If the job isn't running we want to clear out the interval that goes out and checks for stdout updates.
- // This interval is defined in the standard out log directive controller.
- if (data.status === 'successful' || data.status === 'failed' || data.status === 'error' || data.status === 'canceled') {
- if ($rootScope.jobStdOutInterval) {
- window.clearInterval($rootScope.jobStdOutInterval);
- }
- }
- })
- .catch(({data, status}) => {
- ProcessErrors($scope, data, status, null, { hdr: 'Error!',
- msg: 'Failed to retrieve job: ' + job_id + '. GET returned: ' + status });
});
+ // GetChoices can be found in the helper: Utilities.js
+ // It attaches the source choices to $scope.source_choices.
+ // Then, when the callback is fired, $scope.source is bound
+ // to the corresponding label.
+ GetChoices({
+ scope: $scope,
+ url: GetBasePath('inventory_sources'),
+ field: 'source',
+ variable: 'source_choices',
+ choice_name: 'choices',
+ callback: 'ChoicesReady'
+ });
+ }
+
+ // LookUpName can be found in the lookup-name.factory
+ // It attaches the name that it gets (based on the url)
+ // to the $scope variable defined by the attribute scope_var.
+ if (!Empty(updatedJobData.credential)) {
+ LookUpName({
+ scope: $scope,
+ scope_var: 'credential',
+ url: GetBasePath('credentials') + updatedJobData.credential + '/',
+ ignore_403: true
+ });
+ }
+
+ if (!Empty(updatedJobData.inventory)) {
+ LookUpName({
+ scope: $scope,
+ scope_var: 'inventory',
+ url: GetBasePath('inventory') + updatedJobData.inventory + '/'
+ });
+ }
+
+ if (!Empty(updatedJobData.project)) {
+ LookUpName({
+ scope: $scope,
+ scope_var: 'project',
+ url: GetBasePath('projects') + updatedJobData.project + '/'
+ });
+ }
+
+ if (!Empty(updatedJobData.cloud_credential)) {
+ LookUpName({
+ scope: $scope,
+ scope_var: 'cloud_credential',
+ url: GetBasePath('credentials') + updatedJobData.cloud_credential + '/',
+ ignore_403: true
+ });
+ }
+
+ if (!Empty(updatedJobData.inventory_source)) {
+ LookUpName({
+ scope: $scope,
+ scope_var: 'inventory_source',
+ url: GetBasePath('inventory_sources') + updatedJobData.inventory_source + '/',
+ callback: 'getInventorySource'
+ });
+ }
+
+ if (updatedJobData.extra_vars) {
+ ParseTypeChange({
+ scope: $scope,
+ field_id: 'pre-formatted-variables',
+ readOnly: true
+ });
+ }
+
+ // If the job isn't running we want to clear out the interval that goes out and checks for stdout updates.
+ // This interval is defined in the standard out log directive controller.
+ if (updatedJobData.status === 'successful' || updatedJobData.status === 'failed' || updatedJobData.status === 'error' || updatedJobData.status === 'canceled') {
+ if ($rootScope.jobStdOutInterval) {
+ window.clearInterval($rootScope.jobStdOutInterval);
+ }
+ }
}
@@ -255,26 +254,13 @@ export function JobStdoutController ($rootScope, $scope, $state, $stateParams,
});
};
- $scope.relaunchJob = function() {
- var typeId, job = $scope.job;
- if (job.type === 'inventory_update') {
- typeId = job.inventory_source;
- }
- else if (job.type === 'project_update') {
- typeId = job.project;
- }
- else if (job.type === 'job' || job.type === "system_job" || job.type === 'ad_hoc_command') {
- typeId = job.id;
- }
- RelaunchJob({ scope: $scope, id: typeId, type: job.type, name: job.name });
- };
-
- getjobResults();
+ updateJobObj(jobData);
}
JobStdoutController.$inject = [ '$rootScope', '$scope', '$state',
'$stateParams', 'GetBasePath', 'Rest', 'ProcessErrors',
'Empty', 'GetChoices', 'LookUpName', 'ParseTypeChange',
- 'ParseVariableString', 'RelaunchJob', 'DeleteJob', 'Wait', 'i18n',
- 'fieldChoices', 'fieldLabels'];
+ 'ParseVariableString', 'DeleteJob', 'Wait', 'i18n',
+ 'fieldChoices', 'fieldLabels', 'ProjectModel', 'Alert', 'InventorySourceModel',
+ 'jobData'];
diff --git a/awx/ui/client/src/templates/completed-jobs.list.js b/awx/ui/client/src/templates/completed-jobs.list.js
index 8be715f2dd..54ba53a48e 100644
--- a/awx/ui/client/src/templates/completed-jobs.list.js
+++ b/awx/ui/client/src/templates/completed-jobs.list.js
@@ -72,12 +72,9 @@ export default ['i18n', function(i18n) {
columnClass: 'col-lg-2 col-md-2 col-sm-3 col-xs-4',
submit: {
- icon: 'icon-rocket',
- mode: 'all',
- ngClick: 'relaunchJob($event, completed_job.id)',
- awToolTip: i18n._('Relaunch using the same parameters'),
- dataPlacement: 'top',
- ngShow: "!completed_job.type == 'system_job' || completed_job.summary_fields.user_capabilities.start"
+ ngShow: "!completed_job.type == 'system_job' || completed_job.summary_fields.user_capabilities.start",
+ // uses the at-relaunch directive
+ relaunch: true
},
"delete": {
mode: 'all',
diff --git a/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js b/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js
index 38b32b2942..5b05fd426b 100644
--- a/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js
+++ b/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js
@@ -16,7 +16,7 @@ export default
'Rest', 'Alert', 'ProcessErrors', 'GetBasePath', 'md5Setup',
'ParseTypeChange', 'Wait', 'selectedLabels', 'i18n',
'Empty', 'Prompt', 'ToJSON', 'GetChoices', 'CallbackHelpInit',
- 'InitiatePlaybookRun' , 'initSurvey', '$state', 'CreateSelect2',
+ 'initSurvey', '$state', 'CreateSelect2',
'ToggleNotification','$q', 'InstanceGroupsService', 'InstanceGroupsData',
'MultiCredentialService', 'availableLabels', 'projectGetPermissionDenied',
'inventoryGetPermissionDenied', 'jobTemplateData', 'ParseVariableString', 'ConfigData',
@@ -26,7 +26,7 @@ export default
ProcessErrors, GetBasePath, md5Setup,
ParseTypeChange, Wait, selectedLabels, i18n,
Empty, Prompt, ToJSON, GetChoices, CallbackHelpInit,
- InitiatePlaybookRun, SurveyControllerInit, $state, CreateSelect2,
+ SurveyControllerInit, $state, CreateSelect2,
ToggleNotification, $q, InstanceGroupsService, InstanceGroupsData,
MultiCredentialService, availableLabels, projectGetPermissionDenied,
inventoryGetPermissionDenied, jobTemplateData, ParseVariableString, ConfigData
@@ -718,50 +718,5 @@ export default
$scope.formCancel = function () {
$state.go('templates');
};
-
- // Related set: Add button
- $scope.add = function (set) {
- $rootScope.flashMessage = null;
- $location.path('/' + base + '/' + $stateParams.job_template_id + '/' + set);
- };
-
- // Related set: Edit button
- $scope.edit = function (set, id) {
- $rootScope.flashMessage = null;
- $location.path('/' + set + '/' + id);
- };
-
- // Launch a job using the selected template
- $scope.launch = function() {
-
- if ($scope.removePromptForSurvey) {
- $scope.removePromptForSurvey();
- }
- $scope.removePromptForSurvey = $scope.$on('PromptForSurvey', function() {
- var action = function () {
- // $scope.$emit("GatherFormFields");
- Wait('start');
- $('#prompt-modal').modal('hide');
- $scope.addSurvey();
-
- };
- Prompt({
- hdr: 'Incomplete Survey',
- body: 'Do you want to create a survey before proceeding?
',
- action: action
- });
- });
- if($scope.survey_enabled === true && $scope.survey_exists!==true){
- $scope.$emit("PromptForSurvey");
- }
- else {
-
- InitiatePlaybookRun({
- scope: $scope,
- id: id,
- job_type: 'job_template'
- });
- }
- };
}
];
diff --git a/awx/ui/client/src/templates/prompt/prompt.controller.js b/awx/ui/client/src/templates/prompt/prompt.controller.js
index f2f08f9207..79df58c51c 100644
--- a/awx/ui/client/src/templates/prompt/prompt.controller.js
+++ b/awx/ui/client/src/templates/prompt/prompt.controller.js
@@ -65,37 +65,30 @@ export default [ 'Rest', 'GetBasePath', 'ProcessErrors', 'CredentialTypeModel',
}
}));
- vm.promptData.prompts.credentials.passwordsNeededToStart = vm.promptData.launchConf.passwords_needed_to_start;
vm.promptData.prompts.credentials.passwords = {};
- vm.promptData.prompts.credentials.value.forEach((credential) => {
- if (credential.passwords_needed && credential.passwords_needed.length > 0) {
- credential.passwords_needed.forEach(passwordNeeded => {
- let credPassObj = {
- id: credential.id,
- name: credential.name
- };
+ vm.promptData.launchConf.passwords_needed_to_start.forEach((passwordNeeded) => {
+ if(passwordNeeded === "ssh_password") {
+ vm.promptData.prompts.credentials.passwords.ssh = {};
+ }
+ if(passwordNeeded === "become_password") {
+ vm.promptData.prompts.credentials.passwords.become = {};
+ }
+ if(passwordNeeded === "ssh_key_unlock") {
+ vm.promptData.prompts.credentials.passwords.ssh_key_unlock = {};
+ }
+ if(passwordNeeded.startsWith("vault_password")) {
+ let vault_id;
+ if(passwordNeeded.includes('.')) {
+ vault_id = passwordNeeded.split(/\.(.+)/)[1];
+ }
- if(passwordNeeded === "ssh_password") {
- vm.promptData.prompts.credentials.passwords.ssh = credPassObj;
- }
- if(passwordNeeded === "become_password") {
- vm.promptData.prompts.credentials.passwords.become = credPassObj;
- }
- if(passwordNeeded === "ssh_key_unlock") {
- vm.promptData.prompts.credentials.passwords.ssh_key_unlock = credPassObj;
- }
- if(passwordNeeded.startsWith("vault_password")) {
- if(passwordNeeded.includes('.')) {
- credPassObj.vault_id = passwordNeeded.split(/\.(.+)/)[1];
- }
+ if(!vm.promptData.prompts.credentials.passwords.vault) {
+ vm.promptData.prompts.credentials.passwords.vault = [];
+ }
- if(!vm.promptData.prompts.credentials.passwords.vault) {
- vm.promptData.prompts.credentials.passwords.vault = [];
- }
-
- vm.promptData.prompts.credentials.passwords.vault.push(credPassObj);
- }
+ vm.promptData.prompts.credentials.passwords.vault.push({
+ vault_id: vault_id
});
}
});
diff --git a/awx/ui/client/src/templates/prompt/prompt.service.js b/awx/ui/client/src/templates/prompt/prompt.service.js
index 5ab5002c27..2044b7f440 100644
--- a/awx/ui/client/src/templates/prompt/prompt.service.js
+++ b/awx/ui/client/src/templates/prompt/prompt.service.js
@@ -1,7 +1,7 @@
function PromptService (Empty, $filter) {
this.processPromptValues = (params) => {
- let prompts = {
+ const prompts = {
credentials: {},
inventory: {},
variables: {},
@@ -16,10 +16,23 @@ function PromptService (Empty, $filter) {
prompts.credentials.value = _.has(params, 'launchConf.defaults.credentials') ? _.cloneDeep(params.launchConf.defaults.credentials) : [];
prompts.inventory.value = _.has(params, 'currentValues.summary_fields.inventory') ? params.currentValues.summary_fields.inventory : (_.has(params, 'launchConf.defaults.inventory') ? params.launchConf.defaults.inventory : null);
- let skipTags = _.has(params, 'currentValues.skip_tags') && params.currentValues.skip_tags ? params.currentValues.skip_tags : (_.has(params, 'launchConf.defaults.skip_tags') ? params.launchConf.defaults.skip_tags : "");
- let jobTags = _.has(params, 'currentValues.job_tags') && params.currentValues.job_tags ? params.currentValues.job_tags : (_.has(params, 'launchConf.defaults.job_tags') ? params.launchConf.defaults.job_tags : "");
+ const skipTags = _.has(params, 'currentValues.skip_tags') && params.currentValues.skip_tags ? params.currentValues.skip_tags : (_.has(params, 'launchConf.defaults.skip_tags') ? params.launchConf.defaults.skip_tags : "");
+ const jobTags = _.has(params, 'currentValues.job_tags') && params.currentValues.job_tags ? params.currentValues.job_tags : (_.has(params, 'launchConf.defaults.job_tags') ? params.launchConf.defaults.job_tags : "");
- prompts.variables.value = _.has(params, 'launchConf.defaults.extra_vars') && params.launchConf.defaults.extra_vars !== "" ? params.launchConf.defaults.extra_vars : "---";
+ let extraVars = '';
+
+ const hasCurrentExtraVars = _.get(params, 'currentValues.extra_data'),
+ hasDefaultExtraVars = _.get(params, 'launchConf.defaults.extra_vars');
+
+ if(hasCurrentExtraVars && hasDefaultExtraVars) {
+ extraVars = _.merge(jsyaml.safeLoad(params.launchConf.defaults.extra_vars), params.currentValues.extra_data);
+ } else if(hasCurrentExtraVars) {
+ extraVars = params.currentValues.extra_data;
+ } else if(hasDefaultExtraVars) {
+ extraVars = jsyaml.safeLoad(params.launchConf.defaults.extra_vars);
+ }
+
+ prompts.variables.value = extraVars && extraVars !== '' ? '---\n' + jsyaml.safeDump(extraVars) : '---\n';
prompts.verbosity.choices = _.get(params, 'launchOptions.actions.POST.verbosity.choices', []).map(c => ({label: c[1], value: c[0]}));
prompts.verbosity.value = _.has(params, 'currentValues.verbosity') && params.currentValues.verbosity ? _.find(prompts.verbosity.choices, item => item.value === params.currentValues.verbosity) : _.find(prompts.verbosity.choices, item => item.value === params.launchConf.defaults.verbosity);
prompts.jobType.choices = _.get(params, 'launchOptions.actions.POST.job_type.choices', []).map(c => ({label: c[1], value: c[0]}));
@@ -27,7 +40,7 @@ function PromptService (Empty, $filter) {
prompts.limit.value = _.has(params, 'currentValues.limit') && params.currentValues.limit ? params.currentValues.limit : (_.has(params, 'launchConf.defaults.limit') ? params.launchConf.defaults.limit : "");
prompts.tags.options = prompts.tags.value = (jobTags && jobTags !== "") ? jobTags.split(',').map((i) => ({name: i, label: i, value: i})) : [];
prompts.skipTags.options = prompts.skipTags.value = (skipTags && skipTags !== "") ? skipTags.split(',').map((i) => ({name: i, label: i, value: i})) : [];
- prompts.diffMode.value = _.has(params, 'currentValues.diff_mode') && typeof params.currentValues.diff_mode === 'boolean' ? params.currentValues.diff_mode : (_.has(params, 'launchConf.defaults.diff_mode') ? params.launchConf.defaults.diff_mode : false);
+ prompts.diffMode.value = _.has(params, 'currentValues.diff_mode') && typeof params.currentValues.diff_mode === 'boolean' ? params.currentValues.diff_mode : (_.has(params, 'launchConf.defaults.diff_mode') ? params.launchConf.defaults.diff_mode : null);
return prompts;
};
@@ -117,6 +130,144 @@ function PromptService (Empty, $filter) {
missingSurveyValue: missingSurveyValue
};
};
+
+ this.bundlePromptDataForLaunch = (promptData) => {
+ const launchData = {
+ extra_vars: promptData.extraVars
+ };
+
+ if (promptData.launchConf.ask_tags_on_launch){
+ launchData.job_tags = promptData.prompts.tags.value.map(a => a.value).join();
+ }
+ if (promptData.launchConf.ask_skip_tags_on_launch){
+ launchData.skip_tags = promptData.prompts.skipTags.value.map(a => a.value).join();
+ }
+ if (promptData.launchConf.ask_limit_on_launch && _.has(promptData, 'prompts.limit.value')){
+ launchData.limit = promptData.prompts.limit.value;
+ }
+ if (promptData.launchConf.ask_job_type_on_launch && _.has(promptData, 'prompts.jobType.value.value')) {
+ launchData.job_type = promptData.prompts.jobType.value.value;
+ }
+ if (promptData.launchConf.ask_verbosity_on_launch && _.has(promptData, 'prompts.verbosity.value.value')) {
+ launchData.verbosity = promptData.prompts.verbosity.value.value;
+ }
+ if (promptData.launchConf.ask_inventory_on_launch && !Empty(promptData.prompts.inventory.value.id)){
+ launchData.inventory_id = promptData.prompts.inventory.value.id;
+ }
+ if (promptData.launchConf.ask_credential_on_launch){
+ launchData.credentials = [];
+ promptData.prompts.credentials.value.forEach((credential) => {
+ launchData.credentials.push(credential.id);
+ });
+ }
+ if (promptData.launchConf.ask_diff_mode_on_launch && _.has(promptData, 'prompts.diffMode.value')) {
+ launchData.diff_mode = promptData.prompts.diffMode.value;
+ }
+ if (promptData.prompts.credentials.passwords) {
+ _.forOwn(promptData.prompts.credentials.passwords, (val, key) => {
+ if (!launchData.credential_passwords) {
+ launchData.credential_passwords = {};
+ }
+ if (key === "ssh_key_unlock") {
+ launchData.credential_passwords.ssh_key_unlock = val.value;
+ } else if (key !== "vault") {
+ launchData.credential_passwords[`${key}_password`] = val.value;
+ } else {
+ _.each(val, (vaultCred) => {
+ launchData.credential_passwords[vaultCred.vault_id ? `${key}_password.${vaultCred.vault_id}` : `${key}_password`] = vaultCred.value;
+ });
+ }
+ });
+ }
+
+ return launchData;
+ };
+
+ this.bundlePromptDataForRelaunch = (promptData) => {
+ const launchData = {};
+
+ if(promptData.relaunchHostType) {
+ launchData.hosts = promptData.relaunchHostType;
+ }
+
+ if (promptData.prompts.credentials.passwords) {
+ _.forOwn(promptData.prompts.credentials.passwords, (val, key) => {
+ if (!launchData.credential_passwords) {
+ launchData.credential_passwords = {};
+ }
+ if (key === "ssh_key_unlock") {
+ launchData.credential_passwords.ssh_key_unlock = val.value;
+ } else if (key !== "vault") {
+ launchData.credential_passwords[`${key}_password`] = val.value;
+ } else {
+ _.each(val, (vaultCred) => {
+ launchData.credential_passwords[vaultCred.vault_id ? `${key}_password.${vaultCred.vault_id}` : `${key}_password`] = vaultCred.value;
+ });
+ }
+ });
+ }
+
+ return launchData;
+ };
+
+ this.bundlePromptDataForSaving = (params) => {
+
+ const promptDataToSave = params.dataToSave ? params.dataToSave : {};
+
+ if(params.promptData.launchConf.survey_enabled){
+ for (var i=0; i < params.promptData.surveyQuestions.length; i++){
+ var fld = params.promptData.surveyQuestions[i].variable;
+ // grab all survey questions that have answers
+ if(params.promptData.surveyQuestions[i].required || (params.promptData.surveyQuestions[i].required === false && params.promptData.surveyQuestions[i].model.toString()!=="")) {
+ if(!promptDataToSave.extra_data) {
+ promptDataToSave.extra_data = {};
+ }
+ promptDataToSave.extra_data[fld] = params.promptData.surveyQuestions[i].model;
+ }
+
+ if(params.promptData.surveyQuestions[i].required === false && _.isEmpty(params.promptData.surveyQuestions[i].model)) {
+ switch (params.promptData.surveyQuestions[i].type) {
+ // for optional text and text-areas, submit a blank string if min length is 0
+ // -- this is confusing, for an explanation see:
+ // http://docs.ansible.com/ansible-tower/latest/html/userguide/job_templates.html#optional-survey-questions
+ //
+ case "text":
+ case "textarea":
+ if (params.promptData.surveyQuestions[i].min === 0) {
+ promptDataToSave.extra_data[fld] = "";
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if(_.has(params, 'promptData.prompts.jobType.value.value') && _.get(params, 'promptData.launchConf.ask_job_type_on_launch')) {
+ promptDataToSave.job_type = params.promptData.launchConf.defaults.job_type && params.promptData.launchConf.defaults.job_type === params.promptData.prompts.jobType.value.value ? null : params.promptData.prompts.jobType.value.value;
+ }
+ if(_.has(params, 'promptData.prompts.tags.value') && _.get(params, 'promptData.launchConf.ask_tags_on_launch')){
+ const templateDefaultJobTags = params.promptData.launchConf.defaults.job_tags.split(',');
+ promptDataToSave.job_tags = (_.isEqual(templateDefaultJobTags.sort(), params.promptData.prompts.tags.value.map(a => a.value).sort())) ? null : params.promptData.prompts.tags.value.map(a => a.value).join();
+ }
+ if(_.has(params, 'promptData.prompts.skipTags.value') && _.get(params, 'promptData.launchConf.ask_skip_tags_on_launch')){
+ const templateDefaultSkipTags = params.promptData.launchConf.defaults.skip_tags.split(',');
+ promptDataToSave.skip_tags = (_.isEqual(templateDefaultSkipTags.sort(), params.promptData.prompts.skipTags.value.map(a => a.value).sort())) ? null : params.promptData.prompts.skipTags.value.map(a => a.value).join();
+ }
+ if(_.has(params, 'promptData.prompts.limit.value') && _.get(params, 'promptData.launchConf.ask_limit_on_launch')){
+ promptDataToSave.limit = params.promptData.launchConf.defaults.limit && params.promptData.launchConf.defaults.limit === params.promptData.prompts.limit.value ? null : params.promptData.prompts.limit.value;
+ }
+ if(_.has(params, 'promptData.prompts.verbosity.value.value') && _.get(params, 'promptData.launchConf.ask_verbosity_on_launch')){
+ promptDataToSave.verbosity = params.promptData.launchConf.defaults.verbosity && params.promptData.launchConf.defaults.verbosity === params.promptData.prompts.verbosity.value.value ? null : params.promptData.prompts.verbosity.value.value;
+ }
+ if(_.has(params, 'promptData.prompts.inventory.value') && _.get(params, 'promptData.launchConf.ask_inventory_on_launch')){
+ promptDataToSave.inventory = params.promptData.launchConf.defaults.inventory && params.promptData.launchConf.defaults.inventory.id === params.promptData.prompts.inventory.value.id ? null : params.promptData.prompts.inventory.value.id;
+ }
+ if(_.has(params, 'promptData.prompts.diffMode.value') && _.get(params, 'promptData.launchConf.ask_diff_mode_on_launch')){
+ promptDataToSave.diff_mode = params.promptData.launchConf.defaults.diff_mode && params.promptData.launchConf.defaults.diff_mode === params.promptData.prompts.diffMode.value ? null : params.promptData.prompts.diffMode.value;
+ }
+
+ return promptDataToSave;
+ };
}
PromptService.$inject = ['Empty', '$filter'];
diff --git a/awx/ui/client/src/templates/prompt/steps/preview/prompt-preview.controller.js b/awx/ui/client/src/templates/prompt/steps/preview/prompt-preview.controller.js
index 66e7e00600..eb88095284 100644
--- a/awx/ui/client/src/templates/prompt/steps/preview/prompt-preview.controller.js
+++ b/awx/ui/client/src/templates/prompt/steps/preview/prompt-preview.controller.js
@@ -72,7 +72,7 @@ export default
});
}
- scope.promptExtraVars = $.isEmptyObject(scope.promptData.extraVars) ? '---' : jsyaml.safeDump(scope.promptData.extraVars);
+ scope.promptExtraVars = $.isEmptyObject(scope.promptData.extraVars) ? '---' : '---\n' + jsyaml.safeDump(scope.promptData.extraVars);
ParseTypeChange({
scope: scope,
diff --git a/awx/ui/client/src/templates/prompt/steps/preview/prompt-preview.partial.html b/awx/ui/client/src/templates/prompt/steps/preview/prompt-preview.partial.html
index b2a20a541f..452aa6815c 100644
--- a/awx/ui/client/src/templates/prompt/steps/preview/prompt-preview.partial.html
+++ b/awx/ui/client/src/templates/prompt/steps/preview/prompt-preview.partial.html
@@ -1,5 +1,5 @@
-
+
{{:: vm.strings.get('prompt.JOB_TYPE') }}
{{:: vm.strings.get('prompt.PLAYBOOK_RUN') }}
@@ -24,7 +24,7 @@
-
+
{{:: vm.strings.get('prompt.INVENTORY') }}
@@ -32,7 +32,7 @@
{{:: vm.strings.get('prompt.LIMIT') }}
-
+
{{:: vm.strings.get('prompt.VERBOSITY') }}
@@ -68,7 +68,7 @@
-
+
{{:: vm.strings.get('prompt.SHOW_CHANGES') }}
{{:: vm.strings.get('ON') }}
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 85e0336352..8f80fadf84 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
@@ -77,61 +77,36 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
unified_job_template: params.node.unifiedJobTemplate.id
};
+ if(_.has(params, 'node.promptData.extraVars')) {
+ if(_.get(params, 'node.promptData.launchConf.defaults.extra_vars')) {
+ if(!sendableNodeData.extra_data) {
+ sendableNodeData.extra_data = {};
+ }
+
+ const defaultVars = jsyaml.safeLoad(params.node.promptData.launchConf.defaults.extra_vars);
+
+ // Only include extra vars that differ from the template default vars
+ _.forOwn(params.node.promptData.extraVars, (value, key) => {
+ if(!defaultVars[key] || defaultVars[key] !== value) {
+ sendableNodeData.extra_data[key] = value;
+ }
+ });
+ if(sendableNodeData.extra_data === {}) {
+ delete sendableNodeData.extra_data;
+ }
+ } else {
+ sendableNodeData.extra_data = params.node.promptData.extraVars;
+ }
+ }
+
// 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.promptData.launchConf.survey_enabled){
- for (var i=0; i < params.node.promptData.surveyQuestions.length; i++){
- var fld = params.node.promptData.surveyQuestions[i].variable;
- // grab all survey questions that have answers
- if(params.node.promptData.surveyQuestions[i].required || (params.node.promptData.surveyQuestions[i].required === false && params.node.promptData.surveyQuestions[i].model.toString()!=="")) {
- if(!sendableNodeData.extra_data) {
- sendableNodeData.extra_data = {};
- }
- sendableNodeData.extra_data[fld] = params.node.promptData.surveyQuestions[i].model;
- }
-
- if(params.node.promptData.surveyQuestions[i].required === false && _.isEmpty(params.node.promptData.surveyQuestions[i].model)) {
- switch (params.node.promptData.surveyQuestions[i].type) {
- // for optional text and text-areas, submit a blank string if min length is 0
- // -- this is confusing, for an explanation see:
- // http://docs.ansible.com/ansible-tower/latest/html/userguide/job_templates.html#optional-survey-questions
- //
- case "text":
- case "textarea":
- if (params.node.promptData.surveyQuestions[i].min === 0) {
- sendableNodeData.extra_data[fld] = "";
- }
- break;
- }
- }
- }
- }
-
- if(_.has(params, 'node.promptData.prompts.jobType.value.value') && _.get(params, 'node.promptData.launchConf.ask_job_type_on_launch')) {
- sendableNodeData.job_type = params.node.promptData.prompts.jobType.templateDefault === params.node.promptData.prompts.jobType.value.value ? null : params.node.promptData.prompts.jobType.value.value;
- }
- if(_.has(params, 'node.promptData.prompts.tags.value') && _.get(params, 'node.promptData.launchConf.ask_tags_on_launch')){
- let templateDefaultJobTags = params.node.promptData.prompts.tags.templateDefault.split(',');
- sendableNodeData.job_tags = (_.isEqual(templateDefaultJobTags.sort(), params.node.promptData.prompts.tags.value.map(a => a.value).sort())) ? null : params.node.promptData.prompts.tags.value.map(a => a.value).join();
- }
- if(_.has(params, 'node.promptData.prompts.skipTags.value') && _.get(params, 'node.promptData.launchConf.ask_skip_tags_on_launch')){
- let templateDefaultSkipTags = params.node.promptData.prompts.skipTags.templateDefault.split(',');
- sendableNodeData.skip_tags = (_.isEqual(templateDefaultSkipTags.sort(), params.node.promptData.prompts.skipTags.value.map(a => a.value).sort())) ? null : params.node.promptData.prompts.skipTags.value.map(a => a.value).join();
- }
- if(_.has(params, 'node.promptData.prompts.limit.value') && _.get(params, 'node.promptData.launchConf.ask_limit_on_launch')){
- sendableNodeData.limit = params.node.promptData.prompts.limit.templateDefault === params.node.promptData.prompts.limit.value ? null : params.node.promptData.prompts.limit.value;
- }
- if(_.has(params, 'node.promptData.prompts.verbosity.value.value') && _.get(params, 'node.promptData.launchConf.ask_verbosity_on_launch')){
- sendableNodeData.verbosity = params.node.promptData.prompts.verbosity.templateDefault === params.node.promptData.prompts.verbosity.value.value ? null : params.node.promptData.prompts.verbosity.value.value;
- }
- if(_.has(params, 'node.promptData.prompts.inventory.value') && _.get(params, 'node.promptData.launchConf.ask_inventory_on_launch')){
- sendableNodeData.inventory = _.has(params, 'node.promptData.prompts.inventory.templateDefault.id') && params.node.promptData.prompts.inventory.templateDefault.id === params.node.promptData.prompts.inventory.value.id ? null : params.node.promptData.prompts.inventory.value.id;
- }
- if(_.has(params, 'node.promptData.prompts.diffMode.value') && _.get(params, 'node.promptData.launchConf.ask_diff_mode_on_launch')){
- sendableNodeData.diff_mode = params.node.promptData.prompts.diffMode.templateDefault === params.node.promptData.prompts.diffMode.value ? null : params.node.promptData.prompts.diffMode.value;
- }
+ sendableNodeData = PromptService.bundlePromptDataForSaving({
+ promptData: params.node.promptData,
+ dataToSave: sendableNodeData
+ });
}
return sendableNodeData;
@@ -1028,7 +1003,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
}),
};
- $scope.$watch('promptData.surveyQuestions', () => {
+ surveyQuestionWatcher = $scope.$watch('promptData.surveyQuestions', () => {
let missingSurveyValue = false;
_.each($scope.promptData.surveyQuestions, (question) => {
if(question.required && (Empty(question.model) || question.model === [])) {
diff --git a/awx/ui/client/src/workflow-results/workflow-results.service.js b/awx/ui/client/src/workflow-results/workflow-results.service.js
index 4db058a163..41bc410647 100644
--- a/awx/ui/client/src/workflow-results/workflow-results.service.js
+++ b/awx/ui/client/src/workflow-results/workflow-results.service.js
@@ -5,7 +5,7 @@
*************************************************/
-export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors', 'InitiatePlaybookRun', '$interval', 'moment', function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, InitiatePlaybookRun, $interval, moment) {
+export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors', 'WorkflowJobModel', '$interval', 'moment', function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, WorkflowJob, $interval, moment) {
var val = {
getCounts: function(workflowNodes){
var nodeArr = [];
@@ -107,8 +107,13 @@ export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErr
});
},
relaunchJob: function(scope) {
- InitiatePlaybookRun({ scope: scope, id: scope.workflow.id,
- relaunch: true, job_type: 'workflow_job' });
+ const workflowJob = new WorkflowJob();
+
+ workflowJob.postRelaunch({
+ id: scope.workflow.id
+ }).then((launchRes) => {
+ $state.go('workflowResults', { id: launchRes.data.id }, { reload: true });
+ });
},
createOneSecondTimer: function(startTime, fn) {
return $interval(function(){
diff --git a/awx/ui/test/spec/job-results/job-results.controller-test.js b/awx/ui/test/spec/job-results/job-results.controller-test.js
index af3aeeae34..c05f2329f7 100644
--- a/awx/ui/test/spec/job-results/job-results.controller-test.js
+++ b/awx/ui/test/spec/job-results/job-results.controller-test.js
@@ -393,19 +393,6 @@ describe('Controller: jobResultsController', () => {
});
});
- describe('$scope.relaunchJob', () => {
- beforeEach(() => {
- bootstrapTest();
- });
-
- it('should relaunch the job', () => {
- let scope = $scope;
- $scope.relaunchJob();
- expect(jobResultsService.relaunchJob)
- .toHaveBeenCalledWith(scope);
- });
- });
-
describe('count stuff', () => {
beforeEach(() => {
count = {
diff --git a/awx/ui/test/spec/workflow--results/workflow-results.controller-test.js b/awx/ui/test/spec/workflow--results/workflow-results.controller-test.js
index 91616b8a03..a6c89201a6 100644
--- a/awx/ui/test/spec/workflow--results/workflow-results.controller-test.js
+++ b/awx/ui/test/spec/workflow--results/workflow-results.controller-test.js
@@ -18,7 +18,7 @@ describe('Controller: workflowResults', () => {
beforeEach(angular.mock.module('workflowResults', ($provide) => {
['PromptDialog', 'Prompt', 'Wait', 'Rest', '$state', 'ProcessErrors',
- 'InitiatePlaybookRun', 'jobLabels', 'workflowNodes', 'count',
+ 'jobLabels', 'workflowNodes', 'count',
].forEach((item) => {
$provide.value(item, {});
});
diff --git a/awx/ui/test/spec/workflow--results/workflow-results.service-test.js b/awx/ui/test/spec/workflow--results/workflow-results.service-test.js
index 8f0e2ad843..45d1841ec1 100644
--- a/awx/ui/test/spec/workflow--results/workflow-results.service-test.js
+++ b/awx/ui/test/spec/workflow--results/workflow-results.service-test.js
@@ -6,7 +6,7 @@ describe('workflowResultsService', () => {
let $interval;
beforeEach(angular.mock.module('workflowResults', ($provide) => {
- ['PromptDialog', 'Prompt', 'Wait', 'Rest', 'ProcessErrors', 'InitiatePlaybookRun', '$state'].forEach(function(item) {
+ ['PromptDialog', 'Prompt', 'Wait', 'Rest', 'ProcessErrors', '$state'].forEach(function(item) {
$provide.value(item, {});
});
$provide.value('$stateExtender', { addState: jasmine.createSpy('addState'), });