ui: Misc changes for Catalog area (#9414)
* Use DataLoader errors for Service Detail and Service Instance * uiCfg > config use the repo-like async interface where possible * Clean up node show * Make sure you can put `=` in dev cookie values * Never default to default * Tweak chain variable * Remove env service * Pass chain through to the template for the tempalte to clean it up * Delete controller tests * Remove cleanup in Nodes show as this is still being used in another tab * Use dc.Local
This commit is contained in:
parent
325bca338b
commit
ae049feeab
|
@ -1,6 +0,0 @@
|
||||||
{{#if (eq @type 'update')}}
|
|
||||||
{{#if (eq @status 'warning') }}
|
|
||||||
This service has been deregistered and no longer exists in the catalog.
|
|
||||||
{{else}}
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
|
@ -1,28 +0,0 @@
|
||||||
import { inject as service } from '@ember/service';
|
|
||||||
import Controller from '@ember/controller';
|
|
||||||
import { get, action } from '@ember/object';
|
|
||||||
|
|
||||||
export default class InstanceController extends Controller {
|
|
||||||
@service('flashMessages')
|
|
||||||
notify;
|
|
||||||
|
|
||||||
@action
|
|
||||||
error(e) {
|
|
||||||
if (e.target.readyState === 1) {
|
|
||||||
// OPEN
|
|
||||||
if (get(e, 'error.errors.firstObject.status') === '404') {
|
|
||||||
this.notify.add({
|
|
||||||
destroyOnClick: false,
|
|
||||||
sticky: true,
|
|
||||||
type: 'warning',
|
|
||||||
action: 'update',
|
|
||||||
});
|
|
||||||
[e.target, this.proxy].forEach(function(item) {
|
|
||||||
if (item && typeof item.close === 'function') {
|
|
||||||
item.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
import { inject as service } from '@ember/service';
|
|
||||||
import Controller from '@ember/controller';
|
|
||||||
import { get, action } from '@ember/object';
|
|
||||||
|
|
||||||
export default class ShowController extends Controller {
|
|
||||||
@service('flashMessages') notify;
|
|
||||||
|
|
||||||
@action
|
|
||||||
error(e) {
|
|
||||||
if (e.target.readyState === 1) {
|
|
||||||
// OPEN
|
|
||||||
if (get(e, 'error.errors.firstObject.status') === '404') {
|
|
||||||
this.notify.add({
|
|
||||||
destroyOnClick: false,
|
|
||||||
sticky: true,
|
|
||||||
type: 'warning',
|
|
||||||
action: 'update',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
[e.target, this.proxies].forEach(function(item) {
|
|
||||||
if (item && typeof item.close === 'function') {
|
|
||||||
item.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,7 +7,7 @@ export default class InstanceRoute extends Route {
|
||||||
|
|
||||||
async model(params, transition) {
|
async model(params, transition) {
|
||||||
const dc = this.modelFor('dc').dc.Name;
|
const dc = this.modelFor('dc').dc.Name;
|
||||||
const nspace = this.modelFor('nspace').nspace.substr(1) || 'default';
|
const nspace = this.modelFor('nspace').nspace.substr(1);
|
||||||
|
|
||||||
const item = await this.data.source(
|
const item = await this.data.source(
|
||||||
uri => uri`/${nspace}/${dc}/service-instance/${params.id}/${params.node}/${params.name}`
|
uri => uri`/${nspace}/${dc}/service-instance/${params.id}/${params.node}/${params.name}`
|
||||||
|
|
|
@ -11,9 +11,10 @@ export default class ShowRoute extends Route {
|
||||||
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;
|
||||||
let proxies = [];
|
let proxies = [];
|
||||||
|
|
||||||
const urls = this.config.get().dashboard_url_templates;
|
const urls = await this.config.findByPath('dashboard_url_templates');
|
||||||
const items = await this.data.source(
|
const items = await this.data.source(
|
||||||
uri => uri`/${nspace}/${dc.Name}/service-instances/for-service/${params.name}`
|
uri => uri`/${nspace}/${dc.Name}/service-instances/for-service/${params.name}`
|
||||||
);
|
);
|
||||||
|
@ -30,9 +31,7 @@ export default class ShowRoute extends Route {
|
||||||
// use that endpoint here. Eventually if we have an endpoint specific to
|
// 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
|
// a dc that gives us more DC specific info we can use that instead
|
||||||
// higher up the routing hierarchy instead.
|
// higher up the routing hierarchy instead.
|
||||||
let chain = this.data.source(
|
chain = this.data.source(uri => uri`/${nspace}/${dc.Name}/discovery-chain/${params.name}`);
|
||||||
uri => uri`/${nspace}/${dc.Name}/discovery-chain/${params.name}`
|
|
||||||
);
|
|
||||||
[chain, proxies] = await Promise.all([chain, proxies]);
|
[chain, proxies] = await Promise.all([chain, proxies]);
|
||||||
// we close the chain for now, if you enter the routing tab before the
|
// 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
|
// EventSource comes around to request again, this one will just be
|
||||||
|
@ -45,6 +44,7 @@ export default class ShowRoute extends Route {
|
||||||
slug,
|
slug,
|
||||||
items,
|
items,
|
||||||
urls,
|
urls,
|
||||||
|
chain,
|
||||||
proxies,
|
proxies,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ 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('data-source/service') data;
|
@service('data-source/service') data;
|
||||||
@service('repository/intention') repo;
|
@service('repository/intention') repo;
|
||||||
|
|
||||||
|
@ -32,19 +31,20 @@ export default class TopologyRoute extends Route {
|
||||||
const nspace = get(model, 'nspace');
|
const nspace = get(model, 'nspace');
|
||||||
|
|
||||||
const item = get(model, 'items.firstObject');
|
const item = get(model, 'items.firstObject');
|
||||||
if (get(item, 'IsMeshOrigin')) {
|
let kind = get(item, 'Service.Kind');
|
||||||
let kind = get(item, 'Service.Kind');
|
if (typeof kind === 'undefined') {
|
||||||
if (typeof kind === 'undefined') {
|
kind = '';
|
||||||
kind = '';
|
|
||||||
}
|
|
||||||
model.topology = await this.data.source(
|
|
||||||
uri => uri`/${nspace}/${dc.Name}/topology/${model.slug}/${kind}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
const topology = await this.data.source(
|
||||||
|
uri => uri`/${nspace}/${dc.Name}/topology/${model.slug}/${kind}`
|
||||||
|
);
|
||||||
|
let hasMetricsProvider = await this.config.findByPath('metrics_provider');
|
||||||
|
hasMetricsProvider = !!hasMetricsProvider;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...model,
|
...model,
|
||||||
hasMetricsProvider: !!this.config.get().metrics_provider,
|
topology,
|
||||||
isRemoteDC: this.env.var('CONSUL_DATACENTER_LOCAL') !== this.modelFor('dc').dc.Name,
|
hasMetricsProvider,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import RepositoryService from 'consul-ui/services/repository';
|
||||||
// metrics provider
|
// metrics provider
|
||||||
|
|
||||||
export default class MetricsService extends RepositoryService {
|
export default class MetricsService extends RepositoryService {
|
||||||
@service('ui-config') cfg;
|
@service('ui-config') config;
|
||||||
@service('env') env;
|
@service('env') env;
|
||||||
@service('client/http') client;
|
@service('client/http') client;
|
||||||
|
|
||||||
|
@ -13,16 +13,16 @@ export default class MetricsService extends RepositoryService {
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
super.init(...arguments);
|
super.init(...arguments);
|
||||||
const uiCfg = this.cfg.get();
|
const config = this.config.get();
|
||||||
// Inject whether or not the proxy is enabled as an option into the opaque
|
// Inject whether or not the proxy is enabled as an option into the opaque
|
||||||
// JSON options the user provided.
|
// JSON options the user provided.
|
||||||
const opts = uiCfg.metrics_provider_options || {};
|
const opts = config.metrics_provider_options || {};
|
||||||
opts.metrics_proxy_enabled = uiCfg.metrics_proxy_enabled;
|
opts.metrics_proxy_enabled = config.metrics_proxy_enabled;
|
||||||
// Inject a convenience function for dialing through the metrics proxy.
|
// Inject a convenience function for dialing through the metrics proxy.
|
||||||
opts.fetch = (path, params) =>
|
opts.fetch = (path, params) =>
|
||||||
this.client.fetchWithToken(`/v1/internal/ui/metrics-proxy${path}`, params);
|
this.client.fetchWithToken(`/v1/internal/ui/metrics-proxy${path}`, params);
|
||||||
// Inject the base app URL
|
// Inject the base app URL
|
||||||
const provider = uiCfg.metrics_provider || 'prometheus';
|
const provider = config.metrics_provider || 'prometheus';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.provider = window.consul.getMetricsProvider(provider, opts);
|
this.provider = window.consul.getMetricsProvider(provider, opts);
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
{{page-title item.Node}}
|
{{page-title item.Node}}
|
||||||
<DataLoader as |api|>
|
<DataLoader as |loader|>
|
||||||
|
|
||||||
<BlockSlot @name="data">
|
<BlockSlot @name="data">
|
||||||
|
<EventSource @src={{item}} @onerror={{action loader.dispatchError}} />
|
||||||
<EventSource @src={{tomography}} />
|
<EventSource @src={{tomography}} />
|
||||||
<EventSource @src={{item}} @onerror={{queue (action api.dispatchError)}} />
|
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
||||||
<BlockSlot @name="error">
|
<BlockSlot @name="error">
|
||||||
<AppError @error={{api.error}} />
|
<AppError @error={{loader.error}} />
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
||||||
<BlockSlot @name="disconnected" as |Notification|>
|
<BlockSlot @name="disconnected" as |Notification|>
|
||||||
{{#if (eq api.error.status "404")}}
|
{{#if (eq loader.error.status "404")}}
|
||||||
<Notification @sticky={{true}}>
|
<Notification @sticky={{true}}>
|
||||||
<p data-notification role="alert" class="warning notification-update">
|
<p data-notification role="alert" class="warning notification-update">
|
||||||
<strong>Warning!</strong>
|
<strong>Warning!</strong>
|
||||||
|
|
|
@ -21,12 +21,6 @@
|
||||||
as |sort filters items|}}
|
as |sort filters items|}}
|
||||||
|
|
||||||
<AppView>
|
<AppView>
|
||||||
<BlockSlot @name="notification" as |status type|>
|
|
||||||
<Consul::Service::Notifications
|
|
||||||
@type={{type}}
|
|
||||||
@status={{status}}
|
|
||||||
/>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h1>
|
<h1>
|
||||||
Services <em>{{format-number items.length}} total</em>
|
Services <em>{{format-number items.length}} total</em>
|
||||||
|
|
|
@ -1,64 +1,90 @@
|
||||||
{{page-title item.Service.ID}}
|
{{page-title item.Service.ID}}
|
||||||
<EventSource @src={{item}} @onerror={{action "error"}} />
|
<DataLoader as |loader|>
|
||||||
<EventSource @src={{proxy}} />
|
|
||||||
<EventSource @src={{proxyMeta}} />
|
<BlockSlot @name="data">
|
||||||
<AppView>
|
<EventSource @src={{item}} @onerror={{action loader.dispatchError}} />
|
||||||
<BlockSlot @name="notification" as |status type|>
|
{{#if (not loader.error)}}
|
||||||
<Consul::Service::Notifications
|
<EventSource @src={{proxy}} />
|
||||||
@type={{type}}
|
<EventSource @src={{proxyMeta}} />
|
||||||
@status={{status}}
|
{{/if}}
|
||||||
/>
|
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="breadcrumbs">
|
|
||||||
<ol>
|
<BlockSlot @name="error">
|
||||||
<li><a data-test-back href={{href-to 'dc.services'}}>All Services</a></li>
|
<AppError @error={{loader.error}} />
|
||||||
<li><a data-test-back href={{href-to 'dc.services.show'}}>Service ({{item.Service.Service}})</a></li>
|
|
||||||
</ol>
|
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="header">
|
|
||||||
<h1>
|
<BlockSlot @name="disconnected" as |Notification|>
|
||||||
{{ item.Service.ID }}
|
{{#if (eq loader.error.status "404")}}
|
||||||
</h1>
|
<Notification @sticky={{true}}>
|
||||||
<Consul::ExternalSource @item={{item}} />
|
<p data-notification role="alert" class="warning notification-update">
|
||||||
<Consul::Kind @item={{item}} @withInfo={{true}} />
|
<strong>Warning!</strong>
|
||||||
|
This service has been deregistered and no longer exists in the catalog.
|
||||||
|
</p>
|
||||||
|
</Notification>
|
||||||
|
{{else}}
|
||||||
|
<Notification @sticky={{true}}>
|
||||||
|
<p data-notification role="alert" class="warning notification-update">
|
||||||
|
<strong>Warning!</strong>
|
||||||
|
An error was returned whilst loading this data, refresh to try again.
|
||||||
|
</p>
|
||||||
|
</Notification>
|
||||||
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="nav">
|
|
||||||
<dl>
|
<BlockSlot @name="loaded">
|
||||||
<dt>Service Name</dt>
|
<AppView>
|
||||||
<dd><a href="{{href-to 'dc.services.show' item.Service.Service}}">{{item.Service.Service}}</a></dd>
|
<BlockSlot @name="breadcrumbs">
|
||||||
</dl>
|
<ol>
|
||||||
<dl>
|
<li><a data-test-back href={{href-to 'dc.services'}}>All Services</a></li>
|
||||||
<dt>Node Name</dt>
|
<li><a data-test-back href={{href-to 'dc.services.show'}}>Service ({{item.Service.Service}})</a></li>
|
||||||
<dd><a href="{{href-to 'dc.nodes.show' item.Node.Node}}">{{item.Node.Node}}</a></dd>
|
</ol>
|
||||||
</dl>
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="header">
|
||||||
|
<h1>
|
||||||
|
{{ item.Service.ID }}
|
||||||
|
</h1>
|
||||||
|
<Consul::ExternalSource @item={{item}} />
|
||||||
|
<Consul::Kind @item={{item}} @withInfo={{true}} />
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="nav">
|
||||||
|
<dl>
|
||||||
|
<dt>Service Name</dt>
|
||||||
|
<dd><a href="{{href-to 'dc.services.show' item.Service.Service}}">{{item.Service.Service}}</a></dd>
|
||||||
|
</dl>
|
||||||
|
<dl>
|
||||||
|
<dt>Node Name</dt>
|
||||||
|
<dd><a href="{{href-to 'dc.nodes.show' item.Node.Node}}">{{item.Node.Node}}</a></dd>
|
||||||
|
</dl>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="actions">
|
||||||
|
{{#let (or item.Service.Address item.Node.Address) as |address|}}
|
||||||
|
<CopyButton @value={{address}} @name="Address">{{address}}</CopyButton>
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="content">
|
||||||
|
<TabNav @items={{
|
||||||
|
compact
|
||||||
|
(array
|
||||||
|
(hash label="Health Checks" href=(href-to "dc.services.instance.healthchecks") selected=(is-href "dc.services.instance.healthchecks"))
|
||||||
|
(if
|
||||||
|
(eq item.Service.Kind 'mesh-gateway')
|
||||||
|
(hash label="Addresses" href=(href-to "dc.services.instance.addresses") selected=(is-href "dc.services.instance.addresses")) ""
|
||||||
|
)
|
||||||
|
(if proxy
|
||||||
|
(hash label="Upstreams" href=(href-to "dc.services.instance.upstreams") selected=(is-href "dc.services.instance.upstreams"))
|
||||||
|
)
|
||||||
|
(if proxy
|
||||||
|
(hash label="Exposed Paths" href=(href-to "dc.services.instance.exposedpaths") selected=(is-href "dc.services.instance.exposedpaths"))
|
||||||
|
)
|
||||||
|
(hash label="Tags & Meta" href=(href-to "dc.services.instance.metadata") selected=(is-href "dc.services.instance.metadata"))
|
||||||
|
)
|
||||||
|
}}/>
|
||||||
|
<Outlet
|
||||||
|
@name={{routeName}}
|
||||||
|
as |o|>
|
||||||
|
{{outlet}}
|
||||||
|
</Outlet>
|
||||||
|
</BlockSlot>
|
||||||
|
</AppView>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="actions">
|
</DataLoader>
|
||||||
{{#let (or item.Service.Address item.Node.Address) as |address|}}
|
|
||||||
<CopyButton @value={{address}} @name="Address">{{address}}</CopyButton>
|
|
||||||
{{/let}}
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="content">
|
|
||||||
<TabNav @items={{
|
|
||||||
compact
|
|
||||||
(array
|
|
||||||
(hash label="Health Checks" href=(href-to "dc.services.instance.healthchecks") selected=(is-href "dc.services.instance.healthchecks"))
|
|
||||||
(if
|
|
||||||
(eq item.Service.Kind 'mesh-gateway')
|
|
||||||
(hash label="Addresses" href=(href-to "dc.services.instance.addresses") selected=(is-href "dc.services.instance.addresses")) ""
|
|
||||||
)
|
|
||||||
(if proxy
|
|
||||||
(hash label="Upstreams" href=(href-to "dc.services.instance.upstreams") selected=(is-href "dc.services.instance.upstreams"))
|
|
||||||
)
|
|
||||||
(if proxy
|
|
||||||
(hash label="Exposed Paths" href=(href-to "dc.services.instance.exposedpaths") selected=(is-href "dc.services.instance.exposedpaths"))
|
|
||||||
)
|
|
||||||
(hash label="Tags & Meta" href=(href-to "dc.services.instance.metadata") selected=(is-href "dc.services.instance.metadata"))
|
|
||||||
)
|
|
||||||
}}/>
|
|
||||||
<Outlet
|
|
||||||
@name={{routeName}}
|
|
||||||
as |o|>
|
|
||||||
{{outlet}}
|
|
||||||
</Outlet>
|
|
||||||
</BlockSlot>
|
|
||||||
</AppView>
|
|
|
@ -1,73 +1,100 @@
|
||||||
<EventSource @src={{items}} @onerror={{action "error"}} />
|
|
||||||
<EventSource @src={{proxies}} />
|
|
||||||
{{#let items.firstObject as |item|}}
|
{{#let items.firstObject as |item|}}
|
||||||
{{page-title item.Service.Service}}
|
{{page-title item.Service.Service}}
|
||||||
<AppView>
|
<DataLoader as |loader|>
|
||||||
<BlockSlot @name="notification" as |status type|>
|
|
||||||
<Consul::Service::Notifications
|
<BlockSlot @name="data">
|
||||||
@type={{type}}
|
<EventSource @src={{items}} @onerror={{action loader.dispatchError}} />
|
||||||
@status={{status}}
|
{{#if (not loader.error)}}
|
||||||
/>
|
<EventSource @src={{proxies}} />
|
||||||
</BlockSlot>
|
<EventSource @src={{chain}} />
|
||||||
<BlockSlot @name="breadcrumbs">
|
|
||||||
<ol>
|
|
||||||
<li><a data-test-back href={{href-to 'dc.services'}}>All Services</a></li>
|
|
||||||
</ol>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="header">
|
|
||||||
<h1>
|
|
||||||
{{item.Service.Service}}
|
|
||||||
</h1>
|
|
||||||
<Consul::ExternalSource @item={{item.Service}} />
|
|
||||||
<Consul::Kind @item={{item.Service}} @withInfo={{true}} />
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="nav">
|
|
||||||
{{#if (not-eq item.Service.Kind 'mesh-gateway')}}
|
|
||||||
<TabNav @items={{
|
|
||||||
compact
|
|
||||||
(array
|
|
||||||
(if (and dc.MeshEnabled item.IsMeshOrigin (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"))
|
|
||||||
'')
|
|
||||||
(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"))
|
|
||||||
'')
|
|
||||||
(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="Instances" href=(href-to "dc.services.show.instances") selected=(is-href "dc.services.show.instances"))
|
|
||||||
(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"))
|
|
||||||
'')
|
|
||||||
(if (and dc.MeshEnabled item.IsOrigin)
|
|
||||||
(hash label="Routing" href=(href-to "dc.services.show.routing") selected=(is-href "dc.services.show.routing"))
|
|
||||||
'')
|
|
||||||
(if (not item.Service.Kind)
|
|
||||||
(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.Name
|
|
||||||
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">
|
|
||||||
<Outlet
|
<BlockSlot @name="error">
|
||||||
@name={{routeName}}
|
<AppError @error={{loader.error}} />
|
||||||
as |o|>
|
|
||||||
{{outlet}}
|
|
||||||
</Outlet>
|
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</AppView>
|
|
||||||
|
<BlockSlot @name="disconnected" as |Notification|>
|
||||||
|
{{#if (eq loader.error.status "404")}}
|
||||||
|
<Notification @sticky={{true}}>
|
||||||
|
<p data-notification role="alert" class="warning notification-update">
|
||||||
|
<strong>Warning!</strong>
|
||||||
|
This service has been deregistered and no longer exists in the catalog.
|
||||||
|
</p>
|
||||||
|
</Notification>
|
||||||
|
{{else}}
|
||||||
|
<Notification @sticky={{true}}>
|
||||||
|
<p data-notification role="alert" class="warning notification-update">
|
||||||
|
<strong>Warning!</strong>
|
||||||
|
An error was returned whilst loading this data, refresh to try again.
|
||||||
|
</p>
|
||||||
|
</Notification>
|
||||||
|
{{/if}}
|
||||||
|
</BlockSlot>
|
||||||
|
|
||||||
|
<BlockSlot @name="loaded">
|
||||||
|
<AppView>
|
||||||
|
<BlockSlot @name="breadcrumbs">
|
||||||
|
<ol>
|
||||||
|
<li><a data-test-back href={{href-to 'dc.services'}}>All Services</a></li>
|
||||||
|
</ol>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="header">
|
||||||
|
<h1>
|
||||||
|
{{item.Service.Service}}
|
||||||
|
</h1>
|
||||||
|
<Consul::ExternalSource @item={{item.Service}} />
|
||||||
|
<Consul::Kind @item={{item.Service}} @withInfo={{true}} />
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="nav">
|
||||||
|
{{#if (not-eq item.Service.Kind 'mesh-gateway')}}
|
||||||
|
<TabNav @items={{
|
||||||
|
compact
|
||||||
|
(array
|
||||||
|
(if (and dc.MeshEnabled item.IsMeshOrigin (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"))
|
||||||
|
'')
|
||||||
|
(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"))
|
||||||
|
'')
|
||||||
|
(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="Instances" href=(href-to "dc.services.show.instances") selected=(is-href "dc.services.show.instances"))
|
||||||
|
(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"))
|
||||||
|
'')
|
||||||
|
(if (and dc.MeshEnabled item.IsOrigin)
|
||||||
|
(hash label="Routing" href=(href-to "dc.services.show.routing") selected=(is-href "dc.services.show.routing"))
|
||||||
|
'')
|
||||||
|
(if (not item.Service.Kind)
|
||||||
|
(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.Name
|
||||||
|
Service=(hash Name=item.Service.Service)
|
||||||
|
)}}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
data-test-dashboard-anchor>
|
||||||
|
Open Dashboard
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="content">
|
||||||
|
<Outlet
|
||||||
|
@name={{routeName}}
|
||||||
|
as |o|>
|
||||||
|
{{outlet}}
|
||||||
|
</Outlet>
|
||||||
|
</BlockSlot>
|
||||||
|
</AppView>
|
||||||
|
</BlockSlot>
|
||||||
|
</DataLoader>
|
||||||
{{/let}}
|
{{/let}}
|
|
@ -32,7 +32,7 @@
|
||||||
Datacenter=dc.Name
|
Datacenter=dc.Name
|
||||||
Service=items.firstObject
|
Service=items.firstObject
|
||||||
)}}
|
)}}
|
||||||
@isRemoteDC={{isRemoteDC}}
|
@isRemoteDC={{not dc.Local}}
|
||||||
@hasMetricsProvider={{hasMetricsProvider}}
|
@hasMetricsProvider={{hasMetricsProvider}}
|
||||||
@oncreate={{route-action 'createIntention'}}
|
@oncreate={{route-action 'createIntention'}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -23,7 +23,10 @@ export default function(config = {}, win = window, doc = document) {
|
||||||
return str
|
return str
|
||||||
.split(';')
|
.split(';')
|
||||||
.filter(item => item !== '')
|
.filter(item => item !== '')
|
||||||
.map(item => item.trim().split('='));
|
.map(item => {
|
||||||
|
const [key, ...rest] = item.trim().split('=');
|
||||||
|
return [key, rest.join('=')];
|
||||||
|
});
|
||||||
};
|
};
|
||||||
const user = function(str) {
|
const user = function(str) {
|
||||||
const item = win.localStorage.getItem(str);
|
const item = win.localStorage.getItem(str);
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
import { module, test } from 'qunit';
|
|
||||||
import { setupTest } from 'ember-qunit';
|
|
||||||
|
|
||||||
module('Unit | Controller | dc/services/instance', function(hooks) {
|
|
||||||
setupTest(hooks);
|
|
||||||
|
|
||||||
// Replace this with your real tests.
|
|
||||||
test('it exists', function(assert) {
|
|
||||||
let controller = this.owner.lookup('controller:dc/services/instance');
|
|
||||||
assert.ok(controller);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,12 +0,0 @@
|
||||||
import { module, test } from 'qunit';
|
|
||||||
import { setupTest } from 'ember-qunit';
|
|
||||||
|
|
||||||
module('Unit | Controller | dc/services/show', function(hooks) {
|
|
||||||
setupTest(hooks);
|
|
||||||
|
|
||||||
// Replace this with your real tests.
|
|
||||||
test('it exists', function(assert) {
|
|
||||||
let controller = this.owner.lookup('controller:dc/services/show');
|
|
||||||
assert.ok(controller);
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in New Issue