diff --git a/migrations/20170119234504-appdb-add-developmentMode.js b/migrations/20170119234504-appdb-add-developmentMode.js new file mode 100644 index 000000000..a7e7fb570 --- /dev/null +++ b/migrations/20170119234504-appdb-add-developmentMode.js @@ -0,0 +1,15 @@ +dbm = dbm || require('db-migrate'); + +exports.up = function(db, callback) { + db.runSql('ALTER TABLE apps ADD COLUMN developmentMode BOOLEAN DEFAULT 0', function (error) { + if (error) console.error(error); + callback(error); + }); +}; + +exports.down = function(db, callback) { + db.runSql('ALTER TABLE apps DROP COLUMN developmentMode', function (error) { + if (error) console.error(error); + callback(error); + }); +}; diff --git a/src/appdb.js b/src/appdb.js index c61d090a2..bae05f57d 100644 --- a/src/appdb.js +++ b/src/appdb.js @@ -58,7 +58,7 @@ var assert = require('assert'), var APPS_FIELDS_PREFIXED = [ 'apps.id', 'apps.appStoreId', 'apps.installationState', 'apps.installationProgress', 'apps.runState', 'apps.health', 'apps.containerId', 'apps.manifestJson', 'apps.httpPort', 'apps.location', 'apps.dnsRecordId', 'apps.accessRestrictionJson', 'apps.lastBackupId', 'apps.oldConfigJson', 'apps.memoryLimit', 'apps.altDomain', - 'apps.xFrameOptions', 'apps.sso', 'apps.readonlyRootfs' ].join(','); + 'apps.xFrameOptions', 'apps.sso', 'apps.readonlyRootfs', 'apps.developmentMode' ].join(','); var PORT_BINDINGS_FIELDS = [ 'hostPort', 'environmentVariable', 'appId' ].join(','); @@ -97,6 +97,7 @@ function postProcess(result) { result.sso = !!result.sso; // make it bool result.readonlyRootfs = !!result.readonlyRootfs; // make it bool + result.developmentMode = !!result.developmentMode; // make it bool } function get(id, callback) { @@ -185,11 +186,12 @@ function add(id, appStoreId, manifest, location, portBindings, data, callback) { var lastBackupId = data.lastBackupId || null; // used when cloning var sso = 'sso' in data ? data.sso : null; var readonlyRootfs = 'readonlyRootfs' in data ? data.readonlyRootfs : true; + var developmentMode = 'developmentMode' in data ? data.developmentMode : false; var queries = [ ]; queries.push({ - query: 'INSERT INTO apps (id, appStoreId, manifestJson, installationState, location, accessRestrictionJson, memoryLimit, altDomain, xFrameOptions, lastBackupId, sso, readonlyRootfs) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', - args: [ id, appStoreId, manifestJson, installationState, location, accessRestrictionJson, memoryLimit, altDomain, xFrameOptions, lastBackupId, sso, readonlyRootfs ] + query: 'INSERT INTO apps (id, appStoreId, manifestJson, installationState, location, accessRestrictionJson, memoryLimit, altDomain, xFrameOptions, lastBackupId, sso, readonlyRootfs, developmentMode) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', + args: [ id, appStoreId, manifestJson, installationState, location, accessRestrictionJson, memoryLimit, altDomain, xFrameOptions, lastBackupId, sso, readonlyRootfs, developmentMode ] }); Object.keys(portBindings).forEach(function (env) { diff --git a/src/apphealthmonitor.js b/src/apphealthmonitor.js index 47e9b3ffc..9311689e5 100644 --- a/src/apphealthmonitor.js +++ b/src/apphealthmonitor.js @@ -50,7 +50,7 @@ function setHealth(app, health, callback) { debugApp(app, 'marking as unhealthy since not seen for more than %s minutes', UNHEALTHY_THRESHOLD/(60 * 1000)); - if (app.appStoreId !== '') mailer.appDied(app); // do not send mails for dev apps + if (app.developmentMode) mailer.appDied(app); // do not send mails for dev apps gHealthInfo[app.id].emailSent = true; } else { debugApp(app, 'waiting for sometime to update the app health'); @@ -166,7 +166,7 @@ function processDockerEvents() { debug('OOM Context: %s', context); // do not send mails for dev apps - if ((!app || app.appStoreId !== '') && (now - lastOomMailTime > OOM_MAIL_LIMIT)) { + if ((!app || !app.developmentMode) && (now - lastOomMailTime > OOM_MAIL_LIMIT)) { mailer.oomEvent(program, context); // app can be null if it's an addon crash lastOomMailTime = now; } diff --git a/src/apps.js b/src/apps.js index 7f2df4275..00ee640f0 100644 --- a/src/apps.js +++ b/src/apps.js @@ -480,7 +480,8 @@ function install(data, auditSource, callback) { altDomain = data.altDomain || null, xFrameOptions = data.xFrameOptions || 'SAMEORIGIN', sso = 'sso' in data ? data.sso : null, - readonlyRootfs = 'readonlyRootfs' in data ? data.readonlyRootfs : true; + readonlyRootfs = 'readonlyRootfs' in data ? data.readonlyRootfs : true, + developmentMode = 'developmentMode' in data ? data.developmentMode : true; assert(data.appStoreId || data.manifest); // atleast one of them is required @@ -538,7 +539,8 @@ function install(data, auditSource, callback) { altDomain: altDomain, xFrameOptions: xFrameOptions, sso: sso, - readonlyRootfs: readonlyRootfs + readonlyRootfs: readonlyRootfs, + developmentMode: developmentMode }; var from = (location ? location : manifest.title.toLowerCase().replace(/[^a-zA-Z0-9]/g, '')) + '.app'; @@ -623,6 +625,12 @@ function configure(appId, data, auditSource, callback) { values.readonlyRootfs = app.readonlyRootfs; } + if ('developmentMode' in data) { + values.developmentMode = data.developmentMode; + } else { + values.developmentMode = app.developmentMode; + } + // save cert to data/box/certs. TODO: move this to apptask when we have a real task queue if ('cert' in data && 'key' in data) { if (data.cert && data.key) { @@ -710,7 +718,7 @@ function update(appId, data, auditSource, callback) { // this allows cloudron install -f --app for an app installed from the appStore if (app.manifest.id !== values.manifest.id) { if (!data.force) return callback(new AppsError(AppsError.BAD_FIELD, 'manifest id does not match. force to override')); - // clear appStoreId so that this app does not get updates anymore. this will mark it as a dev app + // clear appStoreId so that this app does not get updates anymore values.appStoreId = ''; } diff --git a/src/docker.js b/src/docker.js index 14ed6d3e6..d75b42ff0 100644 --- a/src/docker.js +++ b/src/docker.js @@ -127,7 +127,6 @@ function createSubcontainer(app, name, cmd, options, callback) { isAppContainer = !cmd; // non app-containers are like scheduler containers var manifest = app.manifest; - var developmentMode = !!manifest.developmentMode; var exposedPorts = {}, dockerPortBindings = { }; var domain = app.altDomain || config.appFqdn(app.location); var stdEnv = [ @@ -178,7 +177,7 @@ function createSubcontainer(app, name, cmd, options, callback) { name: name, // used for filtering logs Tty: isAppContainer, Image: app.manifest.dockerImage, - Cmd: (isAppContainer && developmentMode) ? [ '/bin/bash', '-c', 'echo "Development mode. Use cloudron exec to debug. Sleeping" && sleep infinity' ] : cmd, + Cmd: (isAppContainer && app.developmentMode) ? [ '/bin/bash', '-c', 'echo "Development mode. Use cloudron exec to debug. Sleeping" && sleep infinity' ] : cmd, Env: stdEnv.concat(addonEnv).concat(portEnv), ExposedPorts: isAppContainer ? exposedPorts : { }, Volumes: { // see also ReadonlyRootfs diff --git a/src/routes/apps.js b/src/routes/apps.js index 68bdf1479..f4edc75a6 100644 --- a/src/routes/apps.js +++ b/src/routes/apps.js @@ -127,6 +127,7 @@ function installApp(req, res, next) { if ('sso' in data && typeof data.sso !== 'boolean') return next(new HttpError(400, 'sso must be a boolean')); if ('readonlyRootfs' in data && typeof data.readonlyRootfs !== 'boolean') return next(new HttpError(400, 'readonlyRootfs is not a boolean')); + if ('developmentMode' in data && typeof data.developmentMode !== 'boolean') return next(new HttpError(400, 'developmentMode is not a boolean')); debug('Installing app :%j', data); @@ -165,6 +166,7 @@ function configureApp(req, res, next) { if (data.xFrameOptions && typeof data.xFrameOptions !== 'string') return next(new HttpError(400, 'xFrameOptions must be a string')); if ('readonlyRootfs' in data && typeof data.readonlyRootfs !== 'boolean') return next(new HttpError(400, 'readonlyRootfs is not a boolean')); + if ('developmentMode' in data && typeof data.developmentMode !== 'boolean') return next(new HttpError(400, 'developmentMode is not a boolean')); debug('Configuring app id:%s data:%j', req.params.id, data); diff --git a/src/test/database-test.js b/src/test/database-test.js index 5d7aab1cb..18d655d6e 100644 --- a/src/test/database-test.js +++ b/src/test/database-test.js @@ -542,7 +542,8 @@ describe('database', function () { altDomain: null, xFrameOptions: 'DENY', sso: true, - readonlyRootfs: true + readonlyRootfs: true, + developmentMode: false }; var APP_1 = { id: 'appid-1', @@ -564,7 +565,8 @@ describe('database', function () { altDomain: null, xFrameOptions: 'SAMEORIGIN', sso: true, - readonlyRootfs: true + readonlyRootfs: true, + developmentMode: false }; it('add fails due to missing arguments', function () {