remoteshell
This commit is contained in:
+145
-1
@@ -335,12 +335,155 @@ function scp(from, to, params = {}) {
|
||||
}));
|
||||
}
|
||||
|
||||
function loadSsh2Client() {
|
||||
try {
|
||||
return require("ssh2").Client;
|
||||
} catch (e) {
|
||||
throw new Error("remoteShell requires pure JS dependency ssh2. Please run npm install ssh2 in kit project.");
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeRemoteShellConfig(config = {}) {
|
||||
const host = config.host || config.hostname;
|
||||
if (!host) {
|
||||
throw new Error("remoteShell host is required");
|
||||
}
|
||||
|
||||
const username = config.username || config.user;
|
||||
if (!username) {
|
||||
throw new Error("remoteShell username is required");
|
||||
}
|
||||
|
||||
const password = resolveScpPassword(config);
|
||||
const privateKey = config.privateKey || config.keyText || (config.keyFile ? fs.readFileSync(path.resolve(config.keyFile), "utf-8") : undefined);
|
||||
const connectConfig = Object.assign({}, config, {
|
||||
host,
|
||||
username,
|
||||
port: config.port || 22,
|
||||
password,
|
||||
privateKey,
|
||||
passphrase: resolveValue(config.passphrase)
|
||||
});
|
||||
|
||||
delete connectConfig.user;
|
||||
delete connectConfig.hostname;
|
||||
delete connectConfig.pwd;
|
||||
delete connectConfig.plainPwd;
|
||||
delete connectConfig.plainPassword;
|
||||
delete connectConfig.plaintextPwd;
|
||||
delete connectConfig.plaintextPassword;
|
||||
delete connectConfig.keyText;
|
||||
delete connectConfig.keyFile;
|
||||
|
||||
return connectConfig;
|
||||
}
|
||||
|
||||
function remoteShell(config = {}) {
|
||||
const Client = loadSsh2Client();
|
||||
const connectConfig = normalizeRemoteShellConfig(config);
|
||||
let client = null;
|
||||
let connectPromise = null;
|
||||
|
||||
function connect() {
|
||||
if (client) {
|
||||
return Promise.resolve(client);
|
||||
}
|
||||
if (connectPromise) {
|
||||
return connectPromise;
|
||||
}
|
||||
|
||||
connectPromise = new Promise((resolve, reject) => {
|
||||
const conn = new Client();
|
||||
conn.once("ready", () => {
|
||||
client = conn;
|
||||
resolve(conn);
|
||||
});
|
||||
conn.once("error", (e) => {
|
||||
connectPromise = null;
|
||||
reject(e);
|
||||
});
|
||||
conn.once("close", () => {
|
||||
client = null;
|
||||
connectPromise = null;
|
||||
});
|
||||
conn.connect(connectConfig);
|
||||
});
|
||||
return connectPromise;
|
||||
}
|
||||
|
||||
async function exec(cmd, options = {}) {
|
||||
if (!cmd) {
|
||||
throw new Error("remoteShell exec cmd is required");
|
||||
}
|
||||
|
||||
const conn = await connect();
|
||||
return new Promise((resolve, reject) => {
|
||||
const execOptions = Object.assign({}, options);
|
||||
delete execOptions.stdio;
|
||||
delete execOptions.rejectOnError;
|
||||
conn.exec(cmd, execOptions, (err, stream) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
stream.on("error", reject);
|
||||
stream.on("close", (code, signal) => {
|
||||
const result = { code, signal, stdout, stderr };
|
||||
if (options.rejectOnError === false || code === 0) {
|
||||
resolve(result);
|
||||
} else {
|
||||
const error = new Error(`Remote command failed with code ${code}: ${cmd}`);
|
||||
error.result = result;
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
stream.on("data", (data) => {
|
||||
const text = data.toString();
|
||||
stdout += text;
|
||||
if (options.stdio === "inherit") {
|
||||
process.stdout.write(text);
|
||||
}
|
||||
});
|
||||
stream.stderr.on("data", (data) => {
|
||||
const text = data.toString();
|
||||
stderr += text;
|
||||
if (options.stdio === "inherit") {
|
||||
process.stderr.write(text);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function execText(cmd, options = {}) {
|
||||
const result = await exec(cmd, options);
|
||||
return result.stdout;
|
||||
}
|
||||
|
||||
async function call(cmd, options = {}) {
|
||||
return exec(cmd, Object.assign({ stdio: "inherit" }, options));
|
||||
}
|
||||
|
||||
function close() {
|
||||
if (client) {
|
||||
client.end();
|
||||
}
|
||||
client = null;
|
||||
connectPromise = null;
|
||||
}
|
||||
|
||||
return { connect, exec, execText, call, close };
|
||||
}
|
||||
|
||||
function exit(code = 0) {
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
function isSecretLogKey(key) {
|
||||
return /^(pwd|password|plainPwd|plainPassword|plaintextPwd|plaintextPassword)$/i.test(key);
|
||||
return /^(pwd|password|plainPwd|plainPassword|plaintextPwd|plaintextPassword|privateKey|passphrase|keyText)$/i.test(key);
|
||||
}
|
||||
|
||||
function redactLogValue(value) {
|
||||
@@ -403,6 +546,7 @@ module.exports = {
|
||||
readJson: withCallLog("readJson", readJson),
|
||||
writeJson: withCallLog("writeJson", writeJson),
|
||||
scp: withCallLog("scp", scp),
|
||||
remoteShell: withCallLog("remoteShell", remoteShell),
|
||||
exit: withCallLog("exit", exit),
|
||||
setVar: withCallLog("setVar", setVar),
|
||||
getVar: withCallLog("getVar", getVar),
|
||||
|
||||
Reference in New Issue
Block a user