ui: Rearrange Service detail page to load Topology and Routing tabs separately (#9401)
This commit is contained in:
parent
a907922ddb
commit
78ed71b2c9
|
@ -1,12 +1,10 @@
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { alias } from '@ember/object/computed';
|
|
||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
import { get, action } from '@ember/object';
|
import { get, action } from '@ember/object';
|
||||||
|
|
||||||
export default class ShowController extends Controller {
|
export default class ShowController extends Controller {
|
||||||
@service('flashMessages') notify;
|
@service('flashMessages') notify;
|
||||||
|
|
||||||
@alias('items.firstObject') item;
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
error(e) {
|
error(e) {
|
||||||
if (e.target.readyState === 1) {
|
if (e.target.readyState === 1) {
|
||||||
|
@ -19,14 +17,7 @@ export default class ShowController extends Controller {
|
||||||
action: 'update',
|
action: 'update',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
[
|
[e.target, this.proxies].forEach(function(item) {
|
||||||
e.target,
|
|
||||||
this.intentions,
|
|
||||||
this.chain,
|
|
||||||
this.proxies,
|
|
||||||
this.gatewayServices,
|
|
||||||
this.topology,
|
|
||||||
].forEach(function(item) {
|
|
||||||
if (item && typeof item.close === 'function') {
|
if (item && typeof item.close === 'function') {
|
||||||
item.close();
|
item.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import Route from 'consul-ui/routing/route';
|
import Route from 'consul-ui/routing/route';
|
||||||
import { hash, Promise } from 'rsvp';
|
|
||||||
import { get, action } from '@ember/object';
|
import { get, action } from '@ember/object';
|
||||||
|
|
||||||
// TODO: We should potentially move all these nspace related things
|
// TODO: We should potentially move all these nspace related things
|
||||||
|
@ -24,49 +23,35 @@ const findActiveNspace = function(nspaces, nspace) {
|
||||||
return found;
|
return found;
|
||||||
};
|
};
|
||||||
export default class DcRoute extends Route {
|
export default class DcRoute extends Route {
|
||||||
@service('repository/dc')
|
@service('repository/dc') repo;
|
||||||
repo;
|
@service('repository/nspace/disabled') nspacesRepo;
|
||||||
|
@service('settings') settingsRepo;
|
||||||
|
|
||||||
@service('repository/nspace/disabled')
|
async model(params) {
|
||||||
nspacesRepo;
|
|
||||||
|
|
||||||
@service('settings')
|
|
||||||
settingsRepo;
|
|
||||||
|
|
||||||
model(params) {
|
|
||||||
const app = this.modelFor('application');
|
const app = this.modelFor('application');
|
||||||
return hash({
|
|
||||||
nspace: this.nspacesRepo.getActive(),
|
let [token, nspace, dc] = await Promise.all([
|
||||||
token: this.settingsRepo.findBySlug('token'),
|
this.settingsRepo.findBySlug('token'),
|
||||||
dc: this.repo.findBySlug(params.dc, app.dcs),
|
this.nspacesRepo.getActive(),
|
||||||
})
|
this.repo.findBySlug(params.dc, app.dcs),
|
||||||
.then(function(model) {
|
]);
|
||||||
return hash({
|
// if there is only 1 namespace then use that
|
||||||
...model,
|
// otherwise find the namespace object that corresponds
|
||||||
...{
|
// to the active one
|
||||||
// if there is only 1 namespace then use that
|
nspace =
|
||||||
// otherwise find the namespace object that corresponds
|
app.nspaces.length > 1 ? findActiveNspace(app.nspaces, nspace) : app.nspaces.firstObject;
|
||||||
// to the active one
|
|
||||||
nspace:
|
let permissions;
|
||||||
app.nspaces.length > 1
|
if (get(token, 'SecretID')) {
|
||||||
? findActiveNspace(app.nspaces, model.nspace)
|
// When disabled nspaces is [], so nspace is undefined
|
||||||
: app.nspaces.firstObject,
|
permissions = await this.nspacesRepo.authorize(params.dc, get(nspace || {}, 'Name'));
|
||||||
},
|
}
|
||||||
});
|
return {
|
||||||
})
|
dc,
|
||||||
.then(model => {
|
nspace,
|
||||||
if (get(model, 'token.SecretID')) {
|
token,
|
||||||
return hash({
|
permissions,
|
||||||
...model,
|
};
|
||||||
...{
|
|
||||||
// When disabled nspaces is [], so nspace is undefined
|
|
||||||
permissions: this.nspacesRepo.authorize(params.dc, get(model, 'nspace.Name')),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
|
|
|
@ -1,68 +1,51 @@
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import Route from 'consul-ui/routing/route';
|
import Route from 'consul-ui/routing/route';
|
||||||
import { get } from '@ember/object';
|
import { get } from '@ember/object';
|
||||||
import { action } from '@ember/object';
|
|
||||||
|
|
||||||
export default class ShowRoute extends Route {
|
export default class ShowRoute extends Route {
|
||||||
@service('data-source/service') data;
|
@service('data-source/service') data;
|
||||||
@service('repository/intention') repo;
|
|
||||||
@service('ui-config') config;
|
@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) {
|
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 nspace = this.modelFor('nspace').nspace.substr(1);
|
||||||
const slug = params.name;
|
const slug = params.name;
|
||||||
|
|
||||||
let chain = null;
|
|
||||||
let topology = null;
|
|
||||||
let proxies = [];
|
let proxies = [];
|
||||||
|
|
||||||
const urls = this.config.get().dashboard_url_templates;
|
const urls = this.config.get().dashboard_url_templates;
|
||||||
const items = await this.data.source(
|
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');
|
const item = get(items, 'firstObject');
|
||||||
if (get(item, 'IsOrigin')) {
|
if (get(item, 'IsOrigin')) {
|
||||||
chain = await this.data.source(uri => uri`/${nspace}/${dc}/discovery-chain/${params.name}`);
|
proxies = this.data.source(
|
||||||
proxies = await this.data.source(
|
uri => uri`/${nspace}/${dc.Name}/proxies/for-service/${params.name}`
|
||||||
uri => uri`/${nspace}/${dc}/proxies/for-service/${params.name}`
|
|
||||||
);
|
);
|
||||||
|
// TODO: Temporary ping to see if a dc is MeshEnabled which we use in
|
||||||
if (get(item, 'IsMeshOrigin')) {
|
// order to decide whether to show certain tabs in the template. This is
|
||||||
let kind = get(item, 'Service.Kind');
|
// a bit of a weird place to do this but we are trying to avoid wasting
|
||||||
if (typeof kind === 'undefined') {
|
// HTTP requests and as disco chain is the most likely to be reused, we
|
||||||
kind = '';
|
// 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
|
||||||
topology = await this.data.source(
|
// higher up the routing hierarchy instead.
|
||||||
uri => uri`/${nspace}/${dc}/topology/${params.name}/${kind}`
|
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 {
|
return {
|
||||||
dc,
|
dc,
|
||||||
nspace,
|
nspace,
|
||||||
slug,
|
slug,
|
||||||
items,
|
items,
|
||||||
urls,
|
urls,
|
||||||
chain,
|
|
||||||
proxies,
|
proxies,
|
||||||
topology,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,21 +2,20 @@ import Route from '@ember/routing/route';
|
||||||
import { get } from '@ember/object';
|
import { get } from '@ember/object';
|
||||||
|
|
||||||
export default class IndexRoute extends Route {
|
export default class IndexRoute extends Route {
|
||||||
afterModel(model, transition) {
|
async afterModel(model, transition) {
|
||||||
const parent = this.routeName
|
const parent = this.routeName
|
||||||
.split('.')
|
.split('.')
|
||||||
.slice(0, -1)
|
.slice(0, -1)
|
||||||
.join('.');
|
.join('.');
|
||||||
// the default selected tab depends on whether you have any healthchecks or not
|
|
||||||
// so check the length here.
|
|
||||||
let to = 'topology';
|
let to = 'topology';
|
||||||
const parentModel = this.modelFor(parent);
|
const parentModel = this.modelFor(parent);
|
||||||
const hasProxy = get(parentModel, 'proxies.length') !== 0;
|
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) {
|
switch (kind) {
|
||||||
case 'ingress-gateway':
|
case 'ingress-gateway':
|
||||||
if (!get(parentModel, 'topology.Datacenter')) {
|
if (!hasTopology) {
|
||||||
to = 'upstreams';
|
to = 'upstreams';
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -27,11 +26,10 @@ export default class IndexRoute extends Route {
|
||||||
to = 'instances';
|
to = 'instances';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (!hasProxy || !get(parentModel, 'topology.Datacenter')) {
|
if (!hasProxy || !hasTopology) {
|
||||||
to = 'instances';
|
to = 'instances';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.replaceWith(`${parent}.${to}`, parentModel);
|
this.replaceWith(`${parent}.${to}`, parentModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ export default class InstancesRoute extends Route {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
model() {
|
async model() {
|
||||||
const parent = this.routeName
|
const parent = this.routeName
|
||||||
.split('.')
|
.split('.')
|
||||||
.slice(0, -1)
|
.slice(0, -1)
|
||||||
|
|
|
@ -1,16 +1,25 @@
|
||||||
import Route from 'consul-ui/routing/route';
|
import Route from 'consul-ui/routing/route';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
import { get } from '@ember/object';
|
import { get } from '@ember/object';
|
||||||
|
|
||||||
export default class RoutingRoute extends Route {
|
export default class RoutingRoute extends Route {
|
||||||
model() {
|
@service('data-source/service') data;
|
||||||
|
|
||||||
|
async model(params, transition) {
|
||||||
const parent = this.routeName
|
const parent = this.routeName
|
||||||
.split('.')
|
.split('.')
|
||||||
.slice(0, -1)
|
.slice(0, -1)
|
||||||
.join('.');
|
.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')) {
|
if (!get(model, 'chain')) {
|
||||||
const parent = this.routeName
|
const parent = this.routeName
|
||||||
.split('.')
|
.split('.')
|
||||||
|
|
|
@ -1,18 +1,48 @@
|
||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
|
import { get, action } from '@ember/object';
|
||||||
|
|
||||||
export default class TopologyRoute extends Route {
|
export default class TopologyRoute extends Route {
|
||||||
@service('ui-config') config;
|
@service('ui-config') config;
|
||||||
@service('env') env;
|
@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
|
const parent = this.routeName
|
||||||
.split('.')
|
.split('.')
|
||||||
.slice(0, -1)
|
.slice(0, -1)
|
||||||
.join('.');
|
.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 {
|
return {
|
||||||
...this.modelFor(parent),
|
...model,
|
||||||
hasMetricsProvider: !!this.config.get().metrics_provider,
|
hasMetricsProvider: !!this.config.get().metrics_provider,
|
||||||
isRemoteDC: this.env.var('CONSUL_DATACENTER_LOCAL') !== this.modelFor('dc').dc.Name,
|
isRemoteDC: this.env.var('CONSUL_DATACENTER_LOCAL') !== this.modelFor('dc').dc.Name,
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,18 +12,16 @@ export default class DcService extends RepositoryService {
|
||||||
return modelName;
|
return modelName;
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll() {
|
async findAll() {
|
||||||
return this.store.query(this.getModelName(), {});
|
return this.store.query(this.getModelName(), {});
|
||||||
}
|
}
|
||||||
|
|
||||||
findBySlug(name, items) {
|
async findBySlug(name, items) {
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
const item = items.findBy('Name', name);
|
const item = await items.findBy('Name', name);
|
||||||
if (item) {
|
if (typeof item !== 'undefined') {
|
||||||
return this.settings.persist({ dc: get(item, 'Name') }).then(function() {
|
await this.settings.persist({ dc: get(item, 'Name') });
|
||||||
// TODO: create a model
|
return item;
|
||||||
return { Name: get(item, 'Name') };
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const e = new Error('Page not found');
|
const e = new Error('Page not found');
|
||||||
|
@ -31,22 +29,21 @@ export default class DcService extends RepositoryService {
|
||||||
return Promise.reject({ errors: [e] });
|
return Promise.reject({ errors: [e] });
|
||||||
}
|
}
|
||||||
|
|
||||||
getActive(name, items) {
|
async getActive(name, items) {
|
||||||
const settings = this.settings;
|
return Promise.all([name || this.settings.findBySlug('dc'), items || this.findAll()]).then(
|
||||||
return Promise.all([name || settings.findBySlug('dc'), items || this.findAll()]).then(
|
|
||||||
([name, items]) => {
|
([name, items]) => {
|
||||||
return this.findBySlug(name, items).catch(e => {
|
return this.findBySlug(name, items).catch(async e => {
|
||||||
const item =
|
const item =
|
||||||
items.findBy('Name', this.env.var('CONSUL_DATACENTER_LOCAL')) ||
|
items.findBy('Name', this.env.var('CONSUL_DATACENTER_LOCAL')) ||
|
||||||
get(items, 'firstObject');
|
get(items, 'firstObject');
|
||||||
settings.persist({ dc: get(item, 'Name') });
|
await this.settings.persist({ dc: get(item, 'Name') });
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
clearActive() {
|
async clearActive() {
|
||||||
return this.settings.delete('dc');
|
return this.settings.delete('dc');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,75 +1,73 @@
|
||||||
<EventSource @src={{items}} @onerror={{action "error"}} />
|
<EventSource @src={{items}} @onerror={{action "error"}} />
|
||||||
<EventSource @src={{chain}} />
|
|
||||||
<EventSource @src={{intentions}} />
|
|
||||||
<EventSource @src={{proxies}} />
|
<EventSource @src={{proxies}} />
|
||||||
<EventSource @src={{gatewayServices}} />
|
{{#let items.firstObject as |item|}}
|
||||||
<EventSource @src={{topology}} />
|
{{page-title item.Service.Service}}
|
||||||
{{page-title item.Service.Service}}
|
<AppView>
|
||||||
<AppView>
|
<BlockSlot @name="notification" as |status type|>
|
||||||
<BlockSlot @name="notification" as |status type|>
|
<Consul::Service::Notifications
|
||||||
<Consul::Service::Notifications
|
@type={{type}}
|
||||||
@type={{type}}
|
@status={{status}}
|
||||||
@status={{status}}
|
/>
|
||||||
/>
|
</BlockSlot>
|
||||||
</BlockSlot>
|
<BlockSlot @name="breadcrumbs">
|
||||||
<BlockSlot @name="breadcrumbs">
|
<ol>
|
||||||
<ol>
|
<li><a data-test-back href={{href-to 'dc.services'}}>All Services</a></li>
|
||||||
<li><a data-test-back href={{href-to 'dc.services'}}>All Services</a></li>
|
</ol>
|
||||||
</ol>
|
</BlockSlot>
|
||||||
</BlockSlot>
|
<BlockSlot @name="header">
|
||||||
<BlockSlot @name="header">
|
<h1>
|
||||||
<h1>
|
{{item.Service.Service}}
|
||||||
{{item.Service.Service}}
|
</h1>
|
||||||
</h1>
|
<Consul::ExternalSource @item={{item.Service}} />
|
||||||
<Consul::ExternalSource @item={{item.Service}} />
|
<Consul::Kind @item={{item.Service}} @withInfo={{true}} />
|
||||||
<Consul::Kind @item={{item.Service}} @withInfo={{true}} />
|
</BlockSlot>
|
||||||
</BlockSlot>
|
<BlockSlot @name="nav">
|
||||||
<BlockSlot @name="nav">
|
{{#if (not-eq item.Service.Kind 'mesh-gateway')}}
|
||||||
{{#if (not-eq item.Service.Kind 'mesh-gateway')}}
|
<TabNav @items={{
|
||||||
<TabNav @items={{
|
compact
|
||||||
compact
|
(array
|
||||||
(array
|
(if (and dc.MeshEnabled item.IsMeshOrigin (or (gt proxies.length 0) (eq item.Service.Kind 'ingress-gateway')))
|
||||||
(if (and topology.Datacenter (or (gt proxies.length 0) (eq item.Service.Kind 'ingress-gateway')))
|
(hash label="Topology" href=(href-to "dc.services.show.topology") selected=(is-href "dc.services.show.topology"))
|
||||||
(hash label="Topology" href=(href-to "dc.services.show.topology") selected=(is-href "dc.services.show.topology"))
|
'')
|
||||||
'')
|
(if (eq item.Service.Kind 'terminating-gateway')
|
||||||
(if (eq item.Service.Kind 'terminating-gateway')
|
(hash label="Linked Services" href=(href-to "dc.services.show.services") selected=(is-href "dc.services.show.services"))
|
||||||
(hash label="Linked Services" href=(href-to "dc.services.show.services") selected=(is-href "dc.services.show.services"))
|
'')
|
||||||
'')
|
(if (eq item.Service.Kind 'ingress-gateway')
|
||||||
(if (eq item.Service.Kind 'ingress-gateway')
|
(hash label="Upstreams" href=(href-to "dc.services.show.upstreams") selected=(is-href "dc.services.show.upstreams"))
|
||||||
(hash label="Upstreams" href=(href-to "dc.services.show.upstreams") selected=(is-href "dc.services.show.upstreams"))
|
'')
|
||||||
'')
|
(hash label="Instances" href=(href-to "dc.services.show.instances") selected=(is-href "dc.services.show.instances"))
|
||||||
(hash label="Instances" href=(href-to "dc.services.show.instances") selected=(is-href "dc.services.show.instances"))
|
(if (not-eq item.Service.Kind 'terminating-gateway')
|
||||||
(if (not-eq item.Service.Kind 'terminating-gateway')
|
(hash label="Intentions" href=(href-to "dc.services.show.intentions") selected=(is-href "dc.services.show.intentions"))
|
||||||
(hash label="Intentions" href=(href-to "dc.services.show.intentions") selected=(is-href "dc.services.show.intentions"))
|
'')
|
||||||
'')
|
(if (and dc.MeshEnabled item.IsOrigin)
|
||||||
(if chain.Chain
|
(hash label="Routing" href=(href-to "dc.services.show.routing") selected=(is-href "dc.services.show.routing"))
|
||||||
(hash label="Routing" href=(href-to "dc.services.show.routing") selected=(is-href "dc.services.show.routing"))
|
'')
|
||||||
'')
|
(if (not item.Service.Kind)
|
||||||
(if (not item.Service.Kind)
|
(hash label="Tags" href=(href-to "dc.services.show.tags") selected=(is-href "dc.services.show.tags"))
|
||||||
(hash label="Tags" href=(href-to "dc.services.show.tags") selected=(is-href "dc.services.show.tags"))
|
'')
|
||||||
'')
|
)
|
||||||
)
|
}}/>
|
||||||
}}/>
|
|
||||||
{{/if}}
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="actions">
|
|
||||||
{{#if urls.service}}
|
|
||||||
<a href={{render-template urls.service (hash
|
|
||||||
Datacenter=dc
|
|
||||||
Service=(hash Name=item.Service.Service)
|
|
||||||
)}}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
data-test-dashboard-anchor>
|
|
||||||
Open Dashboard
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="content">
|
<BlockSlot @name="actions">
|
||||||
<Outlet
|
{{#if urls.service}}
|
||||||
@name={{routeName}}
|
<a href={{render-template urls.service (hash
|
||||||
as |o|>
|
Datacenter=dc.Name
|
||||||
{{outlet}}
|
Service=(hash Name=item.Service.Service)
|
||||||
</Outlet>
|
)}}
|
||||||
</BlockSlot>
|
target="_blank"
|
||||||
</AppView>
|
rel="noopener noreferrer"
|
||||||
|
data-test-dashboard-anchor>
|
||||||
|
Open Dashboard
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="content">
|
||||||
|
<Outlet
|
||||||
|
@name={{routeName}}
|
||||||
|
as |o|>
|
||||||
|
{{outlet}}
|
||||||
|
</Outlet>
|
||||||
|
</BlockSlot>
|
||||||
|
</AppView>
|
||||||
|
{{/let}}
|
|
@ -1,3 +1,4 @@
|
||||||
|
<EventSource @src={{chain}} />
|
||||||
<div id="routing" class="tab-section">
|
<div id="routing" class="tab-section">
|
||||||
<div role="tabpanel">
|
<div role="tabpanel">
|
||||||
<Consul::DiscoveryChain
|
<Consul::DiscoveryChain
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
<EventSource @src={{topology}} />
|
||||||
<div id="topology" class="tab-section">
|
<div id="topology" class="tab-section">
|
||||||
<div role="tabpanel">
|
<div role="tabpanel">
|
||||||
{{#if topology.FilteredByACLs}}
|
{{#if topology.FilteredByACLs}}
|
||||||
|
@ -6,11 +7,11 @@
|
||||||
{{#if topology}}
|
{{#if topology}}
|
||||||
<TopologyMetrics
|
<TopologyMetrics
|
||||||
@nspace={{nspace}}
|
@nspace={{nspace}}
|
||||||
@dc={{dc}}
|
@dc={{dc.Name}}
|
||||||
@service={{items.firstObject}}
|
@service={{items.firstObject}}
|
||||||
@topology={{topology}}
|
@topology={{topology}}
|
||||||
@metricsHref={{render-template urls.service (hash
|
@metricsHref={{render-template urls.service (hash
|
||||||
Datacenter=dc
|
Datacenter=dc.Name
|
||||||
Service=items.firstObject
|
Service=items.firstObject
|
||||||
)}}
|
)}}
|
||||||
@isRemoteDC={{isRemoteDC}}
|
@isRemoteDC={{isRemoteDC}}
|
||||||
|
|
Loading…
Reference in New Issue