diff --git a/CHANGELOG.md b/CHANGELOG.md index 537fcd3..119bb4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# v1.6.2 + +Released 2022-04-06 + +- dep bumps +- optimize via object instance reuse of various clients etc + # v1.6.1 Released 2022-03-23 diff --git a/Dockerfile b/Dockerfile index 0b045c5..82349b4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,8 +12,7 @@ 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 ENV LANG=en_US.utf8 -#ENV NODE_VERSION=v12.22.6 -ENV NODE_VERSION=v16.14.0 +ENV NODE_VERSION=v16.14.2 ENV NODE_ENV=production # install build deps @@ -33,7 +32,7 @@ WORKDIR /home/csi/app USER csi 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 --only=production --grpc_node_binary_host_mirror=https://grpc-uds-binaries.s3-us-west-2.amazonaws.com/debian-buster COPY --chown=csi:csi . . RUN rm -rf docker diff --git a/Dockerfile.unified b/Dockerfile.unified deleted file mode 100644 index cf4db53..0000000 --- a/Dockerfile.unified +++ /dev/null @@ -1,59 +0,0 @@ -FROM debian:10-slim - -ENV DEBIAN_FRONTEND=noninteractive - -ARG TARGETPLATFORM -ARG BUILDPLATFORM - -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 - -ENV LANG=en_US.utf8 NODE_VERSION=v12.20.0 - -RUN echo "I am running on $BUILDPLATFORM, building for $TARGETPLATFORM" - -# install node -RUN apt-get update && apt-get install -y wget xz-utils -ADD docker/node-installer.sh /usr/local/sbin -RUN chmod +x /usr/local/sbin/node-installer.sh && node-installer.sh -ENV PATH=/usr/local/lib/nodejs/bin:$PATH - -# node service requirements -RUN apt-get update && \ - apt-get install -y e2fsprogs xfsprogs fatresize dosfstools nfs-common cifs-utils sudo && \ - rm -rf /var/lib/apt/lists/* - -# controller requirements -RUN apt-get update && \ - apt-get install -y ansible && \ - rm -rf /var/lib/apt/lists/* - -# npm requirements -# gcc and g++ required by grpc-usd until proper upstream support -RUN apt-get update && \ - apt-get install -y python make gcc g++ && \ - rm -rf /var/lib/apt/lists/* - -# install wrappers -ADD docker/iscsiadm /usr/local/sbin -RUN chmod +x /usr/local/sbin/iscsiadm - -ADD docker/multipath /usr/local/sbin -RUN chmod +x /usr/local/sbin/multipath - -# Run as a non-root user -RUN useradd --create-home csi \ - && mkdir /home/csi/app \ - && chown -R csi: /home/csi -WORKDIR /home/csi/app -USER csi - -COPY package*.json ./ -RUN npm install - -COPY --chown=csi:csi . . - -USER root - -EXPOSE 50051 -ENTRYPOINT [ "bin/democratic-csi" ] diff --git a/bin/democratic-csi b/bin/democratic-csi index ea833bc..79ded9d 100755 --- a/bin/democratic-csi +++ b/bin/democratic-csi @@ -1,4 +1,4 @@ -#!/usr/bin/env -S node --nouse-idle-notification --expose-gc +#!/usr/bin/env -S node --expose-gc ${NODE_OPTIONS_CSI_1} ${NODE_OPTIONS_CSI_2} ${NODE_OPTIONS_CSI_3} ${NODE_OPTIONS_CSI_4} ${NODE_OPTIONS_CSI_5} const yaml = require("js-yaml"); const fs = require("fs"); @@ -115,7 +115,7 @@ try { options ); } catch (err) { - logger.error(err.toString()); + logger.error(`${err.toString()} ${err.stack}`); process.exit(1); } @@ -134,6 +134,7 @@ async function requestHandlerProxy(call, callback, serviceMethodName) { cleansedCall.request[key] = "redacted"; } } + try { logger.info( "new request - driver: %s method: %s call: %j", @@ -193,6 +194,9 @@ async function requestHandlerProxy(call, callback, serviceMethodName) { let message; if (e instanceof Error) { message = e.toString(); + if (e.stack) { + message += ` ${e.stack}`; + } } else { message = JSON.stringify(e); } @@ -337,22 +341,77 @@ logger.info( [`SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`].forEach( (eventType) => { - process.on(eventType, (code) => { + process.on(eventType, async (code) => { console.log(`running server shutdown, exit code: ${code}`); + + // attempt clean shutdown of in-flight requests + try { + await new Promise((resolve, reject) => { + try { + csiServer.tryShutdown(() => { + resolve(); + }); + } catch (e) { + reject(e); + } + }); + + console.log(`grpc server gracefully closed all connections`); + } catch (e) { + console.log("failed to cleanly shutdown grpc server", e); + } + + // NOTE: if the shutdown above finishes cleanly the socket will already be removed let socketPath = bindSocket; socketPath = socketPath.replace(/^unix:\/\//g, ""); if (socketPath && fs.existsSync(socketPath)) { let fsStat = fs.statSync(socketPath); if (fsStat.isSocket()) { fs.unlinkSync(socketPath); + console.log(`removed grpc socket ${socketPath}`); } } + console.log("server fully shutdown, exiting"); process.exit(code); }); } ); +if (process.env.LOG_MEMORY_USAGE == "1") { + setInterval(() => { + console.log("logging memory usages due to LOG_MEMORY_USAGE env var"); + const used = process.memoryUsage(); + for (let key in used) { + console.log( + `[${new Date()}] Memory Usage: ${key} ${ + Math.round((used[key] / 1024 / 1024) * 100) / 100 + } MB` + ); + } + }, process.env.LOG_MEMORY_USAGE_INTERVAL || 5000); +} + +if (process.env.MANUAL_GC == "1") { + setInterval(() => { + console.log("gc invoked due to MANUAL_GC env var"); + try { + if (global.gc) { + global.gc(); + } + } catch (e) {} + }, process.env.MANUAL_GC_INTERVAL || 60000); +} + +if (process.env.LOG_GRPC_SESSIONS == "1") { + setInterval(() => { + console.log("dumping sessions"); + try { + console.log(csiServer.sessions); + } catch (e) {} + }, 5000); +} + if (require.main === module) { (async function () { try { @@ -395,6 +454,8 @@ if (require.main === module) { } ); }); + + fs.chmodSync(socketPath, 0o666); } csiServer.start(); } catch (e) { diff --git a/package-lock.json b/package-lock.json index 8a8ccd8..d6c08d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "democratic-csi", - "version": "1.6.1", + "version": "1.6.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "democratic-csi", - "version": "1.6.1", + "version": "1.6.2", "license": "MIT", "dependencies": { "@grpc/grpc-js": "^1.5.7", @@ -70,9 +70,9 @@ } }, "node_modules/@grpc/grpc-js": { - "version": "1.5.9", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.5.9.tgz", - "integrity": "sha512-un+cXqErq5P4p3+WgYVNVh7FB51MSnaoRef7QWDcMXKR6FX2R6Z/bltcJMxNNdTUMC85lkOQcpnAAetFziPSng==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.2.tgz", + "integrity": "sha512-9+89Ne1K8F9u86T+l1yIV2DS+dWHYVK61SsDZN4MFTFehOOaJ4rHxa1cW8Lwdn2/6tOx7N3+SY/vfcjztOHopA==", "dependencies": { "@grpc/proto-loader": "^0.6.4", "@types/node": ">=12.12.47" @@ -288,6 +288,11 @@ "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==" }, + "node_modules/@types/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ==" + }, "node_modules/@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", @@ -310,9 +315,9 @@ } }, "node_modules/@types/node": { - "version": "17.0.22", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.22.tgz", - "integrity": "sha512-8FwbVoG4fy+ykY86XCAclKZDORttqE5/s7dyWZKLXTdv3vRy5HozBEinG5IqhvPXXzIZEcTVbuHlQEI6iuwcmw==" + "version": "17.0.23", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz", + "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==" }, "node_modules/@types/request": { "version": "2.48.8", @@ -525,6 +530,15 @@ "concat-map": "0.0.1" } }, + "node_modules/buildcheck": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.3.tgz", + "integrity": "sha512-pziaA+p/wdVImfcbsZLNF32EiWyujlQLwolMqUQE8xpKNOH7KmZQaY8sXN7DGOEzPAElo9QTaeNRfGnf3iOJbA==", + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/bunyan": { "version": "1.8.15", "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", @@ -728,6 +742,18 @@ "node": ">= 0.8" } }, + "node_modules/compress-brotli": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/compress-brotli/-/compress-brotli-1.3.6.tgz", + "integrity": "sha512-au99/GqZtUtiCBliqLFbWlhnCxn+XSYjwZ77q6mKN4La4qOXDoLVPZ50iXr0WmAyMxl8yqoq3Yq4OeQNPPkyeQ==", + "dependencies": { + "@types/json-buffer": "~3.0.0", + "json-buffer": "~3.0.1" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -739,16 +765,17 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "node_modules/cpu-features": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.2.tgz", - "integrity": "sha512-/2yieBqvMcRj8McNzkycjW2v3OIUOibBfd2dLEJ0nWts8NobAxwiyw9phVNS6oDL8x8tz9F7uNVFEVpJncQpeA==", + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.4.tgz", + "integrity": "sha512-fKiZ/zp1mUwQbnzb9IghXtHtDoTMtNeb8oYGx6kX2SYfhnG0HNdBEBIzB9b5KlXu5DQPhfy3mInbBxFcgwAr3A==", "hasInstallScript": true, "optional": true, "dependencies": { - "nan": "^2.14.1" + "buildcheck": "0.0.3", + "nan": "^2.15.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=10.0.0" } }, "node_modules/cross-spawn": { @@ -920,9 +947,9 @@ } }, "node_modules/eslint": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz", - "integrity": "sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz", + "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.2.1", @@ -1662,10 +1689,11 @@ } }, "node_modules/keyv": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz", - "integrity": "sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.2.1.tgz", + "integrity": "sha512-cAJq5cTfxQdq1DHZEVNpnk4mEvhP+8UP8UQftLtTtJ98beKkRHf+62M0mIDM2u/IWXyP8bmGB375/6uGdSX2MA==", "dependencies": { + "compress-brotli": "^1.3.6", "json-buffer": "3.0.1" } }, @@ -1729,9 +1757,9 @@ } }, "node_modules/lru-cache": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.7.1.tgz", - "integrity": "sha512-cRffBiTW8s73eH4aTXqBcTLU0xQnwGV3/imttRHGWCrbergmnK4D6JXQd8qin5z43HnDwRI+o7mVW0LEB+tpAw==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.7.3.tgz", + "integrity": "sha512-WY9wjJNQt9+PZilnLbuFKM+SwDull9+6IAguOrarOMoOHTcJ9GnXSO11+Gw6c7xtDkBkthR57OZMtZKYr+1CEw==", "engines": { "node": ">=12" } @@ -1833,9 +1861,9 @@ } }, "node_modules/moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "version": "2.29.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz", + "integrity": "sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==", "optional": true, "engines": { "node": "*" @@ -2493,9 +2521,9 @@ } }, "node_modules/ssh2": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.7.0.tgz", - "integrity": "sha512-u1gdFfqKV1PTGR2szS5FImhFii40o+8FOUpg1M//iimNaS4BkTyUVfVdoydXS93M1SquOU02Z4KFhYDBNqQO+g==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.9.0.tgz", + "integrity": "sha512-rhhIZT0eMPvCBSOG8CpqZZ7gre2vgXaIqmb3Jb83t88rjsxIsFzDanqBJM9Ns8BmP1835A5IbQ199io4EUZwOA==", "hasInstallScript": true, "dependencies": { "asn1": "^0.2.4", @@ -2505,7 +2533,7 @@ "node": ">=10.16.0" }, "optionalDependencies": { - "cpu-features": "0.0.2", + "cpu-features": "~0.0.4", "nan": "^2.15.0" } }, @@ -2815,9 +2843,9 @@ } }, "node_modules/winston": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.6.0.tgz", - "integrity": "sha512-9j8T75p+bcN6D00sF/zjFVmPp+t8KMPB1MzbbzYjeN9VWxdsYnTB40TkbNUEXAmILEfChMvAMgidlX64OG3p6w==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.7.2.tgz", + "integrity": "sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng==", "dependencies": { "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", @@ -2980,9 +3008,9 @@ } }, "@grpc/grpc-js": { - "version": "1.5.9", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.5.9.tgz", - "integrity": "sha512-un+cXqErq5P4p3+WgYVNVh7FB51MSnaoRef7QWDcMXKR6FX2R6Z/bltcJMxNNdTUMC85lkOQcpnAAetFziPSng==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.2.tgz", + "integrity": "sha512-9+89Ne1K8F9u86T+l1yIV2DS+dWHYVK61SsDZN4MFTFehOOaJ4rHxa1cW8Lwdn2/6tOx7N3+SY/vfcjztOHopA==", "requires": { "@grpc/proto-loader": "^0.6.4", "@types/node": ">=12.12.47" @@ -3172,6 +3200,11 @@ "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==" }, + "@types/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ==" + }, "@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", @@ -3194,9 +3227,9 @@ } }, "@types/node": { - "version": "17.0.22", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.22.tgz", - "integrity": "sha512-8FwbVoG4fy+ykY86XCAclKZDORttqE5/s7dyWZKLXTdv3vRy5HozBEinG5IqhvPXXzIZEcTVbuHlQEI6iuwcmw==" + "version": "17.0.23", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz", + "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==" }, "@types/request": { "version": "2.48.8", @@ -3381,6 +3414,12 @@ "concat-map": "0.0.1" } }, + "buildcheck": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.3.tgz", + "integrity": "sha512-pziaA+p/wdVImfcbsZLNF32EiWyujlQLwolMqUQE8xpKNOH7KmZQaY8sXN7DGOEzPAElo9QTaeNRfGnf3iOJbA==", + "optional": true + }, "bunyan": { "version": "1.8.15", "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", @@ -3543,6 +3582,15 @@ "delayed-stream": "~1.0.0" } }, + "compress-brotli": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/compress-brotli/-/compress-brotli-1.3.6.tgz", + "integrity": "sha512-au99/GqZtUtiCBliqLFbWlhnCxn+XSYjwZ77q6mKN4La4qOXDoLVPZ50iXr0WmAyMxl8yqoq3Yq4OeQNPPkyeQ==", + "requires": { + "@types/json-buffer": "~3.0.0", + "json-buffer": "~3.0.1" + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3554,12 +3602,13 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cpu-features": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.2.tgz", - "integrity": "sha512-/2yieBqvMcRj8McNzkycjW2v3OIUOibBfd2dLEJ0nWts8NobAxwiyw9phVNS6oDL8x8tz9F7uNVFEVpJncQpeA==", + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.4.tgz", + "integrity": "sha512-fKiZ/zp1mUwQbnzb9IghXtHtDoTMtNeb8oYGx6kX2SYfhnG0HNdBEBIzB9b5KlXu5DQPhfy3mInbBxFcgwAr3A==", "optional": true, "requires": { - "nan": "^2.14.1" + "buildcheck": "0.0.3", + "nan": "^2.15.0" } }, "cross-spawn": { @@ -3682,9 +3731,9 @@ "dev": true }, "eslint": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz", - "integrity": "sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz", + "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==", "dev": true, "requires": { "@eslint/eslintrc": "^1.2.1", @@ -4229,10 +4278,11 @@ } }, "keyv": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz", - "integrity": "sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.2.1.tgz", + "integrity": "sha512-cAJq5cTfxQdq1DHZEVNpnk4mEvhP+8UP8UQftLtTtJ98beKkRHf+62M0mIDM2u/IWXyP8bmGB375/6uGdSX2MA==", "requires": { + "compress-brotli": "^1.3.6", "json-buffer": "3.0.1" } }, @@ -4290,9 +4340,9 @@ "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" }, "lru-cache": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.7.1.tgz", - "integrity": "sha512-cRffBiTW8s73eH4aTXqBcTLU0xQnwGV3/imttRHGWCrbergmnK4D6JXQd8qin5z43HnDwRI+o7mVW0LEB+tpAw==" + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.7.3.tgz", + "integrity": "sha512-WY9wjJNQt9+PZilnLbuFKM+SwDull9+6IAguOrarOMoOHTcJ9GnXSO11+Gw6c7xtDkBkthR57OZMtZKYr+1CEw==" }, "make-error": { "version": "1.3.6", @@ -4367,9 +4417,9 @@ } }, "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "version": "2.29.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz", + "integrity": "sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==", "optional": true }, "ms": { @@ -4862,13 +4912,13 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "ssh2": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.7.0.tgz", - "integrity": "sha512-u1gdFfqKV1PTGR2szS5FImhFii40o+8FOUpg1M//iimNaS4BkTyUVfVdoydXS93M1SquOU02Z4KFhYDBNqQO+g==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.9.0.tgz", + "integrity": "sha512-rhhIZT0eMPvCBSOG8CpqZZ7gre2vgXaIqmb3Jb83t88rjsxIsFzDanqBJM9Ns8BmP1835A5IbQ199io4EUZwOA==", "requires": { "asn1": "^0.2.4", "bcrypt-pbkdf": "^1.0.2", - "cpu-features": "0.0.2", + "cpu-features": "~0.0.4", "nan": "^2.15.0" } }, @@ -5097,9 +5147,9 @@ } }, "winston": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.6.0.tgz", - "integrity": "sha512-9j8T75p+bcN6D00sF/zjFVmPp+t8KMPB1MzbbzYjeN9VWxdsYnTB40TkbNUEXAmILEfChMvAMgidlX64OG3p6w==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.7.2.tgz", + "integrity": "sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng==", "requires": { "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", diff --git a/package.json b/package.json index 79854a5..098fdd2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "democratic-csi", - "version": "1.6.1", + "version": "1.6.2", "description": "kubernetes csi driver framework", "main": "bin/democratic-csi", "scripts": { diff --git a/src/driver/controller-synology/http/index.js b/src/driver/controller-synology/http/index.js index 7096df3..f1e7c0a 100644 --- a/src/driver/controller-synology/http/index.js +++ b/src/driver/controller-synology/http/index.js @@ -3,8 +3,10 @@ const http = require("http"); const https = require("https"); const { axios_request, stringify } = require("../../../utils/general"); const Mutex = require("async-mutex").Mutex; +const registry = require("../../../utils/registry"); const USER_AGENT = "democratic-csi"; +const __REGISTRY_NS__ = "SynologyHttpClient"; class SynologyHttpClient { constructor(options = {}) { @@ -22,27 +24,23 @@ class SynologyHttpClient { } getHttpAgent() { - if (!this.httpAgent) { - this.httpAgent = new http.Agent({ + return registry.get(`${__REGISTRY_NS__}:http_agent`, () => { + return new http.Agent({ keepAlive: true, maxSockets: Infinity, rejectUnauthorized: !!!this.options.allowInsecure, }); - } - - return this.httpAgent; + }); } getHttpsAgent() { - if (!this.httpsAgent) { - this.httpsAgent = new https.Agent({ + return registry.get(`${__REGISTRY_NS__}:https_agent`, () => { + return new https.Agent({ keepAlive: true, maxSockets: Infinity, rejectUnauthorized: !!!this.options.allowInsecure, }); - } - - return this.httpsAgent; + }); } log_response(error, response, body, options) { diff --git a/src/driver/controller-synology/index.js b/src/driver/controller-synology/index.js index b3e6f4d..6db629f 100644 --- a/src/driver/controller-synology/index.js +++ b/src/driver/controller-synology/index.js @@ -1,9 +1,12 @@ const { CsiBaseDriver } = require("../index"); const { GrpcError, grpc } = require("../../utils/grpc"); +const registry = require("../../utils/registry"); const SynologyHttpClient = require("./http").SynologyHttpClient; const semver = require("semver"); const sleep = require("../../utils/general").sleep; +const __REGISTRY_NS__ = "ControllerSynologyDriver"; + /** * * Driver to provision storage on a synology device @@ -109,10 +112,9 @@ class ControllerSynologyDriver extends CsiBaseDriver { } async getHttpClient() { - if (!this.httpClient) { - this.httpClient = new SynologyHttpClient(this.options.httpConnection); - } - return this.httpClient; + return registry.get(`${__REGISTRY_NS__}:http_client`, () => { + return new SynologyHttpClient(this.options.httpConnection); + }); } getDriverResourceType() { diff --git a/src/driver/controller-zfs-generic/index.js b/src/driver/controller-zfs-generic/index.js index bb44049..31c0fa7 100644 --- a/src/driver/controller-zfs-generic/index.js +++ b/src/driver/controller-zfs-generic/index.js @@ -1,6 +1,7 @@ const _ = require("lodash"); const { ControllerZfsBaseDriver } = require("../controller-zfs"); const { GrpcError, grpc } = require("../../utils/grpc"); +const registry = require("../../utils/registry"); const SshClient = require("../../utils/ssh").SshClient; const sleep = require("../../utils/general").sleep; const { Zetabyte, ZfsSshProcessManager } = require("../../utils/zfs"); @@ -8,36 +9,40 @@ const { Zetabyte, ZfsSshProcessManager } = require("../../utils/zfs"); const Handlebars = require("handlebars"); const ISCSI_ASSETS_NAME_PROPERTY_NAME = "democratic-csi:iscsi_assets_name"; - +const __REGISTRY_NS__ = "ControllerZfsGenericDriver"; class ControllerZfsGenericDriver extends ControllerZfsBaseDriver { getExecClient() { - return new SshClient({ - logger: this.ctx.logger, - connection: this.options.sshConnection, + return registry.get(`${__REGISTRY_NS__}:exec_client`, () => { + return new SshClient({ + logger: this.ctx.logger, + connection: this.options.sshConnection, + }); }); } async getZetabyte() { - const execClient = this.getExecClient(); - const options = {}; - options.executor = new ZfsSshProcessManager(execClient); - options.idempotent = true; + return registry.getAsync(`${__REGISTRY_NS__}:zb`, async () => { + const execClient = this.getExecClient(); + const options = {}; + options.executor = new ZfsSshProcessManager(execClient); + options.idempotent = true; - if ( - this.options.zfs.hasOwnProperty("cli") && - this.options.zfs.cli && - this.options.zfs.cli.hasOwnProperty("paths") - ) { - options.paths = this.options.zfs.cli.paths; - } + if ( + this.options.zfs.hasOwnProperty("cli") && + this.options.zfs.cli && + this.options.zfs.cli.hasOwnProperty("paths") + ) { + options.paths = this.options.zfs.cli.paths; + } - options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false); + options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false); - if (typeof this.setZetabyteCustomOptions === "function") { - await this.setZetabyteCustomOptions(options); - } + if (typeof this.setZetabyteCustomOptions === "function") { + await this.setZetabyteCustomOptions(options); + } - return new Zetabyte(options); + return new Zetabyte(options); + }); } /** @@ -362,7 +367,9 @@ delete ${iscsiName} taregetCliCommand.push("|"); taregetCliCommand.push("targetcli"); - if (_.get(this.options, "iscsi.shareStrategyTargetCli.sudoEnabled", false)) { + if ( + _.get(this.options, "iscsi.shareStrategyTargetCli.sudoEnabled", false) + ) { command = "sudo"; args.unshift("sh"); } @@ -399,7 +406,7 @@ delete ${iscsiName} options ); if (response.code != 0) { - throw new Error(response.stderr); + throw new Error(JSON.stringify(response)); } driver.ctx.logger.verbose( "TargetCLI response: " + JSON.stringify(response) diff --git a/src/driver/controller-zfs-local/index.js b/src/driver/controller-zfs-local/index.js index a960912..04f1a17 100644 --- a/src/driver/controller-zfs-local/index.js +++ b/src/driver/controller-zfs-local/index.js @@ -2,12 +2,14 @@ const _ = require("lodash"); const { ControllerZfsBaseDriver } = require("../controller-zfs"); const { GrpcError, grpc } = require("../../utils/grpc"); const LocalCliExecClient = require("./exec").LocalCliClient; -const os = require("os"); +const registry = require("../../utils/registry"); const { Zetabyte } = require("../../utils/zfs"); const ZFS_ASSET_NAME_PROPERTY_NAME = "zfs_asset_name"; const NODE_TOPOLOGY_KEY_NAME = "org.democratic-csi.topology/node"; +const __REGISTRY_NS__ = "ControllerZfsLocalDriver"; + class ControllerZfsLocalDriver extends ControllerZfsBaseDriver { constructor(ctx, options) { const i_caps = _.get( @@ -29,43 +31,47 @@ class ControllerZfsLocalDriver extends ControllerZfsBaseDriver { } getExecClient() { - return new LocalCliExecClient({ - logger: this.ctx.logger, + return registry.get(`${__REGISTRY_NS__}:exec_client`, () => { + return new LocalCliExecClient({ + logger: this.ctx.logger, + }); }); } async getZetabyte() { - const execClient = this.getExecClient(); + return registry.getAsync(`${__REGISTRY_NS__}:zb`, async () => { + const execClient = this.getExecClient(); - const options = {}; - options.executor = execClient; - options.idempotent = true; + const options = {}; + options.executor = execClient; + options.idempotent = true; - /* - if ( - this.options.zfs.hasOwnProperty("cli") && - this.options.zfs.cli && - this.options.zfs.cli.hasOwnProperty("paths") - ) { - options.paths = this.options.zfs.cli.paths; - } - */ + /* + if ( + this.options.zfs.hasOwnProperty("cli") && + this.options.zfs.cli && + this.options.zfs.cli.hasOwnProperty("paths") + ) { + options.paths = this.options.zfs.cli.paths; + } + */ - // use env based paths to allow for custom wrapper scripts to chroot to the host - options.paths = { - zfs: "zfs", - zpool: "zpool", - sudo: "sudo", - chroot: "chroot", - }; + // use env based paths to allow for custom wrapper scripts to chroot to the host + options.paths = { + zfs: "zfs", + zpool: "zpool", + sudo: "sudo", + chroot: "chroot", + }; - options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false); + options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false); - if (typeof this.setZetabyteCustomOptions === "function") { - await this.setZetabyteCustomOptions(options); - } + if (typeof this.setZetabyteCustomOptions === "function") { + await this.setZetabyteCustomOptions(options); + } - return new Zetabyte(options); + return new Zetabyte(options); + }); } /** diff --git a/src/driver/freenas/api.js b/src/driver/freenas/api.js index cdd3a74..f436ff9 100644 --- a/src/driver/freenas/api.js +++ b/src/driver/freenas/api.js @@ -5,6 +5,7 @@ const HttpClient = require("./http").Client; const TrueNASApiClient = require("./http/api").Api; const { Zetabyte } = require("../../utils/zfs"); const getLargestNumber = require("../../utils/general").getLargestNumber; +const registry = require("../../utils/registry"); const sleep = require("../../utils/general").sleep; const Handlebars = require("handlebars"); @@ -44,6 +45,8 @@ const VOLUME_CONTEXT_PROVISIONER_DRIVER_PROPERTY_NAME = const VOLUME_CONTEXT_PROVISIONER_INSTANCE_ID_PROPERTY_NAME = "democratic-csi:volume_context_provisioner_instance_id"; +const __REGISTRY_NS__ = "FreeNASApiDriver"; + class FreeNASApiDriver extends CsiBaseDriver { constructor(ctx, options) { super(...arguments); @@ -154,14 +157,16 @@ class FreeNASApiDriver extends CsiBaseDriver { * @returns */ async getZetabyte() { - return new Zetabyte({ - executor: { - spawn: function () { - throw new Error( - "cannot use the zb implementation to execute zfs commands, must use the http api" - ); + return registry.get(`${__REGISTRY_NS__}:zb`, () => { + return new Zetabyte({ + executor: { + spawn: function () { + throw new Error( + "cannot use the zb implementation to execute zfs commands, must use the http api" + ); + }, }, - }, + }); }); } @@ -1887,11 +1892,12 @@ class FreeNASApiDriver extends CsiBaseDriver { } async getHttpClient() { - const client = new HttpClient(this.options.httpConnection); - client.logger = this.ctx.logger; - client.setApiVersion(2); // requires version 2 - - return client; + return registry.get(`${__REGISTRY_NS__}:http_client`, () => { + const client = new HttpClient(this.options.httpConnection); + client.logger = this.ctx.logger; + client.setApiVersion(2); // requires version 2 + return client; + }); } async getMinimumVolumeSize() { @@ -1903,11 +1909,10 @@ class FreeNASApiDriver extends CsiBaseDriver { } async getTrueNASHttpApiClient() { - const driver = this; - const httpClient = await this.getHttpClient(); - const apiClient = new TrueNASApiClient(httpClient, driver.ctx.cache); - - return apiClient; + return registry.getAsync(`${__REGISTRY_NS__}:api_client`, async () => { + const httpClient = await this.getHttpClient(); + return new TrueNASApiClient(httpClient, this.ctx.cache); + }); } assertCapabilities(capabilities) { diff --git a/src/driver/freenas/http/api.js b/src/driver/freenas/http/api.js index 594887f..ef4b52a 100644 --- a/src/driver/freenas/http/api.js +++ b/src/driver/freenas/http/api.js @@ -1,8 +1,10 @@ +const registry = require("../../../utils/registry"); const { sleep, stringify } = require("../../../utils/general"); const { Zetabyte } = require("../../../utils/zfs"); // used for in-memory cache of the version info const FREENAS_SYSTEM_VERSION_CACHE_KEY = "freenas:system_version"; +const __REGISTRY_NS__ = "FreeNASHttpApi"; class Api { constructor(client, cache, options = {}) { @@ -20,14 +22,16 @@ class Api { * @returns */ async getZetabyte() { - return new Zetabyte({ - executor: { - spawn: function () { - throw new Error( - "cannot use the zb implementation to execute zfs commands, must use the http api" - ); + return registry.get(`${__REGISTRY_NS__}:zb`, () => { + return new Zetabyte({ + executor: { + spawn: function () { + throw new Error( + "cannot use the zb implementation to execute zfs commands, must use the http api" + ); + }, }, - }, + }); }); } diff --git a/src/driver/freenas/ssh.js b/src/driver/freenas/ssh.js index 29eddf8..12c3821 100644 --- a/src/driver/freenas/ssh.js +++ b/src/driver/freenas/ssh.js @@ -1,6 +1,7 @@ const _ = require("lodash"); const { ControllerZfsBaseDriver } = require("../controller-zfs"); const { GrpcError, grpc } = require("../../utils/grpc"); +const registry = require("../../utils/registry"); const SshClient = require("../../utils/ssh").SshClient; const HttpClient = require("./http").Client; const { Zetabyte, ZfsSshProcessManager } = require("../../utils/zfs"); @@ -22,35 +23,41 @@ const FREENAS_ISCSI_ASSETS_NAME_PROPERTY_NAME = // used for in-memory cache of the version info const FREENAS_SYSTEM_VERSION_CACHE_KEY = "freenas:system_version"; +const __REGISTRY_NS__ = "FreeNASSshDriver"; + class FreeNASSshDriver extends ControllerZfsBaseDriver { getExecClient() { - return new SshClient({ - logger: this.ctx.logger, - connection: this.options.sshConnection, + return registry.get(`${__REGISTRY_NS__}:exec_client`, () => { + return new SshClient({ + logger: this.ctx.logger, + connection: this.options.sshConnection, + }); }); } async getZetabyte() { - const sshClient = this.getExecClient(); - const options = {}; - options.executor = new ZfsSshProcessManager(sshClient); - options.idempotent = true; + return registry.getAsync(`${__REGISTRY_NS__}:zb`, async () => { + const sshClient = this.getExecClient(); + const options = {}; + options.executor = new ZfsSshProcessManager(sshClient); + options.idempotent = true; - if ( - this.options.zfs.hasOwnProperty("cli") && - this.options.zfs.cli && - this.options.zfs.cli.hasOwnProperty("paths") - ) { - options.paths = this.options.zfs.cli.paths; - } + if ( + this.options.zfs.hasOwnProperty("cli") && + this.options.zfs.cli && + this.options.zfs.cli.hasOwnProperty("paths") + ) { + options.paths = this.options.zfs.cli.paths; + } - options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false); + options.sudo = _.get(this.options, "zfs.cli.sudoEnabled", false); - if (typeof this.setZetabyteCustomOptions === "function") { - await this.setZetabyteCustomOptions(options); - } + if (typeof this.setZetabyteCustomOptions === "function") { + await this.setZetabyteCustomOptions(options); + } - return new Zetabyte(options); + return new Zetabyte(options); + }); } /** @@ -88,15 +95,21 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver { } async getHttpClient(autoDetectVersion = true) { - const client = new HttpClient(this.options.httpConnection); - client.logger = this.ctx.logger; + const autodetectkey = autoDetectVersion === true ? 1 : 0; + return registry.getAsync( + `${__REGISTRY_NS__}:http_client:autoDetectVersion_${autodetectkey}`, + async () => { + const client = new HttpClient(this.options.httpConnection); + client.logger = this.ctx.logger; - if (autoDetectVersion && !!!this.options.httpConnection.apiVersion) { - const apiVersion = await this.getApiVersion(); - client.setApiVersion(apiVersion); - } + if (autoDetectVersion && !!!this.options.httpConnection.apiVersion) { + const apiVersion = await this.getApiVersion(); + client.setApiVersion(apiVersion); + } - return client; + return client; + } + ); } getDriverShareType() { diff --git a/src/driver/index.js b/src/driver/index.js index c0e76f5..735effc 100644 --- a/src/driver/index.js +++ b/src/driver/index.js @@ -7,10 +7,13 @@ const { Mount } = require("../utils/mount"); const { OneClient } = require("../utils/oneclient"); const { Filesystem } = require("../utils/filesystem"); const { ISCSI } = require("../utils/iscsi"); +const registry = require("../utils/registry"); const semver = require("semver"); const sleep = require("../utils/general").sleep; const { Zetabyte } = require("../utils/zfs"); +const __REGISTRY_NS__ = "CsiBaseDriver"; + /** * common code shared between all drivers * this is **NOT** meant to work as a proxy @@ -93,6 +96,71 @@ class CsiBaseDriver { return normalized; } + /** + * Get an instance of the Filesystem class + * + * @returns Filesystem + */ + getDefaultFilesystemInstance() { + return registry.get( + `${__REGISTRY_NS__}:default_filesystem_instance`, + () => { + return new Filesystem(); + } + ); + } + + /** + * Get an instance of the Mount class + * + * @returns Mount + */ + getDefaultMountInstance() { + return registry.get(`${__REGISTRY_NS__}:default_mount_instance`, () => { + const filesystem = this.getDefaultFilesystemInstance(); + return new Mount({ filesystem }); + }); + } + + /** + * Get an instance of the ISCSI class + * + * @returns ISCSI + */ + getDefaultISCSIInstance() { + return registry.get(`${__REGISTRY_NS__}:default_iscsi_instance`, () => { + return new ISCSI(); + }); + } + + getDefaultZetabyteInstance() { + return registry.get(`${__REGISTRY_NS__}:default_zb_instance`, () => { + return new Zetabyte({ + idempotent: true, + paths: { + zfs: "zfs", + zpool: "zpool", + sudo: "sudo", + chroot: "chroot", + }, + //logger: driver.ctx.logger, + executor: { + spawn: function () { + const command = `${arguments[0]} ${arguments[1].join(" ")}`; + return cp.exec(command); + }, + }, + log_commands: true, + }); + }); + } + + getDefaultOneClientInstance() { + return registry.get(`${__REGISTRY_NS__}:default_oneclient_instance`, () => { + return new OneClient(); + }); + } + async GetPluginInfo(call) { return { name: this.ctx.args.csiName, @@ -276,9 +344,9 @@ class CsiBaseDriver { */ async NodeStageVolume(call) { const driver = this; - const mount = new Mount(); - const filesystem = new Filesystem(); - const iscsi = new ISCSI(); + const mount = driver.getDefaultMountInstance(); + const filesystem = driver.getDefaultFilesystemInstance(); + const iscsi = driver.getDefaultISCSIInstance(); let result; let device; @@ -590,7 +658,7 @@ class CsiBaseDriver { break; case "oneclient": - let oneclient = new OneClient(); + let oneclient = driver.getDefaultOneClientInstance(); device = "oneclient"; result = await mount.deviceIsMountedAtPath(device, staging_target_path); if (result) { @@ -634,23 +702,7 @@ class CsiBaseDriver { break; case "zfs-local": // TODO: make this a geneic zb instance (to ensure works with node-manual driver) - const zb = new Zetabyte({ - idempotent: true, - paths: { - zfs: "zfs", - zpool: "zpool", - sudo: "sudo", - chroot: "chroot", - }, - //logger: driver.ctx.logger, - executor: { - spawn: function () { - const command = `${arguments[0]} ${arguments[1].join(" ")}`; - return cp.exec(command); - }, - }, - log_commands: true, - }); + const zb = driver.getDefaultZetabyteInstance(); result = await zb.zfs.get(`${volume_context.zfs_asset_name}`, [ "type", "mountpoint", @@ -861,9 +913,9 @@ class CsiBaseDriver { */ async NodeUnstageVolume(call) { const driver = this; - const mount = new Mount(); - const filesystem = new Filesystem(); - const iscsi = new ISCSI(); + const mount = driver.getDefaultMountInstance(); + const filesystem = driver.getDefaultFilesystemInstance(); + const iscsi = driver.getDefaultISCSIInstance(); let result; let is_block = false; let is_device_mapper = false; @@ -1081,8 +1133,8 @@ class CsiBaseDriver { async NodePublishVolume(call) { const driver = this; - const mount = new Mount(); - const filesystem = new Filesystem(); + const mount = driver.getDefaultMountInstance(); + const filesystem = driver.getDefaultFilesystemInstance(); let result; const volume_id = call.request.volume_id; @@ -1236,8 +1288,8 @@ class CsiBaseDriver { async NodeUnpublishVolume(call) { const driver = this; - const mount = new Mount(); - const filesystem = new Filesystem(); + const mount = driver.getDefaultMountInstance(); + const filesystem = driver.getDefaultFilesystemInstance(); let result; const volume_id = call.request.volume_id; @@ -1316,8 +1368,8 @@ class CsiBaseDriver { async NodeGetVolumeStats(call) { const driver = this; - const mount = new Mount(); - const filesystem = new Filesystem(); + const mount = driver.getDefaultMountInstance(); + const filesystem = driver.getDefaultFilesystemInstance(); let result; let device_path; let access_type; @@ -1414,8 +1466,9 @@ class CsiBaseDriver { * @param {*} call */ async NodeExpandVolume(call) { - const mount = new Mount(); - const filesystem = new Filesystem(); + const driver = this; + const mount = driver.getDefaultMountInstance(); + const filesystem = driver.getDefaultFilesystemInstance(); let device; let fs_info; let device_path; diff --git a/src/driver/zfs-local-ephemeral-inline/index.js b/src/driver/zfs-local-ephemeral-inline/index.js index 9db8d90..d9dfebd 100644 --- a/src/driver/zfs-local-ephemeral-inline/index.js +++ b/src/driver/zfs-local-ephemeral-inline/index.js @@ -2,6 +2,7 @@ const fs = require("fs"); const { CsiBaseDriver } = require("../index"); const { GrpcError, grpc } = require("../../utils/grpc"); const { Filesystem } = require("../../utils/filesystem"); +const registry = require("../../utils/registry"); const semver = require("semver"); const SshClient = require("../../utils/ssh").SshClient; const { Zetabyte, ZfsSshProcessManager } = require("../../utils/zfs"); @@ -14,6 +15,7 @@ const VOLUME_CONTEXT_PROVISIONER_DRIVER_PROPERTY_NAME = "democratic-csi:volume_context_provisioner_driver"; const VOLUME_CONTEXT_PROVISIONER_INSTANCE_ID_PROPERTY_NAME = "democratic-csi:volume_context_provisioner_instance_id"; +const __REGISTRY_NS__ = "ZfsLocalEphemeralInlineDriver"; /** * https://github.com/kubernetes/enhancements/blob/master/keps/sig-storage/20190122-csi-inline-volumes.md @@ -123,27 +125,31 @@ class ZfsLocalEphemeralInlineDriver extends CsiBaseDriver { } getSshClient() { - return new SshClient({ - logger: this.ctx.logger, - connection: this.options.sshConnection, + return registry.get(`${__REGISTRY_NS__}:ssh_client`, () => { + return new SshClient({ + logger: this.ctx.logger, + connection: this.options.sshConnection, + }); }); } getZetabyte() { - let sshClient; - let executor; - if (this.options.sshConnection) { - sshClient = this.getSshClient(); - executor = new ZfsSshProcessManager(sshClient); - } - return new Zetabyte({ - executor, - idempotent: true, - chroot: this.options.zfs.chroot, - paths: { - zpool: "/usr/sbin/zpool", - zfs: "/usr/sbin/zfs", - }, + return registry.get(`${__REGISTRY_NS__}:zb`, () => { + let sshClient; + let executor; + if (this.options.sshConnection) { + sshClient = this.getSshClient(); + executor = new ZfsSshProcessManager(sshClient); + } + return new Zetabyte({ + executor, + idempotent: true, + chroot: this.options.zfs.chroot, + paths: { + zpool: "/usr/sbin/zpool", + zfs: "/usr/sbin/zfs", + }, + }); }); } diff --git a/src/utils/filesystem.js b/src/utils/filesystem.js index 6d07a32..f2c897c 100644 --- a/src/utils/filesystem.js +++ b/src/utils/filesystem.js @@ -610,17 +610,17 @@ class Filesystem { const filesystem = this; args = args || []; - let stdout = ""; - let stderr = ""; - if (filesystem.options.sudo) { args.unshift(command); command = filesystem.options.paths.sudo; } console.log("executing filesystem command: %s %s", command, args.join(" ")); - const child = filesystem.options.executor.spawn(command, args, options); - + return new Promise((resolve, reject) => { + const child = filesystem.options.executor.spawn(command, args, options); + let stdout = ""; + let stderr = ""; + child.stdout.on("data", function (data) { stdout = stdout + data; }); diff --git a/src/utils/iscsi.js b/src/utils/iscsi.js index d444289..7ead35e 100644 --- a/src/utils/iscsi.js +++ b/src/utils/iscsi.js @@ -523,17 +523,18 @@ class ISCSI { const iscsi = this; args = args || []; - let stdout = ""; - let stderr = ""; - if (iscsi.options.sudo) { args.unshift(command); command = iscsi.options.paths.sudo; } console.log("executing iscsi command: %s %s", command, args.join(" ")); - const child = iscsi.options.executor.spawn(command, args, options); return new Promise((resolve, reject) => { + const child = iscsi.options.executor.spawn(command, args, options); + + let stdout = ""; + let stderr = ""; + child.stdout.on("data", function (data) { stdout = stdout + data; }); diff --git a/src/utils/logger.js b/src/utils/logger.js index 8b01130..c20bfe7 100644 --- a/src/utils/logger.js +++ b/src/utils/logger.js @@ -1,3 +1,5 @@ +const os = require("os"); + /** * Levels * @@ -27,7 +29,7 @@ let formatters; let defaultMeta; if (env == "production") { formatters = [winston.format.json()]; - defaultMeta = { service: "democratic-csi" }; + defaultMeta = { service: "democratic-csi", host: os.hostname() }; } else { formatters = [winston.format.colorize(), winston.format.simple()]; defaultMeta = {}; @@ -36,6 +38,7 @@ if (env == "production") { const logger = winston.createLogger({ level: level, format: winston.format.combine( + winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.splat(), ...formatters @@ -43,9 +46,9 @@ const logger = winston.createLogger({ defaultMeta: defaultMeta, transports: [ new winston.transports.Console({ - handleExceptions: true - }) - ] + handleExceptions: true, + }), + ], }); /** @@ -102,9 +105,9 @@ var shim = bunyan.createLogger({ { type: "raw", level: "trace", - stream: new Bunyan2Winston(logger) - } - ] + stream: new Bunyan2Winston(logger), + }, + ], }); logger.bunyan = shim; @@ -112,5 +115,5 @@ logger.bunyan = shim; //global.console = logger; module.exports = { - logger: logger + logger: logger, }; diff --git a/src/utils/mount.js b/src/utils/mount.js index f3a61d3..eade177 100644 --- a/src/utils/mount.js +++ b/src/utils/mount.js @@ -2,7 +2,7 @@ const cp = require("child_process"); const { Filesystem } = require("../utils/filesystem"); // avoid using avail,size,used as it causes hangs when the fs is stale -FINDMNT_COMMON_OPTIONS = [ +const FINDMNT_COMMON_OPTIONS = [ "--output", "source,target,fstype,label,options", "-b", @@ -43,6 +43,14 @@ class Mount { spawn: cp.spawn, }; } + + if (!options.filesystem) { + options.filesystem = new Filesystem(); + } + } + + async getFilesystemInstance() { + return this.options.filesystem; } /** @@ -51,12 +59,12 @@ class Mount { * @param {*} device */ async deviceIsMounted(device) { - const filesystem = new Filesystem(); + const mount = this; + const filesystem = await mount.getFilesystemInstance(); if (device.startsWith("/")) { device = await filesystem.realpath(device); } - const mount = this; let args = []; args = args.concat(["--source", device]); args = args.concat(FINDMNT_COMMON_OPTIONS); @@ -114,12 +122,12 @@ class Mount { * @param {*} device */ async deviceIsMountedAtPath(device, path) { - const filesystem = new Filesystem(); + const mount = this; + const filesystem = await mount.getFilesystemInstance(); if (device.startsWith("/") && !device.startsWith("//")) { device = await filesystem.realpath(device); } - const mount = this; let args = []; args = args.concat(["--source", device]); args = args.concat(["--mountpoint", path]); @@ -282,8 +290,8 @@ class Mount { * @param {*} path */ async isBindMountedBlockDevice(path) { - const filesystem = new Filesystem(); const mount = this; + const filesystem = await mount.getFilesystemInstance(); const is_mounted = await mount.pathIsMounted(path); if (!is_mounted) { @@ -385,9 +393,6 @@ class Mount { const mount = this; args = args || []; - let stdout = ""; - let stderr = ""; - if (mount.options.sudo) { args.unshift(command); command = mount.options.paths.sudo; @@ -402,9 +407,13 @@ class Mount { ); console.log("executing mount command: %s", cleansedLog); - const child = mount.options.executor.spawn(command, args, options); return new Promise((resolve, reject) => { + const child = mount.options.executor.spawn(command, args, options); + + let stdout = ""; + let stderr = ""; + child.stdout.on("data", function (data) { stdout = stdout + data; }); diff --git a/src/utils/oneclient.js b/src/utils/oneclient.js index 6165ae8..68567b2 100644 --- a/src/utils/oneclient.js +++ b/src/utils/oneclient.js @@ -77,9 +77,6 @@ class OneClient { const oneclient = this; args = args || []; - let stdout = ""; - let stderr = ""; - if (oneclient.options.sudo) { args.unshift(command); command = oneclient.options.paths.sudo; @@ -93,9 +90,13 @@ class OneClient { ); console.log("executing oneclient command: %s", cleansedLog); - const child = oneclient.options.executor.spawn(command, args, options); return new Promise((resolve, reject) => { + const child = oneclient.options.executor.spawn(command, args, options); + + let stdout = ""; + let stderr = ""; + child.stdout.on("data", function (data) { stdout = stdout + data; }); diff --git a/src/utils/registry.js b/src/utils/registry.js new file mode 100644 index 0000000..b84944e --- /dev/null +++ b/src/utils/registry.js @@ -0,0 +1,53 @@ +class Registry { + constructor() { + this.data = {}; + } + + put(key, value) { + if (!value) { + delete this.data[key]; + return; + } + this.data[key] = value; + } + + get(key, initialValue = null) { + const val = this.data[key]; + if (val) { + return val; + } + + if (typeof initialValue == "function") { + initialValue = initialValue(); + } + + if (initialValue) { + this.put(key, initialValue); + return this.data[key]; + } + } + + async getAsync(key, initialValue = null) { + const val = this.data[key]; + if (val) { + return val; + } + + if (typeof initialValue == "function") { + initialValue = await initialValue(); + } + + if (initialValue) { + this.put(key, initialValue); + return this.data[key]; + } + } + + delete(key) { + delete this.data[key]; + } +} + +const registry = new Registry(); + +module.exports = registry;