diff --git a/changelog/28371.txt b/changelog/28371.txt new file mode 100644 index 000000000000..c719c4be56c0 --- /dev/null +++ b/changelog/28371.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fix UI improperly checking capabilities for enabling performance and dr replication +``` diff --git a/ui/app/app.js b/ui/app/app.js index e5484bf392e1..9409b0cd916f 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -27,6 +27,7 @@ export default class App extends Application { dependencies: { services: [ 'auth', + 'capabilities', 'flash-messages', 'namespace', 'replication-mode', diff --git a/ui/lib/replication/addon/components/enable-replication-form.js b/ui/lib/replication/addon/components/enable-replication-form.js index f27f13709367..bd1145e0d417 100644 --- a/ui/lib/replication/addon/components/enable-replication-form.js +++ b/ui/lib/replication/addon/components/enable-replication-form.js @@ -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 * - * @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; diff --git a/ui/lib/replication/addon/components/page/mode-index.hbs b/ui/lib/replication/addon/components/page/mode-index.hbs index bc45bcc4c37f..d35e024b0f28 100644 --- a/ui/lib/replication/addon/components/page/mode-index.hbs +++ b/ui/lib/replication/addon/components/page/mode-index.hbs @@ -45,8 +45,8 @@ + * + * @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; + }; +} diff --git a/ui/lib/replication/addon/controllers/index.js b/ui/lib/replication/addon/controllers/index.js index cab8fc4eb9ac..e5be14b7a7fc 100644 --- a/ui/lib/replication/addon/controllers/index.js +++ b/ui/lib/replication/addon/controllers/index.js @@ -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; + } } diff --git a/ui/lib/replication/addon/engine.js b/ui/lib/replication/addon/engine.js index f3ddb560ea47..b7757a16b6d4 100644 --- a/ui/lib/replication/addon/engine.js +++ b/ui/lib/replication/addon/engine.js @@ -16,6 +16,7 @@ const Eng = Engine.extend({ dependencies: { services: [ 'auth', + 'capabilities', 'flash-messages', 'namespace', 'replication-mode', diff --git a/ui/lib/replication/addon/routes/application.js b/ui/lib/replication/addon/routes/application.js index 72bdd69741fb..e54b870f9793 100644 --- a/ui/lib/replication/addon/routes/application.js +++ b/ui/lib/replication/addon/routes/application.js @@ -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() { diff --git a/ui/lib/replication/addon/templates/index.hbs b/ui/lib/replication/addon/templates/index.hbs index e2dc0b70e5db..25f497dc9487 100644 --- a/ui/lib/replication/addon/templates/index.hbs +++ b/ui/lib/replication/addon/templates/index.hbs @@ -92,8 +92,8 @@