mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-02 11:38:02 +00:00
HCP Link Status Updates (#17213)
* updates hcp link status message parsing and adds handling for connection errors * adds handling for missing status to link-status component
This commit is contained in:
@@ -7,23 +7,66 @@ import { inject as service } from '@ember/service';
|
|||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```js
|
* ```js
|
||||||
* <LinkStatus @status={{this.currentCluser.cluster.hcpLinkStatus}} />
|
* <LinkStatus @status={{this.currentCluster.cluster.hcpLinkStatus}} />
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @param {string} status - cluster.hcpLinkStatus value from currentCluster service
|
* @param {string} status - cluster.hcpLinkStatus value from currentCluster service -- returned from seal-status endpoint
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default class LinkStatus extends Component {
|
export default class LinkStatus extends Component {
|
||||||
@service store;
|
@service store;
|
||||||
@service version;
|
@service version;
|
||||||
|
|
||||||
get showBanner() {
|
get state() {
|
||||||
// enterprise only feature at this time but will expand to OSS in future release
|
if (!this.args.status) return null;
|
||||||
// there are plans to handle connection failure states -- only alert if connected until further states are returned
|
// connected state is returned with no further information
|
||||||
return this.version.isEnterprise && this.args.status === 'connected';
|
if (this.args.status === 'connected') return this.args.status;
|
||||||
|
// disconnected and connecting states are returned with a timestamp and error
|
||||||
|
// state is always the first word of the string
|
||||||
|
return this.args.status.split(' ', 1).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
get bannerClass() {
|
get timestamp() {
|
||||||
return this.args.status === 'connected' ? 'connected' : 'warning';
|
try {
|
||||||
|
return this.state !== 'connected' ? this.args.status.split('since')[1].split('m=')[0].trim() : null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get message() {
|
||||||
|
if (this.args.status) {
|
||||||
|
const error = this.args.status.split('error:')[1];
|
||||||
|
const time = `[${this.timestamp}]`;
|
||||||
|
if (this.state === 'disconnected') {
|
||||||
|
// if generally disconnected hide the banner
|
||||||
|
return !error || error.includes('UNKNOWN')
|
||||||
|
? null
|
||||||
|
: `Vault has been disconnected from the Hashicorp Cloud Platform since ${time}. Error: ${error}`;
|
||||||
|
} else if (this.state === 'connecting') {
|
||||||
|
if (error.includes('connection refused')) {
|
||||||
|
return `Vault has been trying to connect to the Hashicorp Cloud Platform since ${time}, but the Scada provider is down. Vault will try again soon.`;
|
||||||
|
} else if (error.includes('principal does not have permission to register as provider')) {
|
||||||
|
return `Vault tried connecting to the Hashicorp Cloud Platform, but the Resource ID is invalid. Check your resource ID. ${time}`;
|
||||||
|
} else if (error.includes('cannot fetch token: 401 Unauthorized')) {
|
||||||
|
return `Vault tried connecting to the Hashicorp Cloud Platform, but the authorization information is wrong. Update it and try again. ${time}`;
|
||||||
|
} else {
|
||||||
|
// catch all for any unknown errors
|
||||||
|
return `Vault has been trying to connect to the Hashicorp Cloud Platform since ${time}. Vault will try again soon. Error: ${error}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get showStatus() {
|
||||||
|
// enterprise only feature at this time but will expand to OSS in future release
|
||||||
|
if (!this.version.isEnterprise || !this.args.status) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.state === 'disconnected' && !this.message) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,14 @@
|
|||||||
{{#if this.showBanner}}
|
{{#if this.showStatus}}
|
||||||
<div class="navbar-status {{this.bannerClass}}">
|
<div class="navbar-status {{if (eq this.state 'connected') 'connected' 'warning'}}">
|
||||||
<Icon @name="info" />
|
<Icon @name="info" />
|
||||||
<p data-test-link-status>
|
<p data-test-link-status>
|
||||||
{{#if (eq @status "connected")}}
|
{{#if (eq this.state "connected")}}
|
||||||
This self-managed Vault is linked to the
|
This self-managed Vault is linked to the
|
||||||
<a href="https://portal.cloud.hashicorp.com/sign-in" target="_blank" rel="noopener noreferrer">
|
<a href="https://portal.cloud.hashicorp.com/sign-in" target="_blank" rel="noopener noreferrer">
|
||||||
HashiCorp Cloud Platform.
|
HashiCorp Cloud Platform.
|
||||||
</a>
|
</a>
|
||||||
{{else if (eq @status "401")}}
|
{{else}}
|
||||||
{{! roughing in 401 and 500 connection statuses -- update strings once they are exposed by the API }}
|
{{this.message}}
|
||||||
Vault can’t connect to HashiCorp Cloud Portal. Check your config file to ensure that credentials are correct.
|
|
||||||
{{else if (eq @status "500")}}
|
|
||||||
Vault’s connection to HashiCorp Cloud Portal is down. Check
|
|
||||||
<a href="https://status.hashicorp.com" target="_blank" rel="noopener noreferrer">
|
|
||||||
the status page
|
|
||||||
</a>
|
|
||||||
for details.
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
export const statuses = [
|
||||||
|
'connected',
|
||||||
|
'disconnected since 2022-09-13 14:45:40.666697 -0700 PDT m=+21.065498483; error: UNKNOWN',
|
||||||
|
'disconnected since 2022-09-13 14:45:40.666697 -0700 PDT m=+21.065498483; error: some other error other than unknown',
|
||||||
|
'connecting since 2022-09-13 14:45:40.666697 -0700 PDT m=+21.065498483; error: dial tcp [::1]:28083: connect: connection refused',
|
||||||
|
'connecting since 2022-09-13 14:45:40.666697 -0700 PDT m=+21.065498483; error: principal does not have permission to register as provider: rpc error: code = PermissionDenied desc =',
|
||||||
|
'connecting since 2022-09-13 14:45:40.666697 -0700 PDT m=+21.065498483; error: failed to get access token: oauth2: cannot fetch token: 401 Unauthorized. Response: {"error":"access_denied","error_description":"Unauthorized"}',
|
||||||
|
'connecting since 2022-09-13 14:45:40.666697 -0700 PDT m=+21.065498483; error: connection error we are unaware of',
|
||||||
|
// the following were identified as dev only errors -- leaving in case they need to be handled
|
||||||
|
// 'connecting since 2022-09-13 14:45:40.666697 -0700 PDT m=+21.065498483; error: failed to get access token: Post "https://aauth.idp.hcp.dev/oauth2/token": x509: “*.hcp.dev” certificate name does not match input',
|
||||||
|
// 'connecting since 2022-09-13 14:45:40.666697 -0700 PDT m=+21.065498483; error: UNKNOWN',
|
||||||
|
];
|
||||||
|
let index = null;
|
||||||
|
|
||||||
export default function (server) {
|
export default function (server) {
|
||||||
const handleResponse = (req, props) => {
|
const handleResponse = (req, props) => {
|
||||||
const xhr = req.passthrough();
|
const xhr = req.passthrough();
|
||||||
@@ -16,10 +30,13 @@ export default function (server) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
server.get('sys/seal-status', (schema, req) => {
|
server.get('sys/seal-status', (schema, req) => {
|
||||||
// randomly return one of the various states to test polling
|
// return next status from statuses array
|
||||||
// 401 and 500 are stubs -- update with actual API values once determined
|
if (index === null || index === statuses.length - 1) {
|
||||||
const hcp_link_status = ['connected', 'disconnected', '401', '500'][Math.floor(Math.random() * 2)];
|
index = 0;
|
||||||
return handleResponse(req, { hcp_link_status });
|
} else {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return handleResponse(req, { hcp_link_status: statuses[index] });
|
||||||
});
|
});
|
||||||
// enterprise only feature initially
|
// enterprise only feature initially
|
||||||
server.get('sys/health', (schema, req) => handleResponse(req, { version: '1.12.0-dev1+ent' }));
|
server.get('sys/health', (schema, req) => handleResponse(req, { version: '1.12.0-dev1+ent' }));
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ import { setupRenderingTest } from 'ember-qunit';
|
|||||||
import { render } from '@ember/test-helpers';
|
import { render } from '@ember/test-helpers';
|
||||||
import { hbs } from 'ember-cli-htmlbars';
|
import { hbs } from 'ember-cli-htmlbars';
|
||||||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||||
|
import { statuses } from '../../../mirage/handlers/hcp-link';
|
||||||
|
|
||||||
|
const timestamp = '[2022-09-13 14:45:40.666697 -0700 PDT]';
|
||||||
|
|
||||||
module('Integration | Component | link-status', function (hooks) {
|
module('Integration | Component | link-status', function (hooks) {
|
||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
@@ -11,16 +14,11 @@ module('Integration | Component | link-status', function (hooks) {
|
|||||||
// this can be removed once feature is released for OSS
|
// this can be removed once feature is released for OSS
|
||||||
hooks.beforeEach(function () {
|
hooks.beforeEach(function () {
|
||||||
this.owner.lookup('service:version').set('isEnterprise', true);
|
this.owner.lookup('service:version').set('isEnterprise', true);
|
||||||
});
|
this.statuses = statuses;
|
||||||
|
|
||||||
test('it does not render disconnected status', async function (assert) {
|
|
||||||
await render(hbs`<LinkStatus @status="disconnected" />`);
|
|
||||||
|
|
||||||
assert.dom('.navbar-status').doesNotExist('Banner is hidden for disconnected state');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it renders connected status', async function (assert) {
|
test('it renders connected status', async function (assert) {
|
||||||
await render(hbs`<LinkStatus @status="connected" />`);
|
await render(hbs`<LinkStatus @status={{get this.statuses 0}} />`);
|
||||||
|
|
||||||
assert.dom('.navbar-status').hasClass('connected', 'Correct class renders for connected state');
|
assert.dom('.navbar-status').hasClass('connected', 'Correct class renders for connected state');
|
||||||
assert
|
assert
|
||||||
@@ -33,4 +31,72 @@ module('Integration | Component | link-status', function (hooks) {
|
|||||||
.dom('[data-test-link-status] a')
|
.dom('[data-test-link-status] a')
|
||||||
.hasAttribute('href', 'https://portal.cloud.hashicorp.com/sign-in', 'HCP sign in link renders');
|
.hasAttribute('href', 'https://portal.cloud.hashicorp.com/sign-in', 'HCP sign in link renders');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('it does not render banner for disconnected state with unknown error', async function (assert) {
|
||||||
|
await render(hbs`<LinkStatus @status={{get this.statuses 1}} />`);
|
||||||
|
|
||||||
|
assert.dom('.navbar-status').doesNotExist('Banner is hidden for disconnected state');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it should render for disconnected error state', async function (assert) {
|
||||||
|
await render(hbs`<LinkStatus @status={{get this.statuses 2}} />`);
|
||||||
|
|
||||||
|
assert.dom('.navbar-status').hasClass('warning', 'Correct class renders for disconnected error state');
|
||||||
|
assert
|
||||||
|
.dom('[data-test-link-status]')
|
||||||
|
.hasText(
|
||||||
|
`Vault has been disconnected from the Hashicorp Cloud Platform since ${timestamp}. Error: some other error other than unknown`,
|
||||||
|
'Copy renders for disconnected error state'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it should render for connection refused error state', async function (assert) {
|
||||||
|
await render(hbs`<LinkStatus @status={{get this.statuses 3}} />`);
|
||||||
|
|
||||||
|
assert
|
||||||
|
.dom('.navbar-status')
|
||||||
|
.hasClass('warning', 'Correct class renders for connection refused error state');
|
||||||
|
assert
|
||||||
|
.dom('[data-test-link-status]')
|
||||||
|
.hasText(
|
||||||
|
`Vault has been trying to connect to the Hashicorp Cloud Platform since ${timestamp}, but the Scada provider is down. Vault will try again soon.`,
|
||||||
|
'Copy renders for connection refused error state'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it should render for resource id error state', async function (assert) {
|
||||||
|
await render(hbs`<LinkStatus @status={{get this.statuses 4}} />`);
|
||||||
|
|
||||||
|
assert.dom('.navbar-status').hasClass('warning', 'Correct class renders for resource id error state');
|
||||||
|
assert
|
||||||
|
.dom('[data-test-link-status]')
|
||||||
|
.hasText(
|
||||||
|
`Vault tried connecting to the Hashicorp Cloud Platform, but the Resource ID is invalid. Check your resource ID. ${timestamp}`,
|
||||||
|
'Copy renders for resource id error state'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it should render for unauthorized error state', async function (assert) {
|
||||||
|
await render(hbs`<LinkStatus @status={{get this.statuses 5}} />`);
|
||||||
|
|
||||||
|
assert.dom('.navbar-status').hasClass('warning', 'Correct class renders for unauthorized error state');
|
||||||
|
assert
|
||||||
|
.dom('[data-test-link-status]')
|
||||||
|
.hasText(
|
||||||
|
`Vault tried connecting to the Hashicorp Cloud Platform, but the authorization information is wrong. Update it and try again. ${timestamp}`,
|
||||||
|
'Copy renders for unauthorized error state'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it should render generic message for unknown error state', async function (assert) {
|
||||||
|
await render(hbs`<LinkStatus @status={{get this.statuses 6}} />`);
|
||||||
|
|
||||||
|
assert.dom('.navbar-status').hasClass('warning', 'Correct class renders for unknown error state');
|
||||||
|
assert
|
||||||
|
.dom('[data-test-link-status]')
|
||||||
|
.hasText(
|
||||||
|
`Vault has been trying to connect to the Hashicorp Cloud Platform since ${timestamp}. Vault will try again soon. Error: connection error we are unaware of`,
|
||||||
|
'Copy renders for unknown error state'
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user