From fd610962911498cfc712818df4ab15e3f39da408 Mon Sep 17 00:00:00 2001 From: jaredevantabor Date: Thu, 1 Dec 2016 17:19:22 -0800 Subject: [PATCH] Workflow status bar for completed jobs adjusting workflow results link for job standard out views (job results, projects, inventories, and jobs list) Enhancing workflow status bar for running jobs removing workflow_events group from the UI adding comment fix for updating while job is running removing pending from the status bar, and reload the page on job finish --- awx/ui/client/src/controllers/Jobs.js | 5 +- .../src/job-results/job-results.controller.js | 12 ++++- awx/ui/client/src/lists/AllJobs.js | 2 +- .../src/shared/socket/socket.service.js | 6 --- .../standard-out/standard-out.controller.js | 4 ++ .../workflow-results.controller.js | 49 +++++++++++++------ .../workflow-results.route.js | 15 +++++- .../workflow-results.service.js | 25 ++++++++++ .../workflow-status-bar.block.less | 48 ++++++------------ .../workflow-status-bar.directive.js | 12 ++--- .../workflow-status-bar.partial.html | 26 +++------- 11 files changed, 118 insertions(+), 86 deletions(-) diff --git a/awx/ui/client/src/controllers/Jobs.js b/awx/ui/client/src/controllers/Jobs.js index 9dce01c863..c68c6c5826 100644 --- a/awx/ui/client/src/controllers/Jobs.js +++ b/awx/ui/client/src/controllers/Jobs.js @@ -36,7 +36,10 @@ export function JobsListController($state, $rootScope, $log, $scope, $compile, $ $scope.removeChoicesReady = $scope.$on('choicesReady', function() { $scope[list.name].forEach(function(item, item_idx) { var itm = $scope[list.name][item_idx]; - + if(item.summary_fields && item.summary_fields.source_workflow_job && + item.summary_fields.source_workflow_job.id){ + item.workflow_result_link = `/#/workflows/${item.summary_fields.source_workflow_job.id}`; + } // Set the item type label if (list.fields.type) { $scope.type_choices.every(function(choice) { 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 1c98dd3895..b2f83ede02 100644 --- a/awx/ui/client/src/job-results/job-results.controller.js +++ b/awx/ui/client/src/job-results/job-results.controller.js @@ -64,17 +64,25 @@ export default ['jobData', 'jobDataOptions', 'jobLabels', 'jobFinished', 'count' // turn related api browser routes into tower routes getTowerLinks(); + + // the links below can't be set in getTowerLinks because the + // links on the UI don't directly match the corresponding URL + // on the API browser if(jobData.summary_fields && jobData.summary_fields.job_template && jobData.summary_fields.job_template.id){ $scope.job_template_link = `/#/templates/job_template/${$scope.job.summary_fields.job_template.id}`; } if(jobData.summary_fields && jobData.summary_fields.project_update && jobData.summary_fields.project_update.status){ - $scope.project_status = jobData.summary_fields.project_update.status; + $scope.project_status = jobData.summary_fields.project_update.status; } if(jobData.summary_fields && jobData.summary_fields.project_update && jobData.summary_fields.project_update.id){ - $scope.project_update_link = `/#/scm_update/${jobData.summary_fields.project_update.id}`; + $scope.project_update_link = `/#/scm_update/${jobData.summary_fields.project_update.id}`; + } + if(jobData.summary_fields && jobData.summary_fields.source_workflow_job && + jobData.summary_fields.source_workflow_job.id){ + $scope.workflow_result_link = `/#/workflows/${jobData.summary_fields.source_workflow_job.id}`; } // use options labels to manipulate display of details diff --git a/awx/ui/client/src/lists/AllJobs.js b/awx/ui/client/src/lists/AllJobs.js index e5a47371c4..152bf1185d 100644 --- a/awx/ui/client/src/lists/AllJobs.js +++ b/awx/ui/client/src/lists/AllJobs.js @@ -43,7 +43,7 @@ export default ngClick: "viewJobDetails(job)", badgePlacement: 'right', badgeCustom: true, - badgeIcon: ` diff --git a/awx/ui/client/src/shared/socket/socket.service.js b/awx/ui/client/src/shared/socket/socket.service.js index d38a6e8e66..b636cc1db8 100644 --- a/awx/ui/client/src/shared/socket/socket.service.js +++ b/awx/ui/client/src/shared/socket/socket.service.js @@ -93,12 +93,6 @@ export default // ex: 'ws-jobs-' str = `ws-${data.group_name}-${data.job}`; } - else if(data.group_name==="workflow_events"){ - // The naming scheme is "ws" then a - // dash (-) and the group_name, then the job ID - // ex: 'ws-jobs-' - str = `ws-${data.group_name}-${data.workflow_job_id}`; - } else if(data.group_name==="ad_hoc_command_events"){ // The naming scheme is "ws" then a // dash (-) and the group_name, then the job ID 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 ffed821d7d..2edfa68abd 100644 --- a/awx/ui/client/src/standard-out/standard-out.controller.js +++ b/awx/ui/client/src/standard-out/standard-out.controller.js @@ -58,6 +58,10 @@ export function JobStdoutController ($rootScope, $scope, $state, $stateParams, $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; diff --git a/awx/ui/client/src/workflow-results/workflow-results.controller.js b/awx/ui/client/src/workflow-results/workflow-results.controller.js index d80385141c..af94f001c3 100644 --- a/awx/ui/client/src/workflow-results/workflow-results.controller.js +++ b/awx/ui/client/src/workflow-results/workflow-results.controller.js @@ -7,6 +7,8 @@ export default ['workflowData', 'ParseTypeChange', 'ParseVariableString', 'WorkflowService', + 'count', + '$state', function(workflowData, workflowResultsService, workflowDataOptions, @@ -15,7 +17,9 @@ export default ['workflowData', $scope, ParseTypeChange, ParseVariableString, - WorkflowService + WorkflowService, + count, + $state ) { var getTowerLinks = function() { @@ -57,6 +61,7 @@ export default ['workflowData', $scope.workflow_nodes = workflowNodes; $scope.workflowOptions = workflowDataOptions.actions.GET; $scope.labels = jobLabels; + $scope.count = count.val; // turn related api browser routes into tower routes getTowerLinks(); @@ -113,22 +118,38 @@ export default ['workflowData', init(); - $scope.$on(`ws-workflow_events-${$scope.workflow.id}`, function(e, data) { - - WorkflowService.updateStatusOfNode({ - treeData: $scope.treeData, - nodeId: data.workflow_node_id, - status: data.status, - unified_job_id: data.unified_job_id - }); - - $scope.$broadcast("refreshWorkflowChart"); - }); - // Processing of job-status messages from the websocket $scope.$on(`ws-jobs`, function(e, data) { + // Update the workflow job's unified job: if (parseInt(data.unified_job_id, 10) === parseInt($scope.workflow.id,10)) { - $scope.workflow.status = data.status; + $scope.workflow.status = data.status; + + if(data.status === "successful" || data.status === "failed"){ + $state.go('.', null, { reload: true }); + } + } + // Update the jobs spawned by the workflow: + if(data.hasOwnProperty('workflow_job_id') && + parseInt(data.workflow_job_id, 10) === parseInt($scope.workflow.id,10)){ + + WorkflowService.updateStatusOfNode({ + treeData: $scope.treeData, + nodeId: data.workflow_node_id, + status: data.status, + unified_job_id: data.unified_job_id + }); + + $scope.workflow_nodes.forEach(node => { + if(parseInt(node.id) === parseInt(data.workflow_node_id)){ + node.summary_fields.job = { + status: data.status + }; + } + }); + + $scope.count = workflowResultsService + .getCounts($scope.workflow_nodes); + $scope.$broadcast("refreshWorkflowChart"); } }); }]; diff --git a/awx/ui/client/src/workflow-results/workflow-results.route.js b/awx/ui/client/src/workflow-results/workflow-results.route.js index 70b54c940d..904584ec25 100644 --- a/awx/ui/client/src/workflow-results/workflow-results.route.js +++ b/awx/ui/client/src/workflow-results/workflow-results.route.js @@ -18,8 +18,7 @@ export default { data: { socket: { "groups":{ - "jobs": ["status_changed"], - "workflow_events": [] + "jobs": ["status_changed"] } } }, @@ -61,6 +60,18 @@ export default { }); return defer.promise; }], + // after the GET for the workflow & it's nodes, this helps us keep the + // status bar from flashing as rest data comes in. If the workflow + // is finished and there's a playbook_on_stats event, go ahead and + // resolve the count so you don't get that flashing! + count: ['workflowData', 'workflowNodes', 'workflowResultsService', 'Rest', '$q', function(workflowData, workflowNodes, workflowResultsService, Rest, $q) { + var defer = $q.defer(); + defer.resolve({ + val: workflowResultsService + .getCounts(workflowNodes), + countFinished: true}); + return defer.promise; + }], // GET for the particular jobs labels to be displayed in the // left-hand pane jobLabels: ['Rest', 'GetBasePath', '$stateParams', '$q', function(Rest, GetBasePath, $stateParams, $q) { 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 08d38378d8..2d3fddf2f4 100644 --- a/awx/ui/client/src/workflow-results/workflow-results.service.js +++ b/awx/ui/client/src/workflow-results/workflow-results.service.js @@ -7,6 +7,31 @@ export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors', 'InitiatePlaybookRun', function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, InitiatePlaybookRun) { var val = { + getCounts: function(workflowNodes){ + var nodeArr = []; + workflowNodes.forEach(node => { + if(node && node.summary_fields && node.summary_fields.job && node.summary_fields.job.status){ + nodeArr.push(node.summary_fields.job.status); + } + }); + // use the workflow nodes data populate above to get the count + var count = { + successful : _.filter(nodeArr, function(o){ + return o === "successful"; + }), + failed : _.filter(nodeArr, function(o){ + return o === "failed" || o === "error" || o === "canceled"; + }) + }; + + // turn the count into an actual count, rather than a list of + // statuses + Object.keys(count).forEach(key => { + count[key] = count[key].length; + }); + + return count; + }, deleteJob: function(workflow) { Prompt({ hdr: 'Delete Job', diff --git a/awx/ui/client/src/workflow-results/workflow-status-bar/workflow-status-bar.block.less b/awx/ui/client/src/workflow-results/workflow-status-bar/workflow-status-bar.block.less index 38e57d4883..3f9bf3d8f0 100644 --- a/awx/ui/client/src/workflow-results/workflow-status-bar/workflow-status-bar.block.less +++ b/awx/ui/client/src/workflow-results/workflow-status-bar/workflow-status-bar.block.less @@ -5,42 +5,31 @@ flex: 0 0 auto; width: 100%; margin-top: 10px; + margin-bottom: 15px; } -.WorkflowStatusBar-ok, -.WorkflowStatusBar-changed, -.WorkflowStatusBar-unreachable, -.WorkflowStatusBar-failures, -.WorkflowStatusBar-skipped, +.WorkflowStatusBar-successful, +.WorkflowStatusBar-failed, +.WorkflowStatusBar-pending, .WorkflowStatusBar-noData { height: 15px; border-top: 5px solid @default-bg; border-bottom: 5px solid @default-bg; } -.WorkflowStatusBar-ok { +.WorkflowStatusBar-successful { background-color: @default-succ; display: flex; flex: 0 0 auto; } -.WorkflowStatusBar-changed { - background-color: @default-warning; - flex: 0 0 auto; -} - -.WorkflowStatusBar-unreachable { - background-color: @default-unreachable; - flex: 0 0 auto; -} - -.WorkflowStatusBar-failures { +.WorkflowStatusBar-failed { background-color: @default-err; flex: 0 0 auto; } -.WorkflowStatusBar-skipped { - background-color: @default-link; +.WorkflowStatusBar-pending { + background-color: @b7grey; flex: 0 0 auto; } @@ -58,23 +47,14 @@ border-radius: 5px; } -.WorkflowStatusBar-tooltipBadge--ok { +.WorkflowStatusBar-tooltipBadge--successful { background-color: @default-succ; } -.WorkflowStatusBar-tooltipBadge--unreachable { - background-color: @default-unreachable; -} - -.WorkflowStatusBar-tooltipBadge--skipped { - background-color: @default-link; -} - -.WorkflowStatusBar-tooltipBadge--changed { - background-color: @default-warning; -} - -.WorkflowStatusBar-tooltipBadge--failures { +.WorkflowStatusBar-tooltipBadge--failed { background-color: @default-err; - +} + +.WorkflowStatusBar-tooltipBadge--pending { + background-color: @b7grey; } diff --git a/awx/ui/client/src/workflow-results/workflow-status-bar/workflow-status-bar.directive.js b/awx/ui/client/src/workflow-results/workflow-status-bar/workflow-status-bar.directive.js index a6899eb0da..c53fc2dcba 100644 --- a/awx/ui/client/src/workflow-results/workflow-status-bar/workflow-status-bar.directive.js +++ b/awx/ui/client/src/workflow-results/workflow-status-bar/workflow-status-bar.directive.js @@ -4,27 +4,25 @@ * All Rights Reserved *************************************************/ -// import WorkflowStatusBarController from './host-status-bar.controller'; export default [ 'templateUrl', function(templateUrl) { return { scope: true, templateUrl: templateUrl('workflow-results/workflow-status-bar/workflow-status-bar'), restrict: 'E', - // controller: standardOutLogController, link: function(scope) { - // as count is changed by event data coming in, - // update the host status bar + // as count is changed by jobs coming in, + // update the workflow status bar scope.$watch('count', function(val) { if (val) { Object.keys(val).forEach(key => { - // reposition the hosts status bar by setting + // reposition the workflow status bar by setting // the various flex values to the count of - // those hosts + // those jobs $(`.WorkflowStatusBar-${key}`) .css('flex', `${val[key]} 0 auto`); - // set the tooltip to give how many hosts of + // set the tooltip to give how many jobs of // each type if (val[key] > 0) { scope[`${key}CountTip`] = `${key}${val[key]}`; diff --git a/awx/ui/client/src/workflow-results/workflow-status-bar/workflow-status-bar.partial.html b/awx/ui/client/src/workflow-results/workflow-status-bar/workflow-status-bar.partial.html index e0efddc7b6..c2bc7d87a5 100644 --- a/awx/ui/client/src/workflow-results/workflow-status-bar/workflow-status-bar.partial.html +++ b/awx/ui/client/src/workflow-results/workflow-status-bar/workflow-status-bar.partial.html @@ -1,26 +1,14 @@
-
-
+
-
-
-
+ aw-tool-tip="{{failedCountTip}}" + data-tip-watch="failedCountTip">