make prior output viewable for partial job runs

This commit is contained in:
Jake McDermott
2018-05-21 11:25:47 -04:00
parent 86616c4062
commit 2cbadcd392
5 changed files with 104 additions and 45 deletions

View File

@@ -29,7 +29,7 @@ function JobEventsApiService ($http, $q) {
this.getLastPage = count => Math.ceil(count / this.state.params.page_size); this.getLastPage = count => Math.ceil(count / this.state.params.page_size);
this.fetch = () => { this.clearCache = () => {
delete this.cache; delete this.cache;
delete this.keys; delete this.keys;
delete this.pageSizes; delete this.pageSizes;
@@ -37,10 +37,10 @@ function JobEventsApiService ($http, $q) {
this.cache = {}; this.cache = {};
this.keys = []; this.keys = [];
this.pageSizes = {}; this.pageSizes = {};
return this.getPage(1).then(() => this);
}; };
this.fetch = () => this.first().then(() => this);
this.getPage = number => { this.getPage = number => {
if (number < 1 || number > this.state.last) { if (number < 1 || number > this.state.last) {
return $q.resolve(); return $q.resolve();
@@ -79,11 +79,18 @@ function JobEventsApiService ($http, $q) {
return { results, page: number }; return { results, page: number };
}); });
if (number === 1) {
this.clearCache();
}
this.cache[number] = promise; this.cache[number] = promise;
this.keys.push(number); this.keys.push(number);
if (this.keys.length > PAGE_LIMIT) { if (this.keys.length > PAGE_LIMIT) {
delete this.cache[this.keys.shift()]; const remove = this.keys.shift();
delete this.cache[remove];
delete this.pageSizes[remove];
} }
return promise; return promise;
@@ -107,17 +114,22 @@ function JobEventsApiService ($http, $q) {
const { results, count } = data; const { results, count } = data;
const lastPage = this.getLastPage(count); const lastPage = this.getLastPage(count);
results.reverse(); if (count > PAGE_SIZE) {
const shifted = results.splice(count % PAGE_SIZE); results.splice(count % PAGE_SIZE);
}
this.state.results = shifted; results.reverse();
this.state.results = results;
this.state.count = count; this.state.count = count;
this.state.page = lastPage; this.state.page = lastPage;
this.state.next = lastPage; this.state.next = lastPage;
this.state.last = lastPage; this.state.last = lastPage;
this.state.previous = Math.max(1, this.state.page - 1); this.state.previous = Math.max(1, this.state.page - 1);
return { results: shifted, page: lastPage }; this.clearCache();
return { results, page: lastPage };
}); });
return promise; return promise;

View File

@@ -73,9 +73,8 @@ function JobEventEngine ($q) {
this.buffer = data => { this.buffer = data => {
const pageAdded = this.page.addToBuffer(data); const pageAdded = this.page.addToBuffer(data);
this.pageCount++;
if (pageAdded) { if (pageAdded) {
this.pageCount++;
this.setBatchFrameCount(); this.setBatchFrameCount();
if (this.isPausing()) { if (this.isPausing()) {
@@ -117,6 +116,9 @@ function JobEventEngine ($q) {
this.chain = this.chain this.chain = this.chain
.then(() => { .then(() => {
if (!this.isActive()) { if (!this.isActive()) {
if (data.start_line < (this.lines.min)) {
return $q.resolve();
}
this.start(); this.start();
} else if (data.event === JOB_END) { } else if (data.event === JOB_END) {
if (this.isPaused()) { if (this.isPaused()) {
@@ -146,6 +148,10 @@ function JobEventEngine ($q) {
this.renderFrame = events => this.hooks.onEventFrame(events) this.renderFrame = events => this.hooks.onEventFrame(events)
.then(() => { .then(() => {
if (this.scroll.isLocked()) {
this.scroll.scrollToBottom();
}
if (this.isEnding()) { if (this.isEnding()) {
const lastEvents = this.page.emptyBuffer(); const lastEvents = this.page.emptyBuffer();

View File

@@ -50,8 +50,8 @@ function JobsIndexController (
// Stdout Navigation // Stdout Navigation
vm.scroll = { vm.scroll = {
showBackToTop: false, showBackToTop: false,
home: scrollHome, home: scrollFirst,
end: scrollEnd, end: scrollLast,
down: scrollPageDown, down: scrollPageDown,
up: scrollPageUp up: scrollPageUp
}; };
@@ -97,7 +97,14 @@ function init () {
}); });
streaming = false; streaming = false;
return next().then(() => startListening());
if (status.state.running) {
return scrollLast().then(() => startListening());
} else if (!status.state.finished) {
return scrollFirst().then(() => startListening());
}
return scrollLast();
} }
function stopListening () { function stopListening () {
@@ -117,30 +124,13 @@ function handleStatusEvent (data) {
function handleJobEvent (data) { function handleJobEvent (data) {
streaming = streaming || attachToRunningJob(); streaming = streaming || attachToRunningJob();
streaming.then(() => { streaming.then(() => {
engine.pushJobEvent(data); engine.pushJobEvent(data);
status.pushJobEvent(data); status.pushJobEvent(data);
}); });
} }
function attachToRunningJob () {
if (!status.state.running) {
return $q.resolve();
}
return page.last()
.then(events => {
if (!events) {
return $q.resolve();
}
const minLine = 1 + Math.max(...events.map(event => event.end_line));
return render.clear()
.then(() => engine.setMinLine(minLine));
});
}
function next () { function next () {
return page.next() return page.next()
.then(events => { .then(events => {
@@ -217,8 +207,16 @@ function shift () {
return render.shift(lines); return render.shift(lines);
} }
function scrollHome () { function scrollFirst () {
if (scroll.isPaused()) { if (engine.isActive()) {
if (engine.isTransitioning()) {
return $q.resolve();
}
if (!engine.isPaused()) {
engine.pause(true);
}
} else if (scroll.isPaused()) {
return $q.resolve(); return $q.resolve();
} }
@@ -246,19 +244,57 @@ function scrollHome () {
}); });
} }
function scrollEnd () { function scrollLast () {
if (engine.isActive()) { if (engine.isActive()) {
if (engine.isTransitioning()) { if (engine.isTransitioning()) {
return $q.resolve(); return $q.resolve();
} }
if (engine.isPaused()) { if (engine.isPaused()) {
engine.resume(); engine.resume(true);
} else { }
engine.pause(); } else if (scroll.isPaused()) {
return $q.resolve();
}
scroll.pause();
return render.clear()
.then(() => page.last())
.then(events => {
if (!events) {
return $q.resolve();
}
const minLine = 1 + Math.max(...events.map(event => event.end_line));
engine.setMinLine(minLine);
return append(events);
})
.then(() => {
if (!engine.isActive()) {
scroll.resume();
}
scroll.setScrollPosition(scroll.getScrollHeight());
})
.then(() => {
if (!engine.isActive() && scroll.isMissing()) {
return previous();
}
return $q.resolve();
});
}
function attachToRunningJob () {
if (engine.isActive()) {
if (engine.isTransitioning()) {
return $q.resolve();
} }
return $q.resolve(); if (engine.isPaused()) {
engine.resume(true);
}
} else if (scroll.isPaused()) { } else if (scroll.isPaused()) {
return $q.resolve(); return $q.resolve();
} }
@@ -271,12 +307,13 @@ function scrollEnd () {
return $q.resolve(); return $q.resolve();
} }
return render.clear() const minLine = 1 + Math.max(...events.map(event => event.end_line));
.then(() => append(events)); engine.setMinLine(minLine);
return append(events);
}) })
.then(() => { .then(() => {
scroll.setScrollPosition(scroll.getScrollHeight()); scroll.setScrollPosition(scroll.getScrollHeight());
scroll.resume();
}); });
} }

View File

@@ -17,7 +17,7 @@ function JobPageService ($q) {
this.bookmark = { this.bookmark = {
pending: false, pending: false,
set: false, set: true,
cache: [], cache: [],
state: { state: {
count: 0, count: 0,

View File

@@ -4,7 +4,9 @@ const PLAY_START = 'playbook_on_play_start';
const TASK_START = 'playbook_on_task_start'; const TASK_START = 'playbook_on_task_start';
const HOST_STATUS_KEYS = ['dark', 'failures', 'changed', 'ok', 'skipped']; const HOST_STATUS_KEYS = ['dark', 'failures', 'changed', 'ok', 'skipped'];
const FINISHED = ['successful', 'failed', 'error']; const COMPLETE = ['successful', 'failed'];
const INCOMPLETE = ['canceled', 'error'];
const FINISHED = COMPLETE.concat(INCOMPLETE);
function JobStatusService (moment, message) { function JobStatusService (moment, message) {
this.dispatch = () => message.dispatch('status', this.state); this.dispatch = () => message.dispatch('status', this.state);
@@ -105,8 +107,10 @@ function JobStatusService (moment, message) {
} }
}; };
this.isExpectingStatsEvent = () => (this.jobType === 'job') || this.isExpectingStatsEvent = () => (
(this.jobType === 'project_update'); (this.jobType === 'job') ||
(this.jobType === 'project_update')) &&
(!_.includes(INCOMPLETE, this.state.status));
this.updateStats = () => { this.updateStats = () => {
this.updateHostCounts(); this.updateHostCounts();