Skip to content

Commit

Permalink
UI/license banners (#11759)
Browse files Browse the repository at this point in the history
  • Loading branch information
hashishaw authored Jun 3, 2021
1 parent d6a96bd commit 17bac23
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 2 deletions.
3 changes: 3 additions & 0 deletions changelog/11759.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
ui: show site-wide banners for license warnings if applicable
```
27 changes: 27 additions & 0 deletions ui/app/components/license-banners.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* @module LicenseBanners
* LicenseBanners components are used to display Vault-specific license expiry messages
*
* @example
* ```js
* <LicenseBanners @expiry={expiryDate} />
* ```
* @param {string} expiry - RFC3339 date timestamp
*/

import Component from '@glimmer/component';
import isAfter from 'date-fns/isAfter';
import differenceInDays from 'date-fns/differenceInDays';

export default class LicenseBanners extends Component {
get licenseExpired() {
if (!this.args.expiry) return false;
return isAfter(new Date(), new Date(this.args.expiry));
}

get licenseExpiringInDays() {
// Anything more than 30 does not render a warning
if (!this.args.expiry) return 99;
return differenceInDays(new Date(this.args.expiry), new Date());
}
}
5 changes: 5 additions & 0 deletions ui/app/models/cluster.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ export default Model.extend({
status: attr('string'),
standby: attr('boolean'),
type: attr('string'),
license: attr('object'),

/* Licensing concerns */
licenseExpiry: alias('license.expiry'),
licenseState: alias('license.state'),

needsInit: computed('nodes', '[email protected]', function() {
// needs init if no nodes are initialized
Expand Down
6 changes: 6 additions & 0 deletions ui/app/styles/components/license-banners.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.license-banner-wrapper {
width: 100%;
max-width: 1344px;
margin: $spacing-l auto 0;
padding: 0 1.5rem;
}
1 change: 1 addition & 0 deletions ui/app/styles/core.scss
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
@import './components/input-hint';
@import './components/kmip-role-edit';
@import './components/known-secondaries-card.scss';
@import './components/license-banners';
@import './components/linked-block';
@import './components/list-item-row';
@import './components/list-pagination';
Expand Down
3 changes: 3 additions & 0 deletions ui/app/styles/core/helpers.scss
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@
.has-top-margin-s {
margin-top: $spacing-s;
}
.has-top-margin-l {
margin-top: $spacing-l;
}
.has-top-margin-xl {
margin-top: $spacing-xl;
}
Expand Down
4 changes: 4 additions & 0 deletions ui/app/styles/core/message.scss
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,7 @@
.has-text-highlight {
color: $yellow-500;
}

.message.message-marginless {
margin: 0;
}
23 changes: 23 additions & 0 deletions ui/app/templates/components/license-banners.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{{#if this.licenseExpired}}
<div class="license-banner-wrapper" data-test-license-banner data-test-license-banner-expired>
<AlertBanner
@type="danger"
@title="License expired"
@message="Your Vault license expired on {{date-format @expiry "MMM d, yyyy"}}. Add a new license to your configuration and restart Vault."
@marginless={{true}}
>
<a href="https://learn.hashicorp.com/tutorials/nomad/hashicorp-enterprise-license" target="_blank" rel="noreferrer noopener">Read documentation</a>
</AlertBanner>
</div>
{{else if (lte this.licenseExpiringInDays 30)}}
<div class="license-banner-wrapper" data-test-license-banner data-test-license-banner-warning>
<AlertBanner
@type="warning"
@title="Vault license expiring"
@message="Your Vault license will expire in {{this.licenseExpiringInDays}} days at {{date-format @expiry "hh:mm:ss a"}} on {{date-format @expiry "MMM d, yyyy"}}. Add a new license to your configuration."
@marginless={{true}}
>
<a href="https://learn.hashicorp.com/tutorials/nomad/hashicorp-enterprise-license" target="_blank" rel="noreferrer noopener">Read documentation</a>
</AlertBanner>
</div>
{{/if}}
1 change: 1 addition & 0 deletions ui/app/templates/vault/cluster.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
</Nav.items>
</NavHeader>
{{/if}}
<LicenseBanners @expiry={{activeCluster.licenseExpiry}} />
<div class="global-flash">
{{#each flashMessages.queue as |flash|}}
{{#flash-message data-test-flash-message=true flash=flash as |customComponent flash close|}}
Expand Down
6 changes: 4 additions & 2 deletions ui/lib/core/addon/components/alert-banner.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ export default Component.extend({
secondIconType: null,
progressBar: null,
yieldWithoutColumn: false,
marginless: false,
classNameBindings: ['containerClass'],

containerClass: computed('type', function() {
return 'message ' + messageTypes([this.type]).class;
containerClass: computed('type', 'marginless', function() {
const base = this.marginless ? 'message message-marginless ' : 'message ';
return base + messageTypes([this.type]).class;
}),

alertType: computed('type', function() {
Expand Down
20 changes: 20 additions & 0 deletions ui/mirage/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,25 @@ export default function() {
};
});

this.get('/sys/health', function() {
return {
initialized: true,
sealed: false,
standby: false,
license: {
expiry: '2021-05-12T23:20:50.52Z',
state: 'stored',
},
performance_standby: false,
replication_performance_mode: 'disabled',
replication_dr_mode: 'disabled',
server_time_utc: 1622562585,
version: '1.9.0+ent',
cluster_name: 'vault-cluster-e779cd7c',
cluster_id: '5f20f5ab-acea-0481-787e-71ec2ff5a60b',
last_wal: 121,
};
});

this.passthrough();
}
87 changes: 87 additions & 0 deletions ui/tests/acceptance/enterprise-license-banner-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { module, test } from 'qunit';
import { visit } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import Pretender from 'pretender';
import formatRFC3339 from 'date-fns/formatRFC3339';
import { addDays, subDays } from 'date-fns';

const generateHealthResponse = state => {
let expiry;
switch (state) {
case 'expired':
expiry = subDays(new Date(), 2);
break;
case 'expiring':
expiry = addDays(new Date(), 10);
break;
default:
expiry = addDays(new Date(), 33);
break;
}
return {
initialized: true,
sealed: false,
standby: false,
license: {
expiry: formatRFC3339(expiry),
state: 'stored',
},
performance_standby: false,
replication_performance_mode: 'disabled',
replication_dr_mode: 'disabled',
server_time_utc: 1622562585,
version: '1.9.0+ent',
cluster_name: 'vault-cluster-e779cd7c',
cluster_id: '5f20f5ab-acea-0481-787e-71ec2ff5a60b',
last_wal: 121,
};
};

module('Acceptance | Enterprise | License banner warnings', function(hooks) {
setupApplicationTest(hooks);

test('it shows no license banner if license expires in > 30 days', async function(assert) {
const healthResp = generateHealthResponse();
this.server = new Pretender(function() {
this.get('/v1/sys/health', response => {
return [response, { 'Content-Type': 'application/json' }, JSON.stringify(healthResp)];
});
this.get('/v1/sys/internal/ui/feature-flags', this.passthrough);
// this.get('/v1/sys/health', this.passthrough);
this.get('/v1/sys/seal-status', this.passthrough);
this.get('/v1/sys/license/features', this.passthrough);
});
await visit('/vault/auth');
assert.dom('[data-test-license-banner]').doesNotExist('license banner does not show');
this.server.shutdown();
});
test('it shows license banner warning if license expires within 30 days', async function(assert) {
const healthResp = generateHealthResponse('expiring');
this.server = new Pretender(function() {
this.get('/v1/sys/health', response => {
return [response, { 'Content-Type': 'application/json' }, JSON.stringify(healthResp)];
});
this.get('/v1/sys/internal/ui/feature-flags', this.passthrough);
this.get('/v1/sys/seal-status', this.passthrough);
this.get('/v1/sys/license/features', this.passthrough);
});
await visit('/vault/auth');
assert.dom('[data-test-license-banner-warning]').exists('license warning shows');
this.server.shutdown();
});

test('it shows license banner alert if license has already expired', async function(assert) {
const healthResp = generateHealthResponse('expired');
this.server = new Pretender(function() {
this.get('/v1/sys/health', response => {
return [response, { 'Content-Type': 'application/json' }, JSON.stringify(healthResp)];
});
this.get('/v1/sys/internal/ui/feature-flags', this.passthrough);
this.get('/v1/sys/seal-status', this.passthrough);
this.get('/v1/sys/license/features', this.passthrough);
});
await visit('/vault/auth');
assert.dom('[data-test-license-banner-expired]').exists('expired license message shows');
this.server.shutdown();
});
});
39 changes: 39 additions & 0 deletions ui/tests/integration/components/license-banners-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import subDays from 'date-fns/subDays';
import addDays from 'date-fns/addDays';
import formatRFC3339 from 'date-fns/formatRFC3339';

module('Integration | Component | license-banners', function(hooks) {
setupRenderingTest(hooks);

test('it does not render if no expiry', async function(assert) {
await render(hbs`<LicenseBanners />`);
assert.dom('[data-test-license-banner]').doesNotExist('License banner does not render');
});

test('it renders an error if expiry is before now', async function(assert) {
const yesterday = subDays(new Date(), 1);
this.set('expiry', formatRFC3339(yesterday));
await render(hbs`<LicenseBanners @expiry={{expiry}} />`);
assert.dom('[data-test-license-banner-expired]').exists('Expired license banner renders');
assert.dom('.message-title').hasText('License expired', 'Shows correct title on alert');
});

test('it renders a warning if expiry is within 30 days', async function(assert) {
const nextMonth = addDays(new Date(), 30);
this.set('expiry', formatRFC3339(nextMonth));
await render(hbs`<LicenseBanners @expiry={{expiry}} />`);
assert.dom('[data-test-license-banner-warning]').exists('Warning license banner renders');
assert.dom('.message-title').hasText('Vault license expiring', 'Shows correct title on alert');
});

test('it does not render a banner if expiry is outside 30 days', async function(assert) {
const outside30 = addDays(new Date(), 32);
this.set('expiry', formatRFC3339(outside30));
await render(hbs`<LicenseBanners @expiry={{expiry}} />`);
assert.dom('[data-test-license-banner]').doesNotExist('License banner does not render');
});
});

0 comments on commit 17bac23

Please sign in to comment.