github.com/hernad/nomad@v1.6.112/ui/tests/unit/components/topo-viz-test.js (about) 1 /** 2 * Copyright (c) HashiCorp, Inc. 3 * SPDX-License-Identifier: MPL-2.0 4 */ 5 6 import { module, test } from 'qunit'; 7 import { setupTest } from 'ember-qunit'; 8 import setupGlimmerComponentFactory from 'nomad-ui/tests/helpers/glimmer-factory'; 9 10 module('Unit | Component | TopoViz', function (hooks) { 11 setupTest(hooks); 12 setupGlimmerComponentFactory(hooks, 'topo-viz'); 13 14 test('the topology object properly organizes a tree of datacenters > nodes > allocations', async function (assert) { 15 const nodes = [ 16 { datacenter: 'dc1', id: 'node0', resources: {} }, 17 { datacenter: 'dc2', id: 'node1', resources: {} }, 18 { datacenter: 'dc1', id: 'node2', resources: {} }, 19 ]; 20 21 const node0Allocs = [ 22 alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'group' }), 23 alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'group' }), 24 ]; 25 const node1Allocs = [ 26 alloc({ nodeId: 'node1', jobId: 'job0', taskGroupName: 'group' }), 27 alloc({ nodeId: 'node1', jobId: 'job1', taskGroupName: 'group' }), 28 ]; 29 const node2Allocs = [ 30 alloc({ nodeId: 'node2', jobId: 'job0', taskGroupName: 'group' }), 31 alloc({ nodeId: 'node2', jobId: 'job1', taskGroupName: 'group' }), 32 ]; 33 34 const allocations = [...node0Allocs, ...node1Allocs, ...node2Allocs]; 35 36 const topoViz = this.createComponent({ nodes, allocations }); 37 38 topoViz.buildTopology(); 39 40 assert.deepEqual(topoViz.topology.datacenters.mapBy('name'), [ 41 'dc1', 42 'dc2', 43 ]); 44 assert.deepEqual(topoViz.topology.datacenters[0].nodes.mapBy('node'), [ 45 nodes[0], 46 nodes[2], 47 ]); 48 assert.deepEqual(topoViz.topology.datacenters[1].nodes.mapBy('node'), [ 49 nodes[1], 50 ]); 51 assert.deepEqual( 52 topoViz.topology.datacenters[0].nodes[0].allocations.mapBy('allocation'), 53 node0Allocs 54 ); 55 assert.deepEqual( 56 topoViz.topology.datacenters[1].nodes[0].allocations.mapBy('allocation'), 57 node1Allocs 58 ); 59 assert.deepEqual( 60 topoViz.topology.datacenters[0].nodes[1].allocations.mapBy('allocation'), 61 node2Allocs 62 ); 63 }); 64 65 test('the topology object contains an allocation index keyed by jobId+taskGroupName', async function (assert) { 66 assert.expect(7); 67 68 const allocations = [ 69 alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'one' }), 70 alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'one' }), 71 alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'two' }), 72 alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'one' }), 73 alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'two' }), 74 alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'three' }), 75 alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }), 76 alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }), 77 alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }), 78 alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }), 79 ]; 80 81 const nodes = [{ datacenter: 'dc1', id: 'node0', resources: {} }]; 82 const topoViz = this.createComponent({ nodes, allocations }); 83 84 topoViz.buildTopology(); 85 86 assert.deepEqual( 87 Object.keys(topoViz.topology.allocationIndex).sort(), 88 [ 89 JSON.stringify(['job0', 'one']), 90 JSON.stringify(['job0', 'two']), 91 92 JSON.stringify(['job1', 'one']), 93 JSON.stringify(['job1', 'two']), 94 JSON.stringify(['job1', 'three']), 95 96 JSON.stringify(['job2', 'one']), 97 ].sort() 98 ); 99 100 Object.keys(topoViz.topology.allocationIndex).forEach((key) => { 101 const [jobId, group] = JSON.parse(key); 102 assert.deepEqual( 103 topoViz.topology.allocationIndex[key].mapBy('allocation'), 104 allocations.filter( 105 (alloc) => alloc.jobId === jobId && alloc.taskGroupName === group 106 ) 107 ); 108 }); 109 }); 110 111 test('isSingleColumn is true when there is only one datacenter', async function (assert) { 112 const oneDc = [{ datacenter: 'dc1', id: 'node0', resources: {} }]; 113 const twoDc = [...oneDc, { datacenter: 'dc2', id: 'node1', resources: {} }]; 114 115 const topoViz1 = this.createComponent({ nodes: oneDc, allocations: [] }); 116 const topoViz2 = this.createComponent({ nodes: twoDc, allocations: [] }); 117 118 topoViz1.buildTopology(); 119 topoViz2.buildTopology(); 120 121 assert.ok(topoViz1.isSingleColumn); 122 assert.notOk(topoViz2.isSingleColumn); 123 }); 124 125 test('isSingleColumn is true when there are multiple datacenters with a high variance in node count', async function (assert) { 126 const uniformDcs = [ 127 { datacenter: 'dc1', id: 'node0', resources: {} }, 128 { datacenter: 'dc2', id: 'node1', resources: {} }, 129 ]; 130 const skewedDcs = [ 131 { datacenter: 'dc1', id: 'node0', resources: {} }, 132 { datacenter: 'dc2', id: 'node1', resources: {} }, 133 { datacenter: 'dc2', id: 'node2', resources: {} }, 134 { datacenter: 'dc2', id: 'node3', resources: {} }, 135 { datacenter: 'dc2', id: 'node4', resources: {} }, 136 ]; 137 138 const twoColumnViz = this.createComponent({ 139 nodes: uniformDcs, 140 allocations: [], 141 }); 142 const oneColumViz = this.createComponent({ 143 nodes: skewedDcs, 144 allocations: [], 145 }); 146 147 twoColumnViz.buildTopology(); 148 oneColumViz.buildTopology(); 149 150 assert.notOk(twoColumnViz.isSingleColumn); 151 assert.ok(oneColumViz.isSingleColumn); 152 }); 153 154 test('datacenterIsSingleColumn is only ever false when isSingleColumn is false and the total node count is high', async function (assert) { 155 const manyUniformNodes = Array(25) 156 .fill(null) 157 .map((_, index) => ({ 158 datacenter: index > 12 ? 'dc2' : 'dc1', 159 id: `node${index}`, 160 resources: {}, 161 })); 162 const manySkewedNodes = Array(25) 163 .fill(null) 164 .map((_, index) => ({ 165 datacenter: index > 5 ? 'dc2' : 'dc1', 166 id: `node${index}`, 167 resources: {}, 168 })); 169 170 const oneColumnViz = this.createComponent({ 171 nodes: manyUniformNodes, 172 allocations: [], 173 }); 174 const twoColumnViz = this.createComponent({ 175 nodes: manySkewedNodes, 176 allocations: [], 177 }); 178 179 oneColumnViz.buildTopology(); 180 twoColumnViz.buildTopology(); 181 182 assert.ok(oneColumnViz.datacenterIsSingleColumn); 183 assert.notOk(oneColumnViz.isSingleColumn); 184 185 assert.notOk(twoColumnViz.datacenterIsSingleColumn); 186 assert.ok(twoColumnViz.isSingleColumn); 187 }); 188 189 test('dataForAllocation correctly calculates proportion of node utilization and group key', async function (assert) { 190 const nodes = [ 191 { datacenter: 'dc1', id: 'node0', resources: { cpu: 100, memory: 250 } }, 192 ]; 193 const allocations = [ 194 alloc({ 195 nodeId: 'node0', 196 jobId: 'job0', 197 taskGroupName: 'group', 198 allocatedResources: { cpu: 50, memory: 25 }, 199 }), 200 ]; 201 202 const topoViz = this.createComponent({ nodes, allocations }); 203 topoViz.buildTopology(); 204 205 assert.equal( 206 topoViz.topology.datacenters[0].nodes[0].allocations[0].cpuPercent, 207 0.5 208 ); 209 assert.equal( 210 topoViz.topology.datacenters[0].nodes[0].allocations[0].memoryPercent, 211 0.1 212 ); 213 }); 214 215 test('allocations that reference nonexistent nodes are ignored', async function (assert) { 216 const nodes = [{ datacenter: 'dc1', id: 'node0', resources: {} }]; 217 218 const allocations = [ 219 alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'group' }), 220 alloc({ nodeId: 'node404', jobId: 'job1', taskGroupName: 'group' }), 221 ]; 222 223 const topoViz = this.createComponent({ nodes, allocations }); 224 225 topoViz.buildTopology(); 226 227 assert.deepEqual(topoViz.topology.datacenters[0].nodes.mapBy('node'), [ 228 nodes[0], 229 ]); 230 assert.deepEqual( 231 topoViz.topology.datacenters[0].nodes[0].allocations.mapBy('allocation'), 232 [allocations[0]] 233 ); 234 }); 235 }); 236 237 function alloc(props) { 238 return { 239 ...props, 240 allocatedResources: props.allocatedResources || {}, 241 belongsTo(type) { 242 return { 243 id() { 244 return type === 'job' ? props.jobId : props.nodeId; 245 }, 246 }; 247 }, 248 }; 249 }