mirror of
https://github.com/kemko/nomad.git
synced 2026-01-06 10:25:42 +03:00
New test coverage for the drain capabilities
This commit is contained in:
@@ -42,7 +42,7 @@ export default Watchable.extend({
|
||||
return this.drain(
|
||||
node,
|
||||
Object.assign({}, drainSpec, {
|
||||
Deadline: -1000 * 1000000,
|
||||
Deadline: -1,
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
@@ -110,6 +110,7 @@
|
||||
<div class="toolbar-item is-right-aligned is-top-aligned">
|
||||
{{#if model.isDraining}}
|
||||
{{two-step-button
|
||||
data-test-drain-stop
|
||||
idleText="Stop Drain"
|
||||
cancelText="Cancel"
|
||||
confirmText="Yes, Stop"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{{#popover-menu
|
||||
data-test-drain-popover
|
||||
label=(if client.isDraining "Update" "Drain")
|
||||
label=(if client.isDraining "Update Drain" "Drain")
|
||||
triggerClass=(concat "is-small " (if drain.isRunning "is-loading")) as |m|}}
|
||||
<form onsubmit={{action (queue (action preventDefault) (perform drain m.actions.close))}} class="form is-small">
|
||||
<form data-test-drain-popover-form onsubmit={{action (queue (action preventDefault) (perform drain m.actions.close))}} class="form is-small">
|
||||
<h4 class="group-heading">Drain Options</h4>
|
||||
<div class="field">
|
||||
<label class="label is-interactive">
|
||||
@@ -20,6 +20,8 @@
|
||||
{{#if deadlineEnabled}}
|
||||
<div class="field is-sub-field">
|
||||
{{#power-select
|
||||
data-test-drain-deadline-option-select
|
||||
tagName="div"
|
||||
options=durationQuickOptions
|
||||
selected=selectedDurationQuickOption
|
||||
onChange=(action (mut selectedDurationQuickOption)) as |opt|}}
|
||||
@@ -30,6 +32,7 @@
|
||||
<div class="field is-sub-field">
|
||||
<label class="label">Deadline</label>
|
||||
<input
|
||||
data-test-drain-custom-deadline
|
||||
type="text"
|
||||
class="input {{if parseError "is-danger"}}"
|
||||
placeholder="1h30m"
|
||||
@@ -45,6 +48,7 @@
|
||||
<div class="field">
|
||||
<label class="label is-interactive">
|
||||
{{#toggle
|
||||
data-test-force-drain-toggle
|
||||
isActive=forceDrain
|
||||
onToggle=(action (mut forceDrain) value="target.checked")}}
|
||||
Force Drain
|
||||
@@ -57,6 +61,7 @@
|
||||
<div class="field">
|
||||
<label class="label is-interactive">
|
||||
{{#toggle
|
||||
data-test-system-jobs-toggle
|
||||
isActive=drainSystemJobs
|
||||
onToggle=(action (mut drainSystemJobs) value="target.checked")}}
|
||||
Drain System Jobs
|
||||
@@ -68,12 +73,13 @@
|
||||
</div>
|
||||
<div class="popover-actions">
|
||||
<button
|
||||
data-test-drain-submit
|
||||
type="button"
|
||||
class="popover-action is-primary"
|
||||
onclick={{perform drain m.actions.close}}>
|
||||
Drain
|
||||
</button>
|
||||
<button type="button" class="popover-action" onclick={{action m.actions.close}}>Cancel</button>
|
||||
<button data-test-drain-cancel type="button" class="popover-action" onclick={{action m.actions.close}}>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
{{/popover-menu}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { currentURL } from '@ember/test-helpers';
|
||||
import { currentURL, waitUntil } from '@ember/test-helpers';
|
||||
import { assign } from '@ember/polyfills';
|
||||
import { module, test } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
@@ -549,6 +549,348 @@ module('Acceptance | client detail', function(hooks) {
|
||||
'Drain System Jobs state is shown'
|
||||
);
|
||||
});
|
||||
|
||||
test('toggling node eligibility disables the toggle and sends the correct POST request', async function(assert) {
|
||||
node = server.create('node', {
|
||||
drain: false,
|
||||
schedulingEligibility: 'eligible',
|
||||
});
|
||||
|
||||
server.pretender.post('/v1/node/:id/eligibility', () => [200, {}, ''], true);
|
||||
|
||||
await ClientDetail.visit({ id: node.id });
|
||||
assert.ok(ClientDetail.eligibilityToggle.isActive);
|
||||
|
||||
ClientDetail.eligibilityToggle.toggle();
|
||||
await waitUntil(() => server.pretender.handledRequests.findBy('method', 'POST'));
|
||||
|
||||
assert.ok(ClientDetail.eligibilityToggle.isDisabled);
|
||||
server.pretender.resolve(server.pretender.requestReferences[0].request);
|
||||
|
||||
assert.notOk(ClientDetail.eligibilityToggle.isActive);
|
||||
assert.notOk(ClientDetail.eligibilityToggle.isDisabled);
|
||||
|
||||
const request = server.pretender.handledRequests.findBy('method', 'POST');
|
||||
assert.equal(request.url, `/v1/node/${node.id}/eligibility`);
|
||||
assert.deepEqual(JSON.parse(request.requestBody), {
|
||||
NodeID: node.id,
|
||||
Eligibility: 'ineligible',
|
||||
});
|
||||
|
||||
ClientDetail.eligibilityToggle.toggle();
|
||||
await waitUntil(() => server.pretender.handledRequests.filterBy('method', 'POST').length === 2);
|
||||
server.pretender.resolve(server.pretender.requestReferences[0].request);
|
||||
|
||||
assert.ok(ClientDetail.eligibilityToggle.isActive);
|
||||
const request2 = server.pretender.handledRequests.filterBy('method', 'POST')[1];
|
||||
|
||||
assert.equal(request2.url, `/v1/node/${node.id}/eligibility`);
|
||||
assert.deepEqual(JSON.parse(request2.requestBody), {
|
||||
NodeID: node.id,
|
||||
Eligibility: 'eligible',
|
||||
});
|
||||
});
|
||||
|
||||
test('starting a drain sends the correct POST request', async function(assert) {
|
||||
let request;
|
||||
|
||||
node = server.create('node', {
|
||||
drain: false,
|
||||
schedulingEligibility: 'eligible',
|
||||
});
|
||||
|
||||
await ClientDetail.visit({ id: node.id });
|
||||
await ClientDetail.drainPopover.toggle();
|
||||
await ClientDetail.drainPopover.submit();
|
||||
|
||||
request = server.pretender.handledRequests.filterBy('method', 'POST').pop();
|
||||
|
||||
assert.equal(request.url, `/v1/node/${node.id}/drain`);
|
||||
assert.deepEqual(
|
||||
JSON.parse(request.requestBody),
|
||||
{
|
||||
NodeID: node.id,
|
||||
DrainSpec: {
|
||||
Deadline: 0,
|
||||
IgnoreSystemJobs: false,
|
||||
},
|
||||
},
|
||||
'Drain with default settings'
|
||||
);
|
||||
|
||||
await ClientDetail.drainPopover.toggle();
|
||||
await ClientDetail.drainPopover.deadlineToggle.toggle();
|
||||
await ClientDetail.drainPopover.submit();
|
||||
|
||||
request = server.pretender.handledRequests.filterBy('method', 'POST').pop();
|
||||
|
||||
assert.deepEqual(
|
||||
JSON.parse(request.requestBody),
|
||||
{
|
||||
NodeID: node.id,
|
||||
DrainSpec: {
|
||||
Deadline: 60 * 60 * 1000 * 1000000,
|
||||
IgnoreSystemJobs: false,
|
||||
},
|
||||
},
|
||||
'Drain with deadline toggled'
|
||||
);
|
||||
|
||||
await ClientDetail.drainPopover.toggle();
|
||||
await ClientDetail.drainPopover.deadlineOptions.open();
|
||||
await ClientDetail.drainPopover.deadlineOptions.options[1].choose();
|
||||
await ClientDetail.drainPopover.submit();
|
||||
|
||||
request = server.pretender.handledRequests.filterBy('method', 'POST').pop();
|
||||
|
||||
assert.deepEqual(
|
||||
JSON.parse(request.requestBody),
|
||||
{
|
||||
NodeID: node.id,
|
||||
DrainSpec: {
|
||||
Deadline: 4 * 60 * 60 * 1000 * 1000000,
|
||||
IgnoreSystemJobs: false,
|
||||
},
|
||||
},
|
||||
'Drain with non-default preset deadline set'
|
||||
);
|
||||
|
||||
await ClientDetail.drainPopover.toggle();
|
||||
await ClientDetail.drainPopover.deadlineOptions.open();
|
||||
const optionsCount = ClientDetail.drainPopover.deadlineOptions.options.length;
|
||||
await ClientDetail.drainPopover.deadlineOptions.options.objectAt(optionsCount - 1).choose();
|
||||
await ClientDetail.drainPopover.setCustomDeadline('1h40m20s');
|
||||
await ClientDetail.drainPopover.submit();
|
||||
|
||||
request = server.pretender.handledRequests.filterBy('method', 'POST').pop();
|
||||
|
||||
assert.deepEqual(
|
||||
JSON.parse(request.requestBody),
|
||||
{
|
||||
NodeID: node.id,
|
||||
DrainSpec: {
|
||||
Deadline: ((1 * 60 + 40) * 60 + 20) * 1000 * 1000000,
|
||||
IgnoreSystemJobs: false,
|
||||
},
|
||||
},
|
||||
'Drain with custom deadline set'
|
||||
);
|
||||
|
||||
await ClientDetail.drainPopover.toggle();
|
||||
await ClientDetail.drainPopover.deadlineToggle.toggle();
|
||||
await ClientDetail.drainPopover.forceDrainToggle.toggle();
|
||||
await ClientDetail.drainPopover.submit();
|
||||
|
||||
request = server.pretender.handledRequests.filterBy('method', 'POST').pop();
|
||||
|
||||
assert.deepEqual(
|
||||
JSON.parse(request.requestBody),
|
||||
{
|
||||
NodeID: node.id,
|
||||
DrainSpec: {
|
||||
Deadline: -1,
|
||||
IgnoreSystemJobs: false,
|
||||
},
|
||||
},
|
||||
'Drain with force set'
|
||||
);
|
||||
|
||||
await ClientDetail.drainPopover.toggle();
|
||||
await ClientDetail.drainPopover.systemJobsToggle.toggle();
|
||||
await ClientDetail.drainPopover.submit();
|
||||
|
||||
request = server.pretender.handledRequests.filterBy('method', 'POST').pop();
|
||||
|
||||
assert.deepEqual(
|
||||
JSON.parse(request.requestBody),
|
||||
{
|
||||
NodeID: node.id,
|
||||
DrainSpec: {
|
||||
Deadline: -1,
|
||||
IgnoreSystemJobs: true,
|
||||
},
|
||||
},
|
||||
'Drain system jobs unset'
|
||||
);
|
||||
});
|
||||
|
||||
test('the drain popover cancel button closes the popover', async function(assert) {
|
||||
node = server.create('node', {
|
||||
drain: false,
|
||||
schedulingEligibility: 'eligible',
|
||||
});
|
||||
|
||||
await ClientDetail.visit({ id: node.id });
|
||||
assert.notOk(ClientDetail.drainPopover.isOpen);
|
||||
|
||||
await ClientDetail.drainPopover.toggle();
|
||||
assert.ok(ClientDetail.drainPopover.isOpen);
|
||||
|
||||
await ClientDetail.drainPopover.cancel();
|
||||
assert.notOk(ClientDetail.drainPopover.isOpen);
|
||||
assert.equal(server.pretender.handledRequests.filterBy('method', 'POST'), 0);
|
||||
});
|
||||
|
||||
test('toggling eligibility is disabled while a drain is active', async function(assert) {
|
||||
node = server.create('node', {
|
||||
drain: true,
|
||||
schedulingEligibility: 'ineligible',
|
||||
});
|
||||
|
||||
await ClientDetail.visit({ id: node.id });
|
||||
assert.ok(ClientDetail.eligibilityToggle.isDisabled);
|
||||
});
|
||||
|
||||
test('stopping a drain sends the correct POST request', async function(assert) {
|
||||
node = server.create('node', {
|
||||
drain: true,
|
||||
schedulingEligibility: 'ineligible',
|
||||
});
|
||||
|
||||
await ClientDetail.visit({ id: node.id });
|
||||
assert.ok(ClientDetail.stopDrainIsPresent);
|
||||
|
||||
await ClientDetail.stopDrain.idle();
|
||||
await ClientDetail.stopDrain.confirm();
|
||||
|
||||
const request = server.pretender.handledRequests.findBy('method', 'POST');
|
||||
assert.equal(request.url, `/v1/node/${node.id}/drain`);
|
||||
assert.deepEqual(JSON.parse(request.requestBody), {
|
||||
NodeID: node.id,
|
||||
DrainSpec: null,
|
||||
});
|
||||
});
|
||||
|
||||
test('when a drain is active, the "drain" popover is labeled as the "update" popover', async function(assert) {
|
||||
node = server.create('node', {
|
||||
drain: true,
|
||||
schedulingEligibility: 'ineligible',
|
||||
});
|
||||
|
||||
await ClientDetail.visit({ id: node.id });
|
||||
assert.equal(ClientDetail.drainPopover.label, 'Update Drain');
|
||||
});
|
||||
|
||||
test('forcing a drain sends the correct POST request', async function(assert) {
|
||||
node = server.create('node', {
|
||||
drain: true,
|
||||
schedulingEligibility: 'ineligible',
|
||||
drainStrategy: {
|
||||
Deadline: 0,
|
||||
IgnoreSystemJobs: true,
|
||||
},
|
||||
});
|
||||
|
||||
await ClientDetail.visit({ id: node.id });
|
||||
await ClientDetail.drainDetails.force.idle();
|
||||
await ClientDetail.drainDetails.force.confirm();
|
||||
|
||||
const request = server.pretender.handledRequests.findBy('method', 'POST');
|
||||
assert.equal(request.url, `/v1/node/${node.id}/drain`);
|
||||
assert.deepEqual(JSON.parse(request.requestBody), {
|
||||
NodeID: node.id,
|
||||
DrainSpec: {
|
||||
Deadline: -1,
|
||||
IgnoreSystemJobs: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('when stopping a drain fails, an error is shown', async function(assert) {
|
||||
node = server.create('node', {
|
||||
drain: true,
|
||||
schedulingEligibility: 'ineligible',
|
||||
});
|
||||
|
||||
server.pretender.post('/v1/node/:id/drain', () => [500, {}, '']);
|
||||
|
||||
await ClientDetail.visit({ id: node.id });
|
||||
await ClientDetail.stopDrain.idle();
|
||||
await ClientDetail.stopDrain.confirm();
|
||||
|
||||
assert.ok(ClientDetail.stopDrainError.isPresent);
|
||||
assert.ok(ClientDetail.stopDrainError.title.includes('Stop Drain Error'));
|
||||
|
||||
await ClientDetail.stopDrainError.dismiss();
|
||||
assert.notOk(ClientDetail.stopDrainError.isPresent);
|
||||
});
|
||||
|
||||
test('when starting a drain fails, an error message is shown', async function(assert) {
|
||||
node = server.create('node', {
|
||||
drain: false,
|
||||
schedulingEligibility: 'eligible',
|
||||
});
|
||||
|
||||
server.pretender.post('/v1/node/:id/drain', () => [500, {}, '']);
|
||||
|
||||
await ClientDetail.visit({ id: node.id });
|
||||
await ClientDetail.drainPopover.toggle();
|
||||
await ClientDetail.drainPopover.submit();
|
||||
|
||||
assert.ok(ClientDetail.drainError.isPresent);
|
||||
assert.ok(ClientDetail.drainError.title.includes('Drain Error'));
|
||||
|
||||
await ClientDetail.drainError.dismiss();
|
||||
assert.notOk(ClientDetail.drainError.isPresent);
|
||||
});
|
||||
|
||||
test('when updating a drain fails, an error message is shown', async function(assert) {
|
||||
node = server.create('node', {
|
||||
drain: true,
|
||||
schedulingEligibility: 'ineligible',
|
||||
});
|
||||
|
||||
server.pretender.post('/v1/node/:id/drain', () => [500, {}, '']);
|
||||
|
||||
await ClientDetail.visit({ id: node.id });
|
||||
await ClientDetail.drainPopover.toggle();
|
||||
await ClientDetail.drainPopover.submit();
|
||||
|
||||
assert.ok(ClientDetail.drainError.isPresent);
|
||||
assert.ok(ClientDetail.drainError.title.includes('Drain Error'));
|
||||
|
||||
await ClientDetail.drainError.dismiss();
|
||||
assert.notOk(ClientDetail.drainError.isPresent);
|
||||
});
|
||||
|
||||
test('when toggling eligibility fails, an error message is shown', async function(assert) {
|
||||
node = server.create('node', {
|
||||
drain: false,
|
||||
schedulingEligibility: 'eligible',
|
||||
});
|
||||
|
||||
server.pretender.post('/v1/node/:id/eligibility', () => [500, {}, '']);
|
||||
|
||||
await ClientDetail.visit({ id: node.id });
|
||||
await ClientDetail.eligibilityToggle.toggle();
|
||||
|
||||
assert.ok(ClientDetail.eligibilityError.isPresent);
|
||||
assert.ok(ClientDetail.eligibilityError.title.includes('Eligibility Error'));
|
||||
|
||||
await ClientDetail.eligibilityError.dismiss();
|
||||
assert.notOk(ClientDetail.eligibilityError.isPresent);
|
||||
});
|
||||
|
||||
test('when navigating away from a client that has an error message to another client, the error is not shown', async function(assert) {
|
||||
node = server.create('node', {
|
||||
drain: false,
|
||||
schedulingEligibility: 'eligible',
|
||||
});
|
||||
|
||||
const node2 = server.create('node');
|
||||
|
||||
server.pretender.post('/v1/node/:id/eligibility', () => [500, {}, '']);
|
||||
|
||||
await ClientDetail.visit({ id: node.id });
|
||||
await ClientDetail.eligibilityToggle.toggle();
|
||||
|
||||
assert.ok(ClientDetail.eligibilityError.isPresent);
|
||||
assert.ok(ClientDetail.eligibilityError.title.includes('Eligibility Error'));
|
||||
|
||||
await ClientDetail.visit({ id: node2.id });
|
||||
|
||||
assert.notOk(ClientDetail.eligibilityError.isPresent);
|
||||
});
|
||||
});
|
||||
|
||||
module('Acceptance | client detail (multi-namespace)', function(hooks) {
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
import allocations from 'nomad-ui/tests/pages/components/allocations';
|
||||
import twoStepButton from 'nomad-ui/tests/pages/components/two-step-button';
|
||||
import notification from 'nomad-ui/tests/pages/components/notification';
|
||||
import toggle from 'nomad-ui/tests/pages/components/toggle';
|
||||
|
||||
export default create({
|
||||
visit: visitable('/clients/:id'),
|
||||
@@ -115,19 +116,26 @@ export default create({
|
||||
},
|
||||
|
||||
drainPopover: {
|
||||
label: text('[data-test-drain-popover] [data-test-popover-trigger]'),
|
||||
isOpen: isPresent('[data-test-drain-popover-form]'),
|
||||
toggle: clickable('[data-test-drain-popover] [data-test-popover-trigger]'),
|
||||
toggleDeadline: clickable('[data-test-drain-deadline-toggle]'),
|
||||
|
||||
deadlineToggle: toggle('[data-test-drain-deadline-toggle]'),
|
||||
deadlineOptions: {
|
||||
open: clickable('[data-test-drain-deadline-toggle]'),
|
||||
options: collection('data-test-drain-deadline-options]', {
|
||||
open: clickable('[data-test-drain-deadline-option-select] .ember-power-select-trigger'),
|
||||
options: collection('.ember-power-select-option', {
|
||||
label: text(),
|
||||
choose: clickable(),
|
||||
}),
|
||||
},
|
||||
|
||||
setCustomDeadline: fillable('[data-test-drain-custom-deadline]'),
|
||||
toggleForceDrain: clickable(),
|
||||
toggleSystemJobs: clickable(),
|
||||
customDeadline: attribute('value', '[data-test-drain-custom-deadline]'),
|
||||
forceDrainToggle: toggle('[data-test-force-drain-toggle]'),
|
||||
systemJobsToggle: toggle('[data-test-system-jobs-toggle]'),
|
||||
|
||||
submit: clickable('[data-test-drain-submit]'),
|
||||
cancel: clickable('[data-test-drain-cancel]'),
|
||||
|
||||
setDeadline(label) {
|
||||
this.deadlineOptions.open();
|
||||
@@ -138,10 +146,10 @@ export default create({
|
||||
},
|
||||
},
|
||||
|
||||
stopDrain: twoStepButton('[data-test-stop-drain]'),
|
||||
stopDrainIsPresent: isPresent('[data-test-stop-drain]'),
|
||||
stopDrain: twoStepButton('[data-test-drain-stop]'),
|
||||
stopDrainIsPresent: isPresent('[data-test-drain-stop]'),
|
||||
|
||||
toggleEligibility: clickable('[data-test-eligibility-toggle]'),
|
||||
eligibilityToggle: toggle('[data-test-eligibility-toggle]'),
|
||||
|
||||
eligibilityError: notification('[data-test-eligibility-error]'),
|
||||
stopDrainError: notification('[data-test-stop-drain-error]'),
|
||||
|
||||
@@ -189,7 +189,7 @@ module('Unit | Adapter | Node', function(hooks) {
|
||||
{
|
||||
NodeID: node.id,
|
||||
DrainSpec: {
|
||||
Deadline: -1000000000,
|
||||
Deadline: -1,
|
||||
IgnoreSystemJobs: true,
|
||||
},
|
||||
},
|
||||
@@ -212,7 +212,7 @@ module('Unit | Adapter | Node', function(hooks) {
|
||||
{
|
||||
NodeID: node.id,
|
||||
DrainSpec: {
|
||||
Deadline: -1000000000,
|
||||
Deadline: -1,
|
||||
IgnoreSystemJobs: spec.IgnoreSystemJobs,
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user