ui: Dev/Test environment configurable metrics (#9345)
In order to test certain setups for our metrics visualizations we need to be able to setup several different `ui_config` settings during development/testing. Generally in the UI, we use the Web Inspector to set various cookie values to configure the UI how we need to see it whilst developing, so this PR: 1. Routes `ui_config` through a dev time only `CONSUL_UI_CONFIG` env variable so we can change it via cookies vars. 2. Adds `CONSUL_METRICS_PROXY_ENABLE`, `CONSUL_METRICS_PROVIDER` and `CONSUL_SERVICE_DASHBOARD_URL` so it's easy to set/unset these only values during development. 3. Adds an acceptance testing step so we can setup `ui_config` to whatever we want during testing. 4. Adds an async 'repository-like' method to the `UiConfig` Service so it feels like a repository - incase we ever need to get this via an HTTP API+blocking query. 5. Vaguely unrelated: we allow cookie values to be set via the location.hash whilst in development only e.g. `/ui/services#CONSUL_METRICS_PROXY_ENABLE=1` so we can link to different setups if we ever need to. All values added here are empty/falsey by default, so in order to see how it was previously you'll need to set the appropriate cookies values, but you can now also easily preview/test the the metrics viz in different/disabled states (with differing `ui_config`)
This commit is contained in:
parent
eae9df241d
commit
c24c70af46
|
@ -1,20 +1,13 @@
|
|||
import { inject as service } from '@ember/service';
|
||||
import RepositoryService from 'consul-ui/services/repository';
|
||||
import { env } from 'consul-ui/env';
|
||||
|
||||
// meta is used by DataSource to configure polling. The interval controls how
|
||||
// long between each poll to the metrics provider. TODO - make this configurable
|
||||
// in the UI settings.
|
||||
const meta = {
|
||||
interval: env('CONSUL_METRICS_POLL_INTERVAL') || 10000,
|
||||
};
|
||||
// CONSUL_METRICS_POLL_INTERVAL controls how long between each poll to the
|
||||
// metrics provider
|
||||
|
||||
export default class MetricsService extends RepositoryService {
|
||||
@service('ui-config')
|
||||
cfg;
|
||||
|
||||
@service('client/http')
|
||||
client;
|
||||
@service('ui-config') cfg;
|
||||
@service('env') env;
|
||||
@service('client/http') client;
|
||||
|
||||
error = null;
|
||||
|
||||
|
@ -49,9 +42,11 @@ export default class MetricsService extends RepositoryService {
|
|||
this.provider.serviceRecentSummarySeries(slug, dc, nspace, protocol, {}),
|
||||
this.provider.serviceRecentSummaryStats(slug, dc, nspace, protocol, {}),
|
||||
];
|
||||
return Promise.all(promises).then(function (results) {
|
||||
return Promise.all(promises).then(results => {
|
||||
return {
|
||||
meta: meta,
|
||||
meta: {
|
||||
interval: this.env.var('CONSUL_METRICS_POLL_INTERVAL') || 10000,
|
||||
},
|
||||
series: results[0],
|
||||
stats: results[1].stats,
|
||||
};
|
||||
|
@ -62,8 +57,10 @@ export default class MetricsService extends RepositoryService {
|
|||
if (this.error) {
|
||||
return Promise.reject(this.error);
|
||||
}
|
||||
return this.provider.upstreamRecentSummaryStats(slug, dc, nspace, {}).then(function (result) {
|
||||
result.meta = meta;
|
||||
return this.provider.upstreamRecentSummaryStats(slug, dc, nspace, {}).then(result => {
|
||||
result.meta = {
|
||||
interval: this.env.var('CONSUL_METRICS_POLL_INTERVAL') || 10000,
|
||||
};
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
@ -72,8 +69,10 @@ export default class MetricsService extends RepositoryService {
|
|||
if (this.error) {
|
||||
return Promise.reject(this.error);
|
||||
}
|
||||
return this.provider.downstreamRecentSummaryStats(slug, dc, nspace, {}).then(function (result) {
|
||||
result.meta = meta;
|
||||
return this.provider.downstreamRecentSummaryStats(slug, dc, nspace, {}).then(result => {
|
||||
result.meta = {
|
||||
interval: this.env.var('CONSUL_METRICS_POLL_INTERVAL') || 10000,
|
||||
};
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
import Service from '@ember/service';
|
||||
import Service, { inject as service } from '@ember/service';
|
||||
import { get } from '@ember/object';
|
||||
|
||||
export default class UiConfigService extends Service {
|
||||
config = undefined;
|
||||
@service('env') env;
|
||||
|
||||
async findByPath(path, configuration = {}) {
|
||||
return get(this.get(), path);
|
||||
}
|
||||
|
||||
get() {
|
||||
if (this.config === undefined) {
|
||||
// Load config from our special meta tag for now. Later it might come from
|
||||
// an API instead/as well.
|
||||
var meta = unescape(document.getElementsByName('consul-ui/ui_config')[0].content);
|
||||
this.config = JSON.parse(meta);
|
||||
}
|
||||
return this.config;
|
||||
return this.env.var('CONSUL_UI_CONFIG');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,26 @@
|
|||
import { runInDebug } from '@ember/debug';
|
||||
// 'environment' getter
|
||||
// there are currently 3 levels of environment variables:
|
||||
// 1. Those that can be set by the user by setting localStorage values
|
||||
// 2. Those that can be set by the operator either via ui_config, or inferring
|
||||
// from other server type properties (protocol)
|
||||
// 3. Those that can be set only during development by adding cookie values
|
||||
// via the browsers Web Inspector, or via the browsers hash (#COOKIE_NAME=1),
|
||||
// which is useful for showing the UI with various settings enabled/disabled
|
||||
export default function(config = {}, win = window, doc = document) {
|
||||
const dev = function() {
|
||||
return doc.cookie
|
||||
// look at the hash in the URL and transfer anything after the hash into
|
||||
// cookies to enable linking of the UI with various settings enabled
|
||||
runInDebug(() => {
|
||||
if (
|
||||
typeof win.location !== 'undefined' &&
|
||||
typeof win.location.hash === 'string' &&
|
||||
win.location.hash.length > 0
|
||||
) {
|
||||
doc.cookie = win.location.hash.substr(1);
|
||||
}
|
||||
});
|
||||
const dev = function(str = doc.cookie) {
|
||||
return str
|
||||
.split(';')
|
||||
.filter(item => item !== '')
|
||||
.map(item => item.trim().split('='));
|
||||
|
@ -20,6 +40,7 @@ export default function(config = {}, win = window, doc = document) {
|
|||
return {};
|
||||
}
|
||||
};
|
||||
const ui_config = JSON.parse(unescape(doc.getElementsByName('consul-ui/ui_config')[0].content));
|
||||
const scripts = doc.getElementsByTagName('script');
|
||||
// we use the currently executing script as a reference
|
||||
// to figure out where we are for other things such as
|
||||
|
@ -33,6 +54,21 @@ export default function(config = {}, win = window, doc = document) {
|
|||
const operator = function(str, env) {
|
||||
let protocol;
|
||||
switch (str) {
|
||||
case 'CONSUL_UI_CONFIG':
|
||||
const dashboards = {};
|
||||
const provider = env('CONSUL_METRICS_PROVIDER');
|
||||
const proxy = env('CONSUL_METRICS_PROXY_ENABLED');
|
||||
dashboards.service = env('CONSUL_SERVICE_DASHBOARD_URL');
|
||||
if (provider) {
|
||||
ui_config.metrics_provider = provider;
|
||||
}
|
||||
if (proxy) {
|
||||
ui_config.metrics_proxy_enabled = proxy;
|
||||
}
|
||||
if (dashboards.service) {
|
||||
ui_config.dashboard_url_templates = dashboards;
|
||||
}
|
||||
return ui_config;
|
||||
case 'CONSUL_BASE_UI_URL':
|
||||
return currentSrc
|
||||
.split('/')
|
||||
|
@ -85,6 +121,12 @@ export default function(config = {}, win = window, doc = document) {
|
|||
case 'CONSUL_SSO_ENABLE':
|
||||
prev['CONSUL_SSO_ENABLED'] = !!JSON.parse(String(value).toLowerCase());
|
||||
break;
|
||||
case 'CONSUL_METRICS_PROXY_ENABLE':
|
||||
prev['CONSUL_METRICS_PROXY_ENABLED'] = !!JSON.parse(String(value).toLowerCase());
|
||||
break;
|
||||
case 'CONSUL_UI_CONFIG':
|
||||
prev['CONSUL_UI_CONFIG'] = JSON.parse(value);
|
||||
break;
|
||||
default:
|
||||
prev[key] = value;
|
||||
}
|
||||
|
@ -109,7 +151,10 @@ export default function(config = {}, win = window, doc = document) {
|
|||
case 'CONSUL_UI_REALTIME_RUNNER':
|
||||
// these are strings
|
||||
return user(str) || ui(str);
|
||||
|
||||
case 'CONSUL_UI_CONFIG':
|
||||
case 'CONSUL_METRICS_PROVIDER':
|
||||
case 'CONSUL_METRICS_PROXY_ENABLE':
|
||||
case 'CONSUL_SERVICE_DASHBOARD_URL':
|
||||
case 'CONSUL_BASE_UI_URL':
|
||||
case 'CONSUL_HTTP_PROTOCOL':
|
||||
case 'CONSUL_HTTP_MAX_CONNECTIONS':
|
||||
|
|
|
@ -3,15 +3,7 @@ module.exports = ({ appName, environment, rootURL, config }) => `
|
|||
<meta name="consul-ui/ui_config" content="${
|
||||
environment === 'production'
|
||||
? `{{ jsonEncodeAndEscape .UIConfig }}`
|
||||
: escape(
|
||||
JSON.stringify({
|
||||
metrics_provider: 'prometheus',
|
||||
metrics_proxy_enabled: true,
|
||||
dashboard_url_templates: {
|
||||
service: 'https://example.com?{{Service.Name}}&{{Datacenter}}',
|
||||
},
|
||||
})
|
||||
)
|
||||
: escape(JSON.stringify({}))
|
||||
}" />
|
||||
|
||||
<link rel="icon" type="image/png" href="${rootURL}assets/favicon-32x32.png" sizes="32x32">
|
||||
|
|
|
@ -107,10 +107,15 @@ Feature: dc / services / show: Show Service
|
|||
---
|
||||
Scenario: Given a dashboard template has been set
|
||||
Given 1 datacenter model with the value "dc1"
|
||||
And ui_config from yaml
|
||||
---
|
||||
dashboard_url_templates:
|
||||
service: https://something.com?{{Service.Name}}&{{Datacenter}}
|
||||
---
|
||||
When I visit the service page for yaml
|
||||
---
|
||||
dc: dc1
|
||||
service: service-0
|
||||
---
|
||||
# The Metrics dashboard should use the Service.Name not the ID
|
||||
And I see href on the metricsAnchor like "https://example.com?service-0&dc1"
|
||||
And I see href on the metricsAnchor like "https://something.com?service-0&dc1"
|
||||
|
|
|
@ -9,7 +9,23 @@ import dictionary from '../dictionary';
|
|||
const getDictionary = dictionary(utils);
|
||||
|
||||
const staticClassList = [...document.documentElement.classList];
|
||||
const getCookies = () => {
|
||||
return Object.fromEntries(document.cookie.split(';').map(item => item.split('=')));
|
||||
};
|
||||
const getResetCookies = function() {
|
||||
const start = getCookies();
|
||||
return () => {
|
||||
const startKeys = Object.keys(start);
|
||||
const endKeys = Object.keys(getCookies());
|
||||
const diff = endKeys.filter(key => !startKeys.includes(key));
|
||||
diff.forEach(item => {
|
||||
document.cookie = `${item}= ; expires=${new Date(0)}`;
|
||||
});
|
||||
};
|
||||
};
|
||||
let resetCookies;
|
||||
const reset = function() {
|
||||
resetCookies();
|
||||
window.localStorage.clear();
|
||||
api.server.reset();
|
||||
const list = document.documentElement.classList;
|
||||
|
@ -21,6 +37,7 @@ const reset = function() {
|
|||
});
|
||||
};
|
||||
const startup = function() {
|
||||
resetCookies = getResetCookies();
|
||||
api.server.setCookie('CONSUL_LATENCY', 0);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default function(scenario, create) {
|
||||
export default function(scenario, create, win = window, doc = document) {
|
||||
scenario
|
||||
.given(['an external edit results in $number $model model[s]?'], function(number, model) {
|
||||
return create(number, model);
|
||||
|
@ -17,7 +17,10 @@ export default function(scenario, create) {
|
|||
)
|
||||
.given(['settings from yaml\n$yaml'], function(data) {
|
||||
return Object.keys(data).forEach(function(key) {
|
||||
window.localStorage[key] = JSON.stringify(data[key]);
|
||||
win.localStorage[key] = JSON.stringify(data[key]);
|
||||
});
|
||||
})
|
||||
.given(['ui_config from yaml\n$yaml'], function(data) {
|
||||
doc.cookie = `CONSUL_UI_CONFIG=${JSON.stringify(data)}`;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,11 +9,12 @@ const getEntriesByType = function(type) {
|
|||
},
|
||||
];
|
||||
};
|
||||
const makeGetElementsByTagName = function(src) {
|
||||
const makeGetElementsBy = function(str) {
|
||||
return function(name) {
|
||||
return [
|
||||
{
|
||||
src: src,
|
||||
src: str,
|
||||
content: str,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
@ -22,13 +23,17 @@ const win = {
|
|||
performance: {
|
||||
getEntriesByType: getEntriesByType,
|
||||
},
|
||||
location: {
|
||||
hash: '',
|
||||
},
|
||||
localStorage: {
|
||||
getItem: function(key) {},
|
||||
},
|
||||
};
|
||||
const doc = {
|
||||
cookie: '',
|
||||
getElementsByTagName: makeGetElementsByTagName(''),
|
||||
getElementsByTagName: makeGetElementsBy(''),
|
||||
getElementsByName: makeGetElementsBy('{}'),
|
||||
};
|
||||
module('Unit | Utility | getEnvironment', function() {
|
||||
test('it returns a function', function(assert) {
|
||||
|
@ -55,14 +60,16 @@ module('Unit | Utility | getEnvironment', function() {
|
|||
let expected = 'http://localhost/ui';
|
||||
let doc = {
|
||||
cookie: '',
|
||||
getElementsByTagName: makeGetElementsByTagName(`${expected}/assets/consul-ui.js`),
|
||||
getElementsByTagName: makeGetElementsBy(`${expected}/assets/consul-ui.js`),
|
||||
getElementsByName: makeGetElementsBy('{}'),
|
||||
};
|
||||
let env = getEnvironment(config, win, doc);
|
||||
assert.equal(env('CONSUL_BASE_UI_URL'), expected);
|
||||
expected = 'http://localhost/somewhere/else';
|
||||
doc = {
|
||||
cookie: '',
|
||||
getElementsByTagName: makeGetElementsByTagName(`${expected}/assets/consul-ui.js`),
|
||||
getElementsByTagName: makeGetElementsBy(`${expected}/assets/consul-ui.js`),
|
||||
getElementsByName: makeGetElementsBy('{}'),
|
||||
};
|
||||
env = getEnvironment(config, win, doc);
|
||||
assert.equal(env('CONSUL_BASE_UI_URL'), expected);
|
||||
|
@ -135,7 +142,8 @@ module('Unit | Utility | getEnvironment', function() {
|
|||
};
|
||||
let doc = {
|
||||
cookie: 'CONSUL_NSPACES_ENABLE=1',
|
||||
getElementsByTagName: makeGetElementsByTagName(''),
|
||||
getElementsByTagName: makeGetElementsBy(''),
|
||||
getElementsByName: makeGetElementsBy('{}'),
|
||||
};
|
||||
let env = getEnvironment(config, win, doc);
|
||||
assert.ok(env('CONSUL_NSPACES_ENABLED'));
|
||||
|
@ -145,7 +153,8 @@ module('Unit | Utility | getEnvironment', function() {
|
|||
};
|
||||
doc = {
|
||||
cookie: 'CONSUL_NSPACES_ENABLE=0',
|
||||
getElementsByTagName: makeGetElementsByTagName(''),
|
||||
getElementsByTagName: makeGetElementsBy(''),
|
||||
getElementsByName: makeGetElementsBy('{}'),
|
||||
};
|
||||
env = getEnvironment(config, win, doc);
|
||||
assert.notOk(env('CONSUL_NSPACES_ENABLED'));
|
||||
|
@ -169,7 +178,8 @@ module('Unit | Utility | getEnvironment', function() {
|
|||
};
|
||||
let doc = {
|
||||
cookie: 'CONSUL_NSPACES_ENABLE=1',
|
||||
getElementsByTagName: makeGetElementsByTagName(''),
|
||||
getElementsByTagName: makeGetElementsBy(''),
|
||||
getElementsByName: makeGetElementsBy('{}'),
|
||||
};
|
||||
let env = getEnvironment(config, win, doc);
|
||||
assert.notOk(env('CONSUL_NSPACES_ENABLED'));
|
||||
|
@ -179,7 +189,8 @@ module('Unit | Utility | getEnvironment', function() {
|
|||
};
|
||||
doc = {
|
||||
cookie: 'CONSUL_NSPACES_ENABLE=0',
|
||||
getElementsByTagName: makeGetElementsByTagName(''),
|
||||
getElementsByTagName: makeGetElementsBy(''),
|
||||
getElementsByName: makeGetElementsBy('{}'),
|
||||
};
|
||||
env = getEnvironment(config, win, doc);
|
||||
assert.ok(env('CONSUL_NSPACES_ENABLED'));
|
||||
|
|
Loading…
Reference in New Issue