UI: Show error when seal fails (#23921)

* Show error when seal fails

* cleanup, add headers

* add changelog

* Fix test
This commit is contained in:
Chelsea Shaw
2023-11-08 10:01:53 -06:00
committed by GitHub
parent 1a6c20bfda
commit ba5f856542
5 changed files with 105 additions and 20 deletions

3
changelog/23921.txt Normal file
View File

@@ -0,0 +1,3 @@
```release-note:bug
ui: show error from API when seal fails
```

View File

@@ -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>

View File

@@ -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.');
}
}
}

View File

@@ -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}}

View File

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