197
src/addons.js
197
src/addons.js
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user