2017-04-17 14:46:39 +02:00
|
|
|
/* global it:false */
|
|
|
|
|
/* global describe:false */
|
|
|
|
|
/* global before:false */
|
|
|
|
|
/* global after:false */
|
|
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
var async = require('async'),
|
|
|
|
|
fs = require('fs'),
|
|
|
|
|
os = require('os'),
|
|
|
|
|
path = require('path'),
|
|
|
|
|
readdirp = require('readdirp'),
|
|
|
|
|
rimraf = require('rimraf'),
|
|
|
|
|
BackupsError = require('../backups.js').BackupsError,
|
|
|
|
|
config = require('../config.js'),
|
|
|
|
|
database = require('../database.js'),
|
|
|
|
|
caas = require('../storage/caas.js'),
|
|
|
|
|
s3 = require('../storage/s3.js'),
|
|
|
|
|
filesystem = require('../storage/filesystem.js'),
|
|
|
|
|
expect = require('expect.js'),
|
2017-04-18 17:34:42 +02:00
|
|
|
settings = require('../settings.js'),
|
|
|
|
|
stream = require('stream');
|
2017-04-17 14:46:39 +02:00
|
|
|
|
|
|
|
|
function setup(done) {
|
|
|
|
|
config.set('provider', 'caas');
|
|
|
|
|
|
|
|
|
|
async.series([
|
|
|
|
|
database.initialize,
|
|
|
|
|
settings.initialize,
|
|
|
|
|
function (callback) {
|
|
|
|
|
// a cloudron must have a backup config to startup
|
|
|
|
|
settings.setBackupConfig({ provider: 'caas', token: 'foo', key: 'key'}, function (error) {
|
|
|
|
|
expect(error).to.be(null);
|
|
|
|
|
callback();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
], done);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function cleanup(done) {
|
|
|
|
|
async.series([
|
|
|
|
|
settings.uninitialize,
|
|
|
|
|
database._clear
|
|
|
|
|
], done);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function compareDirectories(one, two, callback) {
|
|
|
|
|
readdirp({ root: one }, function (error, treeOne) {
|
|
|
|
|
if (error) return callback(error);
|
|
|
|
|
|
|
|
|
|
readdirp({ root: two }, function (error, treeTwo) {
|
|
|
|
|
if (error) return callback(error);
|
|
|
|
|
|
|
|
|
|
var mismatch = [];
|
|
|
|
|
|
|
|
|
|
function compareDirs(a, b) {
|
|
|
|
|
a.forEach(function (tmpA) {
|
|
|
|
|
var found = b.find(function (tmpB) {
|
|
|
|
|
return tmpA.path === tmpB.path;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!found) mismatch.push(tmpA);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function compareFiles(a, b) {
|
|
|
|
|
a.forEach(function (tmpA) {
|
|
|
|
|
var found = b.find(function (tmpB) {
|
|
|
|
|
// TODO check file or symbolic link
|
|
|
|
|
return tmpA.path === tmpB.path && tmpA.mode === tmpB.mode;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!found) mismatch.push(tmpA);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
compareDirs(treeOne.directories, treeTwo.directories);
|
|
|
|
|
compareDirs(treeTwo.directories, treeOne.directories);
|
|
|
|
|
compareFiles(treeOne.files, treeTwo.files);
|
|
|
|
|
compareFiles(treeTwo.files, treeOne.files);
|
|
|
|
|
|
|
|
|
|
if (mismatch.length) {
|
|
|
|
|
console.error('Files not found in both: %j', mismatch);
|
|
|
|
|
return callback(new Error('file mismatch'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
callback(null);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe('Storage', function () {
|
|
|
|
|
describe('filesystem', function () {
|
|
|
|
|
var gBackupId_1 = 'someprefix/one';
|
|
|
|
|
var gBackupId_2 = 'someprefix/two';
|
|
|
|
|
var gTmpFolder;
|
|
|
|
|
var gSourceFolder;
|
|
|
|
|
var gDestinationFolder;
|
|
|
|
|
var gBackupConfig = {
|
|
|
|
|
provider: 'filesystem',
|
|
|
|
|
key: 'key',
|
|
|
|
|
backupFolder: null
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
before(function (done) {
|
|
|
|
|
setup(function (error) {
|
|
|
|
|
expect(error).to.be(null);
|
|
|
|
|
|
|
|
|
|
gTmpFolder = fs.mkdtempSync(path.join(os.tmpdir(), 'filesystem-backup-test_'));
|
|
|
|
|
|
|
|
|
|
gBackupConfig.backupFolder = path.join(gTmpFolder, 'backups/');
|
|
|
|
|
gSourceFolder = path.join(__dirname, 'storage');
|
|
|
|
|
gDestinationFolder = path.join(gTmpFolder, 'destination/');
|
|
|
|
|
|
|
|
|
|
settings.setBackupConfig(gBackupConfig, function (error) {
|
|
|
|
|
expect(error).to.be(null);
|
|
|
|
|
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
2017-04-18 17:34:42 +02:00
|
|
|
|
2017-04-17 14:46:39 +02:00
|
|
|
after(function (done) {
|
|
|
|
|
cleanup(function (error) {
|
|
|
|
|
expect(error).to.be(null);
|
|
|
|
|
rimraf(gTmpFolder, done);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can backup', function (done) {
|
|
|
|
|
var backupMapping = [{
|
|
|
|
|
source: path.join(gSourceFolder, 'data'),
|
|
|
|
|
destination: '/datadir'
|
|
|
|
|
}, {
|
|
|
|
|
source: path.join(gSourceFolder, 'addon'),
|
|
|
|
|
destination: '/addondir/'
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
filesystem.backup(gBackupConfig, gBackupId_1, backupMapping, function (error) {
|
|
|
|
|
expect(error).to.be(null);
|
|
|
|
|
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can restore', function (done) {
|
|
|
|
|
var restoreMapping = [{
|
|
|
|
|
source: '/datadir',
|
|
|
|
|
destination: path.join(gDestinationFolder, 'data')
|
|
|
|
|
}, {
|
|
|
|
|
source: '/addondir',
|
|
|
|
|
destination: path.join(gDestinationFolder, 'addon')
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
filesystem.restore(gBackupConfig, gBackupId_1, restoreMapping, function (error) {
|
|
|
|
|
expect(error).to.be(null);
|
|
|
|
|
|
|
|
|
|
compareDirectories(gSourceFolder, gDestinationFolder, function (error) {
|
|
|
|
|
expect(error).to.equal(null);
|
|
|
|
|
rimraf(gDestinationFolder, done);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can copy backup', function (done) {
|
|
|
|
|
// will be verified after removing the first and restoring from the copy
|
|
|
|
|
filesystem.copyBackup(gBackupConfig, gBackupId_1, gBackupId_2, done);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can remove backup', function (done) {
|
|
|
|
|
// will be verified with next test trying to restore the removed one
|
|
|
|
|
filesystem.removeBackup(gBackupConfig, gBackupId_1, [], done);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('cannot restore deleted backup', function (done) {
|
|
|
|
|
var restoreMapping = [{
|
|
|
|
|
source: '/datadir',
|
|
|
|
|
destination: path.join(gDestinationFolder, 'data')
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
filesystem.restore(gBackupConfig, gBackupId_1, restoreMapping, function (error) {
|
|
|
|
|
expect(error).to.be.an('object');
|
|
|
|
|
expect(error.reason).to.equal(BackupsError.NOT_FOUND);
|
|
|
|
|
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can restore backup copy', function (done) {
|
|
|
|
|
var restoreMapping = [{
|
|
|
|
|
source: '/datadir',
|
|
|
|
|
destination: path.join(gDestinationFolder, 'data')
|
|
|
|
|
}, {
|
|
|
|
|
source: '/addondir',
|
|
|
|
|
destination: path.join(gDestinationFolder, 'addon')
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
filesystem.restore(gBackupConfig, gBackupId_2, restoreMapping, function (error) {
|
|
|
|
|
expect(error).to.be(null);
|
|
|
|
|
|
|
|
|
|
rimraf(gDestinationFolder, done);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('cannot get backup download stream from deleted backup', function (done) {
|
|
|
|
|
filesystem.getDownloadStream(gBackupConfig, gBackupId_1, function (error) {
|
|
|
|
|
expect(error).to.be.an('object');
|
|
|
|
|
expect(error.reason).to.equal(BackupsError.NOT_FOUND);
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can get backup download stream', function (done) {
|
|
|
|
|
filesystem.getDownloadStream(gBackupConfig, gBackupId_2, function (error, result) {
|
|
|
|
|
expect(error).to.be(null);
|
2017-04-18 17:34:42 +02:00
|
|
|
expect(result).to.be.a(stream.Readable);
|
2017-04-17 14:46:39 +02:00
|
|
|
done();
|
2017-04-18 17:34:42 +02:00
|
|
|
});
|
2017-04-17 14:46:39 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can remove backup copy', function (done) {
|
|
|
|
|
filesystem.removeBackup(gBackupConfig, gBackupId_2, [], done);
|
|
|
|
|
});
|
|
|
|
|
});
|
2017-04-18 17:34:42 +02:00
|
|
|
|
|
|
|
|
describe('s3', function () {
|
|
|
|
|
this.timeout(10000);
|
|
|
|
|
|
|
|
|
|
var gBackupId_1 = 'someprefix/one';
|
|
|
|
|
var gBackupId_2 = 'someprefix/two';
|
|
|
|
|
var gTmpFolder;
|
|
|
|
|
var gSourceFolder;
|
|
|
|
|
var gDestinationFolder;
|
|
|
|
|
var gBackupConfig = {
|
|
|
|
|
provider: 's3',
|
|
|
|
|
key: 'key',
|
|
|
|
|
prefix: 'unit.test',
|
|
|
|
|
bucket: 'cloudron-storage-test',
|
|
|
|
|
accessKeyId: '',
|
|
|
|
|
secretAccessKey: '',
|
|
|
|
|
region: 'eu-central-1'
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
before(function (done) {
|
|
|
|
|
setup(function (error) {
|
|
|
|
|
expect(error).to.be(null);
|
|
|
|
|
|
|
|
|
|
gTmpFolder = fs.mkdtempSync(path.join(os.tmpdir(), 's3-backup-test_'));
|
|
|
|
|
gSourceFolder = path.join(__dirname, 'storage');
|
|
|
|
|
gDestinationFolder = path.join(gTmpFolder, 'destination/');
|
|
|
|
|
|
|
|
|
|
settings.setBackupConfig(gBackupConfig, function (error) {
|
|
|
|
|
expect(error).to.be(null);
|
|
|
|
|
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
after(function (done) {
|
|
|
|
|
cleanup(function (error) {
|
|
|
|
|
expect(error).to.be(null);
|
|
|
|
|
rimraf(gTmpFolder, done);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can backup', function (done) {
|
|
|
|
|
var backupMapping = [{
|
|
|
|
|
source: path.join(gSourceFolder, 'data'),
|
|
|
|
|
destination: '/datadir'
|
|
|
|
|
}, {
|
|
|
|
|
source: path.join(gSourceFolder, 'addon'),
|
|
|
|
|
destination: '/addondir/'
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
s3.backup(gBackupConfig, gBackupId_1, backupMapping, function (error) {
|
|
|
|
|
expect(error).to.be(null);
|
|
|
|
|
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can restore', function (done) {
|
|
|
|
|
var restoreMapping = [{
|
|
|
|
|
source: '/datadir',
|
|
|
|
|
destination: path.join(gDestinationFolder, 'data')
|
|
|
|
|
}, {
|
|
|
|
|
source: '/addondir',
|
|
|
|
|
destination: path.join(gDestinationFolder, 'addon')
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
s3.restore(gBackupConfig, gBackupId_1, restoreMapping, function (error) {
|
|
|
|
|
expect(error).to.be(null);
|
|
|
|
|
|
|
|
|
|
compareDirectories(gSourceFolder, gDestinationFolder, function (error) {
|
|
|
|
|
expect(error).to.equal(null);
|
|
|
|
|
rimraf(gDestinationFolder, done);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can copy backup', function (done) {
|
|
|
|
|
// will be verified after removing the first and restoring from the copy
|
|
|
|
|
s3.copyBackup(gBackupConfig, gBackupId_1, gBackupId_2, done);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can remove backup', function (done) {
|
|
|
|
|
// will be verified with next test trying to restore the removed one
|
|
|
|
|
s3.removeBackup(gBackupConfig, gBackupId_1, [], done);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('cannot restore deleted backup', function (done) {
|
|
|
|
|
var restoreMapping = [{
|
|
|
|
|
source: '/datadir',
|
|
|
|
|
destination: path.join(gDestinationFolder, 'data')
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
s3.restore(gBackupConfig, gBackupId_1, restoreMapping, function (error) {
|
|
|
|
|
expect(error).to.be.an('object');
|
|
|
|
|
expect(error.reason).to.equal(BackupsError.NOT_FOUND);
|
|
|
|
|
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can restore backup copy', function (done) {
|
|
|
|
|
var restoreMapping = [{
|
|
|
|
|
source: '/datadir',
|
|
|
|
|
destination: path.join(gDestinationFolder, 'data')
|
|
|
|
|
}, {
|
|
|
|
|
source: '/addondir',
|
|
|
|
|
destination: path.join(gDestinationFolder, 'addon')
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
s3.restore(gBackupConfig, gBackupId_2, restoreMapping, function (error) {
|
|
|
|
|
expect(error).to.be(null);
|
|
|
|
|
|
|
|
|
|
rimraf(gDestinationFolder, done);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('cannot get backup download stream from deleted backup', function (done) {
|
|
|
|
|
s3.getDownloadStream(gBackupConfig, gBackupId_1, function (error) {
|
|
|
|
|
expect(error).to.be.an('object');
|
|
|
|
|
expect(error.reason).to.equal(BackupsError.NOT_FOUND);
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can get backup download stream', function (done) {
|
|
|
|
|
s3.getDownloadStream(gBackupConfig, gBackupId_2, function (error, result) {
|
|
|
|
|
expect(error).to.be(null);
|
|
|
|
|
expect(result).to.be.a(stream.Readable);
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can remove backup copy', function (done) {
|
|
|
|
|
s3.removeBackup(gBackupConfig, gBackupId_2, [], done);
|
|
|
|
|
});
|
|
|
|
|
});
|
2017-04-17 14:46:39 +02:00
|
|
|
});
|