Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes ui-side behavior in response to websocket job status updates on several lists #5968

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
60a1372
Changes how the jobs list reacts to socket messages. We now only mak…
mabashian Feb 17, 2020
98f5525
added unified job template ID to websocket emits
rebeccahhh Feb 21, 2020
179c62e
Stop making rest calls for project sync updates on projects list
mabashian Feb 20, 2020
e75f7b0
Stop making rest calls for inventory source sync updates on inventory…
mabashian Feb 20, 2020
51a6194
Removes logic performing GET requests on `api/v2/templates` whenever …
mabashian Feb 24, 2020
1fe2846
added finished job timestamp to websocket emit
rebeccahhh Feb 24, 2020
a6cd325
Removes GET requests in response to websocket messages on the org job…
mabashian Feb 24, 2020
6d6d99b
fixed the spelling of cancelled to be canceled, note to us later -> w…
rebeccahhh Feb 24, 2020
3234f24
Consume finished timestamp from websocket message when it's available…
mabashian Feb 24, 2020
db43341
Consume finished timestamp from websocket message and update the rele…
mabashian Feb 24, 2020
d5dd3c5
Consume finished timestamp on org templates list when available via w…
mabashian Feb 24, 2020
b09ac71
Trims down GET requests made on the dashboard in response to websocke…
mabashian Feb 25, 2020
48a6152
Fix jshint errors
mabashian Feb 26, 2020
d3fa34c
Remove tooltip update when job finishes. This will be handled later …
mabashian Feb 26, 2020
e11ff69
Added in check for the unified_job_template_id attribute to be presen…
rebeccahhh Feb 28, 2020
10b5a10
Jobs that error should trigger us to to update the Recent Jobs list a…
mabashian Mar 2, 2020
07752f4
formatted finished time to match microsecond expected output as is in…
rebeccahhh Mar 2, 2020
457dc95
added a check for the field of finished making sure it's not none, an…
rebeccahhh Mar 4, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions awx/main/models/unified_jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

# Python
from io import StringIO
import datetime
import codecs
import json
import logging
Expand Down Expand Up @@ -1218,12 +1219,17 @@ def _websocket_emit_status(self, status):
status_data['instance_group_name'] = self.instance_group.name
else:
status_data['instance_group_name'] = None
elif status in ['successful', 'failed', 'canceled'] and self.finished:
status_data['finished'] = datetime.datetime.strftime(self.finished, "%Y-%m-%dT%H:%M:%S.%fZ")
status_data.update(self.websocket_emit_data())
status_data['group_name'] = 'jobs'
if getattr(self, 'unified_job_template_id', None):
status_data['unified_job_template_id'] = self.unified_job_template_id
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

emit_channel_notification('jobs-status_changed', status_data)

if self.spawned_by_workflow:
status_data['group_name'] = "workflow_events"
status_data['workflow_job_template_id'] = self.unified_job_template.id
emit_channel_notification('workflow_events-' + str(self.workflow_job_id), status_data)
except IOError: # includes socket errors
logger.exception('%s failed to emit channel msg about status change', self.log_format)
Expand Down
113 changes: 85 additions & 28 deletions awx/ui/client/features/jobs/jobsList.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ function ListJobsController (

vm.strings = strings;

let newJobs = [];

// smart-search
const name = 'jobs';
const iterator = 'job';
let paginateQuerySet = {};

let launchModalOpen = false;
let refreshAfterLaunchClose = false;
let pendingRefresh = false;
let refreshTimerRunning = false;
let newJobsTimerRunning = false;

vm.searchBasePath = SearchBasePath;

Expand Down Expand Up @@ -104,23 +104,53 @@ function ListJobsController (
$scope.$emit('updateCount', vm.job_dataset.count, 'jobs');
});

$scope.$on('ws-jobs', () => {
if (!launchModalOpen) {
if (!refreshTimerRunning) {
refreshJobs();
} else {
pendingRefresh = true;
const canAddRowsDynamically = () => {
const orderByValue = _.get($state.params, 'job_search.order_by');
const pageValue = _.get($state.params, 'job_search.page');
const idInValue = _.get($state.params, 'job_search.id__in');

return (!idInValue && (!pageValue || pageValue === '1')
&& (orderByValue === '-finished' || orderByValue === '-started'));
};

const updateJobRow = (msg) => {
// Loop across the jobs currently shown and update the row
// if it exists
for (let i = 0; i < vm.jobs.length; i++) {
if (vm.jobs[i].id === msg.unified_job_id) {
// Update the job status.
vm.jobs[i].status = msg.status;
if (msg.finished) {
vm.jobs[i].finished = msg.finished;
const orderByValue = _.get($state.params, 'job_search.order_by');
if (orderByValue === '-finished') {
// Attempt to sort the rows in the list by their finish
// timestamp in descending order
vm.jobs.sort((a, b) =>
(!b.finished) - (!a.finished)
|| new Date(b.finished) - new Date(a.finished));
}
}
break;
}
} else {
refreshAfterLaunchClose = true;
}
};

$scope.$on('ws-jobs', (e, msg) => {
if (msg.status === 'pending' && canAddRowsDynamically()) {
newJobs.push(msg.unified_job_id);
if (!launchModalOpen && !newJobsTimerRunning) {
fetchNewJobs();
}
} else if (!newJobs.includes(msg.unified_job_id)) {
updateJobRow(msg);
}
});

$scope.$on('launchModalOpen', (evt, isOpen) => {
evt.stopPropagation();
if (!isOpen && refreshAfterLaunchClose) {
refreshAfterLaunchClose = false;
refreshJobs();
if (!isOpen && newJobs.length > 0) {
fetchNewJobs();
}
launchModalOpen = isOpen;
});
Expand Down Expand Up @@ -289,22 +319,49 @@ function ListJobsController (
});
};

function refreshJobs () {
qs.search(SearchBasePath, $state.params.job_search, { 'X-WS-Session-Quiet': true })
const fetchNewJobs = () => {
newJobsTimerRunning = true;
const newJobIdsFilter = newJobs.join(',');
newJobs = [];
const newJobsSearchParams = Object.assign({}, $state.params.job_search);
newJobsSearchParams.count_disabled = 1;
newJobsSearchParams.id__in = newJobIdsFilter;
delete newJobsSearchParams.page_size;
const stringifiedSearchParams = qs.encodeQueryset(newJobsSearchParams, false);
Rest.setUrl(`${vm.searchBasePath}${stringifiedSearchParams}`);
Rest.get()
.then(({ data }) => {
vm.jobs = data.results;
vm.job_dataset = data;
vm.job_dataset.count += data.results.length;
const pageSize = parseInt($state.params.job_search.page_size, 10) || 20;
const joinedJobs = data.results.concat(vm.jobs);
vm.jobs = joinedJobs.length > pageSize
? joinedJobs.slice(0, pageSize)
: joinedJobs;
$timeout(() => {
if (canAddRowsDynamically()) {
if (newJobs.length > 0 && !launchModalOpen) {
fetchNewJobs();
} else {
newJobsTimerRunning = false;
}
} else {
// Bail out - one of [order_by, page, id__in] params has changed since we
// received these new job messages
newJobs = [];
newJobsTimerRunning = false;
}
}, 5000);
})
.catch(({ data, status }) => {
ProcessErrors($scope, data, status, null, {
hdr: strings.get('error.HEADER'),
msg: strings.get('error.CALL', {
path: `${vm.searchBasePath}${stringifiedSearchParams}`,
status
})
});
});
pendingRefresh = false;
refreshTimerRunning = true;
$timeout(() => {
if (pendingRefresh) {
refreshJobs();
} else {
refreshTimerRunning = false;
}
}, 5000);
}
};

vm.isCollapsed = true;

Expand Down
5 changes: 0 additions & 5 deletions awx/ui/client/features/projects/projectsList.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,6 @@ function projectsListController (
// And we found the affected project
$log.debug(`Received event for project: ${project.name}`);
$log.debug(`Status changed to: ${data.status}`);
if (data.status === 'successful' || data.status === 'failed' || data.status === 'canceled') {
reloadList();
} else {
project.scm_update_tooltip = vm.strings.get('update.UPDATE_RUNNING');
}
project.status = data.status;
buildTooltips(project);
}
Expand Down
65 changes: 32 additions & 33 deletions awx/ui/client/features/templates/templatesList.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,13 @@ function ListTemplatesController(
qs,
GetBasePath,
ngToast,
$timeout
) {
const vm = this || {};
const [jobTemplate, workflowTemplate] = resolvedModels;

const choices = workflowTemplate.options('actions.GET.type.choices')
.concat(jobTemplate.options('actions.GET.type.choices'));

let launchModalOpen = false;
let refreshAfterLaunchClose = false;
let pendingRefresh = false;
let refreshTimerRunning = false;
let paginateQuerySet = {};

vm.strings = strings;
Expand Down Expand Up @@ -120,27 +115,41 @@ function ListTemplatesController(
setToolbarSort();
}, true);

$scope.$on(`ws-jobs`, () => {
if (!launchModalOpen) {
if (!refreshTimerRunning) {
refreshTemplates();
} else {
pendingRefresh = true;
$scope.$on(`ws-jobs`, (e, msg) => {
if (msg.unified_job_template_id && vm.templates) {
const template = vm.templates.find((t) => t.id === msg.unified_job_template_id);
if (template) {
if (msg.status === 'pending') {
// This is a new job - add it to the front of the
// recent_jobs array
if (template.summary_fields.recent_jobs.length === 10) {
template.summary_fields.recent_jobs.pop();
}

template.summary_fields.recent_jobs.unshift({
id: msg.unified_job_id,
status: msg.status,
type: msg.type
});
} else {
// This is an update to an existing job. Check to see
// if we have it in our array of recent_jobs
for (let i=0; i<template.summary_fields.recent_jobs.length; i++) {
const recentJob = template.summary_fields.recent_jobs[i];
if (recentJob.id === msg.unified_job_id) {
recentJob.status = msg.status;
if (msg.finished) {
recentJob.finished = msg.finished;
template.last_job_run = msg.finished;
}
break;
}
};
}
}
} else {
refreshAfterLaunchClose = true;
}
});

$scope.$on('launchModalOpen', (evt, isOpen) => {
evt.stopPropagation();
if (!isOpen && refreshAfterLaunchClose) {
refreshAfterLaunchClose = false;
refreshTemplates();
}
launchModalOpen = isOpen;
});

vm.isInvalid = (template) => {
if(isJobTemplate(template)) {
return template.project === null || (template.inventory === null && template.ask_inventory_on_launch === false);
Expand Down Expand Up @@ -265,15 +274,6 @@ function ListTemplatesController(
vm.templates = vm.dataset.results;
})
.finally(() => Wait('stop'));
pendingRefresh = false;
refreshTimerRunning = true;
$timeout(() => {
if (pendingRefresh) {
refreshTemplates();
} else {
refreshTimerRunning = false;
}
}, 5000);
}

function createErrorHandler(path, action) {
Expand Down Expand Up @@ -483,8 +483,7 @@ ListTemplatesController.$inject = [
'Wait',
'QuerySet',
'GetBasePath',
'ngToast',
'$timeout'
'ngToast'
];

export default ListTemplatesController;
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default
templateUrl: templateUrl('home/dashboard/lists/job-templates/job-templates-list')
};

function link(scope, element, attr) {
function link(scope) {

scope.$watch("data", function(data) {
if (data) {
Expand All @@ -22,7 +22,7 @@ export default
scope.noJobTemplates = true;
}
}
});
}, true);

scope.canAddJobTemplate = false;
let url = GetBasePath('job_templates');
Expand Down
Loading