Skip to content

Commit

Permalink
Ui/secondary token flow dr (#9150)
Browse files Browse the repository at this point in the history
* setup token modal flow

* calc expirationDate

* fix date-format test after moving it in addon

* fix icon conditional in modal title

* decode token to get epoch expiration date and convert

* handle clicking outside of modal

* remove extra copy button

* add modal check in rep  acceptance test

* look only at day and month and remove console

* fix spelling

* cleanup

* replace dr with variable

* make string check longer in test

* fix test variables

* refactor enterprise test for secondary token flow

* make cluster model property replicationModeForDisplay to handle all cases where we were either conditionally displaying the DR, Disaster Recovery, etc. or where we were hardcoding it into the hbs.  For situations where it was DR before, I am now keeping it more consistent and using Disaster Recovery as on the manage page we do not show the Diaster Recovery (DR) anywhere.

* set initial value for ttl picker to fix issue where itwas setting seconds to minutes

* clean up

* add comment about ttl picker
  • Loading branch information
Monkeychip committed Jun 8, 2020
1 parent df031aa commit d214629
Show file tree
Hide file tree
Showing 18 changed files with 187 additions and 113 deletions.
3 changes: 3 additions & 0 deletions ui/app/models/cluster.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ export default DS.Model.extend({
// replicationAttrs will then return the relevant `replication-attributes` fragment
rm: service('replication-mode'),
replicationMode: alias('rm.mode'),
replicationModeForDisplay: computed('replicationMode', function() {
return this.replicationMode === 'dr' ? 'Disaster Recovery' : 'Performance';
}),
replicationIsInitializing: computed('dr.mode', 'performance.mode', function() {
// a mode of null only happens when a cluster is being initialized
// otherwise the mode will be 'disabled', 'primary', 'secondary'
Expand Down
2 changes: 1 addition & 1 deletion ui/lib/core/addon/components/modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default Component.extend({
glyph: computed('type', function() {
const modalType = this.get('type');
if (!modalType) {
return {};
return;
}
return messageTypes([this.get('type')]);
}),
Expand Down
2 changes: 1 addition & 1 deletion ui/lib/core/addon/components/replication-actions-single.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Component from '@ember/component';
export default Component.extend({
onSubmit() {},
replicationMode: null,
replicationDisplayMode: null,
replicationModeForDisplay: null,
model: null,

actions: {
Expand Down
11 changes: 0 additions & 11 deletions ui/lib/core/addon/components/replication-actions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { alias } from '@ember/object/computed';
import Component from '@ember/component';
import { computed } from '@ember/object';
import ReplicationActions from 'core/mixins/replication-actions';
import layout from '../templates/components/replication-actions';

Expand All @@ -25,16 +24,6 @@ export default Component.extend(ReplicationActions, DEFAULTS, {
this.setProperties(DEFAULTS);
},

replicationDisplayMode: computed('replicationMode', function() {
const replicationMode = this.get('replicationMode');
if (replicationMode === 'dr') {
return 'DR';
}
if (replicationMode === 'performance') {
return 'Performance';
}
}),

actions: {
onSubmit() {
return this.submitHandler.perform(...arguments);
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
<div data-test-demote-warning>
<AlertInline
@type="danger"
@message="Demoting this DR primary cluster
would result in a DR secondary and in that mode Vault is read-only. This
@message="Demoting this {{model.replicationModeForDisplay}} primary cluster
would result in a {{model.replicationModeForDisplay}} secondary and in that mode Vault is read-only. This
cluster is also currently operating as a Performance
{{capitalize model.performance.modeForUrl}}, demoting it will leave your
replication setup without a performance primary cluster until a new
Expand All @@ -21,7 +21,7 @@
</div>
{{/if}}
<p>
Demote this {{replicationDisplayMode}} primary cluster to a {{replicationDisplayMode}} secondary. The resulting secondary cluster will not
Demote this {{model.replicationModeForDisplay}} primary cluster to a {{model.replicationModeForDisplay}} secondary. The resulting secondary cluster will not
attempt to connect to a primary, but will maintain knowledge of its cluster
ID and can be reconnected to the same set of replication clusters without
wiping local storage.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
</h4>
<div class="content">
<p>
Disable {{replicationDisplayMode}} Replication entirely on the cluster.
Disable {{model.replicationModeForDisplay}} Replication entirely on the cluster.
{{#if model.replicationAttrs.isPrimary}}
Any secondaries will no longer be able to connect.
{{else if (eq model.replicationAttrs.modeForUrl 'bootstrapping')}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{{#if (and (eq replicationMode 'dr') (eq model.replicationAttrs.modeForUrl 'secondary'))}}
<div class="box is-marginless is-shadowless">
<p>
This cluster is currently running as a DR Replication Secondary.
Promote the cluster to a primary by entering DR Operation Token.
This cluster is currently running as a {{model.replicationModeForDisplay}} Replication Secondary.
Promote the cluster to a primary by entering {{model.replicationModeForDisplay}} Operation Token.
</p>
<div class="field">
<label for="dr_operation_token" class="is-label">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
onSubmit=(action "onSubmit")
replicationMode=replicationMode
model=model
replicationDisplayMode=replicationDisplayMode
}}
</div>
{{/each}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@
{{else}}
<p class="help has-text-grey-dark">
{{#if (eq mode 'dr')}}
DR is designed to protect against catastrophic failure of entire clusters. Secondaries do not forward service requests (until they are elected and become a new primary).
{{model.replicationModeForDisplay}} is designed to protect against catastrophic failure of entire clusters. Secondaries do not forward service requests (until they are elected and become a new primary).
{{else}}
Performance Replication scales workloads horizontally across clusters to make requests faster. Local secondaries handle read requests but forward writes to the primary to be handled.
{{model.replicationModeForDisplay}} Replication scales workloads horizontally across clusters to make requests faster. Local secondaries handle read requests but forward writes to the primary to be handled.
{{/if}}
</p>
{{/if}}
Expand Down
1 change: 1 addition & 0 deletions ui/lib/core/app/helpers/date-format.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'core/helpers/date-format';
1 change: 1 addition & 0 deletions ui/lib/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
],
"dependencies": {
"autosize": "*",
"date-fns": "*",
"Duration.js": "*",
"base64-js": "*",
"ember-auto-import": "*",
Expand Down
2 changes: 1 addition & 1 deletion ui/lib/replication/addon/components/replication-summary.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export default Component.extend(ReplicationActions, DEFAULTS, {
try {
yield this.submitHandler.perform(...arguments);
} catch (e) {
// TODO handle error
// do not handle error
}
}),
actions: {
Expand Down
30 changes: 30 additions & 0 deletions ui/lib/replication/addon/controllers/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { inject as service } from '@ember/service';
import Controller from '@ember/controller';
import { copy } from 'ember-copy';
import { resolve } from 'rsvp';
import decodeConfigFromJWT from 'replication/utils/decode-config-from-jwt';

const DEFAULTS = {
token: null,
Expand All @@ -19,9 +20,12 @@ const DEFAULTS = {
};

export default Controller.extend(copy(DEFAULTS, true), {
isModalActive: false,
expirationDate: null,
store: service(),
rm: service('replication-mode'),
replicationMode: alias('rm.mode'),
flashMessages: service(),

submitError(e) {
if (e.errors) {
Expand Down Expand Up @@ -62,6 +66,13 @@ export default Controller.extend(copy(DEFAULTS, true), {
primary_api_addr: null,
primary_cluster_addr: null,
});

// decode token and return epoch expiration, convert to timestamp
const expirationDate = new Date(decodeConfigFromJWT(this.token).exp * 1000);
this.set('expirationDate', expirationDate);

// open modal
this.toggleProperty('isModalActive');
return cluster.reload();
}
this.reset();
Expand Down Expand Up @@ -105,7 +116,26 @@ export default Controller.extend(copy(DEFAULTS, true), {
onSubmit(/*action, mode, data, event*/) {
return this.submitHandler(...arguments);
},
copyClose(successMessage) {
// separate action for copy & close button so it does not try and use execCommand to copy token to clipboard
if (!!successMessage && typeof successMessage === 'string') {
this.get('flashMessages').success(successMessage);
}
this.toggleProperty('isModalActive');
this.transitionToRoute('mode.secondaries');
},
toggleModal(successMessage) {
if (!!successMessage && typeof successMessage === 'string') {
this.get('flashMessages').success(successMessage);
}
// use copy browser extension to copy token if you close the modal by clicking outside of it.
const htmlSelectedToken = document.querySelector('#token-textarea');
htmlSelectedToken.select();
document.execCommand('copy');

this.toggleProperty('isModalActive');
this.transitionToRoute('mode.secondaries');
},
clear() {
this.reset();
this.setProperties({
Expand Down
12 changes: 2 additions & 10 deletions ui/lib/replication/addon/templates/mode.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,14 @@
</li>
<li>
<span class="sep"></span>
{{#if (eq replicationMode 'dr')}}
Disaster Recovery
{{else if (eq replicationMode 'performance')}}
Performance
{{/if}}
{{model.replicationModeForDisplay}}
</li>
</ul>
</nav>
</p.top>
<p.levelLeft>
<h1 class="has-top-margin-m title is-3 " data-test-replication-title=true>
{{#if (eq replicationMode 'dr')}}
Disaster Recovery
{{else if (eq replicationMode 'performance')}}
Performance
{{/if}}
{{model.replicationModeForDisplay}}
<span class="tag is-light has-text-grey-dark" data-test-replication-mode-display=true>
{{model.replicationAttrs.modeForHeader}}
</span>
Expand Down
135 changes: 69 additions & 66 deletions ui/lib/replication/addon/templates/mode/secondaries/add.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,75 +3,78 @@
<h4 class="title is-5">
Generate a secondary token
</h4>
<p>Generate a token to enable {{replicationMode}} Replication or change primaries on secondary cluster.</p>
<p>Generate a token to enable {{model.replicationModeForDisplay}} replication or change primaries on secondary cluster.</p>
</div>
{{message-error errors=errors}}
{{#if token}}
<div class="field">
<label for="activation-token" class="is-label">
Activation token
</label>
<div class="control">
<textarea readonly value={{token}} class="textarea" />
</div>
</div>
<div class="field is-grouped box is-fullwidth is-bottomless">
<div class="control">
{{#copy-button
clipboardText=token
class="button is-primary"
buttonType="button"
success=(action (set-flash-message 'Activation token copied!'))
}}
Copy
{{/copy-button}}
</div>
<div class="control">
<button {{action 'clear'}} type="button" class="button">
Back
</button>
</div>
<div class="field">
<label for="activation-token-id" class="is-label">
Secondary ID
</label>
<div class="control">
{{input class="input" name="activation-token-id" id="activation-token-id" value=id data-test-replication-secondary-id=true}}
</div>
{{else}}
<div class="field">
<label for="activation-token-id" class="is-label">
Secondary ID
</label>
<div class="control">
{{input class="input" name="activation-token-id" id="activation-token-id" value=id data-test-replication-secondary-id=true}}
</div>
<p class="help has-text-grey">
This will be used to identify secondary cluster once a connection has been established with the primary.
</p>
</div>
<div class="field">
{{ttl-picker onChange=(action (mut ttl)) class="is-marginless"}}
<p class="help has-text-grey">
This is the Time To Live for the generated secondary token. After this period, the generated token will no longer be valid.
</p>
<p class="help has-text-grey">
This will be used to identify a secondary cluster once a connection has been established with the primary.
</p>
</div>
<div class="field">
{{!-- TODO fix so it defaults to 30s like in other places or replace with new TTL picker --}}
{{ttl-picker onChange=(action (mut ttl)) initialValue="30m" class="is-marginless"}}
<p class="help has-text-grey">
This is the Time To Live for the generated secondary token. After this period, the generated token will no longer be valid.
</p>
</div>
{{#if (eq replicationMode "performance")}}
<PathFilterConfigList
@paths={{paths}}
@config={{filterConfig}}
@id={{id}}
/>
{{/if}}
<div class="field is-grouped box is-fullwidth is-bottomless">
<div class="control">
<button
type="submit"
class="button is-primary"
data-test-secondary-add=true
>
Generate token
</button>
</div>
{{#if (eq replicationMode "performance")}}
<PathFilterConfigList
@paths={{paths}}
@config={{filterConfig}}
@id={{id}}
/>
{{/if}}
<div class="field is-grouped box is-fullwidth is-bottomless">
<div class="control">
<button
type="submit"
class="button is-primary"
data-test-secondary-add=true
>
Generate token
</button>
</div>
<div class="control">
{{#link-to "mode.secondaries" replicationMode class="button"}}
Cancel
{{/link-to}}
</div>
<div class="control">
{{#link-to "mode.secondaries" replicationMode class="button"}}
Cancel
{{/link-to}}
</div>
{{/if}}
</div>
</form>

{{#if isModalActive}}
<Modal @title="Copy your token" @onClose={{action "toggleModal" "Token copied!"}} @isActive={{isModalActive}}>
<section class="modal-card-body">
<p>This token can be used to enable {{model.replicationModeForDisplay}} replication or change primaries on the secondary cluster.</p>
<div class="box is-shadowless is-fullwidth is-sideless">
<h2 class="title is-6">Activation token</h2>
<div class="copy-text level">
<div class="is-fullwidth">
<textarea readonly value={{token}} id="token-textarea" class="textarea level-left"/>
</div>
</div>
<div class="has-top-margin-xl has-bottom-margin-s">
{{info-table-row
label="TTL"
value=ttl}}
{{info-table-row
label="Expires"
value=(date-format expirationDate 'MMM DD, YYYY hh:mm:ss A')}}
</div>
</div>
</section>
<footer class="modal-card-foot">
<CopyButton class="button is-primary copy-close" data-test-button="modal-copy-close" @clipboardText={{token}}
@buttonType="button" @type="copy" @success={{action "copyClose" "Token copied!"}}>
Copy &amp; Close
</CopyButton>
</footer>
</Modal>
{{/if}}

0 comments on commit d214629

Please sign in to comment.