From 2aefa57254f565c0c369fffc3a96d8845970eb8c Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Tue, 28 Nov 2023 16:28:13 -0600 Subject: [PATCH 01/11] replication gets its own subnav --- ui/app/app.js | 12 +++++- ui/app/components/sidebar/nav/cluster.hbs | 42 +++++++------------ ui/lib/replication/addon/engine.js | 13 +++++- .../addon/templates/application.hbs | 25 +++++++++++ 4 files changed, 61 insertions(+), 31 deletions(-) diff --git a/ui/app/app.js b/ui/app/app.js index 5e55eb51628c..2fe614d04c53 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -20,9 +20,19 @@ export default class App extends Application { }, replication: { dependencies: { - services: ['auth', 'flash-messages', 'namespace', 'replication-mode', 'router', 'store', 'version'], + services: [ + 'auth', + 'flash-messages', + 'namespace', + 'replication-mode', + 'router', + 'store', + 'version', + '-portal', + ], externalRoutes: { replication: 'vault.cluster.replication.index', + vault: 'vault.cluster', }, }, }, diff --git a/ui/app/components/sidebar/nav/cluster.hbs b/ui/app/components/sidebar/nav/cluster.hbs index c16c55780972..7e1f88cdef48 100644 --- a/ui/app/components/sidebar/nav/cluster.hbs +++ b/ui/app/components/sidebar/nav/cluster.hbs @@ -42,32 +42,6 @@ /> {{/if}} - {{#if - (and - this.version.isEnterprise - this.namespace.inRootNamespace - this.cluster.anyReplicationEnabled - (has-permission "status" routeParams="replication") - ) - }} - Replication - - - {{#if (has-feature "Performance Replication")}} - - {{/if}} - {{/if}} - {{#if (or (and @@ -78,8 +52,20 @@ }} Monitoring {{/if}} - {{#if (and this.version.isEnterprise this.namespace.inRootNamespace (has-permission "status" routeParams="replication"))}} - + {{#if + (and + this.version.isEnterprise + this.namespace.inRootNamespace + this.cluster.anyReplicationEnabled + (has-permission "status" routeParams="replication") + ) + }} + {{/if}} {{#if (and this.cluster.usingRaft this.namespace.inRootNamespace (has-permission "status" routeParams="raft"))}} + + + Replication + + {{#if (has-feature "DR Replication")}} + + {{/if}} + {{#if (has-feature "Performance Replication")}} + + {{/if}} + {{outlet}} \ No newline at end of file From 32b95dddf76bc346472879c186aad4788a30ad7f Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Tue, 28 Nov 2023 16:56:03 -0600 Subject: [PATCH 02/11] glimmerize replication-summary-card --- .../components/replication-summary-card.hbs | 75 +++++++++++++++++++ .../components/replication-summary-card.js | 51 ++++++------- .../components/replication-summary-card.hbs | 63 ---------------- .../replication-summary-card-test.js | 38 +++++++++- 4 files changed, 135 insertions(+), 92 deletions(-) create mode 100644 ui/lib/core/addon/components/replication-summary-card.hbs delete mode 100644 ui/lib/core/addon/templates/components/replication-summary-card.hbs diff --git a/ui/lib/core/addon/components/replication-summary-card.hbs b/ui/lib/core/addon/components/replication-summary-card.hbs new file mode 100644 index 000000000000..4a9b2d3ee41c --- /dev/null +++ b/ui/lib/core/addon/components/replication-summary-card.hbs @@ -0,0 +1,75 @@ +{{! + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: BUSL-1.1 +~}} + + + {{! Check if DR or Performance Card }} + {{#if (eq @title "Disaster Recovery")}} +

{{@title}}

+
+ +
+
+

last_dr_wal

+

+ Index of last WAL entry written on local storage. Updates every ten seconds. +

+
+
+

known_secondaries

+

Number of secondaries connected to this primary.

+
+

{{format-number this.lastDrWAL}}

+

{{format-number this.knownSecondariesDr}}

+
+

merkle_root

+

A snapshot of the merkle tree's root hash.

+
{{this.merkleRootDr}}
+
+ {{else}} +

{{@title}}

+
+ +
+
+

last_performance_wal

+

+ Index of last WAL entry written on local storage. Updates every ten seconds. +

+
+
+

known_secondaries

+

Number of secondaries connected to this primary.

+
+

{{format-number this.lastPerformanceWAL}}

+

+ {{format-number this.knownSecondariesPerformance}} +

+
+

merkle_root

+

A snapshot of the merkle tree's root hash.

+
{{this.merkleRootPerformance}}
+
+ {{/if}} +
\ No newline at end of file diff --git a/ui/lib/core/addon/components/replication-summary-card.js b/ui/lib/core/addon/components/replication-summary-card.js index df25cac695a6..4a051c4b954c 100644 --- a/ui/lib/core/addon/components/replication-summary-card.js +++ b/ui/lib/core/addon/components/replication-summary-card.js @@ -3,9 +3,7 @@ * SPDX-License-Identifier: BUSL-1.1 */ -import Component from '@ember/component'; -import { computed } from '@ember/object'; -import layout from '../templates/components/replication-summary-card'; +import Component from '@glimmer/component'; /** * @module ReplicationSummaryCard @@ -22,28 +20,25 @@ import layout from '../templates/components/replication-summary-card'; * @param {Object} replicationDetails=null - An Ember data object computed off the Ember Model. It combines the Model.dr and Model.performance objects into one and contains details specific to the mode replication. */ -export default Component.extend({ - layout, - title: null, - replicationDetails: null, - lastDrWAL: computed('replicationDetails.dr.lastWAL', function () { - return this.replicationDetails.dr.lastWAL || 0; - }), - lastPerformanceWAL: computed('replicationDetails.performance.lastWAL', function () { - return this.replicationDetails.performance.lastWAL || 0; - }), - merkleRootDr: computed('replicationDetails.dr.merkleRoot', function () { - return this.replicationDetails.dr.merkleRoot || ''; - }), - merkleRootPerformance: computed('replicationDetails.performance.merkleRoot', function () { - return this.replicationDetails.performance.merkleRoot || ''; - }), - knownSecondariesDr: computed('replicationDetails.dr.knownSecondaries', function () { - const knownSecondaries = this.replicationDetails.dr.knownSecondaries; - return knownSecondaries.length; - }), - knownSecondariesPerformance: computed('replicationDetails.performance.knownSecondaries', function () { - const knownSecondaries = this.replicationDetails.performance.knownSecondaries; - return knownSecondaries.length; - }), -}); +export default class ReplicationSummaryCard extends Component { + get lastDrWAL() { + return this.args.replicationDetails.dr?.lastWAL || 0; + } + get lastPerformanceWAL() { + return this.args.replicationDetails.performance?.lastWAL || 0; + } + get merkleRootDr() { + return this.args.replicationDetails.dr?.merkleRoot || ''; + } + get merkleRootPerformance() { + return this.args.replicationDetails.performance?.merkleRoot || ''; + } + get knownSecondariesDr() { + const knownSecondaries = this.args.replicationDetails.dr.knownSecondaries; + return knownSecondaries?.length ?? 0; + } + get knownSecondariesPerformance() { + const knownSecondaries = this.args.replicationDetails.performance.knownSecondaries; + return knownSecondaries?.length ?? 0; + } +} diff --git a/ui/lib/core/addon/templates/components/replication-summary-card.hbs b/ui/lib/core/addon/templates/components/replication-summary-card.hbs deleted file mode 100644 index f4562b523625..000000000000 --- a/ui/lib/core/addon/templates/components/replication-summary-card.hbs +++ /dev/null @@ -1,63 +0,0 @@ -{{! - Copyright (c) HashiCorp, Inc. - SPDX-License-Identifier: BUSL-1.1 -~}} - - - {{! Check if DR or Performance Card }} - {{#if (eq this.title "Disaster Recovery")}} -

{{this.title}}

-
- - Details - -
-
-
last_dr_wal
-

- Index of last WAL entry written on local storage. Updates every ten seconds. -

-
-
-
known_secondaries
-

Number of secondaries connected to this primary.

-
-

{{format-number this.lastDrWAL}}

-

{{format-number this.knownSecondariesDr}}

-
-
merkle_root
-

A snapshot of the merkle tree's root hash.

-
{{this.merkleRootDr}}
-
- {{else}} -

{{this.title}}

-
- - Details - -
-
-
last_performance_wal
-

- Index of last WAL entry written on local storage. Updates every ten seconds. -

-
-
-
known_secondaries
-

Number of secondaries connected to this primary.

-
-

{{format-number this.lastPerformanceWAL}}

-

- {{format-number this.knownSecondariesPerformance}} -

-
-
merkle_root
-

A snapshot of the merkle tree's root hash.

-
{{this.merkleRootPerformance}}
-
- {{/if}} -
\ No newline at end of file diff --git a/ui/tests/integration/components/replication-summary-card-test.js b/ui/tests/integration/components/replication-summary-card-test.js index a8ca8dafdc0b..4f80edf264af 100644 --- a/ui/tests/integration/components/replication-summary-card-test.js +++ b/ui/tests/integration/components/replication-summary-card-test.js @@ -5,7 +5,7 @@ import { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; -import { render } from '@ember/test-helpers'; +import { render, settled } from '@ember/test-helpers'; import hbs from 'htmlbars-inline-precompile'; const TITLE = 'Disaster Recovery'; @@ -15,11 +15,13 @@ const REPLICATION_DETAILS = { state: 'running', lastWAL: 10, knownSecondaries: ['https://127.0.0.1:8201', 'https://127.0.0.1:8202'], + merkleRoot: 'zzzzzzzyyyyyyyxxxxxxxwwwwww', }, performance: { state: 'running', lastWAL: 20, knownSecondaries: ['https://127.0.0.1:8201'], + merkleRoot: 'aaaaaabbbbbbbbccccccccdddddd', }, }; @@ -44,6 +46,9 @@ module('Integration | Component | replication-summary-card', function (hooks) { assert .dom('[data-test-known-secondaries]') .includesText(knownSecondaries, `shows the correct computed value of the known secondaries count`); + assert + .dom('[data-test-merkle-root]') + .includesText(REPLICATION_DETAILS.dr.merkleRoot, `shows the correct merkle root value`); }); test('it shows the correct lastWAL and knownSecondaries when title is Performance', async function (assert) { @@ -58,5 +63,36 @@ module('Integration | Component | replication-summary-card', function (hooks) { assert .dom('[data-test-known-secondaries]') .includesText(knownSecondaries, `shows the correct computed value of the known secondaries count`); + assert + .dom('[data-test-merkle-root]') + .includesText(REPLICATION_DETAILS.performance.merkleRoot, `shows the correct merkle root value`); + }); + + test('it shows reasonable defaults', async function (assert) { + const data = { + dr: { + mode: 'disabled', + }, + performance: { + mode: 'disabled', + }, + }; + this.set('replicationDetails', data); + await render( + hbs`` + ); + assert.dom('[data-test-lastWAL]').includesText('0', `shows the correct lastWAL value`); + assert + .dom('[data-test-known-secondaries]') + .includesText('0', `shows the correct default value of the known secondaries count`); + assert.dom('[data-test-merkle-root]').includesText('', `shows the correct merkle root value`); + + await this.set('title', 'Performance'); + await settled(); + assert.dom('[data-test-lastWAL]').includesText('0', `shows the correct lastWAL value`); + assert + .dom('[data-test-known-secondaries]') + .includesText('0', `shows the correct default value of the known secondaries count`); + assert.dom('[data-test-merkle-root]').includesText('', `shows the correct merkle root value`); }); }); From b8f2d622909149c2daa01d98fb3ef76e5125e636 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Tue, 28 Nov 2023 17:08:08 -0600 Subject: [PATCH 03/11] Simplify replication-summary-card --- .../components/replication-summary-card.hbs | 93 ++++++------------- .../components/replication-summary-card.js | 25 ++--- 2 files changed, 38 insertions(+), 80 deletions(-) diff --git a/ui/lib/core/addon/components/replication-summary-card.hbs b/ui/lib/core/addon/components/replication-summary-card.hbs index 4a9b2d3ee41c..54f7d93b9b05 100644 --- a/ui/lib/core/addon/components/replication-summary-card.hbs +++ b/ui/lib/core/addon/components/replication-summary-card.hbs @@ -5,71 +5,36 @@ - {{! Check if DR or Performance Card }} - {{#if (eq @title "Disaster Recovery")}} -

{{@title}}

-
- -
-
-

last_dr_wal

-

- Index of last WAL entry written on local storage. Updates every ten seconds. -

-
-
-

known_secondaries

-

Number of secondaries connected to this primary.

-
-

{{format-number this.lastDrWAL}}

-

{{format-number this.knownSecondariesDr}}

-
-

merkle_root

-

A snapshot of the merkle tree's root hash.

-
{{this.merkleRootDr}}
-
- {{else}} -

{{@title}}

-
- -
-
-

last_performance_wal

-

- Index of last WAL entry written on local storage. Updates every ten seconds. -

-
-
-

known_secondaries

-

Number of secondaries connected to this primary.

-
-

{{format-number this.lastPerformanceWAL}}

-

- {{format-number this.knownSecondariesPerformance}} +

{{@title}}

+
+ +
+
+

last_dr_wal

+

+ Index of last WAL entry written on local storage. Updates every ten seconds.

-
-

merkle_root

-

A snapshot of the merkle tree's root hash.

-
{{this.merkleRootPerformance}}
-
- {{/if}} +
+
+

known_secondaries

+

Number of secondaries connected to this primary.

+
+

{{format-number this.lastWAL}}

+

{{format-number this.knownSecondariesCount}}

+
+

merkle_root

+

A snapshot of the merkle tree's root hash.

+
{{this.merkleRoot}}
+
\ No newline at end of file diff --git a/ui/lib/core/addon/components/replication-summary-card.js b/ui/lib/core/addon/components/replication-summary-card.js index 4a051c4b954c..9d0439f88dde 100644 --- a/ui/lib/core/addon/components/replication-summary-card.js +++ b/ui/lib/core/addon/components/replication-summary-card.js @@ -3,6 +3,7 @@ * SPDX-License-Identifier: BUSL-1.1 */ +import { get } from '@ember/object'; import Component from '@glimmer/component'; /** @@ -21,24 +22,16 @@ import Component from '@glimmer/component'; */ export default class ReplicationSummaryCard extends Component { - get lastDrWAL() { - return this.args.replicationDetails.dr?.lastWAL || 0; + get key() { + return this.args.title === 'Performance' ? 'performance' : 'dr'; } - get lastPerformanceWAL() { - return this.args.replicationDetails.performance?.lastWAL || 0; + get lastWAL() { + return get(this.args.replicationDetails, `${this.key}.lastWAL`) ?? 0; } - get merkleRootDr() { - return this.args.replicationDetails.dr?.merkleRoot || ''; + get merkleRoot() { + return get(this.args.replicationDetails, `${this.key}.merkleRoot`) ?? ''; } - get merkleRootPerformance() { - return this.args.replicationDetails.performance?.merkleRoot || ''; - } - get knownSecondariesDr() { - const knownSecondaries = this.args.replicationDetails.dr.knownSecondaries; - return knownSecondaries?.length ?? 0; - } - get knownSecondariesPerformance() { - const knownSecondaries = this.args.replicationDetails.performance.knownSecondaries; - return knownSecondaries?.length ?? 0; + get knownSecondariesCount() { + return get(this.args.replicationDetails, `${this.key}.knownSecondaries.length`) ?? 0; } } From 8324b6f78786660681167417a8396a0082672b5a Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Wed, 29 Nov 2023 12:44:23 -0600 Subject: [PATCH 04/11] update replication subnav + tests --- ui/app/components/sidebar/nav/cluster.hbs | 9 +-------- ui/lib/replication/addon/templates/application.hbs | 5 +++-- ui/tests/acceptance/enterprise-sidebar-nav-test.js | 13 ++++++------- .../components/sidebar/nav/cluster-test.js | 4 +--- 4 files changed, 11 insertions(+), 20 deletions(-) diff --git a/ui/app/components/sidebar/nav/cluster.hbs b/ui/app/components/sidebar/nav/cluster.hbs index 7e1f88cdef48..6668bade2e02 100644 --- a/ui/app/components/sidebar/nav/cluster.hbs +++ b/ui/app/components/sidebar/nav/cluster.hbs @@ -52,14 +52,7 @@ }} Monitoring {{/if}} - {{#if - (and - this.version.isEnterprise - this.namespace.inRootNamespace - this.cluster.anyReplicationEnabled - (has-permission "status" routeParams="replication") - ) - }} + {{#if (and this.version.isEnterprise this.namespace.inRootNamespace (has-permission "status" routeParams="replication"))}} {{#if (has-feature "DR Replication")}} - + {{/if}} {{#if (has-feature "Performance Replication")}} {{/if}} + {{outlet}} \ No newline at end of file diff --git a/ui/tests/acceptance/enterprise-sidebar-nav-test.js b/ui/tests/acceptance/enterprise-sidebar-nav-test.js index 28e7671296dd..73246bea8291 100644 --- a/ui/tests/acceptance/enterprise-sidebar-nav-test.js +++ b/ui/tests/acceptance/enterprise-sidebar-nav-test.js @@ -5,7 +5,7 @@ import { module, test } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; -import { click, currentURL, fillIn } from '@ember/test-helpers'; +import { click, currentURL } from '@ember/test-helpers'; import { setupMirage } from 'ember-cli-mirage/test-support'; import authPage from 'vault/tests/pages/auth'; @@ -22,11 +22,15 @@ module('Acceptance | Enterprise | sidebar navigation', function (hooks) { // common links are tested in the sidebar-nav test and will not be covered here test('it should render enterprise only navigation links', async function (assert) { + assert.expect(12); assert.dom(panel('Cluster')).exists('Cluster nav panel renders'); await click(link('Replication')); assert.strictEqual(currentURL(), '/vault/replication', 'Replication route renders'); - await click('[data-test-replication-enable]'); + assert.dom(panel('Replication')).exists(`Replication nav panel renders`); + assert.dom(link('Overview')).hasClass('active', 'Overview link is active'); + assert.dom(link('Performance')).exists('Performance link exists'); + assert.dom(link('Disaster Recovery')).exists('DR link exists'); await click(link('Performance')); assert.strictEqual( @@ -37,11 +41,6 @@ module('Acceptance | Enterprise | sidebar navigation', function (hooks) { await click(link('Disaster Recovery')); assert.strictEqual(currentURL(), '/vault/replication/dr', 'Replication dr route renders'); - // disable replication now that we have checked the links - await click('[data-test-replication-link="manage"]'); - await click('[data-test-replication-action-trigger]'); - await fillIn('[data-test-confirmation-modal-input="Disable Replication?"]', 'Disaster Recovery'); - await click('[data-test-confirm-button="Disable Replication?"]'); await click(link('Client Count')); assert.strictEqual(currentURL(), '/vault/clients/dashboard', 'Client counts route renders'); diff --git a/ui/tests/integration/components/sidebar/nav/cluster-test.js b/ui/tests/integration/components/sidebar/nav/cluster-test.js index ccb9be3bcbf5..b0a477744629 100644 --- a/ui/tests/integration/components/sidebar/nav/cluster-test.js +++ b/ui/tests/integration/components/sidebar/nav/cluster-test.js @@ -21,7 +21,7 @@ module('Integration | Component | sidebar-nav-cluster', function (hooks) { setupRenderingTest(hooks); test('it should render nav headings', async function (assert) { - const headings = ['Vault', 'Replication', 'Monitoring']; + const headings = ['Vault', 'Monitoring']; stubFeaturesAndPermissions(this.owner, true, true); await renderComponent(); @@ -52,8 +52,6 @@ module('Integration | Component | sidebar-nav-cluster', function (hooks) { 'Access', 'Policies', 'Tools', - 'Disaster Recovery', - 'Performance', 'Replication', 'Raft Storage', 'Client Count', From ef32618db2c5cc96e0d537d8a959701df6569d5f Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Wed, 29 Nov 2023 12:45:04 -0600 Subject: [PATCH 05/11] replication action block uses HDS card --- ui/app/styles/components/action-block.scss | 5 ++++- .../templates/components/replication-actions.hbs | 14 ++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/ui/app/styles/components/action-block.scss b/ui/app/styles/components/action-block.scss index ec3c482678a7..f5b152831771 100644 --- a/ui/app/styles/components/action-block.scss +++ b/ui/app/styles/components/action-block.scss @@ -11,8 +11,11 @@ margin-bottom: $spacing-24; } +.action-block-width { + width: 100%; +} + .action-block { - box-shadow: 0 0 0 1px rgba($grey-dark, 0.3); grid-template-columns: 2fr 1fr; display: grid; padding: $spacing-16 $spacing-24; diff --git a/ui/lib/core/addon/templates/components/replication-actions.hbs b/ui/lib/core/addon/templates/components/replication-actions.hbs index 05b4b8c677ef..718ad28a151f 100644 --- a/ui/lib/core/addon/templates/components/replication-actions.hbs +++ b/ui/lib/core/addon/templates/components/replication-actions.hbs @@ -13,12 +13,14 @@ as |replicationAction| }}
- {{component - (concat "replication-action-" replicationAction) - onSubmit=(action "onSubmit") - replicationMode=this.replicationMode - model=this.model - }} + + {{component + (concat "replication-action-" replicationAction) + onSubmit=(action "onSubmit") + replicationMode=this.replicationMode + model=this.model + }} +
{{/each}} From a882455798aacb30e6a424426de4a914eab7818a Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Wed, 29 Nov 2023 12:46:01 -0600 Subject: [PATCH 06/11] add/update test selectors --- .../components/replication-mode-summary.hbs | 21 +++++++------------ .../components/replication-summary.hbs | 4 ++-- .../acceptance/enterprise-replication-test.js | 2 +- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/ui/lib/core/addon/templates/components/replication-mode-summary.hbs b/ui/lib/core/addon/templates/components/replication-mode-summary.hbs index ae94e978e086..5fc328dde6b8 100644 --- a/ui/lib/core/addon/templates/components/replication-mode-summary.hbs +++ b/ui/lib/core/addon/templates/components/replication-mode-summary.hbs @@ -102,20 +102,13 @@
- {{#if this.replicationDisabled}} - - Enable - - {{else}} - - Details - - {{/if}} +
{{/if}} \ No newline at end of file diff --git a/ui/lib/replication/addon/templates/components/replication-summary.hbs b/ui/lib/replication/addon/templates/components/replication-summary.hbs index 3750ad2c5aea..9f7d0bbc1298 100644 --- a/ui/lib/replication/addon/templates/components/replication-summary.hbs +++ b/ui/lib/replication/addon/templates/components/replication-summary.hbs @@ -8,7 +8,7 @@ {{else if (or this.cluster.allReplicationDisabled this.cluster.replicationAttrs.replicationDisabled)}} -

+

{{#if this.initialReplicationMode}} {{#if (eq this.initialReplicationMode "dr")}} Enable Disaster Recovery Replication @@ -291,7 +291,7 @@ {{#if (not (and this.cluster.dr.replicationEnabled this.cluster.performance.replicationEnabled))}} -

+

Replication

diff --git a/ui/tests/acceptance/enterprise-replication-test.js b/ui/tests/acceptance/enterprise-replication-test.js index 9a89204d587f..8d46ad0d9f9d 100644 --- a/ui/tests/acceptance/enterprise-replication-test.js +++ b/ui/tests/acceptance/enterprise-replication-test.js @@ -306,7 +306,7 @@ module('Acceptance | Enterprise | replication', function (hooks) { .doesNotExist(`does not render replication summary card when both modes are not enabled as primary`); // enable DR primary replication - await click('[data-test-replication-promote-secondary]'); + await click('[data-test-replication-details-link="dr"]'); await click('[data-test-replication-enable]'); await pollCluster(this.owner); From b10d243a8c7ada468801366fdf83f488fc54f540 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Wed, 29 Nov 2023 13:23:01 -0600 Subject: [PATCH 07/11] test coverage --- ui/app/models/cluster.js | 4 +- .../components/replication-header.hbs | 2 +- .../addon/templates/application.hbs | 12 +- .../enterprise-replication-modes-test.js | 123 ++++++++++++++++++ ...enterprise-replication-unsupported-test.js | 33 ----- ui/tests/helpers/replication.js | 60 +++++++++ 6 files changed, 194 insertions(+), 40 deletions(-) create mode 100644 ui/tests/acceptance/enterprise-replication-modes-test.js delete mode 100644 ui/tests/acceptance/enterprise-replication-unsupported-test.js diff --git a/ui/app/models/cluster.js b/ui/app/models/cluster.js index 6ac7f90af367..a65ddb562a2d 100644 --- a/ui/app/models/cluster.js +++ b/ui/app/models/cluster.js @@ -76,7 +76,9 @@ export default class ClusterModel extends Model { //replication mode - will only ever be 'unsupported' //otherwise the particular mode will have the relevant mode attr through replication-attributes - @attr('string') mode; + // eg dr.mode or performance.mode + @attr('string') + mode; get allReplicationDisabled() { return this.dr?.replicationDisabled && this.performance?.replicationDisabled; } diff --git a/ui/lib/core/addon/templates/components/replication-header.hbs b/ui/lib/core/addon/templates/components/replication-header.hbs index b8e0f0afc6bc..286a636841ee 100644 --- a/ui/lib/core/addon/templates/components/replication-header.hbs +++ b/ui/lib/core/addon/templates/components/replication-header.hbs @@ -20,7 +20,7 @@ {{/unless}} -

+

{{this.title}} {{#if this.data.anyReplicationEnabled}} diff --git a/ui/lib/replication/addon/templates/application.hbs b/ui/lib/replication/addon/templates/application.hbs index 571a71fa87b1..1e8c8ad0b3ff 100644 --- a/ui/lib/replication/addon/templates/application.hbs +++ b/ui/lib/replication/addon/templates/application.hbs @@ -21,11 +21,13 @@ @text="Overview" data-test-sidebar-nav-link="Overview" /> - {{#if (has-feature "DR Replication")}} - - {{/if}} - {{#if (has-feature "Performance Replication")}} - + {{#if (not-eq this.model.mode "unsupported")}} + {{#if (has-feature "DR Replication")}} + + {{/if}} + {{#if (has-feature "Performance Replication")}} + + {{/if}} {{/if}} diff --git a/ui/tests/acceptance/enterprise-replication-modes-test.js b/ui/tests/acceptance/enterprise-replication-modes-test.js new file mode 100644 index 000000000000..df51407fcd6a --- /dev/null +++ b/ui/tests/acceptance/enterprise-replication-modes-test.js @@ -0,0 +1,123 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import { module, test } from 'qunit'; +import { setupApplicationTest } from 'ember-qunit'; +import { setupMirage } from 'ember-cli-mirage/test-support'; +import { click, visit } from '@ember/test-helpers'; +import authPage from 'vault/tests/pages/auth'; +import { STATUS_DISABLED_RESPONSE, mockReplicationBlock } from 'vault/tests/helpers/replication'; + +const s = { + navLink: (title) => `[data-test-sidebar-nav-link="${title}"]`, + title: '[data-test-replication-title]', + detailLink: (mode) => `[data-test-replication-details-link="${mode}"]`, + summaryCard: '[data-test-replication-summary-card]', +}; + +module('Acceptance | Enterprise | replication modes', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + hooks.beforeEach(async function () { + this.setupMocks = (payload) => { + this.server.get('sys/replication/status', () => ({ + data: payload, + })); + return authPage.login(); + }; + }); + + test('replication page when unsupported', async function (assert) { + await this.setupMocks({ + data: { + mode: 'unsupported', + }, + }); + await visit('/vault/replication'); + assert.dom(s.title).hasText('Replication unsupported', 'it shows the unsupported view'); + + // Nav links + assert.dom(s.navLink('Performance')).doesNotExist('hides performance link'); + assert.dom(s.navLink('Disaster Recovery')).doesNotExist('hides dr link'); + }); + + test('replication page when disabled', async function (assert) { + await this.setupMocks(STATUS_DISABLED_RESPONSE); + await visit('/vault/replication'); + assert.dom(s.title).hasText('Enable Replication', 'it shows the enable view'); + + // Nav links + assert.dom(s.navLink('Performance')).exists('shows performance link'); + assert.dom(s.navLink('Disaster Recovery')).exists('shows dr link'); + + await click(s.navLink('Performance')); + assert.dom(s.title).hasText('Enable Performance Replication', 'it shows the enable view for performance'); + + await click(s.navLink('Disaster Recovery')); + assert.dom(s.title).hasText('Enable Disaster Recovery Replication', 'it shows the enable view for dr'); + }); + + ['primary', 'secondary'].forEach((mode) => { + test(`replication page when perf ${mode} only`, async function (assert) { + await this.setupMocks({ + dr: mockReplicationBlock(), + performance: mockReplicationBlock(mode), + }); + await visit('/vault/replication'); + + assert.dom(s.title).hasText('Replication', 'it shows default view'); + assert.dom(s.detailLink('performance')).hasText('Details', 'CTA to see performance details'); + assert.dom(s.detailLink('dr')).hasText('Enable', 'CTA to enable dr'); + + // Nav links + assert.dom(s.navLink('Performance')).exists('shows performance link'); + assert.dom(s.navLink('Disaster Recovery')).exists('shows dr link'); + + await click(s.navLink('Performance')); + assert.dom(s.title).hasText(`Performance ${mode}`, `it shows the performance mode details`); + + await click(s.navLink('Disaster Recovery')); + assert.dom(s.title).hasText('Enable Disaster Recovery Replication', 'it shows the enable view for dr'); + }); + }); + // DR secondary mode is a whole other thing, test primary only here + test(`replication page when dr primary only`, async function (assert) { + await this.setupMocks({ + dr: mockReplicationBlock('primary'), + performance: mockReplicationBlock(), + }); + await visit('/vault/replication'); + assert.dom(s.title).hasText('Replication', 'it shows default view'); + assert.dom(s.detailLink('performance')).hasText('Enable', 'CTA to enable performance'); + assert.dom(s.detailLink('dr')).hasText('Details', 'CTA to see dr details'); + + // Nav links + assert.dom(s.navLink('Performance')).exists('shows performance link'); + assert.dom(s.navLink('Disaster Recovery')).exists('shows dr link'); + + await click(s.navLink('Performance')); + assert.dom(s.title).hasText(`Enable Performance Replication`, `it shows the enable view for performance`); + + await click(s.navLink('Disaster Recovery')); + assert.dom(s.title).hasText(`Disaster Recovery primary`, 'it shows the dr mode details'); + }); + + test(`replication page both primary`, async function (assert) { + await this.setupMocks({ + dr: mockReplicationBlock('primary'), + performance: mockReplicationBlock('primary'), + }); + await visit('/vault/replication'); + assert.dom(s.title).hasText('Disaster Recovery & Performance primary', 'it shows primary view'); + assert.dom(s.summaryCard).exists({ count: 2 }, 'shows 2 summary cards'); + + await click(s.navLink('Performance')); + assert.dom(s.title).hasText(`Performance primary`, `it shows the performance mode details`); + + await click(s.navLink('Disaster Recovery')); + assert.dom(s.title).hasText(`Disaster Recovery primary`, 'it shows the dr mode details'); + }); +}); diff --git a/ui/tests/acceptance/enterprise-replication-unsupported-test.js b/ui/tests/acceptance/enterprise-replication-unsupported-test.js deleted file mode 100644 index cfcb2a73c273..000000000000 --- a/ui/tests/acceptance/enterprise-replication-unsupported-test.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupApplicationTest } from 'ember-qunit'; -import { setupMirage } from 'ember-cli-mirage/test-support'; -import authPage from 'vault/tests/pages/auth'; -import { visit } from '@ember/test-helpers'; - -module('Acceptance | Enterprise | replication unsupported', function (hooks) { - setupApplicationTest(hooks); - setupMirage(hooks); - - hooks.beforeEach(async function () { - this.server.get('/sys/replication/status', function () { - return { - data: { - mode: 'unsupported', - }, - }; - }); - return authPage.login(); - }); - - test('replication page when unsupported', async function (assert) { - await visit('/vault/replication'); - assert - .dom('[data-test-replication-title]') - .hasText('Replication unsupported', 'it shows the unsupported view'); - }); -}); diff --git a/ui/tests/helpers/replication.js b/ui/tests/helpers/replication.js index be3cc8ab0d32..3024ee9d0e82 100644 --- a/ui/tests/helpers/replication.js +++ b/ui/tests/helpers/replication.js @@ -34,3 +34,63 @@ export const disableReplication = async (type, assert) => { await settled(); } }; + +export const STATUS_DISABLED_RESPONSE = { + dr: mockReplicationBlock(), + performance: mockReplicationBlock(), +}; + +/** + * Mock replication block returns the expected payload for a given replication type + * @param {string} mode disabled | primary | secondary + * @param {string} status connected | disconnected + * @returns expected object for a single replication type, eg dr or performance values + */ +export function mockReplicationBlock(mode = 'disabled', status = 'connected') { + switch (mode) { + case 'primary': + return { + cluster_id: '5f20f5ab-acea-0481-787e-71ec2ff5a60b', + known_secondaries: ['4'], + last_wal: 455, + merkle_root: 'aaaaaabbbbbbbccccccccddddddd', + mode: 'primary', + primary_cluster_addr: '', + secondaries: [ + { + api_address: 'https://127.0.0.1:49277', + cluster_address: 'https://127.0.0.1:49281', + connection_status: status, + last_heartbeat: '2020-06-10T15:40:46-07:00', + node_id: '4', + }, + ], + state: 'stream-wals', + }; + case 'secondary': + return { + cluster_id: '5f20f5ab-acea-0481-787e-71ec2ff5a60b', + known_primary_cluster_addrs: ['https://127.0.0.1:8201'], + last_remote_wal: 291, + merkle_root: 'aaaaaabbbbbbbccccccccddddddd', + corrupted_merkle_tree: false, + last_corruption_check_epoch: '1694456090', + mode: 'secondary', + primaries: [ + { + api_address: 'https://127.0.0.1:49244', + cluster_address: 'https://127.0.0.1:8201', + connection_status: status, + last_heartbeat: '2020-06-10T15:40:46-07:00', + }, + ], + primary_cluster_addr: 'https://127.0.0.1:8201', + secondary_id: '2', + state: 'stream-wals', + }; + default: + return { + mode: 'disabled', + }; + } +} From 9e73af2de89bc4484459079a4954b3f482f7ef5a Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Wed, 29 Nov 2023 13:58:56 -0600 Subject: [PATCH 08/11] Add changelog --- changelog/24283.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog/24283.txt diff --git a/changelog/24283.txt b/changelog/24283.txt new file mode 100644 index 000000000000..f8f885f3e11e --- /dev/null +++ b/changelog/24283.txt @@ -0,0 +1,3 @@ +```release-note:change +ui: add subnav for replication items +``` From c39f5fdc7017897bb5f7884d2a25884f5327d5e7 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Thu, 30 Nov 2023 14:29:50 -0600 Subject: [PATCH 09/11] Update defaults on replication-summary-card --- ui/lib/core/addon/components/replication-summary-card.js | 6 +++--- .../integration/components/replication-summary-card-test.js | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ui/lib/core/addon/components/replication-summary-card.js b/ui/lib/core/addon/components/replication-summary-card.js index 9d0439f88dde..21a15ec89670 100644 --- a/ui/lib/core/addon/components/replication-summary-card.js +++ b/ui/lib/core/addon/components/replication-summary-card.js @@ -26,12 +26,12 @@ export default class ReplicationSummaryCard extends Component { return this.args.title === 'Performance' ? 'performance' : 'dr'; } get lastWAL() { - return get(this.args.replicationDetails, `${this.key}.lastWAL`) ?? 0; + return get(this.args.replicationDetails, `${this.key}.lastWAL`) || 0; } get merkleRoot() { - return get(this.args.replicationDetails, `${this.key}.merkleRoot`) ?? ''; + return get(this.args.replicationDetails, `${this.key}.merkleRoot`) || 'no hash found'; } get knownSecondariesCount() { - return get(this.args.replicationDetails, `${this.key}.knownSecondaries.length`) ?? 0; + return get(this.args.replicationDetails, `${this.key}.knownSecondaries.length`) || 0; } } diff --git a/ui/tests/integration/components/replication-summary-card-test.js b/ui/tests/integration/components/replication-summary-card-test.js index 4f80edf264af..86e98ed98cf2 100644 --- a/ui/tests/integration/components/replication-summary-card-test.js +++ b/ui/tests/integration/components/replication-summary-card-test.js @@ -93,6 +93,8 @@ module('Integration | Component | replication-summary-card', function (hooks) { assert .dom('[data-test-known-secondaries]') .includesText('0', `shows the correct default value of the known secondaries count`); - assert.dom('[data-test-merkle-root]').includesText('', `shows the correct merkle root value`); + assert + .dom('[data-test-merkle-root]') + .includesText('no hash found', `shows the correct merkle root value`); }); }); From b732323588e23308f8b0fbdca131a547a7230be2 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Thu, 30 Nov 2023 15:27:28 -0600 Subject: [PATCH 10/11] test that the view updates between replication types --- .../templates/components/replication-summary.hbs | 1 + .../enterprise-replication-modes-test.js | 15 +++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/ui/lib/replication/addon/templates/components/replication-summary.hbs b/ui/lib/replication/addon/templates/components/replication-summary.hbs index 9f7d0bbc1298..7fa582ffcb1a 100644 --- a/ui/lib/replication/addon/templates/components/replication-summary.hbs +++ b/ui/lib/replication/addon/templates/components/replication-summary.hbs @@ -36,6 +36,7 @@ replicationMode=this.replicationMode ) }} + data-test-replication-enable-form >
diff --git a/ui/tests/acceptance/enterprise-replication-modes-test.js b/ui/tests/acceptance/enterprise-replication-modes-test.js index df51407fcd6a..862286b650eb 100644 --- a/ui/tests/acceptance/enterprise-replication-modes-test.js +++ b/ui/tests/acceptance/enterprise-replication-modes-test.js @@ -15,6 +15,9 @@ const s = { title: '[data-test-replication-title]', detailLink: (mode) => `[data-test-replication-details-link="${mode}"]`, summaryCard: '[data-test-replication-summary-card]', + dashboard: '[data-test-replication-dashboard]', + enableForm: '[data-test-replication-enable-form]', + knownSecondary: (name) => `[data-test-secondaries-node="${name}"]`, }; module('Acceptance | Enterprise | replication modes', function (hooks) { @@ -77,10 +80,12 @@ module('Acceptance | Enterprise | replication modes', function (hooks) { assert.dom(s.navLink('Disaster Recovery')).exists('shows dr link'); await click(s.navLink('Performance')); - assert.dom(s.title).hasText(`Performance ${mode}`, `it shows the performance mode details`); + assert.dom(s.title).hasText(`Performance ${mode}`, `it shows the performance title`); + assert.dom(s.dashboard).exists(`it shows the replication dashboard`); await click(s.navLink('Disaster Recovery')); - assert.dom(s.title).hasText('Enable Disaster Recovery Replication', 'it shows the enable view for dr'); + assert.dom(s.title).hasText('Enable Disaster Recovery Replication', 'it shows the dr title'); + assert.dom(s.enableForm).exists('it shows the enable view for dr'); }); }); // DR secondary mode is a whole other thing, test primary only here @@ -99,10 +104,12 @@ module('Acceptance | Enterprise | replication modes', function (hooks) { assert.dom(s.navLink('Disaster Recovery')).exists('shows dr link'); await click(s.navLink('Performance')); - assert.dom(s.title).hasText(`Enable Performance Replication`, `it shows the enable view for performance`); + assert.dom(s.title).hasText(`Enable Performance Replication`, `it shows the performance title`); + assert.dom(s.enableForm).exists('it shows the enable view for dr'); await click(s.navLink('Disaster Recovery')); - assert.dom(s.title).hasText(`Disaster Recovery primary`, 'it shows the dr mode details'); + assert.dom(s.title).hasText(`Disaster Recovery primary`, 'it shows the dr title'); + assert.dom(s.dashboard).exists(`it shows the replication dashboard`); }); test(`replication page both primary`, async function (assert) { From 77bd8d2861993f2d7778aa8d1a5f3be9d41bb968 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Thu, 30 Nov 2023 15:27:51 -0600 Subject: [PATCH 11/11] typo --- ui/tests/acceptance/enterprise-replication-modes-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/tests/acceptance/enterprise-replication-modes-test.js b/ui/tests/acceptance/enterprise-replication-modes-test.js index 862286b650eb..c06c7181acd8 100644 --- a/ui/tests/acceptance/enterprise-replication-modes-test.js +++ b/ui/tests/acceptance/enterprise-replication-modes-test.js @@ -105,7 +105,7 @@ module('Acceptance | Enterprise | replication modes', function (hooks) { await click(s.navLink('Performance')); assert.dom(s.title).hasText(`Enable Performance Replication`, `it shows the performance title`); - assert.dom(s.enableForm).exists('it shows the enable view for dr'); + assert.dom(s.enableForm).exists('it shows the enable view for performance'); await click(s.navLink('Disaster Recovery')); assert.dom(s.title).hasText(`Disaster Recovery primary`, 'it shows the dr title');