210 lines
7.6 KiB
JavaScript
210 lines
7.6 KiB
JavaScript
import { module, test } from 'qunit';
|
|
import { setupTest } from 'ember-qunit';
|
|
import setupGlimmerComponentFactory from 'nomad-ui/tests/helpers/glimmer-factory';
|
|
|
|
module('Unit | Component | TopoViz', function(hooks) {
|
|
setupTest(hooks);
|
|
setupGlimmerComponentFactory(hooks, 'topo-viz');
|
|
|
|
test('the topology object properly organizes a tree of datacenters > nodes > allocations', async function(assert) {
|
|
const nodes = [
|
|
{ datacenter: 'dc1', id: 'node0', resources: {} },
|
|
{ datacenter: 'dc2', id: 'node1', resources: {} },
|
|
{ datacenter: 'dc1', id: 'node2', resources: {} },
|
|
];
|
|
|
|
const node0Allocs = [
|
|
alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'group' }),
|
|
alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'group' }),
|
|
];
|
|
const node1Allocs = [
|
|
alloc({ nodeId: 'node1', jobId: 'job0', taskGroupName: 'group' }),
|
|
alloc({ nodeId: 'node1', jobId: 'job1', taskGroupName: 'group' }),
|
|
];
|
|
const node2Allocs = [
|
|
alloc({ nodeId: 'node2', jobId: 'job0', taskGroupName: 'group' }),
|
|
alloc({ nodeId: 'node2', jobId: 'job1', taskGroupName: 'group' }),
|
|
];
|
|
|
|
const allocations = [...node0Allocs, ...node1Allocs, ...node2Allocs];
|
|
|
|
const topoViz = this.createComponent({ nodes, allocations });
|
|
|
|
topoViz.buildTopology();
|
|
|
|
assert.deepEqual(topoViz.topology.datacenters.mapBy('name'), ['dc1', 'dc2']);
|
|
assert.deepEqual(topoViz.topology.datacenters[0].nodes.mapBy('node'), [nodes[0], nodes[2]]);
|
|
assert.deepEqual(topoViz.topology.datacenters[1].nodes.mapBy('node'), [nodes[1]]);
|
|
assert.deepEqual(
|
|
topoViz.topology.datacenters[0].nodes[0].allocations.mapBy('allocation'),
|
|
node0Allocs
|
|
);
|
|
assert.deepEqual(
|
|
topoViz.topology.datacenters[1].nodes[0].allocations.mapBy('allocation'),
|
|
node1Allocs
|
|
);
|
|
assert.deepEqual(
|
|
topoViz.topology.datacenters[0].nodes[1].allocations.mapBy('allocation'),
|
|
node2Allocs
|
|
);
|
|
});
|
|
|
|
test('the topology object contains an allocation index keyed by jobId+taskGroupName', async function(assert) {
|
|
const allocations = [
|
|
alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'one' }),
|
|
alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'one' }),
|
|
alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'two' }),
|
|
alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'one' }),
|
|
alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'two' }),
|
|
alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'three' }),
|
|
alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }),
|
|
alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }),
|
|
alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }),
|
|
alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }),
|
|
];
|
|
|
|
const nodes = [{ datacenter: 'dc1', id: 'node0', resources: {} }];
|
|
const topoViz = this.createComponent({ nodes, allocations });
|
|
|
|
topoViz.buildTopology();
|
|
|
|
assert.deepEqual(
|
|
Object.keys(topoViz.topology.allocationIndex).sort(),
|
|
[
|
|
JSON.stringify(['job0', 'one']),
|
|
JSON.stringify(['job0', 'two']),
|
|
|
|
JSON.stringify(['job1', 'one']),
|
|
JSON.stringify(['job1', 'two']),
|
|
JSON.stringify(['job1', 'three']),
|
|
|
|
JSON.stringify(['job2', 'one']),
|
|
].sort()
|
|
);
|
|
|
|
Object.keys(topoViz.topology.allocationIndex).forEach(key => {
|
|
const [jobId, group] = JSON.parse(key);
|
|
assert.deepEqual(
|
|
topoViz.topology.allocationIndex[key].mapBy('allocation'),
|
|
allocations.filter(alloc => alloc.jobId === jobId && alloc.taskGroupName === group)
|
|
);
|
|
});
|
|
});
|
|
|
|
test('isSingleColumn is true when there is only one datacenter', async function(assert) {
|
|
const oneDc = [{ datacenter: 'dc1', id: 'node0', resources: {} }];
|
|
const twoDc = [...oneDc, { datacenter: 'dc2', id: 'node1', resources: {} }];
|
|
|
|
const topoViz1 = this.createComponent({ nodes: oneDc, allocations: [] });
|
|
const topoViz2 = this.createComponent({ nodes: twoDc, allocations: [] });
|
|
|
|
topoViz1.buildTopology();
|
|
topoViz2.buildTopology();
|
|
|
|
assert.ok(topoViz1.isSingleColumn);
|
|
assert.notOk(topoViz2.isSingleColumn);
|
|
});
|
|
|
|
test('isSingleColumn is true when there are multiple datacenters with a high variance in node count', async function(assert) {
|
|
const uniformDcs = [
|
|
{ datacenter: 'dc1', id: 'node0', resources: {} },
|
|
{ datacenter: 'dc2', id: 'node1', resources: {} },
|
|
];
|
|
const skewedDcs = [
|
|
{ datacenter: 'dc1', id: 'node0', resources: {} },
|
|
{ datacenter: 'dc2', id: 'node1', resources: {} },
|
|
{ datacenter: 'dc2', id: 'node2', resources: {} },
|
|
{ datacenter: 'dc2', id: 'node3', resources: {} },
|
|
{ datacenter: 'dc2', id: 'node4', resources: {} },
|
|
];
|
|
|
|
const twoColumnViz = this.createComponent({ nodes: uniformDcs, allocations: [] });
|
|
const oneColumViz = this.createComponent({ nodes: skewedDcs, allocations: [] });
|
|
|
|
twoColumnViz.buildTopology();
|
|
oneColumViz.buildTopology();
|
|
|
|
assert.notOk(twoColumnViz.isSingleColumn);
|
|
assert.ok(oneColumViz.isSingleColumn);
|
|
});
|
|
|
|
test('datacenterIsSingleColumn is only ever false when isSingleColumn is false and the total node count is high', async function(assert) {
|
|
const manyUniformNodes = Array(25)
|
|
.fill(null)
|
|
.map((_, index) => ({
|
|
datacenter: index > 12 ? 'dc2' : 'dc1',
|
|
id: `node${index}`,
|
|
resources: {},
|
|
}));
|
|
const manySkewedNodes = Array(25)
|
|
.fill(null)
|
|
.map((_, index) => ({
|
|
datacenter: index > 5 ? 'dc2' : 'dc1',
|
|
id: `node${index}`,
|
|
resources: {},
|
|
}));
|
|
|
|
const oneColumnViz = this.createComponent({ nodes: manyUniformNodes, allocations: [] });
|
|
const twoColumnViz = this.createComponent({ nodes: manySkewedNodes, allocations: [] });
|
|
|
|
oneColumnViz.buildTopology();
|
|
twoColumnViz.buildTopology();
|
|
|
|
assert.ok(oneColumnViz.datacenterIsSingleColumn);
|
|
assert.notOk(oneColumnViz.isSingleColumn);
|
|
|
|
assert.notOk(twoColumnViz.datacenterIsSingleColumn);
|
|
assert.ok(twoColumnViz.isSingleColumn);
|
|
});
|
|
|
|
test('dataForAllocation correctly calculates proportion of node utilization and group key', async function(assert) {
|
|
const nodes = [{ datacenter: 'dc1', id: 'node0', resources: { cpu: 100, memory: 250 } }];
|
|
const allocations = [
|
|
alloc({
|
|
nodeId: 'node0',
|
|
jobId: 'job0',
|
|
taskGroupName: 'group',
|
|
allocatedResources: { cpu: 50, memory: 25 },
|
|
}),
|
|
];
|
|
|
|
const topoViz = this.createComponent({ nodes, allocations });
|
|
topoViz.buildTopology();
|
|
|
|
assert.equal(topoViz.topology.datacenters[0].nodes[0].allocations[0].cpuPercent, 0.5);
|
|
assert.equal(topoViz.topology.datacenters[0].nodes[0].allocations[0].memoryPercent, 0.1);
|
|
});
|
|
|
|
test('allocations that reference nonexistent nodes are ignored', async function(assert) {
|
|
const nodes = [{ datacenter: 'dc1', id: 'node0', resources: {} }];
|
|
|
|
const allocations = [
|
|
alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'group' }),
|
|
alloc({ nodeId: 'node404', jobId: 'job1', taskGroupName: 'group' }),
|
|
];
|
|
|
|
const topoViz = this.createComponent({ nodes, allocations });
|
|
|
|
topoViz.buildTopology();
|
|
|
|
assert.deepEqual(topoViz.topology.datacenters[0].nodes.mapBy('node'), [nodes[0]]);
|
|
assert.deepEqual(topoViz.topology.datacenters[0].nodes[0].allocations.mapBy('allocation'), [
|
|
allocations[0],
|
|
]);
|
|
});
|
|
});
|
|
|
|
function alloc(props) {
|
|
return {
|
|
...props,
|
|
allocatedResources: props.allocatedResources || {},
|
|
belongsTo(type) {
|
|
return {
|
|
id() {
|
|
return type === 'job' ? props.jobId : props.nodeId;
|
|
},
|
|
};
|
|
},
|
|
};
|
|
}
|