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  }