From 75d22d79880328e66347222ba361f78a8a378857 Mon Sep 17 00:00:00 2001 From: Johannes Zellner Date: Thu, 11 Feb 2016 17:00:21 +0100 Subject: [PATCH] Introduce memoryLimit to apps routes --- src/apps.js | 12 ++++++++---- src/docker.js | 3 ++- src/routes/apps.js | 13 ++++++++----- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/apps.js b/src/apps.js index 5b769dd06..4ae75a18c 100644 --- a/src/apps.js +++ b/src/apps.js @@ -328,7 +328,7 @@ function purchase(appStoreId, callback) { }); } -function install(appId, appStoreId, manifest, location, portBindings, accessRestriction, oauthProxy, icon, cert, key, callback) { +function install(appId, appStoreId, manifest, location, portBindings, accessRestriction, oauthProxy, icon, cert, key, memoryLimit, callback) { assert.strictEqual(typeof appId, 'string'); assert.strictEqual(typeof appStoreId, 'string'); assert(manifest && typeof manifest === 'object'); @@ -339,6 +339,7 @@ function install(appId, appStoreId, manifest, location, portBindings, accessRest assert(!icon || typeof icon === 'string'); assert(cert === null || typeof cert === 'string'); assert(key === null || typeof key === 'string'); + assert.strictEqual(typeof memoryLimit, 'number'); assert.strictEqual(typeof callback, 'function'); var error = manifestFormat.parse(manifest); @@ -356,7 +357,7 @@ function install(appId, appStoreId, manifest, location, portBindings, accessRest error = validateAccessRestriction(accessRestriction); if (error) return callback(new AppsError(AppsError.BAD_FIELD, error.message)); - var memoryLimit = (!manifest.memoryLimit || manifest.memoryLimit <= 268435456) ? 268435456 : manifest.memoryLimit; // 256mb + // TODO to what extent do we need to validate the memoryLimit? // singleUser mode requires accessRestriction to contain exactly one user if (manifest.singleUser && accessRestriction === null) return callback(new AppsError(AppsError.USER_REQUIRED)); @@ -395,7 +396,7 @@ function install(appId, appStoreId, manifest, location, portBindings, accessRest }); } -function configure(appId, location, portBindings, accessRestriction, oauthProxy, cert, key, callback) { +function configure(appId, location, portBindings, accessRestriction, oauthProxy, cert, key, memoryLimit, callback) { assert.strictEqual(typeof appId, 'string'); assert.strictEqual(typeof location, 'string'); assert.strictEqual(typeof portBindings, 'object'); @@ -403,6 +404,7 @@ function configure(appId, location, portBindings, accessRestriction, oauthProxy, assert.strictEqual(typeof oauthProxy, 'boolean'); assert(cert === null || typeof cert === 'string'); assert(key === null || typeof key === 'string'); + assert.strictEqual(typeof memoryLimit, 'number'); assert.strictEqual(typeof callback, 'function'); var error = validateHostname(location, config.fqdn()); @@ -432,12 +434,14 @@ function configure(appId, location, portBindings, accessRestriction, oauthProxy, accessRestriction: accessRestriction, oauthProxy: oauthProxy, portBindings: portBindings, + memoryLimit: memoryLimit, oldConfig: { location: app.location, accessRestriction: app.accessRestriction, portBindings: app.portBindings, - oauthProxy: app.oauthProxy + oauthProxy: app.oauthProxy, + memoryLimit: app.memoryLimit } }; diff --git a/src/docker.js b/src/docker.js index ecd6d52ad..e83902dab 100644 --- a/src/docker.js +++ b/src/docker.js @@ -156,7 +156,8 @@ function createSubcontainer(app, name, cmd, options, callback) { dockerPortBindings[containerPort + '/tcp'] = [ { HostIp: '0.0.0.0', HostPort: hostPort + '' } ]; } - var memoryLimit = app.memoryLimit || (developmentMode ? 0 : 1024 * 1024 * 200); // 200mb by default + var memoryLimit = developmentMode ? 0 : (app.memoryLimit || manifest.memoryLimit || (1024 * 1024 * 256)); // 256mb by default + // for subcontainers, this should ideally be false. but docker does not allow network sharing if the app container is not running // this means cloudron exec does not work var isolatedNetworkNs = true; diff --git a/src/routes/apps.js b/src/routes/apps.js index 1e5a927a6..87c8545fb 100644 --- a/src/routes/apps.js +++ b/src/routes/apps.js @@ -49,7 +49,8 @@ function removeInternalAppFields(app) { manifest: app.manifest, portBindings: app.portBindings, iconUrl: app.iconUrl, - fqdn: app.fqdn + fqdn: app.fqdn, + memoryLimit: app.memoryLimit }; } @@ -122,13 +123,14 @@ function installApp(req, res, next) { if (data.key && typeof data.key !== 'string') return next(new HttpError(400, 'key must be a string')); if (data.cert && !data.key) return next(new HttpError(400, 'key must be provided')); if (!data.cert && data.key) return next(new HttpError(400, 'cert must be provided')); + if ('memoryLimit' in data && typeof data.memoryLimit !== 'number') return next(new HttpError(400, 'memoryLimit is not a number')); // allow tests to provide an appId for testing var appId = (process.env.BOX_ENV === 'test' && typeof data.appId === 'string') ? data.appId : uuid.v4(); - debug('Installing app id:%s storeid:%s loc:%s port:%j accessRestriction:%j oauthproxy:%s manifest:%j', appId, data.appStoreId, data.location, data.portBindings, data.accessRestriction, data.oauthProxy, data.manifest); + debug('Installing app id:%s storeid:%s loc:%s port:%j accessRestriction:%j oauthproxy:%s memoryLimit:%s manifest:%j', appId, data.appStoreId, data.location, data.portBindings, data.accessRestriction, data.oauthProxy, data.memoryLimit, data.manifest); - apps.install(appId, data.appStoreId, data.manifest, data.location, data.portBindings || null, data.accessRestriction, data.oauthProxy, data.icon || null, data.cert || null, data.key || null, function (error) { + apps.install(appId, data.appStoreId, data.manifest, data.location, data.portBindings || null, data.accessRestriction, data.oauthProxy, data.icon || null, data.cert || null, data.key || null, data.memoryLimit || 0, function (error) { if (error && error.reason === AppsError.ALREADY_EXISTS) return next(new HttpError(409, error.message)); if (error && error.reason === AppsError.PORT_RESERVED) return next(new HttpError(409, 'Port ' + error.message + ' is reserved.')); if (error && error.reason === AppsError.PORT_CONFLICT) return next(new HttpError(409, 'Port ' + error.message + ' is already in use.')); @@ -165,10 +167,11 @@ function configureApp(req, res, next) { if (data.key && typeof data.key !== 'string') return next(new HttpError(400, 'key must be a string')); if (data.cert && !data.key) return next(new HttpError(400, 'key must be provided')); if (!data.cert && data.key) return next(new HttpError(400, 'cert must be provided')); + if ('memoryLimit' in data && typeof data.memoryLimit !== 'number') return next(new HttpError(400, 'memoryLimit is not a number')); - debug('Configuring app id:%s location:%s bindings:%j accessRestriction:%j oauthProxy:%s', req.params.id, data.location, data.portBindings, data.accessRestriction, data.oauthProxy); + debug('Configuring app id:%s location:%s bindings:%j accessRestriction:%j memoryLimit:%s oauthProxy:%s', req.params.id, data.location, data.portBindings, data.accessRestriction, data.oauthProxy, data.memoryLimit); - apps.configure(req.params.id, data.location, data.portBindings || null, data.accessRestriction, data.oauthProxy, data.cert || null, data.key || null, function (error) { + apps.configure(req.params.id, data.location, data.portBindings || null, data.accessRestriction, data.oauthProxy, data.cert || null, data.key || null, data.memoryLimit || 0, function (error) { if (error && error.reason === AppsError.ALREADY_EXISTS) return next(new HttpError(409, error.message)); if (error && error.reason === AppsError.PORT_RESERVED) return next(new HttpError(409, 'Port ' + error.message + ' is reserved.')); if (error && error.reason === AppsError.PORT_CONFLICT) return next(new HttpError(409, 'Port ' + error.message + ' is already in use.'));