From ba5f85654223f9d58af823af6c74d70927ab5168 Mon Sep 17 00:00:00 2001
From: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com>
Date: Wed, 8 Nov 2023 10:01:53 -0600
Subject: [PATCH] UI: Show error when seal fails (#23921)
* Show error when seal fails
* cleanup, add headers
* add changelog
* Fix test
---
changelog/23921.txt | 3 ++
ui/app/components/seal-action.hbs | 34 ++++++++++++++
ui/app/components/seal-action.js | 22 +++++++++
.../templates/vault/cluster/settings/seal.hbs | 21 +--------
.../components/seal-action-test.js | 45 +++++++++++++++++++
5 files changed, 105 insertions(+), 20 deletions(-)
create mode 100644 changelog/23921.txt
create mode 100644 ui/app/components/seal-action.hbs
create mode 100644 ui/app/components/seal-action.js
create mode 100644 ui/tests/integration/components/seal-action-test.js
diff --git a/changelog/23921.txt b/changelog/23921.txt
new file mode 100644
index 0000000000..cd03142227
--- /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 0000000000..ae966e676f
--- /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 0000000000..4a2fd873ae
--- /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 972f9a72cf..0b6d738e3f 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 0000000000..19bcf38204
--- /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');
+ });
+});