4e79ddea45
The API is missing values for `ReadAllocs` and `WriteAllocs` fields, resulting in allocation claims not being populated in the web UI. These fields mirror the fields in `nomad/structs.CSIVolume`. Returning a separate list of stubs for read and write would be ideal, but this can't be done without either bloating the API response with repeated full `Allocation` data, or causing a panic in previous versions of the CLI. The `nomad/structs` fields are persisted with nil values and are populated during RPC, so we'll do the same in the HTTP API and populate the `ReadAllocs` and `WriteAllocs` fields with a map of allocation IDs, but with null values. The web UI will then create its `ReadAllocations` and `WriteAllocations` fields by mapping from those IDs to the values in `Allocations`, instead of flattening the map into a list.
364 lines
10 KiB
JavaScript
364 lines
10 KiB
JavaScript
import { module, test } from 'qunit';
|
|
import { setupTest } from 'ember-qunit';
|
|
import VolumeModel from 'nomad-ui/models/volume';
|
|
|
|
module('Unit | Serializer | Volume', function(hooks) {
|
|
setupTest(hooks);
|
|
hooks.beforeEach(function() {
|
|
this.store = this.owner.lookup('service:store');
|
|
this.subject = () => this.store.serializerFor('volume');
|
|
});
|
|
|
|
// Set the milliseconds to avoid possible floating point precision
|
|
// issue that arises from converting to nanos and back.
|
|
const REF_DATE = new Date();
|
|
REF_DATE.setMilliseconds(0);
|
|
|
|
const normalizationTestCases = [
|
|
{
|
|
name:
|
|
'`default` is used as the namespace in the volume ID when there is no namespace in the payload',
|
|
in: {
|
|
ID: 'volume-id',
|
|
Name: 'volume-id',
|
|
PluginID: 'plugin-1',
|
|
ExternalID: 'external-uuid',
|
|
Topologies: {},
|
|
AccessMode: 'access-this-way',
|
|
AttachmentMode: 'attach-this-way',
|
|
Schedulable: true,
|
|
Provider: 'abc.123',
|
|
Version: '1.0.29',
|
|
ControllerRequired: true,
|
|
ControllersHealthy: 1,
|
|
ControllersExpected: 1,
|
|
NodesHealthy: 1,
|
|
NodesExpected: 2,
|
|
CreateIndex: 1,
|
|
ModifyIndex: 38,
|
|
WriteAllocs: {},
|
|
ReadAllocs: {},
|
|
},
|
|
out: {
|
|
data: {
|
|
id: '["csi/volume-id","default"]',
|
|
type: 'volume',
|
|
attributes: {
|
|
plainId: 'volume-id',
|
|
name: 'volume-id',
|
|
externalId: 'external-uuid',
|
|
topologies: {},
|
|
accessMode: 'access-this-way',
|
|
attachmentMode: 'attach-this-way',
|
|
schedulable: true,
|
|
provider: 'abc.123',
|
|
version: '1.0.29',
|
|
controllerRequired: true,
|
|
controllersHealthy: 1,
|
|
controllersExpected: 1,
|
|
nodesHealthy: 1,
|
|
nodesExpected: 2,
|
|
createIndex: 1,
|
|
modifyIndex: 38,
|
|
},
|
|
relationships: {
|
|
plugin: {
|
|
data: {
|
|
id: 'csi/plugin-1',
|
|
type: 'plugin',
|
|
},
|
|
},
|
|
readAllocations: {
|
|
data: [],
|
|
},
|
|
writeAllocations: {
|
|
data: [],
|
|
},
|
|
},
|
|
},
|
|
included: [],
|
|
},
|
|
},
|
|
|
|
{
|
|
name: 'The ID of the record is a composite of both the name and the namespace',
|
|
in: {
|
|
ID: 'volume-id',
|
|
Name: 'volume-id',
|
|
Namespace: 'namespace-2',
|
|
PluginID: 'plugin-1',
|
|
ExternalID: 'external-uuid',
|
|
Topologies: {},
|
|
AccessMode: 'access-this-way',
|
|
AttachmentMode: 'attach-this-way',
|
|
Schedulable: true,
|
|
Provider: 'abc.123',
|
|
Version: '1.0.29',
|
|
ControllerRequired: true,
|
|
ControllersHealthy: 1,
|
|
ControllersExpected: 1,
|
|
NodesHealthy: 1,
|
|
NodesExpected: 2,
|
|
CreateIndex: 1,
|
|
ModifyIndex: 38,
|
|
WriteAllocs: {},
|
|
ReadAllocs: {},
|
|
},
|
|
out: {
|
|
data: {
|
|
id: '["csi/volume-id","namespace-2"]',
|
|
type: 'volume',
|
|
attributes: {
|
|
plainId: 'volume-id',
|
|
name: 'volume-id',
|
|
externalId: 'external-uuid',
|
|
topologies: {},
|
|
accessMode: 'access-this-way',
|
|
attachmentMode: 'attach-this-way',
|
|
schedulable: true,
|
|
provider: 'abc.123',
|
|
version: '1.0.29',
|
|
controllerRequired: true,
|
|
controllersHealthy: 1,
|
|
controllersExpected: 1,
|
|
nodesHealthy: 1,
|
|
nodesExpected: 2,
|
|
createIndex: 1,
|
|
modifyIndex: 38,
|
|
},
|
|
relationships: {
|
|
plugin: {
|
|
data: {
|
|
id: 'csi/plugin-1',
|
|
type: 'plugin',
|
|
},
|
|
},
|
|
namespace: {
|
|
data: {
|
|
id: 'namespace-2',
|
|
type: 'namespace',
|
|
},
|
|
},
|
|
readAllocations: {
|
|
data: [],
|
|
},
|
|
writeAllocations: {
|
|
data: [],
|
|
},
|
|
},
|
|
},
|
|
included: [],
|
|
},
|
|
},
|
|
|
|
{
|
|
name:
|
|
'Allocations are interpreted as embedded records and are properly normalized into included resources in a JSON API shape',
|
|
in: {
|
|
ID: 'volume-id',
|
|
Name: 'volume-id',
|
|
Namespace: 'namespace-2',
|
|
PluginID: 'plugin-1',
|
|
ExternalID: 'external-uuid',
|
|
Topologies: {},
|
|
AccessMode: 'access-this-way',
|
|
AttachmentMode: 'attach-this-way',
|
|
Schedulable: true,
|
|
Provider: 'abc.123',
|
|
Version: '1.0.29',
|
|
ControllerRequired: true,
|
|
ControllersHealthy: 1,
|
|
ControllersExpected: 1,
|
|
NodesHealthy: 1,
|
|
NodesExpected: 2,
|
|
CreateIndex: 1,
|
|
ModifyIndex: 38,
|
|
Allocations: [
|
|
{
|
|
ID: 'alloc-id-1',
|
|
TaskGroup: 'foobar',
|
|
CreateTime: +REF_DATE * 1000000,
|
|
ModifyTime: +REF_DATE * 1000000,
|
|
JobID: 'the-job',
|
|
Namespace: 'namespace-2',
|
|
},
|
|
{
|
|
ID: 'alloc-id-2',
|
|
TaskGroup: 'write-here',
|
|
CreateTime: +REF_DATE * 1000000,
|
|
ModifyTime: +REF_DATE * 1000000,
|
|
JobID: 'the-job',
|
|
Namespace: 'namespace-2',
|
|
},
|
|
{
|
|
ID: 'alloc-id-3',
|
|
TaskGroup: 'look-if-you-must',
|
|
CreateTime: +REF_DATE * 1000000,
|
|
ModifyTime: +REF_DATE * 1000000,
|
|
JobID: 'the-job',
|
|
Namespace: 'namespace-2',
|
|
},
|
|
],
|
|
WriteAllocs: {
|
|
'alloc-id-1': null,
|
|
'alloc-id-2': null,
|
|
},
|
|
ReadAllocs: {
|
|
'alloc-id-3': null,
|
|
},
|
|
},
|
|
out: {
|
|
data: {
|
|
id: '["csi/volume-id","namespace-2"]',
|
|
type: 'volume',
|
|
attributes: {
|
|
plainId: 'volume-id',
|
|
name: 'volume-id',
|
|
externalId: 'external-uuid',
|
|
topologies: {},
|
|
accessMode: 'access-this-way',
|
|
attachmentMode: 'attach-this-way',
|
|
schedulable: true,
|
|
provider: 'abc.123',
|
|
version: '1.0.29',
|
|
controllerRequired: true,
|
|
controllersHealthy: 1,
|
|
controllersExpected: 1,
|
|
nodesHealthy: 1,
|
|
nodesExpected: 2,
|
|
createIndex: 1,
|
|
modifyIndex: 38,
|
|
},
|
|
relationships: {
|
|
plugin: {
|
|
data: {
|
|
id: 'csi/plugin-1',
|
|
type: 'plugin',
|
|
},
|
|
},
|
|
namespace: {
|
|
data: {
|
|
id: 'namespace-2',
|
|
type: 'namespace',
|
|
},
|
|
},
|
|
readAllocations: {
|
|
data: [{ type: 'allocation', id: 'alloc-id-3' }],
|
|
},
|
|
writeAllocations: {
|
|
data: [
|
|
{ type: 'allocation', id: 'alloc-id-1' },
|
|
{ type: 'allocation', id: 'alloc-id-2' },
|
|
],
|
|
},
|
|
},
|
|
},
|
|
included: [
|
|
{
|
|
id: 'alloc-id-1',
|
|
type: 'allocation',
|
|
attributes: {
|
|
createTime: REF_DATE,
|
|
modifyTime: REF_DATE,
|
|
taskGroupName: 'foobar',
|
|
wasPreempted: false,
|
|
states: [],
|
|
allocationTaskGroup: null,
|
|
},
|
|
relationships: {
|
|
followUpEvaluation: {
|
|
data: null,
|
|
},
|
|
job: {
|
|
data: { type: 'job', id: '["the-job","namespace-2"]' },
|
|
},
|
|
nextAllocation: {
|
|
data: null,
|
|
},
|
|
previousAllocation: {
|
|
data: null,
|
|
},
|
|
preemptedAllocations: {
|
|
data: [],
|
|
},
|
|
preemptedByAllocation: {
|
|
data: null,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
id: 'alloc-id-2',
|
|
type: 'allocation',
|
|
attributes: {
|
|
createTime: REF_DATE,
|
|
modifyTime: REF_DATE,
|
|
taskGroupName: 'write-here',
|
|
wasPreempted: false,
|
|
states: [],
|
|
allocationTaskGroup: null,
|
|
},
|
|
relationships: {
|
|
followUpEvaluation: {
|
|
data: null,
|
|
},
|
|
job: {
|
|
data: { type: 'job', id: '["the-job","namespace-2"]' },
|
|
},
|
|
nextAllocation: {
|
|
data: null,
|
|
},
|
|
previousAllocation: {
|
|
data: null,
|
|
},
|
|
preemptedAllocations: {
|
|
data: [],
|
|
},
|
|
preemptedByAllocation: {
|
|
data: null,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
id: 'alloc-id-3',
|
|
type: 'allocation',
|
|
attributes: {
|
|
createTime: REF_DATE,
|
|
modifyTime: REF_DATE,
|
|
taskGroupName: 'look-if-you-must',
|
|
wasPreempted: false,
|
|
states: [],
|
|
allocationTaskGroup: null,
|
|
},
|
|
relationships: {
|
|
followUpEvaluation: {
|
|
data: null,
|
|
},
|
|
job: {
|
|
data: { type: 'job', id: '["the-job","namespace-2"]' },
|
|
},
|
|
nextAllocation: {
|
|
data: null,
|
|
},
|
|
previousAllocation: {
|
|
data: null,
|
|
},
|
|
preemptedAllocations: {
|
|
data: [],
|
|
},
|
|
preemptedByAllocation: {
|
|
data: null,
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
];
|
|
|
|
normalizationTestCases.forEach(testCase => {
|
|
test(`normalization: ${testCase.name}`, async function(assert) {
|
|
assert.deepEqual(this.subject().normalize(VolumeModel, testCase.in), testCase.out);
|
|
});
|
|
});
|
|
});
|