mirror of https://github.com/status-im/consul.git
ui: Split ember-data service model into service/service-instance (#8557)
This commit is contained in:
parent
ad9f118d14
commit
1846e6316c
|
@ -0,0 +1,23 @@
|
||||||
|
import Adapter from './application';
|
||||||
|
// TODO: Update to use this.formatDatacenter()
|
||||||
|
export default Adapter.extend({
|
||||||
|
requestForQuery: function(request, { dc, ns, index, id, uri }) {
|
||||||
|
if (typeof id === 'undefined') {
|
||||||
|
throw new Error('You must specify an id');
|
||||||
|
}
|
||||||
|
return request`
|
||||||
|
GET /v1/health/service/${id}?${{ dc }}
|
||||||
|
X-Request-ID: ${uri}
|
||||||
|
|
||||||
|
${{
|
||||||
|
...this.formatNspace(ns),
|
||||||
|
index,
|
||||||
|
}}
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
requestForQueryRecord: function(request, { dc, ns, index, id, uri }) {
|
||||||
|
// query and queryRecord both use the same endpoint
|
||||||
|
// they are just serialized differently
|
||||||
|
return this.requestForQuery(...arguments);
|
||||||
|
},
|
||||||
|
});
|
|
@ -20,7 +20,7 @@
|
||||||
<ConsulInstanceChecks @type="service" @items={{filter-by 'ServiceID' '' item.Checks}} />
|
<ConsulInstanceChecks @type="service" @items={{filter-by 'ServiceID' '' item.Checks}} />
|
||||||
<ConsulInstanceChecks @type="node" @items={{reject-by 'ServiceID' '' item.Checks}} />
|
<ConsulInstanceChecks @type="node" @items={{reject-by 'ServiceID' '' item.Checks}} />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (get proxies item.Service.ID)}}
|
{{#if item.ProxyInstance}}
|
||||||
<dl class="proxy">
|
<dl class="proxy">
|
||||||
<dt>
|
<dt>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
import { get } from '@ember/object';
|
import { get } from '@ember/object';
|
||||||
|
import { alias } from '@ember/object/computed';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
dom: service('dom'),
|
dom: service('dom'),
|
||||||
notify: service('flashMessages'),
|
notify: service('flashMessages'),
|
||||||
|
item: alias('items.firstObject'),
|
||||||
actions: {
|
actions: {
|
||||||
error: function(e) {
|
error: function(e) {
|
||||||
if (e.target.readyState === 1) {
|
if (e.target.readyState === 1) {
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
import { computed } from '@ember/object';
|
|
||||||
import { alias } from '@ember/object/computed';
|
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
items: alias('item.Nodes'),
|
|
||||||
queryParams: {
|
queryParams: {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
search: {
|
search: {
|
||||||
|
@ -11,12 +8,4 @@ export default Controller.extend({
|
||||||
replace: true,
|
replace: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
keyedProxies: computed('proxies.[]', function() {
|
|
||||||
const proxies = {};
|
|
||||||
this.proxies.forEach(item => {
|
|
||||||
proxies[item.ServiceProxy.DestinationServiceID] = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
return proxies;
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import Model from 'ember-data/model';
|
||||||
|
import attr from 'ember-data/attr';
|
||||||
|
import { belongsTo } from 'ember-data/relationships';
|
||||||
|
import { filter, alias } from '@ember/object/computed';
|
||||||
|
|
||||||
|
export const PRIMARY_KEY = 'uid';
|
||||||
|
export const SLUG_KEY = 'Node.Node,Service.ID';
|
||||||
|
|
||||||
|
export default Model.extend({
|
||||||
|
[PRIMARY_KEY]: attr('string'),
|
||||||
|
[SLUG_KEY]: attr('string'),
|
||||||
|
Datacenter: attr('string'),
|
||||||
|
// ProxyInstance is the ember-data model relationship
|
||||||
|
ProxyInstance: belongsTo('Proxy'),
|
||||||
|
// Proxy is the actual JSON api response
|
||||||
|
Proxy: attr(),
|
||||||
|
Node: attr(),
|
||||||
|
Service: attr(),
|
||||||
|
Checks: attr(),
|
||||||
|
SyncTime: attr('number'),
|
||||||
|
meta: attr(),
|
||||||
|
Tags: alias('Service.Tags'),
|
||||||
|
Meta: alias('Service.Meta'),
|
||||||
|
Namespace: alias('Service.Namespace'),
|
||||||
|
ServiceChecks: filter('Checks', function(item, i, arr) {
|
||||||
|
return item.ServiceID !== '';
|
||||||
|
}),
|
||||||
|
NodeChecks: filter('Checks', function(item, i, arr) {
|
||||||
|
return item.ServiceID === '';
|
||||||
|
}),
|
||||||
|
});
|
|
@ -14,8 +14,8 @@ export default Model.extend({
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
InstanceCount: attr('number'),
|
InstanceCount: attr('number'),
|
||||||
ProxyFor: attr(),
|
|
||||||
Proxy: attr(),
|
Proxy: attr(),
|
||||||
|
ProxyFor: attr(),
|
||||||
Kind: attr('string'),
|
Kind: attr('string'),
|
||||||
ExternalSources: attr(),
|
ExternalSources: attr(),
|
||||||
GatewayConfig: attr(),
|
GatewayConfig: attr(),
|
||||||
|
|
|
@ -10,7 +10,7 @@ export default Route.extend({
|
||||||
return this.modelFor(parent);
|
return this.modelFor(parent);
|
||||||
},
|
},
|
||||||
afterModel: function(model, transition) {
|
afterModel: function(model, transition) {
|
||||||
if (get(model, 'item.Kind') !== 'mesh-gateway') {
|
if (get(model, 'item.Service.Kind') !== 'mesh-gateway') {
|
||||||
const parent = this.routeName
|
const parent = this.routeName
|
||||||
.split('.')
|
.split('.')
|
||||||
.slice(0, -1)
|
.slice(0, -1)
|
||||||
|
|
|
@ -10,7 +10,7 @@ export default Route.extend({
|
||||||
return this.modelFor(parent);
|
return this.modelFor(parent);
|
||||||
},
|
},
|
||||||
afterModel: function(model, transition) {
|
afterModel: function(model, transition) {
|
||||||
if (get(model, 'item.Kind') !== 'connect-proxy') {
|
if (get(model, 'item.Service.Kind') !== 'connect-proxy') {
|
||||||
const parent = this.routeName
|
const parent = this.routeName
|
||||||
.split('.')
|
.split('.')
|
||||||
.slice(0, -1)
|
.slice(0, -1)
|
||||||
|
|
|
@ -13,13 +13,15 @@ export default Route.extend({
|
||||||
slug: params.name,
|
slug: params.name,
|
||||||
dc: dc,
|
dc: dc,
|
||||||
nspace: nspace,
|
nspace: nspace,
|
||||||
item: this.data.source(uri => uri`/${nspace}/${dc}/service/${params.name}`),
|
items: this.data.source(
|
||||||
|
uri => uri`/${nspace}/${dc}/service-instances/for-service/${params.name}`
|
||||||
|
),
|
||||||
urls: this.settings.findBySlug('urls'),
|
urls: this.settings.findBySlug('urls'),
|
||||||
proxies: [],
|
proxies: [],
|
||||||
})
|
})
|
||||||
.then(model => {
|
.then(model => {
|
||||||
return ['connect-proxy', 'mesh-gateway', 'ingress-gateway', 'terminating-gateway'].includes(
|
return ['connect-proxy', 'mesh-gateway', 'ingress-gateway', 'terminating-gateway'].includes(
|
||||||
get(model, 'item.Service.Kind')
|
get(model, 'items.firstObject.Service.Kind')
|
||||||
)
|
)
|
||||||
? model
|
? model
|
||||||
: hash({
|
: hash({
|
||||||
|
@ -31,7 +33,9 @@ export default Route.extend({
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(model => {
|
.then(model => {
|
||||||
return ['ingress-gateway', 'terminating-gateway'].includes(get(model, 'item.Service.Kind'))
|
return ['ingress-gateway', 'terminating-gateway'].includes(
|
||||||
|
get(model, 'items.firstObject.Service.Kind')
|
||||||
|
)
|
||||||
? hash({
|
? hash({
|
||||||
...model,
|
...model,
|
||||||
gatewayServices: this.data.source(
|
gatewayServices: this.data.source(
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
import Serializer from './application';
|
||||||
|
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/service-instance';
|
||||||
|
|
||||||
|
export default Serializer.extend({
|
||||||
|
primaryKey: PRIMARY_KEY,
|
||||||
|
slugKey: SLUG_KEY,
|
||||||
|
respondForQuery: function(respond, query) {
|
||||||
|
return this._super(function(cb) {
|
||||||
|
return respond(function(headers, body) {
|
||||||
|
if (body.length === 0) {
|
||||||
|
const e = new Error();
|
||||||
|
e.errors = [
|
||||||
|
{
|
||||||
|
status: '404',
|
||||||
|
title: 'Not found',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
return cb(headers, body);
|
||||||
|
});
|
||||||
|
}, query);
|
||||||
|
},
|
||||||
|
respondForQueryRecord: function(respond, query) {
|
||||||
|
return this._super(function(cb) {
|
||||||
|
return respond(function(headers, body) {
|
||||||
|
body = body.find(function(item) {
|
||||||
|
return item.Node.Node === query.node && item.Service.ID === query.serviceId;
|
||||||
|
});
|
||||||
|
if (typeof body === 'undefined') {
|
||||||
|
const e = new Error();
|
||||||
|
e.errors = [
|
||||||
|
{
|
||||||
|
status: '404',
|
||||||
|
title: 'Not found',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
body.Namespace = body.Service.Namespace;
|
||||||
|
return cb(headers, body);
|
||||||
|
});
|
||||||
|
}, query);
|
||||||
|
},
|
||||||
|
});
|
|
@ -8,7 +8,8 @@ export default Service.extend({
|
||||||
gateways: service('repository/service'),
|
gateways: service('repository/service'),
|
||||||
services: service('repository/service'),
|
services: service('repository/service'),
|
||||||
service: service('repository/service'),
|
service: service('repository/service'),
|
||||||
['service-instance']: service('repository/service'),
|
['service-instance']: service('repository/service-instance'),
|
||||||
|
['service-instances']: service('repository/service-instance'),
|
||||||
proxies: service('repository/proxy'),
|
proxies: service('repository/proxy'),
|
||||||
['proxy-instance']: service('repository/proxy'),
|
['proxy-instance']: service('repository/proxy'),
|
||||||
['discovery-chain']: service('repository/discovery-chain'),
|
['discovery-chain']: service('repository/discovery-chain'),
|
||||||
|
@ -67,6 +68,14 @@ export default Service.extend({
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'service-instances':
|
||||||
|
[method, ...slug] = rest;
|
||||||
|
switch (method) {
|
||||||
|
case 'for-service':
|
||||||
|
find = configuration => repo.findByService(slug, dc, nspace, configuration);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'coordinates':
|
case 'coordinates':
|
||||||
[method, ...slug] = rest;
|
[method, ...slug] = rest;
|
||||||
switch (method) {
|
switch (method) {
|
||||||
|
@ -102,12 +111,15 @@ export default Service.extend({
|
||||||
case 'token':
|
case 'token':
|
||||||
find = configuration => repo.self(rest[1], dc);
|
find = configuration => repo.self(rest[1], dc);
|
||||||
break;
|
break;
|
||||||
case 'service':
|
|
||||||
case 'discovery-chain':
|
case 'discovery-chain':
|
||||||
case 'node':
|
case 'node':
|
||||||
find = configuration => repo.findBySlug(rest[0], dc, nspace, configuration);
|
find = configuration => repo.findBySlug(rest[0], dc, nspace, configuration);
|
||||||
break;
|
break;
|
||||||
case 'service-instance':
|
case 'service-instance':
|
||||||
|
// id, node, service
|
||||||
|
find = configuration =>
|
||||||
|
repo.findBySlug(rest[0], rest[1], rest[2], dc, nspace, configuration);
|
||||||
|
break;
|
||||||
case 'proxy-instance':
|
case 'proxy-instance':
|
||||||
// id, node, service
|
// id, node, service
|
||||||
find = configuration =>
|
find = configuration =>
|
||||||
|
|
|
@ -19,7 +19,20 @@ export default RepositoryService.extend({
|
||||||
query.index = configuration.cursor;
|
query.index = configuration.cursor;
|
||||||
query.uri = configuration.uri;
|
query.uri = configuration.uri;
|
||||||
}
|
}
|
||||||
return this.store.query(this.getModelName(), query);
|
return this.store.query(this.getModelName(), query).then(items => {
|
||||||
|
items.forEach(item => {
|
||||||
|
// swap out the id for the services id
|
||||||
|
// so we can then assign the proxy to it if it exists
|
||||||
|
const id = JSON.parse(item.uid);
|
||||||
|
id.pop();
|
||||||
|
id.push(item.ServiceProxy.DestinationServiceID);
|
||||||
|
const service = this.store.peekRecord('service-instance', JSON.stringify(id));
|
||||||
|
if (service) {
|
||||||
|
set(service, 'ProxyInstance', item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return items;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
findInstanceBySlug: function(id, node, slug, dc, nspace, configuration) {
|
findInstanceBySlug: function(id, node, slug, dc, nspace, configuration) {
|
||||||
return this.findAllBySlug(slug, dc, nspace, configuration).then(function(items) {
|
return this.findAllBySlug(slug, dc, nspace, configuration).then(function(items) {
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
import RepositoryService from 'consul-ui/services/repository';
|
||||||
|
const modelName = 'service-instance';
|
||||||
|
export default RepositoryService.extend({
|
||||||
|
getModelName: function() {
|
||||||
|
return modelName;
|
||||||
|
},
|
||||||
|
findByService: function(slug, dc, nspace, configuration = {}) {
|
||||||
|
const query = {
|
||||||
|
dc: dc,
|
||||||
|
nspace: nspace,
|
||||||
|
id: slug,
|
||||||
|
};
|
||||||
|
if (typeof configuration.cursor !== 'undefined') {
|
||||||
|
query.index = configuration.cursor;
|
||||||
|
query.uri = configuration.uri;
|
||||||
|
}
|
||||||
|
return this.store.query(this.getModelName(), query);
|
||||||
|
},
|
||||||
|
findBySlug: function(serviceId, node, service, dc, nspace, configuration = {}) {
|
||||||
|
const query = {
|
||||||
|
dc: dc,
|
||||||
|
ns: nspace,
|
||||||
|
serviceId: serviceId,
|
||||||
|
node: node,
|
||||||
|
id: service,
|
||||||
|
};
|
||||||
|
if (typeof configuration.cursor !== 'undefined') {
|
||||||
|
query.index = configuration.cursor;
|
||||||
|
query.uri = configuration.uri;
|
||||||
|
}
|
||||||
|
return this.store.queryRecord(this.getModelName(), query);
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,74 +1,9 @@
|
||||||
import RepositoryService from 'consul-ui/services/repository';
|
import RepositoryService from 'consul-ui/services/repository';
|
||||||
import { get, set } from '@ember/object';
|
|
||||||
const modelName = 'service';
|
const modelName = 'service';
|
||||||
export default RepositoryService.extend({
|
export default RepositoryService.extend({
|
||||||
getModelName: function() {
|
getModelName: function() {
|
||||||
return modelName;
|
return modelName;
|
||||||
},
|
},
|
||||||
findBySlug: function(slug, dc) {
|
|
||||||
return this._super(...arguments).then(function(item) {
|
|
||||||
// TODO: Move this to the Serializer
|
|
||||||
const nodes = get(item, 'Nodes');
|
|
||||||
if (nodes.length === 0) {
|
|
||||||
// TODO: Add an store.error("404", "message") or similar
|
|
||||||
// or move all this to serializer
|
|
||||||
const e = new Error();
|
|
||||||
e.errors = [
|
|
||||||
{
|
|
||||||
status: '404',
|
|
||||||
title: 'Not found',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
const service = get(nodes, 'firstObject');
|
|
||||||
// TODO: Use [...new Set()] instead of uniq
|
|
||||||
const tags = nodes
|
|
||||||
.reduce(function(prev, item) {
|
|
||||||
return prev.concat(get(item, 'Service.Tags') || []);
|
|
||||||
}, [])
|
|
||||||
.uniq();
|
|
||||||
set(service, 'Tags', tags);
|
|
||||||
set(service, 'Nodes', nodes);
|
|
||||||
set(service, 'meta', get(item, 'meta'));
|
|
||||||
set(service, 'Namespace', get(item, 'Namespace'));
|
|
||||||
return service;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
findInstanceBySlug: function(id, node, slug, dc, nspace, configuration) {
|
|
||||||
return this.findBySlug(slug, dc, nspace, configuration).then(function(item) {
|
|
||||||
// TODO: Move this to the Serializer
|
|
||||||
// Loop through all the service instances and pick out the one
|
|
||||||
// that has the same service id AND node name
|
|
||||||
// node names are unique per datacenter
|
|
||||||
const i = item.Nodes.findIndex(function(item) {
|
|
||||||
return item.Service.ID === id && item.Node.Node === node;
|
|
||||||
});
|
|
||||||
if (i !== -1) {
|
|
||||||
const service = item.Nodes[i].Service;
|
|
||||||
service.Node = item.Nodes[i].Node;
|
|
||||||
service.ServiceChecks = item.Nodes[i].Checks.filter(function(item) {
|
|
||||||
return item.ServiceID != '';
|
|
||||||
});
|
|
||||||
service.NodeChecks = item.Nodes[i].Checks.filter(function(item) {
|
|
||||||
return item.ServiceID == '';
|
|
||||||
});
|
|
||||||
set(service, 'meta', get(item, 'meta'));
|
|
||||||
set(service, 'Namespace', get(item, 'Namespace'));
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
// TODO: Add an store.error("404", "message") or similar
|
|
||||||
// or move all this to serializer
|
|
||||||
const e = new Error();
|
|
||||||
e.errors = [
|
|
||||||
{
|
|
||||||
status: '404',
|
|
||||||
title: 'Unable to find instance',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
throw e;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
findGatewayBySlug: function(slug, dc, nspace, configuration = {}) {
|
findGatewayBySlug: function(slug, dc, nspace, configuration = {}) {
|
||||||
const query = {
|
const query = {
|
||||||
dc: dc,
|
dc: dc,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{{title item.ID}}
|
{{title item.Service.ID}}
|
||||||
<EventSource @src={{item}} @onerror={{action "error"}} />
|
<EventSource @src={{item}} @onerror={{action "error"}} />
|
||||||
<EventSource @src={{proxy}} />
|
<EventSource @src={{proxy}} />
|
||||||
<EventSource @src={{proxyMeta}} />
|
<EventSource @src={{proxyMeta}} />
|
||||||
|
@ -9,12 +9,12 @@
|
||||||
<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>
|
||||||
<li><a data-test-back href={{href-to 'dc.services.show'}}>Service ({{item.Service}})</a></li>
|
<li><a data-test-back href={{href-to 'dc.services.show'}}>Service ({{item.Service.Service}})</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h1>
|
<h1>
|
||||||
{{ item.ID }}
|
{{ item.Service.ID }}
|
||||||
</h1>
|
</h1>
|
||||||
<ConsulExternalSource @item={{item}} />
|
<ConsulExternalSource @item={{item}} />
|
||||||
<ConsulKind @item={{item}} @withInfo={{true}} />
|
<ConsulKind @item={{item}} @withInfo={{true}} />
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
<BlockSlot @name="nav">
|
<BlockSlot @name="nav">
|
||||||
<dl>
|
<dl>
|
||||||
<dt>Service Name</dt>
|
<dt>Service Name</dt>
|
||||||
<dd><a href="{{href-to 'dc.services.show' item.Service}}">{{item.Service}}</a></dd>
|
<dd><a href="{{href-to 'dc.services.show' item.Service.Service}}">{{item.Service.Service}}</a></dd>
|
||||||
</dl>
|
</dl>
|
||||||
<dl>
|
<dl>
|
||||||
<dt>Node Name</dt>
|
<dt>Node Name</dt>
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
</dl>
|
</dl>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="actions">
|
<BlockSlot @name="actions">
|
||||||
{{#let (or item.Address item.Node.Address) as |address|}}
|
{{#let (or item.Service.Address item.Node.Address) as |address|}}
|
||||||
<CopyButton @value={{address}} @name="Address">{{address}}</CopyButton>
|
<CopyButton @value={{address}} @name="Address">{{address}}</CopyButton>
|
||||||
{{/let}}
|
{{/let}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
(array
|
(array
|
||||||
(hash label="Health Checks" href=(href-to "dc.services.instance.healthchecks") selected=(is-href "dc.services.instance.healthchecks"))
|
(hash label="Health Checks" href=(href-to "dc.services.instance.healthchecks") selected=(is-href "dc.services.instance.healthchecks"))
|
||||||
(if
|
(if
|
||||||
(eq item.Kind 'mesh-gateway')
|
(eq item.Service.Kind 'mesh-gateway')
|
||||||
(hash label="Addresses" href=(href-to "dc.services.instance.addresses") selected=(is-href "dc.services.instance.addresses")) ""
|
(hash label="Addresses" href=(href-to "dc.services.instance.addresses") selected=(is-href "dc.services.instance.addresses")) ""
|
||||||
)
|
)
|
||||||
(if proxy
|
(if proxy
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<div id="addresses" class="tab-section">
|
<div id="addresses" class="tab-section">
|
||||||
<div role="tabpanel">
|
<div role="tabpanel">
|
||||||
{{#if item.TaggedAddresses }}
|
{{#if item.Service.TaggedAddresses }}
|
||||||
<TabularCollection
|
<TabularCollection
|
||||||
data-test-addresses
|
data-test-addresses
|
||||||
@items={{object-entries item.TaggedAddresses}} as |taggedAddress index|
|
@items={{object-entries item.Service.TaggedAddresses}} as |taggedAddress index|
|
||||||
>
|
>
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<th>Tag</th>
|
<th>Tag</th>
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
<div class="tab-section">
|
<div class="tab-section">
|
||||||
<div role="tabpanel">
|
<div role="tabpanel">
|
||||||
{{#if (gt proxy.Proxy.Upstreams.length 0)}}
|
{{#if (gt proxy.Service.Proxy.Upstreams.length 0)}}
|
||||||
<section class="proxy-upstreams">
|
<section class="proxy-upstreams">
|
||||||
<h3>Upstreams</h3>
|
<h3>Upstreams</h3>
|
||||||
<ConsulUpstreamInstanceList @items={{proxy.Proxy.Upstreams}} @dc={{dc}} @nspace={{nspace}} />
|
<ConsulUpstreamInstanceList @items={{proxy.Service.Proxy.Upstreams}} @dc={{dc}} @nspace={{nspace}} />
|
||||||
</section>
|
</section>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (gt proxy.Proxy.Expose.Paths.length 0)}}
|
{{#if (gt proxy.Service.Proxy.Expose.Paths.length 0)}}
|
||||||
<section class="proxy-exposed-paths">
|
<section class="proxy-exposed-paths">
|
||||||
<h3>Exposed paths</h3>
|
<h3>Exposed paths</h3>
|
||||||
<p>
|
<p>
|
||||||
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.
|
||||||
</p>
|
</p>
|
||||||
<ConsulExposedPathList @items={{proxy.Proxy.Expose.Paths}} @address={{item.Address}} />
|
<ConsulExposedPathList @items={{proxy.Service.Proxy.Expose.Paths}} @address={{item.Address}} />
|
||||||
</section>
|
</section>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (or (gt proxy.ServiceChecks.length 0) (gt proxy.NodeChecks.length 0))}}
|
{{#if (or (gt proxy.ServiceChecks.length 0) (gt proxy.NodeChecks.length 0))}}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
{{title item.Service.Service}}
|
<EventSource @src={{items}} @onerror={{action "error"}} />
|
||||||
<EventSource @src={{item}} @onerror={{action "error"}} />
|
|
||||||
<EventSource @src={{chain}} />
|
<EventSource @src={{chain}} />
|
||||||
<EventSource @src={{intentions}} />
|
<EventSource @src={{intentions}} />
|
||||||
<EventSource @src={{proxies}} />
|
<EventSource @src={{proxies}} />
|
||||||
<EventSource @src={{gatewayServices}} />
|
<EventSource @src={{gatewayServices}} />
|
||||||
|
{{title item.Service.Service}}
|
||||||
<AppView @class="service show">
|
<AppView @class="service show">
|
||||||
<BlockSlot @name="notification" as |status type|>
|
<BlockSlot @name="notification" as |status type|>
|
||||||
{{partial 'dc/services/notifications'}}
|
{{partial 'dc/services/notifications'}}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<ChangeableSet @dispatcher={{searchable 'serviceInstance' items}} @terms={{search}}>
|
<ChangeableSet @dispatcher={{searchable 'serviceInstance' items}} @terms={{search}}>
|
||||||
<BlockSlot @name="set" as |filtered|>
|
<BlockSlot @name="set" as |filtered|>
|
||||||
<ConsulServiceInstanceList @routeName="dc.services.instance" @items={{filtered}} @proxies={{keyedProxies}}/>
|
<ConsulServiceInstanceList @routeName="dc.services.instance" @items={{filtered}}/>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="empty">
|
<BlockSlot @name="empty">
|
||||||
<EmptyState>
|
<EmptyState>
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<div id="tags" class="tab-section">
|
<div id="tags" class="tab-section">
|
||||||
<div role="tabpanel">
|
<div role="tabpanel">
|
||||||
{{#if (gt item.Tags.length 0) }}
|
{{#let (flatten (map-by "Tags" items)) as |tags|}}
|
||||||
<TagList @item={{item}} />
|
{{#if (gt tags.length 0) }}
|
||||||
{{else}}
|
<TagList @item={{hash Tags=tags}} />
|
||||||
|
{{else}}
|
||||||
<EmptyState>
|
<EmptyState>
|
||||||
<BlockSlot @name="body">
|
<BlockSlot @name="body">
|
||||||
<p>
|
<p>
|
||||||
|
@ -10,6 +11,7 @@
|
||||||
</p>
|
</p>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</EmptyState>
|
</EmptyState>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{/let}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { get } from '@ember/object';
|
||||||
export default function(foreignKey, nspaceKey, hash = JSON.stringify) {
|
export default function(foreignKey, nspaceKey, hash = JSON.stringify) {
|
||||||
return function(primaryKey, slugKey, foreignKeyValue) {
|
return function(primaryKey, slugKey, foreignKeyValue) {
|
||||||
if (foreignKeyValue == null || foreignKeyValue.length < 1) {
|
if (foreignKeyValue == null || foreignKeyValue.length < 1) {
|
||||||
|
@ -6,12 +7,12 @@ export default function(foreignKey, nspaceKey, hash = JSON.stringify) {
|
||||||
return function(item) {
|
return function(item) {
|
||||||
const slugKeys = slugKey.split(',');
|
const slugKeys = slugKey.split(',');
|
||||||
const slugValues = slugKeys.map(function(slugKey) {
|
const slugValues = slugKeys.map(function(slugKey) {
|
||||||
if (item[slugKey] == null || item[slugKey].length < 1) {
|
if (get(item, slugKey) == null || get(item, slugKey).length < 1) {
|
||||||
throw new Error('Unable to create fingerprint, missing slug');
|
throw new Error('Unable to create fingerprint, missing slug');
|
||||||
}
|
}
|
||||||
return item[slugKey];
|
return get(item, slugKey);
|
||||||
});
|
});
|
||||||
const nspaceValue = item[nspaceKey] || 'default';
|
const nspaceValue = get(item, nspaceKey) || 'default';
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
...{
|
...{
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { module, test } from 'qunit';
|
||||||
|
import { setupTest } from 'ember-qunit';
|
||||||
|
import { env } from '../../../env';
|
||||||
|
const shouldHaveNspace = function(nspace) {
|
||||||
|
return typeof nspace !== 'undefined' && env('CONSUL_NSPACES_ENABLED');
|
||||||
|
};
|
||||||
|
module('Integration | Adapter | service-instance', function(hooks) {
|
||||||
|
setupTest(hooks);
|
||||||
|
const dc = 'dc-1';
|
||||||
|
const id = 'service-name';
|
||||||
|
const undefinedNspace = 'default';
|
||||||
|
[undefinedNspace, 'team-1', undefined].forEach(nspace => {
|
||||||
|
test(`requestForQueryRecord returns the correct url/method when nspace is ${nspace}`, function(assert) {
|
||||||
|
const adapter = this.owner.lookup('adapter:service-instance');
|
||||||
|
const client = this.owner.lookup('service:client/http');
|
||||||
|
const expected = `GET /v1/health/service/${id}?dc=${dc}${
|
||||||
|
shouldHaveNspace(nspace) ? `&ns=${nspace}` : ``
|
||||||
|
}`;
|
||||||
|
let actual = adapter.requestForQueryRecord(client.requestParams.bind(client), {
|
||||||
|
dc: dc,
|
||||||
|
id: id,
|
||||||
|
ns: nspace,
|
||||||
|
});
|
||||||
|
assert.equal(`${actual.method} ${actual.url}`, expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test("requestForQueryRecord throws if you don't specify an id", function(assert) {
|
||||||
|
const adapter = this.owner.lookup('adapter:service-instance');
|
||||||
|
const client = this.owner.lookup('service:client/http');
|
||||||
|
assert.throws(function() {
|
||||||
|
adapter.requestForQueryRecord(client.url, {
|
||||||
|
dc: dc,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { module, test } from 'qunit';
|
||||||
|
import { setupTest } from 'ember-qunit';
|
||||||
|
import { get } from 'consul-ui/tests/helpers/api';
|
||||||
|
import {
|
||||||
|
HEADERS_SYMBOL as META,
|
||||||
|
HEADERS_DATACENTER as DC,
|
||||||
|
HEADERS_NAMESPACE as NSPACE,
|
||||||
|
} from 'consul-ui/utils/http/consul';
|
||||||
|
module('Integration | Serializer | service-instance', function(hooks) {
|
||||||
|
setupTest(hooks);
|
||||||
|
const dc = 'dc-1';
|
||||||
|
const undefinedNspace = 'default';
|
||||||
|
[undefinedNspace, 'team-1', undefined].forEach(nspace => {
|
||||||
|
test(`respondForQueryRecord returns the correct data for item endpoint when nspace is ${nspace}`, function(assert) {
|
||||||
|
const serializer = this.owner.lookup('serializer:service-instance');
|
||||||
|
const id = 'service-name';
|
||||||
|
const node = 'node-0';
|
||||||
|
const request = {
|
||||||
|
url: `/v1/health/service/${id}?dc=${dc}${
|
||||||
|
typeof nspace !== 'undefined' ? `&ns=${nspace}` : ``
|
||||||
|
}`,
|
||||||
|
};
|
||||||
|
return get(request.url).then(function(payload) {
|
||||||
|
payload[0].Node.Node = node;
|
||||||
|
payload[0].Service.ID = id;
|
||||||
|
const expected = {
|
||||||
|
...payload[0],
|
||||||
|
Datacenter: dc,
|
||||||
|
[META]: {
|
||||||
|
[DC.toLowerCase()]: dc,
|
||||||
|
[NSPACE.toLowerCase()]: payload[0].Service.Namespace || undefinedNspace,
|
||||||
|
},
|
||||||
|
Namespace: payload[0].Service.Namespace || undefinedNspace,
|
||||||
|
uid: `["${payload[0].Service.Namespace || undefinedNspace}","${dc}","${node}","${id}"]`,
|
||||||
|
};
|
||||||
|
const actual = serializer.respondForQueryRecord(
|
||||||
|
function(cb) {
|
||||||
|
const headers = {};
|
||||||
|
const body = payload;
|
||||||
|
return cb(headers, body);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dc: dc,
|
||||||
|
ns: nspace,
|
||||||
|
id: id,
|
||||||
|
node: node,
|
||||||
|
serviceId: id,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert.deepEqual(actual, expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,11 +1,6 @@
|
||||||
import { module, test } from 'qunit';
|
import { module, test } from 'qunit';
|
||||||
import { setupTest } from 'ember-qunit';
|
import { setupTest } from 'ember-qunit';
|
||||||
import { get } from 'consul-ui/tests/helpers/api';
|
import { get } from 'consul-ui/tests/helpers/api';
|
||||||
import {
|
|
||||||
HEADERS_SYMBOL as META,
|
|
||||||
HEADERS_DATACENTER as DC,
|
|
||||||
HEADERS_NAMESPACE as NSPACE,
|
|
||||||
} from 'consul-ui/utils/http/consul';
|
|
||||||
module('Integration | Serializer | service', function(hooks) {
|
module('Integration | Serializer | service', function(hooks) {
|
||||||
setupTest(hooks);
|
setupTest(hooks);
|
||||||
const dc = 'dc-1';
|
const dc = 'dc-1';
|
||||||
|
@ -73,40 +68,5 @@ module('Integration | Serializer | service', function(hooks) {
|
||||||
assert.deepEqual(actual, expected);
|
assert.deepEqual(actual, expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
test(`respondForQueryRecord returns the correct data for item endpoint when nspace is ${nspace}`, function(assert) {
|
|
||||||
const serializer = this.owner.lookup('serializer:service');
|
|
||||||
const id = 'service-name';
|
|
||||||
const request = {
|
|
||||||
url: `/v1/health/service/${id}?dc=${dc}${
|
|
||||||
typeof nspace !== 'undefined' ? `&ns=${nspace}` : ``
|
|
||||||
}`,
|
|
||||||
};
|
|
||||||
return get(request.url).then(function(payload) {
|
|
||||||
const expected = {
|
|
||||||
Datacenter: dc,
|
|
||||||
[META]: {
|
|
||||||
[DC.toLowerCase()]: dc,
|
|
||||||
[NSPACE.toLowerCase()]: payload[0].Service.Namespace || undefinedNspace,
|
|
||||||
},
|
|
||||||
Namespace: payload[0].Service.Namespace || undefinedNspace,
|
|
||||||
uid: `["${payload[0].Service.Namespace || undefinedNspace}","${dc}","${id}"]`,
|
|
||||||
Name: id,
|
|
||||||
Nodes: payload,
|
|
||||||
};
|
|
||||||
const actual = serializer.respondForQueryRecord(
|
|
||||||
function(cb) {
|
|
||||||
const headers = {};
|
|
||||||
const body = payload;
|
|
||||||
return cb(headers, body);
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dc: dc,
|
|
||||||
ns: nspace,
|
|
||||||
id: id,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
assert.deepEqual(actual, expected);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,89 +7,9 @@ moduleFor(`service:repository/${NAME}`, `Integration | Service | ${NAME}`, {
|
||||||
integration: true,
|
integration: true,
|
||||||
});
|
});
|
||||||
const dc = 'dc-1';
|
const dc = 'dc-1';
|
||||||
const id = 'token-name';
|
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
const undefinedNspace = 'default';
|
const undefinedNspace = 'default';
|
||||||
[undefinedNspace, 'team-1', undefined].forEach(nspace => {
|
[undefinedNspace, 'team-1', undefined].forEach(nspace => {
|
||||||
test(`findInstanceBySlug calls findBySlug with the correct arguments when nspace is ${nspace}`, function(assert) {
|
|
||||||
assert.expect(4);
|
|
||||||
const id = 'id';
|
|
||||||
const slug = 'slug';
|
|
||||||
const node = 'node-name';
|
|
||||||
|
|
||||||
const datacenter = 'dc-1';
|
|
||||||
const conf = {
|
|
||||||
cursor: 1,
|
|
||||||
};
|
|
||||||
const service = this.subject();
|
|
||||||
service.findBySlug = function(slug, dc, nspace, configuration) {
|
|
||||||
assert.equal(
|
|
||||||
arguments.length,
|
|
||||||
4,
|
|
||||||
'Expected to be called with the correct number of arguments'
|
|
||||||
);
|
|
||||||
assert.equal(dc, datacenter);
|
|
||||||
assert.deepEqual(configuration, conf);
|
|
||||||
return Promise.resolve({ Nodes: [] });
|
|
||||||
};
|
|
||||||
// This will throw an error as we don't resolve any services that match what was requested
|
|
||||||
// so a 404 is the correct error response
|
|
||||||
return service.findInstanceBySlug(id, slug, node, datacenter, nspace, conf).catch(function(e) {
|
|
||||||
assert.equal(e.errors[0].status, '404');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test(`findBySlug returns the correct data for item endpoint when the nspace is ${nspace}`, function(assert) {
|
|
||||||
return repo(
|
|
||||||
'Service',
|
|
||||||
'findBySlug',
|
|
||||||
this.subject(),
|
|
||||||
function(stub) {
|
|
||||||
return stub(
|
|
||||||
`/v1/health/service/${id}?dc=${dc}${
|
|
||||||
typeof nspace !== 'undefined' ? `&ns=${nspace}` : ``
|
|
||||||
}`,
|
|
||||||
{
|
|
||||||
CONSUL_NODE_COUNT: 1,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
function(service) {
|
|
||||||
return service.findBySlug(id, dc, nspace || undefinedNspace);
|
|
||||||
},
|
|
||||||
function(actual, expected) {
|
|
||||||
assert.deepEqual(
|
|
||||||
actual,
|
|
||||||
expected(function(payload) {
|
|
||||||
// TODO: So this tree is all 'wrong', it's not having any major impact
|
|
||||||
// this this tree needs revisting to something that makes more sense
|
|
||||||
payload = Object.assign(
|
|
||||||
{},
|
|
||||||
{ Nodes: payload },
|
|
||||||
{
|
|
||||||
Datacenter: dc,
|
|
||||||
Namespace: payload[0].Service.Namespace || undefinedNspace,
|
|
||||||
uid: `["${payload[0].Service.Namespace || undefinedNspace}","${dc}","${id}"]`,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const nodes = payload.Nodes;
|
|
||||||
const service = payload.Nodes[0];
|
|
||||||
service.Nodes = nodes;
|
|
||||||
service.Tags = [...new Set(payload.Nodes[0].Service.Tags)];
|
|
||||||
service.Namespace = payload.Namespace;
|
|
||||||
service.meta = {
|
|
||||||
cacheControl: undefined,
|
|
||||||
cursor: undefined,
|
|
||||||
dc: dc,
|
|
||||||
nspace: payload.Namespace,
|
|
||||||
};
|
|
||||||
|
|
||||||
return service;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
test(`findGatewayBySlug returns the correct data for list endpoint when nspace is ${nspace}`, function(assert) {
|
test(`findGatewayBySlug returns the correct data for list endpoint when nspace is ${nspace}`, function(assert) {
|
||||||
get(this.subject(), 'store').serializerFor(NAME).timestamp = function() {
|
get(this.subject(), 'store').serializerFor(NAME).timestamp = function() {
|
||||||
return now;
|
return now;
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { module, test } from 'qunit';
|
||||||
|
import { setupTest } from 'ember-qunit';
|
||||||
|
|
||||||
|
module('Unit | Adapter | service-instance', function(hooks) {
|
||||||
|
setupTest(hooks);
|
||||||
|
|
||||||
|
// Replace this with your real tests.
|
||||||
|
test('it exists', function(assert) {
|
||||||
|
let adapter = this.owner.lookup('adapter:service-instance');
|
||||||
|
assert.ok(adapter);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { module, test } from 'qunit';
|
||||||
|
import { setupTest } from 'ember-qunit';
|
||||||
|
|
||||||
|
module('Unit | Model | service-instance', function(hooks) {
|
||||||
|
setupTest(hooks);
|
||||||
|
|
||||||
|
// Replace this with your real tests.
|
||||||
|
test('it exists', function(assert) {
|
||||||
|
let store = this.owner.lookup('service:store');
|
||||||
|
let model = store.createRecord('service-instance', {});
|
||||||
|
assert.ok(model);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { module, test } from 'qunit';
|
||||||
|
import { setupTest } from 'ember-qunit';
|
||||||
|
|
||||||
|
module('Unit | Serializer | service-instance', function(hooks) {
|
||||||
|
setupTest(hooks);
|
||||||
|
|
||||||
|
// Replace this with your real tests.
|
||||||
|
test('it exists', function(assert) {
|
||||||
|
let store = this.owner.lookup('service:store');
|
||||||
|
let serializer = store.serializerFor('service-instance');
|
||||||
|
|
||||||
|
assert.ok(serializer);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it serializes records', function(assert) {
|
||||||
|
let store = this.owner.lookup('service:store');
|
||||||
|
let record = store.createRecord('service-instance', {});
|
||||||
|
|
||||||
|
let serializedRecord = record.serialize();
|
||||||
|
|
||||||
|
assert.ok(serializedRecord);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { module, test } from 'qunit';
|
||||||
|
import { setupTest } from 'ember-qunit';
|
||||||
|
|
||||||
|
module('Unit | Repository | service-instance', function(hooks) {
|
||||||
|
setupTest(hooks);
|
||||||
|
|
||||||
|
// Replace this with your real tests.
|
||||||
|
test('it exists', function(assert) {
|
||||||
|
const repo = this.owner.lookup('service:repository/service-instance');
|
||||||
|
assert.ok(repo);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue