github.com/emate/nomad@v0.8.2-wo-binpacking/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 // A job model created from the jobs list response will be lacking 88 // task groups. This is an indicator that it needs to be reloaded 89 // if task group information is important. 90 isPartial: equal('taskGroups.length', 0), 91 92 // If a job has only been loaded through the list request, the task groups 93 // are still unknown. However, the count of task groups is available through 94 // the job-summary model which is embedded in the jobs list response. 95 taskGroupCount: or('taskGroups.length', 'taskGroupSummaries.length'), 96 97 // Alias through to the summary, as if there was no relationship 98 taskGroupSummaries: alias('summary.taskGroupSummaries'), 99 queuedAllocs: alias('summary.queuedAllocs'), 100 startingAllocs: alias('summary.startingAllocs'), 101 runningAllocs: alias('summary.runningAllocs'), 102 completeAllocs: alias('summary.completeAllocs'), 103 failedAllocs: alias('summary.failedAllocs'), 104 lostAllocs: alias('summary.lostAllocs'), 105 totalAllocs: alias('summary.totalAllocs'), 106 pendingChildren: alias('summary.pendingChildren'), 107 runningChildren: alias('summary.runningChildren'), 108 deadChildren: alias('summary.deadChildren'), 109 totalChildren: alias('summary.totalChildren'), 110 111 version: attr('number'), 112 113 versions: hasMany('job-versions'), 114 allocations: hasMany('allocations'), 115 deployments: hasMany('deployments'), 116 evaluations: hasMany('evaluations'), 117 namespace: belongsTo('namespace'), 118 119 hasBlockedEvaluation: computed('evaluations.@each.isBlocked', function() { 120 return this.get('evaluations') 121 .toArray() 122 .some(evaluation => evaluation.get('isBlocked')); 123 }), 124 125 hasPlacementFailures: and('latestFailureEvaluation', 'hasBlockedEvaluation'), 126 127 latestEvaluation: computed('evaluations.@each.modifyIndex', 'evaluations.isPending', function() { 128 const evaluations = this.get('evaluations'); 129 if (!evaluations || evaluations.get('isPending')) { 130 return null; 131 } 132 return evaluations.sortBy('modifyIndex').get('lastObject'); 133 }), 134 135 latestFailureEvaluation: computed( 136 'evaluations.@each.modifyIndex', 137 'evaluations.isPending', 138 function() { 139 const evaluations = this.get('evaluations'); 140 if (!evaluations || evaluations.get('isPending')) { 141 return null; 142 } 143 144 const failureEvaluations = evaluations.filterBy('hasPlacementFailures'); 145 if (failureEvaluations) { 146 return failureEvaluations.sortBy('modifyIndex').get('lastObject'); 147 } 148 } 149 ), 150 151 supportsDeployments: equal('type', 'service'), 152 153 runningDeployment: computed('deployments.@each.status', function() { 154 return this.get('deployments').findBy('status', 'running'); 155 }), 156 157 fetchRawDefinition() { 158 return this.store.adapterFor('job').fetchRawDefinition(this); 159 }, 160 161 forcePeriodic() { 162 return this.store.adapterFor('job').forcePeriodic(this); 163 }, 164 165 stop() { 166 return this.store.adapterFor('job').stop(this); 167 }, 168 169 statusClass: computed('status', function() { 170 const classMap = { 171 pending: 'is-pending', 172 running: 'is-primary', 173 dead: 'is-light', 174 }; 175 176 return classMap[this.get('status')] || 'is-dark'; 177 }), 178 179 payload: attr('string'), 180 decodedPayload: computed('payload', function() { 181 // Lazily decode the base64 encoded payload 182 return window.atob(this.get('payload') || ''); 183 }), 184 });