github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/ui/app/models/allocation.js (about)

     1  import { inject as service } from '@ember/service';
     2  import { computed } from '@ember/object';
     3  import { equal, none } from '@ember/object/computed';
     4  import Model from '@ember-data/model';
     5  import { attr, belongsTo, hasMany } from '@ember-data/model';
     6  import { fragment, fragmentArray } from 'ember-data-model-fragments/attributes';
     7  import isEqual from 'lodash.isequal';
     8  import intersection from 'lodash.intersection';
     9  import shortUUIDProperty from '../utils/properties/short-uuid';
    10  import classic from 'ember-classic-decorator';
    11  
    12  const STATUS_ORDER = {
    13    pending: 1,
    14    running: 2,
    15    complete: 3,
    16    unknown: 4,
    17    failed: 5,
    18    lost: 6,
    19  };
    20  
    21  @classic
    22  export default class Allocation extends Model {
    23    @service token;
    24    @service store;
    25  
    26    @shortUUIDProperty('id') shortId;
    27    @belongsTo('job') job;
    28    @belongsTo('node') node;
    29    @attr('string') namespace;
    30    @attr('string') name;
    31    @attr('string') taskGroupName;
    32    @fragment('resources') resources;
    33    @fragment('resources') allocatedResources;
    34    @attr('number') jobVersion;
    35  
    36    @attr('number') modifyIndex;
    37    @attr('date') modifyTime;
    38  
    39    @attr('number') createIndex;
    40    @attr('date') createTime;
    41  
    42    @attr('string') clientStatus;
    43    @attr('string') desiredStatus;
    44  
    45    @attr healthChecks;
    46  
    47    async getServiceHealth() {
    48      const data = await this.store.adapterFor('allocation').check(this);
    49  
    50      // Compare Results
    51      if (!isEqual(this.healthChecks, data)) {
    52        this.set('healthChecks', data);
    53      }
    54    }
    55  
    56    @computed('')
    57    get plainJobId() {
    58      return JSON.parse(this.belongsTo('job').id())[0];
    59    }
    60  
    61    @computed('clientStatus')
    62    get statusIndex() {
    63      return STATUS_ORDER[this.clientStatus] || 100;
    64    }
    65  
    66    @equal('clientStatus', 'running') isRunning;
    67    @attr('boolean') isMigrating;
    68  
    69    @computed('clientStatus')
    70    get isScheduled() {
    71      return ['pending', 'running'].includes(this.clientStatus);
    72    }
    73  
    74    // An allocation model created from any allocation list response will be lacking
    75    // many properties (some of which can always be null). This is an indicator that
    76    // the allocation needs to be reloaded to get the complete allocation state.
    77    @none('allocationTaskGroup') isPartial;
    78  
    79    // When allocations are server-side rescheduled, a paper trail
    80    // is left linking all reschedule attempts.
    81    @belongsTo('allocation', { inverse: 'nextAllocation' }) previousAllocation;
    82    @belongsTo('allocation', { inverse: 'previousAllocation' }) nextAllocation;
    83  
    84    @hasMany('allocation', { inverse: 'preemptedByAllocation' })
    85    preemptedAllocations;
    86    @belongsTo('allocation', { inverse: 'preemptedAllocations' })
    87    preemptedByAllocation;
    88    @attr('boolean') wasPreempted;
    89  
    90    @belongsTo('evaluation') followUpEvaluation;
    91  
    92    @computed('clientStatus')
    93    get statusClass() {
    94      const classMap = {
    95        pending: 'is-pending',
    96        running: 'is-primary',
    97        complete: 'is-complete',
    98        failed: 'is-error',
    99        lost: 'is-light',
   100        unknown: 'is-unknown',
   101      };
   102  
   103      return classMap[this.clientStatus] || 'is-dark';
   104    }
   105  
   106    @computed('jobVersion', 'job.version')
   107    get isOld() {
   108      return this.jobVersion !== this.get('job.version');
   109    }
   110  
   111    @computed('isOld', 'jobTaskGroup', 'allocationTaskGroup')
   112    get taskGroup() {
   113      if (!this.isOld) return this.jobTaskGroup;
   114      return this.allocationTaskGroup;
   115    }
   116  
   117    @computed('taskGroupName', 'job.taskGroups.[]')
   118    get jobTaskGroup() {
   119      const taskGroups = this.get('job.taskGroups');
   120      return taskGroups && taskGroups.findBy('name', this.taskGroupName);
   121    }
   122  
   123    @fragment('task-group', { defaultValue: null }) allocationTaskGroup;
   124  
   125    @computed('taskGroup.drivers.[]', 'node.unhealthyDriverNames.[]')
   126    get unhealthyDrivers() {
   127      const taskGroupUnhealthyDrivers = this.get('taskGroup.drivers');
   128      const nodeUnhealthyDrivers = this.get('node.unhealthyDriverNames');
   129  
   130      if (taskGroupUnhealthyDrivers && nodeUnhealthyDrivers) {
   131        return intersection(taskGroupUnhealthyDrivers, nodeUnhealthyDrivers);
   132      }
   133  
   134      return [];
   135    }
   136  
   137    // When per_alloc is set to true on a volume, the volumes are duplicated between active allocations.
   138    // We differentiate them with a [#] suffix, inferred from a volume's allocation's name property.
   139    @computed('name')
   140    get volumeExtension() {
   141      return this.name.substring(this.name.lastIndexOf('['));
   142    }
   143  
   144    @fragmentArray('task-state') states;
   145    @fragmentArray('reschedule-event') rescheduleEvents;
   146  
   147    @computed('rescheduleEvents.length', 'nextAllocation')
   148    get hasRescheduleEvents() {
   149      return this.get('rescheduleEvents.length') > 0 || this.nextAllocation;
   150    }
   151  
   152    @computed(
   153      'clientStatus',
   154      'followUpEvaluation.content',
   155      'nextAllocation.content'
   156    )
   157    get hasStoppedRescheduling() {
   158      return (
   159        !this.get('nextAllocation.content') &&
   160        !this.get('followUpEvaluation.content') &&
   161        this.clientStatus === 'failed'
   162      );
   163    }
   164  
   165    stop() {
   166      return this.store.adapterFor('allocation').stop(this);
   167    }
   168  
   169    restart(taskName) {
   170      return this.store.adapterFor('allocation').restart(this, taskName);
   171    }
   172  
   173    restartAll() {
   174      return this.store.adapterFor('allocation').restartAll(this);
   175    }
   176  
   177    ls(path) {
   178      return this.store.adapterFor('allocation').ls(this, path);
   179    }
   180  
   181    stat(path) {
   182      return this.store.adapterFor('allocation').stat(this, path);
   183    }
   184  }