backups: add backup multiple targets
This commit is contained in:
@@ -13,7 +13,6 @@ const archives = require('../archives.js'),
|
||||
common = require('./common.js'),
|
||||
expect = require('expect.js'),
|
||||
moment = require('moment'),
|
||||
settings = require('../settings.js'),
|
||||
tasks = require('../tasks.js'),
|
||||
timers = require('timers/promises');
|
||||
|
||||
@@ -127,6 +126,8 @@ describe('backup cleaner', function () {
|
||||
});
|
||||
|
||||
describe('task', function () {
|
||||
let target;
|
||||
|
||||
const BACKUP_0_BOX = {
|
||||
id: null,
|
||||
remotePath: 'backup-box-0',
|
||||
@@ -226,17 +227,17 @@ describe('backup cleaner', function () {
|
||||
};
|
||||
|
||||
before(async function () {
|
||||
await settings._set(settings.BACKUP_STORAGE_KEY, JSON.stringify({
|
||||
target = await backupTargets._getDefault();
|
||||
await backupTargets.setConfig(target, {
|
||||
provider: 'filesystem',
|
||||
password: 'supersecret',
|
||||
backupFolder: '/tmp/someplace',
|
||||
format: 'tgz'
|
||||
}));
|
||||
await backupTargets.setPolicy({ retention: { keepWithinSecs: 1 }, schedule: '00 00 23 * * *' });
|
||||
});
|
||||
await backupTargets.setRetention(target, { keepWithinSecs: 1 });
|
||||
await backupTargets.setSchedule(target, '00 00 23 * * *');
|
||||
});
|
||||
|
||||
async function cleanupBackups() {
|
||||
const taskId = await backupTargets.startCleanupTask({ username: 'test' });
|
||||
async function cleanupBackups(target) {
|
||||
const taskId = await backupTargets.startCleanupTask(target, { username: 'test' });
|
||||
|
||||
console.log('started task', taskId);
|
||||
|
||||
@@ -253,7 +254,7 @@ describe('backup cleaner', function () {
|
||||
}
|
||||
|
||||
it('succeeds without backups', async function () {
|
||||
await cleanupBackups();
|
||||
await cleanupBackups(target);
|
||||
});
|
||||
|
||||
it('add the backups', async function () {
|
||||
@@ -274,7 +275,7 @@ describe('backup cleaner', function () {
|
||||
});
|
||||
|
||||
it('succeeds with box backups, keeps latest', async function () {
|
||||
await cleanupBackups();
|
||||
await cleanupBackups(target);
|
||||
|
||||
const results = await backupListing.getByTypePaged(backupListing.BACKUP_TYPE_BOX, 1, 1000);
|
||||
expect(results.length).to.equal(1);
|
||||
@@ -286,7 +287,7 @@ describe('backup cleaner', function () {
|
||||
});
|
||||
|
||||
it('does not remove expired backups if only one left', async function () {
|
||||
await cleanupBackups();
|
||||
await cleanupBackups(target);
|
||||
|
||||
const results = await backupListing.getByTypePaged(backupListing.BACKUP_TYPE_BOX, 1, 1000);
|
||||
expect(results[0].id).to.equal(BACKUP_1_BOX.id);
|
||||
@@ -304,7 +305,7 @@ describe('backup cleaner', function () {
|
||||
|
||||
await timers.setTimeout(2000); // wait for expiration
|
||||
|
||||
await cleanupBackups();
|
||||
await cleanupBackups(target);
|
||||
|
||||
let result = await backupListing.getByTypePaged(backupListing.BACKUP_TYPE_APP, 1, 1000);
|
||||
expect(result.length).to.equal(4);
|
||||
|
||||
@@ -14,7 +14,7 @@ const backupListing = require('../backuplisting.js'),
|
||||
safe = require('safetydance');
|
||||
|
||||
describe('backups', function () {
|
||||
const { setup, cleanup, defaultBackupTarget } = common;
|
||||
const { setup, cleanup } = common;
|
||||
|
||||
const boxBackup = {
|
||||
id: null,
|
||||
@@ -48,8 +48,11 @@ describe('backups', function () {
|
||||
targetId: null
|
||||
};
|
||||
|
||||
let defaultBackupTarget;
|
||||
|
||||
before(async function () {
|
||||
await setup();
|
||||
defaultBackupTarget = await backupTargets._getDefault();
|
||||
boxBackup.targetId = defaultBackupTarget.id;
|
||||
appBackup.targetId = defaultBackupTarget.id;
|
||||
});
|
||||
@@ -120,40 +123,4 @@ describe('backups', function () {
|
||||
expect(result).to.be(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('config and policy', function () {
|
||||
it('can get backup config', async function () {
|
||||
const backupConfig = await backupTargets.getConfig();
|
||||
expect(backupConfig.provider).to.be('filesystem');
|
||||
expect(backupConfig.backupFolder).to.be('/var/backups');
|
||||
});
|
||||
|
||||
it('can set backup config', async function () {
|
||||
let backupConfig = await backupTargets.getConfig();
|
||||
backupConfig = Object.assign({}, backupConfig, { backupFolder: '/tmp/backups' });
|
||||
await backupTargets.setConfig(backupConfig);
|
||||
|
||||
const newBackupConfig = await backupTargets.getConfig();
|
||||
expect(newBackupConfig.backupFolder).to.be('/tmp/backups');
|
||||
});
|
||||
|
||||
it('cannot set backup policy with invalid schedule', async function () {
|
||||
const [error] = await safe(backupTargets.setPolicy({ schedule: '', retention: { keepWithinSecs: 1 }}));
|
||||
expect(error.reason).to.be(BoxError.BAD_FIELD);
|
||||
});
|
||||
|
||||
it('cannot set backup policy with missing retention', async function () {
|
||||
const [error] = await safe(backupTargets.setPolicy({ schedule: '00 * * * * *'}));
|
||||
expect(error.reason).to.be(BoxError.BAD_FIELD);
|
||||
});
|
||||
|
||||
it('cannot set backup policy with invalid retention', async function () {
|
||||
const [error] = await safe(backupTargets.setPolicy({ schedule: '00 * * * * *', retention: { keepWhenever: 4 }}));
|
||||
expect(error.reason).to.be(BoxError.BAD_FIELD);
|
||||
});
|
||||
|
||||
it('can set valid backup policy', async function () {
|
||||
await backupTargets.setPolicy({ schedule: '00 00 2,23 * * 0,1,2', retention: { keepWithinSecs: 1 }});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
78
src/test/backuptargets-test.js
Normal file
78
src/test/backuptargets-test.js
Normal file
@@ -0,0 +1,78 @@
|
||||
/* jslint node:true */
|
||||
/* global it:false */
|
||||
/* global describe:false */
|
||||
/* global before:false */
|
||||
/* global after:false */
|
||||
|
||||
'use strict';
|
||||
|
||||
const backupTargets = require('../backuptargets.js'),
|
||||
BoxError = require('../boxerror.js'),
|
||||
common = require('./common.js'),
|
||||
constants = require('../constants.js'),
|
||||
expect = require('expect.js'),
|
||||
safe = require('safetydance');
|
||||
|
||||
describe('backups', function () {
|
||||
const { setup, cleanup } = common;
|
||||
|
||||
before(async function () {
|
||||
await setup();
|
||||
});
|
||||
after(cleanup);
|
||||
|
||||
let defaultBackupTarget = null;
|
||||
|
||||
it('can list backup targets', async function () {
|
||||
const result = await backupTargets.list(1, 5);
|
||||
expect(result.length).to.be(1);
|
||||
defaultBackupTarget = result[0];
|
||||
});
|
||||
|
||||
it('can get backup target', async function () {
|
||||
const backupTarget = await backupTargets.get(defaultBackupTarget.id);
|
||||
expect(backupTarget.config.provider).to.be('filesystem');
|
||||
expect(backupTarget.config.backupFolder).to.be('/var/backups');
|
||||
expect(backupTarget.format).to.be('tgz');
|
||||
expect(backupTarget.encryption).to.be(null);
|
||||
});
|
||||
|
||||
it('cannot get random backup target', async function () {
|
||||
const backupTarget = await backupTargets.get('random');
|
||||
expect(backupTarget).to.be(null);
|
||||
});
|
||||
|
||||
it('can set backup config', async function () {
|
||||
const newConfig = Object.assign({}, defaultBackupTarget.config, { backupFolder: '/tmp/backups' });
|
||||
await backupTargets.setConfig(defaultBackupTarget, newConfig);
|
||||
|
||||
const result = await backupTargets.get(defaultBackupTarget.id);
|
||||
expect(result.config.backupFolder).to.be('/tmp/backups');
|
||||
});
|
||||
|
||||
it('cannot set invalid schedule', async function () {
|
||||
const [error] = await safe(backupTargets.setSchedule(defaultBackupTarget, ''));
|
||||
expect(error.reason).to.be(BoxError.BAD_FIELD);
|
||||
});
|
||||
|
||||
it('can set valid schedule', async function () {
|
||||
for (const pattern of [ '00 * * * * *', constants.CRON_PATTERN_NEVER ]) {
|
||||
await backupTargets.setSchedule(defaultBackupTarget, pattern);
|
||||
const backupTarget = await backupTargets.get(defaultBackupTarget.id);
|
||||
expect(backupTarget.schedule).to.be(pattern);
|
||||
}
|
||||
});
|
||||
|
||||
it('cannot set invalid retention', async function () {
|
||||
const [error] = await safe(backupTargets.setRetention(defaultBackupTarget, { keepWhenever: 4 }));
|
||||
expect(error.reason).to.be(BoxError.BAD_FIELD);
|
||||
});
|
||||
|
||||
it('can set valid retention', async function () {
|
||||
for (const retention of [ { keepWithinSecs: 1 }, { keepYearly: 3 }, { keepMonthly: 14 } ]) {
|
||||
await backupTargets.setRetention(defaultBackupTarget, retention);
|
||||
const backupTarget = await backupTargets.get(defaultBackupTarget.id);
|
||||
expect(backupTarget.retention).to.eql(retention);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -28,17 +28,18 @@ describe('backuptask', function () {
|
||||
const backupConfig = {
|
||||
provider: 'filesystem',
|
||||
backupFolder: path.join(os.tmpdir(), 'backupstask-test-filesystem'),
|
||||
format: 'tgz',
|
||||
};
|
||||
|
||||
let defaultBackupTarget;
|
||||
|
||||
before(async function () {
|
||||
fs.rmSync(backupConfig.backupFolder, { recursive: true, force: true });
|
||||
|
||||
await backupTargets.setStorage(backupConfig);
|
||||
defaultBackupTarget = await backupTargets._getDefault();
|
||||
await backupTargets.setConfig(defaultBackupTarget, backupConfig);
|
||||
});
|
||||
|
||||
async function createBackup() {
|
||||
const taskId = await backupTargets.startBackupTask({ username: 'test' });
|
||||
async function createBackup(target) {
|
||||
const taskId = await backupTargets.startBackupTask(target, { username: 'test' });
|
||||
|
||||
while (true) {
|
||||
await timers.setTimeout(1000);
|
||||
@@ -67,7 +68,7 @@ describe('backuptask', function () {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await createBackup();
|
||||
const result = await createBackup(defaultBackupTarget);
|
||||
expect(fs.statSync(path.join(backupConfig.backupFolder, 'snapshot/box.tar.gz')).nlink).to.be(2); // hard linked to a rotated backup
|
||||
expect(fs.statSync(path.join(backupConfig.backupFolder, `${result.remotePath}.tar.gz`)).nlink).to.be(2);
|
||||
|
||||
@@ -81,7 +82,7 @@ describe('backuptask', function () {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await createBackup();
|
||||
const result = await createBackup(defaultBackupTarget);
|
||||
expect(fs.statSync(path.join(backupConfig.backupFolder, 'snapshot/box.tar.gz')).nlink).to.be(2); // hard linked to a rotated backup
|
||||
expect(fs.statSync(path.join(backupConfig.backupFolder, `${result.remotePath}.tar.gz`)).nlink).to.be(2); // hard linked to new backup
|
||||
expect(fs.statSync(path.join(backupConfig.backupFolder, `${backupInfo1.remotePath}.tar.gz`)).nlink).to.be(1); // not hard linked anymore
|
||||
|
||||
@@ -186,8 +186,6 @@ exports = module.exports = {
|
||||
user,
|
||||
appstoreToken: 'atoken',
|
||||
|
||||
defaultBackupTarget: { id: null },
|
||||
|
||||
serverUrl: `http://localhost:${constants.PORT}`,
|
||||
};
|
||||
|
||||
@@ -220,7 +218,7 @@ async function databaseSetup() {
|
||||
await database._clear();
|
||||
await appstore._setApiServerOrigin(exports.mockApiServerOrigin);
|
||||
await dashboard._setLocation(constants.DASHBOARD_SUBDOMAIN, exports.dashboardDomain);
|
||||
exports.defaultBackupTarget.id = await backupTargets._addDefaultTarget();
|
||||
await backupTargets._addDefault();
|
||||
}
|
||||
|
||||
async function domainSetup() {
|
||||
|
||||
@@ -33,18 +33,18 @@ describe('Storage', function () {
|
||||
let gTmpFolder;
|
||||
|
||||
const gBackupConfig = {
|
||||
provider: 'filesystem',
|
||||
key: 'key',
|
||||
backupFolder: null,
|
||||
format: 'tgz',
|
||||
};
|
||||
|
||||
before(function (done) {
|
||||
let defaultBackupTarget;
|
||||
|
||||
before(async function () {
|
||||
gTmpFolder = fs.mkdtempSync(path.join(os.tmpdir(), 'filesystem-storage-test_'));
|
||||
|
||||
gBackupConfig.backupFolder = path.join(gTmpFolder, 'backups/');
|
||||
defaultBackupTarget = await backupTargets._getDefault();
|
||||
|
||||
done();
|
||||
gBackupConfig.backupFolder = path.join(gTmpFolder, 'backups/');
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
@@ -54,12 +54,12 @@ describe('Storage', function () {
|
||||
|
||||
it('fails to set backup storage for bad folder', async function () {
|
||||
const tmp = Object.assign({}, gBackupConfig, { backupFolder: '/root/oof' });
|
||||
const [error] = await safe(backupTargets.setStorage(tmp));
|
||||
const [error] = await safe(backupTargets.setConfig(defaultBackupTarget, tmp));
|
||||
expect(error.reason).to.equal(BoxError.BAD_FIELD);
|
||||
});
|
||||
|
||||
it('succeeds to set backup storage', async function () {
|
||||
await backupTargets.setStorage(gBackupConfig);
|
||||
await backupTargets.setConfig(defaultBackupTarget, gBackupConfig);
|
||||
expect(fs.existsSync(path.join(gBackupConfig.backupFolder, 'snapshot'))).to.be(true); // auto-created
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user