321 lines
6.9 KiB
JavaScript
321 lines
6.9 KiB
JavaScript
const cp = require("child_process");
|
|
|
|
const DEFAULT_TIMEOUT = process.env.MOUNT_DEFAULT_TIMEOUT || 30000;
|
|
|
|
const EXIT_CODE_64 = "administrator can not mount filesystems";
|
|
const EXIT_CODE_78 = "missing or invalid passphrase";
|
|
|
|
/**
|
|
* https://objectivefs.com/
|
|
*/
|
|
class ObjectiveFS {
|
|
constructor(options = {}) {
|
|
const objectivefs = this;
|
|
objectivefs.options = options;
|
|
|
|
options.paths = options.paths || {};
|
|
if (!options.paths.objectivefs) {
|
|
options.paths.objectivefs = "mount.objectivefs";
|
|
}
|
|
|
|
if (!options.paths.sudo) {
|
|
options.paths.sudo = "/usr/bin/sudo";
|
|
}
|
|
|
|
if (!options.paths.chroot) {
|
|
options.paths.chroot = "/usr/sbin/chroot";
|
|
}
|
|
|
|
if (!options.env) {
|
|
options.env = {};
|
|
}
|
|
|
|
if (!options.executor) {
|
|
options.executor = {
|
|
spawn: cp.spawn,
|
|
//spawn: cp.execFile,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* mount.objectivefs [-o <opt>[,<opt>]..] <filesystem> <dir>
|
|
*
|
|
* @param {*} env
|
|
* @param {*} filesystem
|
|
* @param {*} target
|
|
* @param {*} options
|
|
*/
|
|
async mount(env, filesystem, target, options = []) {
|
|
if (!env) {
|
|
env = {};
|
|
}
|
|
const objectivefs = this;
|
|
let args = [];
|
|
args = args.concat(options);
|
|
args = args.concat([filesystem, target]);
|
|
|
|
let result;
|
|
try {
|
|
result = await objectivefs.exec(
|
|
objectivefs.options.paths.objectivefs,
|
|
args,
|
|
{ env, operation: "mount" }
|
|
);
|
|
return result;
|
|
} catch (err) {
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* mount.objectivefs create <your filesystem name>
|
|
* mount.objectivefs create -f <bucket>/<fs>
|
|
*
|
|
* @param {*} env
|
|
* @param {*} filesystem
|
|
* @param {*} options
|
|
*/
|
|
async create(env, filesystem, options = []) {
|
|
if (!env) {
|
|
env = {};
|
|
}
|
|
const objectivefs = this;
|
|
let args = ["create"];
|
|
args = args.concat(options);
|
|
args = args.concat([filesystem]);
|
|
|
|
let result;
|
|
try {
|
|
result = await objectivefs.exec(
|
|
objectivefs.options.paths.objectivefs,
|
|
args,
|
|
{ env }
|
|
);
|
|
return result;
|
|
} catch (err) {
|
|
if (err.code == 1 && err.stderr.includes("filesystem already exists")) {
|
|
return;
|
|
}
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* echo 'y' | mount.objectivefs destroy <bucket>/<fs>
|
|
*
|
|
* @param {*} env
|
|
* @param {*} filesystem
|
|
* @param {*} options
|
|
*/
|
|
async destroy(env, filesystem, options = []) {
|
|
if (!env) {
|
|
env = {};
|
|
}
|
|
const objectivefs = this;
|
|
let args = ["destroy"];
|
|
args = args.concat(options);
|
|
args = args.concat([filesystem]);
|
|
|
|
let result;
|
|
try {
|
|
result = await objectivefs.exec(
|
|
"/bin/bash",
|
|
[
|
|
"-c",
|
|
`echo y | ${objectivefs.options.paths.objectivefs} ${args.join(" ")}`,
|
|
],
|
|
{ env }
|
|
);
|
|
|
|
return result;
|
|
} catch (err) {
|
|
if (
|
|
err.code == 68 &&
|
|
err.stdout.includes("does not look like an ObjectiveFS filesystem")
|
|
) {
|
|
return;
|
|
}
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
parseListOutput(data) {
|
|
const lines = data.split("\n");
|
|
let headers = [];
|
|
let entries = [];
|
|
lines.forEach((line, i) => {
|
|
if (line.length < 1) {
|
|
return;
|
|
}
|
|
const parts = line.split("\t");
|
|
if (i == 0) {
|
|
headers = parts.map((header) => {
|
|
return header.trim();
|
|
});
|
|
return;
|
|
}
|
|
|
|
let entry = {};
|
|
headers.forEach((name, index) => {
|
|
entry[name.trim()] = parts[index].trim();
|
|
});
|
|
|
|
entries.push(entry);
|
|
});
|
|
|
|
return entries;
|
|
}
|
|
|
|
/**
|
|
* mount.objectivefs list [-asvz] [<filesystem>[@<time>]]
|
|
*
|
|
* @param {*} env
|
|
* @param {*} filesystem
|
|
* @param {*} options
|
|
*/
|
|
async list(env, filesystem = null, options = []) {
|
|
if (!env) {
|
|
env = {};
|
|
}
|
|
const objectivefs = this;
|
|
let args = ["list"];
|
|
args = args.concat(options);
|
|
if (filesystem) {
|
|
args = args.concat([filesystem]);
|
|
}
|
|
|
|
let result;
|
|
try {
|
|
result = await objectivefs.exec(
|
|
objectivefs.options.paths.objectivefs,
|
|
args,
|
|
{ env }
|
|
);
|
|
|
|
return objectivefs.parseListOutput(result.stdout);
|
|
} catch (err) {
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* mount.objectivefs snapshot <filesystem>
|
|
*
|
|
* NOTE: fs must be mount on node to function
|
|
*
|
|
* @param {*} env
|
|
* @param {*} filesystem
|
|
* @param {*} options
|
|
*/
|
|
async snapshot(env, filesystem = null, options = []) {
|
|
if (!env) {
|
|
env = {};
|
|
}
|
|
const objectivefs = this;
|
|
let args = ["list"];
|
|
args = args.concat(options);
|
|
if (filesystem) {
|
|
args = args.concat([filesystem]);
|
|
}
|
|
|
|
let result;
|
|
try {
|
|
// NOTE: Successfully created snapshot: minio://ofs/test@2024-02-13T07:56:38Z (2024-02-13T00:56:38)
|
|
result = await objectivefs.exec(
|
|
objectivefs.options.paths.objectivefs,
|
|
args,
|
|
{ env }
|
|
);
|
|
|
|
return result;
|
|
} catch (err) {
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
exec(command, args, options = {}) {
|
|
if (!options.hasOwnProperty("timeout")) {
|
|
options.timeout = DEFAULT_TIMEOUT;
|
|
}
|
|
|
|
const objectivefs = this;
|
|
args = args || [];
|
|
|
|
if (objectivefs.options.sudo) {
|
|
args.unshift(command);
|
|
command = objectivefs.options.paths.sudo;
|
|
}
|
|
|
|
// OBJECTIVEFS_ENV
|
|
options.env = { ...{}, ...objectivefs.options.env, ...options.env };
|
|
//console.log(options);
|
|
|
|
// truncate admin key during mount operations
|
|
if (options.operation == "mount") {
|
|
delete options.operation;
|
|
// standard license is 24
|
|
// admin key is 8
|
|
if (
|
|
options.env.OBJECTIVEFS_LICENSE &&
|
|
options.env.OBJECTIVEFS_LICENSE.length > 24
|
|
) {
|
|
options.env.OBJECTIVEFS_LICENSE =
|
|
options.env.OBJECTIVEFS_LICENSE.substr(0, 24);
|
|
}
|
|
}
|
|
|
|
const cleansedLog = `${command} ${args.join(" ")}`;
|
|
console.log("executing objectivefs command: %s", cleansedLog);
|
|
|
|
return new Promise((resolve, reject) => {
|
|
let stdin;
|
|
if (options.stdin) {
|
|
stdin = options.stdin;
|
|
delete options.stdin;
|
|
}
|
|
const child = objectivefs.options.executor.spawn(command, args, options);
|
|
if (stdin) {
|
|
child.stdin.write(stdin);
|
|
}
|
|
|
|
let stdout = "";
|
|
let stderr = "";
|
|
|
|
child.stdout.on("data", function (data) {
|
|
stdout = stdout + data;
|
|
});
|
|
|
|
child.stderr.on("data", function (data) {
|
|
stderr = stderr + data;
|
|
});
|
|
|
|
child.on("close", function (code) {
|
|
if (code == 78 && !stderr) {
|
|
stderr += EXIT_CODE_78;
|
|
}
|
|
|
|
if (code == 64 && !stderr) {
|
|
stderr += EXIT_CODE_64;
|
|
}
|
|
|
|
const result = { code, stdout, stderr, timeout: false };
|
|
|
|
// timeout scenario
|
|
if (code === null) {
|
|
result.timeout = true;
|
|
reject(result);
|
|
}
|
|
|
|
if (code) {
|
|
reject(result);
|
|
} else {
|
|
resolve(result);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
module.exports.ObjectiveFS = ObjectiveFS;
|