Merge pull request #4350 from jaredevantabor/workflow-status-bar

Workflow status bar for completed jobs
This commit is contained in:
Jared Tabor 2016-12-08 16:08:26 -05:00 committed by GitHub
commit 3a3f7a6f81
11 changed files with 118 additions and 86 deletions

View File

@ -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) {

View File

@ -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

View File

@ -43,7 +43,7 @@ export default
ngClick: "viewJobDetails(job)",
badgePlacement: 'right',
badgeCustom: true,
badgeIcon: `<a href="{{ workflow_result_link }}"
badgeIcon: `<a href="{{ job.workflow_result_link }}"
aw-tool-tip="View workflow results"
data-placement="top"
data-original-title="" title="">

View File

@ -93,12 +93,6 @@ export default
// ex: 'ws-jobs-<jobId>'
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-<jobId>'
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

View File

@ -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;

View File

@ -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");
}
});
}];

View File

@ -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) {

View File

@ -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',

View File

@ -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;
}

View File

@ -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`] = `<span class='WorkflowStatusBar-tooltipLabel'>${key}</span><span class='badge WorkflowStatusBar-tooltipBadge WorkflowStatusBar-tooltipBadge--${key}'>${val[key]}</span>`;

View File

@ -1,26 +1,14 @@
<div class="WorkflowStatusBar">
<div class="WorkflowStatusBar-ok"
<div class="WorkflowStatusBar-successful"
data-placement="top"
aw-tool-tip="{{okCountTip}}"
data-tip-watch="okCountTip"></div>
<div class="WorkflowStatusBar-changed"
aw-tool-tip="{{successfulCountTip}}"
data-tip-watch="successfulCountTip"></div>
<div class="WorkflowStatusBar-failed"
data-placement="top"
aw-tool-tip="{{changedCountTip}}"
data-tip-watch="changedCountTip"></div>
<div class="WorkflowStatusBar-failures"
data-placement="top"
aw-tool-tip="{{failuresCountTip}}"
data-tip-watch="failuresCountTip"></div>
<div class="WorkflowStatusBar-unreachable"
data-placement="top"
aw-tool-tip="{{unreachableCountTip}}"
data-tip-watch="unreachableCountTip"></div>
<div class="WorkflowStatusBar-skipped"
data-placement="top"
aw-tool-tip="{{skippedCountTip}}"
data-tip-watch="skippedCountTip"></div>
aw-tool-tip="{{failedCountTip}}"
data-tip-watch="failedCountTip"></div>
<div class="WorkflowStatusBar-noData"
aw-tool-tip="NO HOSTS FINISHED"
aw-tool-tip="NO JOBS FINISHED"
ng-hide="hostsFinished"
data-placement="top"></div>
</div>