add redis status

part of #671
This commit is contained in:
Girish Ramakrishnan
2020-05-03 17:38:15 -07:00
parent d60714e4e6
commit dcaccc2d7a
5 changed files with 156 additions and 62 deletions

View File

@@ -210,6 +210,14 @@ const SERVICES = {
}
};
const APP_SERVICES = {
redis: {
status: (instance, done) => containerStatus(`redis-${instance}`, 'CLOUDRON_REDIS_TOKEN', done),
restart: (instance, done) => restartContainer(`redis-${instance}`, done),
defaultMemoryLimit: 150 * 1024 * 1024
}
};
function debugApp(app /*, args */) {
assert(typeof app === 'object');
@@ -331,17 +339,57 @@ function getServices(callback) {
let services = Object.keys(SERVICES);
callback(null, services);
appdb.getAll(function (error, apps) {
if (error) return callback(error);
for (let app of apps) {
if (app.manifest.addons && app.manifest.addons['redis']) services.push(`redis:${app.id}`);
}
callback(null, services);
});
}
function getService(serviceName, callback) {
assert.strictEqual(typeof serviceName, 'string');
function getServicesConfig(id, callback) {
assert.strictEqual(typeof id, 'string');
assert.strictEqual(typeof callback, 'function');
if (!SERVICES[serviceName]) return callback(new BoxError(BoxError.NOT_FOUND));
const [name, instance ] = id.split(':');
if (!instance) {
settings.getPlatformConfig(function (error, platformConfig) {
if (error) return callback(error);
callback(null, SERVICES[name], platformConfig);
});
return;
}
appdb.get(instance, function (error, app) {
if (error) return callback(error);
callback(null, APP_SERVICES[name], app.servicesConfig);
});
}
function getService(id, callback) {
assert.strictEqual(typeof id, 'string');
assert.strictEqual(typeof callback, 'function');
const [name, instance ] = id.split(':');
let containerStatusFunc;
if (instance) {
if (!APP_SERVICES[name]) return callback(new BoxError(BoxError.NOT_FOUND));
containerStatusFunc = APP_SERVICES[name].status.bind(null, instance);
} else if (SERVICES[name]) {
containerStatusFunc = SERVICES[name].status;
} else {
return callback(new BoxError(BoxError.NOT_FOUND));
}
var tmp = {
name: serviceName,
name: name,
status: null,
memoryUsed: 0,
memoryPercent: 0,
@@ -353,60 +401,76 @@ function getService(serviceName, callback) {
}
};
settings.getPlatformConfig(function (error, platformConfig) {
containerStatusFunc(function (error, result) {
if (error) return callback(error);
if (platformConfig[serviceName] && platformConfig[serviceName].memory && platformConfig[serviceName].memorySwap) {
tmp.config.memory = platformConfig[serviceName].memory;
tmp.config.memorySwap = platformConfig[serviceName].memorySwap;
} else if (SERVICES[serviceName].defaultMemoryLimit) {
tmp.config.memory = SERVICES[serviceName].defaultMemoryLimit;
tmp.config.memorySwap = tmp.config.memory * 2;
}
tmp.status = result.status;
tmp.memoryUsed = result.memoryUsed;
tmp.memoryPercent = result.memoryPercent;
tmp.error = result.error || null;
SERVICES[serviceName].status(function (error, result) {
getServicesConfig(id, function (error, service, servicesConfig) {
if (error) return callback(error);
tmp.status = result.status;
tmp.memoryUsed = result.memoryUsed;
tmp.memoryPercent = result.memoryPercent;
tmp.error = result.error || null;
const serviceConfig = servicesConfig[name];
if (serviceConfig && serviceConfig.memory && serviceConfig.memorySwap) {
tmp.config.memory = serviceConfig.memory;
tmp.config.memorySwap = serviceConfig.memorySwap;
} else if (service.defaultMemoryLimit) {
tmp.config.memory = service.defaultMemoryLimit;
tmp.config.memorySwap = tmp.config.memory * 2;
}
callback(null, tmp);
});
});
}
function configureService(serviceName, data, callback) {
assert.strictEqual(typeof serviceName, 'string');
function configureService(id, data, callback) {
assert.strictEqual(typeof id, 'string');
assert.strictEqual(typeof data, 'object');
assert.strictEqual(typeof callback, 'function');
if (!SERVICES[serviceName]) return callback(new BoxError(BoxError.NOT_FOUND));
const [name, instance ] = id.split(':');
settings.getPlatformConfig(function (error, platformConfig) {
if (instance) {
if (!APP_SERVICES[name]) return callback(new BoxError(BoxError.NOT_FOUND));
} else if (!SERVICES[name]) {
return callback(new BoxError(BoxError.NOT_FOUND));
}
getServicesConfig(id, function (error, service, servicesConfig) {
if (error) return callback(error);
if (!platformConfig[serviceName]) platformConfig[serviceName] = {};
if (!servicesConfig[name]) servicesConfig[name] = {};
// if not specified we clear the entry and use defaults
if (!data.memory || !data.memorySwap) {
delete platformConfig[serviceName];
delete servicesConfig[name];
} else {
platformConfig[serviceName].memory = data.memory;
platformConfig[serviceName].memorySwap = data.memorySwap;
servicesConfig[name].memory = data.memory;
servicesConfig[name].memorySwap = data.memorySwap;
}
settings.setPlatformConfig(platformConfig, function (error) {
if (error) return callback(error);
if (instance) {
appdb.update(instance, { servicesConfig }, function (error) {
if (error) return callback(error);
callback(null);
});
updateAppServiceConfig(name, instance, servicesConfig, callback);
});
} else {
settings.setPlatformConfig(servicesConfig, function (error) {
if (error) return callback(error);
callback(null);
});
}
});
}
function getServiceLogs(serviceName, options, callback) {
assert.strictEqual(typeof serviceName, 'string');
function getServiceLogs(id, options, callback) {
assert.strictEqual(typeof id, 'string');
assert(options && typeof options === 'object');
assert.strictEqual(typeof callback, 'function');
@@ -414,9 +478,15 @@ function getServiceLogs(serviceName, options, callback) {
assert.strictEqual(typeof options.format, 'string');
assert.strictEqual(typeof options.follow, 'boolean');
if (!SERVICES[serviceName]) return callback(new BoxError(BoxError.NOT_FOUND));
const [name, instance ] = id.split(':');
debug(`Getting logs for ${serviceName}`);
if (instance) {
if (!APP_SERVICES[name]) return callback(new BoxError(BoxError.NOT_FOUND));
} else if (!SERVICES[name]) {
return callback(new BoxError(BoxError.NOT_FOUND));
}
debug(`Getting logs for ${name}`);
var lines = options.lines,
format = options.format || 'json',
@@ -425,11 +495,11 @@ function getServiceLogs(serviceName, options, callback) {
let cmd, args = [];
// docker and unbound use journald
if (serviceName === 'docker' || serviceName === 'unbound') {
if (name === 'docker' || name === 'unbound') {
cmd = 'journalctl';
args.push('--lines=' + (lines === -1 ? 'all' : lines));
args.push(`--unit=${serviceName}`);
args.push(`--unit=${name}`);
args.push('--no-pager');
args.push('--output=short-iso');
@@ -439,7 +509,8 @@ function getServiceLogs(serviceName, options, callback) {
args.push('--lines=' + (lines === -1 ? '+1' : lines));
if (follow) args.push('--follow', '--retry', '--quiet'); // same as -F. to make it work if file doesn't exist, --quiet to not output file headers, which are no logs
args.push(path.join(paths.LOG_DIR, serviceName, 'app.log'));
const containerName = APP_SERVICES[name] ? `${name}-${instance}` : name;
args.push(path.join(paths.LOG_DIR, containerName, 'app.log'));
}
var cp = spawn(cmd, args);
@@ -458,7 +529,7 @@ function getServiceLogs(serviceName, options, callback) {
return JSON.stringify({
realtimeTimestamp: timestamp * 1000,
message: message,
source: serviceName
source: name
}) + '\n';
});
@@ -469,13 +540,21 @@ function getServiceLogs(serviceName, options, callback) {
callback(null, transformStream);
}
function restartService(serviceName, callback) {
assert.strictEqual(typeof serviceName, 'string');
function restartService(id, callback) {
assert.strictEqual(typeof id, 'string');
assert.strictEqual(typeof callback, 'function');
if (!SERVICES[serviceName]) return callback(new BoxError(BoxError.NOT_FOUND));
const [name, instance ] = id.split(':');
SERVICES[serviceName].restart(callback);
if (instance) {
if (!APP_SERVICES[name]) return callback(new BoxError(BoxError.NOT_FOUND));
APP_SERVICES[name].restart(instance, callback);
} else if (SERVICES[name]) {
SERVICES[name].restart(callback);
} else {
return callback(new BoxError(BoxError.NOT_FOUND));
}
}
function waitForContainer(containerName, tokenEnvName, callback) {
@@ -486,7 +565,7 @@ function waitForContainer(containerName, tokenEnvName, callback) {
debug(`Waiting for ${containerName}`);
getContainerDetails(containerName, tokenEnvName, function (error, result) {
if (error) return callback(error);
if (error) return callback(error);
async.retry({ times: 10, interval: 15000 }, function (retryCallback) {
request.get(`https://${result.ip}:3000/healthcheck?access_token=${result.token}`, { json: true, rejectUnauthorized: false }, function (error, response) {
@@ -650,6 +729,28 @@ function updateServiceConfig(platformConfig, callback) {
}, callback);
}
function updateAppServiceConfig(name, instance, servicesConfig, callback) {
assert.strictEqual(typeof name, 'string');
assert.strictEqual(typeof instance, 'string');
assert.strictEqual(typeof servicesConfig, 'object');
assert.strictEqual(typeof callback, 'function');
debug(`updateAppServiceConfig: ${name}-${instance} ${JSON.stringify(servicesConfig)}`);
const serviceConfig = servicesConfig[name];
let memory, memorySwap;
if (serviceConfig && serviceConfig.memory && serviceConfig.memorySwap) {
memory = serviceConfig.memory;
memorySwap = serviceConfig.memorySwap;
} else {
memory = APP_SERVICES[name].defaultMemoryLimit;
memorySwap = memory * 2;
}
const args = `update --memory ${memory} --memory-swap ${memorySwap} ${name}-${instance}`.split(' ');
shell.spawn(`updateAppServiceConfig${name}`, '/usr/bin/docker', args, { }, callback);
}
function startServices(existingInfra, callback) {
assert.strictEqual(typeof existingInfra, 'object');
assert.strictEqual(typeof callback, 'function');
@@ -1616,15 +1717,7 @@ function setupRedis(app, options, callback) {
const redisServiceToken = hat(4 * 48);
// Compute redis memory limit based on app's memory limit (this is arbitrary)
var memoryLimit = app.memoryLimit || app.manifest.memoryLimit || 0;
if (memoryLimit === -1) { // unrestricted (debug mode)
memoryLimit = 0;
} else if (memoryLimit === 0 || memoryLimit <= (2 * 1024 * 1024 * 1024)) { // less than 2G (ram+swap)
memoryLimit = 150 * 1024 * 1024; // 150m
} else {
memoryLimit = 600 * 1024 * 1024; // 600m
}
const memoryLimit = app.servicesConfig['redis'] ? app.servicesConfig['redis'].memoryLimit : APP_SERVICES['redis'].defaultMemoryLimit;
const tag = infra.images.redis.tag;
const label = app.fqdn;