github.com/emate/nomad@v0.8.2-wo-binpacking/ui/app/components/task-log.js (about) 1 import { inject as service } from '@ember/service'; 2 import Component from '@ember/component'; 3 import { computed } from '@ember/object'; 4 import { run } from '@ember/runloop'; 5 import RSVP from 'rsvp'; 6 import { task } from 'ember-concurrency'; 7 import { logger } from 'nomad-ui/utils/classes/log'; 8 import WindowResizable from 'nomad-ui/mixins/window-resizable'; 9 import timeout from 'nomad-ui/utils/timeout'; 10 11 export default Component.extend(WindowResizable, { 12 token: service(), 13 14 classNames: ['boxed-section', 'task-log'], 15 16 allocation: null, 17 task: null, 18 19 // When true, request logs from the server agent 20 useServer: false, 21 22 // When true, logs cannot be fetched from either the client or the server 23 noConnection: false, 24 25 clientTimeout: 1000, 26 serverTimeout: 5000, 27 28 didReceiveAttrs() { 29 if (this.get('allocation') && this.get('task')) { 30 this.send('toggleStream'); 31 } 32 }, 33 34 didInsertElement() { 35 this.fillAvailableHeight(); 36 }, 37 38 windowResizeHandler() { 39 run.once(this, this.fillAvailableHeight); 40 }, 41 42 fillAvailableHeight() { 43 // This math is arbitrary and far from bulletproof, but the UX 44 // of having the log window fill available height is worth the hack. 45 const cliWindow = this.$('.cli-window'); 46 cliWindow.height(window.innerHeight - cliWindow.offset().top - 25); 47 }, 48 49 mode: 'stdout', 50 51 logUrl: computed('allocation.id', 'allocation.node.httpAddr', 'useServer', function() { 52 const address = this.get('allocation.node.httpAddr'); 53 const allocation = this.get('allocation.id'); 54 55 const url = `/v1/client/fs/logs/${allocation}`; 56 return this.get('useServer') ? url : `//${address}${url}`; 57 }), 58 59 logParams: computed('task', 'mode', function() { 60 return { 61 task: this.get('task'), 62 type: this.get('mode'), 63 }; 64 }), 65 66 logger: logger('logUrl', 'logParams', function logFetch() { 67 // If the log request can't settle in one second, the client 68 // must be unavailable and the server should be used instead 69 const timing = this.get('useServer') ? this.get('serverTimeout') : this.get('clientTimeout'); 70 return url => 71 RSVP.race([this.get('token').authorizedRequest(url), timeout(timing)]).then( 72 response => response, 73 error => { 74 if (this.get('useServer')) { 75 this.set('noConnection', true); 76 } else { 77 this.send('failoverToServer'); 78 this.get('stream').perform(); 79 } 80 throw error; 81 } 82 ); 83 }), 84 85 head: task(function*() { 86 yield this.get('logger.gotoHead').perform(); 87 run.scheduleOnce('afterRender', () => { 88 this.$('.cli-window').scrollTop(0); 89 }); 90 }), 91 92 tail: task(function*() { 93 yield this.get('logger.gotoTail').perform(); 94 run.scheduleOnce('afterRender', () => { 95 const cliWindow = this.$('.cli-window'); 96 cliWindow.scrollTop(cliWindow[0].scrollHeight); 97 }); 98 }), 99 100 stream: task(function*() { 101 this.get('logger').on('tick', () => { 102 run.scheduleOnce('afterRender', () => { 103 const cliWindow = this.$('.cli-window'); 104 cliWindow.scrollTop(cliWindow[0].scrollHeight); 105 }); 106 }); 107 108 yield this.get('logger').startStreaming(); 109 this.get('logger').off('tick'); 110 }), 111 112 willDestroy() { 113 this.get('logger').stop(); 114 }, 115 116 actions: { 117 setMode(mode) { 118 this.get('logger').stop(); 119 this.set('mode', mode); 120 this.get('stream').perform(); 121 }, 122 toggleStream() { 123 if (this.get('logger.isStreaming')) { 124 this.get('logger').stop(); 125 } else { 126 this.get('stream').perform(); 127 } 128 }, 129 failoverToServer() { 130 this.set('useServer', true); 131 }, 132 }, 133 });