ui: Gateway Addresses (#6075)

- Removes 'type' icons (basically the proxy icon, not the text itself)

- Add support for Mesh Gateways plus their addresses
This adds a 'Mesh Gateway' type label to service and service instance
pages, plus a new 'Addresses' tab if the service is a Mesh Gateway
showing a table of addresses for the service - plus tests
This commit is contained in:
John Cowen 2019-07-05 09:07:25 +01:00 committed by GitHub
parent 35a839952b
commit b143a3bb66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 141 additions and 43 deletions

View File

@ -57,7 +57,7 @@ export default Controller.extend(WithEventSource, WithSearching, {
// take that off 50% (100% / number of fluid columns) // take that off 50% (100% / number of fluid columns)
// also we added a Type column which we've currently fixed to 100px // also we added a Type column which we've currently fixed to 100px
// so again divide that by 2 and take it off each fluid column // so again divide that by 2 and take it off each fluid column
return htmlSafe(`width: calc(50% - 50px - ${Math.round(get(this, 'maxWidth') / 2)}px)`); return htmlSafe(`width: calc(50% - ${Math.round(get(this, 'maxWidth') / 2)}px)`);
}), }),
maxPassing: computed('items.[]', function() { maxPassing: computed('items.[]', function() {
return max(get(this, 'items'), 'ChecksPassing'); return max(get(this, 'items'), 'ChecksPassing');

View File

@ -0,0 +1,7 @@
import { helper } from '@ember/component/helper';
export function objectEntries([obj = {}] /*, hash*/) {
return Object.entries(obj);
}
export default helper(objectEntries);

View File

@ -17,6 +17,7 @@ export default Model.extend({
ExternalSources: attr(), ExternalSources: attr(),
Meta: attr(), Meta: attr(),
Address: attr('string'), Address: attr('string'),
TaggedAddresses: attr(),
Port: attr('number'), Port: attr('number'),
EnableTagOverride: attr('boolean'), EnableTagOverride: attr('boolean'),
CreateIndex: attr('number'), CreateIndex: attr('number'),

View File

@ -18,7 +18,8 @@ export default Route.extend({
// connect-proxy or vice versa so leave as is for now // connect-proxy or vice versa so leave as is for now
return hash({ return hash({
proxy: proxy:
get(model.item, 'Kind') === 'connect-proxy' // proxies and mesh-gateways can't have proxies themselves so don't even look
['connect-proxy', 'mesh-gateway'].includes(get(model.item, 'Kind'))
? null ? null
: proxyRepo.findInstanceBySlug(params.id, params.node, params.name, dc), : proxyRepo.findInstanceBySlug(params.id, params.node, params.name, dc),
...model, ...model,

View File

@ -1,7 +1,6 @@
@import './app-view/index'; @import './app-view/index';
@import './filter-bar/index'; @import './filter-bar/index';
@import './buttons/index'; @import './buttons/index';
@import './type-icon/index';
main { main {
@extend %app-view; @extend %app-view;
} }
@ -24,7 +23,11 @@ main {
margin-top: 20px; margin-top: 20px;
} }
%app-view h1 span.kind-proxy { %app-view h1 span.kind-proxy {
@extend %type-icon, %with-proxy; @extend %frame-gray-900;
@extend %pill;
}
%app-view h1 span.kind-proxy::before {
width: 0.3em !important;
} }
%app-view h1 em { %app-view h1 em {
color: $gray-600; color: $gray-600;

View File

@ -1,6 +1,5 @@
@import './icons/index'; @import './icons/index';
@import './table/index'; @import './table/index';
@import './type-icon/index';
html.template-service.template-list td:first-child a span, html.template-service.template-list td:first-child a span,
html.template-node.template-show #services td:first-child a span, html.template-node.template-show #services td:first-child a span,
@ -20,11 +19,6 @@ html.template-service.template-list main th:first-child {
td.folder { td.folder {
@extend %with-folder; @extend %with-folder;
} }
td .kind-proxy {
@extend %type-icon, %with-proxy;
text-indent: -9000px !important;
width: 24px;
}
table:not(.sessions) tbody tr { table:not(.sessions) tbody tr {
cursor: pointer; cursor: pointer;
} }

View File

@ -1,2 +0,0 @@
@import './skin';
@import './layout';

View File

@ -1,5 +0,0 @@
%type-icon {
display: inline-block;
text-indent: 20px;
padding: 3px;
}

View File

@ -1,6 +0,0 @@
%type-icon {
border-radius: 4px;
background: $gray-100;
color: $gray-400;
}

View File

@ -65,8 +65,7 @@ td strong,
%breadcrumbs li > *, %breadcrumbs li > *,
%action-group-action, %action-group-action,
%tab-nav, %tab-nav,
%tooltip-bubble, %tooltip-bubble {
%type-icon {
font-weight: $typo-weight-medium; font-weight: $typo-weight-medium;
} }
@ -113,7 +112,7 @@ caption,
%tooltip-bubble, %tooltip-bubble,
%healthchecked-resource strong, %healthchecked-resource strong,
%footer, %footer,
%type-icon { %app-view h1 span.kind-proxy {
font-size: $typo-size-700; font-size: $typo-size-700;
} }
%toggle label span { %toggle label span {

View File

@ -1,3 +1,4 @@
html.template-instance.template-show #addresses table tr,
html.template-instance.template-show #upstreams table tr { html.template-instance.template-show #upstreams table tr {
cursor: default; cursor: default;
} }

View File

@ -0,0 +1,25 @@
{{#if item.TaggedAddresses }}
{{#tabular-collection
data-test-addresses
items=(object-entries item.TaggedAddresses) as |taggedAddress index|
}}
{{#block-slot 'header'}}
<th>Tag</th>
<th>Address</th>
{{/block-slot}}
{{#block-slot 'row'}}
{{#with (object-at 1 taggedAddress) as |address|}}
<td>
{{object-at 0 taggedAddress}}{{#if (and (eq address.Address item.Address) (eq address.Port item.Port))}}&nbsp;<em data-test-address-default>(default)</em>{{/if}}
</td>
<td data-test-address>
{{address.Address}}:{{address.Port}}
</td>
{{/with}}
{{/block-slot}}
{{/tabular-collection}}
{{else}}
<p>
There are no additional addresses.
</p>
{{/if}}

View File

@ -23,7 +23,6 @@
}} }}
{{#block-slot 'header'}} {{#block-slot 'header'}}
<th style={{remainingWidth}}>Service</th> <th style={{remainingWidth}}>Service</th>
<th>Type</th>
<th style={{totalWidth}}>Health Checks<span><em>The number of health checks for the service on all nodes</em></span></th> <th style={{totalWidth}}>Health Checks<span><em>The number of health checks for the service on all nodes</em></span></th>
<th style={{remainingWidth}}>Tags</th> <th style={{remainingWidth}}>Tags</th>
{{/block-slot}} {{/block-slot}}
@ -34,13 +33,6 @@
{{item.Name}} {{item.Name}}
</a> </a>
</td> </td>
<td>
{{#if (eq item.Kind 'connect-proxy')}}
<span class="kind-proxy">Proxy</span>
{{else}}
&nbsp;
{{/if}}
</td>
<td style={{totalWidth}}> <td style={{totalWidth}}>
{{healthcheck-info {{healthcheck-info
passing=item.ChecksPassing warning=item.ChecksWarning critical=item.ChecksCritical passing=item.ChecksPassing warning=item.ChecksWarning critical=item.ChecksCritical

View File

@ -21,6 +21,8 @@
{{/with}} {{/with}}
{{#if (eq item.Kind 'connect-proxy')}} {{#if (eq item.Kind 'connect-proxy')}}
<span class="kind-proxy">Proxy</span> <span class="kind-proxy">Proxy</span>
{{else if (eq item.Kind 'mesh-gateway')}}
<span class="kind-proxy">Mesh Gateway</span>
{{/if}} {{/if}}
</h1> </h1>
<dl> <dl>
@ -64,10 +66,17 @@
{{tab-nav {{tab-nav
items=(compact items=(compact
(array (array
'Service Checks' 'Service Checks'
'Node Checks' 'Node Checks'
(if (eq item.Kind 'connect-proxy') 'Upstreams' '') (if
'Tags' (eq item.Kind 'connect-proxy')
'Upstreams' ''
)
(if
(eq item.Kind 'mesh-gateway')
'Addresses' ''
)
'Tags'
) )
) )
selected=selectedTab selected=selectedTab
@ -75,10 +84,17 @@
{{#each {{#each
(compact (compact
(array (array
(hash id=(slugify 'Service Checks') partial='dc/services/servicechecks') (hash id=(slugify 'Service Checks') partial='dc/services/servicechecks')
(hash id=(slugify 'Node Checks') partial='dc/services/nodechecks') (hash id=(slugify 'Node Checks') partial='dc/services/nodechecks')
(if (eq item.Kind 'connect-proxy') (hash id=(slugify 'Upstreams') partial='dc/services/upstreams') '') (if
(hash id=(slugify 'Tags') partial='dc/services/tags') (eq item.Kind 'connect-proxy')
(hash id=(slugify 'Upstreams') partial='dc/services/upstreams') ''
)
(if
(eq item.Kind 'mesh-gateway')
(hash id=(slugify 'Addresses') partial='dc/services/addresses') ''
)
(hash id=(slugify 'Tags') partial='dc/services/tags')
) )
) as |panel| ) as |panel|
}} }}

View File

@ -19,6 +19,8 @@
{{/with}} {{/with}}
{{#if (eq item.Service.Kind 'connect-proxy')}} {{#if (eq item.Service.Kind 'connect-proxy')}}
<span class="kind-proxy">Proxy</span> <span class="kind-proxy">Proxy</span>
{{else if (eq item.Service.Kind 'mesh-gateway')}}
<span class="kind-proxy">Mesh Gateway</span>
{{/if}} {{/if}}
</h1> </h1>
<label for="toolbar-toggle"></label> <label for="toolbar-toggle"></label>

View File

@ -0,0 +1,37 @@
@setupApplicationTest
Feature: dc / services / instances / gateway: Show Gateway Service Instance
Scenario: A Gateway Service instance
Given 1 datacenter model with the value "dc1"
And 1 instance model from yaml
---
- Service:
Kind: mesh-gateway
Name: gateway
ID: gateway-with-id
TaggedAddresses:
lan:
Address: 127.0.0.1
Port: 8080
wan:
Address: 92.68.0.0
Port: 8081
---
When I visit the instance page for yaml
---
dc: dc1
service: gateway
node: node-0
id: gateway-with-id
---
Then the url should be /dc1/services/gateway/node-0/gateway-with-id
And I see serviceChecksIsSelected on the tabs
When I click addresses on the tabs
And I see addressesIsSelected on the tabs
And I see 2 of the addresses object
And I see address on the addresses like yaml
---
- 127.0.0.1:8080
- 92.68.0.0:8081
---

View File

@ -0,0 +1,10 @@
import steps from '../../../steps';
// step definitions that are shared between features should be moved to the
// tests/acceptance/steps/steps.js file
export default function(assert) {
return steps(assert).then('I should find a file', function() {
assert.ok(true, this.step);
});
}

View File

@ -0,0 +1,20 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('object-entries', 'helper:object-entries', {
integration: true,
});
// Replace this with your real tests.
test('it renders', function(assert) {
this.set('inputValue', '1234');
this.render(hbs`{{object-entries inputValue}}`);
assert.equal(
this.$()
.text()
.trim(),
Object.entries('1234').toString()
);
});

View File

@ -2,7 +2,7 @@ export default function(visitable, attribute, collection, text, radiogroup) {
return { return {
visit: visitable('/:dc/services/:service/:node/:id'), visit: visitable('/:dc/services/:service/:node/:id'),
externalSource: attribute('data-test-external-source', 'h1 span'), externalSource: attribute('data-test-external-source', 'h1 span'),
tabs: radiogroup('tab', ['service-checks', 'node-checks', 'upstreams', 'tags']), tabs: radiogroup('tab', ['service-checks', 'node-checks', 'addresses', 'upstreams', 'tags']),
serviceChecks: collection('#service-checks [data-test-healthchecks] li', {}), serviceChecks: collection('#service-checks [data-test-healthchecks] li', {}),
nodeChecks: collection('#node-checks [data-test-healthchecks] li', {}), nodeChecks: collection('#node-checks [data-test-healthchecks] li', {}),
upstreams: collection('#upstreams [data-test-tabular-row]', { upstreams: collection('#upstreams [data-test-tabular-row]', {
@ -11,6 +11,9 @@ export default function(visitable, attribute, collection, text, radiogroup) {
type: text('[data-test-destination-type]'), type: text('[data-test-destination-type]'),
address: text('[data-test-local-bind-address]'), address: text('[data-test-local-bind-address]'),
}), }),
addresses: collection('#addresses [data-test-tabular-row]', {
address: text('[data-test-address]'),
}),
proxy: { proxy: {
type: attribute('data-test-proxy-type', '[data-test-proxy-type]'), type: attribute('data-test-proxy-type', '[data-test-proxy-type]'),
destination: attribute('data-test-proxy-destination', '[data-test-proxy-destination]'), destination: attribute('data-test-proxy-destination', '[data-test-proxy-destination]'),