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 +1 -2
- package/lib/sshExec.js +29 -15
- package/lib/util.js +5 -6
- package/package.json +1 -1
- package/test/interface.js +6 -1
- package/test/util.js +31 -8
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.
|
|
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
|
|
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}
|
|
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
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
55
|
-
"retryMaxTimeout"
|
|
54
|
+
"retryDuration"
|
|
56
55
|
];
|
|
57
56
|
|
|
58
57
|
const validate = ajv.compile(hostInfoSchema);
|
package/package.json
CHANGED
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 = {
|
|
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({
|
|
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({
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
});
|