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 }