Add migrate route

This commit is contained in:
Girish Ramakrishnan
2016-06-27 22:24:30 -05:00
parent eaa2058b10
commit dac9f29900
4 changed files with 237 additions and 1 deletions
+15
View File
@@ -5,6 +5,7 @@ exports = module.exports = {
setupTokenAuth: setupTokenAuth,
getStatus: getStatus,
reboot: reboot,
migrate: migrate,
getProgress: getProgress,
getConfig: getConfig,
update: update,
@@ -113,6 +114,20 @@ function reboot(req, res, next) {
cloudron.reboot();
}
function migrate(req, res, next) {
if (typeof req.body.size !== 'string') return next(new HttpError(400, 'size must be string'));
if (typeof req.body.region !== 'string') return next(new HttpError(400, 'region must be string'));
debug('Migration requested', req.body.size, req.body.region);
cloudron.migrate(req.body.size, req.body.region, function (error) {
if (error && error.reason === CloudronError.BAD_STATE) return next(new HttpError(409, error.message));
if (error) return next(new HttpError(500, error));
next(new HttpSuccess(202, {}));
});
}
function getConfig(req, res, next) {
cloudron.getConfig(function (error, cloudronConfig) {
if (error) return next(new HttpError(500, error));
+178 -1
View File
@@ -1,6 +1,5 @@
'use strict';
/* jslint node:true */
/* global it:false */
/* global describe:false */
/* global before:false */
@@ -10,6 +9,7 @@ var async = require('async'),
config = require('../../config.js'),
database = require('../../database.js'),
expect = require('expect.js'),
locker = require('../../locker.js'),
nock = require('nock'),
os = require('os'),
superagent = require('superagent'),
@@ -26,6 +26,7 @@ function setup(done) {
nock.cleanAll();
config._reset();
config.set('version', '0.5.0');
config.set('fqdn', 'localhost');
server.start(done);
}
@@ -262,6 +263,182 @@ describe('Cloudron', function () {
});
describe('migrate', function () {
before(function (done) {
async.series([
setup,
function (callback) {
var scope1 = nock(config.apiServerOrigin()).get('/api/v1/boxes/' + config.fqdn() + '/setup/verify?setupToken=somesetuptoken').reply(200, {});
var scope2 = nock(config.apiServerOrigin()).post('/api/v1/boxes/' + config.fqdn() + '/setup/done?setupToken=somesetuptoken').reply(201, {});
superagent.post(SERVER_URL + '/api/v1/cloudron/activate')
.query({ setupToken: 'somesetuptoken' })
.send({ username: USERNAME, password: PASSWORD, email: EMAIL })
.end(function (error, result) {
expect(result).to.be.ok();
expect(scope1.isDone()).to.be.ok();
expect(scope2.isDone()).to.be.ok();
// stash token for further use
token = result.body.token;
callback();
});
},
function setupBackupConfig(callback) {
superagent.post(SERVER_URL + '/api/v1/settings/backup_config')
.send({ provider: 'caas', token: 'BACKUP_TOKEN', bucket: 'Bucket', prefix: 'Prefix' })
.query({ access_token: token })
.end(function (error, result) {
expect(result.statusCode).to.equal(200);
callback();
});
}
], done);
});
after(function (done) {
locker.unlock(locker._operation); // migrate never unlocks
cleanup(done);
});
it('fails without token', function (done) {
superagent.post(SERVER_URL + '/api/v1/cloudron/migrate')
.send({ size: 'small', region: 'sfo'})
.end(function (error, result) {
expect(result.statusCode).to.equal(401);
done();
});
});
it('fails without password', function (done) {
superagent.post(SERVER_URL + '/api/v1/cloudron/migrate')
.send({ size: 'small', region: 'sfo'})
.query({ access_token: token })
.end(function (error, result) {
expect(result.statusCode).to.equal(400);
done();
});
});
it('fails with missing size', function (done) {
superagent.post(SERVER_URL + '/api/v1/cloudron/migrate')
.send({ region: 'sfo', password: PASSWORD })
.query({ access_token: token })
.end(function (error, result) {
expect(result.statusCode).to.equal(400);
done();
});
});
it('fails with wrong size type', function (done) {
superagent.post(SERVER_URL + '/api/v1/cloudron/migrate')
.send({ size: 4, region: 'sfo', password: PASSWORD })
.query({ access_token: token })
.end(function (error, result) {
expect(result.statusCode).to.equal(400);
done();
});
});
it('fails with missing region', function (done) {
superagent.post(SERVER_URL + '/api/v1/cloudron/migrate')
.send({ size: 'small', password: PASSWORD })
.query({ access_token: token })
.end(function (error, result) {
expect(result.statusCode).to.equal(400);
done();
});
});
it('fails with wrong region type', function (done) {
superagent.post(SERVER_URL + '/api/v1/cloudron/migrate')
.send({ size: 'small', region: 4, password: PASSWORD })
.query({ access_token: token })
.end(function (error, result) {
expect(result.statusCode).to.equal(400);
done();
});
});
it('fails when in wrong state', function (done) {
var scope2 = nock(config.apiServerOrigin())
.post('/api/v1/boxes/' + config.fqdn() + '/awscredentials?token=BACKUP_TOKEN')
.reply(201, { credentials: { AccessKeyId: 'accessKeyId', SecretAccessKey: 'secretAccessKey', SessionToken: 'sessionToken' } });
var scope3 = nock(config.apiServerOrigin())
.post('/api/v1/boxes/' + config.fqdn() + '/backupDone?token=APPSTORE_TOKEN', function (body) {
return body.boxVersion && body.restoreKey && !body.appId && !body.appVersion && body.appBackupIds.length === 0;
})
.reply(200, { id: 'someid' });
var scope1 = nock(config.apiServerOrigin())
.post('/api/v1/boxes/' + config.fqdn() + '/migrate?token=APPSTORE_TOKEN', function (body) {
return body.size && body.region && body.restoreKey;
}).reply(409, {});
injectShellMock();
superagent.post(SERVER_URL + '/api/v1/cloudron/migrate')
.send({ size: 'small', region: 'sfo', password: PASSWORD })
.query({ access_token: token })
.end(function (error, result) {
expect(result.statusCode).to.equal(202);
function checkAppstoreServerCalled() {
if (scope1.isDone() && scope2.isDone() && scope3.isDone()) {
restoreShellMock();
return done();
}
setTimeout(checkAppstoreServerCalled, 100);
}
checkAppstoreServerCalled();
});
});
it('succeeds', function (done) {
var scope1 = nock(config.apiServerOrigin()).post('/api/v1/boxes/' + config.fqdn() + '/migrate?token=APPSTORE_TOKEN', function (body) {
return body.size && body.region && body.restoreKey;
}).reply(202, {});
var scope2 = nock(config.apiServerOrigin())
.post('/api/v1/boxes/' + config.fqdn() + '/backupDone?token=APPSTORE_TOKEN', function (body) {
return body.boxVersion && body.restoreKey && !body.appId && !body.appVersion && body.appBackupIds.length === 0;
})
.reply(200, { id: 'someid' });
var scope3 = nock(config.apiServerOrigin())
.post('/api/v1/boxes/' + config.fqdn() + '/awscredentials?token=BACKUP_TOKEN')
.reply(201, { credentials: { AccessKeyId: 'accessKeyId', SecretAccessKey: 'secretAccessKey', SessionToken: 'sessionToken' } });
injectShellMock();
superagent.post(SERVER_URL + '/api/v1/cloudron/migrate')
.send({ size: 'small', region: 'sfo', password: PASSWORD })
.query({ access_token: token })
.end(function (error, result) {
expect(result.statusCode).to.equal(202);
function checkAppstoreServerCalled() {
if (scope1.isDone() && scope2.isDone() && scope3.isDone()) {
restoreShellMock();
return done();
}
setTimeout(checkAppstoreServerCalled, 100);
}
checkAppstoreServerCalled();
});
});
});
describe('feedback', function () {
before(function (done) {
async.series([