From 65bf47fda811a38f788829586fed71346112578d Mon Sep 17 00:00:00 2001 From: Luiz Aoqui Date: Thu, 21 Oct 2021 15:40:38 -0400 Subject: [PATCH 1/6] ui: display client version in the table list --- ui/app/controllers/clients/index.js | 27 ++++++++++++++++--- ui/app/models/node.js | 1 + ui/app/templates/clients/index.hbs | 9 ++++++- .../templates/components/client-node-row.hbs | 3 ++- ui/mirage/factories/node.js | 2 ++ ui/tests/acceptance/clients-list-test.js | 23 +++++++++++++++- ui/tests/pages/clients/list.js | 2 ++ 7 files changed, 61 insertions(+), 6 deletions(-) diff --git a/ui/app/controllers/clients/index.js b/ui/app/controllers/clients/index.js index 14c9195d3f2..ab51dea94be 100644 --- a/ui/app/controllers/clients/index.js +++ b/ui/app/controllers/clients/index.js @@ -12,9 +12,9 @@ import classic from 'ember-classic-decorator'; @classic export default class IndexController extends Controller.extend( - SortableFactory(['id', 'name', 'compositeStatus', 'datacenter']), - Searchable - ) { + SortableFactory(['id', 'name', 'compositeStatus', 'datacenter', 'version']), + Searchable +) { @service userSettings; @controller('clients') clientsController; @@ -43,6 +43,9 @@ export default class IndexController extends Controller.extend( { qpDatacenter: 'dc', }, + { + qpVersion: 'version', + }, { qpVolume: 'volume', }, @@ -62,11 +65,13 @@ export default class IndexController extends Controller.extend( qpClass = ''; qpState = ''; qpDatacenter = ''; + qpVersion = ''; qpVolume = ''; @selection('qpClass') selectionClass; @selection('qpState') selectionState; @selection('qpDatacenter') selectionDatacenter; + @selection('qpVersion') selectionVersion; @selection('qpVolume') selectionVolume; @computed('nodes.[]', 'selectionClass') @@ -108,6 +113,19 @@ export default class IndexController extends Controller.extend( return datacenters.sort().map(dc => ({ key: dc, label: dc })); } + @computed('nodes.[]', 'selectionVersion') + get optionsVersion() { + const versions = Array.from(new Set(this.nodes.mapBy('version'))).compact(); + + // Remove any invalid datacenters from the query param/selection + scheduleOnce('actions', () => { + // eslint-disable-next-line ember/no-side-effects + this.set('qpVersion', serialize(intersection(versions, this.selectionVersion))); + }); + + return versions.sort().map(v => ({ key: v, label: v })); + } + @computed('nodes.[]', 'selectionVolume') get optionsVolume() { const flatten = (acc, val) => acc.concat(val.toArray()); @@ -128,6 +146,7 @@ export default class IndexController extends Controller.extend( 'selectionClass', 'selectionState', 'selectionDatacenter', + 'selectionVersion', 'selectionVolume' ) get filteredNodes() { @@ -135,6 +154,7 @@ export default class IndexController extends Controller.extend( selectionClass: classes, selectionState: states, selectionDatacenter: datacenters, + selectionVersion: versions, selectionVolume: volumes, } = this; @@ -148,6 +168,7 @@ export default class IndexController extends Controller.extend( if (classes.length && !classes.includes(node.get('nodeClass'))) return false; if (statuses.length && !statuses.includes(node.get('status'))) return false; if (datacenters.length && !datacenters.includes(node.get('datacenter'))) return false; + if (versions.length && !versions.includes(node.get('version'))) return false; if (volumes.length && !node.hostVolumes.find(volume => volumes.includes(volume.name))) return false; diff --git a/ui/app/models/node.js b/ui/app/models/node.js index 8d63616c31c..abb0958a2c8 100644 --- a/ui/app/models/node.js +++ b/ui/app/models/node.js @@ -21,6 +21,7 @@ export default class Node extends Model { @attr('string') statusDescription; @shortUUIDProperty('id') shortId; @attr('number') modifyIndex; + @attr('string') version; // Available from single response @attr('string') httpAddr; diff --git a/ui/app/templates/clients/index.hbs b/ui/app/templates/clients/index.hbs index d3d5c9ff076..91d889569e7 100644 --- a/ui/app/templates/clients/index.hbs +++ b/ui/app/templates/clients/index.hbs @@ -32,6 +32,12 @@ @options={{this.optionsDatacenter}} @selection={{this.selectionDatacenter}} @onSelect={{action this.setFacetQueryParam "qpDatacenter"}} /> + ID Name State - Address + Address Datacenter + Version # Volumes # Allocs diff --git a/ui/app/templates/components/client-node-row.hbs b/ui/app/templates/components/client-node-row.hbs index 3e6435af3c2..1f341b0ac06 100644 --- a/ui/app/templates/components/client-node-row.hbs +++ b/ui/app/templates/components/client-node-row.hbs @@ -12,8 +12,9 @@ {{this.node.compositeStatus}} -{{this.node.httpAddr}} +{{this.node.httpAddr}} {{this.node.datacenter}} +{{this.node.version}} {{if this.node.hostVolumes.length this.node.hostVolumes.length}} {{#if this.node.allocations.isPending}} diff --git a/ui/mirage/factories/node.js b/ui/mirage/factories/node.js index f670db32cee..14a269ea2d2 100644 --- a/ui/mirage/factories/node.js +++ b/ui/mirage/factories/node.js @@ -7,6 +7,7 @@ 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 REF_DATE = new Date(); export default Factory.extend({ @@ -22,6 +23,7 @@ export default Factory.extend({ createIndex: i => i, modifyIndex: () => faker.random.number({ min: 10, max: 2000 }), + version: () => faker.helpers.randomize(NODE_VERSIONS), httpAddr() { return this.name.split('@')[1]; diff --git a/ui/tests/acceptance/clients-list-test.js b/ui/tests/acceptance/clients-list-test.js index 3e6ea8e8240..dd76d5a8cc0 100644 --- a/ui/tests/acceptance/clients-list-test.js +++ b/ui/tests/acceptance/clients-list-test.js @@ -66,6 +66,7 @@ module('Acceptance | clients list', function(hooks) { ); assert.equal(nodeRow.address, node.httpAddr); assert.equal(nodeRow.datacenter, node.datacenter, 'Datacenter'); + assert.equal(nodeRow.version, node.version, 'Version'); assert.equal(nodeRow.allocations, allocations.length, '# Allocations'); }); @@ -146,7 +147,11 @@ module('Acceptance | clients list', function(hooks) { assert.equal(ClientsList.nodes[1].compositeStatus.text, 'initializing'); assert.equal(ClientsList.nodes[2].compositeStatus.text, 'down'); - assert.equal(ClientsList.nodes[2].compositeStatus.text, 'down', 'down takes priority over ineligible'); + assert.equal( + ClientsList.nodes[2].compositeStatus.text, + 'down', + 'down takes priority over ineligible' + ); assert.equal(ClientsList.nodes[4].compositeStatus.text, 'ineligible'); assert.ok(ClientsList.nodes[4].compositeStatus.isWarning, 'expected warning class'); @@ -299,6 +304,22 @@ module('Acceptance | clients list', function(hooks) { filter: (node, selection) => selection.includes(node.datacenter), }); + testFacet('Versions', { + facet: ClientsList.facets.version, + paramName: 'version', + expectedOptions(nodes) { + return Array.from(new Set(nodes.mapBy('version'))).sort(); + }, + async beforeEach() { + server.create('agent'); + server.createList('node', 2, { version: '0.12.0' }); + server.createList('node', 2, { version: '1.1.0-beta1' }); + server.createList('node', 2, { version: '1.2.0+ent' }); + await ClientsList.visit(); + }, + filter: (node, selection) => selection.includes(node.version), + }); + testFacet('Volumes', { facet: ClientsList.facets.volume, paramName: 'volume', diff --git a/ui/tests/pages/clients/list.js b/ui/tests/pages/clients/list.js index 652a777dc4e..f00c36a2890 100644 --- a/ui/tests/pages/clients/list.js +++ b/ui/tests/pages/clients/list.js @@ -49,6 +49,7 @@ export default create({ address: text('[data-test-client-address]'), datacenter: text('[data-test-client-datacenter]'), + version: text('[data-test-client-version]'), allocations: text('[data-test-client-allocations]'), clickRow: clickable(), @@ -75,6 +76,7 @@ export default create({ class: multiFacet('[data-test-class-facet]'), state: multiFacet('[data-test-state-facet]'), datacenter: multiFacet('[data-test-datacenter-facet]'), + version: multiFacet('[data-test-version-facet]'), volume: multiFacet('[data-test-volume-facet]'), }, }); From 178af693582567a9a9fa1e565055bf571c78a3e8 Mon Sep 17 00:00:00 2001 From: Luiz Aoqui Date: Thu, 21 Oct 2021 16:13:57 -0400 Subject: [PATCH 2/6] ui: display server version in index table --- ui/app/models/agent.js | 5 +++++ ui/app/templates/components/server-agent-row.hbs | 3 ++- ui/app/templates/servers/index.hbs | 3 ++- ui/mirage/factories/agent.js | 6 ++++++ ui/tests/acceptance/servers-list-test.js | 1 + ui/tests/pages/servers/list.js | 1 + 6 files changed, 17 insertions(+), 2 deletions(-) diff --git a/ui/app/models/agent.js b/ui/app/models/agent.js index be70e5467b3..4f902c670cb 100644 --- a/ui/app/models/agent.js +++ b/ui/app/models/agent.js @@ -28,4 +28,9 @@ export default class Agent extends Model { get isLeader() { return this.get('system.leader.rpcAddr') === this.rpcAddr; } + + @computed('tags.[]') + get version() { + return this.tags?.build || ''; + } } diff --git a/ui/app/templates/components/server-agent-row.hbs b/ui/app/templates/components/server-agent-row.hbs index ea1353f07bd..5c16fc3153c 100644 --- a/ui/app/templates/components/server-agent-row.hbs +++ b/ui/app/templates/components/server-agent-row.hbs @@ -1,6 +1,7 @@ {{this.agent.name}} {{this.agent.status}} {{if this.agent.isLeader "True" "False"}} -{{this.agent.address}} +{{this.agent.address}} {{this.agent.serfPort}} {{this.agent.datacenter}} +{{this.agent.version}} diff --git a/ui/app/templates/servers/index.hbs b/ui/app/templates/servers/index.hbs index 8173b9100da..1daed7b4561 100644 --- a/ui/app/templates/servers/index.hbs +++ b/ui/app/templates/servers/index.hbs @@ -16,9 +16,10 @@ Name Status Leader - Address + Address port Datacenter + Version diff --git a/ui/mirage/factories/agent.js b/ui/mirage/factories/agent.js index cf985d95ba4..47f96df6c50 100644 --- a/ui/mirage/factories/agent.js +++ b/ui/mirage/factories/agent.js @@ -5,6 +5,7 @@ import { DATACENTERS } from '../common'; const UUIDS = provide(100, faker.random.uuid.bind(faker.random)); const AGENT_STATUSES = ['alive', 'leaving', 'left', 'failed']; +const AGENT_BUILDS = ['1.1.0-beta', '1.0.2-alpha+ent', ...provide(5, faker.system.semver)]; export default Factory.extend({ id: i => (i / 100 >= 1 ? `${UUIDS[i]}-${i}` : UUIDS[i]), @@ -29,6 +30,10 @@ export default Factory.extend({ Tags: generateTags(serfPort), }; }, + + version() { + return this.member.Tags?.build || ''; + }, }); function generateName() { @@ -44,5 +49,6 @@ function generateTags(serfPort) { return { port: rpcPortCandidate === serfPort ? rpcPortCandidate + 1 : rpcPortCandidate, dc: faker.helpers.randomize(DATACENTERS), + build: faker.helpers.randomize(AGENT_BUILDS), }; } diff --git a/ui/tests/acceptance/servers-list-test.js b/ui/tests/acceptance/servers-list-test.js index d4b30c580ab..05ac3f327e7 100644 --- a/ui/tests/acceptance/servers-list-test.js +++ b/ui/tests/acceptance/servers-list-test.js @@ -63,6 +63,7 @@ module('Acceptance | servers list', function(hooks) { assert.equal(agentRow.address, agent.member.Address, 'Address'); assert.equal(agentRow.serfPort, agent.member.Port, 'Serf Port'); assert.equal(agentRow.datacenter, agent.member.Tags.dc, 'Datacenter'); + assert.equal(agentRow.version, agent.version, 'Version'); }); test('each server should link to the server detail page', async function(assert) { diff --git a/ui/tests/pages/servers/list.js b/ui/tests/pages/servers/list.js index 9f8081e6bca..cc884d4e1d6 100644 --- a/ui/tests/pages/servers/list.js +++ b/ui/tests/pages/servers/list.js @@ -11,6 +11,7 @@ export default create({ address: text('[data-test-server-address]'), serfPort: text('[data-test-server-port]'), datacenter: text('[data-test-server-datacenter]'), + version: text('[data-test-server-version]'), clickRow: clickable(), clickName: clickable('[data-test-server-name] a'), From ef171629ed9267961122be7bc9222909d85e6265 Mon Sep 17 00:00:00 2001 From: Luiz Aoqui Date: Thu, 21 Oct 2021 16:16:16 -0400 Subject: [PATCH 3/6] ui: revert conflicting prettier rule --- ui/app/controllers/clients/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/app/controllers/clients/index.js b/ui/app/controllers/clients/index.js index ab51dea94be..1bc6c105a89 100644 --- a/ui/app/controllers/clients/index.js +++ b/ui/app/controllers/clients/index.js @@ -12,9 +12,9 @@ import classic from 'ember-classic-decorator'; @classic export default class IndexController extends Controller.extend( - SortableFactory(['id', 'name', 'compositeStatus', 'datacenter', 'version']), - Searchable -) { + SortableFactory(['id', 'name', 'compositeStatus', 'datacenter', 'version']), + Searchable + ) { @service userSettings; @controller('clients') clientsController; From e783aaf265f0c7b7f091051996c6a7dd662bb4b1 Mon Sep 17 00:00:00 2001 From: Luiz Aoqui Date: Thu, 21 Oct 2021 16:22:38 -0400 Subject: [PATCH 4/6] changelog: add entry for #11366 --- .changelog/11366.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/11366.txt diff --git a/.changelog/11366.txt b/.changelog/11366.txt new file mode 100644 index 00000000000..d304935aa19 --- /dev/null +++ b/.changelog/11366.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: Display the Nomad version in the Servers and Clients tables and allow filtering and sorting +``` From 48410e298f567ac706d2e0ee8f88f988b93c39ee Mon Sep 17 00:00:00 2001 From: Luiz Aoqui Date: Thu, 21 Oct 2021 18:57:09 -0400 Subject: [PATCH 5/6] ui: fix lint error --- ui/app/models/agent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/models/agent.js b/ui/app/models/agent.js index 4f902c670cb..5b66ed356a4 100644 --- a/ui/app/models/agent.js +++ b/ui/app/models/agent.js @@ -29,7 +29,7 @@ export default class Agent extends Model { return this.get('system.leader.rpcAddr') === this.rpcAddr; } - @computed('tags.[]') + @computed('tags.build') get version() { return this.tags?.build || ''; } From a94f9e3abe16c08df5a0ab76f0643c3e0628f195 Mon Sep 17 00:00:00 2001 From: Luiz Aoqui Date: Fri, 22 Oct 2021 09:48:18 -0400 Subject: [PATCH 6/6] ui: fix typo --- ui/app/controllers/clients/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/controllers/clients/index.js b/ui/app/controllers/clients/index.js index 1bc6c105a89..f801bc8fe25 100644 --- a/ui/app/controllers/clients/index.js +++ b/ui/app/controllers/clients/index.js @@ -117,7 +117,7 @@ export default class IndexController extends Controller.extend( get optionsVersion() { const versions = Array.from(new Set(this.nodes.mapBy('version'))).compact(); - // Remove any invalid datacenters from the query param/selection + // Remove any invalid versions from the query param/selection scheduleOnce('actions', () => { // eslint-disable-next-line ember/no-side-effects this.set('qpVersion', serialize(intersection(versions, this.selectionVersion)));