Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI: Show error when seal fails #23921

Merged
merged 6 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/23921.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
ui: show error from API when seal fails
```
34 changes: 34 additions & 0 deletions ui/app/components/seal-action.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
~}}

<div class="box is-sideless is-fullwidth is-marginless">
{{#if this.error}}
<Hds::Alert @type="inline" @color="critical" class="has-bottom-margin-m" data-test-seal-error as |A|>
<A.Title>Error</A.Title>
<A.Description>
{{this.error}}
</A.Description>
</Hds::Alert>
{{/if}}
<p>
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.
</p>
</div>

<div class="field is-grouped box is-fullwidth is-bottomless">
<ConfirmAction
@buttonClasses="button is-primary"
@confirmTitle="Seal this cluster?"
@confirmMessage="You will not be able to read or write any data until the cluster is unsealed again."
@confirmButtonText="Seal"
@horizontalPosition="auto-left"
@onConfirmAction={{this.handleSeal}}
data-test-seal
>
Seal
</ConfirmAction>
</div>
22 changes: 22 additions & 0 deletions ui/app/components/seal-action.js
Original file line number Diff line number Diff line change
@@ -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.');
}
}
}
21 changes: 1 addition & 20 deletions ui/app/templates/vault/cluster/settings/seal.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,7 @@
</PageHeader>

{{#if this.model.seal.canUpdate}}
<div class="box is-sideless is-fullwidth is-marginless">
<p>
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.
</p>
</div>
<div class="field is-grouped box is-fullwidth is-bottomless">
<ConfirmAction
@buttonClasses="button is-primary"
@confirmTitle="Seal this cluster?"
@confirmMessage="You will not be able to read or write any data until the cluster is unsealed again."
@confirmButtonText="Seal"
@horizontalPosition="auto-left"
@onConfirmAction={{action "seal"}}
data-test-seal="true"
>
Seal
</ConfirmAction>
</div>
<SealAction @onSeal={{action "seal"}} />
{{else}}
<EmptyState @title="This token does not have sufficient capabilities to seal this vault" />
{{/if}}
45 changes: 45 additions & 0 deletions ui/tests/integration/components/seal-action-test.js
Original file line number Diff line number Diff line change
@@ -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`<SealAction @onSeal={{action this.handleSeal}} />`);

// 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`<SealAction @onSeal={{action this.handleSeal}} />`);

// 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');
});
});
Loading