oneclient support

Signed-off-by: Travis Glenn Hansen <travisghansen@yahoo.com>
This commit is contained in:
Travis Glenn Hansen 2022-02-03 01:51:54 -07:00
parent edf3916c71
commit 38199e90b7
5 changed files with 208 additions and 4 deletions

View File

@ -101,6 +101,8 @@ ADD docker/zfs /usr/local/bin/zfs
RUN chmod +x /usr/local/bin/zfs
ADD docker/zpool /usr/local/bin/zpool
RUN chmod +x /usr/local/bin/zpool
ADD docker/oneclient /usr/local/bin/oneclient
RUN chmod +x /usr/local/bin/oneclient
# Run as a non-root user
RUN useradd --create-home csi \

3
docker/oneclient Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
chroot /host /usr/bin/env -i PATH="/sbin:/bin:/usr/bin:/usr/sbin" oneclient "${@:1}"

View File

@ -4,6 +4,7 @@ const os = require("os");
const fs = require("fs");
const { GrpcError, grpc } = require("../utils/grpc");
const { Mount } = require("../utils/mount");
const { OneClient } = require("../utils/oneclient");
const { Filesystem } = require("../utils/filesystem");
const { ISCSI } = require("../utils/iscsi");
const semver = require("semver");
@ -327,11 +328,19 @@ class CsiBaseDriver {
if (normalizedSecrets.mount_flags) {
mount_flags.push(normalizedSecrets.mount_flags);
}
mount_flags.push("defaults");
// https://github.com/karelzak/util-linux/issues/1429
//mount_flags.push("x-democratic-csi.managed");
//mount_flags.push("x-democratic-csi.staged");
switch (node_attach_driver) {
case "oneclient":
// move along
break;
default:
mount_flags.push("defaults");
// https://github.com/karelzak/util-linux/issues/1429
//mount_flags.push("x-democratic-csi.managed");
//mount_flags.push("x-democratic-csi.staged");
break;
}
if (
semver.satisfies(driver.ctx.csiVersion, ">=1.5.0") &&
@ -566,6 +575,45 @@ class CsiBaseDriver {
);
}
}
break;
case "oneclient":
let oneclient = new OneClient();
device = "oneclient";
result = await mount.deviceIsMountedAtPath(device, staging_target_path);
if (result) {
return {};
}
if (volume_context.space_names) {
volume_context.space_names.split(",").forEach((space) => {
mount_flags.push("--space", space);
});
}
if (volume_context.space_ids) {
volume_context.space_ids.split(",").forEach((space) => {
mount_flags.push("--space-id", space);
});
}
if (volume_context.token) {
mount_flags.push("-t", volume_context.token);
}
result = await oneclient.mount(
staging_target_path,
["-H", volume_context.server].concat(mount_flags)
);
if (result) {
return {};
}
throw new GrpcError(
grpc.status.UNKNOWN,
`failed to mount oneclient: ${volume_context.server}`
);
break;
case "zfs-local":
// TODO: make this a geneic zb instance (to ensure works with node-manual driver)

View File

@ -121,6 +121,10 @@ class NodeManualDriver extends CsiBaseDriver {
driverResourceType = "filesystem";
fs_types = ["lustre"];
break;
case "oneclient":
driverResourceType = "filesystem";
fs_types = ["oneclient", "fuse.oneclient"];
break;
case "iscsi":
driverResourceType = "volume";
fs_types = ["ext3", "ext4", "ext4dev", "xfs"];

147
src/utils/oneclient.js Normal file
View File

@ -0,0 +1,147 @@
const cp = require("child_process");
DEFAULT_TIMEOUT = process.env.MOUNT_DEFAULT_TIMEOUT || 30000;
/**
* - https://github.com/onedata/oneclient
*/
class OneClient {
constructor(options = {}) {
const oneclient = this;
oneclient.options = options;
options.paths = options.paths || {};
if (!options.paths.oneclient) {
options.paths.oneclient = "oneclient";
}
if (!options.paths.sudo) {
options.paths.sudo = "/usr/bin/sudo";
}
if (!options.paths.chroot) {
options.paths.chroot = "/usr/sbin/chroot";
}
if (!options.timeout) {
options.timeout = 10 * 60 * 1000;
}
if (!options.executor) {
options.executor = {
spawn: cp.spawn,
};
}
}
/**
* oneclient [options] <directory>
*
* @param {*} target
* @param {*} options
*/
async mount(target, options = []) {
const oneclient = this;
let args = [];
args = args.concat(options);
args = args.concat([target]);
let result;
try {
result = await oneclient.exec(oneclient.options.paths.oneclient, args);
return result;
} catch (err) {
throw err;
}
}
/**
* oneclient -u <directory>
*
* @param {*} target
*/
async umount(target) {
const oneclient = this;
let args = ["-u"];
args.push(target);
try {
await oneclient.exec(oneclient.options.paths.oneclient, args);
} catch (err) {
throw err;
}
return true;
}
exec(command, args, options = {}) {
if (!options.hasOwnProperty("timeout")) {
options.timeout = DEFAULT_TIMEOUT;
}
const oneclient = this;
args = args || [];
let timeout;
let stdout = "";
let stderr = "";
if (oneclient.options.sudo) {
args.unshift(command);
command = oneclient.options.paths.sudo;
}
// replace -t <token> with -t redacted
const regex = /(?<=\-t) (?:[^\s]+)/gi;
const cleansedLog = `${command} ${args.join(" ")}`.replace(
regex,
" redacted"
);
console.log("executing oneclient command: %s", cleansedLog);
const child = oneclient.options.executor.spawn(command, args, options);
/**
* timeout option natively supported since v16
* TODO: properly handle this based on nodejs version
*/
let didTimeout = false;
if (options && options.timeout) {
timeout = setTimeout(() => {
didTimeout = true;
child.kill(options.killSignal || "SIGTERM");
}, options.timeout);
}
return new Promise((resolve, reject) => {
child.stdout.on("data", function (data) {
stdout = stdout + data;
});
child.stderr.on("data", function (data) {
stderr = stderr + data;
});
child.on("close", function (code) {
const result = { code, stdout, stderr, timeout: false };
if (timeout) {
clearTimeout(timeout);
}
// timeout scenario
if (code === null) {
result.timeout = true;
reject(result);
}
if (code) {
reject(result);
} else {
resolve(result);
}
});
});
}
}
module.exports.OneClient = OneClient;