democratic-csi/src/utils/general.js

295 lines
7.4 KiB
JavaScript

const _ = require("lodash");
const axios = require("axios");
const crypto = require("crypto");
const dns = require("dns");
const crc = require("crc");
axios.interceptors.request.use(
function (config) {
config.metadata = { startTime: new Date() };
return config;
},
function (error) {
return Promise.reject(error);
}
);
axios.interceptors.response.use(
function (response) {
response.config.metadata.endTime = new Date();
response.duration =
response.config.metadata.endTime - response.config.metadata.startTime;
return response;
},
function (error) {
error.config.metadata.endTime = new Date();
error.duration =
error.config.metadata.endTime - error.config.metadata.startTime;
return Promise.reject(error);
}
);
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
function trimchar(str, ch) {
var start = 0,
end = str.length;
while (start < end && str[start] === ch) ++start;
while (end > start && str[end - 1] === ch) --end;
return start > 0 || end < str.length ? str.substring(start, end) : str;
}
function md5(val) {
return crypto.createHash("md5").update(val).digest("hex");
}
function crc8(data) {
return crc.crc8(data);
}
function crc16(data) {
return crc.crc16(data);
}
function crc32(data) {
return crc.crc32(data);
}
function lockKeysFromRequest(call, serviceMethodName) {
switch (serviceMethodName) {
// controller
case "CreateVolume":
return ["create_volume_name_" + call.request.name];
case "DeleteVolume":
case "ControllerExpandVolume":
return ["volume_id_" + call.request.volume_id];
case "CreateSnapshot":
return [
"create_snapshot_name_" + call.request.name,
"volume_id_" + call.request.source_volume_id,
];
case "DeleteSnapshot":
return ["snapshot_id_" + call.request.snapshot_id];
// node
case "NodeStageVolume":
case "NodeUnstageVolume":
case "NodePublishVolume":
case "NodeUnpublishVolume":
case "NodeExpandVolume":
return ["volume_id_" + call.request.volume_id];
case "NodeGetVolumeStats":
default:
return [];
}
}
function getLargestNumber() {
let number;
for (let i = 0; i < arguments.length; i++) {
value = Number(arguments[i]);
if (isNaN(value)) {
continue;
}
if (isNaN(number)) {
number = value;
continue;
}
number = value > number ? value : number;
}
return number;
}
function stripWindowsDriveLetter(path) {
return path.replace(/^[a-zA-Z]:/, "");
}
function hasWindowsDriveLetter(path) {
return /^[a-zA-Z]:/i.test(path);
}
/**
* 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);
let senderr = false;
if (
options.validateStatus &&
typeof options.validateStatus == "function"
) {
senderr = true;
}
callback(senderr ? err : 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());
}
function before_string(target, search) {
if (!target.includes(search)) {
return "";
}
return target.substring(0, target.indexOf(search));
}
function after_string(target, search) {
if (!target.includes(search)) {
return "";
}
return target.substring(target.indexOf(search) + search.length);
}
function default_supported_block_filesystems() {
return ["btrfs", "exfat", "ext3", "ext4", "ext4dev", "ntfs", "vfat", "xfs"];
}
function default_supported_file_filesystems() {
return ["nfs", "cifs"];
}
async function retry(retries, retriesDelay, code, options = {}) {
let current_try = 0;
let maxwait = _.get(options, "maxwait");
let logerrors = _.get(options, "logerrors", false);
let retryCondition = options.retryCondition;
let executeStartTime;
do {
current_try++;
try {
executeStartTime = Date.now();
return await code();
} catch (err) {
if (current_try >= retries) {
throw err;
}
if (retryCondition) {
let retry = retryCondition(err);
if (!retry) {
console.log(`retry - failed condition, not trying again`);
//console.log(code.toString(), retryCondition.toString());
throw err;
}
}
if (logerrors === true) {
console.log(`retry - err:`, err);
}
}
// handle minExecutionTime
if (options.minExecutionTime > 0) {
let executionElapsedTIme = Date.now() - executeStartTime;
let minExecutionDelayTime =
options.minExecutionTime - executionElapsedTIme;
if (minExecutionDelayTime > 0) {
await sleep(minExecutionDelayTime);
}
}
// handle delay
let sleep_time = retriesDelay;
if (_.get(options, "exponential", false) === true) {
sleep_time = retriesDelay * current_try;
}
if (maxwait) {
if (sleep_time > maxwait) {
sleep_time = maxwait;
}
}
if (sleep_time > 0) {
console.log(`retry - waiting ${sleep_time}ms before trying again`);
await sleep(sleep_time);
}
} while (true);
}
async function hostname_lookup(hostname) {
return new Promise((resolve, reject) => {
dns.lookup(hostname, function (err, result) {
if (err) {
return reject(err);
}
return resolve(result);
});
});
}
module.exports.sleep = sleep;
module.exports.md5 = md5;
module.exports.crc32 = crc32;
module.exports.crc16 = crc16;
module.exports.crc8 = crc8;
module.exports.lockKeysFromRequest = lockKeysFromRequest;
module.exports.getLargestNumber = getLargestNumber;
module.exports.stringify = stringify;
module.exports.before_string = before_string;
module.exports.after_string = after_string;
module.exports.stripWindowsDriveLetter = stripWindowsDriveLetter;
module.exports.hasWindowsDriveLetter = hasWindowsDriveLetter;
module.exports.axios_request = axios_request;
module.exports.default_supported_block_filesystems =
default_supported_block_filesystems;
module.exports.default_supported_file_filesystems =
default_supported_file_filesystems;
module.exports.retry = retry;
module.exports.trimchar = trimchar;
module.exports.hostname_lookup = hostname_lookup;