ui: Peer Deletion (#13665)
* ui: Peer Deletion (#13665) * ui: Add sorting peer listing by State (#13684) * ui: Add filtering peer listing by State (#13685)
This commit is contained in:
parent
8d275ac186
commit
8c0da8fdfb
|
@ -0,0 +1,76 @@
|
||||||
|
%pill-pending::before,
|
||||||
|
%pill-establishing::before,
|
||||||
|
%pill-active::before,
|
||||||
|
%pill-failing::before,
|
||||||
|
%pill-terminated::before,
|
||||||
|
%pill-deleting::before {
|
||||||
|
--icon-size: icon-000;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
%pill-pending,
|
||||||
|
%pill-establishing,
|
||||||
|
%pill-active,
|
||||||
|
%pill-failing,
|
||||||
|
%pill-terminated,
|
||||||
|
%pill-deleting {
|
||||||
|
font-weight: var(--typo-weight-medium);
|
||||||
|
font-size: var(--typo-size-700);
|
||||||
|
}
|
||||||
|
|
||||||
|
%pill-pending::before {
|
||||||
|
--icon-name: icon-running;
|
||||||
|
--icon-color: rgb(var(--tone-gray-800));
|
||||||
|
}
|
||||||
|
%pill-pending {
|
||||||
|
background-color: rgb(var(--tone-strawberry-050));
|
||||||
|
color: rgb(var(--tone-strawberry-500));
|
||||||
|
}
|
||||||
|
|
||||||
|
%pill-establishing::before {
|
||||||
|
--icon-name: icon-running;
|
||||||
|
--icon-color: rgb(var(--tone-gray-800));
|
||||||
|
}
|
||||||
|
%pill-establishing {
|
||||||
|
background-color: rgb(var(--tone-blue-050));
|
||||||
|
color: rgb(var(--tone-blue-500));
|
||||||
|
}
|
||||||
|
|
||||||
|
%pill-active::before {
|
||||||
|
--icon-name: icon-check;
|
||||||
|
--icon-color: rgb(var(--tone-green-800));
|
||||||
|
}
|
||||||
|
%pill-active {
|
||||||
|
background-color: rgb(var(--tone-green-050));
|
||||||
|
color: rgb(var(--tone-green-600));
|
||||||
|
}
|
||||||
|
|
||||||
|
%pill-failing::before {
|
||||||
|
--icon-name: icon-x;
|
||||||
|
--icon-color: rgb(var(--tone-red-500));
|
||||||
|
}
|
||||||
|
%pill-failing {
|
||||||
|
background-color: rgb(var(--tone-red-050));
|
||||||
|
color: rgb(var(--tone-red-500));
|
||||||
|
}
|
||||||
|
|
||||||
|
%pill-terminated::before {
|
||||||
|
--icon-name: icon-x-square;
|
||||||
|
--icon-color: rgb(var(--tone-gray-800));
|
||||||
|
}
|
||||||
|
%pill-terminated {
|
||||||
|
background-color: rgb(var(--tone-gray-150));
|
||||||
|
color: rgb(var(--tone-gray-800));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
%pill-deleting::before {
|
||||||
|
--icon-name: icon-loading;
|
||||||
|
--icon-color: rgb(var(--tone-green-800));
|
||||||
|
}
|
||||||
|
%pill-deleting {
|
||||||
|
background-color: rgb(var(--tone-yellow-050));
|
||||||
|
color: rgb(var(--tone-yellow-800));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
@import './components';
|
||||||
|
|
||||||
|
@import './search-bar';
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Consul::Peer::List
|
||||||
|
|
||||||
|
A presentational component for rendering Consul Peers
|
||||||
|
|
||||||
|
```hbs preview-template
|
||||||
|
<DataSource @src={{uri '/partition/default/dc-1/peers'}} as |source|>
|
||||||
|
<Consul::Peer::List
|
||||||
|
@items={{source.data}}
|
||||||
|
@ondelete={{noop}}
|
||||||
|
/>
|
||||||
|
</DataSource>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Arguments
|
||||||
|
|
||||||
|
| Argument/Attribute | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `items` | `array` | | An array of Peers |
|
||||||
|
| `ondelete` | `function` | | An action to execute when the `Delete` action is clicked |
|
||||||
|
|
||||||
|
## See
|
||||||
|
|
||||||
|
- [Template Source Code](./index.hbs)
|
||||||
|
|
||||||
|
---
|
|
@ -0,0 +1,93 @@
|
||||||
|
<ListCollection
|
||||||
|
class="consul-peer-list"
|
||||||
|
...attributes
|
||||||
|
@items={{@items}}
|
||||||
|
@linkable="linkable peer"
|
||||||
|
as |item index|>
|
||||||
|
<BlockSlot @name="header">
|
||||||
|
{{#if (can 'delete peer' item=item)}}
|
||||||
|
<a
|
||||||
|
data-test-peer={{item.Name}}
|
||||||
|
href={{href-to 'dc.peers.edit' item.Name}}
|
||||||
|
>
|
||||||
|
{{item.Name}}
|
||||||
|
</a>
|
||||||
|
{{else}}
|
||||||
|
<p>
|
||||||
|
{{item.Name}}
|
||||||
|
</p>
|
||||||
|
{{/if}}
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="details">
|
||||||
|
<div class="peers__list__peer-detail">
|
||||||
|
<Peerings::Badge @peering={{item}} />
|
||||||
|
|
||||||
|
<div
|
||||||
|
{{tooltip
|
||||||
|
(t 'routes.dc.peers.index.detail.imported.tooltip'
|
||||||
|
name=item.Name
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{{t 'routes.dc.peers.index.detail.imported.count'
|
||||||
|
count=(format-number item.ImportedServiceCount)
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
{{tooltip
|
||||||
|
(t 'routes.dc.peers.index.detail.exported.tooltip'
|
||||||
|
name=item.Name
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{{t 'routes.dc.peers.index.detail.exported.count'
|
||||||
|
count=(format-number item.ExportedServiceCount)
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="actions" as |Actions|>
|
||||||
|
{{#if (can 'delete peer' item=item)}}
|
||||||
|
|
||||||
|
<Actions as |Action|>
|
||||||
|
<Action
|
||||||
|
data-test-edit-action
|
||||||
|
@href={{href-to 'dc.peers.edit' item.Name}}
|
||||||
|
>
|
||||||
|
<BlockSlot @name="label">
|
||||||
|
View
|
||||||
|
</BlockSlot>
|
||||||
|
</Action>
|
||||||
|
<Action
|
||||||
|
data-test-delete-action
|
||||||
|
@onclick={{fn @ondelete item}}
|
||||||
|
class="dangerous"
|
||||||
|
>
|
||||||
|
<BlockSlot @name="label">
|
||||||
|
Delete
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="confirmation" as |Confirmation|>
|
||||||
|
<Confirmation class="warning">
|
||||||
|
<BlockSlot @name="header">
|
||||||
|
Confirm delete
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="body">
|
||||||
|
<p>
|
||||||
|
Are you sure you want to delete this peer?
|
||||||
|
</p>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="confirm" as |Confirm|>
|
||||||
|
<Confirm>
|
||||||
|
Delete
|
||||||
|
</Confirm>
|
||||||
|
</BlockSlot>
|
||||||
|
</Confirmation>
|
||||||
|
</BlockSlot>
|
||||||
|
</Action>
|
||||||
|
</Actions>
|
||||||
|
{{/if}}
|
||||||
|
</BlockSlot>
|
||||||
|
</ListCollection>
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Consul::Peer::Notifications
|
||||||
|
|
||||||
|
A Notification component specifically for Peers. This is only a component as we currently use this in two places and if we need to add more types we can do so in one place.
|
||||||
|
|
||||||
|
We currently one have one 'remove' type due to the fact that Peers can't use the default 'delete' notification as they get 'marked for deletion' instead.
|
||||||
|
|
||||||
|
```hbs preview-template
|
||||||
|
<Consul::Peer::Notifications
|
||||||
|
@type={{'remove'}}
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## See
|
||||||
|
|
||||||
|
- [Template Source Code](./index.hbs)
|
||||||
|
|
||||||
|
---
|
|
@ -0,0 +1,16 @@
|
||||||
|
{{#if (eq @type 'remove')}}
|
||||||
|
<Notice
|
||||||
|
class="notification-delete"
|
||||||
|
@type="success"
|
||||||
|
...attributes
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
|
<strong>Success!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
|
Your Peer has been marked for deletion.
|
||||||
|
</p>
|
||||||
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
|
{{/if}}
|
|
@ -64,6 +64,42 @@ as |key value|}}
|
||||||
</search.Select>
|
</search.Select>
|
||||||
</search.Search>
|
</search.Search>
|
||||||
</:search>
|
</:search>
|
||||||
|
<:filter as |search|>
|
||||||
|
<search.Select
|
||||||
|
class="type-state"
|
||||||
|
@position="left"
|
||||||
|
@onchange={{action @filter.state.change}}
|
||||||
|
@multiple={{true}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
{{t "components.consul.peer.search-bar.state.name"}}
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
{{#each
|
||||||
|
(get
|
||||||
|
(require '/models/peer'
|
||||||
|
path='schema'
|
||||||
|
from='/components/consul/peer/search-bar'
|
||||||
|
)
|
||||||
|
'State.allowedValues'
|
||||||
|
) as |upperState|}}
|
||||||
|
{{#let
|
||||||
|
(string-to-lower-case upperState)
|
||||||
|
as |state|}}
|
||||||
|
<Option class="value-{{state}}" @value={{state}} @selected={{includes state @filter.state.value}}>
|
||||||
|
<span>
|
||||||
|
{{t (concat "components.consul.peer.search-bar.state.options." state)}}
|
||||||
|
</span>
|
||||||
|
</Option>
|
||||||
|
{{/let}}
|
||||||
|
{{/each}}
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</search.Select>
|
||||||
|
</:filter>
|
||||||
<:sort as |search|>
|
<:sort as |search|>
|
||||||
<search.Select
|
<search.Select
|
||||||
class="type-sort"
|
class="type-sort"
|
||||||
|
@ -78,6 +114,8 @@ as |key value|}}
|
||||||
{{#let (from-entries (array
|
{{#let (from-entries (array
|
||||||
(array "Name:asc" (t "common.sort.alpha.asc"))
|
(array "Name:asc" (t "common.sort.alpha.asc"))
|
||||||
(array "Name:desc" (t "common.sort.alpha.desc"))
|
(array "Name:desc" (t "common.sort.alpha.desc"))
|
||||||
|
(array "State:asc" (t "components.consul.peer.search-bar.sort.state.asc"))
|
||||||
|
(array "State:desc" (t "components.consul.peer.search-bar.sort.state.desc"))
|
||||||
))
|
))
|
||||||
as |selectable|
|
as |selectable|
|
||||||
}}
|
}}
|
||||||
|
@ -87,6 +125,10 @@ as |key value|}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="options">
|
<BlockSlot @name="options">
|
||||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
<Optgroup @label={{t "components.consul.peer.search-bar.sort.state.name"}}>
|
||||||
|
<Option @value="State:asc" @selected={{eq "State:asc" @sort.value}}>{{t "components.consul.peer.search-bar.sort.state.asc"}}</Option>
|
||||||
|
<Option @value="State:desc" @selected={{eq "State:desc" @sort.value}}>{{t "components.consul.peer.search-bar.sort.state.desc"}}</Option>
|
||||||
|
</Optgroup>
|
||||||
<Optgroup @label={{t "common.consul.name"}}>
|
<Optgroup @label={{t "common.consul.name"}}>
|
||||||
<Option @value="Name:asc" @selected={{eq "Name:asc" @sort.value}}>{{t "common.sort.alpha.asc"}}</Option>
|
<Option @value="Name:asc" @selected={{eq "Name:asc" @sort.value}}>{{t "common.sort.alpha.asc"}}</Option>
|
||||||
<Option @value="Name:desc" @selected={{eq "Name:desc" @sort.value}}>{{t "common.sort.alpha.desc"}}</Option>
|
<Option @value="Name:desc" @selected={{eq "Name:desc" @sort.value}}>{{t "common.sort.alpha.desc"}}</Option>
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
.consul-peer-search-bar {
|
||||||
|
li button span {
|
||||||
|
@extend %pill-500;
|
||||||
|
}
|
||||||
|
.value-pending span {
|
||||||
|
@extend %pill-pending;
|
||||||
|
}
|
||||||
|
.value-establishing span {
|
||||||
|
@extend %pill-establishing;
|
||||||
|
}
|
||||||
|
.value-active span {
|
||||||
|
@extend %pill-active;
|
||||||
|
}
|
||||||
|
.value-failing span {
|
||||||
|
@extend %pill-failing;
|
||||||
|
}
|
||||||
|
.value-terminated span {
|
||||||
|
@extend %pill-terminated;
|
||||||
|
}
|
||||||
|
.value-deleting span {
|
||||||
|
@extend %pill-deleting;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
export default {
|
||||||
|
state: {
|
||||||
|
pending: (item, value) => item.State.toLowerCase() === value,
|
||||||
|
establishing: (item, value) => item.State.toLowerCase() === value,
|
||||||
|
active: (item, value) => item.State.toLowerCase() === value,
|
||||||
|
failing: (item, value) => item.State.toLowerCase() === value,
|
||||||
|
terminated: (item, value) => item.State.toLowerCase() === value,
|
||||||
|
deleting: (item, value) => item.State.toLowerCase() === value,
|
||||||
|
},
|
||||||
|
};
|
|
@ -12,12 +12,15 @@ const container = new Map();
|
||||||
// `css` already has a caching mechanism under the hood so rely on that, plus
|
// `css` already has a caching mechanism under the hood so rely on that, plus
|
||||||
// we get the advantage of laziness here, i.e. we only call css as and when we
|
// we get the advantage of laziness here, i.e. we only call css as and when we
|
||||||
// need to
|
// need to
|
||||||
export default helper(([path = ''], { from }) => {
|
export default helper(([path = ''], options) => {
|
||||||
const fullPath = resolve(`${appName}${from}`, path);
|
let fullPath = resolve(`${appName}${options.from}`, path);
|
||||||
|
if(path.charAt(0) === '/') {
|
||||||
|
fullPath = `${appName}${fullPath}`;
|
||||||
|
}
|
||||||
|
|
||||||
let module;
|
let module;
|
||||||
if(require.has(fullPath)) {
|
if(require.has(fullPath)) {
|
||||||
module = require(fullPath).default;
|
module = require(fullPath)[options.export || 'default'];
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Unable to resolve '${fullPath}' does the file exist?`)
|
throw new Error(`Unable to resolve '${fullPath}' does the file exist?`)
|
||||||
}
|
}
|
||||||
|
@ -27,7 +30,7 @@ export default helper(([path = ''], { from }) => {
|
||||||
return module(css);
|
return module(css);
|
||||||
case fullPath.endsWith('.xstate'):
|
case fullPath.endsWith('.xstate'):
|
||||||
return module;
|
return module;
|
||||||
default: {
|
case fullPath.endsWith('.element'): {
|
||||||
if(container.has(fullPath)) {
|
if(container.has(fullPath)) {
|
||||||
return container.get(fullPath);
|
return container.get(fullPath);
|
||||||
}
|
}
|
||||||
|
@ -35,5 +38,7 @@ export default helper(([path = ''], { from }) => {
|
||||||
container.set(fullPath, component);
|
container.set(fullPath, component);
|
||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
return module;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,18 @@
|
||||||
import Model, { attr } from '@ember-data/model';
|
import Model, { attr } from '@ember-data/model';
|
||||||
|
|
||||||
|
export const schema = {
|
||||||
|
State: {
|
||||||
|
defaultValue: 'PENDING',
|
||||||
|
allowedValues: [
|
||||||
|
'PENDING',
|
||||||
|
'ESTABLISHING',
|
||||||
|
'ACTIVE',
|
||||||
|
'FAILING',
|
||||||
|
'TERMINATED',
|
||||||
|
'DELETING'
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
export default class Peer extends Model {
|
export default class Peer extends Model {
|
||||||
@attr('string') uri;
|
@attr('string') uri;
|
||||||
@attr() meta;
|
@attr() meta;
|
||||||
|
|
|
@ -3,6 +3,7 @@ import Route from 'consul-ui/routing/route';
|
||||||
export default class PeersRoute extends Route {
|
export default class PeersRoute extends Route {
|
||||||
queryParams = {
|
queryParams = {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
|
state: 'state',
|
||||||
searchproperty: {
|
searchproperty: {
|
||||||
as: 'searchproperty',
|
as: 'searchproperty',
|
||||||
empty: [['Name']],
|
empty: [['Name']],
|
||||||
|
|
|
@ -2,11 +2,14 @@ import Service, { inject as service } from '@ember/service';
|
||||||
import { setProperties } from '@ember/object';
|
import { setProperties } from '@ember/object';
|
||||||
|
|
||||||
export default class HttpService extends Service {
|
export default class HttpService extends Service {
|
||||||
|
@service('client/http') client;
|
||||||
|
|
||||||
@service('settings') settings;
|
@service('settings') settings;
|
||||||
@service('repository/intention') intention;
|
@service('repository/intention') intention;
|
||||||
@service('repository/kv') kv;
|
@service('repository/kv') kv;
|
||||||
@service('repository/nspace') nspace;
|
@service('repository/nspace') nspace;
|
||||||
@service('repository/partition') partition;
|
@service('repository/partition') partition;
|
||||||
|
@service('repository/peer') peer;
|
||||||
@service('repository/session') session;
|
@service('repository/session') session;
|
||||||
|
|
||||||
prepare(sink, data, instance) {
|
prepare(sink, data, instance) {
|
||||||
|
@ -24,12 +27,16 @@ export default class HttpService extends Service {
|
||||||
persist(sink, instance) {
|
persist(sink, instance) {
|
||||||
const [, , , , model] = sink.split('/');
|
const [, , , , model] = sink.split('/');
|
||||||
const repo = this[model];
|
const repo = this[model];
|
||||||
return repo.persist(instance);
|
return this.client.request(
|
||||||
|
request => repo.persist(instance, request)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(sink, instance) {
|
remove(sink, instance) {
|
||||||
const [, , , , model] = sink.split('/');
|
const [, , , , model] = sink.split('/');
|
||||||
const repo = this[model];
|
const repo = this[model];
|
||||||
return repo.remove(instance);
|
return this.client.request(
|
||||||
|
request => repo.remove(instance, request)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import intention from 'consul-ui/filter/predicates/intention';
|
||||||
import token from 'consul-ui/filter/predicates/token';
|
import token from 'consul-ui/filter/predicates/token';
|
||||||
import policy from 'consul-ui/filter/predicates/policy';
|
import policy from 'consul-ui/filter/predicates/policy';
|
||||||
import authMethod from 'consul-ui/filter/predicates/auth-method';
|
import authMethod from 'consul-ui/filter/predicates/auth-method';
|
||||||
|
import peer from 'consul-ui/filter/predicates/peer';
|
||||||
|
|
||||||
const predicates = {
|
const predicates = {
|
||||||
service: andOr(service),
|
service: andOr(service),
|
||||||
|
@ -21,6 +22,7 @@ const predicates = {
|
||||||
intention: andOr(intention),
|
intention: andOr(intention),
|
||||||
token: andOr(token),
|
token: andOr(token),
|
||||||
policy: andOr(policy),
|
policy: andOr(policy),
|
||||||
|
peer: andOr(peer),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class FilterService extends Service {
|
export default class FilterService extends Service {
|
||||||
|
|
|
@ -70,4 +70,30 @@ export default class PeerService extends RepositoryService {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async remove(item, request) {
|
||||||
|
// soft delete
|
||||||
|
// we just return the item we want to delete
|
||||||
|
// but mark it as DELETING ourselves as the request is successfull
|
||||||
|
// and we don't have blocking queries here to get immediate updates
|
||||||
|
return (await request`
|
||||||
|
DELETE /v1/peering/${item.Name}
|
||||||
|
`)((headers, body, cache) => {
|
||||||
|
const partition = item.Partition;
|
||||||
|
const ns = item.Namespace;
|
||||||
|
const dc = item.Datacenter;
|
||||||
|
return {
|
||||||
|
meta: {
|
||||||
|
version: 2,
|
||||||
|
},
|
||||||
|
body: cache(
|
||||||
|
{
|
||||||
|
...item,
|
||||||
|
State: 'DELETING'
|
||||||
|
},
|
||||||
|
uri => uri`peer:///${partition}/${ns}/${dc}/peer/${item.Name}`
|
||||||
|
)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,26 @@
|
||||||
export default ({ properties }) => key => {
|
import { schema } from 'consul-ui/models/peer';
|
||||||
|
|
||||||
|
export default ({ properties }) => (key = 'State:asc') => {
|
||||||
|
if (key.startsWith('State:')) {
|
||||||
|
return function(itemA, itemB) {
|
||||||
|
const [, dir] = key.split(':');
|
||||||
|
let a, b;
|
||||||
|
if (dir === 'asc') {
|
||||||
|
b = itemA;
|
||||||
|
a = itemB;
|
||||||
|
} else {
|
||||||
|
a = itemA;
|
||||||
|
b = itemB;
|
||||||
|
}
|
||||||
|
switch (true) {
|
||||||
|
case schema.State.allowedValues.indexOf(a.State) < schema.State.allowedValues.indexOf(b.State):
|
||||||
|
return 1;
|
||||||
|
case schema.State.allowedValues.indexOf(a.State) > schema.State.allowedValues.indexOf(b.State):
|
||||||
|
return -1;
|
||||||
|
case schema.State.allowedValues.indexOf(a.State) === schema.State.allowedValues.indexOf(b.State):
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
return properties(['Name'])(key);
|
return properties(['Name'])(key);
|
||||||
};
|
};
|
||||||
|
|
|
@ -501,7 +501,7 @@
|
||||||
// @import './rotate-ccw/index.scss';
|
// @import './rotate-ccw/index.scss';
|
||||||
// @import './rotate-cw/index.scss';
|
// @import './rotate-cw/index.scss';
|
||||||
// @import './rss/index.scss';
|
// @import './rss/index.scss';
|
||||||
// @import './running/index.scss';
|
@import './running/index.scss';
|
||||||
// @import './save/index.scss';
|
// @import './save/index.scss';
|
||||||
// @import './scissors/index.scss';
|
// @import './scissors/index.scss';
|
||||||
// @import './search/index.scss';
|
// @import './search/index.scss';
|
||||||
|
@ -620,7 +620,7 @@
|
||||||
// @import './x-diamond-fill/index.scss';
|
// @import './x-diamond-fill/index.scss';
|
||||||
// @import './x-hexagon/index.scss';
|
// @import './x-hexagon/index.scss';
|
||||||
// @import './x-hexagon-fill/index.scss';
|
// @import './x-hexagon-fill/index.scss';
|
||||||
// @import './x-square/index.scss';
|
@import './x-square/index.scss';
|
||||||
// @import './x-square-fill/index.scss';
|
// @import './x-square-fill/index.scss';
|
||||||
// @import './youtube/index.scss';
|
// @import './youtube/index.scss';
|
||||||
// @import './youtube-color/index.scss';
|
// @import './youtube-color/index.scss';
|
||||||
|
|
|
@ -103,7 +103,8 @@
|
||||||
@import 'consul-ui/components/topology-metrics/series';
|
@import 'consul-ui/components/topology-metrics/series';
|
||||||
@import 'consul-ui/components/topology-metrics/stats';
|
@import 'consul-ui/components/topology-metrics/stats';
|
||||||
@import 'consul-ui/components/topology-metrics/status';
|
@import 'consul-ui/components/topology-metrics/status';
|
||||||
|
@import 'consul-ui/components/consul/intention/list/table';
|
||||||
|
@import 'consul-ui/components/consul/peer';
|
||||||
@import 'consul-ui/components/peerings/badge';
|
@import 'consul-ui/components/peerings/badge';
|
||||||
@import 'consul-ui/components/consul/node/peer-info';
|
@import 'consul-ui/components/consul/node/peer-info';
|
||||||
@import 'consul-ui/components/consul/intention/list/table';
|
|
||||||
@import 'consul-ui/components/consul/service/peer-info';
|
@import 'consul-ui/components/consul/service/peer-info';
|
||||||
|
|
|
@ -20,11 +20,15 @@
|
||||||
{{#let
|
{{#let
|
||||||
|
|
||||||
(hash
|
(hash
|
||||||
value=(or sortBy "Name:asc")
|
value=(or sortBy "State:asc")
|
||||||
change=(action (mut sortBy) value="target.selected")
|
change=(action (mut sortBy) value="target.selected")
|
||||||
)
|
)
|
||||||
|
|
||||||
(hash
|
(hash
|
||||||
|
state=(hash
|
||||||
|
value=(if state (split state ',') undefined)
|
||||||
|
change=(action (mut state) value="target.selectedItems")
|
||||||
|
)
|
||||||
searchproperty=(hash
|
searchproperty=(hash
|
||||||
value=(if (not-eq searchproperty undefined)
|
value=(if (not-eq searchproperty undefined)
|
||||||
(split searchproperty ',')
|
(split searchproperty ',')
|
||||||
|
@ -58,111 +62,86 @@ as |sort filters items|}}
|
||||||
|
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="content">
|
<BlockSlot @name="content">
|
||||||
<DataCollection
|
<DataWriter
|
||||||
|
@sink={{uri '/${partition}/${dc}/${nspace}/peer/'
|
||||||
|
(hash
|
||||||
|
partition=route.params.partition
|
||||||
|
nspace=route.params.nspace
|
||||||
|
dc=route.params.dc
|
||||||
|
)
|
||||||
|
}}
|
||||||
@type="peer"
|
@type="peer"
|
||||||
@sort={{sort.value}}
|
@label="Peer"
|
||||||
@filters={{filters}}
|
@ondelete={{refresh-route}}
|
||||||
@search={{search}}
|
as |writer|>
|
||||||
@items={{items}}
|
<BlockSlot @name="removed" as |after|>
|
||||||
as |collection|>
|
<Consul::Peer::Notifications
|
||||||
<collection.Collection>
|
{{notification
|
||||||
|
after=(action after)
|
||||||
|
}}
|
||||||
|
@type="remove"
|
||||||
|
/>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="content">
|
||||||
|
<DataCollection
|
||||||
|
@type="peer"
|
||||||
|
@sort={{sort.value}}
|
||||||
|
@filters={{filters}}
|
||||||
|
@search={{search}}
|
||||||
|
@items={{items}}
|
||||||
|
as |collection|>
|
||||||
|
<collection.Collection>
|
||||||
|
|
||||||
<ListCollection
|
<Consul::Peer::List
|
||||||
@items={{collection.items}}
|
@items={{collection.items}}
|
||||||
@linkable="linkable peer"
|
@onedit={{this.edit.open}}
|
||||||
as |item index|>
|
@ondelete={{writer.delete}}
|
||||||
<BlockSlot @name="header">
|
/>
|
||||||
<p>{{item.Name}}</p>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="details">
|
|
||||||
<div class="peers__list__peer-detail">
|
|
||||||
<Peerings::Badge @peering={{item}} />
|
|
||||||
|
|
||||||
<div
|
</collection.Collection>
|
||||||
{{tooltip
|
<collection.Empty>
|
||||||
(t 'routes.dc.peers.index.detail.imported.tooltip'
|
{{!-- TODO: do we need to check permissions here or will we receive an error automatically? --}}
|
||||||
name=item.Name
|
<EmptyState
|
||||||
)
|
@login={{route.model.app.login.open}}
|
||||||
}}
|
>
|
||||||
>
|
<BlockSlot @name="header">
|
||||||
{{t 'routes.dc.peers.index.detail.imported.count'
|
<h2>
|
||||||
count=(format-number item.ImportedServiceCount)
|
{{#if (gt items.length 0)}}
|
||||||
}}
|
No peers found
|
||||||
</div>
|
{{else}}
|
||||||
|
Welcome to Peers
|
||||||
<div
|
{{/if}}
|
||||||
{{tooltip
|
</h2>
|
||||||
(t 'routes.dc.peers.index.detail.exported.tooltip'
|
</BlockSlot>
|
||||||
name=item.Name
|
<BlockSlot @name="body">
|
||||||
)
|
{{#if (gt items.length 0)}}
|
||||||
}}
|
No peers where found matching that search, or you may not have access to view the peers you are searching for.
|
||||||
>
|
{{else}}
|
||||||
{{t 'routes.dc.peers.index.detail.exported.count'
|
Peering allows an admin partition in one datacenter to communicate with a partition in a different
|
||||||
count=(format-number item.ExportedServiceCount)
|
datacenter. There don't seem to be any peers for this admin partition, or you may not have
|
||||||
}}
|
<code>peering:read</code> permissions to
|
||||||
</div>
|
access this view.
|
||||||
|
{{/if}}
|
||||||
</div>
|
</BlockSlot>
|
||||||
</BlockSlot>
|
<BlockSlot @name="actions">
|
||||||
<BlockSlot @name="actions" as |Actions|>
|
<li class="docs-link">
|
||||||
{{#if (can 'delete peer' item=item)}}
|
{{!-- what's the docs for peering?--}}
|
||||||
|
<a href="https://www.consul.io/docs/agent/kv" rel="noopener noreferrer" target="_blank">
|
||||||
<Actions as |Action|>
|
Documentation on Peers
|
||||||
{{#if true}}
|
</a>
|
||||||
<Action data-test-edit-action @href={{href-to 'dc.peers.edit' item.Name}}>
|
</li>
|
||||||
<BlockSlot @name="label">
|
<li class="learn-link">
|
||||||
View
|
<a href="https://learn.hashicorp.com/consul/getting-started/kv" rel="noopener noreferrer" target="_blank">
|
||||||
</BlockSlot>
|
Take the tutorial
|
||||||
</Action>
|
</a>
|
||||||
{{/if}}
|
</li>
|
||||||
</Actions>
|
</BlockSlot>
|
||||||
{{/if}}
|
</EmptyState>
|
||||||
</BlockSlot>
|
</collection.Empty>
|
||||||
</ListCollection>
|
</DataCollection>
|
||||||
|
</BlockSlot>
|
||||||
</collection.Collection>
|
</DataWriter>
|
||||||
<collection.Empty>
|
|
||||||
{{!-- TODO: do we need to check permissions here or will we receive an error automatically? --}}
|
|
||||||
<EmptyState
|
|
||||||
@login={{route.model.app.login.open}}
|
|
||||||
>
|
|
||||||
<BlockSlot @name="header">
|
|
||||||
<h2>
|
|
||||||
{{#if (gt items.length 0)}}
|
|
||||||
No peers found
|
|
||||||
{{else}}
|
|
||||||
Welcome to Peers
|
|
||||||
{{/if}}
|
|
||||||
</h2>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="body">
|
|
||||||
{{#if (gt items.length 0)}}
|
|
||||||
No peers where found matching that search, or you may not have access to view the peers you are searching for.
|
|
||||||
{{else}}
|
|
||||||
Peering allows an admin partition in one datacenter to communicate with a partition in a different
|
|
||||||
datacenter. There don't seem to be any peers for this admin partition, or you may not have
|
|
||||||
<code>peering:read</code> permissions to
|
|
||||||
access this view.
|
|
||||||
{{/if}}
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="actions">
|
|
||||||
<li class="docs-link">
|
|
||||||
{{!-- what's the docs for peering?--}}
|
|
||||||
<a href="https://www.consul.io/docs/agent/kv" rel="noopener noreferrer" target="_blank">
|
|
||||||
Documentation on Peers
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="learn-link">
|
|
||||||
<a href="https://learn.hashicorp.com/consul/getting-started/kv" rel="noopener noreferrer" target="_blank">
|
|
||||||
Take the tutorial
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</BlockSlot>
|
|
||||||
</EmptyState>
|
|
||||||
</collection.Empty>
|
|
||||||
</DataCollection>
|
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
||||||
</AppView>
|
</AppView>
|
||||||
{{/let}}
|
{{/let}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
peer:
|
||||||
|
search-bar:
|
||||||
|
state:
|
||||||
|
name: Status
|
||||||
|
options:
|
||||||
|
pending: Pending
|
||||||
|
establishing: Establishing
|
||||||
|
active: Active
|
||||||
|
failing: Failing
|
||||||
|
terminated: Terminated
|
||||||
|
deleting: Deleting
|
||||||
|
sort:
|
||||||
|
state:
|
||||||
|
name: Status
|
||||||
|
asc: Pending to Deleting
|
||||||
|
desc: Deleting to Pending
|
||||||
service:
|
service:
|
||||||
search-bar:
|
search-bar:
|
||||||
kind: Service Type
|
kind: Service Type
|
||||||
|
|
Loading…
Reference in New Issue