Skip to content

Instantly share code, notes, and snippets.

@themightychris
Last active December 14, 2020 15:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save themightychris/66465878ae4bab5138317ae637de3943 to your computer and use it in GitHub Desktop.
Save themightychris/66465878ae4bab5138317ae637de3943 to your computer and use it in GitHub Desktop.
tbz -> restic migration
# install restic CLI
hab pkg install --binlink core/restic
# configure restic repository access
export RESTIC_REPOSITORY=/src/restic-local
export RESTIC_PASSWORD=abc123
#!/usr/bin/env node
const path = require('path');
const fs = require('mz/fs');
const shellExec = require('shell-exec');
run();
async function run() {
const cwd = process.cwd();
const extractPath = `${cwd}/home/chris`;
const canonicalPath = '/home/chris';
const studioPath = `/hab/studios/${cwd.substr(1).replace(/\//g, '--')}`;
const snapshotMatchPaths = [
canonicalPath,
path.dirname(extractPath)
];
// check that studio works and has mount at canonical path
const { stdout: mountOutput } = await shellExec(`hab studio -q run mount | grep "on /home "`);
if (!mountOutput.match(/^\/dev\S+ on \/home type /)) {
console.log(`/src/home mount not found, run: mount --bind ${cwd}/home ${studioPath}/home`);
return;
}
// verify inode id matches for extract/canonical directory
const { stdout: extractPathIdOutput } = await shellExec(`ls -id "${path.dirname(extractPath)}"`);
const extractPathId = extractPathIdOutput.match(/(^\d+) \S+$/m)[1];
const { stdout: canonicalPathIdOutput } = await shellExec(`hab studio -q run ls -id "${path.dirname(canonicalPath)}"`);
const canonicalPathId = canonicalPathIdOutput.match(/(^\d+) \S+$/m)[1];
if (!extractPathId || extractPathId != canonicalPathId) {
console.log('containing directory for extract path does not match by inode id inside and outside the studio, check mount');
return;
}
// get list of old backup .tbzs
const backupRe = /^home\.\d{4}-\d{2}-\d{2}\.tbz$/;
const backups = new Map();
for (const name of await fs.readdir(`${cwd}/chris-desktop`)) {
if (backupRe.test(name)) {
backups.set(name.substr(5, 10), `${cwd}/chris-desktop/${name}`);
}
}
console.log('found %s backups', backups.size);
// get list of existing restic snapshots
const { stdout: snapshotsJson } = await shellExec(`sudo -E restic snapshots --json`);
const snapshots = new Map();
for (const snapshot of JSON.parse(snapshotsJson)) {
if (snapshotMatchPaths.indexOf(snapshot.paths[0]) >= 0) {
snapshots.set(snapshot.time.substr(0, 10), snapshot);
}
}
console.log('found %s snapshots', snapshots.size);
// process each backup tbz (in order)
for (const [date, path] of backups) {
if (snapshots.has(date)) {
console.log('%s\tfound snapshot %s', date, snapshots.get(date).id);
continue;
}
console.log('%s\tsnapshotting...', date);
console.log('\t\tcleaning %s', extractPath);
await shellExec(`rm -rf "${extractPath}" && mkdir "${extractPath}"`);
console.log('\t\textracting %s', path);
await shellExec(`tar --directory="${extractPath}" -xjf "${path}"`);
const previousSnapshotDate = [...snapshots.keys()].filter(snapshotDate => snapshotDate < date).pop();
const previousSnapshot = previousSnapshotDate && snapshots.get(previousSnapshotDate);
console.log('\t\tusing parent %s', previousSnapshot ? `${previousSnapshotDate}#${previousSnapshot.id}` : '[none]');
const backupCommand = `hab studio -q run 'source .studiorc && restic backup "${canonicalPath}" --exclude-file=.restic-exclude --time "${date} 00:00:00" --parent ${previousSnapshot.id}'`;
console.log('\t\t%s', backupCommand);
const backupResult = await shellExec(backupCommand);
console.log(`\t\t\t${backupResult.stdout.replace(/\n/g, '\n\t\t\t')}`);
// break; // just one!
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment