support setting custom binary paths for zfs, support custom dataset properties with templating

This commit is contained in:
Travis Glenn Hansen 2020-09-02 12:17:10 -06:00
parent d6dbbbe951
commit 699b65d931
9 changed files with 144 additions and 12 deletions

View File

@ -18,6 +18,22 @@ sshConnection:
...
-----END RSA PRIVATE KEY-----
zfs:
# can be used to override defaults if necessary
# the example below is useful for TrueNAS 12
#cli:
# paths:
# zfs: /usr/local/sbin/zfs
# zpool: /usr/local/sbin/zpool
# sudo: /usr/local/bin/sudo
# chroot: /usr/sbin/chroot
# can be used to set arbitrary values on the dataset/zvol
# can use handlebars templates with the parameters from the storage class/CO
#datasetProperties:
# "org.freenas:description": "{{ parameters.[csi.storage.k8s.io/pvc/namespace] }}/{{ parameters.[csi.storage.k8s.io/pvc/name] }}"
# "org.freenas:test": "{{ parameters.foo }}"
# "org.freenas:test2": "some value"
# total volume name (zvol/<datasetParentName>/<pvc name>) length cannot exceed 63 chars
# https://www.ixsystems.com/documentation/freenas/11.2-U5/storage.html#zfs-zvol-config-opts-tab
# standard volume naming overhead is 46 chars

View File

@ -18,6 +18,22 @@ sshConnection:
...
-----END RSA PRIVATE KEY-----
zfs:
# can be used to override defaults if necessary
# the example below is useful for TrueNAS 12
#cli:
# paths:
# zfs: /usr/local/sbin/zfs
# zpool: /usr/local/sbin/zpool
# sudo: /usr/local/bin/sudo
# chroot: /usr/sbin/chroot
# can be used to set arbitrary values on the dataset/zvol
# can use handlebars templates with the parameters from the storage class/CO
#datasetProperties:
# "org.freenas:description": "{{ parameters.[csi.storage.k8s.io/pvc/namespace] }}/{{ parameters.[csi.storage.k8s.io/pvc/name] }}"
# "org.freenas:test": "{{ parameters.foo }}"
# "org.freenas:test2": "some value"
datasetParentName: tank/k8s/a/vols
detachedSnapshotsDatasetParentName: tank/k8s/a/snaps
datasetEnableQuotas: true

View File

@ -3,6 +3,7 @@ instance_id:
nfs:
shareHost: server address
shareBasePath: "/some/path"
# shareHost:shareBasePath should be mounted at this location in the controller container
controllerBasePath: "/storage"
dirPermissionsMode: "0777"
dirPermissionsUser: root

View File

@ -14,6 +14,22 @@ service:
controller: {}
node: {}
zfs:
# can be used to override defaults if necessary
# the example below is useful for TrueNAS 12
#cli:
# paths:
# zfs: /usr/local/sbin/zfs
# zpool: /usr/local/sbin/zpool
# sudo: /usr/local/bin/sudo
# chroot: /usr/sbin/chroot
# can be used to set arbitrary values on the dataset/zvol
# can use handlebars templates with the parameters from the storage class/CO
#datasetProperties:
# "org.freenas:description": "{{ parameters.[csi.storage.k8s.io/pvc/namespace] }}/{{ parameters.[csi.storage.k8s.io/pvc/name] }}"
# "org.freenas:test": "{{ parameters.foo }}"
# "org.freenas:test2": "some value"
datasetParentName: tank/k8s/test
detachedSnapshotsDatasetParentName: tanks/k8s/test-snapshots

View File

@ -14,6 +14,22 @@ service:
controller: {}
node: {}
zfs:
# can be used to override defaults if necessary
# the example below is useful for TrueNAS 12
#cli:
# paths:
# zfs: /usr/local/sbin/zfs
# zpool: /usr/local/sbin/zpool
# sudo: /usr/local/bin/sudo
# chroot: /usr/sbin/chroot
# can be used to set arbitrary values on the dataset/zvol
# can use handlebars templates with the parameters from the storage class/CO
#datasetProperties:
# "org.freenas:description": "{{ parameters.[csi.storage.k8s.io/pvc/namespace] }}/{{ parameters.[csi.storage.k8s.io/pvc/name] }}"
# "org.freenas:test": "{{ parameters.foo }}"
# "org.freenas:test2": "some value"
datasetParentName: tank/k8s/test
detachedSnapshotsDatasetParentName: tanks/k8s/test-snapshots

33
package-lock.json generated
View File

@ -820,6 +820,18 @@
"protobufjs": "^5.0.3"
}
},
"handlebars": {
"version": "4.7.6",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz",
"integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==",
"requires": {
"minimist": "^1.2.5",
"neo-async": "^2.6.0",
"source-map": "^0.6.1",
"uglify-js": "^3.1.4",
"wordwrap": "^1.0.0"
}
},
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
@ -1127,6 +1139,11 @@
"integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=",
"optional": true
},
"neo-async": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
},
"number-is-nan": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
@ -1502,6 +1519,11 @@
}
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@ -1696,6 +1718,12 @@
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
"integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="
},
"uglify-js": {
"version": "3.10.3",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.3.tgz",
"integrity": "sha512-Lh00i69Uf6G74mvYpHCI9KVVXLcHW/xu79YTvH7Mkc9zyKUeSPz0owW0dguj0Scavns3ZOh3wY63J0Zb97Za2g==",
"optional": true
},
"uri-js": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
@ -1806,6 +1834,11 @@
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="
},
"wordwrap": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
},
"wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",

View File

@ -22,6 +22,7 @@
"bunyan": "^1.8.14",
"eslint": "^7.4.0",
"grpc-uds": "^0.1.4",
"handlebars": "^4.7.6",
"js-yaml": "^3.14.0",
"lru-cache": "^5.1.1",
"request": "^2.88.2",

View File

@ -3,6 +3,8 @@ const SshClient = require("../../utils/ssh").SshClient;
const { GrpcError, grpc } = require("../../utils/grpc");
const { Zetabyte, ZfsSshProcessManager } = require("../../utils/zfs");
const Handlebars = require("handlebars");
const uuidv4 = require("uuid").v4;
// zfs common properties
@ -122,10 +124,18 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver {
getZetabyte() {
const sshClient = this.getSshClient();
return new Zetabyte({
executor: new ZfsSshProcessManager(sshClient),
idempotent: true,
});
const options = {};
options.executor = new ZfsSshProcessManager(sshClient);
options.idempotent = true;
if (
this.options.zfs.hasOwnProperty("cli") &&
this.options.zfs.cli.hasOwnProperty("paths")
) {
options.paths = this.options.zfs.cli.paths;
}
return new Zetabyte(options);
}
getDatasetParentName() {
@ -335,6 +345,20 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver {
let volume_content_source_volume_id;
let fullSnapshotName;
let volumeProperties = {};
// user-supplied properties
// put early to prevent stupid (user-supplied values overwriting system values)
if (driver.options.zfs.datasetProperties) {
for (let property in driver.options.zfs.datasetProperties) {
let value = driver.options.zfs.datasetProperties[property];
const template = Handlebars.compile(value);
volumeProperties[property] = template({
parameters: call.request.parameters,
});
}
}
volumeProperties[VOLUME_CSI_NAME_PROPERTY_NAME] = name;
volumeProperties[MANAGED_PROPERTY_NAME] = "true";
volumeProperties[VOLUME_CONTEXT_PROVISIONER_DRIVER_PROPERTY_NAME] =
@ -346,7 +370,6 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver {
// TODO: also set access_mode as property?
// TODO: also set fsType as property?
// TODO: allow for users to configure arbitrary/custom properties to add
// zvol enables reservation by default
// this implements 'sparse' zvols
@ -691,8 +714,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver {
volume_context = await this.createShare(call, datasetName);
await zb.zfs.set(datasetName, {
[SHARE_VOLUME_CONTEXT_PROPERTY_NAME]:
"'" + JSON.stringify(volume_context) + "'",
[SHARE_VOLUME_CONTEXT_PROPERTY_NAME]: JSON.stringify(volume_context),
});
volume_context["provisioner_driver"] = driver.options.driver;

View File

@ -1,6 +1,11 @@
const events = require("events");
const cp = require("child_process");
const escapeShell = function (cmd) {
cmd = String(cmd);
return '"' + cmd.replace(/(["$`\\])/g, "\\$1") + '"';
};
class Zetabyte {
constructor(options = {}) {
const zb = this;
@ -283,7 +288,8 @@ class Zetabyte {
}
}
if (options.fsProperties) {
for (const [key, value] of Object.entries(options.fsProperties)) {
for (let [key, value] of Object.entries(options.fsProperties)) {
value = escapeShell(value);
args.push("-O");
args.push(`${key}=${value}`);
}
@ -774,6 +780,7 @@ class Zetabyte {
* @param {*} value
*/
set: function (pool, property, value) {
value = escapeShell(value);
return new Promise((resolve, reject) => {
let args = [];
args.push("set");
@ -913,7 +920,8 @@ class Zetabyte {
if (options.unmounted) args.push("-u");
if (options.blocksize) args = args.concat(["-b", options.blocksize]);
if (options.properties) {
for (const [key, value] of Object.entries(options.properties)) {
for (let [key, value] of Object.entries(options.properties)) {
value = escapeShell(value);
args.push("-o");
args.push(`${key}=${value}`);
}
@ -1012,7 +1020,8 @@ class Zetabyte {
args.push("snapshot");
if (options.recurse) args.push("-r");
if (options.properties) {
for (const [key, value] of Object.entries(options.properties)) {
for (let [key, value] of Object.entries(options.properties)) {
value = escapeShell(value);
args.push("-o");
args.push(`${key}=${value}`);
}
@ -1093,7 +1102,8 @@ class Zetabyte {
args.push("clone");
if (options.parents) args.push("-p");
if (options.properties) {
for (const [key, value] of Object.entries(options.properties)) {
for (let [key, value] of Object.entries(options.properties)) {
value = escapeShell(value);
args.push("-o");
args.push(`${key}=${value}`);
}
@ -1301,7 +1311,8 @@ class Zetabyte {
args.push("set");
if (properties) {
for (const [key, value] of Object.entries(properties)) {
for (let [key, value] of Object.entries(properties)) {
value = escapeShell(value);
args.push(`${key}=${value}`);
}
}