diff --git a/src/apps.js b/src/apps.js index 96b64ce60..2733df6ec 100644 --- a/src/apps.js +++ b/src/apps.js @@ -343,7 +343,7 @@ function validateEnv(env) { } function validateDataDir(dataDir) { - if (dataDir === '') return null; // revert back to default dataDir + if (dataDir === null) return null; if (path.resolve(dataDir) !== dataDir) return new AppsError(AppsError.BAD_FIELD, 'dataDir must be an absolute path', { field: 'dataDir' }); @@ -1155,7 +1155,7 @@ function setLocation(appId, data, auditSource, callback) { function setDataDir(appId, dataDir, auditSource, callback) { assert.strictEqual(typeof appId, 'string'); - assert.strictEqual(typeof dataDir, 'string'); + assert(dataDir === null || typeof dataDir === 'string'); assert.strictEqual(typeof auditSource, 'object'); assert.strictEqual(typeof callback, 'function'); diff --git a/src/apptask.js b/src/apptask.js index 939faae48..2f98b025c 100644 --- a/src/apptask.js +++ b/src/apptask.js @@ -708,6 +708,10 @@ function migrateDataDir(app, oldConfig, progressCallback, callback) { progressCallback.bind(null, { percent: 45, message: 'Ensuring app data directory' }), createAppDir.bind(null, app), + // re-setup addons since this creates the localStorage volume + progressCallback.bind(null, { percent: 50, message: 'Setting up addons' }), + addons.setupAddons.bind(null, app, app.manifest.addons), + // migrate dataDir function (next) { if (!dataDirChanged) return next(); @@ -718,7 +722,7 @@ function migrateDataDir(app, oldConfig, progressCallback, callback) { progressCallback.bind(null, { percent: 60, message: 'Creating container' }), createContainer.bind(null, app), - progressCallback.bind(null, { percent: 60, message: 'Starting app' }), + progressCallback.bind(null, { percent: 80, message: 'Starting app' }), runApp.bind(null, app, progressCallback), progressCallback.bind(null, { percent: 100, message: 'Done' }), diff --git a/src/routes/apps.js b/src/routes/apps.js index 6272b11bf..02bd6005b 100644 --- a/src/routes/apps.js +++ b/src/routes/apps.js @@ -349,7 +349,7 @@ function setDataDir(req, res, next) { assert.strictEqual(typeof req.body, 'object'); assert.strictEqual(typeof req.params.id, 'string'); - if (typeof req.body.dataDir !== 'string') return next(new HttpError(400, 'dataDir must be a string')); + if (req.body.dataDir !== null && typeof req.body.dataDir !== 'string') return next(new HttpError(400, 'dataDir must be a string')); apps.setDataDir(req.params.id, req.body.dataDir, auditSource.fromRequest(req), function (error, result) { if (error) return next(toHttpError(error)); diff --git a/src/routes/test/apps-test.js b/src/routes/test/apps-test.js index 5720ab27f..860f74a88 100644 --- a/src/routes/test/apps-test.js +++ b/src/routes/test/apps-test.js @@ -9,6 +9,7 @@ let apps = require('../../apps.js'), child_process = require('child_process'), clients = require('../../clients.js'), constants = require('../../constants.js'), + crypto = require('crypto'), database = require('../../database.js'), docker = require('../../docker.js').connection, expect = require('expect.js'), @@ -1354,6 +1355,80 @@ describe('App API', function () { }); }); + describe('configure data dir', function () { + it('fails with missing datadir', function (done) { + superagent.post(SERVER_URL + '/api/v1/apps/' + APP_ID + '/configure/data_dir') + .query({ access_token: token }) + .end(function (err, res) { + expect(res.statusCode).to.equal(400); + done(); + }); + }); + + it('fails with bad data dir', function (done) { + superagent.post(SERVER_URL + '/api/v1/apps/' + APP_ID + '/configure/data_dir') + .query({ access_token: token }) + .send({ dataDir: 'what' }) + .end(function (err, res) { + expect(res.statusCode).to.equal(400); + done(); + }); + }); + + it('can set data dir', function (done) { + let dataDir = path.join(paths.baseDir(), 'apps-test-datadir-' + crypto.randomBytes(4).readUInt32LE(0)); + fs.mkdirSync(dataDir); + + superagent.post(SERVER_URL + '/api/v1/apps/' + APP_ID + '/configure/data_dir') + .query({ access_token: token }) + .send({ dataDir: dataDir }) + .end(function (err, res) { + expect(res.statusCode).to.equal(202); + taskId = res.body.taskId; + done(); + }); + }); + + it('wait for task', function (done) { + waitForTask(taskId, done); + }); + + it('app can check addons', function (done) { + console.log('This test can take a while as it waits for scheduler addon to tick 4'); + + apps.get(APP_ID, function (error, app) { + if (error) return done(error); + + checkAddons(app, done); + }); + }); + + it('can reset data dir', function (done) { + superagent.post(SERVER_URL + '/api/v1/apps/' + APP_ID + '/configure/data_dir') + .query({ access_token: token }) + .send({ dataDir: null }) + .end(function (err, res) { + expect(res.statusCode).to.equal(202); + taskId = res.body.taskId; + done(); + }); + }); + + it('wait for task', function (done) { + waitForTask(taskId, done); + }); + + it('app can check addons', function (done) { + console.log('This test can take a while as it waits for scheduler addon to tick 4'); + + apps.get(APP_ID, function (error, app) { + if (error) return done(error); + + checkAddons(app, done); + }); + }); + }); + describe('start/stop', function () { it('non admin cannot stop app', function (done) { superagent.post(SERVER_URL + '/api/v1/apps/' + APP_ID + '/stop') diff --git a/src/scripts/mvvolume.sh b/src/scripts/mvvolume.sh index a484303da..2081bfc9a 100755 --- a/src/scripts/mvvolume.sh +++ b/src/scripts/mvvolume.sh @@ -22,8 +22,8 @@ target_dir="$2" if [[ "${BOX_ENV}" == "test" ]]; then # be careful not to nuke some random directory when testing - [[ "${source_dir}" != *"./cloudron_test/"* ]] && exit 1 - [[ "${target_dir}" != *"./cloudron_test/"* ]] && exit 1 + [[ "${source_dir}" != *"/.cloudron_test/"* ]] && exit 1 + [[ "${target_dir}" != *"/.cloudron_test/"* ]] && exit 1 fi # copy and remove - this way if the copy fails, the original is intact