Move app graphs graphite query to backend
This commit is contained in:
+2
-1
@@ -324,7 +324,8 @@ async function createSubcontainer(app, name, cmd, options) {
|
||||
ExposedPorts: isAppContainer ? exposedPorts : { },
|
||||
Volumes: { // see also ReadonlyRootfs
|
||||
'/tmp': {},
|
||||
'/run': {}
|
||||
'/run': {},
|
||||
'/app/code/node_modules': {}
|
||||
},
|
||||
Labels: {
|
||||
'fqdn': app.fqdn,
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
'use strict';
|
||||
|
||||
exports = module.exports = {
|
||||
getByAppId
|
||||
};
|
||||
|
||||
const assert = require('assert'),
|
||||
BoxError = require('./boxerror.js'),
|
||||
safe = require('safetydance'),
|
||||
superagent = require('superagent'),
|
||||
debug = require('debug')('box:graphs');
|
||||
|
||||
const GRAPHITE_RENDER_URL = 'http://127.0.0.1:8417/graphite-web/render';
|
||||
|
||||
async function getByAppId(appId, fromMinutes, noNullPoints) {
|
||||
assert.strictEqual(typeof appId, 'string');
|
||||
assert.strictEqual(typeof fromMinutes, 'number');
|
||||
assert.strictEqual(typeof noNullPoints, 'boolean');
|
||||
|
||||
const timeBucketSize = fromMinutes > (24 * 60) ? (6*60) : 5;
|
||||
|
||||
debug(`getByAppId: ${appId} from ${fromMinutes}`);
|
||||
|
||||
const memoryQuery = {
|
||||
target: `summarize(sum(collectd.localhost.table-${appId}-memory.gauge-rss, collectd.localhost.table-${appId}-memory.gauge-swap), "${timeBucketSize}min", "avg")`,
|
||||
format: 'json',
|
||||
from: `-${fromMinutes}min`,
|
||||
until: 'now'
|
||||
};
|
||||
if (noNullPoints) memoryQuery.noNullPoints = true;
|
||||
|
||||
const [memoryError, memoryResponse] = await safe(superagent.get(GRAPHITE_RENDER_URL)
|
||||
.query(memoryQuery)
|
||||
.timeout(30 * 1000)
|
||||
.ok(() => true));
|
||||
|
||||
if (memoryError) throw new BoxError(BoxError.NETWORK_ERROR, memoryError.message);
|
||||
if (memoryResponse.status !== 200) throw new BoxError(BoxError.EXTERNAL_ERROR, `Unknown error: ${memoryError.message}`);
|
||||
|
||||
const diskQuery = {
|
||||
target: `summarize(collectd.localhost.du-${appId}.capacity-usage, "${timeBucketSize}min", "avg")`,
|
||||
format: 'json',
|
||||
from: `-${fromMinutes}min`,
|
||||
until: 'now'
|
||||
};
|
||||
if (noNullPoints) diskQuery.noNullPoints = true;
|
||||
|
||||
const [diskError, diskResponse] = await safe(superagent.get(GRAPHITE_RENDER_URL)
|
||||
.query(diskQuery)
|
||||
.timeout(30 * 1000)
|
||||
.ok(() => true));
|
||||
|
||||
if (diskError) throw new BoxError(BoxError.NETWORK_ERROR, diskError.message);
|
||||
if (diskResponse.status !== 200) throw new BoxError(BoxError.EXTERNAL_ERROR, `Unknown error: ${diskError.message}`);
|
||||
|
||||
return { memory: memoryResponse.body[0], disk: diskResponse.body[0] };
|
||||
}
|
||||
+20
-3
@@ -1,12 +1,17 @@
|
||||
'use strict';
|
||||
|
||||
exports = module.exports = {
|
||||
getGraphs
|
||||
getGraphs,
|
||||
getAppGraphs
|
||||
};
|
||||
|
||||
const middleware = require('../middleware/index.js'),
|
||||
const assert = require('assert'),
|
||||
graphs = require('../graphs.js'),
|
||||
safe = require('safetydance'),
|
||||
middleware = require('../middleware/index.js'),
|
||||
url = require('url'),
|
||||
HttpError = require('connect-lastmile').HttpError,
|
||||
url = require('url');
|
||||
HttpSuccess = require('connect-lastmile').HttpSuccess;
|
||||
|
||||
// for testing locally: curl 'http://127.0.0.1:8417/graphite-web/render?format=json&from=-1min&target=absolute(collectd.localhost.du-docker.capacity-usage)'
|
||||
// the datapoint is (value, timestamp) https://buildmedia.readthedocs.org/media/pdf/graphite/0.9.16/graphite.pdf
|
||||
@@ -36,3 +41,15 @@ function getGraphs(req, res, next) {
|
||||
});
|
||||
}
|
||||
|
||||
async function getAppGraphs(req, res, next) {
|
||||
assert.strictEqual(typeof req.app, 'object');
|
||||
|
||||
if (!req.query.fromMinutes || !parseInt(req.query.fromMinutes)) return next(new HttpError(400, 'fromMinutes must be a number'));
|
||||
|
||||
const fromMinutes = parseInt(req.query.fromMinutes) || 60;
|
||||
const noNullPoints = !!req.query.noNullPoints;
|
||||
const [error, result] = await safe(graphs.getByAppId(req.app.id, fromMinutes, noNullPoints));
|
||||
if (error) return next(new HttpError(500, error));
|
||||
|
||||
next(new HttpSuccess(200, result));
|
||||
}
|
||||
|
||||
+1
-1
@@ -245,7 +245,7 @@ function initializeExpressSync() {
|
||||
router.get ('/api/v1/apps/:id/eventlog', token, routes.apps.load, authorizeOperator, routes.apps.listEventlog);
|
||||
router.get ('/api/v1/apps/:id/limits', token, routes.apps.load, authorizeOperator, routes.apps.getLimits);
|
||||
router.get ('/api/v1/apps/:id/task', token, routes.apps.load, authorizeOperator, routes.apps.getTask);
|
||||
router.get ('/api/v1/apps/:id/graphs', token, routes.apps.load, authorizeOperator, routes.graphs.getGraphs); // TODO: restrict to app graphs
|
||||
router.get ('/api/v1/apps/:id/graphs', token, routes.apps.load, authorizeOperator, routes.graphs.getAppGraphs);
|
||||
router.post('/api/v1/apps/:id/clone', json, token, routes.apps.load, authorizeAdmin, routes.apps.clone);
|
||||
router.get ('/api/v1/apps/:id/download', token, routes.apps.load, authorizeOperator, routes.apps.downloadFile);
|
||||
router.post('/api/v1/apps/:id/upload', json, token, multipart, routes.apps.load, authorizeOperator, routes.apps.uploadFile);
|
||||
|
||||
Reference in New Issue
Block a user