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  });