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.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|>
|
||||
<search.Select
|
||||
class="type-sort"
|
||||
|
@ -78,6 +114,8 @@ as |key value|}}
|
|||
{{#let (from-entries (array
|
||||
(array "Name:asc" (t "common.sort.alpha.asc"))
|
||||
(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|
|
||||
}}
|
||||
|
@ -87,6 +125,10 @@ as |key value|}}
|
|||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#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"}}>
|
||||
<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>
|
||||
|
|
|
@ -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
|
||||
// we get the advantage of laziness here, i.e. we only call css as and when we
|
||||
// need to
|
||||
export default helper(([path = ''], { from }) => {
|
||||
const fullPath = resolve(`${appName}${from}`, path);
|
||||
export default helper(([path = ''], options) => {
|
||||
let fullPath = resolve(`${appName}${options.from}`, path);
|
||||
if(path.charAt(0) === '/') {
|
||||
fullPath = `${appName}${fullPath}`;
|
||||
}
|
||||
|
||||
let module;
|
||||
if(require.has(fullPath)) {
|
||||
module = require(fullPath).default;
|
||||
module = require(fullPath)[options.export || 'default'];
|
||||
} else {
|
||||
throw new Error(`Unable to resolve '${fullPath}' does the file exist?`)
|
||||
}
|
||||
|
@ -27,7 +30,7 @@ export default helper(([path = ''], { from }) => {
|
|||
return module(css);
|
||||
case fullPath.endsWith('.xstate'):
|
||||
return module;
|
||||
default: {
|
||||
case fullPath.endsWith('.element'): {
|
||||
if(container.has(fullPath)) {
|
||||
return container.get(fullPath);
|
||||
}
|
||||
|
@ -35,5 +38,7 @@ export default helper(([path = ''], { from }) => {
|
|||
container.set(fullPath, component);
|
||||
return component;
|
||||
}
|
||||
default:
|
||||
return module;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,5 +1,18 @@
|
|||
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 {
|
||||
@attr('string') uri;
|
||||
@attr() meta;
|
||||
|
|
|
@ -3,6 +3,7 @@ import Route from 'consul-ui/routing/route';
|
|||
export default class PeersRoute extends Route {
|
||||
queryParams = {
|
||||
sortBy: 'sort',
|
||||
state: 'state',
|
||||
searchproperty: {
|
||||
as: 'searchproperty',
|
||||
empty: [['Name']],
|
||||
|
|
|
@ -2,11 +2,14 @@ import Service, { inject as service } from '@ember/service';
|
|||
import { setProperties } from '@ember/object';
|
||||
|
||||
export default class HttpService extends Service {
|
||||
@service('client/http') client;
|
||||
|
||||
@service('settings') settings;
|
||||
@service('repository/intention') intention;
|
||||
@service('repository/kv') kv;
|
||||
@service('repository/nspace') nspace;
|
||||
@service('repository/partition') partition;
|
||||
@service('repository/peer') peer;
|
||||
@service('repository/session') session;
|
||||
|
||||
prepare(sink, data, instance) {
|
||||
|
@ -24,12 +27,16 @@ export default class HttpService extends Service {
|
|||
persist(sink, instance) {
|
||||
const [, , , , model] = sink.split('/');
|
||||
const repo = this[model];
|
||||
return repo.persist(instance);
|
||||
return this.client.request(
|
||||
request => repo.persist(instance, request)
|
||||
);
|
||||
}
|
||||
|
||||
remove(sink, instance) {
|
||||
const [, , , , model] = sink.split('/');
|
||||
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 policy from 'consul-ui/filter/predicates/policy';
|
||||
import authMethod from 'consul-ui/filter/predicates/auth-method';
|
||||
import peer from 'consul-ui/filter/predicates/peer';
|
||||
|
||||
const predicates = {
|
||||
service: andOr(service),
|
||||
|
@ -21,6 +22,7 @@ const predicates = {
|
|||
intention: andOr(intention),
|
||||
token: andOr(token),
|
||||
policy: andOr(policy),
|
||||
peer: andOr(peer),
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
|
|
@ -501,7 +501,7 @@
|
|||
// @import './rotate-ccw/index.scss';
|
||||
// @import './rotate-cw/index.scss';
|
||||
// @import './rss/index.scss';
|
||||
// @import './running/index.scss';
|
||||
@import './running/index.scss';
|
||||
// @import './save/index.scss';
|
||||
// @import './scissors/index.scss';
|
||||
// @import './search/index.scss';
|
||||
|
@ -620,7 +620,7 @@
|
|||
// @import './x-diamond-fill/index.scss';
|
||||
// @import './x-hexagon/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 './youtube/index.scss';
|
||||
// @import './youtube-color/index.scss';
|
||||
|
|
|
@ -103,7 +103,8 @@
|
|||
@import 'consul-ui/components/topology-metrics/series';
|
||||
@import 'consul-ui/components/topology-metrics/stats';
|
||||
@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/consul/node/peer-info';
|
||||
@import 'consul-ui/components/consul/intention/list/table';
|
||||
@import 'consul-ui/components/consul/service/peer-info';
|
||||
|
|
|
@ -20,11 +20,15 @@
|
|||
{{#let
|
||||
|
||||
(hash
|
||||
value=(or sortBy "Name:asc")
|
||||
value=(or sortBy "State:asc")
|
||||
change=(action (mut sortBy) value="target.selected")
|
||||
)
|
||||
|
||||
(hash
|
||||
state=(hash
|
||||
value=(if state (split state ',') undefined)
|
||||
change=(action (mut state) value="target.selectedItems")
|
||||
)
|
||||
searchproperty=(hash
|
||||
value=(if (not-eq searchproperty undefined)
|
||||
(split searchproperty ',')
|
||||
|
@ -58,111 +62,86 @@ as |sort filters items|}}
|
|||
|
||||
</BlockSlot>
|
||||
<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"
|
||||
@sort={{sort.value}}
|
||||
@filters={{filters}}
|
||||
@search={{search}}
|
||||
@items={{items}}
|
||||
as |collection|>
|
||||
<collection.Collection>
|
||||
@label="Peer"
|
||||
@ondelete={{refresh-route}}
|
||||
as |writer|>
|
||||
<BlockSlot @name="removed" as |after|>
|
||||
<Consul::Peer::Notifications
|
||||
{{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
|
||||
@items={{collection.items}}
|
||||
@linkable="linkable peer"
|
||||
as |item index|>
|
||||
<BlockSlot @name="header">
|
||||
<p>{{item.Name}}</p>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="details">
|
||||
<div class="peers__list__peer-detail">
|
||||
<Peerings::Badge @peering={{item}} />
|
||||
<Consul::Peer::List
|
||||
@items={{collection.items}}
|
||||
@onedit={{this.edit.open}}
|
||||
@ondelete={{writer.delete}}
|
||||
/>
|
||||
|
||||
<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|>
|
||||
{{#if true}}
|
||||
<Action data-test-edit-action @href={{href-to 'dc.peers.edit' item.Name}}>
|
||||
<BlockSlot @name="label">
|
||||
View
|
||||
</BlockSlot>
|
||||
</Action>
|
||||
{{/if}}
|
||||
</Actions>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
</ListCollection>
|
||||
|
||||
</collection.Collection>
|
||||
<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>
|
||||
</collection.Collection>
|
||||
<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>
|
||||
</DataWriter>
|
||||
</BlockSlot>
|
||||
|
||||
</AppView>
|
||||
{{/let}}
|
||||
</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:
|
||||
search-bar:
|
||||
kind: Service Type
|
||||
|
|
Loading…
Reference in New Issue