ssh-client-wrapper 1.1.0 → 1.2.1
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 +3 -3
- package/lib/sshExec.js +31 -2
- package/package.json +1 -1
- package/test/rsyncExec.js +12 -12
- package/test/sshExec.js +3 -3
package/lib/rsyncExec.js
CHANGED
|
@@ -16,7 +16,7 @@ const { sshCmd, rsyncCmd, getSshOption } = require("./util.js");
|
|
|
16
16
|
* @param {string} dst - Destination path.
|
|
17
17
|
* @param {[string]} rsyncOpt - optional arguments for rsync
|
|
18
18
|
*
|
|
19
|
-
* this function execute rsync -
|
|
19
|
+
* this function execute rsync -avv --copy-unsafe-links src rsync://[user@]host[:port]/dst
|
|
20
20
|
*/
|
|
21
21
|
async function send(hostInfo, argSrc, dst, rsyncOpt = []) {
|
|
22
22
|
const promises = await Promise.allSettled(argSrc.map((e)=>{
|
|
@@ -32,7 +32,7 @@ async function send(hostInfo, argSrc, dst, rsyncOpt = []) {
|
|
|
32
32
|
const sshArgs = getSshOption(hostInfo, true);
|
|
33
33
|
const rshOpt = ["-e", `${sshCmd} ${sshArgs.join(" ")}`];
|
|
34
34
|
|
|
35
|
-
const args = ["-
|
|
35
|
+
const args = ["-avv", "--copy-unsafe-links", ...rshOpt, ...rsyncOpt, ...src, `${hostInfo.host}:${dst}`];
|
|
36
36
|
const { dir: remoteDir } = path.parse(dst);
|
|
37
37
|
debug("remoteDir=", remoteDir);
|
|
38
38
|
|
|
@@ -59,7 +59,7 @@ async function recv(hostInfo, argSrc, dst, rsyncOpt = []) {
|
|
|
59
59
|
const sshArgs = getSshOption(hostInfo, true);
|
|
60
60
|
const rshOpt = ["-e", `${sshCmd} ${sshArgs.join(" ")}`];
|
|
61
61
|
|
|
62
|
-
const args = ["-
|
|
62
|
+
const args = ["-avv", "--copy-unsafe-links", ...rshOpt, ...rsyncOpt, src, dst];
|
|
63
63
|
|
|
64
64
|
debug(`recv ${src} to ${dst}`);
|
|
65
65
|
debugVerbose(`rsyncCmd=${rsyncCmd}: args=${args}`);
|
package/lib/sshExec.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
const { setTimeout: setTimeoutPromise } = require("timers/promises");
|
|
2
3
|
const crypto = require("crypto");
|
|
3
4
|
const debug = require("debug")("sshClientWrapper:sshExec");
|
|
4
5
|
const { sshCmd, getSshOption } = require("./util.js");
|
|
@@ -23,7 +24,7 @@ async function sshExec(hostInfo, cmd, outputCallback, opt = null) {
|
|
|
23
24
|
* @param {Object} hostInfo - hostinfo object
|
|
24
25
|
* @param {Integer} timeout - timeout in secconds
|
|
25
26
|
*/
|
|
26
|
-
async function canConnect(hostInfo, timeout) {
|
|
27
|
+
async function canConnect(hostInfo, timeout = 5) {
|
|
27
28
|
debug("check connectibity");
|
|
28
29
|
const words = crypto.randomUUID();
|
|
29
30
|
const args = getSshOption(hostInfo);
|
|
@@ -36,7 +37,35 @@ async function canConnect(hostInfo, timeout) {
|
|
|
36
37
|
const cb = (e)=>{
|
|
37
38
|
output += e;
|
|
38
39
|
};
|
|
39
|
-
|
|
40
|
+
|
|
41
|
+
const ac = new AbortController();
|
|
42
|
+
const signal = ac.signal;
|
|
43
|
+
|
|
44
|
+
setTimeoutPromise(timeout * 1000, "foobar", { signal })
|
|
45
|
+
.then(()=>{
|
|
46
|
+
const err = new Error(`can not connect within ${timeout} sec`);
|
|
47
|
+
err.host = hostInfo.host;
|
|
48
|
+
err.user = hostInfo.user;
|
|
49
|
+
err.port = hostInfo.port;
|
|
50
|
+
throw err;
|
|
51
|
+
})
|
|
52
|
+
.catch((err)=>{
|
|
53
|
+
if (err.name === "AbortError") {
|
|
54
|
+
debug("timeout was aborted because connection check has done");
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const rt = await fork(hostInfo, sshCmd, args, null, cb);
|
|
59
|
+
ac.abort();
|
|
60
|
+
|
|
61
|
+
if (rt !== 0) {
|
|
62
|
+
const err = new Error(`can not connect to ${hostInfo.host}. return code=${rt}`);
|
|
63
|
+
err.rt = rt;
|
|
64
|
+
err.host = hostInfo.host;
|
|
65
|
+
err.user = hostInfo.user;
|
|
66
|
+
err.port = hostInfo.port;
|
|
67
|
+
return err;
|
|
68
|
+
}
|
|
40
69
|
const re = new RegExp(words);
|
|
41
70
|
return re.test(output);
|
|
42
71
|
}
|
package/package.json
CHANGED
package/test/rsyncExec.js
CHANGED
|
@@ -154,7 +154,7 @@ describe("test rsync exec", function() {
|
|
|
154
154
|
await send(hostInfo, [localRoot], remoteEmptyDir);
|
|
155
155
|
|
|
156
156
|
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot)}`, output);
|
|
157
|
-
expect(formatLsOutput(rt)).to.have.members(["foo", "bar", "baz", "hoge"]);
|
|
157
|
+
expect(formatLsOutput(rt)).to.have.members(["foo", "bar", "baz", "hoge", "huga"]);
|
|
158
158
|
|
|
159
159
|
rt.splice(0, rt.length);
|
|
160
160
|
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot, "hoge")}`, output);
|
|
@@ -164,7 +164,7 @@ describe("test rsync exec", function() {
|
|
|
164
164
|
await send(hostInfo, [path.resolve(localRoot)], remoteEmptyDir);
|
|
165
165
|
|
|
166
166
|
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot)}`, output);
|
|
167
|
-
expect(formatLsOutput(rt)).to.have.members(["foo", "bar", "baz", "hoge"]);
|
|
167
|
+
expect(formatLsOutput(rt)).to.have.members(["foo", "bar", "baz", "hoge", "huga"]);
|
|
168
168
|
|
|
169
169
|
rt.splice(0, rt.length);
|
|
170
170
|
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot, "hoge")}`, output);
|
|
@@ -174,7 +174,7 @@ describe("test rsync exec", function() {
|
|
|
174
174
|
await send(hostInfo, [localRoot], path.posix.join(remoteHome, remoteEmptyDir));
|
|
175
175
|
|
|
176
176
|
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot)}`, output);
|
|
177
|
-
expect(formatLsOutput(rt)).to.have.members(["foo", "bar", "baz", "hoge"]);
|
|
177
|
+
expect(formatLsOutput(rt)).to.have.members(["foo", "bar", "baz", "hoge", "huga"]);
|
|
178
178
|
|
|
179
179
|
rt.splice(0, rt.length);
|
|
180
180
|
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot, "hoge")}`, output);
|
|
@@ -184,7 +184,7 @@ describe("test rsync exec", function() {
|
|
|
184
184
|
await send(hostInfo, [path.resolve(localRoot)], path.posix.join(remoteHome, remoteEmptyDir));
|
|
185
185
|
|
|
186
186
|
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot)}`, output);
|
|
187
|
-
expect(formatLsOutput(rt)).to.have.members(["foo", "bar", "baz", "hoge"]);
|
|
187
|
+
expect(formatLsOutput(rt)).to.have.members(["foo", "bar", "baz", "hoge", "huga"]);
|
|
188
188
|
|
|
189
189
|
rt.splice(0, rt.length);
|
|
190
190
|
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot, "hoge")}`, output);
|
|
@@ -205,7 +205,7 @@ describe("test rsync exec", function() {
|
|
|
205
205
|
await send(hostInfo, [localRoot], remoteEmptyDir, ["--exclude=ba*", "--exclude=hoge*}"]);
|
|
206
206
|
|
|
207
207
|
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot)}`, output);
|
|
208
|
-
expect(formatLsOutput(rt)).to.have.members(["foo", "hoge"]);
|
|
208
|
+
expect(formatLsOutput(rt)).to.have.members(["foo", "hoge", "huga"]);
|
|
209
209
|
});
|
|
210
210
|
it.skip("[not implemented] should send directory tree if only filter matched but exclude filter not matched", async ()=>{
|
|
211
211
|
await send(hostInfo, [localRoot], remoteEmptyDir, "*/{ba*,hoge/*}", "**/poyo");
|
|
@@ -217,8 +217,8 @@ describe("test rsync exec", function() {
|
|
|
217
217
|
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot, "hoge")}`, output);
|
|
218
218
|
expect(formatLsOutput(rt)).to.have.members(["piyo", "puyo"]);
|
|
219
219
|
});
|
|
220
|
-
it("shoud not send empty directory", async ()=>{
|
|
221
|
-
await send(hostInfo, [localEmptyDir], remoteEmptyDir);
|
|
220
|
+
it("shoud not send empty directory with -m option", async ()=>{
|
|
221
|
+
await send(hostInfo, [localEmptyDir], remoteEmptyDir, ["-m"]);
|
|
222
222
|
|
|
223
223
|
await sshExec(hostInfo, `ls ${remoteEmptyDir}`, output);
|
|
224
224
|
expect(rt).to.have.lengthOf(0);
|
|
@@ -318,7 +318,7 @@ describe("test rsync exec", function() {
|
|
|
318
318
|
await recv(hostInfo, [remoteRoot], localEmptyDir);
|
|
319
319
|
|
|
320
320
|
let rt2 = await fs.readdir(path.posix.join(localEmptyDir, remoteRoot));
|
|
321
|
-
expect(rt2).to.have.members(["foo", "bar", "baz", "hoge"]);
|
|
321
|
+
expect(rt2).to.have.members(["foo", "bar", "baz", "hoge", "huga"]);
|
|
322
322
|
rt2 = await fs.readdir(path.join(localEmptyDir, remoteRoot, "hoge"));
|
|
323
323
|
expect(rt2).to.have.members(["piyo", "puyo", "poyo"]);
|
|
324
324
|
});
|
|
@@ -326,7 +326,7 @@ describe("test rsync exec", function() {
|
|
|
326
326
|
await recv(hostInfo, [path.posix.join(remoteHome, remoteRoot)], localEmptyDir);
|
|
327
327
|
|
|
328
328
|
let rt2 = await fs.readdir(path.posix.join(localEmptyDir, remoteRoot));
|
|
329
|
-
expect(rt2).to.have.members(["foo", "bar", "baz", "hoge"]);
|
|
329
|
+
expect(rt2).to.have.members(["foo", "bar", "baz", "hoge", "huga"]);
|
|
330
330
|
rt2 = await fs.readdir(path.join(localEmptyDir, remoteRoot, "hoge"));
|
|
331
331
|
expect(rt2).to.have.members(["piyo", "puyo", "poyo"]);
|
|
332
332
|
});
|
|
@@ -334,7 +334,7 @@ describe("test rsync exec", function() {
|
|
|
334
334
|
await recv(hostInfo, [remoteRoot], path.resolve(localEmptyDir));
|
|
335
335
|
|
|
336
336
|
let rt2 = await fs.readdir(path.posix.join(localEmptyDir, remoteRoot));
|
|
337
|
-
expect(rt2).to.have.members(["foo", "bar", "baz", "hoge"]);
|
|
337
|
+
expect(rt2).to.have.members(["foo", "bar", "baz", "hoge", "huga"]);
|
|
338
338
|
rt2 = await fs.readdir(path.join(localEmptyDir, remoteRoot, "hoge"));
|
|
339
339
|
expect(rt2).to.have.members(["piyo", "puyo", "poyo"]);
|
|
340
340
|
});
|
|
@@ -342,7 +342,7 @@ describe("test rsync exec", function() {
|
|
|
342
342
|
await recv(hostInfo, [path.posix.join(remoteHome, remoteRoot)], path.resolve(localEmptyDir));
|
|
343
343
|
|
|
344
344
|
let rt2 = await fs.readdir(path.posix.join(localEmptyDir, remoteRoot));
|
|
345
|
-
expect(rt2).to.have.members(["foo", "bar", "baz", "hoge"]);
|
|
345
|
+
expect(rt2).to.have.members(["foo", "bar", "baz", "hoge", "huga"]);
|
|
346
346
|
rt2 = await fs.readdir(path.join(localEmptyDir, remoteRoot, "hoge"));
|
|
347
347
|
expect(rt2).to.have.members(["piyo", "puyo", "poyo"]);
|
|
348
348
|
});
|
|
@@ -358,7 +358,7 @@ describe("test rsync exec", function() {
|
|
|
358
358
|
await recv(hostInfo, [remoteRoot], localEmptyDir, ["--exclude=*/ba*", "--exclude=hoge"]);
|
|
359
359
|
|
|
360
360
|
const rt2 = await fs.readdir(path.posix.join(localEmptyDir, remoteRoot));
|
|
361
|
-
expect(rt2).to.have.members(["foo"]);
|
|
361
|
+
expect(rt2).to.have.members(["foo", "huga"]);
|
|
362
362
|
});
|
|
363
363
|
it.skip("[not implemented] should recv files which matches only filter but should not recv which matches exclude filter", async ()=>{
|
|
364
364
|
await recv(hostInfo, [remoteRoot], localEmptyDir, "*/{ba*,hoge/*}", "**/piyo");
|
package/test/sshExec.js
CHANGED
|
@@ -68,11 +68,11 @@ describe("test for ssh execution", function() {
|
|
|
68
68
|
});
|
|
69
69
|
it("should be rejected if user does not exist", async ()=>{
|
|
70
70
|
hostInfo2.user = "xxxx";
|
|
71
|
-
return expect(canConnect(hostInfo2)).to.be.
|
|
71
|
+
return expect(canConnect(hostInfo2)).to.be.rejected;
|
|
72
72
|
});
|
|
73
73
|
it("should be rejected if password is wrong", async ()=>{
|
|
74
74
|
hostInfo2.password = "xxxx";
|
|
75
|
-
return expect(canConnect(hostInfo2)).to.be.
|
|
75
|
+
return expect(canConnect(hostInfo2)).to.be.rejected;
|
|
76
76
|
});
|
|
77
77
|
it("should be rejected if host does not exist", async ()=>{
|
|
78
78
|
hostInfo2.host = "foo.bar.example.com";
|
|
@@ -80,7 +80,7 @@ describe("test for ssh execution", function() {
|
|
|
80
80
|
});
|
|
81
81
|
it("should be rejected if host(ip address) does not exist", async ()=>{
|
|
82
82
|
hostInfo2.host = "192.0.2.1";
|
|
83
|
-
return expect(canConnect(hostInfo2
|
|
83
|
+
return expect(canConnect(hostInfo2)).to.be.rejectedWith(255);
|
|
84
84
|
});
|
|
85
85
|
it("should be rejected if port number is out of range(-1)", async ()=>{
|
|
86
86
|
hostInfo2.port = -1;
|