ui: Add vendor directory as a target for JS linting and lint (#9157)
* ui: Add vendor for js linting * Lint all the things
This commit is contained in:
parent
2badb01d30
commit
67b70878f3
|
@ -5,8 +5,8 @@ module.exports = {
|
|||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
legacyDecorators: true
|
||||
}
|
||||
legacyDecorators: true,
|
||||
},
|
||||
},
|
||||
plugins: ['ember'],
|
||||
extends: ['eslint:recommended', 'plugin:ember/recommended'],
|
||||
|
@ -17,7 +17,7 @@ module.exports = {
|
|||
'no-unused-vars': ['error', { args: 'none' }],
|
||||
'ember/no-new-mixins': ['warn'],
|
||||
'ember/no-jquery': 'warn',
|
||||
'ember/no-global-jquery': 'warn'
|
||||
'ember/no-global-jquery': 'warn',
|
||||
},
|
||||
overrides: [
|
||||
// node files
|
||||
|
@ -31,14 +31,14 @@ module.exports = {
|
|||
'blueprints/*/index.js',
|
||||
'config/**/*.js',
|
||||
'lib/*/index.js',
|
||||
'server/**/*.js'
|
||||
'server/**/*.js',
|
||||
],
|
||||
parserOptions: {
|
||||
sourceType: 'script'
|
||||
sourceType: 'script',
|
||||
},
|
||||
env: {
|
||||
browser: false,
|
||||
node: true
|
||||
node: true,
|
||||
},
|
||||
plugins: ['node'],
|
||||
rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, {
|
||||
|
@ -46,8 +46,8 @@ module.exports = {
|
|||
|
||||
// this can be removed once the following is fixed
|
||||
// https://github.com/mysticatea/eslint-plugin-node/issues/77
|
||||
'node/no-unpublished-require': 'off'
|
||||
})
|
||||
}
|
||||
]
|
||||
'node/no-unpublished-require': 'off',
|
||||
}),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@ module.exports = {
|
|||
'no-nested-interactive': false,
|
||||
|
||||
'block-indentation': false,
|
||||
'quotes': false,
|
||||
quotes: false,
|
||||
|
||||
'no-inline-styles': false,
|
||||
'no-triple-curlies': false,
|
||||
|
@ -29,6 +29,6 @@ module.exports = {
|
|||
'no-invalid-role': false,
|
||||
|
||||
'no-unnecessary-component-helper': false,
|
||||
'link-href-attributes': false
|
||||
'link-href-attributes': false,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -5,7 +5,6 @@ import { tracked } from '@glimmer/tracking';
|
|||
import { sort } from '@ember/object/computed';
|
||||
|
||||
export default class ConsulIntentionList extends Component {
|
||||
|
||||
@service('filter') filter;
|
||||
@service('sort') sort;
|
||||
@service('search') search;
|
||||
|
@ -24,10 +23,10 @@ export default class ConsulIntentionList extends Component {
|
|||
}
|
||||
get filtered() {
|
||||
const predicate = this.filter.predicate('intention');
|
||||
return this.args.items.filter(predicate(this.args.filters))
|
||||
return this.args.items.filter(predicate(this.args.filters));
|
||||
}
|
||||
get searched() {
|
||||
if(typeof this.args.search === 'undefined') {
|
||||
if (typeof this.args.search === 'undefined') {
|
||||
return this.filtered;
|
||||
}
|
||||
const predicate = this.search.predicate('intention');
|
||||
|
@ -37,7 +36,7 @@ export default class ConsulIntentionList extends Component {
|
|||
return [this.args.sort];
|
||||
}
|
||||
get checkedItem() {
|
||||
if(this.searched.length === 1) {
|
||||
if (this.searched.length === 1) {
|
||||
return this.searched[0].SourceName === this.args.search ? this.searched[0] : null;
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
export default (collection, clickable, attribute, isPresent, deletable) => (scope = '.consul-intention-list') => {
|
||||
export default (collection, clickable, attribute, isPresent, deletable) => (
|
||||
scope = '.consul-intention-list'
|
||||
) => {
|
||||
const row = {
|
||||
source: attribute('data-test-intention-source', '[data-test-intention-source]'),
|
||||
destination: attribute('data-test-intention-destination', '[data-test-intention-destination]'),
|
||||
|
@ -10,6 +12,6 @@ export default (collection, clickable, attribute, isPresent, deletable) => (scop
|
|||
return {
|
||||
scope: scope,
|
||||
customResourceNotice: isPresent('.consul-intention-notice-custom-resource'),
|
||||
intentions: collection('[data-test-tabular-row]', row)
|
||||
}
|
||||
intentions: collection('[data-test-tabular-row]', row),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default () => (term) => (item) => {
|
||||
export default () => term => item => {
|
||||
const source = item.SourceName.toLowerCase();
|
||||
const destination = item.DestinationName.toLowerCase();
|
||||
const allLabel = 'All Services (*)'.toLowerCase();
|
||||
|
@ -9,4 +9,4 @@ export default () => (term) => (item) => {
|
|||
(source === '*' && allLabel.indexOf(lowerTerm) !== -1) ||
|
||||
(destination === '*' && allLabel.indexOf(lowerTerm) !== -1)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,7 +4,6 @@ import { PRIMARY_KEY } from 'consul-ui/models/intention';
|
|||
|
||||
const modelName = 'intention';
|
||||
export default class IntentionRepository extends RepositoryService {
|
||||
|
||||
managedByCRDs = false;
|
||||
|
||||
getModelName() {
|
||||
|
@ -24,9 +23,11 @@ export default class IntentionRepository extends RepositoryService {
|
|||
}
|
||||
|
||||
isManagedByCRDs() {
|
||||
if(!this.managedByCRDs) {
|
||||
this.managedByCRDs = this.store.peekAll(this.getModelName())
|
||||
.toArray().some(item => item.IsManagedByCRD);
|
||||
if (!this.managedByCRDs) {
|
||||
this.managedByCRDs = this.store
|
||||
.peekAll(this.getModelName())
|
||||
.toArray()
|
||||
.some(item => item.IsManagedByCRD);
|
||||
}
|
||||
return this.managedByCRDs;
|
||||
}
|
||||
|
|
|
@ -8,18 +8,15 @@ module.exports = function(defaults) {
|
|||
const isProdLike = prodlike.indexOf(env) > -1;
|
||||
const sourcemaps = !isProd;
|
||||
let trees = {};
|
||||
if(isProdLike) {
|
||||
if (isProdLike) {
|
||||
// exclude any component/pageobject.js files from production-like environments
|
||||
trees.app = new Funnel(
|
||||
'app',
|
||||
{
|
||||
exclude: [
|
||||
'components/**/pageobject.js',
|
||||
'components/**/*.test-support.js',
|
||||
'components/**/*.test.js'
|
||||
]
|
||||
}
|
||||
);
|
||||
trees.app = new Funnel('app', {
|
||||
exclude: [
|
||||
'components/**/pageobject.js',
|
||||
'components/**/*.test-support.js',
|
||||
'components/**/*.test.js',
|
||||
],
|
||||
});
|
||||
}
|
||||
let app = new EmberApp(
|
||||
Object.assign({}, defaults, {
|
||||
|
@ -31,14 +28,7 @@ module.exports = function(defaults) {
|
|||
includePolyfill: true,
|
||||
},
|
||||
'ember-cli-string-helpers': {
|
||||
only: [
|
||||
'capitalize',
|
||||
'lowercase',
|
||||
'truncate',
|
||||
'uppercase',
|
||||
'humanize',
|
||||
'titleize'
|
||||
],
|
||||
only: ['capitalize', 'lowercase', 'truncate', 'uppercase', 'humanize', 'titleize'],
|
||||
},
|
||||
'ember-cli-math-helpers': {
|
||||
only: ['div'],
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
"lint:dev:js": "eslint -c .dev.eslintrc.js --fix ./*.js ./.*.js app config lib server tests",
|
||||
"lint:hbs": "ember-template-lint .",
|
||||
"lint:js": "eslint .",
|
||||
"format:js": "prettier --write \"{app,config,lib,server,tests}/**/*.js\" ./*.js ./.*.js",
|
||||
"format:js": "prettier --write \"{app,config,lib,server,vendor,tests}/**/*.js\" ./*.js ./.*.js",
|
||||
"format:css": "prettier --write \"app/styles/**/*.*\"",
|
||||
"start": "ember serve --port=${EMBER_SERVE_PORT:-4200} --live-reload-port=${EMBER_LIVE_RELOAD_PORT:-7020}",
|
||||
"start:staging": "ember serve --port=${EMBER_SERVE_PORT:-4200} --live-reload-port=${EMBER_LIVE_RELOAD_PORT:-7020} --environment staging",
|
||||
|
@ -42,7 +42,7 @@
|
|||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"{app,config,lib,server,tests}/**/*.js": [
|
||||
"{app,config,lib,server,vendor,tests}/**/*.js": [
|
||||
"prettier --write"
|
||||
],
|
||||
"app/styles/**/*.*": [
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
module.exports = {
|
||||
test_page: 'tests/index.html?hidepassed',
|
||||
disable_watching: true,
|
||||
launch_in_ci: [
|
||||
'Chrome'
|
||||
],
|
||||
launch_in_dev: [
|
||||
'Chrome'
|
||||
],
|
||||
launch_in_ci: ['Chrome'],
|
||||
launch_in_dev: ['Chrome'],
|
||||
browser_start_timeout: 120,
|
||||
browser_args: {
|
||||
Chrome: {
|
||||
|
@ -39,5 +35,5 @@ if (process.env.EMBER_TEST_REPORT) {
|
|||
* https://github.com/trentmwillis/ember-exam/issues/108
|
||||
*/
|
||||
if (process.env.EMBER_EXAM_PARALLEL) {
|
||||
module.exports.parallel = -1
|
||||
module.exports.parallel = -1;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,6 @@ export default function(visitable, creatable, clickable, intentions, popoverSele
|
|||
visit: visitable('/:dc/intentions'),
|
||||
intentionList: intentions(),
|
||||
sort: popoverSelect('[data-test-sort-control]'),
|
||||
...creatable({})
|
||||
}
|
||||
...creatable({}),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -11,7 +11,11 @@ export default function(scenario, assert, find, currentPage, pauseUntil, plurali
|
|||
return retry();
|
||||
}, `Expected ${num} ${model}s`);
|
||||
})
|
||||
.then('pause until I see $number $model model[s]? on the $component component', function(num, model, component) {
|
||||
.then('pause until I see $number $model model[s]? on the $component component', function(
|
||||
num,
|
||||
model,
|
||||
component
|
||||
) {
|
||||
return pauseUntil(function(resolve, reject, retry) {
|
||||
const obj = find(component);
|
||||
const len = obj[pluralize(model)].filter(function(item) {
|
||||
|
|
|
@ -14,7 +14,8 @@ export default function(scenario, create) {
|
|||
function(number, model, data) {
|
||||
return create(number, model, data);
|
||||
}
|
||||
).given(['settings from yaml\n$yaml'], function(data) {
|
||||
)
|
||||
.given(['settings from yaml\n$yaml'], function(data) {
|
||||
return Object.keys(data).forEach(function(key) {
|
||||
window.localStorage[key] = JSON.stringify(data[key]);
|
||||
});
|
||||
|
|
|
@ -4,22 +4,21 @@ export default function(scenario, find, click) {
|
|||
return click(selector);
|
||||
})
|
||||
// TODO: Probably nicer to think of better vocab than having the 'without " rule'
|
||||
.when([
|
||||
'I click (?!")$property(?!")',
|
||||
'I click $property on the $component',
|
||||
'I click $property on the $component component'
|
||||
], function(
|
||||
property,
|
||||
component,
|
||||
next
|
||||
) {
|
||||
try {
|
||||
if (typeof component === 'string') {
|
||||
property = `${component}.${property}`;
|
||||
.when(
|
||||
[
|
||||
'I click (?!")$property(?!")',
|
||||
'I click $property on the $component',
|
||||
'I click $property on the $component component',
|
||||
],
|
||||
function(property, component, next) {
|
||||
try {
|
||||
if (typeof component === 'string') {
|
||||
property = `${component}.${property}`;
|
||||
}
|
||||
return find(property)();
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
return find(property)();
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,21 +3,36 @@ import { module, test } from 'qunit';
|
|||
|
||||
module('Unit | Helper | selectable-key-values', function() {
|
||||
test('it turns arrays into key values and selects the first item by default', function(assert) {
|
||||
const actual = selectableKeyValues([['key-1', 'value-1'], ['key-2', 'value-2']]);
|
||||
const actual = selectableKeyValues([
|
||||
['key-1', 'value-1'],
|
||||
['key-2', 'value-2'],
|
||||
]);
|
||||
assert.equal(actual.items.length, 2);
|
||||
assert.deepEqual(actual.selected, { key: 'key-1', value: 'value-1' });
|
||||
});
|
||||
test('it turns arrays into key values and selects the defined key', function(assert) {
|
||||
const actual = selectableKeyValues([['key-1', 'value-1'], ['key-2', 'value-2']], {
|
||||
selected: 'key-2',
|
||||
});
|
||||
const actual = selectableKeyValues(
|
||||
[
|
||||
['key-1', 'value-1'],
|
||||
['key-2', 'value-2'],
|
||||
],
|
||||
{
|
||||
selected: 'key-2',
|
||||
}
|
||||
);
|
||||
assert.equal(actual.items.length, 2);
|
||||
assert.deepEqual(actual.selected, { key: 'key-2', value: 'value-2' });
|
||||
});
|
||||
test('it turns arrays into key values and selects the defined index', function(assert) {
|
||||
const actual = selectableKeyValues([['key-1', 'value-1'], ['key-2', 'value-2']], {
|
||||
selected: 1,
|
||||
});
|
||||
const actual = selectableKeyValues(
|
||||
[
|
||||
['key-1', 'value-1'],
|
||||
['key-2', 'value-2'],
|
||||
],
|
||||
{
|
||||
selected: 1,
|
||||
}
|
||||
);
|
||||
assert.equal(actual.items.length, 2);
|
||||
assert.deepEqual(actual.selected, { key: 'key-2', value: 'value-2' });
|
||||
});
|
||||
|
|
|
@ -1,51 +1,47 @@
|
|||
(
|
||||
function(global) {
|
||||
// Current interface is these three methods.
|
||||
const requiredMethods = [
|
||||
'init',
|
||||
'serviceRecentSummarySeries',
|
||||
'serviceRecentSummaryStats',
|
||||
'upstreamRecentSummaryStats',
|
||||
'downstreamRecentSummaryStats',
|
||||
];
|
||||
(function(global) {
|
||||
// Current interface is these three methods.
|
||||
const requiredMethods = [
|
||||
'init',
|
||||
'serviceRecentSummarySeries',
|
||||
'serviceRecentSummaryStats',
|
||||
'upstreamRecentSummaryStats',
|
||||
'downstreamRecentSummaryStats',
|
||||
];
|
||||
|
||||
// This is a bit gross but we want to support simple extensibility by
|
||||
// including JS in the browser without forcing operators to setup a whole
|
||||
// transpiling stack. So for now we use a window global as a thin registry for
|
||||
// these providers.
|
||||
class Consul {
|
||||
constructor() {
|
||||
this.registry = {};
|
||||
this.providers = {};
|
||||
}
|
||||
|
||||
registerMetricsProvider(name, provider) {
|
||||
// Basic check that it matches the type we expect
|
||||
for (var m of requiredMethods) {
|
||||
if (typeof provider[m] !== 'function') {
|
||||
throw new Error(`Can't register metrics provider '${name}': missing ${m} method.`);
|
||||
}
|
||||
}
|
||||
this.registry[name] = provider;
|
||||
}
|
||||
|
||||
getMetricsProvider(name, options) {
|
||||
if (!(name in this.registry)) {
|
||||
throw new Error(`Metrics Provider '${name}' is not registered.`);
|
||||
}
|
||||
if (name in this.providers) {
|
||||
return this.providers[name];
|
||||
}
|
||||
|
||||
this.providers[name] = Object.create(this.registry[name]);
|
||||
this.providers[name].init(options);
|
||||
|
||||
return this.providers[name];
|
||||
}
|
||||
// This is a bit gross but we want to support simple extensibility by
|
||||
// including JS in the browser without forcing operators to setup a whole
|
||||
// transpiling stack. So for now we use a window global as a thin registry for
|
||||
// these providers.
|
||||
class Consul {
|
||||
constructor() {
|
||||
this.registry = {};
|
||||
this.providers = {};
|
||||
}
|
||||
|
||||
global.consul = new Consul();
|
||||
registerMetricsProvider(name, provider) {
|
||||
// Basic check that it matches the type we expect
|
||||
for (var m of requiredMethods) {
|
||||
if (typeof provider[m] !== 'function') {
|
||||
throw new Error(`Can't register metrics provider '${name}': missing ${m} method.`);
|
||||
}
|
||||
}
|
||||
this.registry[name] = provider;
|
||||
}
|
||||
|
||||
getMetricsProvider(name, options) {
|
||||
if (!(name in this.registry)) {
|
||||
throw new Error(`Metrics Provider '${name}' is not registered.`);
|
||||
}
|
||||
if (name in this.providers) {
|
||||
return this.providers[name];
|
||||
}
|
||||
|
||||
this.providers[name] = Object.create(this.registry[name]);
|
||||
this.providers[name].init(options);
|
||||
|
||||
return this.providers[name];
|
||||
}
|
||||
}
|
||||
)(window);
|
||||
|
||||
global.consul = new Consul();
|
||||
})(window);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*eslint no-console: "off"*/
|
||||
(function () {
|
||||
var emptySeries = { unitSuffix: "", labels: {}, data: [] }
|
||||
(function() {
|
||||
var emptySeries = { unitSuffix: '', labels: {}, data: [] };
|
||||
|
||||
var prometheusProvider = {
|
||||
options: {},
|
||||
|
@ -26,7 +26,9 @@
|
|||
init: function(options) {
|
||||
this.options = options;
|
||||
if (!this.options.metrics_proxy_enabled) {
|
||||
throw new Error("prometheus metrics provider currently requires the ui_config.metrics_proxy to be configured in the Consul agent.");
|
||||
throw new Error(
|
||||
'prometheus metrics provider currently requires the ui_config.metrics_proxy to be configured in the Consul agent.'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -36,26 +38,24 @@
|
|||
httpGet: function(url, queryParams, headers) {
|
||||
if (queryParams) {
|
||||
var separator = url.indexOf('?') !== -1 ? '&' : '?';
|
||||
var qs = Object.keys(queryParams).
|
||||
map(function(key) {
|
||||
return encodeURIComponent(key) + "=" + encodeURIComponent(queryParams[key]);
|
||||
}).
|
||||
join("&");
|
||||
var qs = Object.keys(queryParams)
|
||||
.map(function(key) {
|
||||
return encodeURIComponent(key) + '=' + encodeURIComponent(queryParams[key]);
|
||||
})
|
||||
.join('&');
|
||||
url = url + separator + qs;
|
||||
}
|
||||
// fetch the url along with any headers
|
||||
return this.options.fetch(url, {headers: headers || {}}).then(
|
||||
function(response) {
|
||||
if(response.ok) {
|
||||
return response.json();
|
||||
} else {
|
||||
// throw a statusCode error if any errors are received
|
||||
var e = new Error('HTTP Error: ' + response.statusText);
|
||||
e.statusCode = response.status;
|
||||
throw e;
|
||||
}
|
||||
return this.options.fetch(url, { headers: headers || {} }).then(function(response) {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
} else {
|
||||
// throw a statusCode error if any errors are received
|
||||
var e = new Error('HTTP Error: ' + response.statusText);
|
||||
e.statusCode = response.status;
|
||||
throw e;
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -114,13 +114,13 @@
|
|||
*/
|
||||
serviceRecentSummarySeries: function(serviceDC, namespace, serviceName, protocol, options) {
|
||||
// Fetch time-series
|
||||
var series = []
|
||||
var labels = []
|
||||
var series = [];
|
||||
var labels = [];
|
||||
|
||||
// Set the start and end range here so that all queries end up with
|
||||
// identical time axes. Later we might accept these as options.
|
||||
var now = (new Date()).getTime()/1000;
|
||||
options.start = now - (15*60);
|
||||
var now = new Date().getTime() / 1000;
|
||||
options.start = now - 15 * 60;
|
||||
options.end = now;
|
||||
|
||||
if (this.hasL7Metrics(protocol)) {
|
||||
|
@ -169,18 +169,18 @@
|
|||
// Fetch stats
|
||||
var stats = [];
|
||||
if (this.hasL7Metrics(protocol)) {
|
||||
stats.push(this.fetchRPS(serviceName, "service", options))
|
||||
stats.push(this.fetchER(serviceName, "service", options))
|
||||
stats.push(this.fetchPercentile(50, serviceName, "service", options))
|
||||
stats.push(this.fetchPercentile(99, serviceName, "service", options))
|
||||
stats.push(this.fetchRPS(serviceName, 'service', options));
|
||||
stats.push(this.fetchER(serviceName, 'service', options));
|
||||
stats.push(this.fetchPercentile(50, serviceName, 'service', options));
|
||||
stats.push(this.fetchPercentile(99, serviceName, 'service', options));
|
||||
} else {
|
||||
// Fallback to just L4 metrics.
|
||||
stats.push(this.fetchConnRate(serviceName, "service", options))
|
||||
stats.push(this.fetchServiceRx(serviceName, "service", options))
|
||||
stats.push(this.fetchServiceTx(serviceName, "service", options))
|
||||
stats.push(this.fetchServiceNoRoute(serviceName, "service", options))
|
||||
stats.push(this.fetchConnRate(serviceName, 'service', options));
|
||||
stats.push(this.fetchServiceRx(serviceName, 'service', options));
|
||||
stats.push(this.fetchServiceTx(serviceName, 'service', options));
|
||||
stats.push(this.fetchServiceNoRoute(serviceName, 'service', options));
|
||||
}
|
||||
return this.fetchStats(stats)
|
||||
return this.fetchStats(stats);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -217,7 +217,7 @@
|
|||
* }
|
||||
*/
|
||||
upstreamRecentSummaryStats: function(serviceDC, namespace, serviceName, upstreamName, options) {
|
||||
return this.fetchRecentSummaryStats(serviceName, "upstream", options)
|
||||
return this.fetchRecentSummaryStats(serviceName, 'upstream', options);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -260,7 +260,7 @@
|
|||
* }
|
||||
*/
|
||||
downstreamRecentSummaryStats: function(serviceDC, namespace, serviceName, options) {
|
||||
return this.fetchRecentSummaryStats(serviceName, "downstream", options)
|
||||
return this.fetchRecentSummaryStats(serviceName, 'downstream', options);
|
||||
},
|
||||
|
||||
fetchRecentSummaryStats: function(serviceName, type, options) {
|
||||
|
@ -270,74 +270,76 @@
|
|||
// We don't know which upstreams are HTTP/TCP so just fetch all of them.
|
||||
|
||||
// HTTP
|
||||
stats.push(this.fetchRPS(serviceName, type, options))
|
||||
stats.push(this.fetchER(serviceName, type, options))
|
||||
stats.push(this.fetchPercentile(50, serviceName, type, options))
|
||||
stats.push(this.fetchPercentile(99, serviceName, type, options))
|
||||
stats.push(this.fetchRPS(serviceName, type, options));
|
||||
stats.push(this.fetchER(serviceName, type, options));
|
||||
stats.push(this.fetchPercentile(50, serviceName, type, options));
|
||||
stats.push(this.fetchPercentile(99, serviceName, type, options));
|
||||
|
||||
// L4
|
||||
stats.push(this.fetchConnRate(serviceName, type, options))
|
||||
stats.push(this.fetchServiceRx(serviceName, type, options))
|
||||
stats.push(this.fetchServiceTx(serviceName, type, options))
|
||||
stats.push(this.fetchServiceNoRoute(serviceName, type, options))
|
||||
stats.push(this.fetchConnRate(serviceName, type, options));
|
||||
stats.push(this.fetchServiceRx(serviceName, type, options));
|
||||
stats.push(this.fetchServiceTx(serviceName, type, options));
|
||||
stats.push(this.fetchServiceNoRoute(serviceName, type, options));
|
||||
|
||||
return this.fetchStatsGrouped(stats)
|
||||
return this.fetchStatsGrouped(stats);
|
||||
},
|
||||
|
||||
hasL7Metrics: function(protocol) {
|
||||
return protocol === "http" || protocol === "http2" || protocol === "grpc"
|
||||
return protocol === 'http' || protocol === 'http2' || protocol === 'grpc';
|
||||
},
|
||||
|
||||
fetchStats: function(statsPromises) {
|
||||
var all = Promise.all(statsPromises).
|
||||
then(function(results){
|
||||
var all = Promise.all(statsPromises).then(function(results) {
|
||||
var data = {
|
||||
stats: []
|
||||
}
|
||||
stats: [],
|
||||
};
|
||||
// Add all non-empty stats
|
||||
for (var i = 0; i < statsPromises.length; i++) {
|
||||
if (results[i].value) {
|
||||
data.stats.push(results[i]);
|
||||
}
|
||||
}
|
||||
return data
|
||||
})
|
||||
return data;
|
||||
});
|
||||
|
||||
// Fetch the metrics async, and return a promise to the result.
|
||||
return all
|
||||
return all;
|
||||
},
|
||||
|
||||
fetchStatsGrouped: function(statsPromises) {
|
||||
var all = Promise.all(statsPromises).
|
||||
then(function(results){
|
||||
var all = Promise.all(statsPromises).then(function(results) {
|
||||
var data = {
|
||||
stats: {}
|
||||
}
|
||||
stats: {},
|
||||
};
|
||||
// Add all non-empty stats
|
||||
for (var i = 0; i < statsPromises.length; i++) {
|
||||
if (results[i]) {
|
||||
for (var group in results[i]) {
|
||||
if (!results[i].hasOwnProperty(group)) continue;
|
||||
if (!data.stats[group]) {
|
||||
data.stats[group] = []
|
||||
data.stats[group] = [];
|
||||
}
|
||||
data.stats[group].push(results[i][group])
|
||||
data.stats[group].push(results[i][group]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return data
|
||||
})
|
||||
return data;
|
||||
});
|
||||
|
||||
// Fetch the metrics async, and return a promise to the result.
|
||||
return all
|
||||
return all;
|
||||
},
|
||||
|
||||
reformatSeries: function(unitSuffix, labelMap) {
|
||||
return function(response) {
|
||||
// Handle empty result sets gracefully.
|
||||
if (!response.data || !response.data.result || response.data.result.length == 0
|
||||
|| !response.data.result[0].values
|
||||
|| response.data.result[0].values.length == 0) {
|
||||
if (
|
||||
!response.data ||
|
||||
!response.data.result ||
|
||||
response.data.result.length == 0 ||
|
||||
!response.data.result[0].values ||
|
||||
response.data.result[0].values.length == 0
|
||||
) {
|
||||
return emptySeries;
|
||||
}
|
||||
// Reformat the prometheus data to be the format we want with stacked
|
||||
|
@ -362,306 +364,315 @@
|
|||
return {
|
||||
unitSuffix: unitSuffix,
|
||||
labels: labelMap,
|
||||
data: series
|
||||
data: series,
|
||||
};
|
||||
};
|
||||
},
|
||||
|
||||
fetchRequestRateSeries: function(serviceName, options){
|
||||
fetchRequestRateSeries: function(serviceName, options) {
|
||||
// We need the sum of all non-500 error rates as one value and the 500
|
||||
// error rate as a separate series so that they stack to show the full
|
||||
// request rate. Some creative label replacement makes this possible in
|
||||
// one query.
|
||||
var q = `sum by (label) (`+
|
||||
var q =
|
||||
`sum by (label) (` +
|
||||
// The outer label_replace catches 5xx error and relabels them as
|
||||
// err=yes
|
||||
`label_replace(`+
|
||||
// The inner label_replace relabels all !5xx rates as err=no so they
|
||||
// will get summed together.
|
||||
`label_replace(`+
|
||||
// Get rate of requests to the service
|
||||
`irate(envoy_listener_http_downstream_rq_xx{local_cluster="${serviceName}",envoy_http_conn_manager_prefix="public_listener_http"}[10m])`+
|
||||
// ... inner replacement matches all code classes except "5" and
|
||||
// applies err=no
|
||||
`, "label", "Successes", "envoy_response_code_class", "[^5]")`+
|
||||
// ... outer replacement matches code=5 and applies err=yes
|
||||
`, "label", "Errors", "envoy_response_code_class", "5")`+
|
||||
`)`
|
||||
`label_replace(` +
|
||||
// The inner label_replace relabels all !5xx rates as err=no so they
|
||||
// will get summed together.
|
||||
`label_replace(` +
|
||||
// Get rate of requests to the service
|
||||
`irate(envoy_listener_http_downstream_rq_xx{local_cluster="${serviceName}",envoy_http_conn_manager_prefix="public_listener_http"}[10m])` +
|
||||
// ... inner replacement matches all code classes except "5" and
|
||||
// applies err=no
|
||||
`, "label", "Successes", "envoy_response_code_class", "[^5]")` +
|
||||
// ... outer replacement matches code=5 and applies err=yes
|
||||
`, "label", "Errors", "envoy_response_code_class", "5")` +
|
||||
`)`;
|
||||
var labelMap = {
|
||||
Total: 'Total inbound requests per second',
|
||||
Successes: 'Successful responses (with an HTTP response code not in the 5xx range) per second.',
|
||||
Successes:
|
||||
'Successful responses (with an HTTP response code not in the 5xx range) per second.',
|
||||
Errors: 'Error responses (with an HTTP response code in the 5xx range) per second.',
|
||||
};
|
||||
return this.fetchSeries(q, options)
|
||||
.then(this.reformatSeries(" rps", labelMap))
|
||||
return this.fetchSeries(q, options).then(this.reformatSeries(' rps', labelMap));
|
||||
},
|
||||
|
||||
fetchDataRateSeries: function(serviceName, options){
|
||||
fetchDataRateSeries: function(serviceName, options) {
|
||||
// 8 * converts from bytes/second to bits/second
|
||||
var q = `8 * sum by (label) (`+
|
||||
var q =
|
||||
`8 * sum by (label) (` +
|
||||
// Label replace generates a unique label per rx/tx metric to stop them
|
||||
// being summed together.
|
||||
`label_replace(`+
|
||||
// Get the tx rate
|
||||
`irate(envoy_tcp_downstream_cx_tx_bytes_total{local_cluster="${serviceName}",envoy_tcp_prefix="public_listener_tcp"}[10m])`+
|
||||
// Match all and apply the tx label
|
||||
`, "label", "Outbound", "__name__", ".*"`+
|
||||
`label_replace(` +
|
||||
// Get the tx rate
|
||||
`irate(envoy_tcp_downstream_cx_tx_bytes_total{local_cluster="${serviceName}",envoy_tcp_prefix="public_listener_tcp"}[10m])` +
|
||||
// Match all and apply the tx label
|
||||
`, "label", "Outbound", "__name__", ".*"` +
|
||||
// Union those vectors with the RX ones
|
||||
`) or label_replace(`+
|
||||
// Get the rx rate
|
||||
`irate(envoy_tcp_downstream_cx_rx_bytes_total{local_cluster="${serviceName}",envoy_tcp_prefix="public_listener_tcp"}[10m])`+
|
||||
// Match all and apply the rx label
|
||||
`, "label", "Inbound", "__name__", ".*"`+
|
||||
`)`+
|
||||
`)`
|
||||
`) or label_replace(` +
|
||||
// Get the rx rate
|
||||
`irate(envoy_tcp_downstream_cx_rx_bytes_total{local_cluster="${serviceName}",envoy_tcp_prefix="public_listener_tcp"}[10m])` +
|
||||
// Match all and apply the rx label
|
||||
`, "label", "Inbound", "__name__", ".*"` +
|
||||
`)` +
|
||||
`)`;
|
||||
var labelMap = {
|
||||
Total: 'Total bandwidth',
|
||||
Inbound: 'Inbound data rate (data recieved) from the network in bits per second.',
|
||||
Outbound: 'Outbound data rate (data transmitted) from the network in bits per second.',
|
||||
};
|
||||
return this.fetchSeries(q, options)
|
||||
.then(this.reformatSeries("bps", labelMap))
|
||||
return this.fetchSeries(q, options).then(this.reformatSeries('bps', labelMap));
|
||||
},
|
||||
|
||||
makeSubject: function(serviceName, type) {
|
||||
if (type == "upstream") {
|
||||
if (type == 'upstream') {
|
||||
// {{GROUP}} is a placeholder that is replaced by the upstream name
|
||||
return `${serviceName} → {{GROUP}}`;
|
||||
}
|
||||
if (type == "downstream") {
|
||||
if (type == 'downstream') {
|
||||
// {{GROUP}} is a placeholder that is replaced by the downstream name
|
||||
return `{{GROUP}} → ${serviceName}`;
|
||||
}
|
||||
return serviceName
|
||||
return serviceName;
|
||||
},
|
||||
|
||||
makeHTTPSelector: function(serviceName, type) {
|
||||
// Downstreams are totally different
|
||||
if (type == "downstream") {
|
||||
return `consul_service="${serviceName}"`
|
||||
if (type == 'downstream') {
|
||||
return `consul_service="${serviceName}"`;
|
||||
}
|
||||
var lc = `local_cluster="${serviceName}"`
|
||||
if (type == "upstream") {
|
||||
var lc = `local_cluster="${serviceName}"`;
|
||||
if (type == 'upstream') {
|
||||
lc += `,envoy_http_conn_manager_prefix=~"upstream_.*"`;
|
||||
} else {
|
||||
// Only care about inbound public listener
|
||||
lc += `,envoy_http_conn_manager_prefix="public_listener_http"`
|
||||
lc += `,envoy_http_conn_manager_prefix="public_listener_http"`;
|
||||
}
|
||||
return lc
|
||||
return lc;
|
||||
},
|
||||
|
||||
makeTCPSelector: function(serviceName, type) {
|
||||
// Downstreams are totally different
|
||||
if (type == "downstream") {
|
||||
return `consul_service="${serviceName}"`
|
||||
if (type == 'downstream') {
|
||||
return `consul_service="${serviceName}"`;
|
||||
}
|
||||
var lc = `local_cluster="${serviceName}"`
|
||||
if (type == "upstream") {
|
||||
var lc = `local_cluster="${serviceName}"`;
|
||||
if (type == 'upstream') {
|
||||
lc += `,envoy_tcp_prefix=~"upstream_.*"`;
|
||||
} else {
|
||||
// Only care about inbound public listener
|
||||
lc += `,envoy_tcp_prefix="public_listener_tcp"`
|
||||
lc += `,envoy_tcp_prefix="public_listener_tcp"`;
|
||||
}
|
||||
return lc
|
||||
return lc;
|
||||
},
|
||||
|
||||
groupQueryHTTP: function(type, q) {
|
||||
if (type == "upstream") {
|
||||
q += " by (envoy_http_conn_manager_prefix)"
|
||||
if (type == 'upstream') {
|
||||
q += ' by (envoy_http_conn_manager_prefix)';
|
||||
// Extract the raw upstream service name to group results by
|
||||
q = this.upstreamRelabelQueryHTTP(q)
|
||||
} else if (type == "downstream") {
|
||||
q += " by (local_cluster)"
|
||||
q = this.downstreamRelabelQuery(q)
|
||||
q = this.upstreamRelabelQueryHTTP(q);
|
||||
} else if (type == 'downstream') {
|
||||
q += ' by (local_cluster)';
|
||||
q = this.downstreamRelabelQuery(q);
|
||||
}
|
||||
return q
|
||||
return q;
|
||||
},
|
||||
|
||||
groupQueryTCP: function(type, q) {
|
||||
if (type == "upstream") {
|
||||
q += " by (envoy_tcp_prefix)"
|
||||
if (type == 'upstream') {
|
||||
q += ' by (envoy_tcp_prefix)';
|
||||
// Extract the raw upstream service name to group results by
|
||||
q = this.upstreamRelabelQueryTCP(q)
|
||||
} else if (type == "downstream") {
|
||||
q += " by (local_cluster)"
|
||||
q = this.downstreamRelabelQuery(q)
|
||||
q = this.upstreamRelabelQueryTCP(q);
|
||||
} else if (type == 'downstream') {
|
||||
q += ' by (local_cluster)';
|
||||
q = this.downstreamRelabelQuery(q);
|
||||
}
|
||||
return q
|
||||
return q;
|
||||
},
|
||||
|
||||
upstreamRelabelQueryHTTP: function(q) {
|
||||
return `label_replace(${q}, "upstream", "$1", "envoy_http_conn_manager_prefix", "upstream_(.*)_http")`
|
||||
return `label_replace(${q}, "upstream", "$1", "envoy_http_conn_manager_prefix", "upstream_(.*)_http")`;
|
||||
},
|
||||
|
||||
upstreamRelabelQueryTCP: function(q) {
|
||||
return `label_replace(${q}, "upstream", "$1", "envoy_tcp_prefix", "upstream_(.*)_tcp")`
|
||||
return `label_replace(${q}, "upstream", "$1", "envoy_tcp_prefix", "upstream_(.*)_tcp")`;
|
||||
},
|
||||
|
||||
downstreamRelabelQuery: function(q) {
|
||||
return `label_replace(${q}, "downstream", "$1", "local_cluster", "(.*)")`
|
||||
return `label_replace(${q}, "downstream", "$1", "local_cluster", "(.*)")`;
|
||||
},
|
||||
|
||||
groupBy: function(type) {
|
||||
if (type == "service") {
|
||||
return false
|
||||
if (type == 'service') {
|
||||
return false;
|
||||
}
|
||||
return type;
|
||||
},
|
||||
|
||||
metricPrefixHTTP: function(type) {
|
||||
if (type == "downstream") {
|
||||
return "envoy_cluster_upstream_rq"
|
||||
if (type == 'downstream') {
|
||||
return 'envoy_cluster_upstream_rq';
|
||||
}
|
||||
return "envoy_http_downstream_rq";
|
||||
return 'envoy_http_downstream_rq';
|
||||
},
|
||||
|
||||
metricPrefixTCP: function(type) {
|
||||
if (type == "downstream") {
|
||||
return "envoy_cluster_upstream_cx"
|
||||
if (type == 'downstream') {
|
||||
return 'envoy_cluster_upstream_cx';
|
||||
}
|
||||
return "envoy_tcp_downstream_cx";
|
||||
return 'envoy_tcp_downstream_cx';
|
||||
},
|
||||
|
||||
fetchRPS: function(serviceName, type, options){
|
||||
var sel = this.makeHTTPSelector(serviceName, type)
|
||||
var subject = this.makeSubject(serviceName, type)
|
||||
var metricPfx = this.metricPrefixHTTP(type)
|
||||
var q = `sum(rate(${metricPfx}_completed{${sel}}[15m]))`
|
||||
return this.fetchStat(this.groupQueryHTTP(type, q),
|
||||
"RPS",
|
||||
fetchRPS: function(serviceName, type, options) {
|
||||
var sel = this.makeHTTPSelector(serviceName, type);
|
||||
var subject = this.makeSubject(serviceName, type);
|
||||
var metricPfx = this.metricPrefixHTTP(type);
|
||||
var q = `sum(rate(${metricPfx}_completed{${sel}}[15m]))`;
|
||||
return this.fetchStat(
|
||||
this.groupQueryHTTP(type, q),
|
||||
'RPS',
|
||||
`<b>${subject}</b> request rate averaged over the last 15 minutes`,
|
||||
shortNumStr,
|
||||
this.groupBy(type)
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
fetchER: function(serviceName, type, options){
|
||||
var sel = this.makeHTTPSelector(serviceName, type)
|
||||
var subject = this.makeSubject(serviceName, type)
|
||||
var groupBy = ""
|
||||
if (type == "upstream") {
|
||||
groupBy += " by (envoy_http_conn_manager_prefix)"
|
||||
} else if (type == "downstream") {
|
||||
groupBy += " by (local_cluster)"
|
||||
fetchER: function(serviceName, type, options) {
|
||||
var sel = this.makeHTTPSelector(serviceName, type);
|
||||
var subject = this.makeSubject(serviceName, type);
|
||||
var groupBy = '';
|
||||
if (type == 'upstream') {
|
||||
groupBy += ' by (envoy_http_conn_manager_prefix)';
|
||||
} else if (type == 'downstream') {
|
||||
groupBy += ' by (local_cluster)';
|
||||
}
|
||||
var metricPfx = this.metricPrefixHTTP(type)
|
||||
var q = `sum(rate(${metricPfx}_xx{${sel},envoy_response_code_class="5"}[15m]))${groupBy}/sum(rate(${metricPfx}_xx{${sel}}[15m]))${groupBy}`
|
||||
if (type == "upstream") {
|
||||
q = this.upstreamRelabelQueryHTTP(q)
|
||||
} else if (type == "downstream") {
|
||||
q = this.downstreamRelabelQuery(q)
|
||||
var metricPfx = this.metricPrefixHTTP(type);
|
||||
var q = `sum(rate(${metricPfx}_xx{${sel},envoy_response_code_class="5"}[15m]))${groupBy}/sum(rate(${metricPfx}_xx{${sel}}[15m]))${groupBy}`;
|
||||
if (type == 'upstream') {
|
||||
q = this.upstreamRelabelQueryHTTP(q);
|
||||
} else if (type == 'downstream') {
|
||||
q = this.downstreamRelabelQuery(q);
|
||||
}
|
||||
return this.fetchStat(q,
|
||||
"ER",
|
||||
return this.fetchStat(
|
||||
q,
|
||||
'ER',
|
||||
`Percentage of <b>${subject}</b> requests which were 5xx status over the last 15 minutes`,
|
||||
function(val){
|
||||
return shortNumStr(val)+"%"
|
||||
function(val) {
|
||||
return shortNumStr(val) + '%';
|
||||
},
|
||||
this.groupBy(type)
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
fetchPercentile: function(percentile, serviceName, type, options){
|
||||
var sel = this.makeHTTPSelector(serviceName, type)
|
||||
var subject = this.makeSubject(serviceName, type)
|
||||
var groupBy = "le"
|
||||
if (type == "upstream") {
|
||||
groupBy += ",envoy_http_conn_manager_prefix"
|
||||
} else if (type == "downstream") {
|
||||
groupBy += ",local_cluster"
|
||||
fetchPercentile: function(percentile, serviceName, type, options) {
|
||||
var sel = this.makeHTTPSelector(serviceName, type);
|
||||
var subject = this.makeSubject(serviceName, type);
|
||||
var groupBy = 'le';
|
||||
if (type == 'upstream') {
|
||||
groupBy += ',envoy_http_conn_manager_prefix';
|
||||
} else if (type == 'downstream') {
|
||||
groupBy += ',local_cluster';
|
||||
}
|
||||
var metricPfx = this.metricPrefixHTTP(type)
|
||||
var q = `histogram_quantile(${percentile/100}, sum by(${groupBy}) (rate(${metricPfx}_time_bucket{${sel}}[15m])))`
|
||||
if (type == "upstream") {
|
||||
q = this.upstreamRelabelQueryHTTP(q)
|
||||
} else if (type == "downstream") {
|
||||
q = this.downstreamRelabelQuery(q)
|
||||
var metricPfx = this.metricPrefixHTTP(type);
|
||||
var q = `histogram_quantile(${percentile /
|
||||
100}, sum by(${groupBy}) (rate(${metricPfx}_time_bucket{${sel}}[15m])))`;
|
||||
if (type == 'upstream') {
|
||||
q = this.upstreamRelabelQueryHTTP(q);
|
||||
} else if (type == 'downstream') {
|
||||
q = this.downstreamRelabelQuery(q);
|
||||
}
|
||||
return this.fetchStat(q,
|
||||
return this.fetchStat(
|
||||
q,
|
||||
`P${percentile}`,
|
||||
`<b>${subject}</b> ${percentile}th percentile request service time over the last 15 minutes`,
|
||||
shortTimeStr,
|
||||
this.groupBy(type)
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
fetchConnRate: function(serviceName, type, options) {
|
||||
var sel = this.makeTCPSelector(serviceName, type)
|
||||
var subject = this.makeSubject(serviceName, type)
|
||||
var metricPfx = this.metricPrefixTCP(type)
|
||||
var q = `sum(rate(${metricPfx}_total{${sel}}[15m]))`
|
||||
return this.fetchStat(this.groupQueryTCP(type, q),
|
||||
"CR",
|
||||
var sel = this.makeTCPSelector(serviceName, type);
|
||||
var subject = this.makeSubject(serviceName, type);
|
||||
var metricPfx = this.metricPrefixTCP(type);
|
||||
var q = `sum(rate(${metricPfx}_total{${sel}}[15m]))`;
|
||||
return this.fetchStat(
|
||||
this.groupQueryTCP(type, q),
|
||||
'CR',
|
||||
`<b>${subject}</b> inbound TCP connections per second averaged over the last 15 minutes`,
|
||||
shortNumStr,
|
||||
this.groupBy(type)
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
fetchServiceRx: function(serviceName, type, options) {
|
||||
var sel = this.makeTCPSelector(serviceName, type)
|
||||
var subject = this.makeSubject(serviceName, type)
|
||||
var metricPfx = this.metricPrefixTCP(type)
|
||||
var q = `8 * sum(rate(${metricPfx}_rx_bytes_total{${sel}}[15m]))`
|
||||
return this.fetchStat(this.groupQueryTCP(type, q),
|
||||
"RX",
|
||||
var sel = this.makeTCPSelector(serviceName, type);
|
||||
var subject = this.makeSubject(serviceName, type);
|
||||
var metricPfx = this.metricPrefixTCP(type);
|
||||
var q = `8 * sum(rate(${metricPfx}_rx_bytes_total{${sel}}[15m]))`;
|
||||
return this.fetchStat(
|
||||
this.groupQueryTCP(type, q),
|
||||
'RX',
|
||||
`<b>${subject}</b> received bits per second averaged over the last 15 minutes`,
|
||||
shortNumStr,
|
||||
this.groupBy(type)
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
fetchServiceTx: function(serviceName, type, options) {
|
||||
var sel = this.makeTCPSelector(serviceName, type)
|
||||
var subject = this.makeSubject(serviceName, type)
|
||||
var metricPfx = this.metricPrefixTCP(type)
|
||||
var q = `8 * sum(rate(${metricPfx}_tx_bytes_total{${sel}}[15m]))`
|
||||
var self = this
|
||||
return this.fetchStat(this.groupQueryTCP(type, q),
|
||||
"TX",
|
||||
var sel = this.makeTCPSelector(serviceName, type);
|
||||
var subject = this.makeSubject(serviceName, type);
|
||||
var metricPfx = this.metricPrefixTCP(type);
|
||||
var q = `8 * sum(rate(${metricPfx}_tx_bytes_total{${sel}}[15m]))`;
|
||||
var self = this;
|
||||
return this.fetchStat(
|
||||
this.groupQueryTCP(type, q),
|
||||
'TX',
|
||||
`<b>${subject}</b> transmitted bits per second averaged over the last 15 minutes`,
|
||||
shortNumStr,
|
||||
this.groupBy(type)
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
fetchServiceNoRoute: function(serviceName, type, options) {
|
||||
var sel = this.makeTCPSelector(serviceName, type)
|
||||
var subject = this.makeSubject(serviceName, type)
|
||||
var metricPfx = this.metricPrefixTCP(type)
|
||||
var metric = "_no_route"
|
||||
if (type == "downstream") {
|
||||
metric = "_connect_fail"
|
||||
var sel = this.makeTCPSelector(serviceName, type);
|
||||
var subject = this.makeSubject(serviceName, type);
|
||||
var metricPfx = this.metricPrefixTCP(type);
|
||||
var metric = '_no_route';
|
||||
if (type == 'downstream') {
|
||||
metric = '_connect_fail';
|
||||
}
|
||||
var q = `sum(rate(${metricPfx}${metric}{${sel}}[15m]))`
|
||||
return this.fetchStat(this.groupQueryTCP(type, q),
|
||||
"NR",
|
||||
var q = `sum(rate(${metricPfx}${metric}{${sel}}[15m]))`;
|
||||
return this.fetchStat(
|
||||
this.groupQueryTCP(type, q),
|
||||
'NR',
|
||||
`<b>${subject}</b> unroutable (failed) connections per second averaged over the last 15 minutes`,
|
||||
shortNumStr,
|
||||
this.groupBy(type)
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
fetchStat: function(promql, label, desc, formatter, groupBy) {
|
||||
if (!groupBy) {
|
||||
// If we don't have a grouped result and its just a single stat, return
|
||||
// no result as a zero not a missing stat.
|
||||
promql += " OR on() vector(0)";
|
||||
promql += ' OR on() vector(0)';
|
||||
}
|
||||
//console.log(promql)
|
||||
var params = {
|
||||
query: promql,
|
||||
time: (new Date).getTime()/1000
|
||||
}
|
||||
return this.httpGet("/api/v1/query", params).then(function(response){
|
||||
time: new Date().getTime() / 1000,
|
||||
};
|
||||
return this.httpGet('/api/v1/query', params).then(function(response) {
|
||||
if (!groupBy) {
|
||||
// Not grouped, expect just one stat value return that
|
||||
var v = parseFloat(response.data.result[0].value[1])
|
||||
var v = parseFloat(response.data.result[0].value[1]);
|
||||
return {
|
||||
label: label,
|
||||
desc: desc,
|
||||
value: formatter(v)
|
||||
}
|
||||
value: formatter(v),
|
||||
};
|
||||
}
|
||||
|
||||
var data = {};
|
||||
|
@ -672,11 +683,11 @@
|
|||
data[groupName] = {
|
||||
label: label,
|
||||
desc: desc.replace('{{GROUP}}', groupName),
|
||||
value: formatter(v)
|
||||
}
|
||||
value: formatter(v),
|
||||
};
|
||||
}
|
||||
return data;
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
fetchSeries: function(promql, options) {
|
||||
|
@ -684,23 +695,23 @@
|
|||
query: promql,
|
||||
start: options.start,
|
||||
end: options.end,
|
||||
step: "10s",
|
||||
timeout: "8s"
|
||||
}
|
||||
return this.httpGet("/api/v1/query_range", params)
|
||||
step: '10s',
|
||||
timeout: '8s',
|
||||
};
|
||||
return this.httpGet('/api/v1/query_range', params);
|
||||
},
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// Helper functions
|
||||
function shortNumStr(n) {
|
||||
if (n < 1e3) {
|
||||
if (Number.isInteger(n)) return ""+n
|
||||
if (Number.isInteger(n)) return '' + n;
|
||||
if (n >= 100) {
|
||||
// Go to 3 significant figures but wrap it in Number to avoid scientific
|
||||
// notation lie 2.3e+2 for 230.
|
||||
return Number(n.toPrecision(3))
|
||||
} if (n < 1) {
|
||||
return Number(n.toPrecision(3));
|
||||
}
|
||||
if (n < 1) {
|
||||
// Very small numbers show with limited precision to prevent long string
|
||||
// of 0.000000.
|
||||
return Number(n.toFixed(2));
|
||||
|
@ -709,29 +720,28 @@
|
|||
return Number(n.toPrecision(2));
|
||||
}
|
||||
}
|
||||
if (n >= 1e3 && n < 1e6) return +(n / 1e3).toPrecision(3) + "k";
|
||||
if (n >= 1e6 && n < 1e9) return +(n / 1e6).toPrecision(3) + "m";
|
||||
if (n >= 1e9 && n < 1e12) return +(n / 1e9).toPrecision(3) + "g";
|
||||
if (n >= 1e12) return +(n / 1e12).toFixed(0) + "t";
|
||||
if (n >= 1e3 && n < 1e6) return +(n / 1e3).toPrecision(3) + 'k';
|
||||
if (n >= 1e6 && n < 1e9) return +(n / 1e6).toPrecision(3) + 'm';
|
||||
if (n >= 1e9 && n < 1e12) return +(n / 1e9).toPrecision(3) + 'g';
|
||||
if (n >= 1e12) return +(n / 1e12).toFixed(0) + 't';
|
||||
}
|
||||
|
||||
function shortTimeStr(n) {
|
||||
if (n < 1e3) return Math.round(n) + "ms";
|
||||
if (n < 1e3) return Math.round(n) + 'ms';
|
||||
|
||||
var secs = n / 1e3
|
||||
if (secs < 60) return secs.toFixed(1) + "s"
|
||||
var secs = n / 1e3;
|
||||
if (secs < 60) return secs.toFixed(1) + 's';
|
||||
|
||||
var mins = secs/60
|
||||
if (mins < 60) return mins.toFixed(1) + "m"
|
||||
var mins = secs / 60;
|
||||
if (mins < 60) return mins.toFixed(1) + 'm';
|
||||
|
||||
var hours = mins/60
|
||||
if (hours < 24) return hours.toFixed(1) + "h"
|
||||
var hours = mins / 60;
|
||||
if (hours < 24) return hours.toFixed(1) + 'h';
|
||||
|
||||
var days = hours/24
|
||||
return days.toFixed(1) + "d"
|
||||
var days = hours / 24;
|
||||
return days.toFixed(1) + 'd';
|
||||
}
|
||||
|
||||
/* global consul:writable */
|
||||
window.consul.registerMetricsProvider("prometheus", prometheusProvider)
|
||||
|
||||
}());
|
||||
window.consul.registerMetricsProvider('prometheus', prometheusProvider);
|
||||
})();
|
||||
|
|
Loading…
Reference in New Issue