mirror of
https://github.com/kemko/nomad.git
synced 2026-01-06 10:25:42 +03:00
ui: create node pool model (#17301)
Co-authored-by: Phil Renaud <phil@riotindustries.com> Co-authored-by: Luiz Aoqui <luiz@hashicorp.com>
This commit is contained in:
17
ui/app/adapters/node-pool.js
Normal file
17
ui/app/adapters/node-pool.js
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import ApplicationAdapter from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
import { pluralize } from 'ember-inflector';
|
||||
|
||||
@classic
|
||||
export default class NodePoolAdapter extends ApplicationAdapter {
|
||||
urlForFindAll(modelName) {
|
||||
let [relationshipResource, resource] = modelName.split('-');
|
||||
resource = pluralize(resource);
|
||||
return `/v1/${relationshipResource}/${resource}`;
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,9 @@ export default class IndexController extends Controller.extend(
|
||||
{
|
||||
qpVolume: 'volume',
|
||||
},
|
||||
{
|
||||
qpNodePool: 'nodePool',
|
||||
},
|
||||
];
|
||||
|
||||
currentPage = 1;
|
||||
@@ -75,12 +78,14 @@ export default class IndexController extends Controller.extend(
|
||||
qpDatacenter = '';
|
||||
qpVersion = '';
|
||||
qpVolume = '';
|
||||
qpNodePool = '';
|
||||
|
||||
@selection('qpClass') selectionClass;
|
||||
@selection('qpState') selectionState;
|
||||
@selection('qpDatacenter') selectionDatacenter;
|
||||
@selection('qpVersion') selectionVersion;
|
||||
@selection('qpVolume') selectionVolume;
|
||||
@selection('qpNodePool') selectionNodePool;
|
||||
|
||||
@computed('nodes.[]', 'selectionClass')
|
||||
get optionsClass() {
|
||||
@@ -164,11 +169,37 @@ export default class IndexController extends Controller.extend(
|
||||
return volumes.sort().map((volume) => ({ key: volume, label: volume }));
|
||||
}
|
||||
|
||||
@computed('selectionNodePool', 'model.nodePools.[]')
|
||||
get optionsNodePool() {
|
||||
const availableNodePools = this.model.nodePools.filter(
|
||||
(p) => p.name !== 'all'
|
||||
);
|
||||
|
||||
scheduleOnce('actions', () => {
|
||||
// eslint-disable-next-line ember/no-side-effects
|
||||
this.set(
|
||||
'qpNodePool',
|
||||
serialize(
|
||||
intersection(
|
||||
availableNodePools.map(({ name }) => name),
|
||||
this.selectionNodePool
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
return availableNodePools.map((nodePool) => ({
|
||||
key: nodePool.name,
|
||||
label: nodePool.name,
|
||||
}));
|
||||
}
|
||||
|
||||
@computed(
|
||||
'nodes.[]',
|
||||
'selectionClass',
|
||||
'selectionState',
|
||||
'selectionDatacenter',
|
||||
'selectionNodePool',
|
||||
'selectionVersion',
|
||||
'selectionVolume'
|
||||
)
|
||||
@@ -177,6 +208,7 @@ export default class IndexController extends Controller.extend(
|
||||
selectionClass: classes,
|
||||
selectionState: states,
|
||||
selectionDatacenter: datacenters,
|
||||
selectionNodePool: nodePools,
|
||||
selectionVersion: versions,
|
||||
selectionVolume: volumes,
|
||||
} = this;
|
||||
@@ -201,6 +233,9 @@ export default class IndexController extends Controller.extend(
|
||||
!node.hostVolumes.find((volume) => volumes.includes(volume.name))
|
||||
)
|
||||
return false;
|
||||
if (nodePools.length && !nodePools.includes(node.get('nodePool'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (onlyIneligible && node.get('isEligible')) return false;
|
||||
if (onlyDraining && !node.get('isDraining')) return false;
|
||||
|
||||
@@ -57,6 +57,9 @@ export default class IndexController extends Controller.extend(
|
||||
{
|
||||
qpNamespace: 'namespace',
|
||||
},
|
||||
{
|
||||
qpNodePool: 'nodePool',
|
||||
},
|
||||
];
|
||||
|
||||
currentPage = 1;
|
||||
@@ -81,11 +84,13 @@ export default class IndexController extends Controller.extend(
|
||||
qpStatus = '';
|
||||
qpDatacenter = '';
|
||||
qpPrefix = '';
|
||||
qpNodePool = '';
|
||||
|
||||
@selection('qpType') selectionType;
|
||||
@selection('qpStatus') selectionStatus;
|
||||
@selection('qpDatacenter') selectionDatacenter;
|
||||
@selection('qpPrefix') selectionPrefix;
|
||||
@selection('qpNodePool') selectionNodePool;
|
||||
|
||||
@computed
|
||||
get optionsType() {
|
||||
@@ -194,6 +199,29 @@ export default class IndexController extends Controller.extend(
|
||||
return availableNamespaces;
|
||||
}
|
||||
|
||||
@computed('selectionNodePool', 'model.nodePools.[]')
|
||||
get optionsNodePool() {
|
||||
const availableNodePools = this.model.nodePools;
|
||||
|
||||
scheduleOnce('actions', () => {
|
||||
// eslint-disable-next-line ember/no-side-effects
|
||||
this.set(
|
||||
'qpNodePool',
|
||||
serialize(
|
||||
intersection(
|
||||
availableNodePools.map(({ name }) => name),
|
||||
this.selectionNodePool
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
return availableNodePools.map((nodePool) => ({
|
||||
key: nodePool.name,
|
||||
label: nodePool.name,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
Visible jobs are those that match the selected namespace and aren't children
|
||||
of periodic or parameterized jobs.
|
||||
@@ -212,6 +240,7 @@ export default class IndexController extends Controller.extend(
|
||||
'selectionType',
|
||||
'selectionStatus',
|
||||
'selectionDatacenter',
|
||||
'selectionNodePool',
|
||||
'selectionPrefix'
|
||||
)
|
||||
get filteredJobs() {
|
||||
@@ -220,6 +249,7 @@ export default class IndexController extends Controller.extend(
|
||||
selectionStatus: statuses,
|
||||
selectionDatacenter: datacenters,
|
||||
selectionPrefix: prefixes,
|
||||
selectionNodePool: nodePools,
|
||||
} = this;
|
||||
|
||||
// A job must match ALL filter facets, but it can match ANY selection within a facet
|
||||
@@ -246,6 +276,10 @@ export default class IndexController extends Controller.extend(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nodePools.length && !nodePools.includes(job.get('nodePool'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const name = job.get('name');
|
||||
if (
|
||||
prefixes.length &&
|
||||
|
||||
@@ -44,6 +44,9 @@ export default class TopologyControllers extends Controller.extend(Searchable) {
|
||||
{
|
||||
qpDatacenter: 'dc',
|
||||
},
|
||||
{
|
||||
qpNodePool: 'nodePool',
|
||||
},
|
||||
];
|
||||
|
||||
@tracked searchTerm = '';
|
||||
@@ -51,6 +54,7 @@ export default class TopologyControllers extends Controller.extend(Searchable) {
|
||||
qpVersion = '';
|
||||
qpClass = '';
|
||||
qpDatacenter = '';
|
||||
qpNodePool = '';
|
||||
|
||||
setFacetQueryParam(queryParam, selection) {
|
||||
this.set(queryParam, serialize(selection));
|
||||
@@ -59,6 +63,7 @@ export default class TopologyControllers extends Controller.extend(Searchable) {
|
||||
@selection('qpState') selectionState;
|
||||
@selection('qpClass') selectionClass;
|
||||
@selection('qpDatacenter') selectionDatacenter;
|
||||
@selection('qpNodePool') selectionNodePool;
|
||||
@selection('qpVersion') selectionVersion;
|
||||
|
||||
@computed
|
||||
@@ -109,6 +114,29 @@ export default class TopologyControllers extends Controller.extend(Searchable) {
|
||||
return datacenters.sort().map((dc) => ({ key: dc, label: dc }));
|
||||
}
|
||||
|
||||
@computed('model.nodePools.[]', 'selectionNodePool')
|
||||
get optionsNodePool() {
|
||||
const availableNodePools = this.model.nodePools;
|
||||
|
||||
scheduleOnce('actions', () => {
|
||||
// eslint-disable-next-line ember/no-side-effects
|
||||
this.set(
|
||||
'qpNodePool',
|
||||
serialize(
|
||||
intersection(
|
||||
availableNodePools.map(({ name }) => name),
|
||||
this.selectionNodePool
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
return availableNodePools.sort().map((nodePool) => ({
|
||||
key: nodePool.name,
|
||||
label: nodePool.name,
|
||||
}));
|
||||
}
|
||||
|
||||
@computed('model.nodes', 'nodes.[]', 'selectionVersion')
|
||||
get optionsVersion() {
|
||||
const versions = Array.from(
|
||||
@@ -140,6 +168,7 @@ export default class TopologyControllers extends Controller.extend(Searchable) {
|
||||
selectionVersion,
|
||||
selectionDatacenter,
|
||||
selectionClass,
|
||||
selectionNodePool,
|
||||
} = this;
|
||||
return (
|
||||
(selectionState.length ? selectionState.includes(node.status) : true) &&
|
||||
@@ -152,6 +181,9 @@ export default class TopologyControllers extends Controller.extend(Searchable) {
|
||||
(selectionClass.length
|
||||
? selectionClass.includes(node.nodeClass)
|
||||
: true) &&
|
||||
(selectionNodePool.length
|
||||
? selectionNodePool.includes(node.nodePool)
|
||||
: true) &&
|
||||
(node.name.includes(searchTerm) ||
|
||||
node.datacenter.includes(searchTerm) ||
|
||||
node.nodeClass.includes(searchTerm))
|
||||
|
||||
@@ -28,6 +28,7 @@ export default class Job extends Model {
|
||||
@attr('number') createIndex;
|
||||
@attr('number') modifyIndex;
|
||||
@attr('date') submitTime;
|
||||
@attr('string') nodePool; // Jobs are related to Node Pools either directly or via its Namespace, but no relationship.
|
||||
|
||||
@fragment('structured-attributes') meta;
|
||||
|
||||
|
||||
27
ui/app/models/node-pool.js
Normal file
27
ui/app/models/node-pool.js
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import Model from '@ember-data/model';
|
||||
import { attr } from '@ember-data/model';
|
||||
import { computed } from '@ember/object';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class NodePool extends Model {
|
||||
@attr('string') name;
|
||||
@attr('string') description;
|
||||
@attr() meta;
|
||||
@attr() schedulerConfiguration;
|
||||
|
||||
@computed('schedulerConfiguration.SchedulerAlgorithm')
|
||||
get schedulerAlgorithm() {
|
||||
return this.get('schedulerConfiguration.SchedulerAlgorithm');
|
||||
}
|
||||
|
||||
@computed('schedulerConfiguration.MemoryOversubscriptionEnabled')
|
||||
get memoryOversubscriptionEnabled() {
|
||||
return this.get('schedulerConfiguration.MemoryOversubscriptionEnabled');
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ export default class Node extends Model {
|
||||
@shortUUIDProperty('id') shortId;
|
||||
@attr('number') modifyIndex;
|
||||
@attr('string') version;
|
||||
@attr('string') nodePool;
|
||||
|
||||
// Available from single response
|
||||
@attr('string') httpAddr;
|
||||
|
||||
@@ -23,6 +23,7 @@ export default class ClientsRoute extends Route.extend(WithForbiddenState) {
|
||||
return RSVP.hash({
|
||||
nodes: this.store.findAll('node'),
|
||||
agents: this.store.findAll('agent'),
|
||||
nodePools: this.store.findAll('node-pool'),
|
||||
}).catch(notifyForbidden(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ export default class IndexRoute extends Route.extend(
|
||||
.query('job', { namespace: params.qpNamespace, meta: true })
|
||||
.catch(notifyForbidden(this)),
|
||||
namespaces: this.store.findAll('namespace'),
|
||||
nodePools: this.store.findAll('node-pool'),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,10 @@ export default class TopologyRoute extends Route.extend(WithForbiddenState) {
|
||||
namespace: '*',
|
||||
}),
|
||||
nodes: this.store.query('node', { resources: true }),
|
||||
// Nodes are not allowed to be in the 'all' node pool, so filter it out.
|
||||
nodePools: this.store
|
||||
.findAll('node-pool')
|
||||
.then((pools) => pools.filter((p) => p.name !== 'all')),
|
||||
}).catch(notifyForbidden(this));
|
||||
}
|
||||
|
||||
@@ -32,6 +36,7 @@ export default class TopologyRoute extends Route.extend(WithForbiddenState) {
|
||||
controller.model = {
|
||||
allocations: [],
|
||||
nodes: [],
|
||||
nodePools: [],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
12
ui/app/serializers/node-pool.js
Normal file
12
ui/app/serializers/node-pool.js
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class NodePool extends ApplicationSerializer {
|
||||
primaryKey = 'Name';
|
||||
}
|
||||
@@ -17,6 +17,12 @@
|
||||
font-weight: $weight-bold;
|
||||
font-size: $size-3;
|
||||
|
||||
&.justify {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.metric-units {
|
||||
font-size: $size-4;
|
||||
}
|
||||
|
||||
@@ -243,6 +243,12 @@
|
||||
</span>
|
||||
{{this.model.datacenter}}
|
||||
</span>
|
||||
<span class="pair" data-test-node-pool>
|
||||
<span class="term">
|
||||
Node Pool
|
||||
</span>
|
||||
{{this.model.nodePool}}
|
||||
</span>
|
||||
{{#if this.model.nodeClass}}
|
||||
<span class="pair" data-test-node-class>
|
||||
<span class="term">
|
||||
@@ -561,7 +567,7 @@
|
||||
</t.head>
|
||||
<t.body as |row|>
|
||||
<AllocationRow
|
||||
{{keyboard-shortcut
|
||||
{{keyboard-shortcut
|
||||
enumerated=true
|
||||
action=(action "gotoAllocation" row.model)
|
||||
}}
|
||||
@@ -885,7 +891,7 @@
|
||||
type="button"
|
||||
class="button is-primary"
|
||||
{{on "click" (action (mut this.editingMetadata) true)}}
|
||||
{{keyboard-shortcut
|
||||
{{keyboard-shortcut
|
||||
label="Add Dynamic Node Metadata"
|
||||
pattern=(array "m" "e" "t" "a")
|
||||
action=(action (mut this.editingMetadata) true)
|
||||
|
||||
@@ -20,6 +20,13 @@
|
||||
</div>
|
||||
<div class="toolbar-item is-right-aligned is-mobile-full-width">
|
||||
<div class="button-bar">
|
||||
<MultiSelectDropdown
|
||||
data-test-node-pool-facet
|
||||
@label="Node Pool"
|
||||
@options={{this.optionsNodePool}}
|
||||
@selection={{this.selectionNodePool}}
|
||||
@onSelect={{action this.setFacetQueryParam "qpNodePool"}}
|
||||
/>
|
||||
<MultiSelectDropdown
|
||||
data-test-class-facet
|
||||
@label="Class"
|
||||
@@ -81,6 +88,7 @@
|
||||
>Name</t.sort-by>
|
||||
<t.sort-by @prop="compositeStatus">State</t.sort-by>
|
||||
<th class="is-200px is-truncatable">Address</th>
|
||||
<t.sort-by @prop="nodePool">Node Pool</t.sort-by>
|
||||
<t.sort-by @prop="datacenter">Datacenter</t.sort-by>
|
||||
<t.sort-by @prop="version">Version</t.sort-by>
|
||||
<th># Volumes</th>
|
||||
@@ -91,7 +99,7 @@
|
||||
data-test-client-node-row
|
||||
@node={{row.model}}
|
||||
@onClick={{action "gotoNode" row.model}}
|
||||
{{keyboard-shortcut
|
||||
{{keyboard-shortcut
|
||||
enumerated=true
|
||||
action=(action "gotoNode" row.model)
|
||||
}}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
</span>
|
||||
</td>
|
||||
<td data-test-client-address class="is-200px is-truncatable">{{this.node.httpAddr}}</td>
|
||||
<td data-test-client-node-pool title="{{this.node.nodePool}}">{{this.node.nodePool}}</td>
|
||||
<td data-test-client-datacenter>{{this.node.datacenter}}</td>
|
||||
<td data-test-client-version>{{this.node.version}}</td>
|
||||
<td data-test-client-volumes>{{if this.node.hostVolumes.length this.node.hostVolumes.length}}</td>
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
{{@job.namespace.name}}
|
||||
</span>
|
||||
{{/if}}
|
||||
<span class="pair" data-test-job-stat="node-pool">
|
||||
<span class="term">Node Pool</span>
|
||||
{{@job.nodePool}}
|
||||
</span>
|
||||
{{yield to="after-namespace"}}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
{{this.job.namespace.name}}
|
||||
</td>
|
||||
{{/if}}
|
||||
<td data-test-job-node-pool>
|
||||
{{this.job.nodePool}}
|
||||
</td>
|
||||
{{/if}}
|
||||
{{#if (eq @context "child")}}
|
||||
<td data-test-job-submit-time>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
{{/if}}
|
||||
<span class="tooltip" aria-label="Node Name"><strong><LinkTo @route="clients.client" @model={{@node.node.id}}>{{@node.node.name}}</LinkTo></strong></span>
|
||||
<span class="bumper-left tooltip" aria-label="Number of Allocations">{{this.count}} Allocs</span>
|
||||
<span class="bumper-left is-faded tooltip" aria-label="Node Pool">{{@node.node.nodePool}}</span>
|
||||
<span class="bumper-left is-faded">
|
||||
<span class="tooltip" aria-label="Node Memory">{{format-scheduled-bytes @node.memory start="MiB"}}</span>,
|
||||
<span class="tooltip" aria-label="Node CPU">{{format-scheduled-hertz @node.cpu}}</span>
|
||||
|
||||
@@ -58,6 +58,13 @@
|
||||
@selection={{this.selectionType}}
|
||||
@onSelect={{action this.setFacetQueryParam "qpType"}}
|
||||
/>
|
||||
<MultiSelectDropdown
|
||||
data-test-node-pool-facet
|
||||
@label="Node Pool"
|
||||
@options={{this.optionsNodePool}}
|
||||
@selection={{this.selectionNodePool}}
|
||||
@onSelect={{action this.setFacetQueryParam "qpNodePool"}}
|
||||
/>
|
||||
<MultiSelectDropdown
|
||||
data-test-status-facet
|
||||
@label="Status"
|
||||
@@ -89,7 +96,7 @@
|
||||
@query={{hash namespace=this.qpNamespace}}
|
||||
data-test-run-job
|
||||
class="button is-primary"
|
||||
{{keyboard-shortcut
|
||||
{{keyboard-shortcut
|
||||
label="Run Job"
|
||||
pattern=(array "r" "u" "n")
|
||||
action=(action this.goToRun)
|
||||
@@ -136,6 +143,9 @@
|
||||
Namespace
|
||||
</t.sort-by>
|
||||
{{/if}}
|
||||
<t.sort-by @prop="nodePool">
|
||||
Node Pool
|
||||
</t.sort-by>
|
||||
<t.sort-by @prop="status">
|
||||
Status
|
||||
</t.sort-by>
|
||||
|
||||
@@ -370,7 +370,7 @@
|
||||
{{else}}
|
||||
<div class="columns is-flush">
|
||||
<div class="dashboard-metric column">
|
||||
<p data-test-node-count class="metric">
|
||||
<p data-test-node-count class="metric justify">
|
||||
{{this.model.nodes.length}}
|
||||
<span class="metric-label">
|
||||
Clients
|
||||
@@ -378,13 +378,21 @@
|
||||
</p>
|
||||
</div>
|
||||
<div class="dashboard-metric column">
|
||||
<p data-test-alloc-count class="metric">
|
||||
<p data-test-alloc-count class="metric justify">
|
||||
{{this.scheduledAllocations.length}}
|
||||
<span class="metric-label">
|
||||
Allocations
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="dashboard-metric column">
|
||||
<p data-test-node-pool-count class="metric justify">
|
||||
{{this.model.nodePools.length}}
|
||||
<span class="metric-label">
|
||||
Node Pools
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-metric with-divider">
|
||||
<p class="metric">
|
||||
@@ -479,6 +487,13 @@
|
||||
</div>
|
||||
<div class="toolbar-item is-right-aligned is-mobile-full-width">
|
||||
<div class="button-bar">
|
||||
<MultiSelectDropdown
|
||||
data-test-node-pool-facet
|
||||
@label="Node Pool"
|
||||
@options={{this.optionsNodePool}}
|
||||
@selection={{this.selectionNodePool}}
|
||||
@onSelect={{action this.setFacetQueryParam "qpNodePool"}}
|
||||
/>
|
||||
<MultiSelectDropdown
|
||||
data-test-datacenter-facet
|
||||
@label="Datacenter"
|
||||
|
||||
@@ -338,6 +338,10 @@ export default function () {
|
||||
return this.serialize(nodes.find(params.id));
|
||||
});
|
||||
|
||||
this.get('/node/pools', function ({ nodePools }) {
|
||||
return this.serialize(nodePools.all());
|
||||
});
|
||||
|
||||
this.get('/allocations');
|
||||
|
||||
this.get('/allocation/:id');
|
||||
|
||||
@@ -200,6 +200,11 @@ export default Factory.extend({
|
||||
shallow: false,
|
||||
|
||||
afterCreate(job, server) {
|
||||
Ember.assert(
|
||||
'[Mirage] No node pools! make sure node pools are created before jobs',
|
||||
server.db.nodePools.length
|
||||
);
|
||||
|
||||
if (!job.namespaceId) {
|
||||
const namespace = server.db.namespaces.length
|
||||
? pickOne(server.db.namespaces).id
|
||||
@@ -214,6 +219,12 @@ export default Factory.extend({
|
||||
});
|
||||
}
|
||||
|
||||
if (!job.nodePool) {
|
||||
job.update({
|
||||
nodePool: pickOne(server.db.nodePools).name,
|
||||
});
|
||||
}
|
||||
|
||||
const groupProps = {
|
||||
job,
|
||||
createAllocations: job.createAllocations,
|
||||
|
||||
13
ui/mirage/factories/node-pool.js
Normal file
13
ui/mirage/factories/node-pool.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { Factory } from 'ember-cli-mirage';
|
||||
|
||||
export default Factory.extend({
|
||||
name: (i) => `node-pool-${i}`,
|
||||
description: (i) => `describe node-pool-${i}`,
|
||||
meta: {},
|
||||
schedulerConfiguration: {},
|
||||
});
|
||||
@@ -5,28 +5,33 @@
|
||||
|
||||
import { Factory, trait } from 'ember-cli-mirage';
|
||||
import faker from 'nomad-ui/mirage/faker';
|
||||
import { provide } from '../utils';
|
||||
import { provide, pickOne } from '../utils';
|
||||
import { DATACENTERS, HOSTS, generateResources } from '../common';
|
||||
import moment from 'moment';
|
||||
|
||||
const UUIDS = provide(100, faker.random.uuid.bind(faker.random));
|
||||
const NODE_STATUSES = ['initializing', 'ready', 'down'];
|
||||
const NODE_CLASSES = provide(7, faker.company.bsBuzz.bind(faker.company));
|
||||
const NODE_VERSIONS = ['1.1.0-beta', '1.0.2-alpha+ent', ...provide(5, faker.system.semver)];
|
||||
const NODE_VERSIONS = [
|
||||
'1.1.0-beta',
|
||||
'1.0.2-alpha+ent',
|
||||
...provide(5, faker.system.semver),
|
||||
];
|
||||
const REF_DATE = new Date();
|
||||
|
||||
export default Factory.extend({
|
||||
id: i => (i / 100 >= 1 ? `${UUIDS[i]}-${i}` : UUIDS[i]),
|
||||
name: i => `nomad@${HOSTS[i % HOSTS.length]}`,
|
||||
id: (i) => (i / 100 >= 1 ? `${UUIDS[i]}-${i}` : UUIDS[i]),
|
||||
name: (i) => `nomad@${HOSTS[i % HOSTS.length]}`,
|
||||
|
||||
datacenter: () => faker.helpers.randomize(DATACENTERS),
|
||||
nodeClass: () => faker.helpers.randomize(NODE_CLASSES),
|
||||
drain: faker.random.boolean,
|
||||
status: () => faker.helpers.randomize(NODE_STATUSES),
|
||||
tlsEnabled: faker.random.boolean,
|
||||
schedulingEligibility: () => (faker.random.boolean() ? 'eligible' : 'ineligible'),
|
||||
schedulingEligibility: () =>
|
||||
faker.random.boolean() ? 'eligible' : 'ineligible',
|
||||
|
||||
createIndex: i => i,
|
||||
createIndex: (i) => i,
|
||||
modifyIndex: () => faker.random.number({ min: 10, max: 2000 }),
|
||||
version: () => faker.helpers.randomize(NODE_VERSIONS),
|
||||
|
||||
@@ -35,8 +40,8 @@ export default Factory.extend({
|
||||
},
|
||||
|
||||
forceIPv4: trait({
|
||||
name: i => {
|
||||
const ipv4Hosts = HOSTS.filter(h => !h.startsWith('['));
|
||||
name: (i) => {
|
||||
const ipv4Hosts = HOSTS.filter((h) => !h.startsWith('['));
|
||||
return `nomad@${ipv4Hosts[i % ipv4Hosts.length]}`;
|
||||
},
|
||||
}),
|
||||
@@ -45,8 +50,13 @@ export default Factory.extend({
|
||||
drain: true,
|
||||
schedulingEligibility: 'ineligible',
|
||||
drainStrategy: {
|
||||
Deadline: faker.random.number({ min: 30 * 1000, max: 5 * 60 * 60 * 1000 }) * 1000000,
|
||||
ForceDeadline: moment(REF_DATE).add(faker.random.number({ min: 1, max: 5 }), 'd'),
|
||||
Deadline:
|
||||
faker.random.number({ min: 30 * 1000, max: 5 * 60 * 60 * 1000 }) *
|
||||
1000000,
|
||||
ForceDeadline: moment(REF_DATE).add(
|
||||
faker.random.number({ min: 1, max: 5 }),
|
||||
'd'
|
||||
),
|
||||
IgnoreSystemJobs: faker.random.boolean(),
|
||||
},
|
||||
}),
|
||||
@@ -129,17 +139,28 @@ export default Factory.extend({
|
||||
}),
|
||||
|
||||
afterCreate(node, server) {
|
||||
Ember.assert(
|
||||
'[Mirage] No node pools! make sure node pools are created before nodes',
|
||||
server.db.nodePools.length
|
||||
);
|
||||
|
||||
// Each node has a corresponding client stat resource that's queried via node IP.
|
||||
// Create that record, even though it's not a relationship.
|
||||
server.create('client-stat', {
|
||||
id: node.httpAddr,
|
||||
});
|
||||
|
||||
const events = server.createList('node-event', faker.random.number({ min: 1, max: 10 }), {
|
||||
nodeId: node.id,
|
||||
});
|
||||
const events = server.createList(
|
||||
'node-event',
|
||||
faker.random.number({ min: 1, max: 10 }),
|
||||
{ nodeId: node.id }
|
||||
);
|
||||
const nodePool = node.nodePool
|
||||
? server.db.nodePools.findBy({ name: node.nodePool })
|
||||
: pickOne(server.db.nodePools, (pool) => pool.name !== 'all');
|
||||
|
||||
node.update({
|
||||
nodePool: nodePool.name,
|
||||
eventIds: events.mapBy('id'),
|
||||
});
|
||||
|
||||
@@ -150,7 +171,7 @@ export default Factory.extend({
|
||||
});
|
||||
|
||||
function makeDrivers() {
|
||||
const generate = name => {
|
||||
const generate = (name) => {
|
||||
const detected = faker.random.number(10) >= 3;
|
||||
const healthy = detected && faker.random.number(10) >= 3;
|
||||
const attributes = {
|
||||
|
||||
8
ui/mirage/models/node-pool.js
Normal file
8
ui/mirage/models/node-pool.js
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { Model } from 'ember-cli-mirage';
|
||||
|
||||
export default Model.extend({});
|
||||
@@ -44,9 +44,12 @@ export default function (server) {
|
||||
);
|
||||
}
|
||||
|
||||
// Make sure built-in node pools exist.
|
||||
createBuiltInNodePools(server);
|
||||
if (withNamespaces) createNamespaces(server);
|
||||
if (withTokens) createTokens(server);
|
||||
if (withRegions) createRegions(server);
|
||||
|
||||
activeScenario(server);
|
||||
}
|
||||
|
||||
@@ -56,6 +59,7 @@ function smallCluster(server) {
|
||||
faker.seed(1);
|
||||
server.create('feature', { name: 'Dynamic Application Sizing' });
|
||||
server.createList('agent', 3, 'withConsulLink', 'withVaultLink');
|
||||
server.createList('node-pool', 2);
|
||||
server.createList('node', 5);
|
||||
server.create(
|
||||
'node',
|
||||
@@ -347,6 +351,7 @@ function smallCluster(server) {
|
||||
|
||||
function mediumCluster(server) {
|
||||
server.createList('agent', 3, 'withConsulLink', 'withVaultLink');
|
||||
server.createList('node-pool', 5);
|
||||
server.createList('node', 50);
|
||||
server.createList('job', 25);
|
||||
}
|
||||
@@ -356,6 +361,7 @@ function variableTestCluster(server) {
|
||||
createTokens(server);
|
||||
createNamespaces(server);
|
||||
server.createList('agent', 3, 'withConsulLink', 'withVaultLink');
|
||||
server.createList('node-pool', 3);
|
||||
server.createList('node', 5);
|
||||
server.createList('job', 3);
|
||||
server.createList('variable', 3);
|
||||
@@ -431,6 +437,7 @@ function servicesTestCluster(server) {
|
||||
faker.seed(1);
|
||||
server.create('feature', { name: 'Dynamic Application Sizing' });
|
||||
server.createList('agent', 3, 'withConsulLink', 'withVaultLink');
|
||||
server.createList('node-pool', 3);
|
||||
server.createList('node', 5);
|
||||
server.createList('job', 1, { createRecommendations: true });
|
||||
server.create('job', {
|
||||
@@ -563,12 +570,14 @@ function servicesTestCluster(server) {
|
||||
// Due to Mirage performance, large cluster scenarios will be slow
|
||||
function largeCluster(server) {
|
||||
server.createList('agent', 5);
|
||||
server.createList('node-pool', 10);
|
||||
server.createList('node', 1000);
|
||||
server.createList('job', 100);
|
||||
}
|
||||
|
||||
function massiveCluster(server) {
|
||||
server.createList('agent', 7);
|
||||
server.createList('node-pool', 100);
|
||||
server.createList('node', 5000);
|
||||
server.createList('job', 2000);
|
||||
}
|
||||
@@ -602,6 +611,7 @@ function allNodeTypes(server) {
|
||||
|
||||
function everyFeature(server) {
|
||||
server.createList('agent', 3, 'withConsulLink', 'withVaultLink');
|
||||
server.createList('node-pool', 3);
|
||||
|
||||
server.create('node', 'forceIPv4');
|
||||
server.create('node', 'draining');
|
||||
@@ -636,6 +646,11 @@ function emptyCluster(server) {
|
||||
|
||||
// Behaviors
|
||||
|
||||
function createBuiltInNodePools(server) {
|
||||
server.create('node-pool', { name: 'default' });
|
||||
server.create('node-pool', { name: 'all' });
|
||||
}
|
||||
|
||||
function createTokens(server) {
|
||||
server.createList('token', 3);
|
||||
server.create('token', {
|
||||
|
||||
@@ -16,6 +16,7 @@ const genResources = (CPU, Memory) => ({
|
||||
|
||||
export function topoSmall(server) {
|
||||
server.createList('agent', 3, 'withConsulLink', 'withVaultLink');
|
||||
server.createList('node-pool', 4);
|
||||
server.createList('node', 12, {
|
||||
datacenter: 'dc1',
|
||||
status: 'ready',
|
||||
@@ -35,7 +36,7 @@ export function topoSmall(server) {
|
||||
['M: 512, C: 250', 'M: 600, C: 200'],
|
||||
];
|
||||
|
||||
jobResources.forEach(spec => {
|
||||
jobResources.forEach((spec) => {
|
||||
server.create('job', {
|
||||
status: 'running',
|
||||
datacenters: ['dc1'],
|
||||
@@ -98,7 +99,7 @@ export function topoMedium(server) {
|
||||
['M: 512, C: 250', 'M: 600, C: 200'],
|
||||
];
|
||||
|
||||
jobResources.forEach(spec => {
|
||||
jobResources.forEach((spec) => {
|
||||
server.create('job', {
|
||||
status: 'running',
|
||||
datacenters: ['dc1'],
|
||||
|
||||
@@ -16,8 +16,13 @@ export function provider() {
|
||||
return () => provide(...arguments);
|
||||
}
|
||||
|
||||
export function pickOne(list) {
|
||||
return list[faker.random.number(list.length - 1)];
|
||||
export function pickOne(list, filterFn) {
|
||||
let candidates = list;
|
||||
if (filterFn) {
|
||||
candidates = list.filter(filterFn);
|
||||
}
|
||||
|
||||
return candidates[faker.random.number(candidates.length - 1)];
|
||||
}
|
||||
|
||||
export function arrToObj(prop, alias = '') {
|
||||
@@ -31,7 +36,6 @@ export function arrToObj(prop, alias = '') {
|
||||
}
|
||||
|
||||
export const generateAcceptanceTestEvalMock = (id) => {
|
||||
|
||||
return {
|
||||
CreateIndex: 20,
|
||||
CreateTime: 1647899150314738000,
|
||||
|
||||
@@ -28,6 +28,7 @@ module('Acceptance | allocation detail', function (hooks) {
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('agent');
|
||||
|
||||
server.create('node-pool');
|
||||
node = server.create('node');
|
||||
job = server.create('job', {
|
||||
groupsCount: 1,
|
||||
@@ -479,6 +480,7 @@ module('Acceptance | allocation detail (rescheduled)', function (hooks) {
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('agent');
|
||||
|
||||
server.create('node-pool');
|
||||
node = server.create('node');
|
||||
job = server.create('job', { createAllocations: false });
|
||||
allocation = server.create('allocation', 'rescheduled');
|
||||
@@ -501,6 +503,7 @@ module('Acceptance | allocation detail (not running)', function (hooks) {
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('agent');
|
||||
|
||||
server.create('node-pool');
|
||||
node = server.create('node');
|
||||
job = server.create('job', { createAllocations: false });
|
||||
allocation = server.create('allocation', { clientStatus: 'pending' });
|
||||
@@ -531,6 +534,7 @@ module('Acceptance | allocation detail (preemptions)', function (hooks) {
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('agent');
|
||||
server.create('node-pool');
|
||||
node = server.create('node');
|
||||
job = server.create('job', { createAllocations: false });
|
||||
});
|
||||
@@ -669,6 +673,7 @@ module('Acceptance | allocation detail (services)', function (hooks) {
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('feature', { name: 'Dynamic Application Sizing' });
|
||||
server.createList('agent', 3, 'withConsulLink', 'withVaultLink');
|
||||
server.createList('node-pool', 3);
|
||||
server.createList('node', 5);
|
||||
server.createList('job', 1, { createRecommendations: true });
|
||||
const job = server.create('job', {
|
||||
|
||||
@@ -20,6 +20,7 @@ module('Acceptance | allocation fs', function (hooks) {
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('agent');
|
||||
server.create('node-pool');
|
||||
server.create('node', 'forceIPv4');
|
||||
const job = server.create('job', { createAllocations: false });
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ module('Acceptance | application errors ', function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
faker.seed(1);
|
||||
server.create('agent');
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
server.create('job');
|
||||
});
|
||||
|
||||
@@ -46,6 +46,7 @@ module('Acceptance | client detail', function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
window.localStorage.clear();
|
||||
|
||||
server.create('node-pool');
|
||||
server.create('node', 'forceIPv4', { schedulingEligibility: 'eligible' });
|
||||
node = server.db.nodes[0];
|
||||
|
||||
@@ -1257,6 +1258,7 @@ module('Acceptance | client detail', function (hooks) {
|
||||
return Array.from(new Set(allocs.mapBy('jobId'))).sort();
|
||||
},
|
||||
async beforeEach() {
|
||||
server.create('node-pool');
|
||||
server.createList('job', 5);
|
||||
await ClientDetail.visit({ id: node.id });
|
||||
},
|
||||
@@ -1275,6 +1277,7 @@ module('Acceptance | client detail', function (hooks) {
|
||||
'Unknown',
|
||||
],
|
||||
async beforeEach() {
|
||||
server.create('node-pool');
|
||||
server.createList('job', 5, { createAllocations: false });
|
||||
['pending', 'running', 'complete', 'failed', 'lost', 'unknown'].forEach(
|
||||
(s) => {
|
||||
@@ -1306,6 +1309,7 @@ module('Acceptance | client detail (multi-namespace)', function (hooks) {
|
||||
setupMirage(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
server.create('node-pool');
|
||||
server.create('node', 'forceIPv4', { schedulingEligibility: 'eligible' });
|
||||
node = server.db.nodes[0];
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ module('Acceptance | client monitor', function (hooks) {
|
||||
setupMirage(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
server.create('node-pool');
|
||||
node = server.create('node');
|
||||
|
||||
managementToken = server.create('token');
|
||||
|
||||
@@ -20,6 +20,7 @@ module('Acceptance | clients list', function (hooks) {
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
window.localStorage.clear();
|
||||
server.createList('node-pool', 3);
|
||||
});
|
||||
|
||||
test('it passes an accessibility audit', async function (assert) {
|
||||
@@ -73,6 +74,7 @@ module('Acceptance | clients list', function (hooks) {
|
||||
|
||||
assert.equal(nodeRow.id, node.id.split('-')[0], 'ID');
|
||||
assert.equal(nodeRow.name, node.name, 'Name');
|
||||
assert.equal(nodeRow.nodePool, node.nodePool, 'Node Pool');
|
||||
assert.equal(
|
||||
nodeRow.compositeStatus.text,
|
||||
'draining',
|
||||
@@ -324,6 +326,29 @@ module('Acceptance | clients list', function (hooks) {
|
||||
},
|
||||
});
|
||||
|
||||
testFacet('Node Pools', {
|
||||
facet: ClientsList.facets.nodePools,
|
||||
paramName: 'nodePool',
|
||||
expectedOptions() {
|
||||
return server.db.nodePools
|
||||
.filter((p) => p.name !== 'all') // The node pool 'all' should not be a filter.
|
||||
.map((p) => p.name);
|
||||
},
|
||||
async beforeEach() {
|
||||
server.create('agent');
|
||||
server.create('node-pool', { name: 'all' });
|
||||
server.create('node-pool', { name: 'default' });
|
||||
server.createList('node-pool', 10);
|
||||
|
||||
// Make sure each node pool has at least one node.
|
||||
server.db.nodePools.forEach((p) => {
|
||||
server.createList('node', 2, { nodePool: p.name });
|
||||
});
|
||||
await ClientsList.visit();
|
||||
},
|
||||
filter: (node, selection) => selection.includes(node.nodePool),
|
||||
});
|
||||
|
||||
testFacet('Datacenters', {
|
||||
facet: ClientsList.facets.datacenter,
|
||||
paramName: 'dc',
|
||||
|
||||
@@ -643,6 +643,7 @@ module('Acceptance | evaluations list', function (hooks) {
|
||||
|
||||
module('resource linking', function () {
|
||||
test('it should generate a link to the job resource', async function (assert) {
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
const job = server.create('job', { id: 'example', shallow: true });
|
||||
server.create('evaluation', { jobId: job.id });
|
||||
@@ -662,6 +663,7 @@ module('Acceptance | evaluations list', function (hooks) {
|
||||
});
|
||||
|
||||
test('it should generate a link to the node resource', async function (assert) {
|
||||
server.create('node-pool');
|
||||
const node = server.create('node');
|
||||
server.create('evaluation', { nodeId: node.id });
|
||||
await visit('/evaluations');
|
||||
|
||||
@@ -26,6 +26,7 @@ module('Acceptance | exec', function (hooks) {
|
||||
faker.seed(1);
|
||||
|
||||
server.create('agent');
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
|
||||
this.job = server.create('job', {
|
||||
|
||||
@@ -30,6 +30,7 @@ module('Acceptance | job allocations', function (hooks) {
|
||||
setupMirage(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
|
||||
job = server.create('job', {
|
||||
@@ -211,6 +212,7 @@ module('Acceptance | job allocations', function (hooks) {
|
||||
).sort();
|
||||
},
|
||||
async beforeEach() {
|
||||
server.create('node-pool');
|
||||
job = server.create('job', {
|
||||
type: 'service',
|
||||
status: 'running',
|
||||
|
||||
@@ -43,6 +43,7 @@ module('Acceptance | job clients', function (hooks) {
|
||||
},
|
||||
});
|
||||
|
||||
server.createList('node-pool', 5);
|
||||
clients = server.createList('node', 12, {
|
||||
datacenter: 'dc1',
|
||||
status: 'ready',
|
||||
|
||||
@@ -21,6 +21,7 @@ module('Acceptance | job definition', function (hooks) {
|
||||
setupCodeMirror(hooks);
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
server.create('job');
|
||||
job = server.db.jobs[0];
|
||||
@@ -137,12 +138,13 @@ module('Acceptance | job definition', function (hooks) {
|
||||
});
|
||||
});
|
||||
|
||||
module('display and edit using full specification', function (hooks) {
|
||||
module('Acceptance | job definition | full specification', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupCodeMirror(hooks);
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
server.create('job');
|
||||
job = server.db.jobs[0];
|
||||
|
||||
@@ -24,6 +24,7 @@ module('Acceptance | job deployments', function (hooks) {
|
||||
setupMirage(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
job = server.create('job');
|
||||
deployments = server.schema.deployments.where({ jobId: job.id });
|
||||
|
||||
@@ -175,6 +175,7 @@ moduleForJob(
|
||||
job,
|
||||
assert
|
||||
) {
|
||||
assert.notOk(JobDetail.jobsHeader.hasNodePool);
|
||||
assert.notOk(JobDetail.jobsHeader.hasPriority);
|
||||
assert.notOk(JobDetail.jobsHeader.hasType);
|
||||
},
|
||||
@@ -226,6 +227,7 @@ moduleForJob(
|
||||
job,
|
||||
assert
|
||||
) {
|
||||
assert.notOk(JobDetail.jobsHeader.hasNodePool);
|
||||
assert.notOk(JobDetail.jobsHeader.hasPriority);
|
||||
assert.notOk(JobDetail.jobsHeader.hasType);
|
||||
},
|
||||
@@ -319,6 +321,7 @@ module('Acceptance | job detail (with namespaces)', function (hooks) {
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
server.createList('namespace', 2);
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
job = server.create('job', {
|
||||
type: 'service',
|
||||
|
||||
@@ -47,6 +47,7 @@ function moduleForJobDispatch(title, jobFactory) {
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
// Required for placing allocations (a result of dispatching jobs)
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
|
||||
job = jobFactory();
|
||||
|
||||
@@ -19,6 +19,7 @@ module('Acceptance | job evaluations', function (hooks) {
|
||||
setupMirage(hooks);
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('node-pool');
|
||||
job = server.create('job', {
|
||||
noFailedPlacements: true,
|
||||
createAllocations: false,
|
||||
|
||||
@@ -66,6 +66,7 @@ module('Acceptance | job run', function (hooks) {
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
// Required for placing allocations (a result of creating jobs)
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
|
||||
managementToken = server.create('token');
|
||||
|
||||
@@ -28,6 +28,7 @@ module('Acceptance | job status panel', function (hooks) {
|
||||
setupMirage(hooks);
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
});
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ module('Acceptance | job versions', function (hooks) {
|
||||
setupMirage(hooks);
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('node-pool');
|
||||
server.create('namespace');
|
||||
namespace = server.create('namespace');
|
||||
|
||||
@@ -176,6 +177,7 @@ module('Acceptance | job versions (with client token)', function (hooks) {
|
||||
setupMirage(hooks);
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('node-pool');
|
||||
job = server.create('job', { createAllocations: false });
|
||||
versions = server.db.jobVersions.where({ jobId: job.id });
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ module('Acceptance | jobs list', function (hooks) {
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
// Required for placing allocations (a result of creating jobs)
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
|
||||
managementToken = server.create('token');
|
||||
@@ -70,6 +71,7 @@ module('Acceptance | jobs list', function (hooks) {
|
||||
|
||||
assert.equal(jobRow.name, job.name, 'Name');
|
||||
assert.notOk(jobRow.hasNamespace);
|
||||
assert.equal(jobRow.nodePool, job.nodePool, 'Node Pool');
|
||||
assert.equal(jobRow.link, `/ui/jobs/${job.id}@default`, 'Detail Link');
|
||||
assert.equal(jobRow.status, job.status, 'Status');
|
||||
assert.equal(jobRow.type, typeForJob(job), 'Type');
|
||||
|
||||
@@ -248,6 +248,7 @@ module('Acceptance | keyboard', function (hooks) {
|
||||
|
||||
module('Dynamic Nav', function (dynamicHooks) {
|
||||
dynamicHooks.beforeEach(async function () {
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
});
|
||||
test('Dynamic Table Nav', async function (assert) {
|
||||
|
||||
@@ -40,6 +40,7 @@ module('Acceptance | optimize', function (hooks) {
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('feature', { name: 'Dynamic Application Sizing' });
|
||||
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
|
||||
server.createList('namespace', 2);
|
||||
@@ -440,6 +441,7 @@ module('Acceptance | optimize search and facets', function (hooks) {
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('feature', { name: 'Dynamic Application Sizing' });
|
||||
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
|
||||
server.createList('namespace', 2);
|
||||
|
||||
@@ -19,6 +19,7 @@ module('Acceptance | plugin allocations', function (hooks) {
|
||||
let plugin;
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
window.localStorage.clear();
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ module('Acceptance | plugin detail', function (hooks) {
|
||||
let plugin;
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
plugin = server.create('csi-plugin', { controllerRequired: true });
|
||||
});
|
||||
|
||||
@@ -17,6 +17,7 @@ module('Acceptance | plugins list', function (hooks) {
|
||||
setupMirage(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
window.localStorage.clear();
|
||||
});
|
||||
|
||||
@@ -22,6 +22,7 @@ module('Acceptance | regions (only one)', function (hooks) {
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
server.create('agent');
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
server.createList('job', 2, {
|
||||
createAllocations: false,
|
||||
@@ -88,6 +89,7 @@ module('Acceptance | regions (many)', function (hooks) {
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
server.create('agent');
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
server.createList('job', 2, {
|
||||
createAllocations: false,
|
||||
|
||||
@@ -19,6 +19,7 @@ module('Acceptance | search', function (hooks) {
|
||||
setupMirage(hooks);
|
||||
|
||||
test('search exposes and navigates to results from the fuzzy search endpoint', async function (assert) {
|
||||
server.create('node-pool');
|
||||
server.create('node', { name: 'xyz' });
|
||||
const otherNode = server.create('node', { name: 'ghi' });
|
||||
|
||||
@@ -184,6 +185,7 @@ module('Acceptance | search', function (hooks) {
|
||||
});
|
||||
|
||||
test('results are truncated at 10 per group', async function (assert) {
|
||||
server.create('node-pool');
|
||||
server.create('node', { name: 'xyz' });
|
||||
|
||||
for (let i = 0; i < 11; i++) {
|
||||
@@ -203,6 +205,7 @@ module('Acceptance | search', function (hooks) {
|
||||
});
|
||||
|
||||
test('server-side truncation is indicated in the group label', async function (assert) {
|
||||
server.create('node-pool');
|
||||
server.create('node', { name: 'xyz' });
|
||||
|
||||
for (let i = 0; i < 21; i++) {
|
||||
@@ -241,6 +244,7 @@ module('Acceptance | search', function (hooks) {
|
||||
});
|
||||
|
||||
test('pressing slash when an input element is focused does not start a search', async function (assert) {
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
server.create('job');
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import faker from 'nomad-ui/mirage/faker';
|
||||
|
||||
const minimumSetup = () => {
|
||||
faker.seed(1);
|
||||
server.createList('node-pool', 1);
|
||||
server.createList('node', 1);
|
||||
server.createList('agent', 1);
|
||||
};
|
||||
@@ -42,6 +43,7 @@ module('Acceptance | servers list', function (hooks) {
|
||||
|
||||
test('/servers should list all servers', async function (assert) {
|
||||
faker.seed(1);
|
||||
server.createList('node-pool', 1);
|
||||
server.createList('node', 1);
|
||||
server.createList('agent', 10);
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ module('Acceptance | task detail', function (hooks) {
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('agent');
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
server.create('job', { createAllocations: false });
|
||||
allocation = server.create('allocation', 'withTaskWithPorts', {
|
||||
@@ -337,6 +338,7 @@ module('Acceptance | task detail (no addresses)', function (hooks) {
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('agent');
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
server.create('job');
|
||||
allocation = server.create('allocation', 'withoutTaskWithPorts', {
|
||||
@@ -354,6 +356,7 @@ module('Acceptance | task detail (different namespace)', function (hooks) {
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('agent');
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
server.create('namespace');
|
||||
server.create('namespace', { id: 'other-namespace' });
|
||||
@@ -412,6 +415,7 @@ module('Acceptance | task detail (not running)', function (hooks) {
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('agent');
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
server.create('namespace');
|
||||
server.create('namespace', { id: 'other-namespace' });
|
||||
@@ -447,6 +451,7 @@ module('Acceptance | proxy task detail', function (hooks) {
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('agent');
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
server.create('job', { createAllocations: false });
|
||||
allocation = server.create('allocation', 'withTaskWithPorts', {
|
||||
|
||||
@@ -21,6 +21,7 @@ module('Acceptance | task fs', function (hooks) {
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('agent');
|
||||
server.create('node-pool');
|
||||
server.create('node', 'forceIPv4');
|
||||
const job = server.create('job', { createAllocations: false });
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ module('Acceptance | task group detail', function (hooks) {
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('agent');
|
||||
server.create('node-pool');
|
||||
server.create('node', 'forceIPv4');
|
||||
|
||||
job = server.create('job', {
|
||||
|
||||
@@ -25,6 +25,7 @@ module('Acceptance | task logs', function (hooks) {
|
||||
hooks.beforeEach(async function () {
|
||||
faker.seed(1);
|
||||
server.create('agent');
|
||||
server.create('node-pool');
|
||||
server.create('node', 'forceIPv4');
|
||||
job = server.create('job', { createAllocations: false });
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ module('Acceptance | tokens', function (hooks) {
|
||||
faker.seed(1);
|
||||
|
||||
server.create('agent');
|
||||
server.create('node-pool');
|
||||
node = server.create('node');
|
||||
job = server.create('job');
|
||||
managementToken = server.create('token');
|
||||
|
||||
@@ -29,6 +29,7 @@ module('Acceptance | topology', function (hooks) {
|
||||
setupMirage(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
server.createList('node-pool', 5);
|
||||
server.create('job', { createAllocations: false });
|
||||
});
|
||||
|
||||
@@ -44,6 +45,7 @@ module('Acceptance | topology', function (hooks) {
|
||||
|
||||
test('by default the info panel shows cluster aggregate stats', async function (assert) {
|
||||
faker.seed(1);
|
||||
server.create('node-pool', { name: 'all' });
|
||||
server.createList('node', 3);
|
||||
server.createList('allocation', 5);
|
||||
|
||||
@@ -68,6 +70,15 @@ module('Acceptance | topology', function (hooks) {
|
||||
`${scheduledAllocs.length} Allocations`
|
||||
);
|
||||
|
||||
// Node pool count ignores 'all'.
|
||||
const nodePools = server.schema.nodePools
|
||||
.all()
|
||||
.models.filter((p) => p.name !== 'all');
|
||||
assert.equal(
|
||||
Topology.clusterInfoPanel.nodePoolCount,
|
||||
`${nodePools.length} Node Pools`
|
||||
);
|
||||
|
||||
const nodeResources = server.schema.nodes
|
||||
.all()
|
||||
.models.mapBy('nodeResources');
|
||||
@@ -322,22 +333,41 @@ module('Acceptance | topology', function (hooks) {
|
||||
server.createList('node', 2, {
|
||||
nodeClass: 'foo-bar-baz',
|
||||
});
|
||||
|
||||
// Create node pool exclusive for these nodes.
|
||||
server.create('node-pool', { name: 'test-node-pool' });
|
||||
server.createList('node', 3, {
|
||||
nodePool: 'test-node-pool',
|
||||
});
|
||||
|
||||
server.createList('allocation', 5);
|
||||
|
||||
await Topology.visit();
|
||||
assert.dom('[data-test-topo-viz-node]').exists({ count: 12 });
|
||||
assert.dom('[data-test-topo-viz-node]').exists({ count: 15 });
|
||||
|
||||
await typeIn('input.node-search', server.schema.nodes.first().name);
|
||||
assert.dom('[data-test-topo-viz-node]').exists({ count: 1 });
|
||||
await typeIn('input.node-search', server.schema.nodes.first().name);
|
||||
assert.dom('[data-test-topo-viz-node]').doesNotExist();
|
||||
await click('[title="Clear search"]');
|
||||
assert.dom('[data-test-topo-viz-node]').exists({ count: 12 });
|
||||
assert.dom('[data-test-topo-viz-node]').exists({ count: 15 });
|
||||
|
||||
await Topology.facets.class.toggle();
|
||||
await Topology.facets.class.options
|
||||
.findOneBy('label', 'foo-bar-baz')
|
||||
.toggle();
|
||||
assert.dom('[data-test-topo-viz-node]').exists({ count: 2 });
|
||||
await Topology.facets.class.options
|
||||
.findOneBy('label', 'foo-bar-baz')
|
||||
.toggle();
|
||||
|
||||
await Topology.facets.nodePool.toggle();
|
||||
await Topology.facets.nodePool.options
|
||||
.findOneBy('label', 'test-node-pool')
|
||||
.toggle();
|
||||
assert.dom('[data-test-topo-viz-node]').exists({ count: 3 });
|
||||
await Topology.facets.nodePool.options
|
||||
.findOneBy('label', 'test-node-pool')
|
||||
.toggle();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -33,6 +33,7 @@ module('Acceptance | volume detail', function (hooks) {
|
||||
let volume;
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
server.create('csi-plugin', { createVolumes: false });
|
||||
volume = server.create('csi-volume');
|
||||
@@ -243,6 +244,7 @@ module('Acceptance | volume detail (with namespaces)', function (hooks) {
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
server.createList('namespace', 2);
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
server.create('csi-plugin', { createVolumes: false });
|
||||
volume = server.create('csi-volume');
|
||||
|
||||
@@ -32,6 +32,7 @@ module('Acceptance | volumes list', function (hooks) {
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
faker.seed(1);
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
server.create('csi-plugin', { createVolumes: false });
|
||||
window.localStorage.clear();
|
||||
|
||||
@@ -41,6 +41,7 @@ export default function moduleForJob(
|
||||
});
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
server.create('node-pool');
|
||||
server.create('node');
|
||||
job = jobFactory();
|
||||
if (!job.namespace || job.namespace === 'default') {
|
||||
@@ -103,6 +104,19 @@ export default function moduleForJob(
|
||||
}
|
||||
});
|
||||
|
||||
test('page header displays job information', async function (assert) {
|
||||
assert.equal(JobDetail.statFor('type').text, `Type ${job.type}`);
|
||||
assert.equal(
|
||||
JobDetail.statFor('priority').text,
|
||||
`Priority ${job.priority}`
|
||||
);
|
||||
assert.equal(JobDetail.statFor('version').text, `Version ${job.version}`);
|
||||
assert.equal(
|
||||
JobDetail.statFor('node-pool').text,
|
||||
`Node Pool ${job.nodePool}`
|
||||
);
|
||||
});
|
||||
|
||||
if (context === 'allocations') {
|
||||
test('allocations for the job are shown in the overview', async function (assert) {
|
||||
if (jobTypesWithStatusPanel.includes(job.type)) {
|
||||
@@ -252,6 +266,7 @@ export function moduleForJobWithClientStatus(
|
||||
setupMirage(hooks);
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
server.createList('node-pool', 3);
|
||||
const clients = server.createList('node', 3, {
|
||||
datacenter: 'dc1',
|
||||
status: 'ready',
|
||||
|
||||
@@ -21,6 +21,7 @@ module('Integration | Component | allocation row', function (hooks) {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.server = startMirage();
|
||||
this.server.create('namespace');
|
||||
this.server.create('node-pool');
|
||||
this.server.create('node');
|
||||
this.server.create('job', { createAllocations: false });
|
||||
});
|
||||
|
||||
@@ -31,6 +31,7 @@ module('Integration | Component | job-editor', function (hooks) {
|
||||
this.server = startMirage();
|
||||
|
||||
// Required for placing allocations (a result of creating jobs)
|
||||
this.server.create('node-pool');
|
||||
this.server.create('node');
|
||||
});
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ module('Integration | Component | job-page/parts/children', function (hooks) {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.server = startMirage();
|
||||
this.server.create('namespace');
|
||||
this.server.create('node-pool');
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
|
||||
@@ -24,6 +24,7 @@ module(
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.server = startMirage();
|
||||
this.server.create('namespace');
|
||||
this.server.create('node-pool');
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
|
||||
@@ -20,6 +20,7 @@ module('Integration | Component | job-page/parts/summary', function (hooks) {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.server = startMirage();
|
||||
this.server.create('namespace');
|
||||
this.server.create('node-pool');
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
|
||||
@@ -25,6 +25,7 @@ module(
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.server = startMirage();
|
||||
this.server.create('namespace');
|
||||
this.server.create('node-pool');
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
|
||||
@@ -39,6 +39,7 @@ module('Integration | Component | job-page/periodic', function (hooks) {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.server = startMirage();
|
||||
this.server.create('namespace');
|
||||
this.server.create('node-pool');
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
|
||||
@@ -31,6 +31,7 @@ module('Integration | Component | job-page/service', function (hooks) {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.server = startMirage();
|
||||
this.server.create('namespace');
|
||||
this.server.create('node-pool');
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
|
||||
@@ -22,6 +22,7 @@ module(
|
||||
window.localStorage.clear();
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.server = startMirage();
|
||||
this.server.create('node-pool');
|
||||
this.server.create('namespace');
|
||||
});
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ module('Integration | Component | plugin allocation row', function (hooks) {
|
||||
fragmentSerializerInitializer(this.owner);
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.server = startMirage();
|
||||
this.server.create('node-pool');
|
||||
this.server.create('node');
|
||||
});
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ module('Integration | Component | PrimaryMetric::Allocation', function (hooks) {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.server = startMirage();
|
||||
this.server.create('namespace');
|
||||
this.server.create('node-pool');
|
||||
this.server.create('node');
|
||||
this.server.create('job', {
|
||||
groupsCount: 1,
|
||||
|
||||
@@ -22,6 +22,7 @@ module('Integration | Component | PrimaryMetric::Node', function (hooks) {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.server = startMirage();
|
||||
this.server.create('namespace');
|
||||
this.server.create('node-pool');
|
||||
this.server.create('node');
|
||||
});
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ module('Integration | Component | PrimaryMetric::Task', function (hooks) {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.server = startMirage();
|
||||
this.server.create('namespace');
|
||||
this.server.create('node-pool');
|
||||
this.server.create('node');
|
||||
const job = this.server.create('job', {
|
||||
groupsCount: 1,
|
||||
|
||||
@@ -18,6 +18,7 @@ module('Integration | Component | reschedule event timeline', function (hooks) {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.server = startMirage();
|
||||
this.server.create('namespace');
|
||||
this.server.create('node-pool');
|
||||
this.server.create('node');
|
||||
this.server.create('job', { createAllocations: false });
|
||||
});
|
||||
|
||||
@@ -20,6 +20,7 @@ module('Integration | Component | scale-events-accordion', function (hooks) {
|
||||
fragmentSerializerInitializer(this.owner);
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.server = startMirage();
|
||||
this.server.create('node-pool');
|
||||
this.server.create('node');
|
||||
this.taskGroupWithEvents = async function (events) {
|
||||
const job = this.server.create('job', { createAllocations: false });
|
||||
|
||||
@@ -58,6 +58,7 @@ module('Integration | Component | task group row', function (hooks) {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.token = this.owner.lookup('service:token');
|
||||
this.server = startMirage();
|
||||
this.server.create('node-pool');
|
||||
this.server.create('node');
|
||||
|
||||
managementToken = this.server.create('token');
|
||||
|
||||
@@ -50,6 +50,7 @@ export default create({
|
||||
},
|
||||
|
||||
address: text('[data-test-client-address]'),
|
||||
nodePool: text('[data-test-client-node-pool]'),
|
||||
datacenter: text('[data-test-client-datacenter]'),
|
||||
version: text('[data-test-client-version]'),
|
||||
allocations: text('[data-test-client-allocations]'),
|
||||
@@ -75,6 +76,7 @@ export default create({
|
||||
},
|
||||
|
||||
facets: {
|
||||
nodePools: multiFacet('[data-test-node-pool-facet]'),
|
||||
class: multiFacet('[data-test-class-facet]'),
|
||||
state: multiFacet('[data-test-state-facet]'),
|
||||
datacenter: multiFacet('[data-test-datacenter-facet]'),
|
||||
|
||||
@@ -108,6 +108,7 @@ export default create({
|
||||
scope: '[data-test-jobs-header]',
|
||||
hasSubmitTime: isPresent('[data-test-jobs-submit-time-header]'),
|
||||
hasNamespace: isPresent('[data-test-jobs-namespace-header]'),
|
||||
hasNodePool: isPresent('[data-test-jobs-node-pool-header]'),
|
||||
hasType: isPresent('[data-test-jobs-type-header]'),
|
||||
hasPriority: isPresent('[data-test-jobs-priority-header]'),
|
||||
},
|
||||
@@ -115,8 +116,9 @@ export default create({
|
||||
jobs: collection('[data-test-job-row]', {
|
||||
id: attribute('data-test-job-row'),
|
||||
name: text('[data-test-job-name]'),
|
||||
namespace: text('[data-test-job-namespace]'),
|
||||
link: attribute('href', '[data-test-job-name] a'),
|
||||
namespace: text('[data-test-job-namespace]'),
|
||||
nodePool: text('[data-test-job-node-pool]'),
|
||||
submitTime: text('[data-test-job-submit-time]'),
|
||||
status: text('[data-test-job-status]'),
|
||||
type: text('[data-test-job-type]'),
|
||||
|
||||
@@ -36,8 +36,9 @@ export default create({
|
||||
jobs: collection('[data-test-job-row]', {
|
||||
id: attribute('data-test-job-row'),
|
||||
name: text('[data-test-job-name]'),
|
||||
namespace: text('[data-test-job-namespace]'),
|
||||
link: attribute('href', '[data-test-job-name] a'),
|
||||
namespace: text('[data-test-job-namespace]'),
|
||||
nodePool: text('[data-test-job-node-pool]'),
|
||||
status: text('[data-test-job-status]'),
|
||||
type: text('[data-test-job-type]'),
|
||||
priority: text('[data-test-job-priority]'),
|
||||
|
||||
@@ -26,6 +26,7 @@ export default create({
|
||||
viz: TopoViz('[data-test-topo-viz]'),
|
||||
|
||||
facets: {
|
||||
nodePool: multiFacet('[data-test-node-pool-facet]'),
|
||||
datacenter: multiFacet('[data-test-datacenter-facet]'),
|
||||
class: multiFacet('[data-test-class-facet]'),
|
||||
state: multiFacet('[data-test-state-facet]'),
|
||||
@@ -36,6 +37,7 @@ export default create({
|
||||
scope: '[data-test-info-panel]',
|
||||
nodeCount: text('[data-test-node-count]'),
|
||||
allocCount: text('[data-test-alloc-count]'),
|
||||
nodePoolCount: text('[data-test-node-pool-count]'),
|
||||
|
||||
memoryProgressValue: attribute('value', '[data-test-memory-progress-bar]'),
|
||||
memoryAbsoluteValue: text('[data-test-memory-absolute-value]'),
|
||||
|
||||
@@ -25,6 +25,7 @@ module('Unit | Adapter | Allocation', function (hooks) {
|
||||
this.server.create('region', { id: 'region-1' });
|
||||
this.server.create('region', { id: 'region-2' });
|
||||
|
||||
this.server.create('node-pool');
|
||||
this.server.create('node');
|
||||
this.server.create('job', { createAllocations: false });
|
||||
this.server.create('allocation', { id: 'alloc-1' });
|
||||
|
||||
@@ -25,6 +25,7 @@ module('Unit | Adapter | Deployment', function (hooks) {
|
||||
this.server.create('region', { id: 'region-1' });
|
||||
this.server.create('region', { id: 'region-2' });
|
||||
|
||||
this.server.create('node-pool');
|
||||
this.server.create('node');
|
||||
const job = this.server.create('job', { createAllocations: false });
|
||||
const deploymentRecord = server.schema.deployments.where({
|
||||
|
||||
@@ -34,6 +34,7 @@ module('Unit | Adapter | Job', function (hooks) {
|
||||
|
||||
this.server.create('namespace');
|
||||
this.server.create('namespace', { id: 'some-namespace' });
|
||||
this.server.create('node-pool');
|
||||
this.server.create('node');
|
||||
this.server.create('job', { id: 'job-1', namespaceId: 'default' });
|
||||
this.server.create('job', { id: 'job-2', namespaceId: 'some-namespace' });
|
||||
|
||||
@@ -23,6 +23,7 @@ module('Unit | Adapter | Node', function (hooks) {
|
||||
this.server.create('region', { id: 'region-1' });
|
||||
this.server.create('region', { id: 'region-2' });
|
||||
|
||||
this.server.create('node-pool');
|
||||
this.server.create('node', { id: 'node-1' });
|
||||
this.server.create('node', { id: 'node-2' });
|
||||
this.server.create('job', { id: 'job-1', createAllocations: false });
|
||||
|
||||
@@ -25,6 +25,7 @@ module('Unit | Adapter | Volume', function (hooks) {
|
||||
this.initializeUI = async () => {
|
||||
this.server.create('namespace');
|
||||
this.server.create('namespace', { id: 'some-namespace' });
|
||||
this.server.create('node-pool');
|
||||
this.server.create('node');
|
||||
this.server.create('job', { id: 'job-1', namespaceId: 'default' });
|
||||
this.server.create('csi-plugin', 2);
|
||||
|
||||
121
ui/tests/unit/serializers/node-pool-test.js
Normal file
121
ui/tests/unit/serializers/node-pool-test.js
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Serializer | NodePool', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.subject = () => this.store.serializerFor('node-pool');
|
||||
});
|
||||
|
||||
test('should serialize a NodePool', function (assert) {
|
||||
const testCases = [
|
||||
{
|
||||
name: 'full node pool',
|
||||
input: {
|
||||
name: 'prod-eng',
|
||||
description: 'Production workloads',
|
||||
meta: {
|
||||
env: 'production',
|
||||
team: 'engineering',
|
||||
},
|
||||
schedulerConfiguration: {
|
||||
SchedulerAlgorithm: 'spread',
|
||||
MemoryOversubscriptionEnabled: true,
|
||||
},
|
||||
},
|
||||
expected: {
|
||||
Name: 'prod-eng',
|
||||
Description: 'Production workloads',
|
||||
Meta: {
|
||||
env: 'production',
|
||||
team: 'engineering',
|
||||
},
|
||||
SchedulerConfiguration: {
|
||||
SchedulerAlgorithm: 'spread',
|
||||
MemoryOversubscriptionEnabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'node pool without scheduler configuration',
|
||||
input: {
|
||||
name: 'prod-eng',
|
||||
description: 'Production workloads',
|
||||
meta: {
|
||||
env: 'production',
|
||||
team: 'engineering',
|
||||
},
|
||||
},
|
||||
expected: {
|
||||
Name: 'prod-eng',
|
||||
Description: 'Production workloads',
|
||||
Meta: {
|
||||
env: 'production',
|
||||
team: 'engineering',
|
||||
},
|
||||
SchedulerConfiguration: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'node pool with null scheduler configuration',
|
||||
input: {
|
||||
name: 'prod-eng',
|
||||
description: 'Production workloads',
|
||||
meta: {
|
||||
env: 'production',
|
||||
team: 'engineering',
|
||||
},
|
||||
schedulerConfiguration: null,
|
||||
},
|
||||
expected: {
|
||||
Name: 'prod-eng',
|
||||
Description: 'Production workloads',
|
||||
Meta: {
|
||||
env: 'production',
|
||||
team: 'engineering',
|
||||
},
|
||||
SchedulerConfiguration: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'node pool with empty scheduler configuration',
|
||||
input: {
|
||||
name: 'prod-eng',
|
||||
description: 'Production workloads',
|
||||
meta: {
|
||||
env: 'production',
|
||||
team: 'engineering',
|
||||
},
|
||||
schedulerConfiguration: {},
|
||||
},
|
||||
expected: {
|
||||
Name: 'prod-eng',
|
||||
Description: 'Production workloads',
|
||||
Meta: {
|
||||
env: 'production',
|
||||
team: 'engineering',
|
||||
},
|
||||
SchedulerConfiguration: {},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
assert.expect(testCases.length);
|
||||
for (const tc of testCases) {
|
||||
const nodePool = this.store.createRecord('node-pool', tc.input);
|
||||
const got = this.subject().serialize(nodePool._createSnapshot());
|
||||
assert.deepEqual(
|
||||
got,
|
||||
tc.expected,
|
||||
`${tc.name} failed, got ${JSON.stringify(got)}`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user