From 9d9e77f417f0a69fa817d11cf48b7093c5d5c00e Mon Sep 17 00:00:00 2001 From: John Cowen Date: Wed, 2 Dec 2020 15:42:18 +0000 Subject: [PATCH] ui: Asyncify Service Model Hooks (#9312) * ui: Add Kind grouping computed properties * Use new computed properties and move model hooks to async methods --- .../consul-ui/app/models/service-instance.js | 21 +++++ .../consul-ui/app/routes/dc/services/index.js | 14 ++-- .../app/routes/dc/services/instance.js | 76 ++++++++--------- .../consul-ui/app/routes/dc/services/show.js | 81 +++++++++---------- .../app/routes/dc/services/show/services.js | 16 ++-- 5 files changed, 108 insertions(+), 100 deletions(-) diff --git a/ui/packages/consul-ui/app/models/service-instance.js b/ui/packages/consul-ui/app/models/service-instance.js index 8703996a76..2be6ece67b 100644 --- a/ui/packages/consul-ui/app/models/service-instance.js +++ b/ui/packages/consul-ui/app/models/service-instance.js @@ -38,6 +38,27 @@ export default class ServiceInstance extends Model { return [...new Set(sources)]; } + @computed('Service.Kind') + get IsProxy() { + return ['connect-proxy', 'mesh-gateway', 'ingress-gateway', 'terminating-gateway'].includes( + this.Service.Kind + ); + } + + // IsOrigin means that the service can have associated up or down streams, + // this service being the origin point of those streams + @computed('Service.Kind') + get IsOrigin() { + return !['connect-proxy', 'mesh-gateway'].includes(this.Service.Kind); + } + + // IsMeshOrigin means that the service can have associated up or downstreams + // that are in the Consul mesh itself + @computed('IsOrigin') + get IsMeshOrigin() { + return this.IsOrigin && !['terminating-gateway'].includes(this.Service.Kind); + } + @computed('ChecksPassing', 'ChecksWarning', 'ChecksCritical') get Status() { switch (true) { diff --git a/ui/packages/consul-ui/app/routes/dc/services/index.js b/ui/packages/consul-ui/app/routes/dc/services/index.js index e63dc91e56..3cbb1cce34 100644 --- a/ui/packages/consul-ui/app/routes/dc/services/index.js +++ b/ui/packages/consul-ui/app/routes/dc/services/index.js @@ -1,6 +1,5 @@ import { inject as service } from '@ember/service'; import Route from 'consul-ui/routing/route'; -import { hash } from 'rsvp'; export default class IndexRoute extends Route { @service('data-source/service') data; @@ -20,14 +19,15 @@ export default class IndexRoute extends Route { }, }; - model(params) { + async model(params, transition) { const nspace = this.modelFor('nspace').nspace.substr(1); const dc = this.modelFor('dc').dc.Name; - return hash({ - nspace: nspace, - dc: dc, - items: this.data.source(uri => uri`/${nspace}/${dc}/services`), - }); + const items = await this.data.source(uri => uri`/${nspace}/${dc}/services`); + return { + dc, + nspace, + items, + }; } setupController(controller, model) { diff --git a/ui/packages/consul-ui/app/routes/dc/services/instance.js b/ui/packages/consul-ui/app/routes/dc/services/instance.js index ae73574a1a..861a071e89 100644 --- a/ui/packages/consul-ui/app/routes/dc/services/instance.js +++ b/ui/packages/consul-ui/app/routes/dc/services/instance.js @@ -1,54 +1,46 @@ import { inject as service } from '@ember/service'; import Route from 'consul-ui/routing/route'; -import { hash } from 'rsvp'; import { get } from '@ember/object'; export default class InstanceRoute extends Route { - @service('data-source/service') - data; + @service('data-source/service') data; - model(params) { + async model(params, transition) { const dc = this.modelFor('dc').dc.Name; const nspace = this.modelFor('nspace').nspace.substr(1) || 'default'; - return hash({ - dc: dc, - nspace: nspace, - item: this.data.source( - uri => uri`/${nspace}/${dc}/service-instance/${params.id}/${params.node}/${params.name}` - ), - }).then(model => { - // this will not be run in a blocking loop, but this is ok as - // its highly unlikely that a service will suddenly change to being a - // connect-proxy or vice versa so leave as is for now - return hash({ - ...model, - proxyMeta: - // proxies and mesh-gateways can't have proxies themselves so don't even look - ['connect-proxy', 'mesh-gateway'].includes(get(model.item, 'Kind')) - ? null - : this.data.source( - uri => - uri`/${nspace}/${dc}/proxy-instance/${params.id}/${params.node}/${params.name}` - ), - }).then(model => { - if (typeof get(model, 'proxyMeta.ServiceID') === 'undefined') { - return model; - } - const proxy = { - id: get(model, 'proxyMeta.ServiceID'), - node: get(model, 'proxyMeta.Node'), - name: get(model, 'proxyMeta.ServiceName'), + + const item = await this.data.source( + uri => uri`/${nspace}/${dc}/service-instance/${params.id}/${params.node}/${params.name}` + ); + + let proxyMeta, proxy; + if (get(item, 'IsOrigin')) { + proxyMeta = await this.data.source( + uri => uri`/${nspace}/${dc}/proxy-instance/${params.id}/${params.node}/${params.name}` + ); + if (typeof get(proxyMeta, 'ServiceID') !== 'undefined') { + const proxyParams = { + id: get(proxyMeta, 'ServiceID'), + node: get(proxyMeta, 'Node'), + name: get(proxyMeta, 'ServiceName'), }; - return hash({ - ...model, - // Proxies have identical dc/nspace as their parent instance - // No need to use Proxy's dc/nspace response - proxy: this.data.source( - uri => uri`/${nspace}/${dc}/service-instance/${proxy.id}/${proxy.node}/${proxy.name}` - ), - }); - }); - }); + // Proxies have identical dc/nspace as their parent instance + // so no need to use Proxy's dc/nspace response + // the proxy itself is just a normal service model + proxy = await this.data.source( + uri => + uri`/${nspace}/${dc}/service-instance/${proxyParams.id}/${proxyParams.node}/${proxyParams.name}` + ); + } + } + + return { + dc, + nspace, + item, + proxyMeta, + proxy, + }; } setupController(controller, model) { diff --git a/ui/packages/consul-ui/app/routes/dc/services/show.js b/ui/packages/consul-ui/app/routes/dc/services/show.js index b3fa4c15e1..18bc162673 100644 --- a/ui/packages/consul-ui/app/routes/dc/services/show.js +++ b/ui/packages/consul-ui/app/routes/dc/services/show.js @@ -1,6 +1,5 @@ import { inject as service } from '@ember/service'; import Route from 'consul-ui/routing/route'; -import { hash } from 'rsvp'; import { get } from '@ember/object'; import { action, setProperties } from '@ember/object'; @@ -24,54 +23,48 @@ export default class ShowRoute extends Route { this.refresh(); } - model(params, transition) { + async model(params, transition) { const dc = this.modelFor('dc').dc.Name; const nspace = this.modelFor('nspace').nspace.substr(1); - return hash({ - slug: params.name, - dc: dc, - nspace: nspace, - items: this.data.source( - uri => uri`/${nspace}/${dc}/service-instances/for-service/${params.name}` - ), - urls: this.config.get().dashboard_url_templates, - chain: null, - proxies: [], - topology: null, - }) - .then(model => { - return ['connect-proxy', 'mesh-gateway', 'ingress-gateway', 'terminating-gateway'].includes( - get(model, 'items.firstObject.Service.Kind') - ) - ? model - : hash({ - ...model, - chain: this.data.source(uri => uri`/${nspace}/${dc}/discovery-chain/${params.name}`), - // Whilst `proxies` isn't used anywhere in the show templates - // it provides a relationship of ProxyInstance on the ServiceInstance - // which can respond at a completely different blocking rate to - // the ServiceInstance itself - proxies: this.data.source( - uri => uri`/${nspace}/${dc}/proxies/for-service/${params.name}` - ), - }); - }) - .then(model => { - let kind = get(model, 'items.firstObject.Service.Kind'); + const slug = params.name; + + let chain = null; + let topology = null; + let proxies = []; + + const urls = this.config.get().dashboard_url_templates; + const items = await this.data.source( + uri => uri`/${nspace}/${dc}/service-instances/for-service/${params.name}` + ); + + const item = get(items, 'firstObject'); + if (get(item, 'IsOrigin')) { + chain = await this.data.source(uri => uri`/${nspace}/${dc}/discovery-chain/${params.name}`); + proxies = await this.data.source( + uri => uri`/${nspace}/${dc}/proxies/for-service/${params.name}` + ); + + if (get(item, 'IsMeshOrigin')) { + let kind = get(item, 'Service.Kind'); if (typeof kind === 'undefined') { kind = ''; } - return ['mesh-gateway', 'terminating-gateway'].includes( - get(model, 'items.firstObject.Service.Kind') - ) - ? model - : hash({ - ...model, - topology: this.data.source( - uri => uri`/${nspace}/${dc}/topology/${params.name}/${kind}` - ), - }); - }); + topology = await this.data.source( + uri => uri`/${nspace}/${dc}/topology/${params.name}/${kind}` + ); + } + } + + return { + dc, + nspace, + slug, + items, + urls, + chain, + proxies, + topology, + }; } setupController(controller, model) { diff --git a/ui/packages/consul-ui/app/routes/dc/services/show/services.js b/ui/packages/consul-ui/app/routes/dc/services/show/services.js index 5289da2936..22f1ecf9ea 100644 --- a/ui/packages/consul-ui/app/routes/dc/services/show/services.js +++ b/ui/packages/consul-ui/app/routes/dc/services/show/services.js @@ -1,6 +1,5 @@ import { inject as service } from '@ember/service'; import Route from 'consul-ui/routing/route'; -import { hash } from 'rsvp'; export default class ServicesRoute extends Route { @service('data-source/service') data; @@ -18,7 +17,7 @@ export default class ServicesRoute extends Route { }, }; - model() { + async model(params, transition) { const dc = this.modelFor('dc').dc.Name; const nspace = this.modelFor('nspace').nspace.substr(1); const parent = this.routeName @@ -26,11 +25,14 @@ export default class ServicesRoute extends Route { .slice(0, -1) .join('.'); const name = this.modelFor(parent).slug; - return hash({ - dc: dc, - nspace: nspace, - gatewayServices: this.data.source(uri => uri`/${nspace}/${dc}/gateways/for-service/${name}`), - }); + const gatewayServices = await this.data.source( + uri => uri`/${nspace}/${dc}/gateways/for-service/${name}` + ); + return { + dc, + nspace, + gatewayServices, + }; } setupController(controller, model) {