diff --git a/ui/app/styles/components/replication-mode-summary.scss b/ui/app/styles/components/replication-mode-summary.scss deleted file mode 100644 index a96a3d15c6..0000000000 --- a/ui/app/styles/components/replication-mode-summary.scss +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -.replication-description { - flex-shrink: 1; - - .title { - margin-bottom: $spacing-8; - } - - .detail-tags { - margin-bottom: $spacing-16; - } -} diff --git a/ui/app/styles/core.scss b/ui/app/styles/core.scss index 2d020970b7..73d9e17963 100644 --- a/ui/app/styles/core.scss +++ b/ui/app/styles/core.scss @@ -91,7 +91,6 @@ @import './components/read-more'; @import './components/regex-validator'; @import './components/replication-dashboard'; -@import './components/replication-mode-summary'; @import './components/replication-page'; @import './components/replication-summary'; @import './components/role-item'; diff --git a/ui/lib/core/addon/components/replication-mode-summary.js b/ui/lib/core/addon/components/replication-mode-summary.js deleted file mode 100644 index 0938b0399d..0000000000 --- a/ui/lib/core/addon/components/replication-mode-summary.js +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { service } from '@ember/service'; -import { equal } from '@ember/object/computed'; -import { get, computed } from '@ember/object'; -import Component from '@ember/component'; -import layout from '../templates/components/replication-mode-summary'; - -const replicationAttr = function (attr) { - return computed(`cluster.{dr,performance}.${attr}`, 'cluster', 'mode', function () { - const { mode, cluster } = this; - return get(cluster, `${mode}.${attr}`); - }); -}; -export default Component.extend({ - layout, - version: service(), - router: service(), - namespace: service(), - classNameBindings: ['isMenu::box'], - attributeBindings: ['href', 'target'], - display: 'banner', - isMenu: equal('display', 'menu'), - href: computed( - 'cluster.id', - 'display', - 'mode', - 'replicationEnabled', - 'version.hasPerfReplication', - function () { - const display = this.display; - const mode = this.mode; - if (mode === 'performance' && display === 'menu' && this.version.hasPerfReplication === false) { - return 'https://www.hashicorp.com/products/vault'; - } - if (this.replicationEnabled || display === 'menu') { - return this.router.urlFor('vault.cluster.replication.mode.index', this.cluster.id, mode); - } - return null; - } - ), - target: computed('isPerformance', 'version.hasPerfReplication', function () { - if (this.isPerformance && this.version.hasPerfReplication === false) { - return '_blank'; - } - return null; - }), - internalLink: false, - isPerformance: equal('mode', 'performance'), - replicationEnabled: replicationAttr('replicationEnabled'), - replicationUnsupported: equal('cluster.mode', 'unsupported'), - replicationDisabled: replicationAttr('replicationDisabled'), - syncProgressPercent: replicationAttr('syncProgressPercent'), - syncProgress: replicationAttr('syncProgress'), - secondaryId: replicationAttr('secondaryId'), - modeForUrl: replicationAttr('modeForUrl'), - clusterIdDisplay: replicationAttr('clusterIdDisplay'), - mode: null, - cluster: null, - modeState: computed('cluster', 'mode', function () { - const { cluster, mode } = this; - const clusterState = cluster[mode].state; - return clusterState; - }), -}); diff --git a/ui/lib/core/addon/templates/components/replication-mode-summary.hbs b/ui/lib/core/addon/templates/components/replication-mode-summary.hbs deleted file mode 100644 index bd2eec9950..0000000000 --- a/ui/lib/core/addon/templates/components/replication-mode-summary.hbs +++ /dev/null @@ -1,122 +0,0 @@ -{{! - Copyright (c) HashiCorp, Inc. - SPDX-License-Identifier: BUSL-1.1 -~}} - -{{#if this.isMenu}} - {{! this is the status menu }} -
-
- {{#if this.replicationUnsupported}} - Unsupported - {{else if this.replicationEnabled}} -
- {{concat (if (eq this.mode "performance") "Performance " "Disaster Recovery ") (capitalize this.modeForUrl)}} -
- {{#if this.secondaryId}} - - - {{this.secondaryId}} - - - {{/if}} - - - {{this.clusterIdDisplay}} - - - {{else if (and (eq this.mode "performance") (not (has-feature "Performance Replication")))}} - Learn more - {{else if this.auth.currentToken}} - Enable - {{if (eq this.mode "performance") "Performance" "Disaster Recovery"}} - {{else}} - - {{if (eq this.mode "performance") "Performance" "Disaster Recovery"}} - - {{/if}} -
-
- {{#if this.replicationEnabled}} - {{#if (cluster-states this.modeState)}} - - - - {{else if this.syncProgress}} - - {{this.syncProgress.progress}} - of - {{this.syncProgress.total}} - keys - - {{/if}} - {{else}} - - {{/if}} -
-
-{{else}} - {{! this is the replication index page }} -
-
-
- {{#if (and (eq this.mode "performance") (not (has-feature "Performance Replication")))}} -

- Performance Replication is a feature of Vault Enterprise Premium. - - Upgrade - -

- {{else if (and (eq this.mode "dr") (not (has-feature "DR Replication")))}} -

- Disaster Recovery is a feature of Vault Enterprise Premium. - - Upgrade - -

- {{else if this.replicationEnabled}} -
- Enabled -
-
- - {{capitalize this.modeForUrl}} - - {{#if this.secondaryId}} - - - {{this.secondaryId}} - - - {{/if}} - - - {{this.clusterIdDisplay}} - - -
- {{/if}} -

- {{replication-mode-description this.mode}} -

-
-
-
- -
-
-{{/if}} \ No newline at end of file diff --git a/ui/lib/core/app/components/replication-mode-summary.js b/ui/lib/core/app/components/replication-mode-summary.js deleted file mode 100644 index 605fceb563..0000000000 --- a/ui/lib/core/app/components/replication-mode-summary.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -export { default } from 'core/components/replication-mode-summary'; diff --git a/ui/lib/replication/addon/components/replication-overview-mode.hbs b/ui/lib/replication/addon/components/replication-overview-mode.hbs new file mode 100644 index 0000000000..424b895fec --- /dev/null +++ b/ui/lib/replication/addon/components/replication-overview-mode.hbs @@ -0,0 +1,49 @@ +{{! + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: BUSL-1.1 +~}} + + +
+
+ + {{this.details.blockTitle}} +
+
+ {{#if (not (has-feature this.details.feature))}} + + {{this.details.upgradeTitle}} + + Upgrade + + + {{else if @model.replicationEnabled}} + ENABLED +
+ {{capitalize @model.modeForUrl}} + {{#if @model.secondaryId}} + + {{/if}} + {{#if @model.clusterIdDisplay}} + + {{/if}} +
+ {{/if}} + + {{replication-mode-description @mode}} +
+ {{#if (has-feature this.details.feature)}} + + {{/if}} +
+
\ No newline at end of file diff --git a/ui/lib/replication/addon/components/replication-overview-mode.js b/ui/lib/replication/addon/components/replication-overview-mode.js new file mode 100644 index 0000000000..0a733d90c1 --- /dev/null +++ b/ui/lib/replication/addon/components/replication-overview-mode.js @@ -0,0 +1,39 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import Component from '@glimmer/component'; + +/** + * @module ReplicationOverviewModeComponent + * ReplicationOverviewMode components are used on the Replication index page to display + * details about a given mode (DR or Performance) status. + * + * @example + * + * + * @param {string} mode - should be "dr" or "performance" + * @param {ReplicationAttributesModel} model - either the dr or performance attribute of the cluster model + * @param {string} clusterName - used for the link to the mode details + */ +export default class ReplicationOverviewModeComponent extends Component { + get details() { + if (this.args.mode === 'dr') { + return { + blockTitle: 'Disaster Recovery (DR)', + upgradeTitle: 'Disaster Recovery is a feature of Vault Enterprise Premium.', + upgradeLink: 'https://hashicorp.com/products/vault/trial?source=vaultui_DR%20Replication', + feature: 'DR Replication', + icon: 'replication-direct', + }; + } + return { + blockTitle: 'Performance', + upgradeTitle: 'Performance Replication is a feature of Vault Enterprise Premium.', + upgradeLink: 'https://hashicorp.com/products/vault/trial?source=vaultui_Performance%20Replication', + feature: 'Performance Replication', + icon: 'replication-perf', + }; + } +} diff --git a/ui/lib/replication/addon/components/replication-summary.js b/ui/lib/replication/addon/components/replication-summary.js index 6c9e1f576d..43632c6463 100644 --- a/ui/lib/replication/addon/components/replication-summary.js +++ b/ui/lib/replication/addon/components/replication-summary.js @@ -8,6 +8,13 @@ import { computed } from '@ember/object'; import Component from '@ember/component'; import ReplicationActions from 'core/mixins/replication-actions'; +/** + * @module ReplicationSummary + * ReplicationSummary component is a component to show the mode-specific summary for replication + * + * @param {ClusterModel} cluster - the cluster ember-data model + * @param {string} initialReplicationMode - mode for replication details we want to see, either "dr" or "performance" + */ export default Component.extend(ReplicationActions, { 'data-test-replication-summary': true, attributeBindings: ['data-test-replication-summary'], @@ -22,7 +29,6 @@ export default Component.extend(ReplicationActions, { this.set('replicationMode', initialReplicationMode); } }, - showModeSummary: false, initialReplicationMode: null, cluster: null, diff --git a/ui/lib/replication/addon/templates/components/replication-summary.hbs b/ui/lib/replication/addon/templates/components/replication-summary.hbs index 133144adbb..138b364581 100644 --- a/ui/lib/replication/addon/templates/components/replication-summary.hbs +++ b/ui/lib/replication/addon/templates/components/replication-summary.hbs @@ -3,88 +3,39 @@ SPDX-License-Identifier: BUSL-1.1 ~}} -{{#if (not (has-feature "DR Replication"))}} - -{{else if this.showModeSummary}} - {{#if (not (and this.cluster.dr.replicationEnabled this.cluster.performance.replicationEnabled))}} - - -

- Replication -

-
-
- {{/if}} - - {{#if (and (eq this.cluster.dr.mode "primary") (eq this.cluster.performance.mode "primary"))}} +{{#if (eq this.attrsForCurrentMode.mode "initializing")}} + The cluster is initializing replication. This may take some time. +{{else}} +

{{this.cluster.replicationModeStatus.cluster_id}}

+
- - - - + + {{#if (eq this.attrsForCurrentMode.mode "secondary")}} + + + {{else}} + + + + {{/if}} - {{else}} -
-

- - Disaster Recovery (DR) -

- {{#if this.cluster.dr.replicationEnabled}} - {{#if this.submit.isRunning}} - - {{else}} - - {{/if}} - {{else}} - - {{/if}} -
- {{#if (not (and this.submit.isRunning (eq this.cluster.dr.mode "bootstrapping")))}} -
-

- - Performance -

- -
- {{/if}} - {{/if}} -{{else}} - {{#if (eq this.attrsForCurrentMode.mode "initializing")}} - The cluster is initializing replication. This may take some time. - {{else}} -

{{this.cluster.replicationModeStatus.cluster_id}}

-
- - - {{#if (eq this.attrsForCurrentMode.mode "secondary")}} - - - {{else}} - - - - {{/if}} - - -
- {{/if}} +
{{/if}} \ No newline at end of file diff --git a/ui/lib/replication/addon/templates/index.hbs b/ui/lib/replication/addon/templates/index.hbs index 79aefcc044..e2dc0b70e5 100644 --- a/ui/lib/replication/addon/templates/index.hbs +++ b/ui/lib/replication/addon/templates/index.hbs @@ -6,6 +6,7 @@
{{#if (eq this.model.mode "unsupported")}} + {{! Replication is unsupported in non-enterprise or when using non-transactional storage (eg inmem) }}

@@ -98,8 +99,36 @@ @onSuccess={{this.onEnableSuccess}} @doTransition={{true}} /> + {{else if (not (has-feature "DR Replication"))}} + + {{else if (and (eq this.model.dr.mode "primary") (eq this.model.performance.mode "primary"))}} + {{! Renders when cluster is primary for both replication modes }} + + + + + + + {{else}} - + {{! Renders when at least one mode is not enabled }} + + +

+ Replication +

+
+
+ +
+ + +
{{/if}}

\ No newline at end of file diff --git a/ui/tests/integration/components/page/mode-index-test.js b/ui/tests/integration/components/page/mode-index-test.js index c1a8db07ad..1dca929ab7 100644 --- a/ui/tests/integration/components/page/mode-index-test.js +++ b/ui/tests/integration/components/page/mode-index-test.js @@ -22,7 +22,7 @@ module('Integration | Component | replication page/mode-index', function (hooks) hooks.beforeEach(function () { this.store = this.owner.lookup('service:store'); this.onEnable = () => {}; - this.clusterModel = {}; + this.clusterModel = { replicationAttrs: {} }; this.replicationMode = ''; this.replicationDisabled = true; diff --git a/ui/tests/integration/components/replication-overview-mode-test.js b/ui/tests/integration/components/replication-overview-mode-test.js new file mode 100644 index 0000000000..dc8729dc6d --- /dev/null +++ b/ui/tests/integration/components/replication-overview-mode-test.js @@ -0,0 +1,124 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'vault/tests/helpers'; +import { render, settled } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; +import { setupEngine } from 'ember-engines/test-support'; + +const OVERVIEW_MODE = { + title: '[data-test-overview-mode-title]', + body: '[data-test-overview-mode-body]', + detailsLink: '[data-test-replication-details-link]', +}; +module('Integration | Component | replication-overview-mode', function (hooks) { + setupRenderingTest(hooks); + setupEngine(hooks, 'replication'); + + hooks.beforeEach(function () { + this.versionService = this.owner.lookup('service:version'); + this.versionService.features = []; + this.mode = 'dr'; + this.clusterName = 'foobar'; + this.modeDetails = { mode: 'disabled' }; + + this.renderComponent = async () => { + return render( + hbs` + `, + { owner: this.engine } + ); + }; + }); + + test('without features', async function (assert) { + await this.renderComponent(); + assert.dom(OVERVIEW_MODE.title).hasText('Disaster Recovery (DR)'); + assert + .dom(OVERVIEW_MODE.body) + .includesText('Disaster Recovery is a feature of Vault Enterprise Premium. Upgrade'); + assert.dom(OVERVIEW_MODE.detailsLink).doesNotExist('does not show link to replication (dr)'); + + this.set('mode', 'performance'); + await settled(); + assert.dom(OVERVIEW_MODE.title).hasText('Performance'); + assert + .dom(OVERVIEW_MODE.body) + .includesText('Performance Replication is a feature of Vault Enterprise Premium. Upgrade'); + assert.dom(OVERVIEW_MODE.detailsLink).doesNotExist('does not show link to replication (perf)'); + }); + + module('with features', function (hooks) { + hooks.beforeEach(function () { + this.versionService.features = ['DR Replication', 'Performance Replication']; + }); + + test('it renders when replication disabled', async function (assert) { + await this.renderComponent(); + assert.dom(OVERVIEW_MODE.title).hasText('Disaster Recovery (DR)'); + assert + .dom(OVERVIEW_MODE.body) + .hasText( + 'Disaster Recovery Replication is designed to protect against catastrophic failure of entire clusters. Secondaries do not forward service requests until they are elected and become a new primary.' + ); + assert.dom(OVERVIEW_MODE.detailsLink).hasText('Enable'); + + this.set('mode', 'performance'); + await settled(); + assert.dom(OVERVIEW_MODE.title).hasText('Performance'); + assert + .dom(OVERVIEW_MODE.body) + .hasText( + 'Performance Replication scales workloads horizontally across clusters to make requests faster. Local secondaries handle read requests but forward writes to the primary to be handled.' + ); + assert.dom(OVERVIEW_MODE.detailsLink).hasText('Enable'); + }); + + test('it renders when replication enabled', async function (assert) { + this.mode = 'performance'; + this.modeDetails = { + replicationEnabled: true, + mode: 'primary', + modeForUrl: 'primary', + clusterIdDisplay: 'foobar12', + }; + await this.renderComponent(); + assert.dom(OVERVIEW_MODE.title).hasText('Performance'); + assert + .dom(OVERVIEW_MODE.body) + .includesText('ENABLED Primary foobar12', 'renders mode type and cluster ID if passed'); + assert.dom(OVERVIEW_MODE.detailsLink).hasText('Details'); + + this.set('modeDetails', { + replicationEnabled: true, + mode: 'secondary', + modeForUrl: 'secondary', + clusterIdDisplay: 'foobar12', + secondaryId: 'some-secondary', + }); + await settled(); + assert.dom(OVERVIEW_MODE.title).hasText('Performance'); + assert.dom(OVERVIEW_MODE.body).includesText('ENABLED Secondary some-secondary foobar12'); + assert.dom(OVERVIEW_MODE.detailsLink).hasText('Details'); + }); + + test('it renders when replication bootstrapping', async function (assert) { + this.modeDetails = { + replicationEnabled: true, + mode: 'bootstrapping', + modeForUrl: 'bootstrapping', + }; + await this.renderComponent(); + assert.dom(OVERVIEW_MODE.title).hasText('Disaster Recovery (DR)'); + assert.dom(OVERVIEW_MODE.body).includesText('ENABLED Bootstrapping'); + assert.dom(OVERVIEW_MODE.detailsLink).hasText('Details'); + }); + }); +});