ui: Adds basic support for partition exports to Service listings (#11702)

Also:

* ui: Add cross partition linking and rollout BucketList (#11712)

* ui: Add exported service partition to the source filter menu (#11727)
This commit is contained in:
John Cowen 2021-12-06 11:06:33 +00:00 committed by GitHub
parent a90a65c9d8
commit 442df6d27d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 286 additions and 100 deletions

3
.changelog/11702.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:feature
ui: Adds basic support for showing Services exported from another partition.
```

View File

@ -0,0 +1,44 @@
# Consul::Bucket::List
A presentational component for rendering a list of Consul 'buckets'
(a single partition and/or a single namepace).
Please note this is not your usual "scrollable list component" more a list of
'buckets' that make up a partition / namespace combination.
If only a the namespace is different to the currently selected namespace, then
the namespace will be displayed, whereas if the partition is different it will
show both the partition and namespace (as a namespace called 'team-1' in
`partition-1` is different to a namespace called 'team-1' in `partition-2`)
If you don't need the nspace only support for the view you are building then
omit the `@nspace` argument.
At the time of writing, this is not currently used across the entire UI
(specifically in intentions and maybe other areas) but eventually should be.
```hbs preview-template
<DataSource
@src={{uri "/partition/default/dc-1/gateways/for-service/service-name"}} as |source|>
<Consul::Bucket::List
@item={{object-at 0 source.data}}
@nspace={{'nspace'}}
@partition={{'partition'}}
/>
</DataSource>
```
## Arguments
| Argument/Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `item` | `array` | | A Consul object that could have both a `Partition` and a `Namespace` property |
| `nspace` | `string` | | The name of the current namespace |
| `partition` | `string` | | The name of the current partition |
## See
- [Template Source Code](./index.hbs)
---

View File

@ -0,0 +1,39 @@
{{#if (and @partition (can 'use partitions'))}}
{{#if (not-eq @item.Partition @partition)}}
<dl class="consul-bucket-list">
<dt
class="partition"
{{tooltip}}
>
Admin Partition
</dt>
<dd>
{{@item.Partition}}
</dd>
<dt
class="nspace"
{{tooltip}}
>
Namespace
</dt>
<dd>
{{@item.Namespace}}
</dd>
</dl>
{{/if}}
{{else if (and @nspace (can 'use nspace'))}}
{{#if (not-eq @item.Namespace @nspace)}}
<dl>
<dt
class="nspace"
{{tooltip}}
>
Namespace
</dt>
<dd>
{{@item.Namespace}}
</dd>
</dl>
{{/if}}
{{/if}}

View File

@ -0,0 +1,26 @@
%consul-bucket-list {
& {
@extend %horizontal-kv-list;
}
.partition::before {
@extend %with-user-team-mask, %as-pseudo;
}
.nspace::before {
@extend %with-folder-outline-mask, %as-pseudo;
}
/* potential for some sort of %composite-kv thing here */
.partition + dd::after {
display: inline-block;
content: '/';
margin: 0 3px;
/*TODO: In isolation this is not needed */
margin-right: 6px;
}
.partition + dd + .nspace {
margin-left: 0 !important;
}
/**/
}
.consul-bucket-list {
@extend %consul-bucket-list;
}

View File

@ -0,0 +1,28 @@
# Consul::Service::List
A presentational component for rendering a list of Consul Services.
```hbs preview-template
<DataSource
@src={{uri "/partition/default/dc-1/gateways/for-service/service-name"}} as |source|>
<Consul::Service::List
@items={{source.data}}
@nspace={{'nspace'}}
@partition={{'partition'}}
/>
</DataSource>
```
## Arguments
| Argument/Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `items` | `array` | | An array of Consul Services |
| `nspace` | `string` | | The name of the current namespace |
| `partition` | `string` | | The name of the current partition |
## See
- [Template Source Code](./index.hbs)
---

View File

@ -25,7 +25,18 @@
</dd>
</dl>
{{#if (gt item.InstanceCount 0)}}
<a data-test-service-name href={{href-to "dc.services.show.index" item.Name}}>
<a
data-test-service-name
href={{href-to "dc.services.show.index" item.Name
params=(if (not-eq item.Partition @partition)
(hash
partition=item.Partition
nspace=item.Namespace
)
(hash)
)
}}
>
{{item.Name}}
</a>
{{else}}
@ -35,20 +46,6 @@
{{/if}}
</BlockSlot>
<BlockSlot @name="details">
{{#if (and nspace (env 'CONSUL_NSPACES_ENABLED'))}}
{{#if (not-eq item.Namespace nspace)}}
<dl class="nspace">
<dt>
<Tooltip>
Namespace
</Tooltip>
</dt>
<dd>
{{item.Namespace}}
</dd>
</dl>
{{/if}}
{{/if}}
<Consul::Kind @item={{item}} />
<Consul::ExternalSource @item={{item}} />
{{#if (and (not-eq item.InstanceCount 0) (and (not-eq item.Kind 'terminating-gateway') (not-eq item.Kind 'ingress-gateway'))) }}
@ -87,6 +84,11 @@
{{/if}}
</dl>
{{/if}}
<Consul::Bucket::List
@item={{item}}
@nspace={{@nspace}}
@partition={{@partition}}
/>
<TagList @item={{item}} />
</BlockSlot>
</ListCollection>

View File

@ -140,11 +140,33 @@ as |key value|}}
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
{{#let
(reject-by 'Partition' @partition @partitions)
as |nonDefaultPartitions|}}
{{#if (gt nonDefaultPartitions.length 0)}}
<Optgroup
@label={{t 'common.brand.consul'}}
>
{{#each @partitions as |partition|}}
<Option class="partition" @value={{partition}} @selected={{contains partition @filter.source.value}}>
{{partition}}
</Option>
{{/each}}
</Optgroup>
{{/if}}
{{/let}}
{{#if (gt @sources.length 0)}}
<Optgroup
@label={{t 'common.search.integrations'}}
>
{{#each @sources as |source|}}
<Option class={{source}} @value={{source}} @selected={{contains source @filter.source.value}}>
{{t (concat "common.brand." source)}}
</Option>
{{/each}}
</Optgroup>
{{/if}}
{{/let}}
</BlockSlot>
</search.Select>

View File

@ -5,40 +5,26 @@
<ul>
{{#each @items as |item|}}
<li>
<div class="header">
<p>
{{item.DestinationName}}
</p>
</div>
<div class="detail">
{{#if (can 'use partitions')}}
{{#if (not-eq item.DestinationType 'prepared_query')}}
<dl class="partition">
<dt
{{tooltip}}
>
Admin Partition
</dt>
<dd>
{{or item.DestinationPartition 'default'}}
</dd>
</dl>
{{/if}}
{{/if}}
{{#if (can 'use nspaces')}}
{{#if (not-eq item.DestinationType 'prepared_query')}}
<dl class="nspace">
<dt
{{tooltip}}
>
Namespace
</dt>
<dd>
{{or item.DestinationNamespace 'default'}}
</dd>
</dl>
{{/if}}
{{#if (not-eq item.DestinationType 'prepared_query')}}
<Consul::Bucket::List
@item={{hash
Namespace=(or item.DestinationNamespace @nspace)
Partition=(or item.DestinationPartition @partition)
}}
@partition={{@partition}}
@nspace={{@nspace}}
/>
{{/if}}
{{#if (and (not-eq item.Datacenter @dc) (not-eq item.Datacenter ""))}}
<dl class="datacenter">
<dt
@ -51,6 +37,7 @@
</dd>
</dl>
{{/if}}
{{#if item.LocalBindSocketPath}}
<dl class="local-bind-socket-path">
<dt>
@ -73,23 +60,26 @@
</dd>
</dl>
{{else}}
{{#if (gt item.LocalBindPort 0)}}
{{#let (concat (or item.LocalBindAddress '127.0.0.1') ':' item.LocalBindPort) as |combinedAddress|}}
<dl class="local-bind-address">
<dt>
Address
</dt>
<dd>
<CopyButton
@value={{combinedAddress}}
@name="Address"
/>
{{combinedAddress}}
</dd>
</dl>
<dl class="local-bind-address">
<dt>
Address
</dt>
<dd>
<CopyButton
@value={{combinedAddress}}
@name="Address"
/>
{{combinedAddress}}
</dd>
</dl>
{{/let}}
{{/if}}
{{/if}}
</div>
</li>
{{/each}}

View File

@ -24,27 +24,21 @@ as |item index|>
{{/if}}
</dd>
</dl>
{{#if (and (can 'use partitions') (not-eq item.Partition @partition))}}
<dl class="partition">
<dt
{{tooltip}}
>
Admin Partition
</dt>
<dd>
{{item.Partition}}
</dd>
</dl>
{{/if}}
<a
data-test-service-name
href={{if (and (can 'use nspaces') (not-eq item.Namespace @nspace))
(href-to 'dc.services.show' @dc item.Name
params=(hash
href={{href-to "dc.services.show" item.Name
params=(if (not-eq item.Partition @partition)
(hash
partition=item.Partition
nspace=item.Namespace
)
(if (not-eq item.Namespace @nspace)
(hash
nspace=item.Namespace
)
(hash)
)
)
(href-to 'dc.services.show' item.Name)
}}
>
{{item.Name}}
@ -56,18 +50,11 @@ as |item index|>
{{/if}}
</BlockSlot>
<BlockSlot @name="details">
{{#if (and (can 'use nspaces') (not-eq item.Namespace @nspace))}}
<dl class="nspace">
<dt
{{tooltip}}
>
Namespace
</dt>
<dd>
{{item.Namespace}}
</dd>
</dl>
{{/if}}
<Consul::Bucket::List
@item={{item}}
@nspace={{@nspace}}
@partition={{@partition}}
/>
{{#each item.GatewayConfig.Addresses as |address|}}
<dl>
<dt>

View File

@ -21,6 +21,8 @@
margin-right: 10px;
}
/* TODO: Consider moving these to their specific search bard componets or */
/* even their own search bar sub menu components */
%popover-select .value-passing button::before {
@extend %with-check-circle-fill-mask, %as-pseudo;
color: rgb(var(--tone-green-500));
@ -37,12 +39,16 @@
@extend %with-minus-square-fill-mask, %as-pseudo;
color: rgb(var(--tone-gray-400));
}
%popover-select.type-source li button {
%popover-select.type-source li:not(.partition) button {
text-transform: capitalize;
}
%popover-select.type-source li.aws button {
text-transform: uppercase;
}
%popover-select.type-source li.partition button::before {
@extend %with-user-team-mask, %as-pseudo;
color: rgb(var(--tone-gray-500));
}
%popover-select .aws button::before {
@extend %with-logo-aws-color-icon, %as-pseudo;
}
@ -68,3 +74,4 @@
%popover-select .terraform button::before {
@extend %with-logo-terraform-color-icon, %as-pseudo;
}
/**/

View File

@ -20,6 +20,9 @@ export default {
'not-registered': (item, value) => item.InstanceCount === 0,
},
source: (item, values) => {
return setHelpers.intersectionSize(values, new Set(item.ExternalSources || [])) !== 0;
return (
setHelpers.intersectionSize(values, new Set(item.ExternalSources || [])) !== 0 ||
values.includes(item.Partition)
);
},
};

View File

@ -15,11 +15,17 @@ export const Collection = class Collection {
}
get ExternalSources() {
const sources = this.items.reduce(function(prev, item) {
const items = this.items.reduce(function(prev, item) {
return prev.concat(item.ExternalSources || []);
}, []);
// unique, non-empty values, alpha sort
return [...new Set(sources)].filter(Boolean).sort();
return [...new Set(items)].filter(Boolean).sort();
}
// TODO: Think about when this/collections is worthwhile using and explain
// when and when not somewhere in the docs
get Partitions() {
// unique, non-empty values, alpha sort
return [...new Set(this.items.map(item => item.Partition))].sort();
}
};
export default class Service extends Model {

View File

@ -75,6 +75,7 @@
@import 'consul-ui/components/consul/loader';
@import 'consul-ui/components/consul/tomography/graph';
@import 'consul-ui/components/consul/discovery-chain';
@import 'consul-ui/components/consul/bucket/list';
@import 'consul-ui/components/consul/upstream/list';
@import 'consul-ui/components/consul/upstream-instance/list';
@import 'consul-ui/components/consul/health-check/list';

View File

@ -53,7 +53,10 @@ as |route|>
(reject-by 'Kind' 'connect-proxy' api.data)
as |sort filters items|}}
(or route.params.partition route.model.user.token.Partition 'default')
(or route.params.nspace route.model.user.token.Namespace 'default')
as |sort filters items partition nspace|}}
<AppView>
<BlockSlot @name="header">
@ -63,9 +66,12 @@ as |sort filters items|}}
<label for="toolbar-toggle"></label>
</BlockSlot>
<BlockSlot @name="toolbar">
{{#if (gt items.length 0) }}
{{#if (gt items.length 0) }}
{{#let (collection items) as |items|}}
<Consul::Service::SearchBar
@sources={{get (collection items) 'ExternalSources'}}
@sources={{get items 'ExternalSources'}}
@partitions={{get items 'Partitions'}}
@partition={{partition}}
@search={{search}}
@onsearch={{action (mut search) value="target.value"}}
@ -75,7 +81,8 @@ as |sort filters items|}}
@filter={{filters}}
/>
{{/if}}
{{/let}}
{{/if}}
</BlockSlot>
<BlockSlot @name="content">
<DataCollection
@ -88,6 +95,7 @@ as |sort filters items|}}
<collection.Collection>
<Consul::Service::List
@items={{collection.items}}
@partition={{partition}}
>
</Consul::Service::List>
</collection.Collection>
@ -115,10 +123,20 @@ as |sort filters items|}}
</BlockSlot>
<BlockSlot @name="actions">
<li class="docs-link">
<a href="{{env 'CONSUL_DOCS_URL'}}/commands/services" rel="noopener noreferrer" target="_blank">Documentation on services</a>
<Action
@href="{{env 'CONSUL_DOCS_URL'}}/commands/services"
@external={{true}}
>
Documentation on services
</Action>
</li>
<li class="learn-link">
<a href="{{env 'CONSUL_DOCS_LEARN_URL'}}/consul/getting-started/services" rel="noopener noreferrer" target="_blank">Read the guide</a>
<Action
@href="{{env 'CONSUL_DOCS_LEARN_URL'}}/consul/getting-started/services"
@external={{true}}
>
Read the guide
</Action>
</li>
</BlockSlot>
</EmptyState>

View File

@ -20,12 +20,14 @@ as |route|>
)
)
(or route.params.partition route.model.user.token.Partition 'default')
(or route.params.nspace route.model.user.token.Namespace 'default')
route.params.dc
route.params.nspace
route.model.proxy
route.model.proxy.Service.Proxy.Upstreams
as |sort filters dc nspace proxy items|}}
as |sort filters partition nspace dc proxy items|}}
{{#if (gt items.length 0)}}
<input type="checkbox" id="toolbar-toggle" />
<Consul::UpstreamInstance::SearchBar

View File

@ -69,7 +69,8 @@ as |route|>
as |collection|>
<collection.Collection>
<Consul::Service::List
@nspace={{nspace}}
@nspace={{or route.params.nspace route.model.user.token.Namespace 'default'}}
@partition={{or route.params.partition route.model.user.token.Partition 'default'}}
@items={{collection.items}}
>
</Consul::Service::List>

View File

@ -42,11 +42,12 @@ as |route|>
)
)
route.params.nspace
(or route.params.partition route.model.user.token.Partition 'default')
(or route.params.nspace route.model.user.token.Namespace 'default')
route.params.dc
loader.data
as |sort filters nspace dc items|}}
as |sort filters partition nspace dc items|}}
{{#if (gt items.length 0)}}
<input type="checkbox" id="toolbar-toggle" />
<Consul::Upstream::SearchBar
@ -74,6 +75,7 @@ as |route|>
@items={{collection.items}}
@dc={{dc}}
@nspace={{nspace}}
@partition={{partition}}
>
</Consul::Upstream::List>
</collection.Collection>

View File

@ -53,7 +53,7 @@ ${typeof location.search.ns !== 'undefined' ? `
"Namespace": "${location.search.ns}",
` : ``}
${typeof location.search.partition !== 'undefined' ? `
"Partition": "${location.search.partition}",
"Partition": "${fake.helpers.randomize([env('CONSUL_PARTITION_EXPORTER', location.search.partition), location.search.partition])}",
` : ``}
"Tags":[
${
@ -132,6 +132,7 @@ ${range(env('CONSUL_UPSTREAM_COUNT', 10)).map((item, j) => `
"Datacenter": "${fake.address.countryCode().toLowerCase()} ${ i % 2 ? "west" : "east"}-${j}",
"DestinationName": "${fake.hacker.noun()}",
"DestinationNamespace": "${fake.hacker.noun()}",
"DestinationPartition": "${fake.hacker.noun()}",
"DestinationType": "${fake.helpers.randomize(['service', 'prepared_query'])}",
${fake.random.number({min: 1, max: 10}) > 5 ? `
"LocalBindAddress": "${fake.internet.ip()}",

View File

@ -17,6 +17,9 @@ ${i === 1 ? `
` : `
"Namespace": "${fake.hacker.noun()}-ns-${i}",
`}
${typeof location.search.partition !== 'undefined' ? `
"Partition": "${fake.helpers.randomize([env('CONSUL_PARTITION_EXPORTER', location.search.partition), location.search.partition])}",
` : ``}
"Tags": [
${
range(env('CONSUL_TAG_COUNT', fake.random.number(10))).map(

View File

@ -47,7 +47,7 @@ ${typeof location.search.ns !== 'undefined' ? `
"Namespace": "${location.search.ns}",
` : ``}
${typeof location.search.partition !== 'undefined' ? `
"Partition": "${location.search.ns}",
"Partition": "${fake.helpers.randomize([env('CONSUL_PARTITION_EXPORTER', location.search.partition), location.search.partition])}",
` : ``}
"Tags": [
${

View File

@ -46,6 +46,7 @@ search:
critical: Failing
in-mesh: In service mesh
not-in-mesh: Not in service mesh
integrations: Integrations
sort:
alpha:
asc: A to Z