github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/ui/mirage/factories/job.js (about) 1 import { assign } from '@ember/polyfills'; 2 import { Factory, trait } from 'ember-cli-mirage'; 3 import faker from 'nomad-ui/mirage/faker'; 4 import { provide, pickOne } from '../utils'; 5 import { DATACENTERS } from '../common'; 6 import { dasherize } from '@ember/string'; 7 8 const REF_TIME = new Date(); 9 const JOB_PREFIXES = provide(5, faker.hacker.abbreviation); 10 const JOB_TYPES = ['service', 'batch', 'system', 'sysbatch']; 11 const JOB_STATUSES = ['pending', 'running', 'dead']; 12 13 export default Factory.extend({ 14 id(i) { 15 if (this.parameterized && this.parentId) { 16 const shortUUID = faker.random.uuid().split('-')[0]; 17 const dispatchId = `dispatch-${this.submitTime / 1000}-${shortUUID}`; 18 return `${this.parentId}/${dispatchId}`; 19 } 20 21 return `${faker.helpers.randomize(JOB_PREFIXES)}-${dasherize( 22 faker.hacker.noun() 23 )}-${i}`.toLowerCase(); 24 }, 25 26 name() { 27 return this.id; 28 }, 29 30 version: 1, 31 submitTime: () => faker.date.past(2 / 365, REF_TIME) * 1000000, 32 33 // When provided, the resourceSpec will inform how many task groups to create 34 // and how much of each resource that task group reserves. 35 // 36 // One task group, 256 MiB memory and 500 MHz cpu 37 // resourceSpec: ['M: 256, C: 500'] 38 // 39 // Two task groups 40 // resourceSpec: ['M: 256, C: 500', 'M: 1024, C: 1200'] 41 resourceSpec: null, 42 43 groupsCount() { 44 return this.resourceSpec 45 ? this.resourceSpec.length 46 : faker.random.number({ min: 1, max: 2 }); 47 }, 48 49 region: () => 'global', 50 type: () => faker.helpers.randomize(JOB_TYPES), 51 priority: () => faker.random.number(100), 52 allAtOnce: faker.random.boolean, 53 status: () => faker.helpers.randomize(JOB_STATUSES), 54 datacenters: () => 55 faker.helpers 56 .shuffle(DATACENTERS) 57 .slice(0, faker.random.number({ min: 1, max: 4 })), 58 59 childrenCount: () => faker.random.number({ min: 1, max: 2 }), 60 61 meta: null, 62 63 periodic: trait({ 64 type: 'batch', 65 periodic: true, 66 // periodic details object 67 // serializer update for bool vs details object 68 periodicDetails: () => ({ 69 Enabled: true, 70 ProhibitOverlap: true, 71 Spec: '*/5 * * * * *', 72 SpecType: 'cron', 73 TimeZone: 'UTC', 74 }), 75 }), 76 77 periodicSysbatch: trait({ 78 type: 'sysbatch', 79 periodic: true, 80 // periodic details object 81 // serializer update for bool vs details object 82 periodicDetails: () => ({ 83 Enabled: true, 84 ProhibitOverlap: true, 85 Spec: '*/5 * * * * *', 86 SpecType: 'cron', 87 TimeZone: 'UTC', 88 }), 89 }), 90 91 parameterized: trait({ 92 type: 'batch', 93 parameterized: true, 94 // parameterized job object 95 // serializer update for bool vs details object 96 parameterizedJob: () => ({ 97 MetaOptional: generateMetaFields(faker.random.number(10), 'optional'), 98 MetaRequired: generateMetaFields(faker.random.number(10), 'required'), 99 Payload: faker.random.boolean() ? 'required' : null, 100 }), 101 }), 102 103 parameterizedSysbatch: trait({ 104 type: 'sysbatch', 105 parameterized: true, 106 // parameterized job object 107 // serializer update for bool vs details object 108 parameterizedJob: () => ({ 109 MetaOptional: generateMetaFields(faker.random.number(10), 'optional'), 110 MetaRequired: generateMetaFields(faker.random.number(10), 'required'), 111 Payload: faker.random.boolean() ? 'required' : null, 112 }), 113 }), 114 115 periodicChild: trait({ 116 // Periodic children need a parent job, 117 // It is the Periodic job's responsibility to create 118 // periodicChild jobs and provide a parent job. 119 type: 'batch', 120 }), 121 122 periodicSysbatchChild: trait({ 123 // Periodic children need a parent job, 124 // It is the Periodic job's responsibility to create 125 // periodicChild jobs and provide a parent job. 126 type: 'sysbatch', 127 }), 128 129 parameterizedChild: trait({ 130 // Parameterized children need a parent job, 131 // It is the Parameterized job's responsibility to create 132 // parameterizedChild jobs and provide a parent job. 133 type: 'batch', 134 parameterized: true, 135 dispatched: true, 136 payload: window.btoa(faker.lorem.sentence()), 137 }), 138 139 parameterizedSysbatchChild: trait({ 140 // Parameterized children need a parent job, 141 // It is the Parameterized job's responsibility to create 142 // parameterizedChild jobs and provide a parent job. 143 type: 'sysbatch', 144 parameterized: true, 145 dispatched: true, 146 payload: window.btoa(faker.lorem.sentence()), 147 }), 148 149 pack: trait({ 150 meta: () => ({ 151 'pack.name': faker.hacker.noun(), 152 'pack.version': faker.system.semver(), 153 }), 154 }), 155 156 createIndex: (i) => i, 157 modifyIndex: () => faker.random.number({ min: 10, max: 2000 }), 158 159 // Directive used to control sub-resources 160 161 // When false, no allocations are made 162 createAllocations: true, 163 164 // When true, deployments for the job will never have a 'running' status 165 noActiveDeployment: false, 166 167 // When true, deployments for the job will always have a 'running' status 168 activeDeployment: false, 169 170 // When true, the job will have no versions or deployments (and in turn no latest deployment) 171 noDeployments: false, 172 173 // When true, an evaluation with a high modify index and placement failures is created 174 failedPlacements: false, 175 176 // When true, no evaluations have failed placements 177 noFailedPlacements: false, 178 179 // When true, all task groups get the noHostVolumes trait 180 noHostVolumes: false, 181 182 // When true, allocations for this job will fail and reschedule, randomly succeeding or not 183 withRescheduling: false, 184 185 // When true, task groups will have services 186 withGroupServices: false, 187 188 // When true, tasks will have services 189 withTaskServices: false, 190 191 // When true, dynamic application sizing recommendations will be made 192 createRecommendations: false, 193 194 // When true, only task groups and allocations are made 195 shallow: false, 196 197 afterCreate(job, server) { 198 if (!job.namespaceId) { 199 const namespace = server.db.namespaces.length 200 ? pickOne(server.db.namespaces).id 201 : null; 202 job.update({ 203 namespace, 204 namespaceId: namespace, 205 }); 206 } else { 207 job.update({ 208 namespace: job.namespaceId, 209 }); 210 } 211 212 const groupProps = { 213 job, 214 createAllocations: job.createAllocations, 215 withRescheduling: job.withRescheduling, 216 withServices: job.withGroupServices, 217 withTaskServices: job.withTaskServices, 218 createRecommendations: job.createRecommendations, 219 shallow: job.shallow, 220 }; 221 222 if (job.groupTaskCount) { 223 groupProps.count = job.groupTaskCount; 224 } 225 226 let groups; 227 if (job.noHostVolumes) { 228 groups = provide(job.groupsCount, (_, idx) => 229 server.create('task-group', 'noHostVolumes', { 230 ...groupProps, 231 resourceSpec: 232 job.resourceSpec && 233 job.resourceSpec.length && 234 job.resourceSpec[idx], 235 }) 236 ); 237 } else { 238 groups = provide(job.groupsCount, (_, idx) => 239 server.create('task-group', { 240 ...groupProps, 241 resourceSpec: 242 job.resourceSpec && 243 job.resourceSpec.length && 244 job.resourceSpec[idx], 245 }) 246 ); 247 } 248 249 job.update({ 250 taskGroupIds: groups.mapBy('id'), 251 }); 252 253 const hasChildren = job.periodic || (job.parameterized && !job.parentId); 254 const jobSummary = server.create( 255 'job-summary', 256 hasChildren ? 'withChildren' : 'withSummary', 257 { 258 jobId: job.id, 259 groupNames: groups.mapBy('name'), 260 namespace: job.namespace, 261 } 262 ); 263 264 job.update({ 265 jobSummaryId: jobSummary.id, 266 }); 267 268 const jobScale = server.create('job-scale', { 269 groupNames: groups.mapBy('name'), 270 jobId: job.id, 271 namespace: job.namespace, 272 shallow: job.shallow, 273 }); 274 275 job.update({ 276 jobScaleId: jobScale.id, 277 }); 278 279 if (!job.noDeployments) { 280 Array(faker.random.number({ min: 1, max: 3 })) 281 .fill(null) 282 .map((_, index) => { 283 return server.create('job-version', { 284 job, 285 namespace: job.namespace, 286 version: index, 287 noActiveDeployment: job.noActiveDeployment, 288 activeDeployment: job.activeDeployment, 289 }); 290 }); 291 } 292 293 if (!job.shallow) { 294 const knownEvaluationProperties = { 295 jobId: job.id, 296 namespace: job.namespace, 297 }; 298 server.createList( 299 'evaluation', 300 faker.random.number({ min: 1, max: 5 }), 301 knownEvaluationProperties 302 ); 303 if (!job.noFailedPlacements) { 304 server.createList( 305 'evaluation', 306 faker.random.number(3), 307 'withPlacementFailures', 308 knownEvaluationProperties 309 ); 310 } 311 312 if (job.failedPlacements) { 313 server.create( 314 'evaluation', 315 'withPlacementFailures', 316 assign(knownEvaluationProperties, { 317 modifyIndex: 4000, 318 }) 319 ); 320 } 321 } 322 323 if (job.periodic) { 324 let childType; 325 switch (job.type) { 326 case 'batch': 327 childType = 'periodicChild'; 328 break; 329 case 'sysbatch': 330 childType = 'periodicSysbatchChild'; 331 break; 332 } 333 334 // Create child jobs 335 server.createList('job', job.childrenCount, childType, { 336 parentId: job.id, 337 namespaceId: job.namespaceId, 338 namespace: job.namespace, 339 datacenters: job.datacenters, 340 createAllocations: job.createAllocations, 341 shallow: job.shallow, 342 }); 343 } 344 345 if (job.parameterized && !job.parentId) { 346 let childType; 347 switch (job.type) { 348 case 'batch': 349 childType = 'parameterizedChild'; 350 break; 351 case 'sysbatch': 352 childType = 'parameterizedSysbatchChild'; 353 break; 354 } 355 356 // Create child jobs 357 server.createList('job', job.childrenCount, childType, { 358 parentId: job.id, 359 namespaceId: job.namespaceId, 360 namespace: job.namespace, 361 datacenters: job.datacenters, 362 createAllocations: job.createAllocations, 363 shallow: job.shallow, 364 }); 365 } 366 }, 367 }); 368 369 function generateMetaFields(num, prefix = '') { 370 // Use an object to avoid duplicate meta fields. 371 // The prefix param helps to avoid duplicate entries across function calls. 372 let meta = {}; 373 for (let i = 0; i < num; i++) { 374 const field = `${prefix}-${faker.hacker.noun()}`; 375 meta[field] = true; 376 } 377 return Object.keys(meta); 378 }