Compare commits

...

4 Commits

Author SHA1 Message Date
Jan Sušnik e9fe7ea61f
Merge 8ef05936d8 into 55c36d62ff 2025-06-13 14:37:40 -05:00
Travis Glenn Hansen 55c36d62ff newer ubuntu image for ci
Signed-off-by: Travis Glenn Hansen <travisghansen@yahoo.com>
2025-05-30 08:14:54 -06:00
Travis Glenn Hansen edfdf86f2d fix for scale with ssh driver
Signed-off-by: Travis Glenn Hansen <travisghansen@yahoo.com>
2025-05-30 07:59:09 -06:00
Jan Sušnik 8ef05936d8 ZFS and sudo permissions related improvements 2022-10-10 11:26:53 +02:00
7 changed files with 260 additions and 48 deletions

View File

@ -20,7 +20,7 @@ jobs:
access_token: ${{ github.token }}
build-npm-linux-amd64:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4

View File

@ -15,6 +15,15 @@ zfs:
# the example below is useful for TrueNAS 12
#cli:
# sudoEnabled: true
#
# instead of setting sudoEnabled to true, sudo can be
# reduced only to specific (supported) zfs commands
# sudoEnabledCommands:
# - mount
# - unmount
# - share
# - unshare
#
# paths:
# zfs: /usr/local/sbin/zfs
# zpool: /usr/local/sbin/zpool

View File

@ -48,6 +48,7 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver {
}
options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false);
options.sudoCommands = _.get(this.options, "zfs.cli.sudoEnabledCommands", []);
if (typeof this.setZetabyteCustomOptions === "function") {
await this.setZetabyteCustomOptions(options);
@ -92,6 +93,37 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver {
return name;
}
async setShareProperty(zb, datasetName, propertyDict) {
try {
await zb.zfs.set(datasetName, propertyDict);
} catch (err) {
if (
err.toString().includes("unable to reshare") &&
zb.options.sudoCommands.includes("mount")
) {
await zb.zfs.share(datasetName);
} else {
throw err;
}
}
}
async unsetShareProperty(zb, datasetName, propertyKey) {
if (zb.options.sudoCommands.includes("unshare")) {
await zb.zfs.unshare(datasetName, { idempotent: true });
}
try {
await zb.zfs.inherit(datasetName, propertyKey);
} catch (err) {
if (err.toString().includes("dataset does not exist")) {
// do nothing
} else {
throw err;
}
}
}
/**
* should create any necessary share resources
* should set the SHARE_VOLUME_CONTEXT_PROPERTY_NAME propery
@ -118,7 +150,7 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver {
key
]
) {
await zb.zfs.set(datasetName, {
await this.setShareProperty(zb, datasetName, {
[key]:
this.options.nfs.shareStrategySetDatasetProperties
.properties[key],
@ -152,7 +184,7 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver {
key
]
) {
await zb.zfs.set(datasetName, {
await this.setShareProperty(zb, datasetName, {
[key]:
this.options.smb.shareStrategySetDatasetProperties
.properties[key],
@ -572,15 +604,7 @@ save_config filename=${this.options.nvmeof.shareStrategySpdkCli.configPath}
key
]
) {
try {
await zb.zfs.inherit(datasetName, key);
} catch (err) {
if (err.toString().includes("dataset does not exist")) {
// do nothing
} else {
throw err;
}
}
await this.unsetShareProperty(zb, datasetName, key);
}
}
await GeneralUtils.sleep(2000); // let things settle
@ -602,15 +626,7 @@ save_config filename=${this.options.nvmeof.shareStrategySpdkCli.configPath}
key
]
) {
try {
await zb.zfs.inherit(datasetName, key);
} catch (err) {
if (err.toString().includes("dataset does not exist")) {
// do nothing
} else {
throw err;
}
}
await this.unsetShareProperty(zb, datasetName, key);
}
}
await GeneralUtils.sleep(2000); // let things settle

View File

@ -66,6 +66,7 @@ class ControllerZfsLocalDriver extends ControllerZfsBaseDriver {
};
options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false);
options.sudoCommands = _.get(this.options, "zfs.cli.sudoEnabledCommands", []);
if (typeof this.setZetabyteCustomOptions === "function") {
await this.setZetabyteCustomOptions(options);

View File

@ -159,7 +159,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
driver.ctx.logger.verbose("whoami command: %s", command);
const response = await execClient.exec(command);
if (response.code !== 0) {
throw new Error("failed to run uname to determine max zvol name length");
throw new Error("failed to run whoami to determine name of user");
} else {
return response.stdout.trim();
}
@ -499,9 +499,10 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
async setFilesystemMode(path, mode) {
const driver = this;
const execClient = this.getExecClient();
const sudoEnabled = _.get(this.options, "zfs.cli.sudoEnabled", false);
let command = execClient.buildCommand("chmod", [mode, path]);
if ((await driver.getWhoAmI()) != "root") {
if ((await driver.getWhoAmI()) != "root" && sudoEnabled) {
command = (await driver.getSudoPath()) + " " + command;
}

View File

@ -80,6 +80,7 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
}
options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false);
options.sudoCommands = _.get(this.options, "zfs.cli.sudoEnabledCommands", []);
if (typeof this.setZetabyteCustomOptions === "function") {
await this.setZetabyteCustomOptions(options);
@ -120,6 +121,15 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
chroot: "/usr/sbin/chroot",
};
}
if (isScale && Number(majorMinor) >= 25) {
options.paths = {
zfs: "/usr/sbin/zfs",
zpool: "/usr/sbin/zpool",
sudo: "/usr/bin/sudo",
chroot: "/usr/sbin/chroot",
};
}
}
}
@ -142,10 +152,13 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
}
async getTrueNASHttpApiClient() {
return this.ctx.registry.getAsync(`${__REGISTRY_NS__}:api_client`, async () => {
const httpClient = await this.getHttpClient();
return new TrueNASApiClient(httpClient, this.ctx.cache);
});
return this.ctx.registry.getAsync(
`${__REGISTRY_NS__}:api_client`,
async () => {
const httpClient = await this.getHttpClient();
return new TrueNASApiClient(httpClient, this.ctx.cache);
}
);
}
getDriverShareType() {

View File

@ -904,6 +904,13 @@ class Zetabyte {
};
zb.zfs = {
SUDO_COMMANDS: [
"mount",
"unmount",
"share",
"unshare"
],
/**
* zfs create [-pu] [-o property=value]... filesystem
* zfs create [-ps] [-b blocksize] [-o property=value]... -V size volume
@ -913,6 +920,7 @@ class Zetabyte {
*/
create: function (dataset, options = {}) {
if (!(arguments.length >= 1)) throw new (Error("Invalid arguments"))();
const zfs = this;
return new Promise((resolve, reject) => {
const idempotent =
@ -921,11 +929,12 @@ class Zetabyte {
: "idempotent" in zb.options
? zb.options.idempotent
: false;
const shouldSudoMount = !zb.options.sudo && zb.options.sudoCommands.includes("mount");
let args = [];
args.push("create");
if (options.parents) args.push("-p");
if (options.unmounted) args.push("-u");
if (options.unmounted || shouldSudoMount) args.push("-u");
if (options.blocksize) args = args.concat(["-b", options.blocksize]);
if (options.properties) {
for (let [key, value] of Object.entries(options.properties)) {
@ -945,9 +954,17 @@ class Zetabyte {
if (
error &&
!(idempotent && stderr.includes("dataset already exists"))
)
) {
return reject(zb.helpers.zfsError(error, stderr));
return resolve(stdout);
}
if (shouldSudoMount) {
zfs.mount(dataset, { idempotent })
.then(out => resolve(stdout + "; " + out))
.catch(err => reject(err));
} else {
return resolve(stdout);
}
}
);
});
@ -964,6 +981,7 @@ class Zetabyte {
*/
destroy: function (dataset, options = {}) {
if (!(arguments.length >= 1)) throw Error("Invalid arguments");
const zfs = this;
return new Promise((resolve, reject) => {
const idempotent =
@ -985,23 +1003,32 @@ class Zetabyte {
if (options.defer) args.push("-d");
args.push(dataset);
zb.exec(
zb.options.paths.zfs,
args,
{ timeout: zb.options.timeout },
function (error, stdout, stderr) {
if (
error &&
!(
idempotent &&
(stderr.includes("dataset does not exist") ||
stderr.includes("could not find any snapshots to destroy"))
const destroyExec = function() {
zb.exec(
zb.options.paths.zfs,
args,
{ timeout: zb.options.timeout },
function (error, stdout, stderr) {
if (
error &&
!(
idempotent &&
(stderr.includes("dataset does not exist") ||
stderr.includes("could not find any snapshots to destroy"))
)
)
)
return reject(zb.helpers.zfsError(error, stderr));
return resolve(stdout);
}
);
return reject(zb.helpers.zfsError(error, stderr));
return resolve(stdout);
}
);
};
if (zb.options.sudoCommands.includes("unmount")) {
zfs.unmount(dataset, { idempotent })
.then(() => destroyExec())
.catch(err => reject(err));
} else {
destroyExec();
}
});
},
@ -1224,7 +1251,7 @@ class Zetabyte {
let args = [];
args.push("rename");
if (options.parents) args.push("-p");
if (options.unmounted) args.push("-u");
if (options.unmounted || (!zb.options.sudo && zb.options.sudoCommands.includes("mount"))) args.push("-u");
if (options.force) args.push("-f");
if (options.recurse) args.push("-r");
args.push(source);
@ -1522,6 +1549,145 @@ class Zetabyte {
);
});
},
/**
* zfs mount filesystem
*
* @param {*} dataset
* @param {*} options
*/
mount: function (dataset, options = {}) {
if (!(arguments.length >= 1)) throw Error("Invalid arguments");
return new Promise((resolve, reject) => {
const idempotent =
"idempotent" in options
? options.idempotent
: "idempotent" in zb.options
? zb.options.idempotent
: false;
let args = [];
args.push("mount");
args.push(dataset);
zb.exec(
zb.options.paths.zfs,
args,
{ timeout: zb.options.timeout },
function (error, stdout, stderr) {
if (
error &&
!(idempotent && stderr.includes("filesystem already mounted"))
) {
return reject(zb.helpers.zfsError(error, stderr));
}
return resolve(stdout);
}
);
});
},
/**
* zfs unmount filesystem
*
* @param {*} dataset
* @param {*} options
*/
unmount: function (dataset, options = {}) {
if (!(arguments.length >= 1)) throw Error("Invalid arguments");
return new Promise((resolve, reject) => {
const idempotent =
"idempotent" in options
? options.idempotent
: "idempotent" in zb.options
? zb.options.idempotent
: false;
let args = [];
args.push("unmount");
args.push(dataset);
zb.exec(
zb.options.paths.zfs,
args,
{ timeout: zb.options.timeout },
function (error, stdout, stderr) {
if (
error &&
!(idempotent && stderr.includes("not currently mounted"))
) {
return reject(zb.helpers.zfsError(error, stderr));
}
return resolve(stdout);
}
);
});
},
/**
* zfs share filesystem
*
* @param {*} dataset
*/
share: function (dataset) {
if (arguments.length != 1) throw Error("Invalid arguments");
return new Promise((resolve, reject) => {
let args = [];
args.push("share");
args.push(dataset);
zb.exec(
zb.options.paths.zfs,
args,
{ timeout: zb.options.timeout },
function (error, stdout, stderr) {
if (error) return reject(zb.helpers.zfsError(error, stderr));
return resolve(stdout);
}
);
});
},
/**
* zfs unshare filesystem
*
* @param {*} dataset
* @param {*} options
*/
unshare: function (dataset, options = {}) {
if (!(arguments.length >= 1)) throw Error("Invalid arguments");
return new Promise((resolve, reject) => {
const idempotent =
"idempotent" in options
? options.idempotent
: "idempotent" in zb.options
? zb.options.idempotent
: false;
let args = [];
args.push("unshare");
args.push(dataset);
zb.exec(
zb.options.paths.zfs,
args,
{ timeout: zb.options.timeout },
function (error, stdout, stderr) {
if (
error &&
!(idempotent && stderr.includes("not currently shared"))
) {
return reject(zb.helpers.zfsError(error, stderr));
}
return resolve(stdout);
}
);
});
},
};
}
@ -1564,7 +1730,13 @@ class Zetabyte {
use_sudo = options.sudo;
}
if (use_sudo) {
if (
use_sudo || (
Array.isArray(args) && args.length > 0 &&
zb.zfs.SUDO_COMMANDS.includes(args[0]) &&
zb.options.sudoCommands.includes(args[0])
)
) {
args = args || [];
args.unshift(command);
command = zb.options.paths.sudo;