github.com/hspak/nomad@v0.7.2-0.20180309000617-bc4ae22a39a5/ui/app/models/job.js (about)

     1  import { alias, equal, or, and } from '@ember/object/computed';
     2  import { computed } from '@ember/object';
     3  import Model from 'ember-data/model';
     4  import attr from 'ember-data/attr';
     5  import { belongsTo, hasMany } from 'ember-data/relationships';
     6  import { fragmentArray } from 'ember-data-model-fragments/attributes';
     7  
     8  const JOB_TYPES = ['service', 'batch', 'system'];
     9  
    10  export default Model.extend({
    11    region: attr('string'),
    12    name: attr('string'),
    13    plainId: attr('string'),
    14    type: attr('string'),
    15    priority: attr('number'),
    16    allAtOnce: attr('boolean'),
    17  
    18    status: attr('string'),
    19    statusDescription: attr('string'),
    20    createIndex: attr('number'),
    21    modifyIndex: attr('number'),
    22  
    23    // True when the job is the parent periodic or parameterized jobs
    24    // Instances of periodic or parameterized jobs are false for both properties
    25    periodic: attr('boolean'),
    26    parameterized: attr('boolean'),
    27  
    28    periodicDetails: attr(),
    29    parameterizedDetails: attr(),
    30  
    31    hasChildren: or('periodic', 'parameterized'),
    32  
    33    parent: belongsTo('job', { inverse: 'children' }),
    34    children: hasMany('job', { inverse: 'parent' }),
    35  
    36    // The parent job name is prepended to child launch job names
    37    trimmedName: computed('name', 'parent', function() {
    38      return this.get('parent.content') ? this.get('name').replace(/.+?\//, '') : this.get('name');
    39    }),
    40  
    41    // A composite of type and other job attributes to determine
    42    // a better type descriptor for human interpretation rather
    43    // than for scheduling.
    44    displayType: computed('type', 'periodic', 'parameterized', function() {
    45      if (this.get('periodic')) {
    46        return 'periodic';
    47      } else if (this.get('parameterized')) {
    48        return 'parameterized';
    49      }
    50      return this.get('type');
    51    }),
    52  
    53    // A composite of type and other job attributes to determine
    54    // type for templating rather than scheduling
    55    templateType: computed(
    56      'type',
    57      'periodic',
    58      'parameterized',
    59      'parent.periodic',
    60      'parent.parameterized',
    61      function() {
    62        const type = this.get('type');
    63  
    64        if (this.get('periodic')) {
    65          return 'periodic';
    66        } else if (this.get('parameterized')) {
    67          return 'parameterized';
    68        } else if (this.get('parent.periodic')) {
    69          return 'periodic-child';
    70        } else if (this.get('parent.parameterized')) {
    71          return 'parameterized-child';
    72        } else if (JOB_TYPES.includes(type)) {
    73          // Guard against the API introducing a new type before the UI
    74          // is prepared to handle it.
    75          return this.get('type');
    76        }
    77  
    78        // A fail-safe in the event the API introduces a new type.
    79        return 'service';
    80      }
    81    ),
    82  
    83    datacenters: attr(),
    84    taskGroups: fragmentArray('task-group', { defaultValue: () => [] }),
    85    summary: belongsTo('job-summary'),
    86  
    87    // Alias through to the summary, as if there was no relationship
    88    taskGroupSummaries: alias('summary.taskGroupSummaries'),
    89    queuedAllocs: alias('summary.queuedAllocs'),
    90    startingAllocs: alias('summary.startingAllocs'),
    91    runningAllocs: alias('summary.runningAllocs'),
    92    completeAllocs: alias('summary.completeAllocs'),
    93    failedAllocs: alias('summary.failedAllocs'),
    94    lostAllocs: alias('summary.lostAllocs'),
    95    totalAllocs: alias('summary.totalAllocs'),
    96    pendingChildren: alias('summary.pendingChildren'),
    97    runningChildren: alias('summary.runningChildren'),
    98    deadChildren: alias('summary.deadChildren'),
    99    totalChildren: alias('summary.childrenList'),
   100  
   101    version: attr('number'),
   102  
   103    versions: hasMany('job-versions'),
   104    allocations: hasMany('allocations'),
   105    deployments: hasMany('deployments'),
   106    evaluations: hasMany('evaluations'),
   107    namespace: belongsTo('namespace'),
   108  
   109    hasBlockedEvaluation: computed('evaluations.@each.isBlocked', function() {
   110      return this.get('evaluations')
   111        .toArray()
   112        .some(evaluation => evaluation.get('isBlocked'));
   113    }),
   114  
   115    hasPlacementFailures: and('latestFailureEvaluation', 'hasBlockedEvaluation'),
   116  
   117    latestEvaluation: computed('evaluations.@each.modifyIndex', 'evaluations.isPending', function() {
   118      const evaluations = this.get('evaluations');
   119      if (!evaluations || evaluations.get('isPending')) {
   120        return null;
   121      }
   122      return evaluations.sortBy('modifyIndex').get('lastObject');
   123    }),
   124  
   125    latestFailureEvaluation: computed(
   126      'evaluations.@each.modifyIndex',
   127      'evaluations.isPending',
   128      function() {
   129        const evaluations = this.get('evaluations');
   130        if (!evaluations || evaluations.get('isPending')) {
   131          return null;
   132        }
   133  
   134        const failureEvaluations = evaluations.filterBy('hasPlacementFailures');
   135        if (failureEvaluations) {
   136          return failureEvaluations.sortBy('modifyIndex').get('lastObject');
   137        }
   138      }
   139    ),
   140  
   141    supportsDeployments: equal('type', 'service'),
   142  
   143    runningDeployment: computed('deployments.@each.status', function() {
   144      return this.get('deployments').findBy('status', 'running');
   145    }),
   146  
   147    fetchRawDefinition() {
   148      return this.store.adapterFor('job').fetchRawDefinition(this);
   149    },
   150  
   151    forcePeriodic() {
   152      return this.store.adapterFor('job').forcePeriodic(this);
   153    },
   154  
   155    statusClass: computed('status', function() {
   156      const classMap = {
   157        pending: 'is-pending',
   158        running: 'is-primary',
   159        dead: 'is-light',
   160      };
   161  
   162      return classMap[this.get('status')] || 'is-dark';
   163    }),
   164  
   165    payload: attr('string'),
   166    decodedPayload: computed('payload', function() {
   167      // Lazily decode the base64 encoded payload
   168      return window.atob(this.get('payload') || '');
   169    }),
   170  });