Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ui/replication status discoverability #8705

Merged
merged 56 commits into from
Jun 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
e51ba17
Sidebranch: add new route on DR secondary (#8640)
Monkeychip Apr 1, 2020
72f2743
Sidebranch: Setup Replication Page as component to be consumed in all…
Monkeychip Apr 2, 2020
635f963
Sidebranch: setup replication dashboard with scss and initial card co…
Monkeychip Apr 6, 2020
bf23bc0
Ui/dr primary/initial page setup (#8671)
Apr 8, 2020
2eb3c0d
Ui/dr primary components (#8711)
Apr 9, 2020
f8123ed
Sidebranch: DR Secondary Dashboard - pr4 (#8706)
Monkeychip Apr 13, 2020
5eb4838
Sidebranch: DR Secondary Dashboard state message handlers (#8741)
Monkeychip Apr 16, 2020
7f992f3
Ui/dr dashboard tests (#8732)
Apr 20, 2020
e477dc9
show dynamic state glyph (#8747)
Apr 20, 2020
358feb5
Merge branch 'master' into ui/replication-status-discoverability
Monkeychip Apr 24, 2020
2f78a9a
Sidebranch: dr-secondary-dashboard-pr7 (#8792)
Monkeychip Apr 24, 2020
36f0a80
Replication Primary Dashboard: handle errors (#8845)
Apr 28, 2020
c5ce3a2
Ui/replication/refactor dashboard components (#8878)
Apr 29, 2020
be645f3
Ui/replication/primary reindexing (#8906)
May 6, 2020
241c1f0
Merge branch 'master' into ui/replication-status-discoverability
May 6, 2020
f31dc15
Sidebranch: component and acceptance tests (#8903)
Monkeychip May 7, 2020
5293685
Merge branch 'master' into ui/replication-status-discoverability
May 7, 2020
f60d159
Sidebranch: remove delta, toggle, and make auto-refresh (#8945)
Monkeychip May 8, 2020
05a985d
ensure status menu displays replication state, not just one (#8959)
May 8, 2020
fe96778
Merge branch 'master' into ui/replication-status-discoverability
May 8, 2020
b1735c8
Add Replication Reindexing Progress Bar (#8975)
May 13, 2020
c5f3e92
Sidebranch: Performance Secondary Dashboard (#8956)
Monkeychip May 13, 2020
11aa304
Merge branch 'master' into ui/replication-status-discoverability
May 15, 2020
4448ad8
Ui/replication/test update (#8995)
May 18, 2020
1dcb6ba
Merge branch 'master' into ui/replication-status-discoverability
May 19, 2020
b88c367
prepare KnownSecondariesTable for backend compatibility (#9029)
May 19, 2020
dd81d58
Ui/replication mgmt action block (#9053)
chelshaw May 21, 2020
83c8944
Sidebranch: address transition issues on replication engine and actio…
Monkeychip May 21, 2020
ef8e476
add Replication Learn Links to wizard (#9106)
May 29, 2020
bf88fdc
Ui/summary dashboard (#9079)
Monkeychip May 29, 2020
6fa0f54
change message with bold 'not' if primary (#9112)
Monkeychip Jun 1, 2020
bd59ffc
Merge branch 'master' into ui/replication-status-discoverability
Jun 1, 2020
65e53f8
Add JSDocs to components (#9125)
Monkeychip Jun 2, 2020
df031aa
Merge branch 'master' into ui/replication-status-discoverability
Jun 2, 2020
764b8e1
Replication Management Sidebranch: Replication Action Disable (#9061)
chelshaw May 29, 2020
77c7f31
Refactor Replication Action: Promote to use modal flow (#9122)
chelshaw Jun 2, 2020
85d9470
Ui/replication mgmt/reindex action (#9126)
chelshaw Jun 3, 2020
da4c6db
Ui/replication mgmt/recover action (#9127)
chelshaw Jun 3, 2020
0d2c486
Close link-to tag in header (#9139)
chelshaw Jun 3, 2020
6f0411d
UI: Fix replication management tests (#9136)
Jun 4, 2020
6a63041
Ui/replication mgmt/update primary action (#9149)
chelshaw Jun 5, 2020
d214629
Ui/secondary token flow dr (#9150)
Monkeychip Jun 8, 2020
1d27584
Add known primaries info table (#9152)
Jun 8, 2020
f8ad326
Ui/rep design updates (#9169)
Jun 9, 2020
d93798d
Merge branch 'master' into ui/replication-status-discoverability
Jun 9, 2020
eb36143
Ui/fix enable overflow (#9173)
Jun 9, 2020
68bc4ab
Merge branch 'ui/replication-mgmt-sidebranch' into ui/replication-sta…
Monkeychip Jun 10, 2020
b0b9f20
Ui/replication mgmt/demote action (#9168)
chelshaw Jun 10, 2020
07817f0
Ui/replication merge cleanup 2 (#9212)
Monkeychip Jun 15, 2020
dd81267
Add real connected state and API address (#9219)
Jun 15, 2020
87d29b4
fix enterprise test (#9229)
Monkeychip Jun 15, 2020
4f92449
Merge branch 'master' into ui/replication-status-discoverability
Jun 15, 2020
b13b1bf
Ui/replication mgmt/generate token action (#9187)
chelshaw Jun 17, 2020
9068757
Merge branch 'master' into ui/replication-status-discoverability
Monkeychip Jun 18, 2020
dca26c9
Merge branch 'master' into ui/replication-status-discoverability
Jun 18, 2020
8c19624
use none set instead of not defined
Jun 18, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- [Writing Stories](#writing-stories)
- [Adding a new story](#adding-a-new-story)
- [Code Generators](#code-generators-1)
- [Storybook Deployment](#storybook-deployment)
- [Further Reading / Useful Links](#further-reading--useful-links)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->
Expand Down Expand Up @@ -67,6 +68,11 @@ long-form version of the npm script:

Make use of the many generators for code, try `ember help generate` for more details. If you're using a component that can be widely-used, consider making it an `addon` component instead (see [this PR](https://github.com/hashicorp/vault/pull/6629) for more details)

eg. a reusable component named foo that you'd like in the core engine

- `ember g component foo --in lib/core`
- `echo "export { default } from 'core/components/foo';" > lib/core/app/components/foo.js`

### Running Tests

Running tests will spin up a Vault dev server on port 9200 via a
Expand Down Expand Up @@ -158,7 +164,7 @@ Note that placing a param inside brackets (e.g. `[closedLabel=More options]` ind

2. Generate a new story with `ember generate story [name-of-component]`
3. Inside the newly generated `stories` file, add at least one example of the component. If the component should be interactive, enable the [Storybook Knobs addon](https://github.com/storybooks/storybook/tree/master/addons/knobs).
4. Generate the `notes` file for the component with `yarn gen-story-md [name-of-component]` (e.g. `yarn gen-md alert-banner`). This will generate markdown documentation of the component and place it at `vault/ui/stories/[name-of-component].md`. If your component is a template-only component, you will need to manually create the markdown file.
4. Generate the `notes` file for the component with `yarn gen-story-md [name-of-component] [name-of-engine-or-addon]` (e.g. `yarn gen-md alert-banner core`). This will generate markdown documentation of the component and place it at `vault/ui/stories/[name-of-component].md`. If your component is a template-only component, you will need to manually create the markdown file.

See the [Storybook Docs](https://storybook.js.org/docs/basics/introduction/) for more information on writing stories.

Expand Down
5 changes: 4 additions & 1 deletion ui/app/adapters/cluster.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,10 @@ export default ApplicationAdapter.extend({
},

generateDrOperationToken(data, options) {
const verb = options && options.checkStatus ? 'GET' : 'PUT';
let verb = options && options.checkStatus ? 'GET' : 'PUT';
if (options.cancel) {
verb = 'DELETE';
}
let url = `${this.buildURL()}/replication/dr/secondary/generate-operation-token/`;
if (!data || data.pgp_key || data.attempt) {
// start the generation
Expand Down
14 changes: 14 additions & 0 deletions ui/app/adapters/replication-mode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import ApplicationAdapter from './application';

export default ApplicationAdapter.extend({
getStatusUrl(mode) {
return this.buildURL() + `/replication/${mode}/status`;
},

fetchStatus(mode) {
let url = this.getStatusUrl(mode);
return this.ajax(url, 'GET', { unauthenticated: true }).then(resp => {
return resp.data;
});
},
});
3 changes: 3 additions & 0 deletions ui/app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ App = Application.extend({
'version',
'wizard',
],
externalRoutes: {
replication: 'vault.cluster.replication.index',
},
},
},
kmip: {
Expand Down
39 changes: 39 additions & 0 deletions ui/app/components/shamir-modal-flow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* @module ShamirModalFlow
* ShamirModalFlow is an extension of the ShamirFlow component that does the Generate Action Token workflow inside of a Modal.
* Please note, this is not an extensive list of the required parameters -- please see ShamirFlow for others
*
* @example
* ```js
* <ShamirModalFlow @onClose={action 'onClose'}>This copy is the main paragraph when the token flow has not started</ShamirModalFlow>
* ```
* @param {function} onClose - This function will be triggered when the modal intends to be closed
*/
import { inject as service } from '@ember/service';
import ShamirFlow from './shamir-flow';
import layout from '../templates/components/shamir-modal-flow';

export default ShamirFlow.extend({
layout,
store: service(),
onClose: () => {},
actions: {
onCancelClose() {
if (this.encoded_token) {
this.send('reset');
} else if (this.generateAction && !this.started) {
if (this.generateStep !== 'chooseMethod') {
this.send('reset');
}
} else {
const adapter = this.get('store').adapterFor('cluster');
adapter.generateDrOperationToken(this.model, { cancel: true });
this.send('reset');
}
this.onClose();
},
onClose() {
this.onClose();
},
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,9 @@ import Controller from '@ember/controller';
export default Controller.extend({
queryParams: ['action'],
action: '',
actions: {
onPromote() {
this.transitionToRoute('vault.cluster.replication.mode.index', 'dr');
},
},
});
8 changes: 8 additions & 0 deletions ui/app/mixins/cluster-route.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const CLUSTER = 'vault.cluster';
const CLUSTER_INDEX = 'vault.cluster.index';
const OIDC_CALLBACK = 'vault.cluster.oidc-callback';
const DR_REPLICATION_SECONDARY = 'vault.cluster.replication-dr-promote';
const DR_REPLICATION_SECONDARY_DETAILS = 'vault.cluster.replication-dr-promote.details';
const EXCLUDED_REDIRECT_URLS = ['/vault/logout'];

export { INIT, UNSEAL, AUTH, CLUSTER, CLUSTER_INDEX, DR_REPLICATION_SECONDARY };
Expand Down Expand Up @@ -70,6 +71,13 @@ export default Mixin.create({
return UNSEAL;
}
if (get(cluster, 'dr.isSecondary')) {
if (transition && transition.targetName === DR_REPLICATION_SECONDARY_DETAILS) {
return DR_REPLICATION_SECONDARY_DETAILS;
}
if (this.router.currentRouteName === DR_REPLICATION_SECONDARY_DETAILS) {
return DR_REPLICATION_SECONDARY_DETAILS;
}

return DR_REPLICATION_SECONDARY;
}
if (!isAuthed) {
Expand Down
51 changes: 8 additions & 43 deletions ui/app/models/cluster.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,57 +46,22 @@ export default DS.Model.extend({
//otherwise the particular mode will have the relevant mode attr through replication-attributes
mode: attr('string'),
allReplicationDisabled: and('{dr,performance}.replicationDisabled'),

anyReplicationEnabled: or('{dr,performance}.replicationEnabled'),

stateDisplay(state) {
if (!state) {
return null;
}
const defaultDisp = 'Synced';
const displays = {
'stream-wals': 'Streaming',
'merkle-diff': 'Determining sync status',
'merkle-sync': 'Syncing',
};

return displays[state] || defaultDisp;
},

drStateDisplay: computed('dr.state', function() {
return this.stateDisplay(this.get('dr.state'));
}),

performanceStateDisplay: computed('performance.state', function() {
return this.stateDisplay(this.get('performance.state'));
}),

stateGlyph(state) {
const glyph = 'check-circle-outline';

const glyphs = {
'stream-wals': 'android-sync',
'merkle-diff': 'android-sync',
'merkle-sync': null,
};

return glyphs[state] || glyph;
},

drStateGlyph: computed('dr.state', function() {
return this.stateGlyph(this.get('dr.state'));
}),

performanceStateGlyph: computed('performance.state', function() {
return this.stateGlyph(this.get('performance.state'));
}),

dr: fragment('replication-attributes'),
performance: fragment('replication-attributes'),
// this service exposes what mode the UI is currently viewing
// 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'
return !this.dr.mode || !this.performance.mode;
}),
replicationAttrs: computed('dr.mode', 'performance.mode', 'replicationMode', function() {
const replicationMode = this.get('replicationMode');
return replicationMode ? get(this, replicationMode) : null;
Expand Down
11 changes: 10 additions & 1 deletion ui/app/models/replication-attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,25 @@ export default Fragment.extend({
isPrimary: match('mode', /primary/),

knownSecondaries: attr('array'),
secondaries: attr('array'),

// secondary attrs
isSecondary: match('mode', /secondary/),

connection_state: attr('string'),
modeForUrl: computed('mode', function() {
const mode = this.get('mode');
return mode === 'bootstrapping'
? 'bootstrapping'
: (this.get('isSecondary') && 'secondary') || (this.get('isPrimary') && 'primary');
}),
modeForHeader: computed('mode', function() {
const mode = this.mode;
if (!mode) {
// mode will be false or undefined if it calls the status endpoint while still setting up the cluster
return 'loading';
}
return mode;
}),
secondaryId: attr('string'),
primaryClusterAddr: attr('string'),
knownPrimaryClusterAddrs: attr('array'),
Expand Down
38 changes: 38 additions & 0 deletions ui/app/models/replication-mode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import DS from 'ember-data';
const { attr } = DS;

/* sample response

{
"request_id": "d81bba81-e8a1-0ee9-240e-a77d36e3e08f",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"cluster_id": "ab7d4191-d1a3-b4d6-6297-5a41af6154ae",
"known_secondaries": [
"test"
],
"last_performance_wal": 72,
"last_reindex_epoch": "1588281113",
"last_wal": 73,
"merkle_root": "c8d258d376f01d98156f74e8d8f82ea2aca8dc4a",
"mode": "primary",
"primary_cluster_addr": "",
"reindex_building_progress": 26838,
"reindex_building_total": 305443,
"reindex_in_progress": true,
"reindex_stage": "building",
"state": "running"
},
"wrap_info": null,
"warnings": null,
"auth": null
}


*/

export default DS.Model.extend({
status: attr('object'),
});
4 changes: 3 additions & 1 deletion ui/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ Router.map(function() {
this.route('show', { path: '/:policy_name' });
this.route('edit', { path: '/:policy_name/edit' });
});
this.route('replication-dr-promote');
this.route('replication-dr-promote', function() {
this.route('details');
});
if (config.addRootMounts) {
config.addRootMounts.call(this);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { inject as service } from '@ember/service';
import Base from './cluster-route-base';
import Base from '../cluster-route-base';

export default Base.extend({
replicationMode: service(),
Expand Down
10 changes: 10 additions & 0 deletions ui/app/routes/vault/cluster/replication-dr-promote/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { inject as service } from '@ember/service';
import Base from '../cluster-route-base';

export default Base.extend({
replicationMode: service(),
beforeModel() {
this._super(...arguments);
this.get('replicationMode').setMode('dr');
},
});
12 changes: 12 additions & 0 deletions ui/app/serializers/replication-mode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import ApplicationSerializer from './application';

export default ApplicationSerializer.extend({
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
const normalizedPayload = {
id: payload.id,
status: payload.data,
};

return this._super(store, primaryModelClass, normalizedPayload, id, requestType);
},
});
67 changes: 67 additions & 0 deletions ui/app/styles/components/action-block.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
@mixin stacked-grid {
grid-template-columns: 1fr;
grid-row: 1/1;
}
@mixin stacked-content {
margin-bottom: $spacing-l;
}

.action-block {
@extend .selectable-card;
grid-template-columns: 2fr 1fr;
display: grid;
padding: $spacing-m $spacing-l;
line-height: inherit;
grid-gap: $spacing-m;

@include until($mobile) {
@include stacked-grid();
}
}

.action-block-info {
@include until($mobile) {
@include stacked-content();
}
}

.action-block.stacked {
@include stacked-grid();
}
.stacked > .action-block-info {
@include stacked-content();
}

.action-block-title {
font-size: $size-5;
font-weight: $font-weight-bold;
}
.action-block-action {
text-align: right;
@include until($mobile) {
text-align: left;
}
}

/* Action Block Grid */
.replication-actions-grid-layout {
display: flex;
flex-wrap: wrap;
margin: $spacing-m 0;
@include until($tablet) {
display: block;
}
}

.replication-actions-grid-item {
flex-basis: 50%;
padding: $spacing-s;
}

.replication-actions-grid-item .action-block {
height: 100%;
width: 100%;
@include until($tablet) {
height: inherit;
}
}
7 changes: 6 additions & 1 deletion ui/app/styles/components/empty-state.scss
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
.empty-state {
align-items: center;
color: $grey;
display: flex;
background: $ui-gray-010;
display: flex;
justify-content: center;
padding: $spacing-xxl $spacing-s;
box-shadow: 0 -2px 0 -1px $ui-gray-300;
Expand Down Expand Up @@ -36,3 +36,8 @@
margin-left: $spacing-s;
}
}

.empty-state-icon > .hs-icon {
float: left;
margin-right: $spacing-xs;
}
Loading