diff --git a/.changelog/11903.txt b/.changelog/11903.txt new file mode 100644 index 0000000000..4b9baa6fa8 --- /dev/null +++ b/.changelog/11903.txt @@ -0,0 +1,4 @@ +```release-note:bug +ui: Fixes a bug where proxy service health checks would sometimes not appear +until refresh +``` diff --git a/ui/packages/consul-ui/app/components/outlet/index.js b/ui/packages/consul-ui/app/components/outlet/index.js index 3792e5ebd1..4910f1d1bf 100644 --- a/ui/packages/consul-ui/app/components/outlet/index.js +++ b/ui/packages/consul-ui/app/components/outlet/index.js @@ -28,6 +28,10 @@ export default class Outlet extends Component { return this.args.model || {}; } + get name() { + return this.args.name; + } + setAppRoute(name) { if (name !== 'loading' || name === 'oidc-provider-debug') { const doc = this.element.ownerDocument.documentElement; @@ -55,7 +59,9 @@ export default class Outlet extends Component { } break; case 'model': - this.route._model = this.args.model; + if(typeof this.route !== 'undefined') { + this.route._model = value; + } break; } } diff --git a/ui/packages/consul-ui/app/components/route/index.hbs b/ui/packages/consul-ui/app/components/route/index.hbs index d3ea2f07c3..783e62db54 100644 --- a/ui/packages/consul-ui/app/components/route/index.hbs +++ b/ui/packages/consul-ui/app/components/route/index.hbs @@ -1,7 +1,7 @@ {{did-insert this.connect}} {{will-destroy this.disconnect}} {{yield (hash - model=(or this.model this._model) + model=this.model params=this.params currentName=this.router.currentRoute.name diff --git a/ui/packages/consul-ui/app/components/route/index.js b/ui/packages/consul-ui/app/components/route/index.js index cae255e6ca..d484560c1c 100644 --- a/ui/packages/consul-ui/app/components/route/index.js +++ b/ui/packages/consul-ui/app/components/route/index.js @@ -14,17 +14,14 @@ export default class RouteComponent extends Component { } get model() { - if(this.args.name) { - const temp = this.args.name.split('.'); - temp.pop(); - const name = temp.join('.'); - let model = this.routlet.modelFor(name); - if(Object.keys(model).length === 0) { - return null; - } - return model; + if(this._model) { + return this._model; } - return null; + if (this.args.name) { + const outlet = this.routlet.outletFor(this.args.name); + return this.routlet.modelFor(outlet.name); + } + return undefined; } @action diff --git a/ui/packages/consul-ui/app/filter/predicates/service-instance.js b/ui/packages/consul-ui/app/filter/predicates/service-instance.js index 76111c5760..c55f9e79e3 100644 --- a/ui/packages/consul-ui/app/filter/predicates/service-instance.js +++ b/ui/packages/consul-ui/app/filter/predicates/service-instance.js @@ -5,7 +5,7 @@ export default { passing: (item, value) => item.Status === value, warning: (item, value) => item.Status === value, critical: (item, value) => item.Status === value, - empty: (item, value) => item.MeshChecks.length === 0, + empty: (item, value) => item.ServiceChecks.length === 0, }, source: (item, values) => { return setHelpers.intersectionSize(values, new Set(item.ExternalSources || [])) !== 0; diff --git a/ui/packages/consul-ui/app/helpers/merge-checks.js b/ui/packages/consul-ui/app/helpers/merge-checks.js new file mode 100644 index 0000000000..ffa7646402 --- /dev/null +++ b/ui/packages/consul-ui/app/helpers/merge-checks.js @@ -0,0 +1,9 @@ +import { helper } from '@ember/component/helper'; +import mergeChecks from 'consul-ui/utils/merge-checks'; + +export default helper(function([checks, exposed], hash) { + return mergeChecks( + checks, + exposed + ); +}); diff --git a/ui/packages/consul-ui/app/models/proxy.js b/ui/packages/consul-ui/app/models/proxy.js index 2763452670..2e0ae12d28 100644 --- a/ui/packages/consul-ui/app/models/proxy.js +++ b/ui/packages/consul-ui/app/models/proxy.js @@ -5,7 +5,7 @@ export const PRIMARY_KEY = 'uid'; export const SLUG_KEY = 'Node,ServiceID'; // TODO: This should be changed to ProxyInstance -export default class Proxy extends ServiceInstanceModel { +export default class ProxyServiceInstance extends ServiceInstanceModel { @attr('string') uid; @attr('string') ID; diff --git a/ui/packages/consul-ui/app/models/service-instance.js b/ui/packages/consul-ui/app/models/service-instance.js index 05769fc4f6..c69d302002 100644 --- a/ui/packages/consul-ui/app/models/service-instance.js +++ b/ui/packages/consul-ui/app/models/service-instance.js @@ -1,9 +1,8 @@ -import Model, { attr, belongsTo } from '@ember-data/model'; +import Model, { attr } from '@ember-data/model'; import { fragmentArray } from 'ember-data-model-fragments/attributes'; -import { computed, get } from '@ember/object'; +import { computed } from '@ember/object'; import { or, filter, alias } from '@ember/object/computed'; import { tracked } from '@glimmer/tracking'; -import mergeChecks from 'consul-ui/utils/merge-checks'; export const PRIMARY_KEY = 'uid'; export const SLUG_KEY = 'Node.Node,Service.ID'; @@ -28,8 +27,6 @@ export default class ServiceInstance extends Model { @attr('string') uid; @attr('string') Datacenter; - // ProxyInstance is the ember-data model relationship - @belongsTo('Proxy') ProxyInstance; // Proxy is the actual JSON api response @attr() Proxy; @attr() Node; @@ -55,18 +52,6 @@ export default class ServiceInstance extends Model { @filter('Checks.@each.Kind', (item, i, arr) => item.Kind === 'service') ServiceChecks; @filter('Checks.@each.Kind', (item, i, arr) => item.Kind === 'node') NodeChecks; - // MeshChecks are a concatenation of Checks for the Instance and Checks for - // the ProxyInstance. - @computed('Checks.[]', 'ProxyInstance.{Checks.[],ServiceProxy.Expose.Checks}') - get MeshChecks() { - // merge the instance and proxy checks together, avoiding duplicate node - // checks and additionally setting any checks to exposed if required - return mergeChecks( - [get(this, 'Checks'), get(this, 'ProxyInstance.Checks')], - get(this, 'ProxyInstance.ServiceProxy.Expose.Checks') - ); - } - @computed('Service.Meta') get ExternalSources() { const sources = Object.entries(this.Service.Meta || {}) diff --git a/ui/packages/consul-ui/app/services/repository/proxy.js b/ui/packages/consul-ui/app/services/repository/proxy.js index 8aed43bdb4..4032945aab 100644 --- a/ui/packages/consul-ui/app/services/repository/proxy.js +++ b/ui/packages/consul-ui/app/services/repository/proxy.js @@ -36,24 +36,24 @@ export default class ProxyService extends RepositoryService { } @dataSource('/:partition/:ns/:dc/proxy-instance/:serviceId/:node/:id') - findInstanceBySlug(params, configuration) { - return this.findAllBySlug(params, configuration).then(function(items) { - let res = {}; - if (get(items, 'length') > 0) { - let instance = items - .filterBy('ServiceProxy.DestinationServiceID', params.serviceId) - .findBy('NodeName', params.node); + async findInstanceBySlug(params, configuration) { + const items = await this.findAllBySlug(params, configuration); + + let res = {}; + if (get(items, 'length') > 0) { + let instance = items + .filterBy('ServiceProxy.DestinationServiceID', params.serviceId) + .findBy('NodeName', params.node); + if (instance) { + res = instance; + } else { + instance = items.findBy('ServiceProxy.DestinationServiceName', params.id); if (instance) { res = instance; - } else { - instance = items.findBy('ServiceProxy.DestinationServiceName', params.id); - if (instance) { - res = instance; - } } } - set(res, 'meta', get(items, 'meta')); - return res; - }); + } + set(res, 'meta', get(items, 'meta')); + return res; } } diff --git a/ui/packages/consul-ui/app/services/repository/service-instance.js b/ui/packages/consul-ui/app/services/repository/service-instance.js index d69fc067de..749262abce 100644 --- a/ui/packages/consul-ui/app/services/repository/service-instance.js +++ b/ui/packages/consul-ui/app/services/repository/service-instance.js @@ -1,12 +1,10 @@ import RepositoryService from 'consul-ui/services/repository'; -import { inject as service } from '@ember/service'; import { set } from '@ember/object'; import { ACCESS_READ } from 'consul-ui/abilities/base'; import dataSource from 'consul-ui/decorators/data-source'; const modelName = 'service-instance'; export default class ServiceInstanceService extends RepositoryService { - @service('repository/proxy') proxyRepo; getModelName() { return modelName; } @@ -34,44 +32,6 @@ export default class ServiceInstanceService extends RepositoryService { @dataSource('/:partition/:ns/:dc/service-instance/:serviceId/:node/:id') async findBySlug(params, configuration = {}) { - if (typeof configuration.cursor !== 'undefined') { - params.index = configuration.cursor; - params.uri = configuration.uri; - } - return this.authorizeBySlug( - async () => this.store.queryRecord(this.getModelName(), params), - ACCESS_READ, - params - ); - } - - @dataSource('/:partition/:ns/:dc/proxy-service-instance/:serviceId/:node/:id') - async findProxyBySlug(params, configuration = {}) { - const instance = await this.findBySlug(...arguments); - let proxy = this.store.peekRecord('proxy', instance.uid); - // Currently, we call the proxy endpoint before this endpoint - // therefore proxy is never undefined. If we ever call this endpoint - // first we'll need to do something like the following - // if(typeof proxy === 'undefined') { - // await proxyRepo.create({}) - // } - - // Copy over all the things to the ProxyServiceInstance - ['Service', 'Node', 'meta'].forEach(prop => { - set(proxy, prop, instance[prop]); - }); - ['Checks'].forEach(prop => { - // completely wipe out any previous values so we don't accumulate things - // eternally - proxy.set(prop, []); - instance[prop].forEach(item => { - if (typeof item !== 'undefined') { - proxy[prop].addFragment(item.copy()); - } - }); - }); - // delete the ServiceInstance record as we now have a ProxyServiceInstance - instance.unloadRecord(); - return proxy; + return super.findBySlug(...arguments); } } diff --git a/ui/packages/consul-ui/app/services/routlet.js b/ui/packages/consul-ui/app/services/routlet.js index b743e51b6e..4ff011158e 100644 --- a/ui/packages/consul-ui/app/services/routlet.js +++ b/ui/packages/consul-ui/app/services/routlet.js @@ -75,21 +75,11 @@ export default class RoutletService extends Service { return key; } - addOutlet(name, outlet) { - outlets.set(name, outlet); - } - - removeOutlet(name) { - outlets.delete(name); - } - - // modelFor gets the model for Outlet specified by `name`, not the Route - modelFor(name) { - const outlet = outlets.get(name); - if (typeof outlet !== 'undefined') { - return outlet.model || {}; - } - return {}; + outletFor(routeName) { + const keys = [...outlets.keys()]; + const pos = keys.indexOf(routeName); + const key = pos + 1; + return outlets.get(keys[key]); } /** @@ -149,20 +139,43 @@ export default class RoutletService extends Service { }; } - addRoute(name, route) { - const keys = [...outlets.keys()]; - const pos = keys.indexOf(name); - const key = pos + 1; - const outlet = outlets.get(keys[key]); + + // modelFor gets the model for Outlet specified by `name`, not the Route + modelFor(name) { + const outlet = outlets.get(name); + if (typeof outlet !== 'undefined') { + return outlet.model; + } + } + + addRoute(name, route) { + const outlet = this.outletFor(name); if (typeof outlet !== 'undefined') { - route._model = outlet.model; outlet.route = route; // TODO: Try to avoid the double computation bug schedule('afterRender', () => { - outlet.routeName = route.args.name; + outlet.routeName = name; }); } } - removeRoute(name, route) {} + removeRoute(name, route) { + const outlet = this.outletFor(name); + route._model = undefined; + if (typeof outlet !== 'undefined') { + schedule('afterRender', () => { + outlet.route = undefined; + }); + } + } + + addOutlet(name, outlet) { + outlets.set(name, outlet); + } + + removeOutlet(name) { + schedule('afterRender', () => { + outlets.delete(name); + }); + } } diff --git a/ui/packages/consul-ui/app/templates/dc/services/instance.hbs b/ui/packages/consul-ui/app/templates/dc/services/instance.hbs index cab1312781..052ee14b15 100644 --- a/ui/packages/consul-ui/app/templates/dc/services/instance.hbs +++ b/ui/packages/consul-ui/app/templates/dc/services/instance.hbs @@ -94,6 +94,7 @@ as |item|}} name=route.params.name ) }} + @onchange={{action (mut meta) value="data"}} as |meta|> {{! We only really need meta to get the correct ServiceID }} {{! but we may as well use the NodeName and ServiceName }} @@ -101,8 +102,13 @@ as |item|}} {{! so if we can ever get ServiceID from elsewhere we could save }} {{! a HTTP request/long poll here }} {{#if meta.data.ServiceID}} + {{! if we have a proxy then get the additional instance information }} + {{! for the proxy itself so if the service is called `backend` }} + {{! its likely to have a proxy service called `backend-sidecar-proxy` }} + {{! and this second request get the info for that instance and saves }} + {{! it into the `proxy` variable }} + @onchange={{action (mut proxy) value="data"}} + /> {{/if}} {{/if}} @@ -129,7 +136,9 @@ as |item|}} - {{#if (eq proxy.ServiceProxy.Mode 'transparent')}} + {{! TODO: Looks like we can get this straight from item.Proxy.Mode }} + {{! the less we need `proxy` and `meta` the better }} + {{#if (eq meta.ServiceProxy.Mode 'transparent')}} {{/if}} @@ -149,27 +158,22 @@ as |item|}} {{/let}} - + diff --git a/ui/packages/consul-ui/app/templates/dc/services/instance/addresses.hbs b/ui/packages/consul-ui/app/templates/dc/services/instance/addresses.hbs index bc42b919f6..45980f3ab7 100644 --- a/ui/packages/consul-ui/app/templates/dc/services/instance/addresses.hbs +++ b/ui/packages/consul-ui/app/templates/dc/services/instance/addresses.hbs @@ -2,14 +2,14 @@ @name={{routeName}} as |route|> {{#let - route.model.item -as |item|}} + (entries route.model.item.Service.TaggedAddresses) +as |items|}}
- {{#if item.Service.TaggedAddresses }} + {{#if (gt items.length 0)}} Tag diff --git a/ui/packages/consul-ui/app/templates/dc/services/instance/exposedpaths.hbs b/ui/packages/consul-ui/app/templates/dc/services/instance/exposedpaths.hbs index cae11c18bb..df7e723d6a 100644 --- a/ui/packages/consul-ui/app/templates/dc/services/instance/exposedpaths.hbs +++ b/ui/packages/consul-ui/app/templates/dc/services/instance/exposedpaths.hbs @@ -3,13 +3,17 @@ as |route|> {{#let route.model.proxy -as |proxy|}} + route.model.meta +as |item proxy|}}
- {{#if (gt proxy.Service.Proxy.Expose.Paths.length 0)}} + {{#if (gt proxy.ServiceProxy.Expose.Paths.length 0)}}

- The following list shows individual HTTP paths exposed through Envoy for external services like Prometheus. Read more about this in our documentation. + The following list shows individual HTTP paths exposed through Envoy for external services like Prometheus. Read more about this in our documentation.

- + {{else}} diff --git a/ui/packages/consul-ui/app/templates/dc/services/instance/healthchecks.hbs b/ui/packages/consul-ui/app/templates/dc/services/instance/healthchecks.hbs index 1e3648b5f1..454e92f1a6 100644 --- a/ui/packages/consul-ui/app/templates/dc/services/instance/healthchecks.hbs +++ b/ui/packages/consul-ui/app/templates/dc/services/instance/healthchecks.hbs @@ -27,7 +27,7 @@ as |route|> ) ) - route.model.item.MeshChecks + (merge-checks (array route.model.item.Checks route.model.proxy.Checks) route.model.proxy.ServiceProxy.Expose.Checks) as |sort filters items|}}
diff --git a/ui/packages/consul-ui/app/templates/dc/services/instance/upstreams.hbs b/ui/packages/consul-ui/app/templates/dc/services/instance/upstreams.hbs index 526073d51c..30830ca4ca 100644 --- a/ui/packages/consul-ui/app/templates/dc/services/instance/upstreams.hbs +++ b/ui/packages/consul-ui/app/templates/dc/services/instance/upstreams.hbs @@ -25,9 +25,10 @@ as |route|> route.params.dc route.model.proxy + route.model.meta route.model.proxy.Service.Proxy.Upstreams - as |sort filters partition nspace dc proxy items|}} + as |sort filters partition nspace dc proxy meta items|}} {{#if (gt items.length 0)}} @filter={{filters}} /> {{/if}} - {{#if (eq proxy.ServiceProxy.Mode 'transparent')}} - - -

{{t "routes.dc.services.instance.upstreams.tproxy-mode.header"}}

-
- -

- {{t "routes.dc.services.instance.upstreams.tproxy-mode.body"}} -

-
- -

- - {{t "routes.dc.services.instance.upstreams.tproxy-mode.footer"}} - -

-
-
+ {{! TODO: Looks like we can get this straight from item.Proxy.Mode }} + {{! the less we need `proxy` and `meta` the better }} + {{#if (eq meta.ServiceProxy.Mode 'transparent')}} + + +

+ {{t "routes.dc.services.instance.upstreams.tproxy-mode.header"}} +

+
+ +

+ {{t "routes.dc.services.instance.upstreams.tproxy-mode.body"}} +

+
+ +

+ + {{t "routes.dc.services.instance.upstreams.tproxy-mode.footer"}} + +

+
+
{{/if}} ` `)} ] }, - "Mode": "${fake.helpers.randomize(['', 'direct', 'transparent'])}", + "Mode": "${env('CONSUL_TPROXY_ENABLE') ? `transparent` : fake.helpers.randomize(['', 'direct', 'transparent'])}", "TransparentProxy": {}, "DestinationServiceName": "${location.pathname.slice(4)}" ${ location.pathname.slice(4) === "service-0" ? ` diff --git a/ui/packages/consul-ui/mock-api/v1/catalog/connect/backend b/ui/packages/consul-ui/mock-api/v1/catalog/connect/backend new file mode 100644 index 0000000000..87989f145e --- /dev/null +++ b/ui/packages/consul-ui/mock-api/v1/catalog/connect/backend @@ -0,0 +1,82 @@ +[ + { + "ID": "237b9eeb-bba1-e6e3-3c99-c527d6d76cc0", + "Node": "node", + "Address": "172.17.0.2", + "Datacenter": "dc1", + "TaggedAddresses": { + "lan": "172.17.0.2", + "lan_ipv4": "172.17.0.2", + "wan": "172.17.0.2", + "wan_ipv4": "172.17.0.2" + }, + "NodeMeta": { + "consul-network-segment": "" + }, + "ServiceKind": "connect-proxy", + "ServiceID": "backend-sidecar-proxy", + "ServiceName": "backend-sidecar-proxy", + "ServiceTags": [], + "ServiceAddress": "", + "ServiceWeights": { + "Passing": 1, + "Warning": 1 + }, + "ServiceMeta": {}, + "ServicePort": 21000, + "ServiceSocketPath": "", + "ServiceEnableTagOverride": false, + "ServiceProxy": { + "DestinationServiceName": "backend", + "DestinationServiceID": "backend", + "LocalServiceAddress": "127.0.0.1", + "LocalServicePort": 7000, + "Mode": "", + "MeshGateway": {}, + "Expose": {} + }, + "ServiceConnect": {}, + "CreateIndex": 19, + "ModifyIndex": 19 + }, + { + "ID": "237b9eeb-bba1-e6e3-3c99-c527d6d76cc0", + "Node": "node", + "Address": "172.17.0.2", + "Datacenter": "dc1", + "TaggedAddresses": { + "lan": "172.17.0.2", + "lan_ipv4": "172.17.0.2", + "wan": "172.17.0.2", + "wan_ipv4": "172.17.0.2" + }, + "NodeMeta": { + "consul-network-segment": "" + }, + "ServiceKind": "connect-proxy", + "ServiceID": "backend-v2-sidecar-proxy", + "ServiceName": "backend-sidecar-proxy", + "ServiceTags": [], + "ServiceAddress": "", + "ServiceWeights": { + "Passing": 1, + "Warning": 1 + }, + "ServiceMeta": {}, + "ServicePort": 21001, + "ServiceSocketPath": "", + "ServiceEnableTagOverride": false, + "ServiceProxy": { + "DestinationServiceName": "backend", + "DestinationServiceID": "backend-v2", + "LocalServiceAddress": "127.0.0.1", + "LocalServicePort": 7001, + "Mode": "", + "MeshGateway": {}, + "Expose": {} + }, + "ServiceConnect": {}, + "CreateIndex": 21, + "ModifyIndex": 21 + } +] diff --git a/ui/packages/consul-ui/mock-api/v1/health/service/backend b/ui/packages/consul-ui/mock-api/v1/health/service/backend new file mode 100644 index 0000000000..40b1cbbac4 --- /dev/null +++ b/ui/packages/consul-ui/mock-api/v1/health/service/backend @@ -0,0 +1,122 @@ +[ + { + "Node": { + "ID": "237b9eeb-bba1-e6e3-3c99-c527d6d76cc0", + "Node": "node", + "Address": "172.17.0.2", + "Datacenter": "dc1", + "TaggedAddresses": { + "lan": "172.17.0.2", + "lan_ipv4": "172.17.0.2", + "wan": "172.17.0.2", + "wan_ipv4": "172.17.0.2" + }, + "Meta": { + "consul-network-segment": "" + }, + "CreateIndex": 14, + "ModifyIndex": 17 + }, + "Service": { + "ID": "backend", + "Service": "backend", + "Tags": [], + "Address": "", + "Meta": null, + "Port": 7000, + "Weights": { + "Passing": 1, + "Warning": 1 + }, + "EnableTagOverride": false, + "Proxy": { + "Mode": "", + "MeshGateway": {}, + "Expose": {} + }, + "Connect": {}, + "CreateIndex": 18, + "ModifyIndex": 18 + }, + "Checks": [ + { + "Node": "node", + "CheckID": "serfHealth", + "Name": "Serf Health Status", + "Status": "passing", + "Notes": "", + "Output": "Agent alive and reachable", + "ServiceID": "", + "ServiceName": "", + "ServiceTags": [], + "Type": "", + "Interval": "", + "Timeout": "", + "ExposedPort": 0, + "Definition": {}, + "CreateIndex": 14, + "ModifyIndex": 14 + } + ] + }, + { + "Node": { + "ID": "237b9eeb-bba1-e6e3-3c99-c527d6d76cc0", + "Node": "node", + "Address": "172.17.0.2", + "Datacenter": "dc1", + "TaggedAddresses": { + "lan": "172.17.0.2", + "lan_ipv4": "172.17.0.2", + "wan": "172.17.0.2", + "wan_ipv4": "172.17.0.2" + }, + "Meta": { + "consul-network-segment": "" + }, + "CreateIndex": 14, + "ModifyIndex": 17 + }, + "Service": { + "ID": "backend-v2", + "Service": "backend", + "Tags": [], + "Address": "", + "Meta": null, + "Port": 7001, + "Weights": { + "Passing": 1, + "Warning": 1 + }, + "EnableTagOverride": false, + "Proxy": { + "Mode": "", + "MeshGateway": {}, + "Expose": {} + }, + "Connect": {}, + "CreateIndex": 20, + "ModifyIndex": 20 + }, + "Checks": [ + { + "Node": "node", + "CheckID": "serfHealth", + "Name": "Serf Health Status", + "Status": "passing", + "Notes": "", + "Output": "Agent alive and reachable", + "ServiceID": "", + "ServiceName": "", + "ServiceTags": [], + "Type": "", + "Interval": "", + "Timeout": "", + "ExposedPort": 0, + "Definition": {}, + "CreateIndex": 14, + "ModifyIndex": 14 + } + ] + } +] diff --git a/ui/packages/consul-ui/mock-api/v1/health/service/backend-sidecar-proxy b/ui/packages/consul-ui/mock-api/v1/health/service/backend-sidecar-proxy new file mode 100644 index 0000000000..f1a2bdfe48 --- /dev/null +++ b/ui/packages/consul-ui/mock-api/v1/health/service/backend-sidecar-proxy @@ -0,0 +1,204 @@ +[ + { + "Node": { + "ID": "237b9eeb-bba1-e6e3-3c99-c527d6d76cc0", + "Node": "node", + "Address": "172.17.0.2", + "Datacenter": "dc1", + "TaggedAddresses": { + "lan": "172.17.0.2", + "lan_ipv4": "172.17.0.2", + "wan": "172.17.0.2", + "wan_ipv4": "172.17.0.2" + }, + "Meta": { + "consul-network-segment": "" + }, + "CreateIndex": 14, + "ModifyIndex": 17 + }, + "Service": { + "Kind": "connect-proxy", + "ID": "backend-sidecar-proxy", + "Service": "backend-sidecar-proxy", + "Tags": [], + "Address": "", + "Meta": null, + "Port": 21000, + "Weights": { + "Passing": 1, + "Warning": 1 + }, + "EnableTagOverride": false, + "Proxy": { + "DestinationServiceName": "backend", + "DestinationServiceID": "backend", + "LocalServiceAddress": "127.0.0.1", + "LocalServicePort": 7000, + "Mode": "", + "MeshGateway": {}, + "Expose": {} + }, + "Connect": {}, + "CreateIndex": 19, + "ModifyIndex": 19 + }, + "Checks": [ + { + "Node": "node", + "CheckID": "serfHealth", + "Name": "Serf Health Status", + "Status": "passing", + "Notes": "", + "Output": "Agent alive and reachable", + "ServiceID": "", + "ServiceName": "", + "ServiceTags": [], + "Type": "", + "Interval": "", + "Timeout": "", + "ExposedPort": 0, + "Definition": {}, + "CreateIndex": 14, + "ModifyIndex": 14 + }, + { + "Node": "node", + "CheckID": "service:backend-sidecar-proxy:1", + "Name": "Connect Sidecar Listening", + "Status": "critical", + "Notes": "", + "Output": "dial tcp 127.0.0.1:21000: connect: connection refused", + "ServiceID": "backend-sidecar-proxy", + "ServiceName": "backend-sidecar-proxy", + "ServiceTags": [], + "Type": "tcp", + "Interval": "", + "Timeout": "", + "ExposedPort": 0, + "Definition": {}, + "CreateIndex": 19, + "ModifyIndex": 42 + }, + { + "Node": "node", + "CheckID": "service:backend-sidecar-proxy:2", + "Name": "Connect Sidecar Aliasing backend", + "Status": "critical", + "Notes": "", + "Output": "No checks found.", + "ServiceID": "backend-sidecar-proxy", + "ServiceName": "backend-sidecar-proxy", + "ServiceTags": [], + "Type": "alias", + "Interval": "", + "Timeout": "", + "ExposedPort": 0, + "Definition": {}, + "CreateIndex": 19, + "ModifyIndex": 19 + } + ] + }, + { + "Node": { + "ID": "237b9eeb-bba1-e6e3-3c99-c527d6d76cc0", + "Node": "node", + "Address": "172.17.0.2", + "Datacenter": "dc1", + "TaggedAddresses": { + "lan": "172.17.0.2", + "lan_ipv4": "172.17.0.2", + "wan": "172.17.0.2", + "wan_ipv4": "172.17.0.2" + }, + "Meta": { + "consul-network-segment": "" + }, + "CreateIndex": 14, + "ModifyIndex": 17 + }, + "Service": { + "Kind": "connect-proxy", + "ID": "backend-v2-sidecar-proxy", + "Service": "backend-sidecar-proxy", + "Tags": [], + "Address": "", + "Meta": null, + "Port": 21001, + "Weights": { + "Passing": 1, + "Warning": 1 + }, + "EnableTagOverride": false, + "Proxy": { + "DestinationServiceName": "backend", + "DestinationServiceID": "backend-v2", + "LocalServiceAddress": "127.0.0.1", + "LocalServicePort": 7001, + "Mode": "", + "MeshGateway": {}, + "Expose": {} + }, + "Connect": {}, + "CreateIndex": 21, + "ModifyIndex": 21 + }, + "Checks": [ + { + "Node": "node", + "CheckID": "serfHealth", + "Name": "Serf Health Status", + "Status": "passing", + "Notes": "", + "Output": "Agent alive and reachable", + "ServiceID": "", + "ServiceName": "", + "ServiceTags": [], + "Type": "", + "Interval": "", + "Timeout": "", + "ExposedPort": 0, + "Definition": {}, + "CreateIndex": 14, + "ModifyIndex": 14 + }, + { + "Node": "node", + "CheckID": "service:backend-v2-sidecar-proxy:1", + "Name": "Connect Sidecar Listening", + "Status": "critical", + "Notes": "", + "Output": "dial tcp 127.0.0.1:21001: connect: connection refused", + "ServiceID": "backend-v2-sidecar-proxy", + "ServiceName": "backend-sidecar-proxy", + "ServiceTags": [], + "Type": "tcp", + "Interval": "", + "Timeout": "", + "ExposedPort": 0, + "Definition": {}, + "CreateIndex": 21, + "ModifyIndex": 44 + }, + { + "Node": "node", + "CheckID": "service:backend-v2-sidecar-proxy:2", + "Name": "Connect Sidecar Aliasing backend-v2", + "Status": "passing", + "Notes": "", + "Output": "No checks found.", + "ServiceID": "backend-v2-sidecar-proxy", + "ServiceName": "backend-sidecar-proxy", + "ServiceTags": [], + "Type": "alias", + "Interval": "", + "Timeout": "", + "ExposedPort": 0, + "Definition": {}, + "CreateIndex": 21, + "ModifyIndex": 21 + } + ] + } +] diff --git a/ui/packages/consul-ui/tests/integration/services/routlet-test.js b/ui/packages/consul-ui/tests/integration/services/routlet-test.js new file mode 100644 index 0000000000..c0faa5f408 --- /dev/null +++ b/ui/packages/consul-ui/tests/integration/services/routlet-test.js @@ -0,0 +1,32 @@ +import { moduleFor, test } from 'ember-qunit'; +moduleFor('service:routlet', 'Integration | Routlet', { + // Specify the other units that are required for this test. + integration: true, +}); +test('outletFor works', function(assert) { + const routlet = this.subject(); + routlet.addOutlet('application', { + name: 'application' + }); + routlet.addRoute('dc', {}); + routlet.addOutlet('dc', { + name: 'dc' + }); + routlet.addRoute('dc.services', {}); + routlet.addOutlet('dc.services', { + name: 'dc.services' + }); + routlet.addRoute('dc.services.instances', {}); + + let actual = routlet.outletFor('dc.services'); + let expected = 'dc'; + assert.equal(actual.name, expected); + + actual = routlet.outletFor('dc'); + expected = 'application'; + assert.equal(actual.name, expected); + + actual = routlet.outletFor('application'); + expected = undefined; + assert.equal(actual, expected); +});