diff --git a/.changelog/12713.txt b/.changelog/12713.txt new file mode 100644 index 000000000..3094100bf --- /dev/null +++ b/.changelog/12713.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fixed a bug where volumes were being incorrectly linked when per_alloc=true +``` \ No newline at end of file diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 000000000..f408cac8c --- /dev/null +++ b/jsconfig.json @@ -0,0 +1 @@ +{"compilerOptions":{"target":"es6","experimentalDecorators":true},"exclude":["node_modules","bower_components","tmp","vendor",".git","dist"]} \ No newline at end of file diff --git a/ui/.template-lintrc.js b/ui/.template-lintrc.js index fdfa6bd90..28057918d 100644 --- a/ui/.template-lintrc.js +++ b/ui/.template-lintrc.js @@ -7,5 +7,6 @@ module.exports = { 'no-action': 'off', 'no-invalid-interactive': 'off', 'no-inline-styles': 'off', + 'no-curly-component-invocation': { allow: ['format-volume-name'] }, }, }; diff --git a/ui/app/components/task-row.js b/ui/app/components/task-row.js index 35343205d..64bccf1a5 100644 --- a/ui/app/components/task-row.js +++ b/ui/app/components/task-row.js @@ -5,6 +5,7 @@ import { computed } from '@ember/object'; import { alias } from '@ember/object/computed'; import { task, timeout } from 'ember-concurrency'; import { lazyClick } from '../helpers/lazy-click'; + import { classNames, tagName, diff --git a/ui/app/helpers/format-volume-name.js b/ui/app/helpers/format-volume-name.js new file mode 100644 index 000000000..5ec96e281 --- /dev/null +++ b/ui/app/helpers/format-volume-name.js @@ -0,0 +1,18 @@ +import { helper } from '@ember/component/helper'; + +/** + * Volume Name Formatter + * + * Usage: {{format-volume-name source=string isPerAlloc=boolean volumeExtension=string}} + * + * Outputs a title/link for volumes that are per_alloc-aware. + * (when a volume is per_alloc, its route location requires an additional extension) + */ +export function formatVolumeName( + _, + { source = '', isPerAlloc, volumeExtension } +) { + return `${source}${isPerAlloc ? volumeExtension : ''}`; +} + +export default helper(formatVolumeName); diff --git a/ui/app/models/allocation.js b/ui/app/models/allocation.js index fb3aa8445..9f9e1a054 100644 --- a/ui/app/models/allocation.js +++ b/ui/app/models/allocation.js @@ -119,6 +119,13 @@ export default class Allocation extends Model { return []; } + // When per_alloc is set to true on a volume, the volumes are duplicated between active allocations. + // We differentiate them with a [#] suffix, inferred from a volume's allocation's name property. + @computed('name') + get volumeExtension() { + return this.name.substring(this.name.lastIndexOf('[')); + } + @fragmentArray('task-state') states; @fragmentArray('reschedule-event') rescheduleEvents; diff --git a/ui/app/models/volume-definition.js b/ui/app/models/volume-definition.js index 77e6c0819..20624d796 100644 --- a/ui/app/models/volume-definition.js +++ b/ui/app/models/volume-definition.js @@ -11,6 +11,7 @@ export default class VolumeDefinition extends Fragment { @attr('string') source; @attr('string') type; @attr('boolean') readOnly; + @attr('boolean') perAlloc; @equal('type', 'csi') isCSI; @alias('taskGroup.job.namespace') namespace; diff --git a/ui/app/templates/allocations/allocation/task/index.hbs b/ui/app/templates/allocations/allocation/task/index.hbs index e6d62dc20..3191a1601 100644 --- a/ui/app/templates/allocations/allocation/task/index.hbs +++ b/ui/app/templates/allocations/allocation/task/index.hbs @@ -163,10 +163,19 @@ {{#if row.model.isCSI}} - {{row.model.volume}} + {{format-volume-name + source=row.model.source + isPerAlloc=row.model.volumeDeclaration.perAlloc + volumeExtension=this.model.allocation.volumeExtension}} {{else}} {{row.model.source}} diff --git a/ui/app/templates/components/task-row.hbs b/ui/app/templates/components/task-row.hbs index 521f0b088..f6e79314e 100644 --- a/ui/app/templates/components/task-row.hbs +++ b/ui/app/templates/components/task-row.hbs @@ -47,9 +47,19 @@ {{#if volume.isCSI}} - {{volume.source}} + {{format-volume-name + source=volume.source + isPerAlloc=volume.volumeDeclaration.perAlloc + volumeExtension=this.task.allocation.volumeExtension}} {{else}} {{volume.source}} diff --git a/ui/app/templates/jobs/job/task-group.hbs b/ui/app/templates/jobs/job/task-group.hbs index 2402b5df6..83160e68a 100644 --- a/ui/app/templates/jobs/job/task-group.hbs +++ b/ui/app/templates/jobs/job/task-group.hbs @@ -293,12 +293,17 @@ {{#if row.model.isCSI}} - - {{row.model.name}} - + {{!-- if volume is per_alloc=true, there's no one specific volume. So, link to the volumes index with an active query --}} + {{#if row.model.perAlloc}} + {{row.model.name}} + {{else}} + + {{row.model.name}} + + {{/if}} {{else}} {{row.model.name}} {{/if}} diff --git a/ui/tests/unit/helpers/format-volume-name-test.js b/ui/tests/unit/helpers/format-volume-name-test.js new file mode 100644 index 000000000..4fafb7c83 --- /dev/null +++ b/ui/tests/unit/helpers/format-volume-name-test.js @@ -0,0 +1,39 @@ +import { module, test } from 'qunit'; +import { formatVolumeName } from 'nomad-ui/helpers/format-volume-name'; + +module('Unit | Helper | formatVolumeName', function () { + test('Returns source as string when isPerAlloc is false', function (assert) { + const expectation = 'my-volume-source'; + assert.equal( + formatVolumeName(null, { + source: 'my-volume-source', + isPerAlloc: false, + volumeExtension: '[arbitrary]', + }), + expectation, + 'false perAlloc' + ); + assert.equal( + formatVolumeName(null, { + source: 'my-volume-source', + isPerAlloc: null, + volumeExtension: '[arbitrary]', + }), + expectation, + 'null perAlloc' + ); + }); + + test('Returns concatonated name when isPerAlloc is true', function (assert) { + const expectation = 'my-volume-source[1]'; + assert.equal( + formatVolumeName(null, { + source: 'my-volume-source', + isPerAlloc: true, + volumeExtension: '[1]', + }), + expectation, + expectation + ); + }); +});