github.com/aminovpavel/nomad@v0.11.8/ui/app/components/fs/file.js (about) 1 import { inject as service } from '@ember/service'; 2 import Component from '@ember/component'; 3 import { computed } from '@ember/object'; 4 import { gt } from '@ember/object/computed'; 5 import { equal } from '@ember/object/computed'; 6 import RSVP from 'rsvp'; 7 import Log from 'nomad-ui/utils/classes/log'; 8 import timeout from 'nomad-ui/utils/timeout'; 9 10 export default Component.extend({ 11 token: service(), 12 13 classNames: ['boxed-section', 'task-log'], 14 15 'data-test-file-viewer': true, 16 17 allocation: null, 18 task: null, 19 file: null, 20 stat: null, // { Name, IsDir, Size, FileMode, ModTime, ContentType } 21 22 // When true, request logs from the server agent 23 useServer: false, 24 25 // When true, logs cannot be fetched from either the client or the server 26 noConnection: false, 27 28 clientTimeout: 1000, 29 serverTimeout: 5000, 30 31 mode: 'head', 32 33 fileComponent: computed('stat.ContentType', function() { 34 const contentType = this.stat.ContentType || ''; 35 36 if (contentType.startsWith('image/')) { 37 return 'image'; 38 } else if (contentType.startsWith('text/') || contentType.startsWith('application/json')) { 39 return 'stream'; 40 } else { 41 return 'unknown'; 42 } 43 }), 44 45 isLarge: gt('stat.Size', 50000), 46 47 fileTypeIsUnknown: equal('fileComponent', 'unknown'), 48 isStreamable: equal('fileComponent', 'stream'), 49 isStreaming: false, 50 51 catUrl: computed('allocation.id', 'task.name', 'file', function() { 52 const taskUrlPrefix = this.task ? `${this.task.name}/` : ''; 53 const encodedPath = encodeURIComponent(`${taskUrlPrefix}${this.file}`); 54 return `/v1/client/fs/cat/${this.allocation.id}?path=${encodedPath}`; 55 }), 56 57 fetchMode: computed('isLarge', 'mode', function() { 58 if (this.mode === 'streaming') { 59 return 'stream'; 60 } 61 62 if (!this.isLarge) { 63 return 'cat'; 64 } else if (this.mode === 'head' || this.mode === 'tail') { 65 return 'readat'; 66 } 67 }), 68 69 fileUrl: computed( 70 'allocation.id', 71 'allocation.node.httpAddr', 72 'fetchMode', 73 'useServer', 74 function() { 75 const address = this.get('allocation.node.httpAddr'); 76 const url = `/v1/client/fs/${this.fetchMode}/${this.allocation.id}`; 77 return this.useServer ? url : `//${address}${url}`; 78 } 79 ), 80 81 fileParams: computed('task.name', 'file', 'mode', function() { 82 // The Log class handles encoding query params 83 const taskUrlPrefix = this.task ? `${this.task.name}/` : ''; 84 const path = `${taskUrlPrefix}${this.file}`; 85 86 switch (this.mode) { 87 case 'head': 88 return { path, offset: 0, limit: 50000 }; 89 case 'tail': 90 return { path, offset: this.stat.Size - 50000, limit: 50000 }; 91 case 'streaming': 92 return { path, offset: 50000, origin: 'end' }; 93 default: 94 return { path }; 95 } 96 }), 97 98 logger: computed('fileUrl', 'fileParams', 'mode', function() { 99 // The cat and readat APIs are in plainText while the stream API is always encoded. 100 const plainText = this.mode === 'head' || this.mode === 'tail'; 101 102 // If the file request can't settle in one second, the client 103 // must be unavailable and the server should be used instead 104 const timing = this.useServer ? this.serverTimeout : this.clientTimeout; 105 const logFetch = url => 106 RSVP.race([this.token.authorizedRequest(url), timeout(timing)]).then( 107 response => { 108 if (!response || !response.ok) { 109 this.nextErrorState(response); 110 } 111 return response; 112 }, 113 error => this.nextErrorState(error) 114 ); 115 116 return Log.create({ 117 logFetch, 118 plainText, 119 params: this.fileParams, 120 url: this.fileUrl, 121 }); 122 }), 123 124 nextErrorState(error) { 125 if (this.useServer) { 126 this.set('noConnection', true); 127 } else { 128 this.send('failoverToServer'); 129 } 130 throw error; 131 }, 132 133 actions: { 134 toggleStream() { 135 this.set('mode', 'streaming'); 136 this.toggleProperty('isStreaming'); 137 }, 138 gotoHead() { 139 this.set('mode', 'head'); 140 this.set('isStreaming', false); 141 }, 142 gotoTail() { 143 this.set('mode', 'tail'); 144 this.set('isStreaming', false); 145 }, 146 failoverToServer() { 147 this.set('useServer', true); 148 }, 149 }, 150 });