mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-29 09:42:25 +00:00
UI: Fix enabling replication capabilities bug (#28371)
* add capabilities service to replication engine * fix capabilities paths in route file * pass updated capabilities using getters * add changelog * fix logic so default is based on undefined capabilities (not no mode)
This commit is contained in:
3
changelog/28371.txt
Normal file
3
changelog/28371.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
```release-note:bug
|
||||
ui: Fix UI improperly checking capabilities for enabling performance and dr replication
|
||||
```
|
||||
@@ -27,6 +27,7 @@ export default class App extends Application {
|
||||
dependencies: {
|
||||
services: [
|
||||
'auth',
|
||||
'capabilities',
|
||||
'flash-messages',
|
||||
'namespace',
|
||||
'replication-mode',
|
||||
|
||||
@@ -19,16 +19,15 @@ import { waitFor } from '@ember/test-waiters';
|
||||
* but otherwise it handles the rest of the form inputs. On success it will clear the form and call the onSuccess callback.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* <EnableReplicationForm @replicationMode="dr" @canEnablePrimary={{true}} @canEnableSecondary={{false}} @performanceReplicationDisabled={{false}} @onSuccess={{this.reloadCluster}} />
|
||||
* @param {string} replicationMode - should be one of "dr" or "performance"
|
||||
* @param {boolean} canEnablePrimary - if the capabilities allow the user to enable a primary cluster
|
||||
* @param {boolean} canEnableSecondary - if the capabilities allow the user to enable a secondary cluster
|
||||
* @param {boolean} performanceMode - should be "primary", "secondary", or "disabled". If enabled, form will show a warning when attempting to enable DR secondary
|
||||
* @param {Promise} onSuccess - (optional) callback called after successful replication enablement. Must be a promise.
|
||||
* @param {boolean} doTransition - (optional) if provided, passed to onSuccess callback to determine if a transition should be done
|
||||
* />
|
||||
* ```
|
||||
*
|
||||
* @param {string} replicationMode - should be one of "dr" or "performance"
|
||||
* @param {boolean} canEnablePrimary - if the capabilities allow the user to enable a primary cluster, parent getter returns capabilities based on type (i.e. "dr" or "performance")
|
||||
* @param {boolean} canEnableSecondary - if the capabilities allow the user to enable a secondary cluster, parent getter returns capabilities based on type (i.e. "dr" or "performance")
|
||||
* @param {boolean} performanceMode - should be "primary", "secondary", or "disabled". If enabled, form will show a warning when attempting to enable DR secondary
|
||||
* @param {Promise} onSuccess - (optional) callback called after successful replication enablement. Must be a promise.
|
||||
* @param {boolean} doTransition - (optional) if provided, passed to onSuccess callback to determine if a transition should be done
|
||||
*
|
||||
*/
|
||||
export default class EnableReplicationFormComponent extends Component {
|
||||
@service version;
|
||||
|
||||
@@ -45,8 +45,8 @@
|
||||
</div>
|
||||
<EnableReplicationForm
|
||||
@replicationMode={{@replicationMode}}
|
||||
@canEnablePrimary={{@cluster.canEnablePrimary}}
|
||||
@canEnableSecondary={{@cluster.canEnableSecondary}}
|
||||
@canEnablePrimary={{this.canEnable "Primary"}}
|
||||
@canEnableSecondary={{this.canEnable "Secondary"}}
|
||||
@performanceReplicationDisabled={{@cluster.performance.replicationDisabled}}
|
||||
@performanceMode={{if @cluster.performance.replicationDisabled "disabled" @cluster.performance.modeForUrl}}
|
||||
@onSuccess={{@onEnableSuccess}}
|
||||
|
||||
40
ui/lib/replication/addon/components/page/mode-index.js
Normal file
40
ui/lib/replication/addon/components/page/mode-index.js
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
|
||||
/**
|
||||
* @module PageModeIndex
|
||||
*
|
||||
* @example
|
||||
* <Page::ModeIndex
|
||||
* @cluster={{this.model}}
|
||||
* @onEnableSuccess={{this.onEnableSuccess}}
|
||||
* @replicationDisabled={{this.replicationForMode.replicationDisabled}
|
||||
* @replicationMode={{this.replicationMode}}
|
||||
* />
|
||||
*
|
||||
* @param {model} cluster - cluster route model
|
||||
* @param {function} onEnableSuccess - callback after enabling is successful, handles transition if enabled from the top-level index route
|
||||
* @param {boolean} replicationDisabled - whether or not replication is enabled
|
||||
* @param {string} replicationMode - should be "dr" or "performance"
|
||||
*/
|
||||
export default class PageModeIndex extends Component {
|
||||
canEnable = (type) => {
|
||||
const { cluster, replicationMode } = this.args;
|
||||
let perm;
|
||||
if (replicationMode === 'dr') {
|
||||
// returns canEnablePrimaryDr or canEnableSecondaryDr
|
||||
perm = `canEnable${type}Dr`;
|
||||
}
|
||||
if (replicationMode === 'performance') {
|
||||
// returns canEnablePrimaryPerformance or canEnableSecondaryPerformance
|
||||
perm = `canEnable${type}Performance`;
|
||||
}
|
||||
// if there's a problem checking capabilities, default to true
|
||||
// since the backend can gate as a fallback
|
||||
return cluster[perm] ?? true;
|
||||
};
|
||||
}
|
||||
@@ -8,4 +8,24 @@ import { tracked } from '@glimmer/tracking';
|
||||
|
||||
export default class ReplicationIndexController extends ReplicationModeBaseController {
|
||||
@tracked modeSelection = 'dr';
|
||||
|
||||
getPerm(type) {
|
||||
if (this.modeSelection === 'dr') {
|
||||
// returns canEnablePrimaryDr or canEnableSecondaryDr
|
||||
return `canEnable${type}Dr`;
|
||||
}
|
||||
if (this.modeSelection === 'performance') {
|
||||
// returns canEnablePrimaryPerformance or canEnableSecondaryPerformance
|
||||
return `canEnable${type}Performance`;
|
||||
}
|
||||
}
|
||||
|
||||
// if there's a problem checking capabilities, default to true
|
||||
// since the backend will gate as a fallback
|
||||
get canEnablePrimary() {
|
||||
return this.model[this.getPerm('Primary')] ?? true;
|
||||
}
|
||||
get canEnableSecondary() {
|
||||
return this.model[this.getPerm('Secondary')] ?? true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ const Eng = Engine.extend({
|
||||
dependencies: {
|
||||
services: [
|
||||
'auth',
|
||||
'capabilities',
|
||||
'flash-messages',
|
||||
'namespace',
|
||||
'replication-mode',
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import { service } from '@ember/service';
|
||||
import { setProperties } from '@ember/object';
|
||||
import { hash } from 'rsvp';
|
||||
import Route from '@ember/routing/route';
|
||||
import ClusterRoute from 'vault/mixins/cluster-route';
|
||||
|
||||
@@ -14,6 +13,23 @@ export default Route.extend(ClusterRoute, {
|
||||
store: service(),
|
||||
auth: service(),
|
||||
router: service(),
|
||||
capabilities: service(),
|
||||
|
||||
async fetchCapabilities() {
|
||||
const enablePath = (type, cluster) => `sys/replication/${type}/${cluster}/enable`;
|
||||
const perms = await this.capabilities.fetchMultiplePaths([
|
||||
enablePath('dr', 'primary'),
|
||||
enablePath('dr', 'primary'),
|
||||
enablePath('performance', 'secondary'),
|
||||
enablePath('performance', 'secondary'),
|
||||
]);
|
||||
return {
|
||||
canEnablePrimaryDr: perms[enablePath('dr', 'primary')].canUpdate,
|
||||
canEnableSecondaryDr: perms[enablePath('dr', 'primary')].canUpdate,
|
||||
canEnablePrimaryPerformance: perms[enablePath('performance', 'secondary')].canUpdate,
|
||||
canEnableSecondaryPerformance: perms[enablePath('performance', 'secondary')].canUpdate,
|
||||
};
|
||||
},
|
||||
|
||||
beforeModel() {
|
||||
if (this.auth.activeCluster.replicationRedacted) {
|
||||
@@ -29,21 +45,21 @@ export default Route.extend(ClusterRoute, {
|
||||
return this.auth.activeCluster;
|
||||
},
|
||||
|
||||
afterModel(model) {
|
||||
return hash({
|
||||
canEnablePrimary: this.store
|
||||
.findRecord('capabilities', 'sys/replication/primary/enable')
|
||||
.then((c) => c.canUpdate),
|
||||
canEnableSecondary: this.store
|
||||
.findRecord('capabilities', 'sys/replication/secondary/enable')
|
||||
.then((c) => c.canUpdate),
|
||||
}).then(({ canEnablePrimary, canEnableSecondary }) => {
|
||||
setProperties(model, {
|
||||
canEnablePrimary,
|
||||
canEnableSecondary,
|
||||
});
|
||||
return model;
|
||||
async afterModel(model) {
|
||||
const {
|
||||
canEnablePrimaryDr,
|
||||
canEnableSecondaryDr,
|
||||
canEnablePrimaryPerformance,
|
||||
canEnableSecondaryPerformance,
|
||||
} = await this.fetchCapabilities();
|
||||
|
||||
setProperties(model, {
|
||||
canEnablePrimaryDr,
|
||||
canEnableSecondaryDr,
|
||||
canEnablePrimaryPerformance,
|
||||
canEnableSecondaryPerformance,
|
||||
});
|
||||
return model;
|
||||
},
|
||||
actions: {
|
||||
refresh() {
|
||||
|
||||
@@ -92,8 +92,8 @@
|
||||
</div>
|
||||
<EnableReplicationForm
|
||||
@replicationMode={{this.modeSelection}}
|
||||
@canEnablePrimary={{this.model.canEnablePrimary}}
|
||||
@canEnableSecondary={{this.model.canEnableSecondary}}
|
||||
@canEnablePrimary={{this.canEnablePrimary}}
|
||||
@canEnableSecondary={{this.canEnableSecondary}}
|
||||
@performanceReplicationDisabled={{this.model.performance.replicationDisabled}}
|
||||
@performanceMode={{if this.model.performance.replicationDisabled "disabled" this.model.performance.modeForUrl}}
|
||||
@onSuccess={{this.onEnableSuccess}}
|
||||
|
||||
@@ -13,7 +13,9 @@ const S = {
|
||||
title: 'h1',
|
||||
subtitle: 'h2',
|
||||
enableForm: '[data-test-replication-enable-form]',
|
||||
enableBtn: '[data-test-replication-enable]',
|
||||
summary: '[data-test-replication-summary]',
|
||||
notAllowed: '[data-test-not-allowed]',
|
||||
};
|
||||
module('Integration | Component | replication page/mode-index', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
@@ -43,6 +45,8 @@ module('Integration | Component | replication page/mode-index', function (hooks)
|
||||
|
||||
assert.dom(S.title).hasText('Enable Disaster Recovery Replication');
|
||||
assert.dom(S.enableForm).exists();
|
||||
assert.dom(S.notAllowed).doesNotExist();
|
||||
assert.dom(S.enableBtn).exists('Enable button shows by default if no permissions available');
|
||||
});
|
||||
test('it renders correctly when replication enabled', async function (assert) {
|
||||
this.replicationDisabled = false;
|
||||
@@ -51,6 +55,24 @@ module('Integration | Component | replication page/mode-index', function (hooks)
|
||||
assert.dom(S.enableForm).doesNotExist();
|
||||
assert.dom(S.summary).exists();
|
||||
});
|
||||
|
||||
test('it hides enable button if no permissions', async function (assert) {
|
||||
this.clusterModel.canEnablePrimaryDr = false;
|
||||
await this.renderComponent();
|
||||
|
||||
assert.dom(S.enableForm).exists();
|
||||
assert.dom(S.notAllowed).exists();
|
||||
assert.dom(S.enableBtn).doesNotExist();
|
||||
});
|
||||
|
||||
test('it shows enable button if has permissions', async function (assert) {
|
||||
this.clusterModel.canEnablePrimaryDr = true;
|
||||
await this.renderComponent();
|
||||
|
||||
assert.dom(S.enableForm).exists();
|
||||
assert.dom(S.notAllowed).doesNotExist();
|
||||
assert.dom(S.enableBtn).exists();
|
||||
});
|
||||
});
|
||||
|
||||
module('Performance mode', function (hooks) {
|
||||
@@ -62,6 +84,8 @@ module('Integration | Component | replication page/mode-index', function (hooks)
|
||||
|
||||
assert.dom(S.title).hasText('Enable Performance Replication');
|
||||
assert.dom(S.enableForm).exists();
|
||||
assert.dom(S.notAllowed).doesNotExist();
|
||||
assert.dom(S.enableBtn).exists('Enable button shows by default if no permissions available');
|
||||
});
|
||||
test('it renders correctly when replication enabled', async function (assert) {
|
||||
this.replicationDisabled = false;
|
||||
@@ -70,5 +94,23 @@ module('Integration | Component | replication page/mode-index', function (hooks)
|
||||
assert.dom(S.enableForm).doesNotExist();
|
||||
assert.dom(S.summary).exists();
|
||||
});
|
||||
|
||||
test('it hides enable button if no permissions', async function (assert) {
|
||||
this.clusterModel.canEnablePrimaryPerformance = false;
|
||||
await this.renderComponent();
|
||||
|
||||
assert.dom(S.enableForm).exists();
|
||||
assert.dom(S.notAllowed).exists();
|
||||
assert.dom(S.enableBtn).doesNotExist();
|
||||
});
|
||||
|
||||
test('it shows enable button if has permissions', async function (assert) {
|
||||
this.clusterModel.canEnablePrimaryPerformance = true;
|
||||
await this.renderComponent();
|
||||
|
||||
assert.dom(S.enableForm).exists();
|
||||
assert.dom(S.notAllowed).doesNotExist();
|
||||
assert.dom(S.enableBtn).exists();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user