Refactored and image support of the task-file component

This commit is contained in:
Michael Lange 2019-07-02 13:01:07 -07:00
parent 29dacd0c2a
commit f2d31fdf1a
2 changed files with 82 additions and 79 deletions

View file

@ -13,6 +13,7 @@ export default Component.extend({
allocation: null, allocation: null,
task: null, task: null,
file: null, file: null,
stat: null, // { Name, IsDir, Size, FileMode, ModTime, ContentType }
// When true, request logs from the server agent // When true, request logs from the server agent
useServer: false, useServer: false,
@ -23,49 +24,78 @@ export default Component.extend({
clientTimeout: 1000, clientTimeout: 1000,
serverTimeout: 5000, serverTimeout: 5000,
didReceiveAttrs() { mode: 'head',
if (this.allocation && this.task) {
// this.send('toggleStream'); fileComponent: computed('stat', function() {
// TODO: Switch to this.stat.ContentType
// TODO: Determine binary/unsupported non-text files to set to "cannot view" component
const matches = this.stat.Name.match(/^.+?\.(.+)$/);
const ext = matches ? matches[1] : '';
switch (ext) {
case 'jpg':
case 'jpeg':
case 'gif':
case 'png':
return 'image';
default:
return 'stream';
} }
}, }),
didInsertElement() { isLarge: computed('stat', function() {
this.fillAvailableHeight(); return this.stat.Size > 50000;
}, }),
windowResizeHandler() { isStreamable: computed('stat', function() {
run.once(this, this.fillAvailableHeight); return false;
}, return this.stat.ContentType.startsWith('text/');
}),
fillAvailableHeight() { isStreaming: false,
// This math is arbitrary and far from bulletproof, but the UX
// of having the log window fill available height is worth the hack.
const margins = 30 + 30; // Account for padding and margin on either side of the CLI
const cliWindow = this.$('.cli-window');
cliWindow.height(window.innerHeight - cliWindow.offset().top - margins);
},
fileUrl: computed('task.name', 'allocation.id', 'file', function() { catUrl: computed('allocation.id', 'task.name', 'file', function() {
return `/v1/client/fs/cat/${this.allocation.id}?path=${this.task.name}/${this.file}`; return `/v1/client/fs/cat/${this.allocation.id}?path=${this.task.name}/${this.file}`;
}), }),
logUrl: computed('allocation.id', 'allocation.node.httpAddr', 'useServer', function() { fetchMode: computed('isLarge', 'mode', function() {
const address = this.get('allocation.node.httpAddr'); if (!this.isLarge) {
const allocation = this.get('allocation.id'); return 'cat';
} else if (this.mode === 'head') {
return 'readat';
}
const url = `/v1/client/fs/logs/${allocation}`; return 'stream';
return this.useServer ? url : `//${address}${url}`;
}), }),
logParams: computed('task', 'mode', function() { fileUrl: computed(
return { 'allocation.id',
task: this.task, 'allocation.node.httpAddr',
type: this.mode, 'fetchMode',
}; 'useServer',
function() {
const address = this.get('allocation.node.httpAddr');
const url = `/v1/client/fs/${this.fetchMode}/${this.allocation.id}`;
return this.useServer ? url : `//${address}${url}`;
}
),
fileParams: computed('task.name', 'file', 'mode', function() {
const path = `${this.task.name}/${this.file}`;
switch (this.mode) {
case 'head':
return { path, offset: 0, limit: 50000 };
case 'tail':
case 'stream':
return { path, offset: 50000, origin: 'end' };
default:
return { path };
}
}), }),
logger: logger('logUrl', 'logParams', function logFetch() { logger: logger('fileUrl', 'fileParams', function logFetch() {
// If the log request can't settle in one second, the client // If the file request can't settle in one second, the client
// must be unavailable and the server should be used instead // must be unavailable and the server should be used instead
const timing = this.useServer ? this.serverTimeout : this.clientTimeout; const timing = this.useServer ? this.serverTimeout : this.clientTimeout;
return url => return url =>
@ -83,49 +113,17 @@ export default Component.extend({
); );
}), }),
head: task(function*() {
yield this.get('logger.gotoHead').perform();
run.scheduleOnce('afterRender', () => {
this.$('.cli-window').scrollTop(0);
});
}),
tail: task(function*() {
yield this.get('logger.gotoTail').perform();
run.scheduleOnce('afterRender', () => {
const cliWindow = this.$('.cli-window');
cliWindow.scrollTop(cliWindow[0].scrollHeight);
});
}),
stream: task(function*() {
this.logger.on('tick', () => {
run.scheduleOnce('afterRender', () => {
const cliWindow = this.$('.cli-window');
cliWindow.scrollTop(cliWindow[0].scrollHeight);
});
});
yield this.logger.startStreaming();
this.logger.off('tick');
}),
willDestroy() {
this.logger.stop();
},
actions: { actions: {
setMode(mode) {
this.logger.stop();
this.set('mode', mode);
this.stream.perform();
},
toggleStream() { toggleStream() {
if (this.get('logger.isStreaming')) { this.toggleProperty('isStreaming');
this.logger.stop(); },
} else { gotoHead() {
this.stream.perform(); this.set('mode', 'head');
} this.set('isStreaming', false);
},
gotoTail() {
this.set('mode', 'tail');
this.set('isStreaming', false);
}, },
failoverToServer() { failoverToServer() {
this.set('useServer', true); this.set('useServer', true);

View file

@ -6,19 +6,24 @@
{{/if}} {{/if}}
<div class="boxed-section-head"> <div class="boxed-section-head">
<span class="pull-right"> <span class="pull-right">
<a data-test-log-action="raw" class="button is-white" href="{{fileUrl}}" target="_blank" rel="noopener noreferrer">View Raw File</a> <a data-test-log-action="raw" class="button is-white" href="{{catUrl}}" target="_blank" rel="noopener noreferrer">View Raw File</a>
{{#if isLarge}} {{#if (and isLarge isStreamable)}}
<button data-test-log-action="head" class="button is-white" onclick={{perform head}}>Head</button> <button data-test-log-action="head" class="button is-white" onclick={{action "gotoHead"}}>Head</button>
<button data-test-log-action="tail" class="button is-white" onclick={{perform tail}}>Tail</button> <button data-test-log-action="tail" class="button is-white" onclick={{action "gotoTail"}}>Tail</button>
{{/if}} {{/if}}
{{#if isStreaming}} {{#if isStreamable}}
<button data-test-log-action="toggle-stream" class="button is-white" onclick={{action "toggleStream"}}> <button data-test-log-action="toggle-stream" class="button is-white" onclick={{action "toggleStream"}}>
{{x-icon (if logger.isStreaming "media-pause" "media-play") class="is-text"}} {{x-icon (if logger.isStreaming "media-pause" "media-play") class="is-text"}}
</button> </button>
{{/if}} {{/if}}
</span> </span>
</div> </div>
<div data-test-log-box class="boxed-section-body is-dark is-full-bleed"> <div data-test-file-box class="boxed-section-body {{if (eq fileComponent "stream") "is-dark is-full-bleed"}}">
{{!-- switch file component here --}} {{#if (eq fileComponent "stream")}}
<pre data-test-log-cli class="cli-window"><code>{{logger.output}}</code></pre> {{streaming-file logger=logger mode=mode isStreaming=isStreaming}}
{{else if (eq fileComponent "image")}}
{{image-file src=catUrl alt=stat.Name size=stat.Size}}
{{else}}
<h1>No component</h1>
{{/if}}
</div> </div>