Paul Banks 27048a0612
Add metrics rendering to the new topology view. (#8858)
* Remove unused StatsCard component

* Create Card and Stats contextual components with styling

* Send endpoint, item, and protocol to Stats as props

* WIP basic plumbing for metrics in Ember

* WIP metrics data source now works for different protocols and produces reasonable mock responses

* WIP sparkline component

* Mostly working metrics and graphs in topology

* Fix date in tooltip to actually be correct

* Clean up console.log

* Add loading frame and create a style sheet for Stats

* Various polish fixes:

 - Loading state for graph
 - Added fake latency cookie value to test loading
 - If metrics provider has no series/stats for the service show something that doesn't look broken
 - Graph hover works right to the edge now
 - Stats boxes now wrap so they are either shown or not as will fit not cut off
 - Graph resizes when browser window size changes
 - Some tweaks to number formats and stat metrics to make them more compact/useful

* Thread Protocol through topology model correctly

* Rebuild assetfs

* Fix failing tests and remove stats-card now it's changed and become different

* Fix merge conflict

* Update api doublt

* more merge fixes

* Add data-permission and id attr to Card

* Run JS linter

* Move things around so the tests run with everything available

* Get tests passing:

1. Remove fakeLatency setTimeout (will be replaced with CONSUL_LATENCY
in mocks)
2. Make sure any event handlers are removed

* Make sure the Consul/scripts are available before the app

* Make sure interval gets set if there is no cookie value

* Upgrade mocks so we can use CONSUL_LATENCY

* Fix handling of no series values from Prometheus

* Update assetfs and fix a comment

* Rebase and rebuild assetfs; fix tcp metric series units to be bits not bytes

* Rebuild assetfs

* Hide stats when provider is not configured

Co-authored-by: kenia <keniavalladarez@gmail.com>
Co-authored-by: John Cowen <jcowen@hashicorp.com>
2020-10-09 21:31:15 +01:00

184 lines
6.3 KiB
JavaScript

import Service, { inject as service } from '@ember/service';
import { get } from '@ember/object';
export default Service.extend({
datacenters: service('repository/dc'),
nodes: service('repository/node'),
node: service('repository/node'),
leader: service('repository/node'),
gateways: service('repository/service'),
services: service('repository/service'),
service: service('repository/service'),
['service-instance']: service('repository/service-instance'),
['service-instances']: service('repository/service-instance'),
proxies: service('repository/proxy'),
['proxy-instance']: service('repository/proxy'),
['discovery-chain']: service('repository/discovery-chain'),
['topology']: service('repository/topology'),
coordinates: service('repository/coordinate'),
sessions: service('repository/session'),
namespaces: service('repository/nspace'),
intentions: service('repository/intention'),
intention: service('repository/intention'),
kv: service('repository/kv'),
token: service('repository/token'),
policies: service('repository/policy'),
policy: service('repository/policy'),
roles: service('repository/role'),
oidc: service('repository/oidc-provider'),
metrics: service('repository/metrics'),
type: service('data-source/protocols/http/blocking'),
source: function(src, configuration) {
// TODO: Consider adding/requiring 'action': nspace, dc, model, action, ...rest
const [, nspace, dc, model, ...rest] = src.split('/').map(decodeURIComponent);
// nspaces can be filled, blank or *
// so we might get urls like //dc/services
let find;
const repo = this[model];
configuration.createEvent = function(result = {}, configuration) {
const event = {
type: 'message',
data: result,
};
const meta = get(event, 'data.meta') || {};
if (typeof meta.range === 'undefined') {
repo.reconcile(meta);
}
return event;
};
let method, slug, more, protocol;
switch (model) {
case 'metrics':
[method, slug, ...more] = rest;
switch (method) {
case 'summary-for-service':
[protocol, ...more] = more;
find = configuration =>
repo.findServiceSummary(protocol, slug, dc, nspace, configuration);
break;
case 'upstream-summary-for-service':
find = configuration => repo.findUpstreamSummary(slug, dc, nspace, configuration);
break;
case 'downstream-summary-for-service':
find = configuration => repo.findDownstreamSummary(slug, dc, nspace, configuration);
break;
}
break;
case 'datacenters':
case 'namespaces':
find = configuration => repo.findAll(configuration);
break;
case 'services':
case 'nodes':
case 'roles':
case 'policies':
find = configuration => repo.findAllByDatacenter(dc, nspace, configuration);
break;
case 'leader':
find = configuration => repo.findLeader(dc, configuration);
break;
case 'intentions':
[method, ...slug] = rest;
switch (method) {
case 'for-service':
find = configuration => repo.findByService(slug, dc, nspace, configuration);
break;
default:
find = configuration => repo.findAllByDatacenter(dc, nspace, configuration);
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':
[method, ...slug] = rest;
switch (method) {
case 'for-node':
find = configuration => repo.findAllByNode(slug, dc, configuration);
break;
}
break;
case 'proxies':
[method, ...slug] = rest;
switch (method) {
case 'for-service':
find = configuration => repo.findAllBySlug(slug, dc, nspace, configuration);
break;
}
break;
case 'gateways':
[method, ...slug] = rest;
switch (method) {
case 'for-service':
find = configuration => repo.findGatewayBySlug(slug, dc, nspace, configuration);
break;
}
break;
case 'sessions':
[method, ...slug] = rest;
switch (method) {
case 'for-node':
find = configuration => repo.findByNode(slug, dc, nspace, configuration);
break;
}
break;
case 'token':
find = configuration => repo.self(rest[1], dc);
break;
case 'discovery-chain':
case 'node':
find = configuration => repo.findBySlug(rest[0], dc, nspace, configuration);
break;
case 'service-instance':
// id, node, service
find = configuration =>
repo.findBySlug(rest[0], rest[1], rest[2], dc, nspace, configuration);
break;
case 'proxy-instance':
// id, node, service
find = configuration =>
repo.findInstanceBySlug(rest[0], rest[1], rest[2], dc, nspace, configuration);
break;
case 'topology':
// id, service kind
find = configuration => repo.findBySlug(rest[0], rest[1], dc, nspace, configuration);
break;
case 'policy':
case 'kv':
case 'intention':
slug = rest[0];
if (slug) {
find = configuration => repo.findBySlug(slug, dc, nspace, configuration);
} else {
find = configuration =>
Promise.resolve(repo.create({ Datacenter: dc, Namespace: nspace }));
}
break;
case 'oidc':
[method, ...slug] = rest;
switch (method) {
case 'providers':
find = configuration => repo.findAllByDatacenter(dc, nspace, configuration);
break;
case 'provider':
find = configuration => repo.findBySlug(slug[0], dc, nspace);
break;
case 'authorize':
find = configuration =>
repo.authorize(slug[0], slug[1], slug[2], dc, nspace, configuration);
break;
}
break;
}
return this.type.source(find, configuration);
},
});