github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/ui/mirage/factories/task-group.js (about)

     1  import { Factory, trait } from 'ember-cli-mirage';
     2  import faker from 'nomad-ui/mirage/faker';
     3  import { provide } from '../utils';
     4  import { generateResources } from '../common';
     5  
     6  const DISK_RESERVATIONS = [200, 500, 1000, 2000, 5000, 10000, 100000];
     7  
     8  export default Factory.extend({
     9    name: id => `${faker.hacker.noun().dasherize()}-g-${id}`,
    10    count: () => faker.random.number({ min: 1, max: 2 }),
    11  
    12    ephemeralDisk: () => ({
    13      Sticky: faker.random.boolean(),
    14      SizeMB: faker.helpers.randomize(DISK_RESERVATIONS),
    15      Migrate: faker.random.boolean(),
    16    }),
    17  
    18    noHostVolumes: trait({
    19      volumes: () => ({}),
    20    }),
    21  
    22    withScaling: faker.random.boolean,
    23  
    24    volumes: makeHostVolumes(),
    25  
    26    // Directive used to control whether or not allocations are automatically
    27    // created.
    28    createAllocations: true,
    29  
    30    // Directived used to control whether or not the allocation should fail
    31    // and reschedule, creating reschedule events.
    32    withRescheduling: false,
    33  
    34    // Directive used to control whether the task group should have services.
    35    withServices: false,
    36  
    37    // Directive used to control whether dynamic application sizing recommendations
    38    // should be created.
    39    createRecommendations: false,
    40  
    41    // When true, only creates allocations
    42    shallow: false,
    43  
    44    // When set, passed into tasks to set resource values
    45    resourceSpec: null,
    46  
    47    afterCreate(group, server) {
    48      let taskIds = [];
    49      let volumes = Object.keys(group.volumes);
    50  
    51      if (group.withScaling) {
    52        group.update({
    53          scaling: {
    54            Min: 1,
    55            Max: 5,
    56            Policy: faker.random.boolean() && {
    57              EvaluationInterval: '10s',
    58              Cooldown: '2m',
    59              Check: {
    60                avg_conn: {
    61                  Source: 'prometheus',
    62                  Query:
    63                    'scalar(avg((haproxy_server_current_sessions{backend="http_back"}) and (haproxy_server_up{backend="http_back"} == 1)))',
    64                  Strategy: {
    65                    'target-value': {
    66                      target: 20,
    67                    },
    68                  },
    69                },
    70              },
    71            },
    72          },
    73        });
    74      }
    75  
    76      if (!group.shallow) {
    77        const resources =
    78          group.resourceSpec && divide(group.count, parseResourceSpec(group.resourceSpec));
    79        const tasks = provide(group.count, (_, idx) => {
    80          const mounts = faker.helpers
    81            .shuffle(volumes)
    82            .slice(0, faker.random.number({ min: 1, max: 3 }));
    83  
    84          const maybeResources = {};
    85          if (resources) {
    86            maybeResources.originalResources = generateResources(resources[idx]);
    87          }
    88          return server.create('task', {
    89            taskGroup: group,
    90            ...maybeResources,
    91            volumeMounts: mounts.map(mount => ({
    92              Volume: mount,
    93              Destination: `/${faker.internet.userName()}/${faker.internet.domainWord()}/${faker.internet.color()}`,
    94              PropagationMode: '',
    95              ReadOnly: faker.random.boolean(),
    96            })),
    97            createRecommendations: group.createRecommendations,
    98          });
    99        });
   100        taskIds = tasks.mapBy('id');
   101      }
   102  
   103      group.update({
   104        taskIds: taskIds,
   105      });
   106  
   107      if (group.createAllocations) {
   108        Array(group.count)
   109          .fill(null)
   110          .forEach((_, i) => {
   111            const props = {
   112              jobId: group.job.id,
   113              namespace: group.job.namespace,
   114              taskGroup: group.name,
   115              name: `${group.name}.[${i}]`,
   116              rescheduleSuccess: group.withRescheduling ? faker.random.boolean() : null,
   117              rescheduleAttempts: group.withRescheduling
   118                ? faker.random.number({ min: 1, max: 5 })
   119                : 0,
   120            };
   121  
   122            if (group.withRescheduling) {
   123              server.create('allocation', 'rescheduled', props);
   124            } else {
   125              server.create('allocation', props);
   126            }
   127          });
   128      }
   129  
   130      if (group.withServices) {
   131        Array(faker.random.number({ min: 1, max: 3 }))
   132          .fill(null)
   133          .forEach(() => {
   134            server.create('service', {
   135              taskGroup: group,
   136            });
   137          });
   138      }
   139    },
   140  });
   141  
   142  function makeHostVolumes() {
   143    const generate = () => ({
   144      Name: faker.internet.domainWord(),
   145      Type: 'host',
   146      Source: faker.internet.domainWord(),
   147      ReadOnly: faker.random.boolean(),
   148    });
   149  
   150    const volumes = provide(faker.random.number({ min: 1, max: 5 }), generate);
   151    return volumes.reduce((hash, volume) => {
   152      hash[volume.Name] = volume;
   153      return hash;
   154    }, {});
   155  }
   156  
   157  function parseResourceSpec(spec) {
   158    const mapping = {
   159      M: 'MemoryMB',
   160      C: 'CPU',
   161      D: 'DiskMB',
   162      I: 'IOPS',
   163    };
   164  
   165    const terms = spec.split(',').map(t => {
   166      const [k, v] = t
   167        .trim()
   168        .split(':')
   169        .map(kv => kv.trim());
   170      return [k, +v];
   171    });
   172  
   173    return terms.reduce((hash, term) => {
   174      hash[mapping[term[0]]] = term[1];
   175      return hash;
   176    }, {});
   177  }
   178  
   179  // Split a single resources object into N resource objects where
   180  // the sum of each property of the new resources objects equals
   181  // the original resources properties
   182  // ex: divide(2, { Mem: 400, Cpu: 250 }) -> [{ Mem: 80, Cpu: 50 }, { Mem: 320, Cpu: 200 }]
   183  function divide(count, resources) {
   184    const wheel = roulette(1, count);
   185  
   186    const ret = provide(count, (_, idx) => {
   187      return Object.keys(resources).reduce((hash, key) => {
   188        hash[key] = Math.round(resources[key] * wheel[idx]);
   189        return hash;
   190      }, {});
   191    });
   192  
   193    return ret;
   194  }
   195  
   196  // Roulette splits a number into N divisions
   197  // Variance is a value between 0 and 1 that determines how much each division in
   198  // size. At 0 each division is even, at 1, it's entirely random but the sum of all
   199  // divisions is guaranteed to equal the total value.
   200  function roulette(total, divisions, variance = 0.8) {
   201    let roulette = new Array(divisions).fill(total / divisions);
   202    roulette.forEach((v, i) => {
   203      if (i === roulette.length - 1) return;
   204      roulette.splice(i, 2, ...rngDistribute(roulette[i], roulette[i + 1], variance));
   205    });
   206    return roulette;
   207  }
   208  
   209  function rngDistribute(a, b, variance = 0.8) {
   210    const move = a * faker.random.number({ min: 0, max: variance, precision: 0.01 });
   211    return [a - move, b + move];
   212  }