2021-07-14 19:03:12 -07:00
|
|
|
/* jslint node:true */
|
|
|
|
|
/* global it:false */
|
|
|
|
|
/* global describe:false */
|
|
|
|
|
/* global before:false */
|
|
|
|
|
/* global after:false */
|
|
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
const backups = require('../backups.js'),
|
|
|
|
|
backuptask = require('../backuptask.js'),
|
|
|
|
|
common = require('./common.js'),
|
|
|
|
|
DataLayout = require('../datalayout.js'),
|
2022-04-15 07:52:35 -05:00
|
|
|
delay = require('../delay.js'),
|
2021-07-14 19:03:12 -07:00
|
|
|
expect = require('expect.js'),
|
|
|
|
|
fs = require('fs'),
|
|
|
|
|
os = require('os'),
|
|
|
|
|
path = require('path'),
|
|
|
|
|
settings = require('../settings.js'),
|
|
|
|
|
tasks = require('../tasks.js');
|
|
|
|
|
|
|
|
|
|
describe('backuptask', function () {
|
|
|
|
|
const { setup, cleanup, createTree } = common;
|
|
|
|
|
|
|
|
|
|
before(setup);
|
|
|
|
|
after(cleanup);
|
|
|
|
|
|
|
|
|
|
describe('fs meta data', function () {
|
|
|
|
|
let tmpdir;
|
|
|
|
|
before(function () {
|
|
|
|
|
tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), 'backups-test'));
|
|
|
|
|
});
|
|
|
|
|
after(function () {
|
|
|
|
|
fs.rmSync(tmpdir, { recursive: true, force: true });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('saves special files', async function () {
|
|
|
|
|
createTree(tmpdir, { 'data': { 'subdir': { 'emptydir': { } } }, 'dir2': { 'file': 'stuff' } });
|
|
|
|
|
fs.chmodSync(path.join(tmpdir, 'dir2/file'), parseInt('0755', 8));
|
|
|
|
|
|
|
|
|
|
let dataLayout = new DataLayout(tmpdir, []);
|
|
|
|
|
|
|
|
|
|
await backuptask._saveFsMetadata(dataLayout, `${dataLayout.localRoot()}/fsmetadata.json`);
|
|
|
|
|
|
|
|
|
|
const emptyDirs = JSON.parse(fs.readFileSync(path.join(tmpdir, 'fsmetadata.json'), 'utf8')).emptyDirs;
|
|
|
|
|
expect(emptyDirs).to.eql(['./data/subdir/emptydir']);
|
|
|
|
|
|
|
|
|
|
const execFiles = JSON.parse(fs.readFileSync(path.join(tmpdir, 'fsmetadata.json'), 'utf8')).execFiles;
|
|
|
|
|
expect(execFiles).to.eql(['./dir2/file']);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('restores special files', async function () {
|
|
|
|
|
fs.rmSync(path.join(tmpdir, 'data'), { recursive: true, force: true });
|
|
|
|
|
|
|
|
|
|
expect(fs.existsSync(path.join(tmpdir, 'data/subdir/emptydir'))).to.be(false); // just make sure rimraf worked
|
|
|
|
|
|
|
|
|
|
let dataLayout = new DataLayout(tmpdir, []);
|
|
|
|
|
|
|
|
|
|
await backuptask._restoreFsMetadata(dataLayout, `${dataLayout.localRoot()}/fsmetadata.json`);
|
|
|
|
|
|
|
|
|
|
expect(fs.existsSync(path.join(tmpdir, 'data/subdir/emptydir'))).to.be(true);
|
|
|
|
|
const mode = fs.statSync(path.join(tmpdir, 'dir2/file')).mode;
|
|
|
|
|
expect(mode & ~fs.constants.S_IFREG).to.be(parseInt('0755', 8));
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
2021-09-26 18:37:04 -07:00
|
|
|
describe('fullBackup', function () {
|
2021-07-14 19:03:12 -07:00
|
|
|
let backupInfo1;
|
|
|
|
|
|
|
|
|
|
const backupConfig = {
|
|
|
|
|
provider: 'filesystem',
|
|
|
|
|
backupFolder: path.join(os.tmpdir(), 'backupstask-test-filesystem'),
|
|
|
|
|
format: 'tgz',
|
|
|
|
|
retentionPolicy: { keepWithinSecs: 10000 },
|
|
|
|
|
schedulePattern: '00 00 23 * * *'
|
|
|
|
|
};
|
|
|
|
|
|
2021-08-20 09:19:44 -07:00
|
|
|
before(async function () {
|
2021-07-14 19:03:12 -07:00
|
|
|
fs.rmSync(backupConfig.backupFolder, { recursive: true, force: true });
|
|
|
|
|
|
2021-08-20 09:19:44 -07:00
|
|
|
await settings.setBackupConfig(backupConfig);
|
2021-07-14 19:03:12 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
after(function () {
|
|
|
|
|
fs.rmSync(backupConfig.backupFolder, { recursive: true, force: true });
|
|
|
|
|
});
|
|
|
|
|
|
2021-09-10 12:10:10 -07:00
|
|
|
async function createBackup() {
|
|
|
|
|
const taskId = await backups.startBackupTask({ username: 'test' });
|
2021-07-14 19:03:12 -07:00
|
|
|
|
2021-09-10 12:10:10 -07:00
|
|
|
// eslint-disable-next-line no-constant-condition
|
|
|
|
|
while (true) {
|
|
|
|
|
await delay(1000);
|
2021-07-14 19:03:12 -07:00
|
|
|
|
2021-09-10 12:10:10 -07:00
|
|
|
const p = await tasks.get(taskId);
|
2021-07-14 19:03:12 -07:00
|
|
|
|
2021-09-10 12:10:10 -07:00
|
|
|
if (p.percent !== 100) continue;
|
|
|
|
|
if (p.error) throw new Error(`backup failed: ${p.error.message}`);
|
|
|
|
|
if (!p.result) throw new Error('backup has no result:' + p);
|
2021-07-14 19:03:12 -07:00
|
|
|
|
2021-09-10 12:10:10 -07:00
|
|
|
const result = await backups.getByIdentifierAndStatePaged(backups.BACKUP_IDENTIFIER_BOX, backups.BACKUP_STATE_NORMAL, 1, 1);
|
2021-07-14 19:03:12 -07:00
|
|
|
|
2021-09-10 12:10:10 -07:00
|
|
|
if (result.length !== 1) throw new Error('result is not of length 1');
|
2021-07-14 19:03:12 -07:00
|
|
|
|
2021-09-10 12:10:10 -07:00
|
|
|
// the task progress and the db entry is set in the worker. wait for 2 seconds for backup lock to get released in parent process
|
|
|
|
|
await delay(2000);
|
2021-07-14 19:03:12 -07:00
|
|
|
|
2021-09-10 12:10:10 -07:00
|
|
|
return result[0];
|
|
|
|
|
}
|
2021-07-14 19:03:12 -07:00
|
|
|
}
|
|
|
|
|
|
2021-09-10 12:10:10 -07:00
|
|
|
it('can backup', async function () {
|
2021-07-14 19:03:12 -07:00
|
|
|
// arch only has maria db which lacks some mysqldump options we need, this is only here to allow running the tests :-/
|
|
|
|
|
if (require('child_process').execSync('/usr/bin/mysqldump --version').toString().indexOf('MariaDB') !== -1) {
|
|
|
|
|
console.log('test skipped because of MariaDB');
|
2021-09-10 12:10:10 -07:00
|
|
|
return;
|
2021-07-14 19:03:12 -07:00
|
|
|
}
|
|
|
|
|
|
2021-09-10 12:10:10 -07:00
|
|
|
const result = await createBackup();
|
|
|
|
|
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.id}.tar.gz`)).nlink).to.be(2);
|
2021-07-14 19:03:12 -07:00
|
|
|
|
2021-09-10 12:10:10 -07:00
|
|
|
backupInfo1 = result;
|
2021-07-14 19:03:12 -07:00
|
|
|
});
|
|
|
|
|
|
2021-09-10 12:10:10 -07:00
|
|
|
it('can take another backup', async function () {
|
2021-07-14 19:03:12 -07:00
|
|
|
// arch only has maria db which lacks some mysqldump options we need, this is only here to allow running the tests :-/
|
|
|
|
|
if (require('child_process').execSync('/usr/bin/mysqldump --version').toString().indexOf('MariaDB') !== -1) {
|
|
|
|
|
console.log('test skipped because of MariaDB');
|
2021-09-10 12:10:10 -07:00
|
|
|
return;
|
2021-07-14 19:03:12 -07:00
|
|
|
}
|
|
|
|
|
|
2021-09-10 12:10:10 -07:00
|
|
|
const result = await createBackup();
|
|
|
|
|
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.id}.tar.gz`)).nlink).to.be(2); // hard linked to new backup
|
|
|
|
|
expect(fs.statSync(path.join(backupConfig.backupFolder, `${backupInfo1.id}.tar.gz`)).nlink).to.be(1); // not hard linked anymore
|
2021-07-14 19:03:12 -07:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|