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'),
|
2017-09-27 10:25:36 -07:00
|
|
|
backups = require('../backups.js'),
|
|
|
|
|
BackupsError = require('../backups.js').BackupsError,
|
|
|
|
|
config = require('../config.js'),
|
|
|
|
|
database = require('../database.js'),
|
|
|
|
|
expect = require('expect.js'),
|
|
|
|
|
filesystem = require('../storage/filesystem.js'),
|
2017-04-17 14:46:39 +02:00
|
|
|
fs = require('fs'),
|
2017-09-27 10:25:36 -07:00
|
|
|
mkdirp = require('mkdirp'),
|
|
|
|
|
MockS3 = require('mock-aws-s3'),
|
2017-04-17 14:46:39 +02:00
|
|
|
os = require('os'),
|
|
|
|
|
path = require('path'),
|
|
|
|
|
readdirp = require('readdirp'),
|
|
|
|
|
rimraf = require('rimraf'),
|
|
|
|
|
s3 = require('../storage/s3.js'),
|
2017-04-18 17:34:42 +02:00
|
|
|
settings = require('../settings.js'),
|
2017-09-09 17:48:20 -07:00
|
|
|
SettingsError = settings.SettingsError;
|
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
|
2017-09-27 10:25:36 -07:00
|
|
|
settings.setBackupConfig({ provider: 'filesystem', format: 'tgz', backupFolder: '/tmp'}, function (error) {
|
2017-04-17 14:46:39 +02:00
|
|
|
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 () {
|
2017-09-20 09:57:16 -07:00
|
|
|
var gBackupId_1;
|
|
|
|
|
var gBackupId_2;
|
2017-04-17 14:46:39 +02:00
|
|
|
var gTmpFolder;
|
|
|
|
|
var gSourceFolder;
|
|
|
|
|
var gDestinationFolder;
|
|
|
|
|
var gBackupConfig = {
|
|
|
|
|
provider: 'filesystem',
|
|
|
|
|
key: 'key',
|
2017-09-23 14:27:35 -07:00
|
|
|
backupFolder: null,
|
|
|
|
|
format: 'tgz'
|
2017-04-17 14:46:39 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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/');
|
|
|
|
|
|
2017-09-27 17:34:49 -07:00
|
|
|
gBackupId_1 = backups._getBackupFilePath(gBackupConfig, 'someprefix/one', gBackupConfig.format);
|
|
|
|
|
gBackupId_2 = backups._getBackupFilePath(gBackupConfig, 'someprefix/two', gBackupConfig.format);
|
2017-09-20 09:57:16 -07:00
|
|
|
|
2017-04-21 17:21:10 +02:00
|
|
|
done();
|
2017-04-17 14:46:39 +02:00
|
|
|
});
|
|
|
|
|
});
|
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);
|
2017-09-23 14:27:35 -07:00
|
|
|
rimraf.sync(gTmpFolder);
|
2017-09-19 20:40:38 -07:00
|
|
|
done();
|
2017-04-21 17:21:10 +02:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('fails to set backup config for non-existing folder', function (done) {
|
|
|
|
|
settings.setBackupConfig(gBackupConfig, function (error) {
|
|
|
|
|
expect(error).to.be.a(SettingsError);
|
|
|
|
|
expect(error.reason).to.equal(SettingsError.BAD_FIELD);
|
|
|
|
|
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('succeeds to set backup config', function (done) {
|
|
|
|
|
mkdirp.sync(gBackupConfig.backupFolder);
|
|
|
|
|
|
|
|
|
|
settings.setBackupConfig(gBackupConfig, function (error) {
|
|
|
|
|
expect(error).to.be(null);
|
|
|
|
|
|
|
|
|
|
done();
|
2017-04-17 14:46:39 +02:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can backup', function (done) {
|
2017-09-20 09:57:16 -07:00
|
|
|
var tarStream = backups._createTarPackStream(gSourceFolder, gBackupConfig.key);
|
|
|
|
|
filesystem.upload(gBackupConfig, gBackupId_1, tarStream, function (error) {
|
2017-04-17 14:46:39 +02:00
|
|
|
expect(error).to.be(null);
|
|
|
|
|
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2017-09-17 18:50:29 -07:00
|
|
|
it('can download', function (done) {
|
2017-09-20 09:57:16 -07:00
|
|
|
filesystem.download(gBackupConfig, gBackupId_1, function (error, stream) {
|
2017-04-17 14:46:39 +02:00
|
|
|
expect(error).to.be(null);
|
|
|
|
|
|
2017-09-20 09:57:16 -07:00
|
|
|
backups._tarExtract(stream, gDestinationFolder, gBackupConfig.key || null, function (error) {
|
|
|
|
|
expect(error).to.be(null);
|
2017-04-21 17:21:10 +02:00
|
|
|
|
2017-09-20 09:57:16 -07:00
|
|
|
compareDirectories(path.join(gSourceFolder, 'data'), path.join(gDestinationFolder, 'data'), function (error) {
|
2017-04-21 17:21:10 +02:00
|
|
|
expect(error).to.equal(null);
|
|
|
|
|
|
2017-09-20 09:57:16 -07:00
|
|
|
compareDirectories(path.join(gSourceFolder, 'addon'), path.join(gDestinationFolder, 'addon'), function (error) {
|
|
|
|
|
expect(error).to.equal(null);
|
|
|
|
|
|
|
|
|
|
rimraf(gDestinationFolder, done);
|
|
|
|
|
});
|
2017-04-21 17:21:10 +02:00
|
|
|
});
|
2017-04-17 14:46:39 +02:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can copy backup', function (done) {
|
|
|
|
|
// will be verified after removing the first and restoring from the copy
|
2017-09-17 18:50:29 -07:00
|
|
|
filesystem.copy(gBackupConfig, gBackupId_1, gBackupId_2, done);
|
2017-04-17 14:46:39 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can remove backup', function (done) {
|
2017-09-17 18:50:29 -07:00
|
|
|
// will be verified with next test trying to download the removed one
|
2017-09-23 11:09:36 -07:00
|
|
|
filesystem.remove(gBackupConfig, gBackupId_1, done);
|
2017-04-17 14:46:39 +02:00
|
|
|
});
|
|
|
|
|
|
2017-09-17 18:50:29 -07:00
|
|
|
it('cannot download deleted backup', function (done) {
|
2017-09-20 09:57:16 -07:00
|
|
|
filesystem.download(gBackupConfig, gBackupId_1, function (error, stream) {
|
|
|
|
|
expect(error).to.be(null);
|
2017-04-17 14:46:39 +02:00
|
|
|
|
2017-09-20 09:57:16 -07:00
|
|
|
stream.on('error', function (error) {
|
|
|
|
|
expect(error).to.be.an('object');
|
|
|
|
|
expect(error.reason).to.equal(BackupsError.NOT_FOUND);
|
|
|
|
|
|
|
|
|
|
done();
|
|
|
|
|
});
|
2017-04-17 14:46:39 +02:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2017-09-17 18:50:29 -07:00
|
|
|
it('can download backup copy', function (done) {
|
2017-09-20 09:57:16 -07:00
|
|
|
filesystem.download(gBackupConfig, gBackupId_2, function (error, stream) {
|
2017-04-17 14:46:39 +02:00
|
|
|
expect(error).to.be(null);
|
|
|
|
|
|
2017-09-20 09:57:16 -07:00
|
|
|
backups._tarExtract(stream, gDestinationFolder, gBackupConfig.key || null, function (error) {
|
|
|
|
|
expect(error).to.be(null);
|
2017-04-21 17:21:10 +02:00
|
|
|
|
2017-09-20 09:57:16 -07:00
|
|
|
compareDirectories(path.join(gSourceFolder, 'data'), path.join(gDestinationFolder, 'data'), function (error) {
|
2017-04-21 17:21:10 +02:00
|
|
|
expect(error).to.equal(null);
|
|
|
|
|
|
2017-09-20 09:57:16 -07:00
|
|
|
compareDirectories(path.join(gSourceFolder, 'addon'), path.join(gDestinationFolder, 'addon'), function (error) {
|
|
|
|
|
expect(error).to.equal(null);
|
|
|
|
|
|
|
|
|
|
rimraf(gDestinationFolder, done);
|
|
|
|
|
});
|
2017-04-21 17:21:10 +02:00
|
|
|
});
|
|
|
|
|
});
|
2017-04-17 14:46:39 +02:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can remove backup copy', function (done) {
|
2017-09-23 11:09:36 -07:00
|
|
|
filesystem.remove(gBackupConfig, gBackupId_2, done);
|
2017-04-17 14:46:39 +02:00
|
|
|
});
|
|
|
|
|
});
|
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',
|
2017-04-18 19:15:56 +02:00
|
|
|
accessKeyId: 'testkeyid',
|
|
|
|
|
secretAccessKey: 'testsecret',
|
2017-09-23 14:27:35 -07:00
|
|
|
region: 'eu-central-1',
|
|
|
|
|
format: 'tgz'
|
2017-04-18 17:34:42 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
before(function (done) {
|
2017-04-18 19:15:56 +02:00
|
|
|
MockS3.config.basePath = path.join(os.tmpdir(), 's3-backup-test-buckets/');
|
|
|
|
|
|
|
|
|
|
s3._mockInject(MockS3);
|
|
|
|
|
|
2017-04-18 17:34:42 +02:00
|
|
|
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) {
|
2017-04-18 19:15:56 +02:00
|
|
|
s3._mockRestore();
|
|
|
|
|
rimraf.sync(MockS3.config.basePath);
|
|
|
|
|
|
2017-04-18 17:34:42 +02:00
|
|
|
cleanup(function (error) {
|
|
|
|
|
expect(error).to.be(null);
|
2017-09-23 14:27:35 -07:00
|
|
|
rimraf.sync(gTmpFolder);
|
|
|
|
|
done();
|
2017-04-18 17:34:42 +02:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can backup', function (done) {
|
2017-09-20 09:57:16 -07:00
|
|
|
var tarStream = backups._createTarPackStream(gSourceFolder, gBackupConfig.key);
|
|
|
|
|
s3.upload(gBackupConfig, gBackupId_1, tarStream, function (error) {
|
2017-04-18 17:34:42 +02:00
|
|
|
expect(error).to.be(null);
|
|
|
|
|
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2017-09-17 18:50:29 -07:00
|
|
|
it('can download', function (done) {
|
2017-09-20 09:57:16 -07:00
|
|
|
s3.download(gBackupConfig, gBackupId_1, function (error, stream) {
|
2017-04-18 17:34:42 +02:00
|
|
|
expect(error).to.be(null);
|
|
|
|
|
|
2017-09-20 09:57:16 -07:00
|
|
|
backups._tarExtract(stream, gDestinationFolder, gBackupConfig.key || null, function (error) {
|
|
|
|
|
expect(error).to.be(null);
|
2017-04-21 17:21:10 +02:00
|
|
|
|
2017-09-20 09:57:16 -07:00
|
|
|
compareDirectories(path.join(gSourceFolder, 'data'), path.join(gDestinationFolder, 'data'), function (error) {
|
2017-04-21 17:21:10 +02:00
|
|
|
expect(error).to.equal(null);
|
|
|
|
|
|
2017-09-20 09:57:16 -07:00
|
|
|
compareDirectories(path.join(gSourceFolder, 'addon'), path.join(gDestinationFolder, 'addon'), function (error) {
|
|
|
|
|
expect(error).to.equal(null);
|
|
|
|
|
|
|
|
|
|
rimraf(gDestinationFolder, done);
|
|
|
|
|
});
|
2017-04-21 17:21:10 +02:00
|
|
|
});
|
2017-04-18 17:34:42 +02:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can copy backup', function (done) {
|
|
|
|
|
// will be verified after removing the first and restoring from the copy
|
2017-09-17 18:50:29 -07:00
|
|
|
s3.copy(gBackupConfig, gBackupId_1, gBackupId_2, done);
|
2017-04-18 17:34:42 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can remove backup', function (done) {
|
2017-09-17 18:50:29 -07:00
|
|
|
// will be verified with next test trying to download the removed one
|
2017-09-23 11:09:36 -07:00
|
|
|
s3.remove(gBackupConfig, gBackupId_1, done);
|
2017-04-18 17:34:42 +02:00
|
|
|
});
|
|
|
|
|
|
2017-09-17 18:50:29 -07:00
|
|
|
it('cannot download deleted backup', function (done) {
|
2017-09-20 09:57:16 -07:00
|
|
|
s3.download(gBackupConfig, gBackupId_1, function (error, stream) {
|
|
|
|
|
expect(error).to.be(null);
|
2017-04-18 17:34:42 +02:00
|
|
|
|
2017-09-20 09:57:16 -07:00
|
|
|
stream.on('error', function (error) {
|
|
|
|
|
expect(error).to.be.an('object');
|
|
|
|
|
expect(error.reason).to.equal(BackupsError.NOT_FOUND);
|
|
|
|
|
|
|
|
|
|
done();
|
|
|
|
|
});
|
2017-04-18 17:34:42 +02:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2017-09-17 18:50:29 -07:00
|
|
|
it('can download backup copy', function (done) {
|
2017-09-20 09:57:16 -07:00
|
|
|
s3.download(gBackupConfig, gBackupId_2, function (error, stream) {
|
2017-04-18 17:34:42 +02:00
|
|
|
expect(error).to.be(null);
|
|
|
|
|
|
2017-09-20 09:57:16 -07:00
|
|
|
backups._tarExtract(stream, gDestinationFolder, gBackupConfig.key || null, function (error) {
|
|
|
|
|
expect(error).to.be(null);
|
2017-04-21 17:21:10 +02:00
|
|
|
|
2017-09-20 09:57:16 -07:00
|
|
|
compareDirectories(path.join(gSourceFolder, 'data'), path.join(gDestinationFolder, 'data'), function (error) {
|
2017-04-21 17:21:10 +02:00
|
|
|
expect(error).to.equal(null);
|
|
|
|
|
|
2017-09-20 09:57:16 -07:00
|
|
|
compareDirectories(path.join(gSourceFolder, 'addon'), path.join(gDestinationFolder, 'addon'), function (error) {
|
|
|
|
|
expect(error).to.equal(null);
|
|
|
|
|
|
|
|
|
|
rimraf(gDestinationFolder, done);
|
|
|
|
|
});
|
2017-04-21 17:21:10 +02:00
|
|
|
});
|
|
|
|
|
});
|
2017-04-18 17:34:42 +02:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('can remove backup copy', function (done) {
|
2017-09-23 11:09:36 -07:00
|
|
|
s3.remove(gBackupConfig, gBackupId_2, done);
|
2017-04-18 17:34:42 +02:00
|
|
|
});
|
|
|
|
|
});
|
2017-04-17 14:46:39 +02:00
|
|
|
});
|