diff --git a/migrations/20181011202336-appEnvVars-table.js b/migrations/20181011202336-appEnvVars-table.js new file mode 100644 index 000000000..a9ff81a6e --- /dev/null +++ b/migrations/20181011202336-appEnvVars-table.js @@ -0,0 +1,21 @@ +'use strict'; + +exports.up = function(db, callback) { + var cmd = 'CREATE TABLE IF NOT EXISTS appEnvVars(' + + 'appId VARCHAR(128) NOT NULL,' + + 'name TEXT NOT NULL,' + + 'value TEXT NOT NULL,' + + 'FOREIGN KEY(appId) REFERENCES apps(id)) CHARACTER SET utf8 COLLATE utf8_bin'; + + db.runSql(cmd, function (error) { + if (error) console.error(error); + callback(error); + }); +}; + +exports.down = function(db, callback) { + db.runSql('DROP TABLE appEnvVars', function (error) { + if (error) console.error(error); + callback(error); + }); +}; diff --git a/migrations/schema.sql b/migrations/schema.sql index 13e1d4405..832255961 100644 --- a/migrations/schema.sql +++ b/migrations/schema.sql @@ -118,6 +118,12 @@ CREATE TABLE IF NOT EXISTS appAddonConfigs( value VARCHAR(512) NOT NULL, FOREIGN KEY(appId) REFERENCES apps(id)); +CREATE TABLE IF NOT EXISTS appEnvVars( + appId VARCHAR(128) NOT NULL, + name TEXT NOT NULL, + value TEXT NOT NULL, + FOREIGN KEY(appId) REFERENCES apps(id)); + CREATE TABLE IF NOT EXISTS backups( id VARCHAR(128) NOT NULL, creationTime TIMESTAMP, diff --git a/package-lock.json b/package-lock.json index a47a02e83..bd9339342 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1651,12 +1651,12 @@ }, "semver": { "version": "4.3.6", - "resolved": "http://registry.npmjs.org/semver/-/semver-4.3.6.tgz", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=" }, "validator": { "version": "3.43.0", - "resolved": "http://registry.npmjs.org/validator/-/validator-3.43.0.tgz", + "resolved": "https://registry.npmjs.org/validator/-/validator-3.43.0.tgz", "integrity": "sha1-lkZLmS1BloM9l6GUv0Cxn/VLrgU=" } } @@ -2684,6 +2684,7 @@ "version": "0.8.6", "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.6.tgz", "integrity": "sha1-QooiOv4DQl0s1tY0f99AxmkDVj0=", + "optional": true, "requires": { "nan": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz" } @@ -3218,6 +3219,32 @@ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, + "fs-extra": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.6.4.tgz", + "integrity": "sha1-9G8MdbeEH40gCzNIzU1pHVoJnRU=", + "dev": true, + "requires": { + "jsonfile": "1.0.1", + "mkdirp": "0.3.5", + "ncp": "0.4.2", + "rimraf": "2.2.8" + }, + "dependencies": { + "mkdirp": { + "version": "0.3.5", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=", + "dev": true + }, + "rimraf": { + "version": "2.2.8", + "resolved": "http://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", + "dev": true + } + } + }, "fstream": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", @@ -4548,6 +4575,12 @@ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, + "jsonfile": { + "version": "1.0.1", + "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-1.0.1.tgz", + "integrity": "sha1-6l7+QLg2kLmGZ2FKc5L8YOhCwN0=", + "dev": true + }, "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", @@ -5151,6 +5184,22 @@ } } }, + "mock-aws-s3": { + "version": "git+https://github.com/cloudron-io/mock-aws-s3.git#1306f1722b82897382a2339d52a94ded15003d8c", + "dev": true, + "requires": { + "fs-extra": "0.6.4", + "underscore": "1.8.3" + }, + "dependencies": { + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", + "dev": true + } + } + }, "modelo": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/modelo/-/modelo-4.2.3.tgz", @@ -5383,6 +5432,12 @@ "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=", "optional": true }, + "ncp": { + "version": "0.4.2", + "resolved": "http://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", + "integrity": "sha1-q8xsvT7C7Spyn/bnwfqPAXhKhXQ=", + "dev": true + }, "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", @@ -7382,7 +7437,8 @@ "safe-json-stringify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.1.0.tgz", - "integrity": "sha512-EzBtUaFH9bHYPc69wqjp0efJI/DPNHdFbGE3uIMn4sVbO0zx8vZ8cG4WKxQfOpUOKsQyGBiT2mTqnCw+6nLswA==" + "integrity": "sha512-EzBtUaFH9bHYPc69wqjp0efJI/DPNHdFbGE3uIMn4sVbO0zx8vZ8cG4WKxQfOpUOKsQyGBiT2mTqnCw+6nLswA==", + "optional": true }, "safer-buffer": { "version": "2.1.2", diff --git a/src/appdb.js b/src/appdb.js index d814ea769..271c1c525 100644 --- a/src/appdb.js +++ b/src/appdb.js @@ -106,7 +106,7 @@ function postProcess(result) { delete result.environmentVariables; delete result.portTypes; - for (var i = 0; i < environmentVariables.length; i++) { + for (let i = 0; i < environmentVariables.length; i++) { result.portBindings[environmentVariables[i]] = { hostPort: parseInt(hostPorts[i], 10), type: portTypes[i] }; } @@ -130,6 +130,14 @@ function postProcess(result) { delete d.appId; delete d.type; }); + + let envNames = JSON.parse(result.envNames), envValues = JSON.parse(result.envValues); + delete result.envNames; + delete result.envValues; + result.env = {}; + for (let i = 0; i < envNames.length; i++) { // NOTE: envNames is [ null ] when env of an app is empty + if (envNames[i]) result.env[envNames[i]] = envValues[i]; + } } function get(id, callback) { @@ -137,9 +145,11 @@ function get(id, callback) { assert.strictEqual(typeof callback, 'function'); database.query('SELECT ' + APPS_FIELDS_PREFIXED + ',' - + 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes' + + 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes, ' + + 'JSON_ARRAYAGG(appEnvVars.name) AS envNames, JSON_ARRAYAGG(appEnvVars.value) AS envValues' + ' FROM apps' + ' LEFT OUTER JOIN appPortBindings ON apps.id = appPortBindings.appId' + + ' LEFT OUTER JOIN appEnvVars ON apps.id = appEnvVars.appId' + ' LEFT OUTER JOIN subdomains ON apps.id = subdomains.appId AND subdomains.type = ?' + ' WHERE apps.id = ? GROUP BY apps.id', [ exports.SUBDOMAIN_TYPE_PRIMARY, id ], function (error, result) { if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error)); @@ -153,7 +163,7 @@ function get(id, callback) { postProcess(result[0]); callback(null, result[0]); - }) + }); }); } @@ -162,9 +172,11 @@ function getByHttpPort(httpPort, callback) { assert.strictEqual(typeof callback, 'function'); database.query('SELECT ' + APPS_FIELDS_PREFIXED + ',' - + 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes' + + 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes,' + + 'JSON_ARRAYAGG(appEnvVars.name) AS envNames, JSON_ARRAYAGG(appEnvVars.value) AS envValues' + ' FROM apps' + ' LEFT OUTER JOIN appPortBindings ON apps.id = appPortBindings.appId' + + ' LEFT OUTER JOIN appEnvVars ON apps.id = appEnvVars.appId' + ' LEFT OUTER JOIN subdomains ON apps.id = subdomains.appId AND subdomains.type = ?' + ' WHERE httpPort = ? GROUP BY apps.id', [ exports.SUBDOMAIN_TYPE_PRIMARY, httpPort ], function (error, result) { if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error)); @@ -186,9 +198,11 @@ function getByContainerId(containerId, callback) { assert.strictEqual(typeof callback, 'function'); database.query('SELECT ' + APPS_FIELDS_PREFIXED + ',' - + 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes' + + 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes,' + + 'JSON_ARRAYAGG(appEnvVars.name) AS envNames, JSON_ARRAYAGG(appEnvVars.value) AS envValues' + ' FROM apps' + ' LEFT OUTER JOIN appPortBindings ON apps.id = appPortBindings.appId' + + ' LEFT OUTER JOIN appEnvVars ON apps.id = appEnvVars.appId' + ' LEFT OUTER JOIN subdomains ON apps.id = subdomains.appId AND subdomains.type = ?' + ' WHERE containerId = ? GROUP BY apps.id', [ exports.SUBDOMAIN_TYPE_PRIMARY, containerId ], function (error, result) { if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error)); @@ -209,9 +223,11 @@ function getAll(callback) { assert.strictEqual(typeof callback, 'function'); database.query('SELECT ' + APPS_FIELDS_PREFIXED + ',' - + 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes' + + 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes,' + + 'JSON_ARRAYAGG(appEnvVars.name) AS envNames, JSON_ARRAYAGG(appEnvVars.value) AS envValues' + ' FROM apps' + ' LEFT OUTER JOIN appPortBindings ON apps.id = appPortBindings.appId' + + ' LEFT OUTER JOIN appEnvVars ON apps.id = appEnvVars.appId' + ' LEFT OUTER JOIN subdomains ON apps.id = subdomains.appId AND subdomains.type = ?' + ' GROUP BY apps.id ORDER BY apps.id', [ exports.SUBDOMAIN_TYPE_PRIMARY ], function (error, results) { if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error)); @@ -259,6 +275,7 @@ function add(id, appStoreId, manifest, location, domain, ownerId, portBindings, var sso = 'sso' in data ? data.sso : null; var robotsTxt = 'robotsTxt' in data ? data.robotsTxt : null; var debugModeJson = data.debugMode ? JSON.stringify(data.debugMode) : null; + var env = data.env || {}; var queries = []; @@ -280,6 +297,13 @@ function add(id, appStoreId, manifest, location, domain, ownerId, portBindings, }); }); + Object.keys(env).forEach(function (name) { + queries.push({ + query: 'INSERT INTO appEnvVars (appId, name, value) VALUES (?, ?, ?)', + args: [ id, name, env[name] ] + }); + }); + // only allocate a mailbox if mailboxName is set if (data.mailboxName) { queries.push({ @@ -354,12 +378,13 @@ function del(id, callback) { { query: 'DELETE FROM subdomains WHERE appId = ?', args: [ id ] }, { query: 'DELETE FROM mailboxes WHERE ownerId=?', args: [ id ] }, { query: 'DELETE FROM appPortBindings WHERE appId = ?', args: [ id ] }, + { query: 'DELETE FROM appEnvVars WHERE appId = ?', args: [ id ] }, { query: 'DELETE FROM apps WHERE id = ?', args: [ id ] } ]; database.transaction(queries, function (error, results) { if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error)); - if (results[3].affectedRows !== 1) return callback(new DatabaseError(DatabaseError.NOT_FOUND)); + if (results[4].affectedRows !== 1) return callback(new DatabaseError(DatabaseError.NOT_FOUND)); callback(null); }); @@ -372,6 +397,7 @@ function clear(callback) { database.query.bind(null, 'DELETE FROM subdomains'), database.query.bind(null, 'DELETE FROM appPortBindings'), database.query.bind(null, 'DELETE FROM appAddonConfigs'), + database.query.bind(null, 'DELETE FROM appEnvVars'), database.query.bind(null, 'DELETE FROM apps') ], function (error) { if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error)); @@ -391,6 +417,7 @@ function updateWithConstraints(id, app, constraints, callback) { assert(!('portBindings' in app) || typeof app.portBindings === 'object'); assert(!('accessRestriction' in app) || typeof app.accessRestriction === 'object' || app.accessRestriction === ''); assert(!('alternateDomains' in app) || Array.isArray(app.alternateDomains)); + assert(!('env' in app) || typeof app.env === 'object'); var queries = [ ]; @@ -404,6 +431,17 @@ function updateWithConstraints(id, app, constraints, callback) { }); } + if ('env' in app) { + queries.push({ query: 'DELETE FROM appEnvVars WHERE appId = ?', args: [ id ] }); + + Object.keys(app.env).forEach(function (name) { + queries.push({ + query: 'INSERT INTO appEnvVars (appId, name, value) VALUES (?, ?, ?)', + args: [ id, name, app.env[name] ] + }); + }); + } + if ('location' in app) { queries.push({ query: 'UPDATE subdomains SET subdomain = ? WHERE appId = ? AND type = ?', args: [ app.location, id, exports.SUBDOMAIN_TYPE_PRIMARY ]}); } @@ -424,7 +462,7 @@ function updateWithConstraints(id, app, constraints, callback) { if (p === 'manifest' || p === 'oldConfig' || p === 'updateConfig' || p === 'restoreConfig' || p === 'accessRestriction' || p === 'debugMode') { fields.push(`${p}Json = ?`); values.push(JSON.stringify(app[p])); - } else if (p !== 'portBindings' && p !== 'location' && p !== 'domain' && p !== 'alternateDomains') { + } else if (p !== 'portBindings' && p !== 'location' && p !== 'domain' && p !== 'alternateDomains' && p !== 'env') { fields.push(p + ' = ?'); values.push(app[p]); } @@ -617,7 +655,7 @@ function transferOwnership(oldOwnerId, newOwnerId, callback) { assert.strictEqual(typeof newOwnerId, 'string'); assert.strictEqual(typeof callback, 'function'); - database.query('UPDATE apps SET ownerId=? WHERE ownerId=?', [ newOwnerId, oldOwnerId ], function (error, results) { + database.query('UPDATE apps SET ownerId=? WHERE ownerId=?', [ newOwnerId, oldOwnerId ], function (error) { if (error && error.code === 'ER_NO_REFERENCED_ROW_2') return callback(new DatabaseError(DatabaseError.NOT_FOUND, 'No such user')); if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error)); diff --git a/src/apps.js b/src/apps.js index 835dcbdff..c3845a33e 100644 --- a/src/apps.js +++ b/src/apps.js @@ -325,7 +325,8 @@ function getAppConfig(app) { xFrameOptions: app.xFrameOptions || 'SAMEORIGIN', robotsTxt: app.robotsTxt, sso: app.sso, - alternateDomains: app.alternateDomains || [] + alternateDomains: app.alternateDomains || [], + env: app.env }; } @@ -335,7 +336,7 @@ function removeInternalFields(app) { 'location', 'domain', 'fqdn', 'mailboxName', 'accessRestriction', 'manifest', 'portBindings', 'iconUrl', 'memoryLimit', 'xFrameOptions', 'sso', 'debugMode', 'robotsTxt', 'enableBackup', 'creationTime', 'updateTime', 'ts', - 'alternateDomains', 'ownerId'); + 'alternateDomains', 'ownerId', 'env'); } function removeRestrictedFields(app) { @@ -535,7 +536,8 @@ function install(data, user, auditSource, callback) { backupId = data.backupId || null, backupFormat = data.backupFormat || 'tgz', ownerId = data.ownerId, - alternateDomains = data.alternateDomains || []; + alternateDomains = data.alternateDomains || [], + env = data.env || {}; assert(data.appStoreId || data.manifest); // atleast one of them is required @@ -611,7 +613,8 @@ function install(data, user, auditSource, callback) { restoreConfig: backupId ? { backupId: backupId, backupFormat: backupFormat } : null, enableBackup: enableBackup, robotsTxt: robotsTxt, - alternateDomains: alternateDomains + alternateDomains: alternateDomains, + env: env }; appdb.add(appId, appStoreId, manifest, location, domain, ownerId, translatePortBindings(portBindings, manifest), data, function (error) { @@ -732,6 +735,10 @@ function configure(appId, data, user, auditSource, callback) { values.alternateDomains.forEach(function (ad) { ad.subdomain = addSpacesSuffix(ad.subdomain, user); }); // TODO: validate these } + if ('env' in data) { + values.env = data.env; + } + domains.get(domain, function (error, domainObject) { if (error && error.reason === DomainsError.NOT_FOUND) return callback(new AppsError(AppsError.NOT_FOUND, 'No such domain')); if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, 'Could not get domain info:' + error.message)); @@ -1016,7 +1023,8 @@ function clone(appId, data, user, auditSource, callback) { sso: !!app.sso, mailboxName: (location ? location : manifest.title.toLowerCase().replace(/[^a-zA-Z0-9]/g, '')) + '.app', enableBackup: app.enableBackup, - robotsTxt: app.robotsTxt + robotsTxt: app.robotsTxt, + env: app.env }; appdb.add(newAppId, app.appStoreId, manifest, location, domain, ownerId, translatePortBindings(portBindings, manifest), data, function (error) { diff --git a/src/docker.js b/src/docker.js index a91a8395c..803af511e 100644 --- a/src/docker.js +++ b/src/docker.js @@ -147,6 +147,8 @@ function createSubcontainer(app, name, cmd, options, callback) { dockerPortBindings[`${containerPort}/${portType}`] = [ { HostIp: '0.0.0.0', HostPort: hostPort + '' } ]; } + let appEnv = []; + Object.keys(app.env).forEach(function (name) { appEnv.push(`${name}=${app.env[name]}`); }); // first check db record, then manifest var memoryLimit = app.memoryLimit || manifest.memoryLimit || 0; @@ -177,7 +179,7 @@ function createSubcontainer(app, name, cmd, options, callback) { Tty: isAppContainer, Image: app.manifest.dockerImage, Cmd: (isAppContainer && app.debugMode && app.debugMode.cmd) ? app.debugMode.cmd : cmd, - Env: stdEnv.concat(addonEnv).concat(portEnv), + Env: stdEnv.concat(addonEnv).concat(portEnv).concat(appEnv), ExposedPorts: isAppContainer ? exposedPorts : { }, Volumes: { // see also ReadonlyRootfs '/tmp': {}, diff --git a/src/routes/apps.js b/src/routes/apps.js index f737e3c2f..66add2a8e 100644 --- a/src/routes/apps.js +++ b/src/routes/apps.js @@ -144,6 +144,11 @@ function installApp(req, res, next) { if (data.alternateDomains.some(function (d) { return (typeof d.domain !== 'string' || typeof d.subdomain !== 'string'); })) return next(new HttpError(400, 'alternateDomains array must contain objects with domain and subdomain strings')); } + if ('env' in data) { + if (!data.env || typeof data.env !== 'object') return next(new HttpError(400, 'env must be an object')); + if (Object.keys(data.env).some(function (key) { return typeof data.env[key] !== 'string'; })) return next(new HttpError(400, 'env must contain values as strings')); + } + debug('Installing app :%j', data); apps.install(data, req.user, auditSource(req), function (error, app) { @@ -194,6 +199,11 @@ function configureApp(req, res, next) { if (data.alternateDomains.some(function (d) { return (typeof d.domain !== 'string' || typeof d.subdomain !== 'string'); })) return next(new HttpError(400, 'alternateDomains array must contain objects with domain and subdomain strings')); } + if ('env' in data) { + if (!data.env || typeof data.env !== 'object') return next(new HttpError(400, 'env must be an object')); + if (Object.keys(data.env).some(function (key) { return typeof data.env[key] !== 'string'; })) return next(new HttpError(400, 'env must contain values as strings')); + } + debug('Configuring app id:%s data:%j', req.params.id, data); apps.configure(req.params.id, data, req.user, auditSource(req), function (error) { diff --git a/src/test/apps-test.js b/src/test/apps-test.js index 5c22247fe..22b5bec9b 100644 --- a/src/test/apps-test.js +++ b/src/test/apps-test.js @@ -110,7 +110,10 @@ describe('Apps', function () { memoryLimit: 0, robotsTxt: null, sso: false, - ownerId: USER_0.id + ownerId: USER_0.id, + env: { + 'CUSTOM_KEY': 'CUSTOM_VALUE' + } }; var APP_1 = { @@ -125,7 +128,8 @@ describe('Apps', function () { portBindings: {}, accessRestriction: { users: [ 'someuser' ], groups: [ GROUP_0.id ] }, memoryLimit: 0, - ownerId: USER_0.id + ownerId: USER_0.id, + env: {} }; var APP_2 = { @@ -142,7 +146,8 @@ describe('Apps', function () { memoryLimit: 0, robotsTxt: null, sso: false, - ownerId: USER_0.id + ownerId: USER_0.id, + env: {} }; before(function (done) { diff --git a/src/test/database-test.js b/src/test/database-test.js index 08516616a..015032648 100644 --- a/src/test/database-test.js +++ b/src/test/database-test.js @@ -233,7 +233,8 @@ describe('database', function () { debugMode: null, robotsTxt: null, enableBackup: true, - ownerId: USER_0.id + ownerId: USER_0.id, + env: {} }; it('cannot delete referenced domain', function (done) { @@ -751,7 +752,10 @@ describe('database', function () { robotsTxt: null, enableBackup: true, ownerId: USER_0.id, - alternateDomains: [] + alternateDomains: [], + env: { + 'CUSTOM_KEY': 'CUSTOM_VALUE' + } }; var APP_1 = { @@ -778,7 +782,8 @@ describe('database', function () { robotsTxt: null, enableBackup: true, ownerId: USER_0.id, - alternateDomains: [] + alternateDomains: [], + env: {} }; before(function (done) {