commit
35181ae715
|
|
@ -254,6 +254,7 @@ jobs:
|
||||||
chmod a+x ~/.docker/cli-plugins/docker-buildx
|
chmod a+x ~/.docker/cli-plugins/docker-buildx
|
||||||
docker info
|
docker info
|
||||||
docker buildx version
|
docker buildx version
|
||||||
|
docker buildx ls
|
||||||
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||||
docker buildx create --name xbuilder --use
|
docker buildx create --name xbuilder --use
|
||||||
docker buildx inspect --bootstrap
|
docker buildx inspect --bootstrap
|
||||||
|
|
@ -265,4 +266,4 @@ jobs:
|
||||||
GHCR_USERNAME: ${{ secrets.GHCR_USERNAME }}
|
GHCR_USERNAME: ${{ secrets.GHCR_USERNAME }}
|
||||||
GHCR_PASSWORD: ${{ secrets.GHCR_PASSWORD }}
|
GHCR_PASSWORD: ${{ secrets.GHCR_PASSWORD }}
|
||||||
DOCKER_CLI_EXPERIMENTAL: enabled
|
DOCKER_CLI_EXPERIMENTAL: enabled
|
||||||
DOCKER_BUILD_PLATFORM: linux/amd64,linux/arm64,linux/arm/v7
|
DOCKER_BUILD_PLATFORM: linux/amd64,linux/arm64,linux/arm/v7,linux/s390x,linux/ppc64le
|
||||||
|
|
|
||||||
27
CHANGELOG.md
27
CHANGELOG.md
|
|
@ -1,3 +1,30 @@
|
||||||
|
# v1.6.0
|
||||||
|
|
||||||
|
Released 2022-03-
|
||||||
|
|
||||||
|
This is a **massive** release with substantial changes. Ideally this release
|
||||||
|
should be installed with chart version `>=0.11.0`. Make note that due to the
|
||||||
|
updated base image from `buster` to `bullseye` that the filesystem tools have
|
||||||
|
all been updated as well (`mkfs.foo`, `resize2fs`, `fsck.foo`, etc).
|
||||||
|
|
||||||
|
To facilitate the removal `grpc-uds` package a new sister project was created:
|
||||||
|
|
||||||
|
https://github.com/democratic-csi/csi-grpc-proxy
|
||||||
|
|
||||||
|
Not all environments require the usage of the proxy, but it is enabled by
|
||||||
|
default with `helm` chart versions `>=0.11.0`.
|
||||||
|
|
||||||
|
- update `nodejs` version to `v16`
|
||||||
|
- remove dependency on `grpc-uds` package (replaced entirely by
|
||||||
|
`@grpc/grpc-js`)
|
||||||
|
- remove dependency on `request` package (replaced by `axios`)
|
||||||
|
- use native `timeout` functionality for `spawn` operations
|
||||||
|
- update http clients to use `keep-alive` logic
|
||||||
|
- add a default 30s `timeout` to `iscsiadm` commands
|
||||||
|
- base docker image on `bullseye`
|
||||||
|
- support for `btrfs` as a `fs_type`
|
||||||
|
- support `s390x` and `ppc64le` docker images
|
||||||
|
|
||||||
# v1.5.4
|
# v1.5.4
|
||||||
|
|
||||||
Released 2022-03-03
|
Released 2022-03-03
|
||||||
|
|
|
||||||
12
Dockerfile
12
Dockerfile
|
|
@ -1,4 +1,4 @@
|
||||||
FROM debian:10-slim AS build
|
FROM debian:11-slim AS build
|
||||||
#FROM --platform=$BUILDPLATFORM debian:10-slim AS build
|
#FROM --platform=$BUILDPLATFORM debian:10-slim AS build
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
@ -12,8 +12,8 @@ RUN apt-get update && apt-get install -y locales && rm -rf /var/lib/apt/lists/*
|
||||||
&& localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
|
&& localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
|
||||||
|
|
||||||
ENV LANG=en_US.utf8
|
ENV LANG=en_US.utf8
|
||||||
ENV NODE_VERSION=v12.22.6
|
#ENV NODE_VERSION=v12.22.6
|
||||||
#ENV NODE_VERSION=v14.15.1
|
ENV NODE_VERSION=v16.14.0
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
# install build deps
|
# install build deps
|
||||||
|
|
@ -32,7 +32,7 @@ RUN useradd --create-home csi \
|
||||||
WORKDIR /home/csi/app
|
WORKDIR /home/csi/app
|
||||||
USER csi
|
USER csi
|
||||||
|
|
||||||
COPY package*.json ./
|
COPY --chown=csi:csi package*.json ./
|
||||||
RUN npm install --grpc_node_binary_host_mirror=https://grpc-uds-binaries.s3-us-west-2.amazonaws.com/debian-buster
|
RUN npm install --grpc_node_binary_host_mirror=https://grpc-uds-binaries.s3-us-west-2.amazonaws.com/debian-buster
|
||||||
COPY --chown=csi:csi . .
|
COPY --chown=csi:csi . .
|
||||||
RUN rm -rf docker
|
RUN rm -rf docker
|
||||||
|
|
@ -41,7 +41,7 @@ RUN rm -rf docker
|
||||||
######################
|
######################
|
||||||
# actual image
|
# actual image
|
||||||
######################
|
######################
|
||||||
FROM debian:10-slim
|
FROM debian:11-slim
|
||||||
|
|
||||||
LABEL org.opencontainers.image.source https://github.com/democratic-csi/democratic-csi
|
LABEL org.opencontainers.image.source https://github.com/democratic-csi/democratic-csi
|
||||||
|
|
||||||
|
|
@ -74,7 +74,7 @@ COPY --from=build /usr/local/lib/nodejs/bin/node /usr/local/bin/node
|
||||||
# netbase is required by rpcbind/rpcinfo to work properly
|
# netbase is required by rpcbind/rpcinfo to work properly
|
||||||
# /etc/{services,rpc} are required
|
# /etc/{services,rpc} are required
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y netbase socat e2fsprogs xfsprogs fatresize dosfstools nfs-common cifs-utils sudo && \
|
apt-get install -y netbase socat e2fsprogs xfsprogs btrfs-progs fatresize dosfstools nfs-common cifs-utils sudo && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# controller requirements
|
# controller requirements
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
const yaml = require("js-yaml");
|
const yaml = require("js-yaml");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
|
const { grpc } = require("../src/utils/grpc");
|
||||||
|
const { stringify } = require("../src/utils/general");
|
||||||
|
|
||||||
let options;
|
let options;
|
||||||
const args = require("yargs")
|
const args = require("yargs")
|
||||||
|
|
@ -78,8 +80,6 @@ if (!args.serverSocket && !args.serverAddress && !args.serverPort) {
|
||||||
const package = require("../package.json");
|
const package = require("../package.json");
|
||||||
args.version = package.version;
|
args.version = package.version;
|
||||||
|
|
||||||
//const grpc = require("grpc");
|
|
||||||
const grpc = require("grpc-uds");
|
|
||||||
const protoLoader = require("@grpc/proto-loader");
|
const protoLoader = require("@grpc/proto-loader");
|
||||||
const LRU = require("lru-cache");
|
const LRU = require("lru-cache");
|
||||||
const cache = new LRU({ max: 500 });
|
const cache = new LRU({ max: 500 });
|
||||||
|
|
@ -91,7 +91,7 @@ if (args.logLevel) {
|
||||||
logger.level = args.logLevel;
|
logger.level = args.logLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
const csiVersion = process.env.CSI_VERSION || args.csiVersion || "1.2.0";
|
const csiVersion = process.env.CSI_VERSION || args.csiVersion || "1.5.0";
|
||||||
const PROTO_PATH = __dirname + "/../csi_proto/csi-v" + csiVersion + ".proto";
|
const PROTO_PATH = __dirname + "/../csi_proto/csi-v" + csiVersion + ".proto";
|
||||||
|
|
||||||
// Suggested options for similarity to existing grpc.load behavior
|
// Suggested options for similarity to existing grpc.load behavior
|
||||||
|
|
@ -128,7 +128,7 @@ try {
|
||||||
let operationLock = new Set();
|
let operationLock = new Set();
|
||||||
|
|
||||||
async function requestHandlerProxy(call, callback, serviceMethodName) {
|
async function requestHandlerProxy(call, callback, serviceMethodName) {
|
||||||
const cleansedCall = JSON.parse(JSON.stringify(call));
|
const cleansedCall = JSON.parse(stringify(call));
|
||||||
for (const key in cleansedCall.request) {
|
for (const key in cleansedCall.request) {
|
||||||
if (key.includes("secret")) {
|
if (key.includes("secret")) {
|
||||||
cleansedCall.request[key] = "redacted";
|
cleansedCall.request[key] = "redacted";
|
||||||
|
|
@ -335,27 +335,71 @@ logger.info(
|
||||||
bindSocket
|
bindSocket
|
||||||
);
|
);
|
||||||
|
|
||||||
if (bindAddress) {
|
|
||||||
csiServer.bind(bindAddress, grpc.ServerCredentials.createInsecure());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bindSocket) {
|
|
||||||
csiServer.bind(bindSocket, grpc.ServerCredentials.createInsecure());
|
|
||||||
}
|
|
||||||
|
|
||||||
csiServer.start();
|
|
||||||
|
|
||||||
[`SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`].forEach(
|
[`SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`].forEach(
|
||||||
(eventType) => {
|
(eventType) => {
|
||||||
process.on(eventType, (code) => {
|
process.on(eventType, (code) => {
|
||||||
console.log(`running server shutdown, exit code: ${code}`);
|
console.log(`running server shutdown, exit code: ${code}`);
|
||||||
let socketPath = args.serverSocket || "";
|
let socketPath = bindSocket;
|
||||||
socketPath = socketPath.replace(/^unix:\/\//g, "");
|
socketPath = socketPath.replace(/^unix:\/\//g, "");
|
||||||
if (socketPath && fs.existsSync(socketPath)) {
|
if (socketPath && fs.existsSync(socketPath)) {
|
||||||
fs.unlinkSync(socketPath);
|
let fsStat = fs.statSync(socketPath);
|
||||||
|
if (fsStat.isSocket()) {
|
||||||
|
fs.unlinkSync(socketPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
process.exit(code);
|
process.exit(code);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
(async function () {
|
||||||
|
try {
|
||||||
|
if (bindAddress) {
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
csiServer.bindAsync(
|
||||||
|
bindAddress,
|
||||||
|
grpc.ServerCredentials.createInsecure(),
|
||||||
|
(err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bindSocket) {
|
||||||
|
let socketPath = bindSocket;
|
||||||
|
socketPath = socketPath.replace(/^unix:\/\//g, "");
|
||||||
|
if (socketPath && fs.existsSync(socketPath)) {
|
||||||
|
let fsStat = fs.statSync(socketPath);
|
||||||
|
if (fsStat.isSocket()) {
|
||||||
|
fs.unlinkSync(socketPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
csiServer.bindAsync(
|
||||||
|
bindSocket,
|
||||||
|
grpc.ServerCredentials.createInsecure(),
|
||||||
|
(err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
csiServer.start();
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
#!/usr/bin/env -S node --nouse-idle-notification --expose-gc
|
#!/usr/bin/env -S node --nouse-idle-notification --expose-gc
|
||||||
|
|
||||||
const yaml = require("js-yaml");
|
const { grpc } = require("../src/utils/grpc");
|
||||||
const fs = require("fs");
|
|
||||||
|
|
||||||
let options;
|
|
||||||
const args = require("yargs")
|
const args = require("yargs")
|
||||||
.env("DEMOCRATIC_CSI_LIVENESS_PROBE")
|
.env("DEMOCRATIC_CSI_LIVENESS_PROBE")
|
||||||
.scriptName("liveness-probe")
|
.scriptName("liveness-probe")
|
||||||
|
|
@ -33,10 +31,8 @@ const args = require("yargs")
|
||||||
const package = require("../package.json");
|
const package = require("../package.json");
|
||||||
args.version = package.version;
|
args.version = package.version;
|
||||||
|
|
||||||
//const grpc = require("grpc");
|
|
||||||
const grpc = require("grpc-uds");
|
|
||||||
const protoLoader = require("@grpc/proto-loader");
|
const protoLoader = require("@grpc/proto-loader");
|
||||||
const csiVersion = process.env.CSI_VERSION || args.csiVersion || "1.2.0";
|
const csiVersion = process.env.CSI_VERSION || args.csiVersion || "1.5.0";
|
||||||
const PROTO_PATH = __dirname + "/../csi_proto/csi-v" + csiVersion + ".proto";
|
const PROTO_PATH = __dirname + "/../csi_proto/csi-v" + csiVersion + ".proto";
|
||||||
|
|
||||||
// Suggested options for similarity to existing grpc.load behavior
|
// Suggested options for similarity to existing grpc.load behavior
|
||||||
|
|
@ -51,8 +47,18 @@ const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
|
||||||
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
|
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
|
||||||
const csi = protoDescriptor.csi.v1;
|
const csi = protoDescriptor.csi.v1;
|
||||||
|
|
||||||
|
let csiAddress = args.csiAddress;
|
||||||
|
const tcpRegex = /[^\:]+:[0-9]*$/;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!tcpRegex.test(csiAddress) &&
|
||||||
|
!csiAddress.toLowerCase().startsWith("unix://")
|
||||||
|
) {
|
||||||
|
csiAddress = "unix://" + csiAddress;
|
||||||
|
}
|
||||||
|
|
||||||
const clientIdentity = new csi.Identity(
|
const clientIdentity = new csi.Identity(
|
||||||
args.csiAddress,
|
csiAddress,
|
||||||
grpc.credentials.createInsecure()
|
grpc.credentials.createInsecure()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,10 @@ set -e
|
||||||
set -x
|
set -x
|
||||||
|
|
||||||
export PATH="/usr/local/lib/nodejs/bin:${PATH}"
|
export PATH="/usr/local/lib/nodejs/bin:${PATH}"
|
||||||
|
|
||||||
|
node --version
|
||||||
|
npm --version
|
||||||
|
|
||||||
# install deps
|
# install deps
|
||||||
npm i
|
npm i
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ container_supported_filesystems=(
|
||||||
"ext3"
|
"ext3"
|
||||||
"ext4"
|
"ext4"
|
||||||
"ext4dev"
|
"ext4dev"
|
||||||
|
"btrfs"
|
||||||
"xfs"
|
"xfs"
|
||||||
"vfat"
|
"vfat"
|
||||||
"nfs"
|
"nfs"
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,17 @@ if [[ "x${PLATFORM}" == "x" ]]; then
|
||||||
PLATFORM="linux/amd64"
|
PLATFORM="linux/amd64"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# these come from the --platform option of buildx, indirectly from DOCKER_BUILD_PLATFORM in main.yaml
|
||||||
if [ "$PLATFORM" = "linux/amd64" ]; then
|
if [ "$PLATFORM" = "linux/amd64" ]; then
|
||||||
export NODE_DISTRO="linux-x64"
|
export NODE_DISTRO="linux-x64"
|
||||||
elif [ "$PLATFORM" = "linux/arm64" ]; then
|
elif [ "$PLATFORM" = "linux/arm64" ]; then
|
||||||
export NODE_DISTRO="linux-arm64"
|
export NODE_DISTRO="linux-arm64"
|
||||||
elif [ "$PLATFORM" = "linux/arm/v7" ]; then
|
elif [ "$PLATFORM" = "linux/arm/v7" ]; then
|
||||||
export NODE_DISTRO="linux-armv7l"
|
export NODE_DISTRO="linux-armv7l"
|
||||||
|
elif [ "$PLATFORM" = "linux/s390x" ]; then
|
||||||
|
export NODE_DISTRO="linux-s390x"
|
||||||
|
elif [ "$PLATFORM" = "linux/ppc64le" ]; then
|
||||||
|
export NODE_DISTRO="linux-ppc64le"
|
||||||
else
|
else
|
||||||
echo "unsupported/unknown PLATFORM ${PLATFORM}"
|
echo "unsupported/unknown PLATFORM ${PLATFORM}"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ container_supported_filesystems=(
|
||||||
"ext3"
|
"ext3"
|
||||||
"ext4"
|
"ext4"
|
||||||
"ext4dev"
|
"ext4dev"
|
||||||
|
"btrfs"
|
||||||
"xfs"
|
"xfs"
|
||||||
"vfat"
|
"vfat"
|
||||||
"nfs"
|
"nfs"
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,10 @@ node:
|
||||||
enabled: false
|
enabled: false
|
||||||
customOptions: []
|
customOptions: []
|
||||||
customFilesystemOptions: []
|
customFilesystemOptions: []
|
||||||
|
btrfs:
|
||||||
|
enabled: false
|
||||||
|
customOptions: []
|
||||||
|
customFilesystemOptions: []
|
||||||
format:
|
format:
|
||||||
xfs:
|
xfs:
|
||||||
customOptions: []
|
customOptions: []
|
||||||
|
|
@ -21,3 +25,5 @@ node:
|
||||||
#- -E
|
#- -E
|
||||||
#- nodiscard
|
#- nodiscard
|
||||||
# ...
|
# ...
|
||||||
|
btrfs:
|
||||||
|
customOptions: []
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "democratic-csi",
|
"name": "democratic-csi",
|
||||||
"version": "1.5.4",
|
"version": "1.6.0",
|
||||||
"description": "kubernetes csi driver framework",
|
"description": "kubernetes csi driver framework",
|
||||||
"main": "bin/democratic-csi",
|
"main": "bin/democratic-csi",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
@ -22,14 +22,13 @@
|
||||||
"@grpc/proto-loader": "^0.6.0",
|
"@grpc/proto-loader": "^0.6.0",
|
||||||
"@kubernetes/client-node": "^0.16.3",
|
"@kubernetes/client-node": "^0.16.3",
|
||||||
"async-mutex": "^0.3.1",
|
"async-mutex": "^0.3.1",
|
||||||
|
"axios": "^0.26.1",
|
||||||
"bunyan": "^1.8.15",
|
"bunyan": "^1.8.15",
|
||||||
"grpc-uds": "^0.1.6",
|
|
||||||
"handlebars": "^4.7.7",
|
"handlebars": "^4.7.7",
|
||||||
"js-yaml": "^4.0.0",
|
"js-yaml": "^4.0.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lru-cache": "^7.4.0",
|
"lru-cache": "^7.4.0",
|
||||||
"prompt": "^1.2.2",
|
"prompt": "^1.2.2",
|
||||||
"request": "^2.88.2",
|
|
||||||
"semver": "^7.3.4",
|
"semver": "^7.3.4",
|
||||||
"ssh2": "^1.1.0",
|
"ssh2": "^1.1.0",
|
||||||
"uri-js": "^4.4.1",
|
"uri-js": "^4.4.1",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
const request = require("request");
|
const _ = require("lodash");
|
||||||
|
const http = require("http");
|
||||||
|
const https = require("https");
|
||||||
|
const { axios_request, stringify } = require("../../../utils/general");
|
||||||
const Mutex = require("async-mutex").Mutex;
|
const Mutex = require("async-mutex").Mutex;
|
||||||
|
|
||||||
const USER_AGENT = "democratic-csi";
|
const USER_AGENT = "democratic-csi";
|
||||||
|
|
@ -18,34 +21,57 @@ class SynologyHttpClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async login() {
|
getHttpAgent() {
|
||||||
if (!this.sid) {
|
if (!this.httpAgent) {
|
||||||
// See https://global.download.synology.com/download/Document/Software/DeveloperGuide/Os/DSM/All/enu/DSM_Login_Web_API_Guide_enu.pdf
|
this.httpAgent = new http.Agent({
|
||||||
const data = {
|
keepAlive: true,
|
||||||
api: "SYNO.API.Auth",
|
maxSockets: Infinity,
|
||||||
version: "6",
|
rejectUnauthorized: !!!this.options.allowInsecure,
|
||||||
method: "login",
|
});
|
||||||
account: this.options.username,
|
|
||||||
passwd: this.options.password,
|
|
||||||
session: this.options.session,
|
|
||||||
format: "sid",
|
|
||||||
};
|
|
||||||
|
|
||||||
let response = await this.do_request("GET", "auth.cgi", data);
|
|
||||||
this.sid = response.body.data.sid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.sid;
|
return this.httpAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
getHttpsAgent() {
|
||||||
|
if (!this.httpsAgent) {
|
||||||
|
this.httpsAgent = new https.Agent({
|
||||||
|
keepAlive: true,
|
||||||
|
maxSockets: Infinity,
|
||||||
|
rejectUnauthorized: !!!this.options.allowInsecure,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.httpsAgent;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_response(error, response, body, options) {
|
log_response(error, response, body, options) {
|
||||||
this.logger.debug("SYNOLOGY HTTP REQUEST: " + JSON.stringify(options));
|
let prop;
|
||||||
|
let val;
|
||||||
|
|
||||||
|
prop = "auth.username";
|
||||||
|
val = _.get(options, prop, false);
|
||||||
|
if (val) {
|
||||||
|
_.set(options, prop, "redacted");
|
||||||
|
}
|
||||||
|
|
||||||
|
prop = "auth.password";
|
||||||
|
val = _.get(options, prop, false);
|
||||||
|
if (val) {
|
||||||
|
_.set(options, prop, "redacted");
|
||||||
|
}
|
||||||
|
|
||||||
|
prop = "headers.Authorization";
|
||||||
|
val = _.get(options, prop, false);
|
||||||
|
if (val) {
|
||||||
|
_.set(options, prop, "redacted");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.debug("SYNOLOGY HTTP REQUEST: " + stringify(options));
|
||||||
this.logger.debug("SYNOLOGY HTTP ERROR: " + error);
|
this.logger.debug("SYNOLOGY HTTP ERROR: " + error);
|
||||||
this.logger.debug("SYNOLOGY HTTP STATUS: " + response.statusCode);
|
this.logger.debug("SYNOLOGY HTTP STATUS: " + response.statusCode);
|
||||||
this.logger.debug(
|
this.logger.debug("SYNOLOGY HTTP HEADERS: " + stringify(response.headers));
|
||||||
"SYNOLOGY HTTP HEADERS: " + JSON.stringify(response.headers)
|
this.logger.debug("SYNOLOGY HTTP BODY: " + stringify(body));
|
||||||
);
|
|
||||||
this.logger.debug("SYNOLOGY HTTP BODY: " + JSON.stringify(body));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async do_request(method, path, data = {}, options = {}) {
|
async do_request(method, path, data = {}, options = {}) {
|
||||||
|
|
@ -82,10 +108,10 @@ class SynologyHttpClient {
|
||||||
? "application/x-www-form-urlencoded"
|
? "application/x-www-form-urlencoded"
|
||||||
: "application/json",
|
: "application/json",
|
||||||
},
|
},
|
||||||
json: invoke_options.use_form_encoded ? false : true,
|
responseType: "json",
|
||||||
agentOptions: {
|
httpAgent: this.getHttpAgent(),
|
||||||
rejectUnauthorized: !!!client.options.allowInsecure,
|
httpsAgent: this.getHttpsAgent(),
|
||||||
},
|
timeout: 60 * 1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (method) {
|
switch (method) {
|
||||||
|
|
@ -96,20 +122,19 @@ class SynologyHttpClient {
|
||||||
qsData[p] = JSON.stringify(qsData[p]);
|
qsData[p] = JSON.stringify(qsData[p]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
options.qs = qsData;
|
options.params = qsData;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (invoke_options.use_form_encoded) {
|
if (invoke_options.use_form_encoded) {
|
||||||
//options.body = URLSearchParams(data);
|
options.data = URLSearchParams(data).toString();
|
||||||
options.form = data;
|
|
||||||
} else {
|
} else {
|
||||||
options.body = data;
|
options.data = data;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
request(options, function (error, response, body) {
|
axios_request(options, function (error, response, body) {
|
||||||
client.log_response(...arguments, options);
|
client.log_response(...arguments, options);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|
@ -147,6 +172,26 @@ class SynologyHttpClient {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async login() {
|
||||||
|
if (!this.sid) {
|
||||||
|
// See https://global.download.synology.com/download/Document/Software/DeveloperGuide/Os/DSM/All/enu/DSM_Login_Web_API_Guide_enu.pdf
|
||||||
|
const data = {
|
||||||
|
api: "SYNO.API.Auth",
|
||||||
|
version: "6",
|
||||||
|
method: "login",
|
||||||
|
account: this.options.username,
|
||||||
|
passwd: this.options.password,
|
||||||
|
session: this.options.session,
|
||||||
|
format: "sid",
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = await this.do_request("GET", "auth.cgi", data);
|
||||||
|
this.sid = response.body.data.sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.sid;
|
||||||
|
}
|
||||||
|
|
||||||
async GetLuns() {
|
async GetLuns() {
|
||||||
const lun_list = {
|
const lun_list = {
|
||||||
api: "SYNO.Core.ISCSI.LUN",
|
api: "SYNO.Core.ISCSI.LUN",
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ class ControllerSynologyDriver extends CsiBaseDriver {
|
||||||
if (capability.access_type == "mount") {
|
if (capability.access_type == "mount") {
|
||||||
if (
|
if (
|
||||||
capability.mount.fs_type &&
|
capability.mount.fs_type &&
|
||||||
!["ext3", "ext4", "ext4dev", "xfs"].includes(
|
!["btrfs", "ext3", "ext4", "ext4dev", "xfs"].includes(
|
||||||
capability.mount.fs_type
|
capability.mount.fs_type
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ class ControllerZfsLocalDriver extends ControllerZfsBaseDriver {
|
||||||
case "filesystem":
|
case "filesystem":
|
||||||
return ["zfs"];
|
return ["zfs"];
|
||||||
case "volume":
|
case "volume":
|
||||||
return ["ext3", "ext4", "ext4dev", "xfs"];
|
return ["btrfs", "ext3", "ext4", "ext4dev", "xfs"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ const VOLUME_CONTEXT_PROVISIONER_DRIVER_PROPERTY_NAME =
|
||||||
const VOLUME_CONTEXT_PROVISIONER_INSTANCE_ID_PROPERTY_NAME =
|
const VOLUME_CONTEXT_PROVISIONER_INSTANCE_ID_PROPERTY_NAME =
|
||||||
"democratic-csi:volume_context_provisioner_instance_id";
|
"democratic-csi:volume_context_provisioner_instance_id";
|
||||||
|
|
||||||
|
const MAX_ZVOL_NAME_LENGTH_CACHE_KEY = "controller-zfs:max_zvol_name_length";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base driver to provisin zfs assets using zfs cli commands.
|
* Base driver to provisin zfs assets using zfs cli commands.
|
||||||
* Derived drivers only need to implement:
|
* Derived drivers only need to implement:
|
||||||
|
|
@ -201,7 +203,7 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
|
||||||
case "filesystem":
|
case "filesystem":
|
||||||
return ["nfs", "cifs"];
|
return ["nfs", "cifs"];
|
||||||
case "volume":
|
case "volume":
|
||||||
return ["ext3", "ext4", "ext4dev", "xfs"];
|
return ["btrfs", "ext3", "ext4", "ext4dev", "xfs"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -413,6 +415,14 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
|
||||||
let kernel;
|
let kernel;
|
||||||
let kernel_release;
|
let kernel_release;
|
||||||
|
|
||||||
|
const cachedValue = await driver.ctx.cache.get(
|
||||||
|
MAX_ZVOL_NAME_LENGTH_CACHE_KEY
|
||||||
|
);
|
||||||
|
|
||||||
|
if (cachedValue) {
|
||||||
|
return cachedValue;
|
||||||
|
}
|
||||||
|
|
||||||
// get kernel
|
// get kernel
|
||||||
command = "uname -s";
|
command = "uname -s";
|
||||||
driver.ctx.logger.verbose("uname command: %s", command);
|
driver.ctx.logger.verbose("uname command: %s", command);
|
||||||
|
|
@ -423,12 +433,14 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
|
||||||
kernel = response.stdout.trim();
|
kernel = response.stdout.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let max;
|
||||||
switch (kernel.toLowerCase().trim()) {
|
switch (kernel.toLowerCase().trim()) {
|
||||||
// Linux is 255 (probably larger 4096) but scst may have a 255 limit
|
// Linux is 255 (probably larger 4096) but scst may have a 255 limit
|
||||||
// https://ngelinux.com/what-is-the-maximum-file-name-length-in-linux-and-how-to-see-this-is-this-really-255-characters-answer-is-no/
|
// https://ngelinux.com/what-is-the-maximum-file-name-length-in-linux-and-how-to-see-this-is-this-really-255-characters-answer-is-no/
|
||||||
// https://github.com/dmeister/scst/blob/master/iscsi-scst/include/iscsi_scst.h#L28
|
// https://github.com/dmeister/scst/blob/master/iscsi-scst/include/iscsi_scst.h#L28
|
||||||
case "linux":
|
case "linux":
|
||||||
return 255;
|
max = 255;
|
||||||
|
break;
|
||||||
case "freebsd":
|
case "freebsd":
|
||||||
// get kernel_release
|
// get kernel_release
|
||||||
command = "uname -r";
|
command = "uname -r";
|
||||||
|
|
@ -444,14 +456,20 @@ class ControllerZfsBaseDriver extends CsiBaseDriver {
|
||||||
let kernel_release_major = parts[0];
|
let kernel_release_major = parts[0];
|
||||||
|
|
||||||
if (kernel_release_major >= 13) {
|
if (kernel_release_major >= 13) {
|
||||||
return 255;
|
max = 255;
|
||||||
} else {
|
} else {
|
||||||
return 63;
|
max = 63;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`unknown kernel: ${kernel}`);
|
throw new Error(`unknown kernel: ${kernel}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await driver.ctx.cache.set(MAX_ZVOL_NAME_LENGTH_CACHE_KEY, max, {
|
||||||
|
ttl: 60 * 1000,
|
||||||
|
});
|
||||||
|
return max;
|
||||||
}
|
}
|
||||||
|
|
||||||
async setFilesystemMode(path, mode) {
|
async setFilesystemMode(path, mode) {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
const _ = require("lodash");
|
||||||
const { GrpcError, grpc } = require("../../utils/grpc");
|
const { GrpcError, grpc } = require("../../utils/grpc");
|
||||||
const { CsiBaseDriver } = require("../index");
|
const { CsiBaseDriver } = require("../index");
|
||||||
const HttpClient = require("./http").Client;
|
const HttpClient = require("./http").Client;
|
||||||
|
|
@ -1539,7 +1540,30 @@ class FreeNASApiDriver extends CsiBaseDriver {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deleteAsset) {
|
if (deleteAsset) {
|
||||||
|
let retries = 0;
|
||||||
|
let maxRetries = 5;
|
||||||
|
let retryWait = 1000;
|
||||||
response = await httpClient.delete(endpoint);
|
response = await httpClient.delete(endpoint);
|
||||||
|
|
||||||
|
// sometimes after an initiator has detached it takes a moment for TrueNAS to settle
|
||||||
|
// code: 422 body: {\"message\":\"Target csi-ci-55877e95sanity-node-expand-volume-e54f81fa-cd38e798 is in use.\",\"errno\":14}
|
||||||
|
while (
|
||||||
|
response.statusCode == 422 &&
|
||||||
|
retries < maxRetries &&
|
||||||
|
_.get(response, "body.message").includes("Target") &&
|
||||||
|
_.get(response, "body.message").includes("is in use") &&
|
||||||
|
_.get(response, "body.errno") == 14
|
||||||
|
) {
|
||||||
|
retries++;
|
||||||
|
this.ctx.logger.debug(
|
||||||
|
"target: %s is in use, retry %s shortly",
|
||||||
|
targetId,
|
||||||
|
retries
|
||||||
|
);
|
||||||
|
await sleep(retryWait);
|
||||||
|
response = await httpClient.delete(endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
if (![200, 204, 404].includes(response.statusCode)) {
|
if (![200, 204, 404].includes(response.statusCode)) {
|
||||||
throw new GrpcError(
|
throw new GrpcError(
|
||||||
grpc.status.UNKNOWN,
|
grpc.status.UNKNOWN,
|
||||||
|
|
@ -1929,7 +1953,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
|
||||||
if (capability.access_type == "mount") {
|
if (capability.access_type == "mount") {
|
||||||
if (
|
if (
|
||||||
capability.mount.fs_type &&
|
capability.mount.fs_type &&
|
||||||
!["ext3", "ext4", "ext4dev", "xfs"].includes(
|
!["btrfs", "ext3", "ext4", "ext4dev", "xfs"].includes(
|
||||||
capability.mount.fs_type
|
capability.mount.fs_type
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -208,11 +208,9 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
async setVersionInfoCache(versionInfo) {
|
async setVersionInfoCache(versionInfo) {
|
||||||
await this.cache.set(
|
await this.cache.set(FREENAS_SYSTEM_VERSION_CACHE_KEY, versionInfo, {
|
||||||
FREENAS_SYSTEM_VERSION_CACHE_KEY,
|
ttl: 60 * 1000,
|
||||||
versionInfo,
|
});
|
||||||
60 * 1000
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSystemVersion() {
|
async getSystemVersion() {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
const request = require("request");
|
const _ = require("lodash");
|
||||||
|
const http = require("http");
|
||||||
|
const https = require("https");
|
||||||
const URI = require("uri-js");
|
const URI = require("uri-js");
|
||||||
|
const { axios_request, stringify } = require("../../../utils/general");
|
||||||
const USER_AGENT = "democratic-csi-driver";
|
const USER_AGENT = "democratic-csi-driver";
|
||||||
|
|
||||||
class Client {
|
class Client {
|
||||||
|
|
@ -12,6 +15,31 @@ class Client {
|
||||||
this.options.apiVersion = 1;
|
this.options.apiVersion = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getHttpAgent() {
|
||||||
|
if (!this.httpAgent) {
|
||||||
|
this.httpAgent = new http.Agent({
|
||||||
|
keepAlive: true,
|
||||||
|
maxSockets: Infinity,
|
||||||
|
rejectUnauthorized: !!!this.options.allowInsecure,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.httpAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
getHttpsAgent() {
|
||||||
|
if (!this.httpsAgent) {
|
||||||
|
this.httpsAgent = new https.Agent({
|
||||||
|
keepAlive: true,
|
||||||
|
maxSockets: Infinity,
|
||||||
|
rejectUnauthorized: !!!this.options.allowInsecure,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.httpsAgent;
|
||||||
|
}
|
||||||
|
|
||||||
getBaseURL() {
|
getBaseURL() {
|
||||||
const server = this.options;
|
const server = this.options;
|
||||||
if (!server.protocol) {
|
if (!server.protocol) {
|
||||||
|
|
@ -46,14 +74,59 @@ class Client {
|
||||||
return this.options.apiVersion;
|
return this.options.apiVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getRequestCommonOptions() {
|
||||||
|
const client = this;
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Accept: "application/json",
|
||||||
|
"User-Agent": USER_AGENT,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
httpAgent: this.getHttpAgent(),
|
||||||
|
httpsAgent: this.getHttpsAgent(),
|
||||||
|
timeout: 60 * 1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (client.options.apiKey) {
|
||||||
|
options.headers.Authorization = `Bearer ${client.options.apiKey}`;
|
||||||
|
} else if (client.options.username && client.options.password) {
|
||||||
|
options.auth = {
|
||||||
|
username: client.options.username,
|
||||||
|
password: client.options.password,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
log_repsonse(error, response, body, options) {
|
log_repsonse(error, response, body, options) {
|
||||||
this.logger.debug("FREENAS HTTP REQUEST: " + JSON.stringify(options));
|
let prop;
|
||||||
|
let val;
|
||||||
|
|
||||||
|
prop = "auth.username";
|
||||||
|
val = _.get(options, prop, false);
|
||||||
|
if (val) {
|
||||||
|
_.set(options, prop, "redacted");
|
||||||
|
}
|
||||||
|
|
||||||
|
prop = "auth.password";
|
||||||
|
val = _.get(options, prop, false);
|
||||||
|
if (val) {
|
||||||
|
_.set(options, prop, "redacted");
|
||||||
|
}
|
||||||
|
|
||||||
|
prop = "headers.Authorization";
|
||||||
|
val = _.get(options, prop, false);
|
||||||
|
if (val) {
|
||||||
|
_.set(options, prop, "redacted");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.debug("FREENAS HTTP REQUEST: " + stringify(options));
|
||||||
this.logger.debug("FREENAS HTTP ERROR: " + error);
|
this.logger.debug("FREENAS HTTP ERROR: " + error);
|
||||||
this.logger.debug("FREENAS HTTP STATUS: " + response.statusCode);
|
this.logger.debug("FREENAS HTTP STATUS: " + response.statusCode);
|
||||||
this.logger.debug(
|
this.logger.debug("FREENAS HTTP HEADERS: " + stringify(response.headers));
|
||||||
"FREENAS HTTP HEADERS: " + JSON.stringify(response.headers)
|
this.logger.debug("FREENAS HTTP BODY: " + stringify(body));
|
||||||
);
|
|
||||||
this.logger.debug("FREENAS HTTP BODY: " + JSON.stringify(body));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(endpoint, data) {
|
async get(endpoint, data) {
|
||||||
|
|
@ -63,33 +136,18 @@ class Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const options = {
|
const options = client.getRequestCommonOptions();
|
||||||
method: "GET",
|
options.method = "GET";
|
||||||
url: this.getBaseURL() + endpoint,
|
options.url = this.getBaseURL() + endpoint;
|
||||||
headers: {
|
options.params = data;
|
||||||
Accept: "application/json",
|
|
||||||
"User-Agent": USER_AGENT,
|
axios_request(options, function (err, res, body) {
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
json: true,
|
|
||||||
qs: data,
|
|
||||||
agentOptions: {
|
|
||||||
rejectUnauthorized: !!!client.options.allowInsecure,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
request(options, function (err, res, body) {
|
|
||||||
client.log_repsonse(...arguments, options);
|
client.log_repsonse(...arguments, options);
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(res);
|
resolve(res);
|
||||||
}).auth(
|
});
|
||||||
client.options.username,
|
|
||||||
client.options.password,
|
|
||||||
true,
|
|
||||||
client.options.apiKey
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,33 +158,19 @@ class Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const options = {
|
const options = client.getRequestCommonOptions();
|
||||||
method: "POST",
|
options.method = "POST";
|
||||||
url: this.getBaseURL() + endpoint,
|
options.url = this.getBaseURL() + endpoint;
|
||||||
headers: {
|
options.data = data;
|
||||||
Accept: "application/json",
|
|
||||||
"User-Agent": USER_AGENT,
|
axios_request(options, function (err, res, body) {
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
json: true,
|
|
||||||
body: data,
|
|
||||||
agentOptions: {
|
|
||||||
rejectUnauthorized: !!!client.options.allowInsecure,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
request(options, function (err, res, body) {
|
|
||||||
client.log_repsonse(...arguments, options);
|
client.log_repsonse(...arguments, options);
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(res);
|
resolve(res);
|
||||||
}).auth(
|
});
|
||||||
client.options.username,
|
|
||||||
client.options.password,
|
|
||||||
true,
|
|
||||||
client.options.apiKey
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,33 +181,19 @@ class Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const options = {
|
const options = client.getRequestCommonOptions();
|
||||||
method: "PUT",
|
options.method = "PUT";
|
||||||
url: this.getBaseURL() + endpoint,
|
options.url = this.getBaseURL() + endpoint;
|
||||||
headers: {
|
options.data = data;
|
||||||
Accept: "application/json",
|
|
||||||
"User-Agent": USER_AGENT,
|
axios_request(options, function (err, res, body) {
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
json: true,
|
|
||||||
body: data,
|
|
||||||
agentOptions: {
|
|
||||||
rejectUnauthorized: !!!client.options.allowInsecure,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
request(options, function (err, res, body) {
|
|
||||||
client.log_repsonse(...arguments, options);
|
client.log_repsonse(...arguments, options);
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(res);
|
resolve(res);
|
||||||
}).auth(
|
});
|
||||||
client.options.username,
|
|
||||||
client.options.password,
|
|
||||||
true,
|
|
||||||
client.options.apiKey
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -174,33 +204,19 @@ class Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const options = {
|
const options = client.getRequestCommonOptions();
|
||||||
method: "DELETE",
|
options.method = "DELETE";
|
||||||
url: this.getBaseURL() + endpoint,
|
options.url = this.getBaseURL() + endpoint;
|
||||||
headers: {
|
options.data = data;
|
||||||
Accept: "application/json",
|
|
||||||
"User-Agent": USER_AGENT,
|
axios_request(options, function (err, res, body) {
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
json: true,
|
|
||||||
body: data,
|
|
||||||
agentOptions: {
|
|
||||||
rejectUnauthorized: !!!client.options.allowInsecure,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
request(options, function (err, res, body) {
|
|
||||||
client.log_repsonse(...arguments, options);
|
client.log_repsonse(...arguments, options);
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(res);
|
resolve(res);
|
||||||
}).auth(
|
});
|
||||||
client.options.username,
|
|
||||||
client.options.password,
|
|
||||||
true,
|
|
||||||
client.options.apiKey
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ const { GrpcError, grpc } = require("../../utils/grpc");
|
||||||
const SshClient = require("../../utils/ssh").SshClient;
|
const SshClient = require("../../utils/ssh").SshClient;
|
||||||
const HttpClient = require("./http").Client;
|
const HttpClient = require("./http").Client;
|
||||||
const { Zetabyte, ZfsSshProcessManager } = require("../../utils/zfs");
|
const { Zetabyte, ZfsSshProcessManager } = require("../../utils/zfs");
|
||||||
|
const sleep = require("../../utils/general").sleep;
|
||||||
|
|
||||||
const Handlebars = require("handlebars");
|
const Handlebars = require("handlebars");
|
||||||
|
|
||||||
|
|
@ -1572,7 +1573,30 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deleteAsset) {
|
if (deleteAsset) {
|
||||||
|
let retries = 0;
|
||||||
|
let maxRetries = 5;
|
||||||
|
let retryWait = 1000;
|
||||||
response = await httpClient.delete(endpoint);
|
response = await httpClient.delete(endpoint);
|
||||||
|
|
||||||
|
// sometimes after an initiator has detached it takes a moment for TrueNAS to settle
|
||||||
|
// code: 422 body: {\"message\":\"Target csi-ci-55877e95sanity-node-expand-volume-e54f81fa-cd38e798 is in use.\",\"errno\":14}
|
||||||
|
while (
|
||||||
|
response.statusCode == 422 &&
|
||||||
|
retries < maxRetries &&
|
||||||
|
_.get(response, "body.message").includes("Target") &&
|
||||||
|
_.get(response, "body.message").includes("is in use") &&
|
||||||
|
_.get(response, "body.errno") == 14
|
||||||
|
) {
|
||||||
|
retries++;
|
||||||
|
this.ctx.logger.debug(
|
||||||
|
"target: %s is in use, retry %s shortly",
|
||||||
|
targetId,
|
||||||
|
retries
|
||||||
|
);
|
||||||
|
await sleep(retryWait);
|
||||||
|
response = await httpClient.delete(endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
if (![200, 204].includes(response.statusCode)) {
|
if (![200, 204].includes(response.statusCode)) {
|
||||||
throw new GrpcError(
|
throw new GrpcError(
|
||||||
grpc.status.UNKNOWN,
|
grpc.status.UNKNOWN,
|
||||||
|
|
@ -2011,11 +2035,9 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
|
||||||
async setVersionInfoCache(versionInfo) {
|
async setVersionInfoCache(versionInfo) {
|
||||||
const driver = this;
|
const driver = this;
|
||||||
|
|
||||||
await driver.ctx.cache.set(
|
await driver.ctx.cache.set(FREENAS_SYSTEM_VERSION_CACHE_KEY, versionInfo, {
|
||||||
FREENAS_SYSTEM_VERSION_CACHE_KEY,
|
ttl: 60 * 1000,
|
||||||
versionInfo,
|
});
|
||||||
60 * 1000
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSystemVersion() {
|
async getSystemVersion() {
|
||||||
|
|
|
||||||
|
|
@ -461,6 +461,8 @@ class CsiBaseDriver {
|
||||||
normalizedSecrets[key];
|
normalizedSecrets[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create 'DB' entry
|
||||||
await iscsi.iscsiadm.createNodeDBEntry(
|
await iscsi.iscsiadm.createNodeDBEntry(
|
||||||
iscsiConnection.iqn,
|
iscsiConnection.iqn,
|
||||||
iscsiConnection.portal,
|
iscsiConnection.portal,
|
||||||
|
|
@ -804,6 +806,7 @@ class CsiBaseDriver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "btrfs":
|
||||||
case "xfs":
|
case "xfs":
|
||||||
//await filesystem.checkFilesystem(device, fs_info.type);
|
//await filesystem.checkFilesystem(device, fs_info.type);
|
||||||
await filesystem.expandFilesystem(staging_target_path, fs_type);
|
await filesystem.expandFilesystem(staging_target_path, fs_type);
|
||||||
|
|
@ -1493,9 +1496,10 @@ class CsiBaseDriver {
|
||||||
//await filesystem.checkFilesystem(device, fs_info.type);
|
//await filesystem.checkFilesystem(device, fs_info.type);
|
||||||
await filesystem.expandFilesystem(device, fs_type);
|
await filesystem.expandFilesystem(device, fs_type);
|
||||||
break;
|
break;
|
||||||
|
case "btrfs":
|
||||||
case "xfs":
|
case "xfs":
|
||||||
let mount_info = await mount.getMountDetails(device_path);
|
let mount_info = await mount.getMountDetails(device_path);
|
||||||
if (mount_info.fstype == "xfs") {
|
if (["btrfs", "xfs"].includes(mount_info.fstype)) {
|
||||||
//await filesystem.checkFilesystem(device, fs_info.type);
|
//await filesystem.checkFilesystem(device, fs_info.type);
|
||||||
await filesystem.expandFilesystem(device_path, fs_type);
|
await filesystem.expandFilesystem(device_path, fs_type);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,11 +130,11 @@ class NodeManualDriver extends CsiBaseDriver {
|
||||||
break;
|
break;
|
||||||
case "iscsi":
|
case "iscsi":
|
||||||
driverResourceType = "volume";
|
driverResourceType = "volume";
|
||||||
fs_types = ["ext3", "ext4", "ext4dev", "xfs"];
|
fs_types = ["btrfs", "ext3", "ext4", "ext4dev", "xfs"];
|
||||||
break;
|
break;
|
||||||
case "zfs-local":
|
case "zfs-local":
|
||||||
driverResourceType = "volume";
|
driverResourceType = "volume";
|
||||||
fs_types = ["ext3", "ext4", "ext4dev", "xfs", "zfs"];
|
fs_types = ["btrfs", "ext3", "ext4", "ext4dev", "xfs", "zfs"];
|
||||||
access_modes = [
|
access_modes = [
|
||||||
"UNKNOWN",
|
"UNKNOWN",
|
||||||
"SINGLE_NODE_WRITER",
|
"SINGLE_NODE_WRITER",
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,7 @@ class ZfsLocalEphemeralInlineDriver extends CsiBaseDriver {
|
||||||
if (capability.access_type == "mount") {
|
if (capability.access_type == "mount") {
|
||||||
if (
|
if (
|
||||||
capability.mount.fs_type &&
|
capability.mount.fs_type &&
|
||||||
!["ext3", "ext4", "ext4dev", "xfs"].includes(
|
!["btrfs", "ext3", "ext4", "ext4dev", "xfs"].includes(
|
||||||
capability.mount.fs_type
|
capability.mount.fs_type
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
const cp = require("child_process");
|
const cp = require("child_process");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
|
|
||||||
|
const DEFAULT_TIMEOUT = process.env.FILESYSTEM_DEFAULT_TIMEOUT || 30000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://github.com/kubernetes/kubernetes/tree/master/pkg/util/mount
|
* https://github.com/kubernetes/kubernetes/tree/master/pkg/util/mount
|
||||||
* https://github.com/kubernetes/kubernetes/blob/master/pkg/util/mount/mount_linux.go
|
* https://github.com/kubernetes/kubernetes/blob/master/pkg/util/mount/mount_linux.go
|
||||||
|
|
@ -16,10 +18,6 @@ class Filesystem {
|
||||||
options.paths.sudo = "/usr/bin/sudo";
|
options.paths.sudo = "/usr/bin/sudo";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!options.timeout) {
|
|
||||||
options.timeout = 10 * 60 * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!options.executor) {
|
if (!options.executor) {
|
||||||
options.executor = {
|
options.executor = {
|
||||||
spawn: cp.spawn,
|
spawn: cp.spawn,
|
||||||
|
|
@ -454,6 +452,12 @@ class Filesystem {
|
||||||
let result;
|
let result;
|
||||||
|
|
||||||
switch (fstype.toLowerCase()) {
|
switch (fstype.toLowerCase()) {
|
||||||
|
case "btrfs":
|
||||||
|
command = "btrfs";
|
||||||
|
//args = args.concat(options);
|
||||||
|
args = args.concat(["filesystem", "resize", "max"]);
|
||||||
|
args.push(device); // in this case should be a mounted path
|
||||||
|
break;
|
||||||
case "ext4":
|
case "ext4":
|
||||||
case "ext3":
|
case "ext3":
|
||||||
case "ext4dev":
|
case "ext4dev":
|
||||||
|
|
@ -500,6 +504,12 @@ class Filesystem {
|
||||||
let result;
|
let result;
|
||||||
|
|
||||||
switch (fstype.toLowerCase()) {
|
switch (fstype.toLowerCase()) {
|
||||||
|
case "btrfs":
|
||||||
|
command = "btrfs";
|
||||||
|
args = args.concat(options);
|
||||||
|
args.push("check");
|
||||||
|
args.push(device);
|
||||||
|
break;
|
||||||
case "ext4":
|
case "ext4":
|
||||||
case "ext3":
|
case "ext3":
|
||||||
case "ext4dev":
|
case "ext4dev":
|
||||||
|
|
@ -591,11 +601,15 @@ class Filesystem {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
exec(command, args, options) {
|
exec(command, args, options = {}) {
|
||||||
|
if (!options.hasOwnProperty("timeout")) {
|
||||||
|
// TODO: cannot use this as fsck etc are too risky to kill
|
||||||
|
//options.timeout = DEFAULT_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
const filesystem = this;
|
const filesystem = this;
|
||||||
args = args || [];
|
args = args || [];
|
||||||
|
|
||||||
let timeout;
|
|
||||||
let stdout = "";
|
let stdout = "";
|
||||||
let stderr = "";
|
let stderr = "";
|
||||||
|
|
||||||
|
|
@ -606,14 +620,6 @@ class Filesystem {
|
||||||
console.log("executing filesystem command: %s %s", command, args.join(" "));
|
console.log("executing filesystem command: %s %s", command, args.join(" "));
|
||||||
const child = filesystem.options.executor.spawn(command, args, options);
|
const child = filesystem.options.executor.spawn(command, args, options);
|
||||||
|
|
||||||
let didTimeout = false;
|
|
||||||
if (options && options.timeout) {
|
|
||||||
timeout = setTimeout(() => {
|
|
||||||
didTimeout = true;
|
|
||||||
child.kill(options.killSignal || "SIGTERM");
|
|
||||||
}, options.timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
child.stdout.on("data", function (data) {
|
child.stdout.on("data", function (data) {
|
||||||
stdout = stdout + data;
|
stdout = stdout + data;
|
||||||
|
|
@ -625,10 +631,8 @@ class Filesystem {
|
||||||
|
|
||||||
child.on("close", function (code) {
|
child.on("close", function (code) {
|
||||||
const result = { code, stdout, stderr, timeout: false };
|
const result = { code, stdout, stderr, timeout: false };
|
||||||
if (timeout) {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// timeout scenario
|
||||||
if (code === null) {
|
if (code === null) {
|
||||||
result.timeout = true;
|
result.timeout = true;
|
||||||
reject(result);
|
reject(result);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
const axios = require("axios");
|
||||||
|
|
||||||
function sleep(ms) {
|
function sleep(ms) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
setTimeout(resolve, ms);
|
setTimeout(resolve, ms);
|
||||||
|
|
@ -51,6 +53,65 @@ function getLargestNumber() {
|
||||||
return number;
|
return number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* transition function to replicate `request` style requests using axios
|
||||||
|
*
|
||||||
|
* @param {*} options
|
||||||
|
* @param {*} callback
|
||||||
|
*/
|
||||||
|
function axios_request(options, callback = function () {}) {
|
||||||
|
function prep_response(res) {
|
||||||
|
res["statusCode"] = res["status"];
|
||||||
|
delete res["status"];
|
||||||
|
|
||||||
|
res["body"] = res["data"];
|
||||||
|
delete res["data"];
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
axios(options)
|
||||||
|
.then((res) => {
|
||||||
|
res = prep_response(res);
|
||||||
|
callback(null, res, res.body);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
if (err.response) {
|
||||||
|
// The request was made and the server responded with a status code
|
||||||
|
// that falls out of the range of 2xx
|
||||||
|
let res = prep_response(err.response);
|
||||||
|
callback(null, res, res.body);
|
||||||
|
} else if (err.request) {
|
||||||
|
// The request was made but no response was received
|
||||||
|
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||||
|
// http.ClientRequest in node.js
|
||||||
|
callback(err, null, null);
|
||||||
|
} else {
|
||||||
|
// Something happened in setting up the request that triggered an Error
|
||||||
|
callback(err, null, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringify(value) {
|
||||||
|
const getCircularReplacer = () => {
|
||||||
|
const seen = new WeakSet();
|
||||||
|
return (key, value) => {
|
||||||
|
if (typeof value === "object" && value !== null) {
|
||||||
|
if (seen.has(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
seen.add(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return JSON.stringify(value, getCircularReplacer());
|
||||||
|
}
|
||||||
|
|
||||||
module.exports.sleep = sleep;
|
module.exports.sleep = sleep;
|
||||||
module.exports.lockKeysFromRequest = lockKeysFromRequest;
|
module.exports.lockKeysFromRequest = lockKeysFromRequest;
|
||||||
module.exports.getLargestNumber = getLargestNumber;
|
module.exports.getLargestNumber = getLargestNumber;
|
||||||
|
module.exports.stringify = stringify;
|
||||||
|
module.exports.axios_request = axios_request;
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,7 @@ class GrpcError {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.GrpcError = GrpcError;
|
module.exports.GrpcError = GrpcError;
|
||||||
module.exports.grpc = require("grpc-uds");
|
|
||||||
|
const grpcImplementation = process.env.GRPC_IMPLEMENTATION || "@grpc/grpc-js";
|
||||||
|
console.log(`grpc implementation: ${grpcImplementation}`);
|
||||||
|
module.exports.grpc = require(grpcImplementation);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
const cp = require("child_process");
|
const cp = require("child_process");
|
||||||
|
const { sleep } = require("./general");
|
||||||
|
|
||||||
function getIscsiValue(value) {
|
function getIscsiValue(value) {
|
||||||
if (value == "<empty>") return null;
|
if (value == "<empty>") return null;
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEFAULT_TIMEOUT = process.env.ISCSI_DEFAULT_TIMEOUT || 30000;
|
||||||
|
|
||||||
class ISCSI {
|
class ISCSI {
|
||||||
constructor(options = {}) {
|
constructor(options = {}) {
|
||||||
const iscsi = this;
|
const iscsi = this;
|
||||||
|
|
@ -19,10 +22,6 @@ class ISCSI {
|
||||||
options.paths.sudo = "/usr/bin/sudo";
|
options.paths.sudo = "/usr/bin/sudo";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!options.timeout) {
|
|
||||||
options.timeout = 10 * 60 * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!options.executor) {
|
if (!options.executor) {
|
||||||
options.executor = {
|
options.executor = {
|
||||||
spawn: cp.spawn,
|
spawn: cp.spawn,
|
||||||
|
|
@ -105,7 +104,10 @@ class ISCSI {
|
||||||
"-o",
|
"-o",
|
||||||
"new",
|
"new",
|
||||||
]);
|
]);
|
||||||
|
// create DB entry
|
||||||
await iscsi.exec(options.paths.iscsiadm, args);
|
await iscsi.exec(options.paths.iscsiadm, args);
|
||||||
|
|
||||||
|
// update attributes 1 by 1
|
||||||
for (let attribute in attributes) {
|
for (let attribute in attributes) {
|
||||||
let args = [];
|
let args = [];
|
||||||
args = args.concat([
|
args = args.concat([
|
||||||
|
|
@ -122,7 +124,30 @@ class ISCSI {
|
||||||
"--value",
|
"--value",
|
||||||
attributes[attribute],
|
attributes[attribute],
|
||||||
]);
|
]);
|
||||||
await iscsi.exec(options.paths.iscsiadm, args);
|
// https://bugzilla.redhat.com/show_bug.cgi?id=884427
|
||||||
|
// Could not execute operation on all records: encountered iSCSI database failure
|
||||||
|
let retries = 0;
|
||||||
|
let maxRetries = 5;
|
||||||
|
let retryWait = 1000;
|
||||||
|
while (retries < maxRetries) {
|
||||||
|
retries++;
|
||||||
|
try {
|
||||||
|
//throw {stderr: "Could not execute operation on all records: encountered iSCSI database failure"};
|
||||||
|
await iscsi.exec(options.paths.iscsiadm, args);
|
||||||
|
break;
|
||||||
|
} catch (err) {
|
||||||
|
if (
|
||||||
|
retries < maxRetries &&
|
||||||
|
err.stderr.includes(
|
||||||
|
"Could not execute operation on all records: encountered iSCSI database failure"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
await sleep(retryWait);
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -155,10 +180,7 @@ class ISCSI {
|
||||||
|
|
||||||
let session = false;
|
let session = false;
|
||||||
sessions.every((i_session) => {
|
sessions.every((i_session) => {
|
||||||
if (
|
if (`${i_session.iqn}` == tgtIQN && portal == i_session.portal) {
|
||||||
`${i_session.iqn}` == tgtIQN &&
|
|
||||||
portal == i_session.portal
|
|
||||||
) {
|
|
||||||
session = i_session;
|
session = i_session;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -493,11 +515,14 @@ class ISCSI {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
exec(command, args, options) {
|
exec(command, args, options = {}) {
|
||||||
|
if (!options.hasOwnProperty("timeout")) {
|
||||||
|
options.timeout = DEFAULT_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
const iscsi = this;
|
const iscsi = this;
|
||||||
args = args || [];
|
args = args || [];
|
||||||
|
|
||||||
let timeout;
|
|
||||||
let stdout = "";
|
let stdout = "";
|
||||||
let stderr = "";
|
let stderr = "";
|
||||||
|
|
||||||
|
|
@ -508,14 +533,6 @@ class ISCSI {
|
||||||
console.log("executing iscsi command: %s %s", command, args.join(" "));
|
console.log("executing iscsi command: %s %s", command, args.join(" "));
|
||||||
const child = iscsi.options.executor.spawn(command, args, options);
|
const child = iscsi.options.executor.spawn(command, args, options);
|
||||||
|
|
||||||
let didTimeout = false;
|
|
||||||
if (options && options.timeout) {
|
|
||||||
timeout = setTimeout(() => {
|
|
||||||
didTimeout = true;
|
|
||||||
child.kill(options.killSignal || "SIGTERM");
|
|
||||||
}, options.timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
child.stdout.on("data", function (data) {
|
child.stdout.on("data", function (data) {
|
||||||
stdout = stdout + data;
|
stdout = stdout + data;
|
||||||
|
|
@ -526,10 +543,14 @@ class ISCSI {
|
||||||
});
|
});
|
||||||
|
|
||||||
child.on("close", function (code) {
|
child.on("close", function (code) {
|
||||||
const result = { code, stdout, stderr };
|
const result = { code, stdout, stderr, timeout: false };
|
||||||
if (timeout) {
|
|
||||||
clearTimeout(timeout);
|
// timeout scenario
|
||||||
|
if (code === null) {
|
||||||
|
result.timeout = true;
|
||||||
|
reject(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (code) {
|
if (code) {
|
||||||
reject(result);
|
reject(result);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ FINDMNT_COMMON_OPTIONS = [
|
||||||
"--nofsroot", // prevents unwanted behavior with cifs volumes
|
"--nofsroot", // prevents unwanted behavior with cifs volumes
|
||||||
];
|
];
|
||||||
|
|
||||||
DEFAULT_TIMEOUT = process.env.MOUNT_DEFAULT_TIMEOUT || 30000;
|
const DEFAULT_TIMEOUT = process.env.MOUNT_DEFAULT_TIMEOUT || 30000;
|
||||||
|
|
||||||
class Mount {
|
class Mount {
|
||||||
constructor(options = {}) {
|
constructor(options = {}) {
|
||||||
|
|
@ -38,10 +38,6 @@ class Mount {
|
||||||
options.paths.chroot = "/usr/sbin/chroot";
|
options.paths.chroot = "/usr/sbin/chroot";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!options.timeout) {
|
|
||||||
options.timeout = 10 * 60 * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!options.executor) {
|
if (!options.executor) {
|
||||||
options.executor = {
|
options.executor = {
|
||||||
spawn: cp.spawn,
|
spawn: cp.spawn,
|
||||||
|
|
@ -389,7 +385,6 @@ class Mount {
|
||||||
const mount = this;
|
const mount = this;
|
||||||
args = args || [];
|
args = args || [];
|
||||||
|
|
||||||
let timeout;
|
|
||||||
let stdout = "";
|
let stdout = "";
|
||||||
let stderr = "";
|
let stderr = "";
|
||||||
|
|
||||||
|
|
@ -409,18 +404,6 @@ class Mount {
|
||||||
console.log("executing mount command: %s", cleansedLog);
|
console.log("executing mount command: %s", cleansedLog);
|
||||||
const child = mount.options.executor.spawn(command, args, options);
|
const child = mount.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) => {
|
return new Promise((resolve, reject) => {
|
||||||
child.stdout.on("data", function (data) {
|
child.stdout.on("data", function (data) {
|
||||||
stdout = stdout + data;
|
stdout = stdout + data;
|
||||||
|
|
@ -433,10 +416,6 @@ class Mount {
|
||||||
child.on("close", function (code) {
|
child.on("close", function (code) {
|
||||||
const result = { code, stdout, stderr, timeout: false };
|
const result = { code, stdout, stderr, timeout: false };
|
||||||
|
|
||||||
if (timeout) {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
// timeout scenario
|
// timeout scenario
|
||||||
if (code === null) {
|
if (code === null) {
|
||||||
result.timeout = true;
|
result.timeout = true;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
const cp = require("child_process");
|
const cp = require("child_process");
|
||||||
|
|
||||||
DEFAULT_TIMEOUT = process.env.MOUNT_DEFAULT_TIMEOUT || 30000;
|
const DEFAULT_TIMEOUT = process.env.MOUNT_DEFAULT_TIMEOUT || 30000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* - https://github.com/onedata/oneclient
|
* - https://github.com/onedata/oneclient
|
||||||
|
|
@ -23,10 +23,6 @@ class OneClient {
|
||||||
options.paths.chroot = "/usr/sbin/chroot";
|
options.paths.chroot = "/usr/sbin/chroot";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!options.timeout) {
|
|
||||||
options.timeout = 10 * 60 * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!options.executor) {
|
if (!options.executor) {
|
||||||
options.executor = {
|
options.executor = {
|
||||||
spawn: cp.spawn,
|
spawn: cp.spawn,
|
||||||
|
|
@ -81,7 +77,6 @@ class OneClient {
|
||||||
const oneclient = this;
|
const oneclient = this;
|
||||||
args = args || [];
|
args = args || [];
|
||||||
|
|
||||||
let timeout;
|
|
||||||
let stdout = "";
|
let stdout = "";
|
||||||
let stderr = "";
|
let stderr = "";
|
||||||
|
|
||||||
|
|
@ -100,18 +95,6 @@ class OneClient {
|
||||||
console.log("executing oneclient command: %s", cleansedLog);
|
console.log("executing oneclient command: %s", cleansedLog);
|
||||||
const child = oneclient.options.executor.spawn(command, args, options);
|
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) => {
|
return new Promise((resolve, reject) => {
|
||||||
child.stdout.on("data", function (data) {
|
child.stdout.on("data", function (data) {
|
||||||
stdout = stdout + data;
|
stdout = stdout + data;
|
||||||
|
|
@ -124,10 +107,6 @@ class OneClient {
|
||||||
child.on("close", function (code) {
|
child.on("close", function (code) {
|
||||||
const result = { code, stdout, stderr, timeout: false };
|
const result = { code, stdout, stderr, timeout: false };
|
||||||
|
|
||||||
if (timeout) {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
// timeout scenario
|
// timeout scenario
|
||||||
if (code === null) {
|
if (code === null) {
|
||||||
result.timeout = true;
|
result.timeout = true;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue