diff --git a/changelog/23921.txt b/changelog/23921.txt new file mode 100644 index 000000000000..cd03142227d0 --- /dev/null +++ b/changelog/23921.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: show error from API when seal fails +``` diff --git a/ui/app/components/seal-action.hbs b/ui/app/components/seal-action.hbs new file mode 100644 index 000000000000..ae966e676f6f --- /dev/null +++ b/ui/app/components/seal-action.hbs @@ -0,0 +1,34 @@ +{{! + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: BUSL-1.1 +~}} + +
+ {{#if this.error}} + + Error + + {{this.error}} + + + {{/if}} +

+ Sealing a vault tells the Vault server to stop responding to any access operations until it is unsealed again. A sealed + vault throws away its root key to unlock the data, so it physically is blocked from responding to operations again until + the Vault is unsealed again with the "unseal" command or via the API. +

+
+ +
+ + Seal + +
\ No newline at end of file diff --git a/ui/app/components/seal-action.js b/ui/app/components/seal-action.js new file mode 100644 index 000000000000..4a2fd873ae47 --- /dev/null +++ b/ui/app/components/seal-action.js @@ -0,0 +1,22 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import { action } from '@ember/object'; +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import errorMessage from 'vault/utils/error-message'; + +export default class SealActionComponent extends Component { + @tracked error; + + @action + async handleSeal() { + try { + await this.args.onSeal(); + } catch (e) { + this.error = errorMessage(e, 'Seal attempt failed. Check Vault logs for details.'); + } + } +} diff --git a/ui/app/templates/vault/cluster/settings/seal.hbs b/ui/app/templates/vault/cluster/settings/seal.hbs index 972f9a72cf85..0b6d738e3f08 100644 --- a/ui/app/templates/vault/cluster/settings/seal.hbs +++ b/ui/app/templates/vault/cluster/settings/seal.hbs @@ -12,26 +12,7 @@ {{#if this.model.seal.canUpdate}} -
-

- Sealing a vault tells the Vault server to stop responding to any access operations until it is unsealed again. A sealed - vault throws away its root key to unlock the data, so it physically is blocked from responding to operations again - until the Vault is unsealed again with the "unseal" command or via the API. -

-
-
- - Seal - -
+ {{else}} {{/if}} \ No newline at end of file diff --git a/ui/tests/integration/components/seal-action-test.js b/ui/tests/integration/components/seal-action-test.js new file mode 100644 index 000000000000..19bcf382048c --- /dev/null +++ b/ui/tests/integration/components/seal-action-test.js @@ -0,0 +1,45 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'vault/tests/helpers'; +import { click, render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; +import sinon from 'sinon'; + +const SEAL_WHEN_STANDBY_MSG = 'vault cannot seal when in standby mode; please restart instead'; + +module('Integration | Component | seal-action', function (hooks) { + setupRenderingTest(hooks); + + hooks.beforeEach(function () { + this.sealSuccess = sinon.spy(() => new Promise((resolve) => resolve({}))); + this.sealError = sinon.stub().throws({ message: SEAL_WHEN_STANDBY_MSG }); + }); + + test('it handles success', async function (assert) { + this.set('handleSeal', this.sealSuccess); + await render(hbs``); + + // attempt seal + await click('[data-test-seal] button'); + await click('[data-test-confirm-button]'); + + assert.ok(this.sealSuccess.calledOnce, 'called onSeal action'); + assert.dom('[data-test-seal-error]').doesNotExist('Does not show error when successful'); + }); + + test('it handles error', async function (assert) { + this.set('handleSeal', this.sealError); + await render(hbs``); + + // attempt seal + await click('[data-test-seal] button'); + await click('[data-test-confirm-button]'); + + assert.ok(this.sealError.calledOnce, 'called onSeal action'); + assert.dom('[data-test-seal-error]').includesText(SEAL_WHEN_STANDBY_MSG, 'Shows error returned from API'); + }); +});