From 78ed71b2c970186c7349c9b5ae99e36bd0d09222 Mon Sep 17 00:00:00 2001 From: John Cowen Date: Wed, 16 Dec 2020 09:18:29 +0000 Subject: [PATCH] ui: Rearrange Service detail page to load Topology and Routing tabs separately (#9401) --- .../app/controllers/dc/services/show.js | 13 +- ui/packages/consul-ui/app/routes/dc.js | 69 ++++----- .../consul-ui/app/routes/dc/services/show.js | 55 +++---- .../app/routes/dc/services/show/index.js | 14 +- .../app/routes/dc/services/show/instances.js | 2 +- .../app/routes/dc/services/show/routing.js | 15 +- .../app/routes/dc/services/show/topology.js | 34 ++++- .../consul-ui/app/services/repository/dc.js | 25 ++- .../app/templates/dc/services/show.hbs | 142 +++++++++--------- .../templates/dc/services/show/routing.hbs | 1 + .../templates/dc/services/show/topology.hbs | 5 +- 11 files changed, 184 insertions(+), 191 deletions(-) diff --git a/ui/packages/consul-ui/app/controllers/dc/services/show.js b/ui/packages/consul-ui/app/controllers/dc/services/show.js index 639e7f10a..650f078d3 100644 --- a/ui/packages/consul-ui/app/controllers/dc/services/show.js +++ b/ui/packages/consul-ui/app/controllers/dc/services/show.js @@ -1,12 +1,10 @@ import { inject as service } from '@ember/service'; -import { alias } from '@ember/object/computed'; import Controller from '@ember/controller'; import { get, action } from '@ember/object'; + export default class ShowController extends Controller { @service('flashMessages') notify; - @alias('items.firstObject') item; - @action error(e) { if (e.target.readyState === 1) { @@ -19,14 +17,7 @@ export default class ShowController extends Controller { action: 'update', }); } - [ - e.target, - this.intentions, - this.chain, - this.proxies, - this.gatewayServices, - this.topology, - ].forEach(function(item) { + [e.target, this.proxies].forEach(function(item) { if (item && typeof item.close === 'function') { item.close(); } diff --git a/ui/packages/consul-ui/app/routes/dc.js b/ui/packages/consul-ui/app/routes/dc.js index f13ed2cf4..d503d6868 100644 --- a/ui/packages/consul-ui/app/routes/dc.js +++ b/ui/packages/consul-ui/app/routes/dc.js @@ -1,6 +1,5 @@ import { inject as service } from '@ember/service'; import Route from 'consul-ui/routing/route'; -import { hash, Promise } from 'rsvp'; import { get, action } from '@ember/object'; // TODO: We should potentially move all these nspace related things @@ -24,49 +23,35 @@ const findActiveNspace = function(nspaces, nspace) { return found; }; export default class DcRoute extends Route { - @service('repository/dc') - repo; + @service('repository/dc') repo; + @service('repository/nspace/disabled') nspacesRepo; + @service('settings') settingsRepo; - @service('repository/nspace/disabled') - nspacesRepo; - - @service('settings') - settingsRepo; - - model(params) { + async model(params) { const app = this.modelFor('application'); - return hash({ - nspace: this.nspacesRepo.getActive(), - token: this.settingsRepo.findBySlug('token'), - dc: this.repo.findBySlug(params.dc, app.dcs), - }) - .then(function(model) { - return hash({ - ...model, - ...{ - // if there is only 1 namespace then use that - // otherwise find the namespace object that corresponds - // to the active one - nspace: - app.nspaces.length > 1 - ? findActiveNspace(app.nspaces, model.nspace) - : app.nspaces.firstObject, - }, - }); - }) - .then(model => { - if (get(model, 'token.SecretID')) { - return hash({ - ...model, - ...{ - // When disabled nspaces is [], so nspace is undefined - permissions: this.nspacesRepo.authorize(params.dc, get(model, 'nspace.Name')), - }, - }); - } else { - return model; - } - }); + + let [token, nspace, dc] = await Promise.all([ + this.settingsRepo.findBySlug('token'), + this.nspacesRepo.getActive(), + this.repo.findBySlug(params.dc, app.dcs), + ]); + // if there is only 1 namespace then use that + // otherwise find the namespace object that corresponds + // to the active one + nspace = + app.nspaces.length > 1 ? findActiveNspace(app.nspaces, nspace) : app.nspaces.firstObject; + + let permissions; + if (get(token, 'SecretID')) { + // When disabled nspaces is [], so nspace is undefined + permissions = await this.nspacesRepo.authorize(params.dc, get(nspace || {}, 'Name')); + } + return { + dc, + nspace, + token, + permissions, + }; } 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 b5b37cc2e..63339ca97 100644 --- a/ui/packages/consul-ui/app/routes/dc/services/show.js +++ b/ui/packages/consul-ui/app/routes/dc/services/show.js @@ -1,68 +1,51 @@ import { inject as service } from '@ember/service'; import Route from 'consul-ui/routing/route'; import { get } from '@ember/object'; -import { action } from '@ember/object'; export default class ShowRoute extends Route { @service('data-source/service') data; - @service('repository/intention') repo; @service('ui-config') config; - @action - async createIntention(source, destination) { - const model = this.repo.create({ - Datacenter: source.Datacenter, - SourceName: source.Name, - SourceNS: source.Namespace || 'default', - DestinationName: destination.Name, - DestinationNS: destination.Namespace || 'default', - Action: 'allow', - }); - await this.repo.persist(model); - this.refresh(); - } - async model(params, transition) { - const dc = this.modelFor('dc').dc.Name; + const dc = this.modelFor('dc').dc; const nspace = this.modelFor('nspace').nspace.substr(1); 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}` + uri => uri`/${nspace}/${dc.Name}/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}` + proxies = this.data.source( + uri => uri`/${nspace}/${dc.Name}/proxies/for-service/${params.name}` ); - - if (get(item, 'IsMeshOrigin')) { - let kind = get(item, 'Service.Kind'); - if (typeof kind === 'undefined') { - kind = ''; - } - topology = await this.data.source( - uri => uri`/${nspace}/${dc}/topology/${params.name}/${kind}` - ); - } + // TODO: Temporary ping to see if a dc is MeshEnabled which we use in + // order to decide whether to show certain tabs in the template. This is + // a bit of a weird place to do this but we are trying to avoid wasting + // HTTP requests and as disco chain is the most likely to be reused, we + // use that endpoint here. Eventually if we have an endpoint specific to + // a dc that gives us more DC specific info we can use that instead + // higher up the routing hierarchy instead. + let chain = this.data.source( + uri => uri`/${nspace}/${dc.Name}/discovery-chain/${params.name}` + ); + [chain, proxies] = await Promise.all([chain, proxies]); + // we close the chain for now, if you enter the routing tab before the + // EventSource comes around to request again, this one will just be + // reopened and reused + chain.close(); } - return { dc, nspace, slug, items, urls, - chain, proxies, - topology, }; } diff --git a/ui/packages/consul-ui/app/routes/dc/services/show/index.js b/ui/packages/consul-ui/app/routes/dc/services/show/index.js index 18647d63f..983bfef6a 100644 --- a/ui/packages/consul-ui/app/routes/dc/services/show/index.js +++ b/ui/packages/consul-ui/app/routes/dc/services/show/index.js @@ -2,21 +2,20 @@ import Route from '@ember/routing/route'; import { get } from '@ember/object'; export default class IndexRoute extends Route { - afterModel(model, transition) { + async afterModel(model, transition) { const parent = this.routeName .split('.') .slice(0, -1) .join('.'); - // the default selected tab depends on whether you have any healthchecks or not - // so check the length here. let to = 'topology'; const parentModel = this.modelFor(parent); const hasProxy = get(parentModel, 'proxies.length') !== 0; - const kind = get(parentModel, 'items.firstObject.Service.Kind'); - + const item = get(parentModel, 'items.firstObject'); + const kind = get(item, 'Service.Kind'); + const hasTopology = get(parentModel, 'dc.MeshEnabled') && get(item, 'IsMeshOrigin'); switch (kind) { case 'ingress-gateway': - if (!get(parentModel, 'topology.Datacenter')) { + if (!hasTopology) { to = 'upstreams'; } break; @@ -27,11 +26,10 @@ export default class IndexRoute extends Route { to = 'instances'; break; default: - if (!hasProxy || !get(parentModel, 'topology.Datacenter')) { + if (!hasProxy || !hasTopology) { to = 'instances'; } } - this.replaceWith(`${parent}.${to}`, parentModel); } } diff --git a/ui/packages/consul-ui/app/routes/dc/services/show/instances.js b/ui/packages/consul-ui/app/routes/dc/services/show/instances.js index 6690bd457..f84dc2d11 100644 --- a/ui/packages/consul-ui/app/routes/dc/services/show/instances.js +++ b/ui/packages/consul-ui/app/routes/dc/services/show/instances.js @@ -15,7 +15,7 @@ export default class InstancesRoute extends Route { }, }; - model() { + async model() { const parent = this.routeName .split('.') .slice(0, -1) diff --git a/ui/packages/consul-ui/app/routes/dc/services/show/routing.js b/ui/packages/consul-ui/app/routes/dc/services/show/routing.js index e5045782b..e70cacd61 100644 --- a/ui/packages/consul-ui/app/routes/dc/services/show/routing.js +++ b/ui/packages/consul-ui/app/routes/dc/services/show/routing.js @@ -1,16 +1,25 @@ import Route from 'consul-ui/routing/route'; +import { inject as service } from '@ember/service'; import { get } from '@ember/object'; export default class RoutingRoute extends Route { - model() { + @service('data-source/service') data; + + async model(params, transition) { const parent = this.routeName .split('.') .slice(0, -1) .join('.'); - return this.modelFor(parent); + const model = this.modelFor(parent); + return { + ...model, + chain: await this.data.source( + uri => uri`/${model.nspace}/${model.dc.Name}/discovery-chain/${model.slug}` + ), + }; } - afterModel(model, transition) { + async afterModel(model, transition) { if (!get(model, 'chain')) { const parent = this.routeName .split('.') diff --git a/ui/packages/consul-ui/app/routes/dc/services/show/topology.js b/ui/packages/consul-ui/app/routes/dc/services/show/topology.js index 30fa001c0..4d5216719 100644 --- a/ui/packages/consul-ui/app/routes/dc/services/show/topology.js +++ b/ui/packages/consul-ui/app/routes/dc/services/show/topology.js @@ -1,18 +1,48 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; +import { get, action } from '@ember/object'; export default class TopologyRoute extends Route { @service('ui-config') config; @service('env') env; + @service('data-source/service') data; + @service('repository/intention') repo; - model() { + @action + async createIntention(source, destination) { + const model = this.repo.create({ + Datacenter: source.Datacenter, + SourceName: source.Name, + SourceNS: source.Namespace || 'default', + DestinationName: destination.Name, + DestinationNS: destination.Namespace || 'default', + Action: 'allow', + }); + await this.repo.persist(model); + this.refresh(); + } + + async model() { const parent = this.routeName .split('.') .slice(0, -1) .join('.'); + const model = this.modelFor(parent); + const dc = get(model, 'dc'); + const nspace = get(model, 'nspace'); + const item = get(model, 'items.firstObject'); + if (get(item, 'IsMeshOrigin')) { + let kind = get(item, 'Service.Kind'); + if (typeof kind === 'undefined') { + kind = ''; + } + model.topology = await this.data.source( + uri => uri`/${nspace}/${dc.Name}/topology/${model.slug}/${kind}` + ); + } return { - ...this.modelFor(parent), + ...model, hasMetricsProvider: !!this.config.get().metrics_provider, isRemoteDC: this.env.var('CONSUL_DATACENTER_LOCAL') !== this.modelFor('dc').dc.Name, }; diff --git a/ui/packages/consul-ui/app/services/repository/dc.js b/ui/packages/consul-ui/app/services/repository/dc.js index 552a7939c..cb0a3cd61 100644 --- a/ui/packages/consul-ui/app/services/repository/dc.js +++ b/ui/packages/consul-ui/app/services/repository/dc.js @@ -12,18 +12,16 @@ export default class DcService extends RepositoryService { return modelName; } - findAll() { + async findAll() { return this.store.query(this.getModelName(), {}); } - findBySlug(name, items) { + async findBySlug(name, items) { if (name != null) { - const item = items.findBy('Name', name); - if (item) { - return this.settings.persist({ dc: get(item, 'Name') }).then(function() { - // TODO: create a model - return { Name: get(item, 'Name') }; - }); + const item = await items.findBy('Name', name); + if (typeof item !== 'undefined') { + await this.settings.persist({ dc: get(item, 'Name') }); + return item; } } const e = new Error('Page not found'); @@ -31,22 +29,21 @@ export default class DcService extends RepositoryService { return Promise.reject({ errors: [e] }); } - getActive(name, items) { - const settings = this.settings; - return Promise.all([name || settings.findBySlug('dc'), items || this.findAll()]).then( + async getActive(name, items) { + return Promise.all([name || this.settings.findBySlug('dc'), items || this.findAll()]).then( ([name, items]) => { - return this.findBySlug(name, items).catch(e => { + return this.findBySlug(name, items).catch(async e => { const item = items.findBy('Name', this.env.var('CONSUL_DATACENTER_LOCAL')) || get(items, 'firstObject'); - settings.persist({ dc: get(item, 'Name') }); + await this.settings.persist({ dc: get(item, 'Name') }); return item; }); } ); } - clearActive() { + async clearActive() { return this.settings.delete('dc'); } } diff --git a/ui/packages/consul-ui/app/templates/dc/services/show.hbs b/ui/packages/consul-ui/app/templates/dc/services/show.hbs index a051e3caf..e10f68be7 100644 --- a/ui/packages/consul-ui/app/templates/dc/services/show.hbs +++ b/ui/packages/consul-ui/app/templates/dc/services/show.hbs @@ -1,75 +1,73 @@ - - - - -{{page-title item.Service.Service}} - - - - - -
    -
  1. All Services
  2. -
-
- -

- {{item.Service.Service}} -

- - -
- - {{#if (not-eq item.Service.Kind 'mesh-gateway')}} - - {{/if}} - - - {{#if urls.service}} - - Open Dashboard - +{{#let items.firstObject as |item|}} + {{page-title item.Service.Service}} + + + + + +
    +
  1. All Services
  2. +
+
+ +

+ {{item.Service.Service}} +

+ + +
+ + {{#if (not-eq item.Service.Kind 'mesh-gateway')}} + {{/if}} - - - - {{outlet}} - - -
\ No newline at end of file +
+ + {{#if urls.service}} + + Open Dashboard + + {{/if}} + + + + {{outlet}} + + +
+{{/let}} \ No newline at end of file diff --git a/ui/packages/consul-ui/app/templates/dc/services/show/routing.hbs b/ui/packages/consul-ui/app/templates/dc/services/show/routing.hbs index c493ae005..134cc590d 100644 --- a/ui/packages/consul-ui/app/templates/dc/services/show/routing.hbs +++ b/ui/packages/consul-ui/app/templates/dc/services/show/routing.hbs @@ -1,3 +1,4 @@ +
{{#if topology.FilteredByACLs}} @@ -6,11 +7,11 @@ {{#if topology}}