consul/ui/packages/consul-ui/app/models/service-instance.js

147 lines
4.4 KiB
JavaScript

import Model, { attr, belongsTo } from '@ember-data/model';
import { fragmentArray } from 'ember-data-model-fragments/attributes';
import { computed, get } from '@ember/object';
import { or, filter, alias } from '@ember/object/computed';
import { tracked } from '@glimmer/tracking';
import mergeChecks from 'consul-ui/utils/merge-checks';
export const PRIMARY_KEY = 'uid';
export const SLUG_KEY = 'Node.Node,Service.ID';
export const Collection = class Collection {
@tracked items;
constructor(items) {
this.items = items;
}
get ExternalSources() {
const sources = this.items.reduce(function(prev, item) {
return prev.concat(item.ExternalSources || []);
}, []);
// unique, non-empty values, alpha sort
return [...new Set(sources)].filter(Boolean).sort();
}
};
export default class ServiceInstance extends Model {
@attr('string') uid;
@attr('string') Datacenter;
// ProxyInstance is the ember-data model relationship
@belongsTo('Proxy') ProxyInstance;
// Proxy is the actual JSON api response
@attr() Proxy;
@attr() Node;
@attr() Service;
@fragmentArray('health-check') Checks;
@attr('number') SyncTime;
@attr() meta;
@attr({ defaultValue: () => [] }) Resources; // []
// The name is the Name of the Service (the grouping of instances)
@alias('Service.Service') Name;
// If the ID is blank fallback to the Service.Service (the Name)
@or('Service.{ID,Service}') ID;
@or('Service.Address', 'Node.Service') Address;
@attr('string') SocketPath;
@alias('Service.Tags') Tags;
@alias('Service.Meta') Meta;
@alias('Service.Namespace') Namespace;
// FIXME: Is parition top level or Service.Partition?
@attr('string') Partition;
@filter('Checks.@each.Kind', (item, i, arr) => item.Kind === 'service') ServiceChecks;
@filter('Checks.@each.Kind', (item, i, arr) => item.Kind === 'node') NodeChecks;
// MeshChecks are a concatenation of Checks for the Instance and Checks for
// the ProxyInstance.
@computed('Checks.[]', 'ProxyInstance.{Checks.[],ServiceProxy.Expose.Checks}')
get MeshChecks() {
// merge the instance and proxy checks together, avoiding duplicate node
// checks and additionally setting any checks to exposed if required
return mergeChecks(
[get(this, 'Checks'), get(this, 'ProxyInstance.Checks')],
get(this, 'ProxyInstance.ServiceProxy.Expose.Checks')
);
}
@computed('Service.Meta')
get ExternalSources() {
const sources = Object.entries(this.Service.Meta || {})
.filter(([key, value]) => key === 'external-source')
.map(([key, value]) => {
return value;
});
return [...new Set(sources)];
}
@computed('Service.Kind')
get IsProxy() {
return ['connect-proxy', 'mesh-gateway', 'ingress-gateway', 'terminating-gateway'].includes(
this.Service.Kind
);
}
// IsOrigin means that the service can have associated up or down streams,
// this service being the origin point of those streams
@computed('Service.Kind')
get IsOrigin() {
return !['connect-proxy', 'mesh-gateway'].includes(this.Service.Kind);
}
// IsMeshOrigin means that the service can have associated up or downstreams
// that are in the Consul mesh itself
@computed('IsOrigin')
get IsMeshOrigin() {
return this.IsOrigin && !['terminating-gateway'].includes(this.Service.Kind);
}
@computed('ChecksPassing', 'ChecksWarning', 'ChecksCritical')
get Status() {
switch (true) {
case this.ChecksCritical.length !== 0:
return 'critical';
case this.ChecksWarning.length !== 0:
return 'warning';
case this.ChecksPassing.length !== 0:
return 'passing';
default:
return 'empty';
}
}
@computed('Checks.[]')
get ChecksPassing() {
return this.Checks.filter(item => item.Status === 'passing');
}
@computed('Checks.[]')
get ChecksWarning() {
return this.Checks.filter(item => item.Status === 'warning');
}
@computed('Checks.[]')
get ChecksCritical() {
return this.Checks.filter(item => item.Status === 'critical');
}
@computed('Checks.[]', 'ChecksPassing')
get PercentageChecksPassing() {
return (this.ChecksPassing.length / this.Checks.length) * 100;
}
@computed('Checks.[]', 'ChecksWarning')
get PercentageChecksWarning() {
return (this.ChecksWarning.length / this.Checks.length) * 100;
}
@computed('Checks.[]', 'ChecksCritical')
get PercentageChecksCritical() {
return (this.ChecksCritical.length / this.Checks.length) * 100;
}
}