From cf8dde0850a23d4e8b0b48bc5fea2f66388ab711 Mon Sep 17 00:00:00 2001 From: Phil Renaud Date: Wed, 20 Sep 2023 17:05:04 -0400 Subject: [PATCH] [ui] Color indicators for server/client status (#18318) * Color the status cell for servers and nodes * Testfix and changelog * Leader indicator moved post-word * Icon and badge treatment * Capitalizing test checks * HDS badges dont expose statusClass like we used to, so stop checking for it --- .changelog/18318.txt | 3 ++ .github/workflows/ember-test-audit.yml | 4 +-- ui/app/components/client-node-row.js | 30 ++++++++++++++--- ui/app/components/server-agent-row.js | 16 ++++++++++ .../templates/components/client-node-row.hbs | 7 +++- .../templates/components/server-agent-row.hbs | 18 +++++++++-- ui/tests/acceptance/clients-list-test.js | 32 +++++++------------ ui/tests/acceptance/servers-list-test.js | 6 +++- ui/tests/pages/clients/list.js | 5 +-- 9 files changed, 88 insertions(+), 33 deletions(-) create mode 100644 .changelog/18318.txt diff --git a/.changelog/18318.txt b/.changelog/18318.txt new file mode 100644 index 00000000000..073fd28cb52 --- /dev/null +++ b/.changelog/18318.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: color-code node and server status cells +``` diff --git a/.github/workflows/ember-test-audit.yml b/.github/workflows/ember-test-audit.yml index 67aa066a668..b71d5fcfc8c 100644 --- a/.github/workflows/ember-test-audit.yml +++ b/.github/workflows/ember-test-audit.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 with: ref: ${{ github.event.pull_request.base.sha }} - - uses: nanasess/setup-chromedriver@6fb8f5ffa6b7dc11e631ff695fbd2fec0b04bb52 # v2.1.1 + - uses: nanasess/setup-chromedriver@69cc01d772a1595b8aee87d52f53e71b3904d9d0 # v2.1.2 - name: Use Node.js uses: actions/setup-node@e33196f7422957bea03ed53f6fbb155025ffc7b8 # v3.7.0 with: @@ -35,7 +35,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - uses: nanasess/setup-chromedriver@6fb8f5ffa6b7dc11e631ff695fbd2fec0b04bb52 # v2.1.1 + - uses: nanasess/setup-chromedriver@69cc01d772a1595b8aee87d52f53e71b3904d9d0 # v2.1.2 - name: Use Node.js uses: actions/setup-node@e33196f7422957bea03ed53f6fbb155025ffc7b8 # v3.7.0 with: diff --git a/ui/app/components/client-node-row.js b/ui/app/components/client-node-row.js index 16d964b0022..1375136294f 100644 --- a/ui/app/components/client-node-row.js +++ b/ui/app/components/client-node-row.js @@ -58,15 +58,37 @@ export default class ClientNodeRow extends Component.extend( @watchRelationship('allocations') watch; @computed('node.compositeStatus') - get compositeStatusClass() { + get nodeStatusColor() { let compositeStatus = this.get('node.compositeStatus'); if (compositeStatus === 'draining') { - return 'status-text is-info'; + return 'neutral'; } else if (compositeStatus === 'ineligible') { - return 'status-text is-warning'; + return 'warning'; } else if (compositeStatus === 'down') { - return 'status-text is-danger'; + return 'critical'; + } else if (compositeStatus === 'ready') { + return 'success'; + } else if (compositeStatus === 'initializing') { + return 'neutral'; + } else { + return 'neutral'; + } + } + @computed('node.compositeStatus') + get nodeStatusIcon() { + let compositeStatus = this.get('node.compositeStatus'); + + if (compositeStatus === 'draining') { + return 'minus-circle'; + } else if (compositeStatus === 'ineligible') { + return 'skip'; + } else if (compositeStatus === 'down') { + return 'x-circle'; + } else if (compositeStatus === 'ready') { + return 'check-circle'; + } else if (compositeStatus === 'initializing') { + return 'entry-point'; } else { return ''; } diff --git a/ui/app/components/server-agent-row.js b/ui/app/components/server-agent-row.js index ee1a757e94a..a711e7e0de4 100644 --- a/ui/app/components/server-agent-row.js +++ b/ui/app/components/server-agent-row.js @@ -55,4 +55,20 @@ export default class ServerAgentRow extends Component { click() { this.goToAgent(); } + + @computed('agent.status') + get agentStatusColor() { + let agentStatus = this.get('agent.status'); + if (agentStatus === 'alive') { + return 'success'; + } else if (agentStatus === 'failed') { + return 'critical'; + } else if (agentStatus === 'leaving') { + return 'neutral'; + } else if (agentStatus === 'left') { + return 'neutral'; + } else { + return ''; + } + } } diff --git a/ui/app/templates/components/client-node-row.hbs b/ui/app/templates/components/client-node-row.hbs index 4f0ee33c299..66780e7bc33 100644 --- a/ui/app/templates/components/client-node-row.hbs +++ b/ui/app/templates/components/client-node-row.hbs @@ -14,7 +14,12 @@ {{this.node.name}} - {{this.node.compositeStatus}} + {{this.node.httpAddr}} diff --git a/ui/app/templates/components/server-agent-row.hbs b/ui/app/templates/components/server-agent-row.hbs index dde32fded6a..302ace102b2 100644 --- a/ui/app/templates/components/server-agent-row.hbs +++ b/ui/app/templates/components/server-agent-row.hbs @@ -9,8 +9,22 @@ action=(action this.goToAgent) }} >{{this.agent.name}} -{{this.agent.status}} -{{if this.agent.isLeader "True" "False"}} + + + + + + + {{this.agent.address}} {{this.agent.serfPort}} {{this.agent.datacenter}} diff --git a/ui/tests/acceptance/clients-list-test.js b/ui/tests/acceptance/clients-list-test.js index 9bc8747d4ea..82fd597c740 100644 --- a/ui/tests/acceptance/clients-list-test.js +++ b/ui/tests/acceptance/clients-list-test.js @@ -77,7 +77,7 @@ module('Acceptance | clients list', function (hooks) { assert.equal(nodeRow.nodePool, node.nodePool, 'Node Pool'); assert.equal( nodeRow.compositeStatus.text, - 'draining', + 'Draining', 'Combined status, draining, and eligbility' ); assert.equal(nodeRow.address, node.httpAddr); @@ -111,7 +111,7 @@ module('Acceptance | clients list', function (hooks) { assert.equal(nodeRow.id, node.id.split('-')[0], 'ID'); assert.equal( nodeRow.compositeStatus.text, - 'ready', + 'Ready', 'Combined status, draining, and eligbility' ); assert.equal(nodeRow.allocations, running.length, '# Allocations'); @@ -156,38 +156,28 @@ module('Acceptance | clients list', function (hooks) { }); await ClientsList.visit(); - ClientsList.nodes[0].compositeStatus.as((readyClient) => { - assert.equal(readyClient.text, 'ready'); - assert.ok(readyClient.isUnformatted, 'expected no status class'); + assert.equal(readyClient.text, 'Ready'); + console.log('readyClient', readyClient.text); assert.equal(readyClient.tooltip, 'ready / not draining / eligible'); }); - assert.equal(ClientsList.nodes[1].compositeStatus.text, 'initializing'); - assert.equal(ClientsList.nodes[2].compositeStatus.text, 'down'); + 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', 'down takes priority over ineligible' ); + assert.equal(ClientsList.nodes[4].compositeStatus.text, 'Ineligible'); - assert.equal(ClientsList.nodes[4].compositeStatus.text, 'ineligible'); - assert.ok( - ClientsList.nodes[4].compositeStatus.isWarning, - 'expected warning class' - ); - - assert.equal(ClientsList.nodes[5].compositeStatus.text, 'draining'); - assert.ok( - ClientsList.nodes[5].compositeStatus.isInfo, - 'expected info class' - ); + assert.equal(ClientsList.nodes[5].compositeStatus.text, 'Draining'); await ClientsList.sortBy('compositeStatus'); assert.deepEqual( ClientsList.nodes.map((n) => n.compositeStatus.text), - ['ready', 'initializing', 'ineligible', 'draining', 'down', 'down'] + ['Ready', 'Initializing', 'Ineligible', 'Draining', 'Down', 'Down'] ); // Simulate a client state change arriving through polling @@ -201,7 +191,7 @@ module('Acceptance | clients list', function (hooks) { assert.deepEqual( ClientsList.nodes.map((n) => n.compositeStatus.text), - ['initializing', 'ineligible', 'ineligible', 'draining', 'down', 'down'] + ['Initializing', 'Ineligible', 'Ineligible', 'Draining', 'Down', 'Down'] ); }); diff --git a/ui/tests/acceptance/servers-list-test.js b/ui/tests/acceptance/servers-list-test.js index ef1ae3f27c1..defdcd033f7 100644 --- a/ui/tests/acceptance/servers-list-test.js +++ b/ui/tests/acceptance/servers-list-test.js @@ -80,7 +80,11 @@ module('Acceptance | servers list', function (hooks) { const agentRow = ServersList.servers.objectAt(0); assert.equal(agentRow.name, agent.name, 'Name'); - assert.equal(agentRow.status, agent.member.Status, 'Status'); + assert.equal( + agentRow.status, + agent.member.Status[0].toUpperCase() + agent.member.Status.substring(1), + 'Status' + ); assert.equal(agentRow.leader, 'True', 'Leader?'); assert.equal(agentRow.address, agent.member.Address, 'Address'); assert.equal(agentRow.serfPort, agent.member.Port, 'Serf Port'); diff --git a/ui/tests/pages/clients/list.js b/ui/tests/pages/clients/list.js index ce4964a42a6..c5249ee6b10 100644 --- a/ui/tests/pages/clients/list.js +++ b/ui/tests/pages/clients/list.js @@ -44,8 +44,9 @@ export default create({ tooltip: attribute('aria-label', '.tooltip'), - isInfo: hasClass('is-info', '.status-text'), - isWarning: hasClass('is-warning', '.status-text'), + isInfo: hasClass('is-info'), + isSuccess: hasClass('is-success'), + isWarning: hasClass('is-warning'), isUnformatted: isHidden('.status-text'), },