ssh-client-wrapper 2.3.0 → 2.4.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/index.js CHANGED
@@ -27,8 +27,7 @@ const { isArrayOfString, sanityCheck } = require("./util.js");
27
27
  * @param {Integer} hostInfo.ControlPersist=180 - how long keep master connection after last client connection has been closed (sec).
28
28
  * @param {Integer} hostInfo.ConnectTimeout=60 - timeout used when connectiong to ths SSH server
29
29
  * @param {Integer} hostInfo.maxRetry=3 - max number of retry
30
- * @param {Integer} hostInfo.retryMinTimeout=60 - minimum duration before first retry
31
- * @param {Integer} hostInfo.retryMaxTimeout=300 - maximum duration between each retry
30
+ * @param {Integer} hostInfo.retryDuration=1000 - duration time (msec) between each retry
32
31
  * @param {string[]} hostInfo.sshOpt - additional options for ssh
33
32
  */
34
33
 
package/lib/sshExec.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  const crypto = require("crypto");
3
- const promiseRetry = require("promise-retry");
3
+ const { setTimeout: setTimeoutPromise } = require("timers/promises");
4
4
  const debug = require("debug")("sshClientWrapper:debug:sshExec");
5
5
  const debugVerbose = require("debug")("sshClientWrapper:verbose:sshExec");
6
6
  const debugSendMasterPty = require("debug")("sshClientWrapper:insecure:sshExec_masterPty");
@@ -242,22 +242,36 @@ async function ls (hostInfo, target, lsOpt, timeout) {
242
242
 
243
243
  /**
244
244
  * retry fork() if it throw retryable error
245
- * @param {Function} func - function to be wrapped
245
+ * @param {Function} func - function to be wrapped
246
+ * @param {Object} hostInfo - hostinfo object
247
+ * @param {Integer} argMaxRetry - max retry number
246
248
  */
247
- async function retryWrapper (func, hostInfo) {
248
- const minTimeout = hostInfo.retryMinTimeout * 1000 || 1000;
249
- const maxTimeout = hostInfo.retryMaxTimeout * 1000 || 5000;
250
- const retries = hostInfo.maxRetry || 1;
251
- return promiseRetry({ retries, minTimeout, maxTimeout }, (retry, number) => {
252
- return func()
253
- .catch((e) => {
254
- if (e.retryable) {
255
- debug(`retrying: ${number} times`);
256
- disconnect(hostInfo).then(retry);
257
- }
249
+ async function retryWrapper (func, hostInfo, argMaxRetry, argDuration) {
250
+ const maxRetry = argMaxRetry || hostInfo.maxRetry || 1;
251
+ const duration = argDuration || hostInfo.retryDuration || 1000;
252
+ if (maxRetry === 1) {
253
+ return func();
254
+ }
255
+ let retryCount = 0;
256
+ while (true) {
257
+ let rt;
258
+ try {
259
+ rt = await func();
260
+ return rt;
261
+ } catch (e) {
262
+ if (!e.retryable) {
258
263
  throw e;
259
- });
260
- });
264
+ }
265
+ if (retryCount > maxRetry) {
266
+ debug(`max retry count exceeded: ${retryCount}`);
267
+ throw e;
268
+ }
269
+ retryCount++;
270
+ debug(`retryable error occurred ${e.message}`);
271
+ debug(`retrying: ${retryCount} times`);
272
+ await setTimeoutPromise(duration, true);
273
+ }
274
+ }
261
275
  }
262
276
 
263
277
  module.exports = {
package/lib/util.js CHANGED
@@ -16,6 +16,7 @@ const Ajv = require("ajv");
16
16
  const ajv = new Ajv({
17
17
  allErrors: true,
18
18
  coerceTypes: true,
19
+ useDefaults: "empty",
19
20
  logger: {
20
21
  log: debug,
21
22
  warn: debug,
@@ -33,11 +34,10 @@ const hostInfoSchema = {
33
34
  port: { type: "number", minimum: 0, maximum: 65535 },
34
35
  keyFile: { type: "string", pattern: "\\S+", transform: ["trim"] },
35
36
  noStrictHostkeyChecking: { type: "boolean" },
36
- ControlPersist: { type: "number", minimum: 0 },
37
+ ControlPersist: { type: "number", minimum: 0, default: 180 },
37
38
  ConnectTimeout: { type: "number", minimum: 0 },
38
- maxRetry: { type: "number", minimum: 0 },
39
- retryMinTimeout: { type: "number", minimum: 0 },
40
- retryMaxTimeout: { type: "number", minimum: 0 },
39
+ maxRetry: { type: "number", minimum: 0, default: 3 },
40
+ retryDuration: { type: "number", minimum: 0, default: 1000 },
41
41
  sshOpt: { type: "array", minItems: 1, items: { type: "string", pattern: "\\S+", transform: ["trim"] } }
42
42
  },
43
43
  required: ["host"]
@@ -51,8 +51,7 @@ const numberOptions = [
51
51
  "ControlPersist",
52
52
  "ConnectTimeout",
53
53
  "maxRetry",
54
- "retryMinTimeout",
55
- "retryMaxTimeout"
54
+ "retryDuration"
56
55
  ];
57
56
 
58
57
  const validate = ajv.compile(hostInfoSchema);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ssh-client-wrapper",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "wrapper library for open ssh client and rsync",
5
5
  "keywords": [
6
6
  "ssh",
package/test/interface.js CHANGED
@@ -49,7 +49,12 @@ describe("test for interface", () => {
49
49
  });
50
50
  describe("test for public method", () => {
51
51
  let ssh;
52
- const hostInfoOrg = { host: "hoge" };
52
+ const hostInfoOrg = {
53
+ host: "hoge",
54
+ ControlPersist: 180,
55
+ maxRetry: 3,
56
+ retryDuration: 1000
57
+ };
53
58
  const hostInfo = { ...hostInfoOrg, masterPty: null, rsyncVersion: null };
54
59
  beforeEach(() => {
55
60
  ssh = new SshClientWrapper(hostInfoOrg);
package/test/util.js CHANGED
@@ -11,6 +11,11 @@ const { sanityCheck } = require("../lib/util.js");
11
11
 
12
12
  describe("test for sanityCheck", () => {
13
13
  const host = "testHostName";
14
+ const defaultValues = {
15
+ ControlPersist: 180,
16
+ maxRetry: 3,
17
+ retryDuration: 1000
18
+ };
14
19
  it("should throw error for empty host", () => {
15
20
  expect(sanityCheck.bind(null, { host: " ", user: " ", port: 22 })).to.throw(/empty host is not allowed/);
16
21
  });
@@ -18,10 +23,29 @@ describe("test for sanityCheck", () => {
18
23
  expect(sanityCheck.bind(null, { user: " ", port: 22 })).to.throw(/host is required/);
19
24
  });
20
25
  it("should just remove empty string props", () => {
21
- expect(sanityCheck({ host, user: " ", port: 22 })).to.deep.equal({ host, port: 22 });
26
+ expect(sanityCheck({
27
+ host,
28
+ user: " ",
29
+ port: 22
30
+ })).to.deep.equal({
31
+ host,
32
+ ...defaultValues,
33
+ port: 22
34
+ });
22
35
  });
23
36
  it("should remove empty string member in sshOpt", () => {
24
- expect(sanityCheck({ host, sshOpt: ["foo", " ", "bar"], user: "user", port: 33 })).to.deep.equal({ host, sshOpt: ["foo", "bar"], user: "user", port: 33 });
37
+ expect(sanityCheck({
38
+ host,
39
+ sshOpt: ["foo", " ", "bar"],
40
+ user: "user",
41
+ port: 33
42
+ })).to.deep.equal({
43
+ host,
44
+ ...defaultValues,
45
+ sshOpt: ["foo", "bar"],
46
+ user: "user",
47
+ port: 33
48
+ });
25
49
  });
26
50
  it("should just change type if string value specified for number", () => {
27
51
  expect(sanityCheck({
@@ -30,16 +54,14 @@ describe("test for sanityCheck", () => {
30
54
  ControlPersist: "22",
31
55
  ConnectTimeout: "33",
32
56
  maxRetry: "44",
33
- retryMinTimeout: "55",
34
- retryMaxTimeout: "66"
57
+ retryDuration: "55"
35
58
  })).to.deep.equal({
36
59
  host,
37
60
  port: 11,
38
61
  ControlPersist: 22,
39
62
  ConnectTimeout: 33,
40
63
  maxRetry: 44,
41
- retryMinTimeout: 55,
42
- retryMaxTimeout: 66
64
+ retryDuration: 55
43
65
  });
44
66
  });
45
67
  it("should remove out of range members", () => {
@@ -48,8 +70,7 @@ describe("test for sanityCheck", () => {
48
70
  ControlPersist: "-1",
49
71
  ConnectTimeout: "-1",
50
72
  maxRetry: "-1",
51
- retryMinTimeout: "-1",
52
- retryMaxTimeout: "-1"
73
+ retryDuration: "-1"
53
74
  })).to.deep.equal({
54
75
  host
55
76
  });
@@ -60,6 +81,7 @@ describe("test for sanityCheck", () => {
60
81
  noStrictHostkeyChecking: "true"
61
82
  })).to.deep.equal({
62
83
  host,
84
+ ...defaultValues,
63
85
  noStrictHostkeyChecking: true
64
86
  });
65
87
  });
@@ -69,6 +91,7 @@ describe("test for sanityCheck", () => {
69
91
  noStrictHostkeyChecking: 0
70
92
  })).to.deep.equal({
71
93
  host,
94
+ ...defaultValues,
72
95
  noStrictHostkeyChecking: false
73
96
  });
74
97
  });