docker-snap
Version:
A utility for managing Docker volumes and images backup and restore
145 lines (107 loc) • 3.64 kB
JavaScript
const fs = require("fs");
const path = require("path");
const micromatch = require("micromatch");
const fg = require("fast-glob");
const {exec, spawn, dockerImage, createTimestamp} = require("./util");
exports.listVolumes = async filter => {
let volumes = (await exec("docker volume ls --format \"{{.Name}}\"")).stdout.split("\n").filter(Boolean);
if (filter)
volumes = micromatch(volumes, filter);
return volumes;
};
exports.createVolume = async volume => await exec(`docker volume create --name ${volume}`);
exports.removeVolume = async volume => await exec(`docker volume rm ${volume}`);
exports.duplicateVolume = async (source, destination) => {
if (!destination)
destination = source + "-" + createTimestamp();
exports.createVolume(destination);
await spawn("docker", [
"run",
"--rm",
"-v",
source + ":/from",
"-v",
destination + ":/to",
"alpine",
"cp -a /from/ /to",
]);
};
exports.saveVolume = async (volume, filename, overwrite = false) => {
if (!filename)
filename = volume + "-" + createTimestamp() + ".tgz";
else if (path.parse(filename).ext !== ".tgz")
throw new Error(`Backup file '${filename}' must have a .tgz extension.`);
filename = path.resolve(filename);
if (fs.existsSync(filename) && fs.statSync(filename).isDirectory())
filename = path.join(filename, volume + ".tgz");
if (fs.existsSync(filename)) {
if (overwrite)
fs.unlinkSync(filename);
else
throw new Error(`File already exists for volume '${volume}'.`);
}
await spawn("docker", [
"run",
"--rm",
"-w",
"/from",
"-v",
volume + ":/from",
"-v",
path.dirname(filename) + ":/to",
dockerImage,
"tar",
"-czvf",
"/to/" + path.basename(filename),
".",
]);
};
exports.loadVolume = async (filename, volume, overwrite = false) => {
filename = path.resolve(filename);
if (!volume)
volume = path.parse(filename).name;
if (path.parse(filename).ext !== ".tgz")
throw new Error(`Backup file '${filename}' must have a .tgz extension.`);
if ((await exports.listVolumes(volume)).length > 0) {
if (!overwrite)
throw new Error(`Volume '${volume}' already exists.`);
else
exports.removeVolume(volume);
}
exports.createVolume(volume);
await spawn("docker", [
"run",
"--rm",
"-w",
"/to",
"-v",
volume + ":/to",
"-v",
filename + ":/from/volume.tgz",
dockerImage,
"tar",
"-xzvf",
"/from/volume.tgz",
]);
};
exports.saveVolumes = async (filter, directory, overwrite = false) => {
const volumes = micromatch(await exports.listVolumes(), filter);
if (!overwrite) {
const existings = volumes.filter(volume => fs.existsSync(path.join(directory, `${volume}.tgz`)));
if (existings.length > 0)
throw new Error(`File already exists:\n${existings.map(volume => ` ${volume}.tgz`).join("\n")}`);
}
for (const volume of volumes)
await exports.saveVolume(volume, path.join(directory, volume + ".tgz"), overwrite);
};
exports.loadVolumes = async (files, overwrite = false) => {
files = await fg(path.resolve(files).replace(/\\/g, "/"), {onlyFiles: true});
const volumes = files.map(file => path.parse(file).name);
if (!overwrite) {
const existings = (await exports.listVolumes()).filter(volume => volumes.includes(volume));
if (existings.length > 0)
throw new Error(`Volume already exists:\n${existings.map(volume => ` ${volume}`).join("\n")}`);
}
for (let f = 0; f < files.length; f++)
await exports.loadVolume(files[f], volumes[f], overwrite);
};