From ed7eb9e874ef42239fe9b688a499f71469360ef2 Mon Sep 17 00:00:00 2001 From: hashishaw Date: Wed, 2 Jun 2021 12:06:21 -0500 Subject: [PATCH 01/12] Add license banners component with tests --- ui/app/components/license-banners.js | 28 +++++++++++++ .../templates/components/license-banners.hbs | 21 ++++++++++ .../components/license-banners-test.js | 39 +++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 ui/app/components/license-banners.js create mode 100644 ui/app/templates/components/license-banners.hbs create mode 100644 ui/tests/integration/components/license-banners-test.js diff --git a/ui/app/components/license-banners.js b/ui/app/components/license-banners.js new file mode 100644 index 000000000000..ac1273ab7e04 --- /dev/null +++ b/ui/app/components/license-banners.js @@ -0,0 +1,28 @@ +/** + * @module LicenseBanners + * LicenseBanners components are used to display Vault-specific license expiry messages + * + * @example + * ```js + * + * ``` + * @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; + const now = new Date(); + return isAfter(now, new Date(this.args.expiry)); + } + + get licenseExpiring() { + if (!this.args.expiry) return -1; + const now = new Date(); + return differenceInDays(new Date(this.args.expiry), now); + } +} diff --git a/ui/app/templates/components/license-banners.hbs b/ui/app/templates/components/license-banners.hbs new file mode 100644 index 000000000000..e6b1e3d17c18 --- /dev/null +++ b/ui/app/templates/components/license-banners.hbs @@ -0,0 +1,21 @@ +{{#if this.licenseExpired}} + +{{else if (lte this.licenseExpiringInDays 30)}} + +{{/if}} diff --git a/ui/tests/integration/components/license-banners-test.js b/ui/tests/integration/components/license-banners-test.js new file mode 100644 index 000000000000..707c7a1dc927 --- /dev/null +++ b/ui/tests/integration/components/license-banners-test.js @@ -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``); + 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``); + 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``); + 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``); + assert.dom('[data-test-license-banner]').doesNotExist('License banner does not render'); + }); +}); From 36e2840597bffdb9f5cb045945e7f5b7b5e7ca41 Mon Sep 17 00:00:00 2001 From: hashishaw Date: Wed, 2 Jun 2021 12:07:37 -0500 Subject: [PATCH 02/12] Fix license banner attribute name --- ui/app/components/license-banners.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/components/license-banners.js b/ui/app/components/license-banners.js index ac1273ab7e04..7133987d3599 100644 --- a/ui/app/components/license-banners.js +++ b/ui/app/components/license-banners.js @@ -20,7 +20,7 @@ export default class LicenseBanners extends Component { return isAfter(now, new Date(this.args.expiry)); } - get licenseExpiring() { + get licenseExpiringInDays() { if (!this.args.expiry) return -1; const now = new Date(); return differenceInDays(new Date(this.args.expiry), now); From 33ab8d98ced19403c5120323bcc88a43d83883d3 Mon Sep 17 00:00:00 2001 From: hashishaw Date: Wed, 2 Jun 2021 12:08:29 -0500 Subject: [PATCH 03/12] Add license to cluster model --- ui/app/models/cluster.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ui/app/models/cluster.js b/ui/app/models/cluster.js index 39b692a81fd1..c54a94c198f9 100644 --- a/ui/app/models/cluster.js +++ b/ui/app/models/cluster.js @@ -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', 'nodes.@each.initialized', function() { // needs init if no nodes are initialized From 0ef668fed278f94c48ce8805b9d9b2d23b26a396 Mon Sep 17 00:00:00 2001 From: hashishaw Date: Wed, 2 Jun 2021 12:08:50 -0500 Subject: [PATCH 04/12] Add LicenseBanner to cluster template --- ui/app/templates/vault/cluster.hbs | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/app/templates/vault/cluster.hbs b/ui/app/templates/vault/cluster.hbs index eca0ca96c19e..6202867159b9 100644 --- a/ui/app/templates/vault/cluster.hbs +++ b/ui/app/templates/vault/cluster.hbs @@ -101,6 +101,7 @@ {{/if}} +
{{#each flashMessages.queue as |flash|}} {{#flash-message data-test-flash-message=true flash=flash as |customComponent flash close|}} From c4b4b0fcf6b6a781723ee3588a5a95d72fcf6ee5 Mon Sep 17 00:00:00 2001 From: hashishaw Date: Wed, 2 Jun 2021 14:29:40 -0500 Subject: [PATCH 05/12] Clean up license banner logic --- ui/app/components/license-banners.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ui/app/components/license-banners.js b/ui/app/components/license-banners.js index 7133987d3599..91f4d2b15044 100644 --- a/ui/app/components/license-banners.js +++ b/ui/app/components/license-banners.js @@ -16,13 +16,12 @@ import differenceInDays from 'date-fns/differenceInDays'; export default class LicenseBanners extends Component { get licenseExpired() { if (!this.args.expiry) return false; - const now = new Date(); - return isAfter(now, new Date(this.args.expiry)); + return isAfter(new Date(), new Date(this.args.expiry)); } get licenseExpiringInDays() { - if (!this.args.expiry) return -1; - const now = new Date(); - return differenceInDays(new Date(this.args.expiry), now); + // Anything more than 30 does not render a warning + if (!this.args.expiry) return 99; + return differenceInDays(new Date(this.args.expiry), new Date()); } } From 6d1b69e0a3fd61670640ea0851cd33c9c850e8b4 Mon Sep 17 00:00:00 2001 From: hashishaw Date: Wed, 2 Jun 2021 15:50:22 -0500 Subject: [PATCH 06/12] Add marginless option to alert-banner component --- ui/app/styles/core/message.scss | 4 ++++ ui/lib/core/addon/components/alert-banner.js | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ui/app/styles/core/message.scss b/ui/app/styles/core/message.scss index 72760fac0ed7..c6348b2874b3 100644 --- a/ui/app/styles/core/message.scss +++ b/ui/app/styles/core/message.scss @@ -132,3 +132,7 @@ .has-text-highlight { color: $yellow-500; } + +.message.message-marginless { + margin: 0; +} diff --git a/ui/lib/core/addon/components/alert-banner.js b/ui/lib/core/addon/components/alert-banner.js index 30e9b3f91e59..d6970c97383b 100644 --- a/ui/lib/core/addon/components/alert-banner.js +++ b/ui/lib/core/addon/components/alert-banner.js @@ -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() { From ae8e568253db65faf1edb46416d21503e2cb92fb Mon Sep 17 00:00:00 2001 From: hashishaw Date: Wed, 2 Jun 2021 15:51:43 -0500 Subject: [PATCH 07/12] Add has-top-margin-l helper and use on license banner --- ui/app/styles/core/helpers.scss | 3 +++ ui/app/templates/components/license-banners.hbs | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ui/app/styles/core/helpers.scss b/ui/app/styles/core/helpers.scss index 74715cbf04df..2af3cb0196e2 100644 --- a/ui/app/styles/core/helpers.scss +++ b/ui/app/styles/core/helpers.scss @@ -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; } diff --git a/ui/app/templates/components/license-banners.hbs b/ui/app/templates/components/license-banners.hbs index e6b1e3d17c18..8c243aa08c09 100644 --- a/ui/app/templates/components/license-banners.hbs +++ b/ui/app/templates/components/license-banners.hbs @@ -1,19 +1,21 @@ {{#if this.licenseExpired}} -
+ {{else if (lte this.licenseExpiringInDays 30)}} -
+
Read documentation From b1e148fb918e04691aeb0023ba3113d0b66cebab Mon Sep 17 00:00:00 2001 From: hashishaw Date: Thu, 3 Jun 2021 10:05:34 -0500 Subject: [PATCH 08/12] Add sys/health mirage response --- ui/mirage/config.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ui/mirage/config.js b/ui/mirage/config.js index 5fb7e4a01e4d..d70eae4acded 100644 --- a/ui/mirage/config.js +++ b/ui/mirage/config.js @@ -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(); } From 6f06ff363319eb65f9865fa0357d7acf7c49e8c4 Mon Sep 17 00:00:00 2001 From: hashishaw Date: Thu, 3 Jun 2021 10:22:44 -0500 Subject: [PATCH 09/12] Fix width on smaller screens --- ui/app/templates/components/license-banners.hbs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/app/templates/components/license-banners.hbs b/ui/app/templates/components/license-banners.hbs index 8c243aa08c09..a38a5cc076e0 100644 --- a/ui/app/templates/components/license-banners.hbs +++ b/ui/app/templates/components/license-banners.hbs @@ -1,5 +1,5 @@ {{#if this.licenseExpired}} -
+
{{else if (lte this.licenseExpiringInDays 30)}} -
+
Date: Thu, 3 Jun 2021 10:35:28 -0500 Subject: [PATCH 10/12] Fix responsive styling --- ui/app/styles/components/license-banners.scss | 6 ++++++ ui/app/styles/core.scss | 1 + ui/app/templates/components/license-banners.hbs | 4 ++-- 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 ui/app/styles/components/license-banners.scss diff --git a/ui/app/styles/components/license-banners.scss b/ui/app/styles/components/license-banners.scss new file mode 100644 index 000000000000..2c12f180099a --- /dev/null +++ b/ui/app/styles/components/license-banners.scss @@ -0,0 +1,6 @@ +.license-banner-wrapper { + width: 100%; + max-width: 1344px; + margin: $spacing-l auto 0; + padding: 0 1.5rem; +} diff --git a/ui/app/styles/core.scss b/ui/app/styles/core.scss index f0b4fb898350..31a825cb9e2d 100644 --- a/ui/app/styles/core.scss +++ b/ui/app/styles/core.scss @@ -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'; diff --git a/ui/app/templates/components/license-banners.hbs b/ui/app/templates/components/license-banners.hbs index a38a5cc076e0..87cf705feee6 100644 --- a/ui/app/templates/components/license-banners.hbs +++ b/ui/app/templates/components/license-banners.hbs @@ -1,5 +1,5 @@ {{#if this.licenseExpired}} -
+
{{else if (lte this.licenseExpiringInDays 30)}} -
+
Date: Thu, 3 Jun 2021 14:41:47 -0500 Subject: [PATCH 11/12] Add enterprise license banner acceptance test --- .../enterprise-license-banner-test.js | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 ui/tests/acceptance/enterprise-license-banner-test.js diff --git a/ui/tests/acceptance/enterprise-license-banner-test.js b/ui/tests/acceptance/enterprise-license-banner-test.js new file mode 100644 index 000000000000..55ce4bbc2d4f --- /dev/null +++ b/ui/tests/acceptance/enterprise-license-banner-test.js @@ -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(); + }); +}); From f3cb2ef9209d822cf2179b3679e52d413c7abb7b Mon Sep 17 00:00:00 2001 From: hashishaw Date: Thu, 3 Jun 2021 14:42:07 -0500 Subject: [PATCH 12/12] Add changelog --- changelog/11759.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog/11759.txt diff --git a/changelog/11759.txt b/changelog/11759.txt new file mode 100644 index 000000000000..0b0776a65cbf --- /dev/null +++ b/changelog/11759.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: show site-wide banners for license warnings if applicable +```