reduce memory footprint by reusing object instances

Signed-off-by: Travis Glenn Hansen <travisghansen@yahoo.com>
This commit is contained in:
Travis Glenn Hansen 2022-04-06 11:14:39 -06:00
parent 1783aebb75
commit 1f2c8cf58d
19 changed files with 412 additions and 229 deletions

View File

@ -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 # v1.6.1
Released 2022-03-23 Released 2022-03-23

View File

@ -134,6 +134,7 @@ async function requestHandlerProxy(call, callback, serviceMethodName) {
cleansedCall.request[key] = "redacted"; cleansedCall.request[key] = "redacted";
} }
} }
try { try {
logger.info( logger.info(
"new request - driver: %s method: %s call: %j", "new request - driver: %s method: %s call: %j",
@ -337,17 +338,38 @@ logger.info(
[`SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`].forEach( [`SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`].forEach(
(eventType) => { (eventType) => {
process.on(eventType, (code) => { process.on(eventType, async (code) => {
console.log(`running server shutdown, exit code: ${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; let socketPath = bindSocket;
socketPath = socketPath.replace(/^unix:\/\//g, ""); socketPath = socketPath.replace(/^unix:\/\//g, "");
if (socketPath && fs.existsSync(socketPath)) { if (socketPath && fs.existsSync(socketPath)) {
let fsStat = fs.statSync(socketPath); let fsStat = fs.statSync(socketPath);
if (fsStat.isSocket()) { if (fsStat.isSocket()) {
fs.unlinkSync(socketPath); fs.unlinkSync(socketPath);
console.log(`removed grpc socket ${socketPath}`);
} }
} }
console.log("server fully shutdown, exiting");
process.exit(code); process.exit(code);
}); });
} }
@ -378,6 +400,15 @@ if (process.env.MANUAL_GC == "1") {
}, process.env.MANUAL_GC_INTERVAL || 60000); }, 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) { if (require.main === module) {
(async function () { (async function () {
try { try {
@ -420,6 +451,8 @@ if (require.main === module) {
} }
); );
}); });
fs.chmodSync(socketPath, 0o666);
} }
csiServer.start(); csiServer.start();
} catch (e) { } catch (e) {

36
package-lock.json generated
View File

@ -70,9 +70,9 @@
} }
}, },
"node_modules/@grpc/grpc-js": { "node_modules/@grpc/grpc-js": {
"version": "1.6.1", "version": "1.6.2",
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.1.tgz", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.2.tgz",
"integrity": "sha512-ix3rQS64rKL1s6CfIaRgnts+RNYZZ2NaYyTK7iimai6an/0GGDbukzy990hJ5vtKHjhaqJxJMB6Qq7BMZ0zZSQ==", "integrity": "sha512-9+89Ne1K8F9u86T+l1yIV2DS+dWHYVK61SsDZN4MFTFehOOaJ4rHxa1cW8Lwdn2/6tOx7N3+SY/vfcjztOHopA==",
"dependencies": { "dependencies": {
"@grpc/proto-loader": "^0.6.4", "@grpc/proto-loader": "^0.6.4",
"@types/node": ">=12.12.47" "@types/node": ">=12.12.47"
@ -1689,9 +1689,9 @@
} }
}, },
"node_modules/keyv": { "node_modules/keyv": {
"version": "4.2.0", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.2.0.tgz", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.2.1.tgz",
"integrity": "sha512-mcRm0yqY2Z2FdE3Tkb7hNEUN7J7VdedNZ8F6vS5jX04gNo2pyOWqfyW+chW9amiS3gbULPucyRzVq0gjPUmhTA==", "integrity": "sha512-cAJq5cTfxQdq1DHZEVNpnk4mEvhP+8UP8UQftLtTtJ98beKkRHf+62M0mIDM2u/IWXyP8bmGB375/6uGdSX2MA==",
"dependencies": { "dependencies": {
"compress-brotli": "^1.3.6", "compress-brotli": "^1.3.6",
"json-buffer": "3.0.1" "json-buffer": "3.0.1"
@ -2843,9 +2843,9 @@
} }
}, },
"node_modules/winston": { "node_modules/winston": {
"version": "3.6.0", "version": "3.7.2",
"resolved": "https://registry.npmjs.org/winston/-/winston-3.6.0.tgz", "resolved": "https://registry.npmjs.org/winston/-/winston-3.7.2.tgz",
"integrity": "sha512-9j8T75p+bcN6D00sF/zjFVmPp+t8KMPB1MzbbzYjeN9VWxdsYnTB40TkbNUEXAmILEfChMvAMgidlX64OG3p6w==", "integrity": "sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng==",
"dependencies": { "dependencies": {
"@dabh/diagnostics": "^2.0.2", "@dabh/diagnostics": "^2.0.2",
"async": "^3.2.3", "async": "^3.2.3",
@ -3008,9 +3008,9 @@
} }
}, },
"@grpc/grpc-js": { "@grpc/grpc-js": {
"version": "1.6.1", "version": "1.6.2",
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.1.tgz", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.2.tgz",
"integrity": "sha512-ix3rQS64rKL1s6CfIaRgnts+RNYZZ2NaYyTK7iimai6an/0GGDbukzy990hJ5vtKHjhaqJxJMB6Qq7BMZ0zZSQ==", "integrity": "sha512-9+89Ne1K8F9u86T+l1yIV2DS+dWHYVK61SsDZN4MFTFehOOaJ4rHxa1cW8Lwdn2/6tOx7N3+SY/vfcjztOHopA==",
"requires": { "requires": {
"@grpc/proto-loader": "^0.6.4", "@grpc/proto-loader": "^0.6.4",
"@types/node": ">=12.12.47" "@types/node": ">=12.12.47"
@ -4278,9 +4278,9 @@
} }
}, },
"keyv": { "keyv": {
"version": "4.2.0", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.2.0.tgz", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.2.1.tgz",
"integrity": "sha512-mcRm0yqY2Z2FdE3Tkb7hNEUN7J7VdedNZ8F6vS5jX04gNo2pyOWqfyW+chW9amiS3gbULPucyRzVq0gjPUmhTA==", "integrity": "sha512-cAJq5cTfxQdq1DHZEVNpnk4mEvhP+8UP8UQftLtTtJ98beKkRHf+62M0mIDM2u/IWXyP8bmGB375/6uGdSX2MA==",
"requires": { "requires": {
"compress-brotli": "^1.3.6", "compress-brotli": "^1.3.6",
"json-buffer": "3.0.1" "json-buffer": "3.0.1"
@ -5147,9 +5147,9 @@
} }
}, },
"winston": { "winston": {
"version": "3.6.0", "version": "3.7.2",
"resolved": "https://registry.npmjs.org/winston/-/winston-3.6.0.tgz", "resolved": "https://registry.npmjs.org/winston/-/winston-3.7.2.tgz",
"integrity": "sha512-9j8T75p+bcN6D00sF/zjFVmPp+t8KMPB1MzbbzYjeN9VWxdsYnTB40TkbNUEXAmILEfChMvAMgidlX64OG3p6w==", "integrity": "sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng==",
"requires": { "requires": {
"@dabh/diagnostics": "^2.0.2", "@dabh/diagnostics": "^2.0.2",
"async": "^3.2.3", "async": "^3.2.3",

View File

@ -1,6 +1,6 @@
{ {
"name": "democratic-csi", "name": "democratic-csi",
"version": "1.6.1", "version": "1.6.2",
"description": "kubernetes csi driver framework", "description": "kubernetes csi driver framework",
"main": "bin/democratic-csi", "main": "bin/democratic-csi",
"scripts": { "scripts": {

View File

@ -3,8 +3,10 @@ const http = require("http");
const https = require("https"); const https = require("https");
const { axios_request, stringify } = require("../../../utils/general"); const { axios_request, stringify } = require("../../../utils/general");
const Mutex = require("async-mutex").Mutex; const Mutex = require("async-mutex").Mutex;
const registry = require("../../../utils/registry");
const USER_AGENT = "democratic-csi"; const USER_AGENT = "democratic-csi";
const __REGISTRY_NS__ = "SynologyHttpClient";
class SynologyHttpClient { class SynologyHttpClient {
constructor(options = {}) { constructor(options = {}) {
@ -22,27 +24,23 @@ class SynologyHttpClient {
} }
getHttpAgent() { getHttpAgent() {
if (!this.httpAgent) { return registry.get(`${__REGISTRY_NS__}:http_agent`, () => {
this.httpAgent = new http.Agent({ return new http.Agent({
keepAlive: true, keepAlive: true,
maxSockets: Infinity, maxSockets: Infinity,
rejectUnauthorized: !!!this.options.allowInsecure, rejectUnauthorized: !!!this.options.allowInsecure,
}); });
} });
return this.httpAgent;
} }
getHttpsAgent() { getHttpsAgent() {
if (!this.httpsAgent) { return registry.get(`${__REGISTRY_NS__}:https_agent`, () => {
this.httpsAgent = new https.Agent({ return new https.Agent({
keepAlive: true, keepAlive: true,
maxSockets: Infinity, maxSockets: Infinity,
rejectUnauthorized: !!!this.options.allowInsecure, rejectUnauthorized: !!!this.options.allowInsecure,
}); });
} });
return this.httpsAgent;
} }
log_response(error, response, body, options) { log_response(error, response, body, options) {

View File

@ -1,9 +1,12 @@
const { CsiBaseDriver } = require("../index"); const { CsiBaseDriver } = require("../index");
const { GrpcError, grpc } = require("../../utils/grpc"); const { GrpcError, grpc } = require("../../utils/grpc");
const registry = require("../../utils/registry");
const SynologyHttpClient = require("./http").SynologyHttpClient; const SynologyHttpClient = require("./http").SynologyHttpClient;
const semver = require("semver"); const semver = require("semver");
const sleep = require("../../utils/general").sleep; const sleep = require("../../utils/general").sleep;
const __REGISTRY_NS__ = "ControllerSynologyDriver";
/** /**
* *
* Driver to provision storage on a synology device * Driver to provision storage on a synology device
@ -109,10 +112,9 @@ class ControllerSynologyDriver extends CsiBaseDriver {
} }
async getHttpClient() { async getHttpClient() {
if (!this.httpClient) { return registry.get(`${__REGISTRY_NS__}:http_client`, () => {
this.httpClient = new SynologyHttpClient(this.options.httpConnection); return new SynologyHttpClient(this.options.httpConnection);
} });
return this.httpClient;
} }
getDriverResourceType() { getDriverResourceType() {

View File

@ -1,6 +1,7 @@
const _ = require("lodash"); const _ = require("lodash");
const { ControllerZfsBaseDriver } = require("../controller-zfs"); const { ControllerZfsBaseDriver } = require("../controller-zfs");
const { GrpcError, grpc } = require("../../utils/grpc"); const { GrpcError, grpc } = require("../../utils/grpc");
const registry = require("../../utils/registry");
const SshClient = require("../../utils/ssh").SshClient; const SshClient = require("../../utils/ssh").SshClient;
const sleep = require("../../utils/general").sleep; const sleep = require("../../utils/general").sleep;
const { Zetabyte, ZfsSshProcessManager } = require("../../utils/zfs"); const { Zetabyte, ZfsSshProcessManager } = require("../../utils/zfs");
@ -8,16 +9,19 @@ const { Zetabyte, ZfsSshProcessManager } = require("../../utils/zfs");
const Handlebars = require("handlebars"); const Handlebars = require("handlebars");
const ISCSI_ASSETS_NAME_PROPERTY_NAME = "democratic-csi:iscsi_assets_name"; const ISCSI_ASSETS_NAME_PROPERTY_NAME = "democratic-csi:iscsi_assets_name";
const __REGISTRY_NS__ = "ControllerZfsGenericDriver";
class ControllerZfsGenericDriver extends ControllerZfsBaseDriver { class ControllerZfsGenericDriver extends ControllerZfsBaseDriver {
getExecClient() { getExecClient() {
return registry.get(`${__REGISTRY_NS__}:exec_client`, () => {
return new SshClient({ return new SshClient({
logger: this.ctx.logger, logger: this.ctx.logger,
connection: this.options.sshConnection, connection: this.options.sshConnection,
}); });
});
} }
async getZetabyte() { async getZetabyte() {
return registry.get(`${__REGISTRY_NS__}:zb`, () => {
const execClient = this.getExecClient(); const execClient = this.getExecClient();
const options = {}; const options = {};
options.executor = new ZfsSshProcessManager(execClient); options.executor = new ZfsSshProcessManager(execClient);
@ -38,6 +42,7 @@ class ControllerZfsGenericDriver extends ControllerZfsBaseDriver {
} }
return new Zetabyte(options); return new Zetabyte(options);
});
} }
/** /**
@ -362,7 +367,9 @@ delete ${iscsiName}
taregetCliCommand.push("|"); taregetCliCommand.push("|");
taregetCliCommand.push("targetcli"); taregetCliCommand.push("targetcli");
if (_.get(this.options, "iscsi.shareStrategyTargetCli.sudoEnabled", false)) { if (
_.get(this.options, "iscsi.shareStrategyTargetCli.sudoEnabled", false)
) {
command = "sudo"; command = "sudo";
args.unshift("sh"); args.unshift("sh");
} }

View File

@ -2,12 +2,14 @@ const _ = require("lodash");
const { ControllerZfsBaseDriver } = require("../controller-zfs"); const { ControllerZfsBaseDriver } = require("../controller-zfs");
const { GrpcError, grpc } = require("../../utils/grpc"); const { GrpcError, grpc } = require("../../utils/grpc");
const LocalCliExecClient = require("./exec").LocalCliClient; const LocalCliExecClient = require("./exec").LocalCliClient;
const os = require("os"); const registry = require("../../utils/registry");
const { Zetabyte } = require("../../utils/zfs"); const { Zetabyte } = require("../../utils/zfs");
const ZFS_ASSET_NAME_PROPERTY_NAME = "zfs_asset_name"; const ZFS_ASSET_NAME_PROPERTY_NAME = "zfs_asset_name";
const NODE_TOPOLOGY_KEY_NAME = "org.democratic-csi.topology/node"; const NODE_TOPOLOGY_KEY_NAME = "org.democratic-csi.topology/node";
const __REGISTRY_NS__ = "ControllerZfsLocalDriver";
class ControllerZfsLocalDriver extends ControllerZfsBaseDriver { class ControllerZfsLocalDriver extends ControllerZfsBaseDriver {
constructor(ctx, options) { constructor(ctx, options) {
const i_caps = _.get( const i_caps = _.get(
@ -29,12 +31,15 @@ class ControllerZfsLocalDriver extends ControllerZfsBaseDriver {
} }
getExecClient() { getExecClient() {
return registry.get(`${__REGISTRY_NS__}:exec_client`, () => {
return new LocalCliExecClient({ return new LocalCliExecClient({
logger: this.ctx.logger, logger: this.ctx.logger,
}); });
})
} }
async getZetabyte() { async getZetabyte() {
return registry.get(`${__REGISTRY_NS__}:zb`, () => {
const execClient = this.getExecClient(); const execClient = this.getExecClient();
const options = {}; const options = {};
@ -66,6 +71,7 @@ class ControllerZfsLocalDriver extends ControllerZfsBaseDriver {
} }
return new Zetabyte(options); return new Zetabyte(options);
});
} }
/** /**

View File

@ -5,6 +5,7 @@ const HttpClient = require("./http").Client;
const TrueNASApiClient = require("./http/api").Api; const TrueNASApiClient = require("./http/api").Api;
const { Zetabyte } = require("../../utils/zfs"); const { Zetabyte } = require("../../utils/zfs");
const getLargestNumber = require("../../utils/general").getLargestNumber; const getLargestNumber = require("../../utils/general").getLargestNumber;
const registry = require("../../utils/registry");
const sleep = require("../../utils/general").sleep; const sleep = require("../../utils/general").sleep;
const Handlebars = require("handlebars"); const Handlebars = require("handlebars");
@ -44,6 +45,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 __REGISTRY_NS__ = "FreeNASApiDriver";
class FreeNASApiDriver extends CsiBaseDriver { class FreeNASApiDriver extends CsiBaseDriver {
constructor(ctx, options) { constructor(ctx, options) {
super(...arguments); super(...arguments);
@ -154,6 +157,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
* @returns * @returns
*/ */
async getZetabyte() { async getZetabyte() {
return registry.get(`${__REGISTRY_NS__}:zb`, () => {
return new Zetabyte({ return new Zetabyte({
executor: { executor: {
spawn: function () { spawn: function () {
@ -163,6 +167,7 @@ class FreeNASApiDriver extends CsiBaseDriver {
}, },
}, },
}); });
});
} }
/** /**
@ -1887,11 +1892,12 @@ class FreeNASApiDriver extends CsiBaseDriver {
} }
async getHttpClient() { async getHttpClient() {
return registry.get(`${__REGISTRY_NS__}:http_client`, () => {
const client = new HttpClient(this.options.httpConnection); const client = new HttpClient(this.options.httpConnection);
client.logger = this.ctx.logger; client.logger = this.ctx.logger;
client.setApiVersion(2); // requires version 2 client.setApiVersion(2); // requires version 2
return client; return client;
});
} }
async getMinimumVolumeSize() { async getMinimumVolumeSize() {
@ -1903,11 +1909,10 @@ class FreeNASApiDriver extends CsiBaseDriver {
} }
async getTrueNASHttpApiClient() { async getTrueNASHttpApiClient() {
const driver = this; return registry.get(`${__REGISTRY_NS__}:api_client`, () => {
const httpClient = await this.getHttpClient(); const httpClient = await this.getHttpClient();
const apiClient = new TrueNASApiClient(httpClient, driver.ctx.cache); return new TrueNASApiClient(httpClient, this.ctx.cache);
});
return apiClient;
} }
assertCapabilities(capabilities) { assertCapabilities(capabilities) {

View File

@ -1,8 +1,10 @@
const registry = require("../../../utils/registry");
const { sleep, stringify } = require("../../../utils/general"); const { sleep, stringify } = require("../../../utils/general");
const { Zetabyte } = require("../../../utils/zfs"); const { Zetabyte } = require("../../../utils/zfs");
// used for in-memory cache of the version info // used for in-memory cache of the version info
const FREENAS_SYSTEM_VERSION_CACHE_KEY = "freenas:system_version"; const FREENAS_SYSTEM_VERSION_CACHE_KEY = "freenas:system_version";
const __REGISTRY_NS__ = "FreeNASHttpApi";
class Api { class Api {
constructor(client, cache, options = {}) { constructor(client, cache, options = {}) {
@ -20,6 +22,7 @@ class Api {
* @returns * @returns
*/ */
async getZetabyte() { async getZetabyte() {
return registry.get(`${__REGISTRY_NS__}:zb`, () => {
return new Zetabyte({ return new Zetabyte({
executor: { executor: {
spawn: function () { spawn: function () {
@ -29,6 +32,7 @@ class Api {
}, },
}, },
}); });
});
} }
async findResourceByProperties(endpoint, match) { async findResourceByProperties(endpoint, match) {

View File

@ -1,6 +1,7 @@
const _ = require("lodash"); const _ = require("lodash");
const { ControllerZfsBaseDriver } = require("../controller-zfs"); const { ControllerZfsBaseDriver } = require("../controller-zfs");
const { GrpcError, grpc } = require("../../utils/grpc"); const { GrpcError, grpc } = require("../../utils/grpc");
const registry = require("../../utils/registry");
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");
@ -22,15 +23,20 @@ const FREENAS_ISCSI_ASSETS_NAME_PROPERTY_NAME =
// used for in-memory cache of the version info // used for in-memory cache of the version info
const FREENAS_SYSTEM_VERSION_CACHE_KEY = "freenas:system_version"; const FREENAS_SYSTEM_VERSION_CACHE_KEY = "freenas:system_version";
const __REGISTRY_NS__ = "FreeNASSshDriver";
class FreeNASSshDriver extends ControllerZfsBaseDriver { class FreeNASSshDriver extends ControllerZfsBaseDriver {
getExecClient() { getExecClient() {
return registry.get(`${__REGISTRY_NS__}:exec_client`, () => {
return new SshClient({ return new SshClient({
logger: this.ctx.logger, logger: this.ctx.logger,
connection: this.options.sshConnection, connection: this.options.sshConnection,
}); });
});
} }
async getZetabyte() { async getZetabyte() {
return registry.get(`${__REGISTRY_NS__}:zb`, () => {
const sshClient = this.getExecClient(); const sshClient = this.getExecClient();
const options = {}; const options = {};
options.executor = new ZfsSshProcessManager(sshClient); options.executor = new ZfsSshProcessManager(sshClient);
@ -51,6 +57,7 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
} }
return new Zetabyte(options); return new Zetabyte(options);
});
} }
/** /**
@ -88,6 +95,8 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
} }
async getHttpClient(autoDetectVersion = true) { async getHttpClient(autoDetectVersion = true) {
const autodetectkey = autoDetectVersion === true ? 1 : 0
return registry.get(`${__REGISTRY_NS__}:http_client:autoDetectVersion_${autodetectkey}`, () => {
const client = new HttpClient(this.options.httpConnection); const client = new HttpClient(this.options.httpConnection);
client.logger = this.ctx.logger; client.logger = this.ctx.logger;
@ -97,6 +106,8 @@ class FreeNASSshDriver extends ControllerZfsBaseDriver {
} }
return client; return client;
});
} }
getDriverShareType() { getDriverShareType() {

View File

@ -7,10 +7,13 @@ const { Mount } = require("../utils/mount");
const { OneClient } = require("../utils/oneclient"); const { OneClient } = require("../utils/oneclient");
const { Filesystem } = require("../utils/filesystem"); const { Filesystem } = require("../utils/filesystem");
const { ISCSI } = require("../utils/iscsi"); const { ISCSI } = require("../utils/iscsi");
const registry = require("../utils/registry");
const semver = require("semver"); const semver = require("semver");
const sleep = require("../utils/general").sleep; const sleep = require("../utils/general").sleep;
const { Zetabyte } = require("../utils/zfs"); const { Zetabyte } = require("../utils/zfs");
const __REGISTRY_NS__ = "CsiBaseDriver";
/** /**
* common code shared between all drivers * common code shared between all drivers
* this is **NOT** meant to work as a proxy * this is **NOT** meant to work as a proxy
@ -93,6 +96,71 @@ class CsiBaseDriver {
return normalized; 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) { async GetPluginInfo(call) {
return { return {
name: this.ctx.args.csiName, name: this.ctx.args.csiName,
@ -276,9 +344,9 @@ class CsiBaseDriver {
*/ */
async NodeStageVolume(call) { async NodeStageVolume(call) {
const driver = this; const driver = this;
const mount = new Mount(); const mount = driver.getDefaultMountInstance();
const filesystem = new Filesystem(); const filesystem = driver.getDefaultFilesystemInstance();
const iscsi = new ISCSI(); const iscsi = driver.getDefaultISCSIInstance();
let result; let result;
let device; let device;
@ -590,7 +658,7 @@ class CsiBaseDriver {
break; break;
case "oneclient": case "oneclient":
let oneclient = new OneClient(); let oneclient = driver.getDefaultOneClientInstance();
device = "oneclient"; device = "oneclient";
result = await mount.deviceIsMountedAtPath(device, staging_target_path); result = await mount.deviceIsMountedAtPath(device, staging_target_path);
if (result) { if (result) {
@ -634,23 +702,7 @@ class CsiBaseDriver {
break; break;
case "zfs-local": case "zfs-local":
// TODO: make this a geneic zb instance (to ensure works with node-manual driver) // TODO: make this a geneic zb instance (to ensure works with node-manual driver)
const zb = new Zetabyte({ const zb = driver.getDefaultZetabyteInstance();
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,
});
result = await zb.zfs.get(`${volume_context.zfs_asset_name}`, [ result = await zb.zfs.get(`${volume_context.zfs_asset_name}`, [
"type", "type",
"mountpoint", "mountpoint",
@ -861,9 +913,9 @@ class CsiBaseDriver {
*/ */
async NodeUnstageVolume(call) { async NodeUnstageVolume(call) {
const driver = this; const driver = this;
const mount = new Mount(); const mount = driver.getDefaultMountInstance();
const filesystem = new Filesystem(); const filesystem = driver.getDefaultFilesystemInstance();
const iscsi = new ISCSI(); const iscsi = driver.getDefaultISCSIInstance();
let result; let result;
let is_block = false; let is_block = false;
let is_device_mapper = false; let is_device_mapper = false;
@ -1081,8 +1133,8 @@ class CsiBaseDriver {
async NodePublishVolume(call) { async NodePublishVolume(call) {
const driver = this; const driver = this;
const mount = new Mount(); const mount = driver.getDefaultMountInstance();
const filesystem = new Filesystem(); const filesystem = driver.getDefaultFilesystemInstance();
let result; let result;
const volume_id = call.request.volume_id; const volume_id = call.request.volume_id;
@ -1236,8 +1288,8 @@ class CsiBaseDriver {
async NodeUnpublishVolume(call) { async NodeUnpublishVolume(call) {
const driver = this; const driver = this;
const mount = new Mount(); const mount = driver.getDefaultMountInstance();
const filesystem = new Filesystem(); const filesystem = driver.getDefaultFilesystemInstance();
let result; let result;
const volume_id = call.request.volume_id; const volume_id = call.request.volume_id;
@ -1316,8 +1368,8 @@ class CsiBaseDriver {
async NodeGetVolumeStats(call) { async NodeGetVolumeStats(call) {
const driver = this; const driver = this;
const mount = new Mount(); const mount = driver.getDefaultMountInstance();
const filesystem = new Filesystem(); const filesystem = driver.getDefaultFilesystemInstance();
let result; let result;
let device_path; let device_path;
let access_type; let access_type;
@ -1414,8 +1466,9 @@ class CsiBaseDriver {
* @param {*} call * @param {*} call
*/ */
async NodeExpandVolume(call) { async NodeExpandVolume(call) {
const mount = new Mount(); const driver = this;
const filesystem = new Filesystem(); const mount = driver.getDefaultMountInstance();
const filesystem = driver.getDefaultFilesystemInstance();
let device; let device;
let fs_info; let fs_info;
let device_path; let device_path;

View File

@ -2,6 +2,7 @@ const fs = require("fs");
const { CsiBaseDriver } = require("../index"); const { CsiBaseDriver } = require("../index");
const { GrpcError, grpc } = require("../../utils/grpc"); const { GrpcError, grpc } = require("../../utils/grpc");
const { Filesystem } = require("../../utils/filesystem"); const { Filesystem } = require("../../utils/filesystem");
const registry = require("../../utils/registry");
const semver = require("semver"); const semver = require("semver");
const SshClient = require("../../utils/ssh").SshClient; const SshClient = require("../../utils/ssh").SshClient;
const { Zetabyte, ZfsSshProcessManager } = require("../../utils/zfs"); const { Zetabyte, ZfsSshProcessManager } = require("../../utils/zfs");
@ -14,6 +15,7 @@ const VOLUME_CONTEXT_PROVISIONER_DRIVER_PROPERTY_NAME =
"democratic-csi:volume_context_provisioner_driver"; "democratic-csi:volume_context_provisioner_driver";
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 __REGISTRY_NS__ = "ZfsLocalEphemeralInlineDriver";
/** /**
* https://github.com/kubernetes/enhancements/blob/master/keps/sig-storage/20190122-csi-inline-volumes.md * https://github.com/kubernetes/enhancements/blob/master/keps/sig-storage/20190122-csi-inline-volumes.md
@ -123,13 +125,16 @@ class ZfsLocalEphemeralInlineDriver extends CsiBaseDriver {
} }
getSshClient() { getSshClient() {
return registry.get(`${__REGISTRY_NS__}:ssh_client`, () => {
return new SshClient({ return new SshClient({
logger: this.ctx.logger, logger: this.ctx.logger,
connection: this.options.sshConnection, connection: this.options.sshConnection,
}); });
});
} }
getZetabyte() { getZetabyte() {
return registry.get(`${__REGISTRY_NS__}:zb`, () => {
let sshClient; let sshClient;
let executor; let executor;
if (this.options.sshConnection) { if (this.options.sshConnection) {
@ -145,6 +150,7 @@ class ZfsLocalEphemeralInlineDriver extends CsiBaseDriver {
zfs: "/usr/sbin/zfs", zfs: "/usr/sbin/zfs",
}, },
}); });
});
} }
getDatasetParentName() { getDatasetParentName() {

View File

@ -610,17 +610,17 @@ class Filesystem {
const filesystem = this; const filesystem = this;
args = args || []; args = args || [];
let stdout = "";
let stderr = "";
if (filesystem.options.sudo) { if (filesystem.options.sudo) {
args.unshift(command); args.unshift(command);
command = filesystem.options.paths.sudo; command = filesystem.options.paths.sudo;
} }
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);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const child = filesystem.options.executor.spawn(command, args, options);
let stdout = "";
let stderr = "";
child.stdout.on("data", function (data) { child.stdout.on("data", function (data) {
stdout = stdout + data; stdout = stdout + data;
}); });

View File

@ -523,17 +523,18 @@ class ISCSI {
const iscsi = this; const iscsi = this;
args = args || []; args = args || [];
let stdout = "";
let stderr = "";
if (iscsi.options.sudo) { if (iscsi.options.sudo) {
args.unshift(command); args.unshift(command);
command = iscsi.options.paths.sudo; command = iscsi.options.paths.sudo;
} }
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);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const child = iscsi.options.executor.spawn(command, args, options);
let stdout = "";
let stderr = "";
child.stdout.on("data", function (data) { child.stdout.on("data", function (data) {
stdout = stdout + data; stdout = stdout + data;
}); });

View File

@ -1,3 +1,5 @@
const os = require("os");
/** /**
* Levels * Levels
* *
@ -27,7 +29,7 @@ let formatters;
let defaultMeta; let defaultMeta;
if (env == "production") { if (env == "production") {
formatters = [winston.format.json()]; formatters = [winston.format.json()];
defaultMeta = { service: "democratic-csi" }; defaultMeta = { service: "democratic-csi", host: os.hostname() };
} else { } else {
formatters = [winston.format.colorize(), winston.format.simple()]; formatters = [winston.format.colorize(), winston.format.simple()];
defaultMeta = {}; defaultMeta = {};
@ -36,6 +38,7 @@ if (env == "production") {
const logger = winston.createLogger({ const logger = winston.createLogger({
level: level, level: level,
format: winston.format.combine( format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }), winston.format.errors({ stack: true }),
winston.format.splat(), winston.format.splat(),
...formatters ...formatters
@ -43,9 +46,9 @@ const logger = winston.createLogger({
defaultMeta: defaultMeta, defaultMeta: defaultMeta,
transports: [ transports: [
new winston.transports.Console({ new winston.transports.Console({
handleExceptions: true handleExceptions: true,
}) }),
] ],
}); });
/** /**
@ -102,9 +105,9 @@ var shim = bunyan.createLogger({
{ {
type: "raw", type: "raw",
level: "trace", level: "trace",
stream: new Bunyan2Winston(logger) stream: new Bunyan2Winston(logger),
} },
] ],
}); });
logger.bunyan = shim; logger.bunyan = shim;
@ -112,5 +115,5 @@ logger.bunyan = shim;
//global.console = logger; //global.console = logger;
module.exports = { module.exports = {
logger: logger logger: logger,
}; };

View File

@ -2,7 +2,7 @@ const cp = require("child_process");
const { Filesystem } = require("../utils/filesystem"); const { Filesystem } = require("../utils/filesystem");
// avoid using avail,size,used as it causes hangs when the fs is stale // avoid using avail,size,used as it causes hangs when the fs is stale
FINDMNT_COMMON_OPTIONS = [ const FINDMNT_COMMON_OPTIONS = [
"--output", "--output",
"source,target,fstype,label,options", "source,target,fstype,label,options",
"-b", "-b",
@ -43,6 +43,14 @@ class Mount {
spawn: cp.spawn, spawn: cp.spawn,
}; };
} }
if (!options.filesystem) {
options.filesystem = new Filesystem();
}
}
async getFilesystemInstance() {
return this.options.filesystem;
} }
/** /**
@ -51,12 +59,12 @@ class Mount {
* @param {*} device * @param {*} device
*/ */
async deviceIsMounted(device) { async deviceIsMounted(device) {
const filesystem = new Filesystem(); const mount = this;
const filesystem = await mount.getFilesystemInstance();
if (device.startsWith("/")) { if (device.startsWith("/")) {
device = await filesystem.realpath(device); device = await filesystem.realpath(device);
} }
const mount = this;
let args = []; let args = [];
args = args.concat(["--source", device]); args = args.concat(["--source", device]);
args = args.concat(FINDMNT_COMMON_OPTIONS); args = args.concat(FINDMNT_COMMON_OPTIONS);
@ -114,12 +122,12 @@ class Mount {
* @param {*} device * @param {*} device
*/ */
async deviceIsMountedAtPath(device, path) { async deviceIsMountedAtPath(device, path) {
const filesystem = new Filesystem(); const mount = this;
const filesystem = await mount.getFilesystemInstance();
if (device.startsWith("/") && !device.startsWith("//")) { if (device.startsWith("/") && !device.startsWith("//")) {
device = await filesystem.realpath(device); device = await filesystem.realpath(device);
} }
const mount = this;
let args = []; let args = [];
args = args.concat(["--source", device]); args = args.concat(["--source", device]);
args = args.concat(["--mountpoint", path]); args = args.concat(["--mountpoint", path]);
@ -282,8 +290,8 @@ class Mount {
* @param {*} path * @param {*} path
*/ */
async isBindMountedBlockDevice(path) { async isBindMountedBlockDevice(path) {
const filesystem = new Filesystem();
const mount = this; const mount = this;
const filesystem = await mount.getFilesystemInstance();
const is_mounted = await mount.pathIsMounted(path); const is_mounted = await mount.pathIsMounted(path);
if (!is_mounted) { if (!is_mounted) {
@ -385,9 +393,6 @@ class Mount {
const mount = this; const mount = this;
args = args || []; args = args || [];
let stdout = "";
let stderr = "";
if (mount.options.sudo) { if (mount.options.sudo) {
args.unshift(command); args.unshift(command);
command = mount.options.paths.sudo; command = mount.options.paths.sudo;
@ -402,9 +407,13 @@ 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);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const child = mount.options.executor.spawn(command, args, options);
let stdout = "";
let stderr = "";
child.stdout.on("data", function (data) { child.stdout.on("data", function (data) {
stdout = stdout + data; stdout = stdout + data;
}); });

View File

@ -77,9 +77,6 @@ class OneClient {
const oneclient = this; const oneclient = this;
args = args || []; args = args || [];
let stdout = "";
let stderr = "";
if (oneclient.options.sudo) { if (oneclient.options.sudo) {
args.unshift(command); args.unshift(command);
command = oneclient.options.paths.sudo; command = oneclient.options.paths.sudo;
@ -93,9 +90,13 @@ 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);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const child = oneclient.options.executor.spawn(command, args, options);
let stdout = "";
let stderr = "";
child.stdout.on("data", function (data) { child.stdout.on("data", function (data) {
stdout = stdout + data; stdout = stdout + data;
}); });

37
src/utils/registry.js Normal file
View File

@ -0,0 +1,37 @@
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];
}
}
delete(key) {
delete this.data[key];
}
}
const registry = new Registry();
module.exports = registry;