ssh-client-wrapper 2.4.1 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/rsyncExec.js CHANGED
@@ -7,7 +7,7 @@ const { glob } = require("glob");
7
7
  const { connect, retryWrapper, sshExec } = require("./sshExec.js");
8
8
  const { fork } = require("./fork.js");
9
9
 
10
- const { sshCmd, rsyncCmd, getSshOption } = require("./util.js");
10
+ const { sshCmd, rsyncCmd, acceptableRsyncRetrunCodes, getSshOption } = require("./util.js");
11
11
 
12
12
  async function checkRsyncVersion (timeout) {
13
13
  const rt = {};
@@ -79,7 +79,7 @@ async function send (hostInfo, argSrc, dst, rsyncOpt, timeout) {
79
79
  }
80
80
  debug(`send ${src} to ${hostInfo.host}:${dst}`);
81
81
  debugVerbose(`send: ${rsyncCmd} ${args.join(" ")}`);
82
- return retryWrapper(fork.bind(null, hostInfo, rsyncCmd, args, timeout, null, [12]), hostInfo);
82
+ return retryWrapper(fork.bind(null, hostInfo, rsyncCmd, args, timeout, null, acceptableRsyncRetrunCodes), hostInfo);
83
83
  }
84
84
 
85
85
  /**
@@ -119,7 +119,7 @@ async function recv (hostInfo, argSrc, dst, rsyncOpt, timeout) {
119
119
 
120
120
  debug(`recv ${src} to ${dst}`);
121
121
  debugVerbose(`recv: ${rsyncCmd} ${args.join(" ")}`);
122
- return retryWrapper(fork.bind(null, hostInfo, rsyncCmd, args, timeout, null, [12]), hostInfo);
122
+ return retryWrapper(fork.bind(null, hostInfo, rsyncCmd, args, timeout, null, acceptableRsyncRetrunCodes), hostInfo);
123
123
  }
124
124
  module.exports = {
125
125
  checkRsyncVersion,
package/lib/sshExec.js CHANGED
@@ -247,7 +247,7 @@ async function ls (hostInfo, target, lsOpt, timeout) {
247
247
  * @param {Integer} argMaxRetry - max retry number
248
248
  */
249
249
  async function retryWrapper (func, hostInfo, argMaxRetry, argDuration) {
250
- const maxRetry = argMaxRetry || hostInfo.maxRetry || 1;
250
+ const maxRetry = argMaxRetry || hostInfo.maxRetry || 3;
251
251
  const duration = argDuration || hostInfo.retryDuration || 1000;
252
252
  if (maxRetry === 1) {
253
253
  return func();
package/lib/util.js CHANGED
@@ -6,6 +6,7 @@ const { setTimeout: setTimeoutPromise } = require("timers/promises");
6
6
 
7
7
  const sshCmd = "ssh";
8
8
  const rsyncCmd = "rsync";
9
+ const acceptableRsyncRetrunCodes = [10, 11, 12, 13, 14];
9
10
 
10
11
  const rePwPrompt = /password:/;
11
12
  const rePhPrompt = /Enter passphrase for key/;
@@ -36,6 +37,7 @@ const hostInfoSchema = {
36
37
  noStrictHostkeyChecking: { type: "boolean" },
37
38
  ControlPersist: { type: "number", minimum: 0, default: 180 },
38
39
  ConnectTimeout: { type: "number", minimum: 0 },
40
+ ControlPersistDir: { type: "string", pattern: "\\S+", transform: ["trim"] },
39
41
  maxRetry: { type: "number", minimum: 0, default: 3 },
40
42
  retryDuration: { type: "number", minimum: 0, default: 1000 },
41
43
  sshOpt: { type: "array", minItems: 1, items: { type: "string", pattern: "\\S+", transform: ["trim"] } }
@@ -45,7 +47,8 @@ const hostInfoSchema = {
45
47
 
46
48
  const stringOptions = [
47
49
  "user",
48
- "keyFile"
50
+ "keyFile",
51
+ "ControlPersistDir"
49
52
  ];
50
53
  const numberOptions = [
51
54
  "ControlPersist",
@@ -129,6 +132,18 @@ const sanityCheck = (hostInfo) => {
129
132
  return hostInfo;
130
133
  };
131
134
 
135
+ function getControlPersistDir (hostInfo) {
136
+ for (const candidate of [hostInfo.ControlPersistDir, process.env.SSH_CONTROL_PERSIST_DIR]) {
137
+ if (typeof candidate === "string") {
138
+ const stat = fs.statSync(candidate, { throwIfNoEntry: false });
139
+ if (stat.isDirectory()) {
140
+ return candidate;
141
+ }
142
+ }
143
+ }
144
+ return "~/.ssh";
145
+ }
146
+
132
147
  /**
133
148
  * return ssh option with some hard corded options
134
149
  * @param { Object} hostInfo - host information object defined in index.js
@@ -178,10 +193,11 @@ const getSshOption = (hostInfo, withoutDestination) => {
178
193
  if (hostInfo.noStrictHostkeyChecking) {
179
194
  args.push("-oStrictHostKeyChecking=no");
180
195
  }
181
- const ControlPersist = Number.isInteger(hostInfo.ControlPersist) && hostInfo.ControlPersist >= 0 ? hostInfo.ControlPersist : "180";
196
+ const controlPersist = Number.isInteger(hostInfo.ControlPersist) && hostInfo.ControlPersist >= 0 ? hostInfo.ControlPersist : "180";
197
+ const controlPersistDir = getControlPersistDir(hostInfo);
182
198
  args.push("-oControlMaster=auto");
183
- args.push("-oControlPath=~/.ssh/ssh-client-wrapper-%r@%h:%p");
184
- args.push(`-oControlPersist=${ControlPersist}`);
199
+ args.push(`-oControlPath=${controlPersistDir}/ssh-client-wrapper-%r@%h:%p`);
200
+ args.push(`-oControlPersist=${controlPersist}`);
185
201
 
186
202
  if (Number.isInteger(hostInfo.ConnectTimeout) && hostInfo.ConnectTimeout > 0) {
187
203
  args.push(`-oConnectTimeout=${hostInfo.ConnectTimeout}`);
@@ -244,6 +260,7 @@ const watchDogTimer = (timeout, label, cb) => {
244
260
  module.exports = {
245
261
  sshCmd,
246
262
  rsyncCmd,
263
+ acceptableRsyncRetrunCodes,
247
264
  rePwPrompt,
248
265
  rePhPrompt,
249
266
  reNewHostPrompt,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ssh-client-wrapper",
3
- "version": "2.4.1",
3
+ "version": "2.6.0",
4
4
  "description": "wrapper library for open ssh client and rsync",
5
5
  "keywords": [
6
6
  "ssh",
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ process.on("unhandledRejection", console.dir); // eslint-disable-line no-console
3
+
4
+ // setup test framework
5
+ const chai = require("chai");
6
+ const { expect } = require("chai");
7
+ chai.use(require("chai-as-promised"));
8
+
9
+ // testee
10
+ const { getSshOption } = require("../lib/util.js");
11
+
12
+ describe("test for getSshOption", () => {
13
+ const defaultValues = {
14
+ host: "testHostName",
15
+ ControlPersist: 180,
16
+ maxRetry: 3,
17
+ retryDuration: 1000
18
+ };
19
+ it("should return minimum ssh option array", () => {
20
+ const sshOpts = getSshOption(defaultValues);
21
+ expect(sshOpts).to.be.an("array");
22
+ expect(sshOpts).to.have.lengthOf(4);
23
+ expect(sshOpts[0]).to.equal(defaultValues.host);
24
+ expect(sshOpts[1]).to.equal("-oControlMaster=auto");
25
+ expect(sshOpts[2]).to.equal("-oControlPath=~/.ssh/ssh-client-wrapper-%r@%h:%p");
26
+ expect(sshOpts[3]).to.equal("-oControlPersist=180");
27
+ });
28
+ it("should change ControlPath by ControlPersistDir", () => {
29
+ const hostInfo = { ...defaultValues };
30
+ hostInfo.ControlPersistDir = "/tmp";
31
+ const sshOpts = getSshOption(hostInfo);
32
+ expect(sshOpts).to.be.an("array");
33
+ expect(sshOpts).to.have.lengthOf(4);
34
+ expect(sshOpts[0]).to.equal(defaultValues.host);
35
+ expect(sshOpts[1]).to.equal("-oControlMaster=auto");
36
+ expect(sshOpts[2]).to.equal("-oControlPath=/tmp/ssh-client-wrapper-%r@%h:%p");
37
+ expect(sshOpts[3]).to.equal("-oControlPersist=180");
38
+ });
39
+ it("should change ControlPath by Environment variable", () => {
40
+ process.env.SSH_CONTROL_PERSIST_DIR = "/tmp";
41
+ const sshOpts = getSshOption(defaultValues);
42
+ expect(sshOpts).to.be.an("array");
43
+ expect(sshOpts).to.have.lengthOf(4);
44
+ expect(sshOpts[0]).to.equal(defaultValues.host);
45
+ expect(sshOpts[1]).to.equal("-oControlMaster=auto");
46
+ expect(sshOpts[2]).to.equal("-oControlPath=/tmp/ssh-client-wrapper-%r@%h:%p");
47
+ expect(sshOpts[3]).to.equal("-oControlPersist=180");
48
+ });
49
+ });
File without changes