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]'), }, });