ssh-client-wrapper 1.8.3 → 2.0.2
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/.github/workflows/coverage_report.yml +24 -0
- package/.github/workflows/{npm-publish.yml → npm_publish.yml} +2 -2
- package/.github/workflows/run_test.yml +6 -0
- package/lib/fork.js +17 -7
- package/lib/index.js +44 -31
- package/lib/rsyncExec.js +6 -4
- package/lib/sshExec.js +28 -54
- package/lib/util.js +28 -1
- package/package.json +3 -2
- package/test/interface.js +180 -0
- package/test/rsyncExec.js +67 -67
- package/test/sshExec.js +12 -9
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: report coverage data to code climate
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
tags:
|
|
5
|
+
- 'v*'
|
|
6
|
+
jobs:
|
|
7
|
+
test:
|
|
8
|
+
uses: ./.github/workflows/run_test.yml
|
|
9
|
+
coverage:
|
|
10
|
+
needs:
|
|
11
|
+
- test
|
|
12
|
+
name: coverage report
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v3
|
|
16
|
+
- uses: actions/download-artifact@v3
|
|
17
|
+
with:
|
|
18
|
+
name: code-coverage-report
|
|
19
|
+
- uses: paambaati/codeclimate-action@v5.0.0
|
|
20
|
+
env:
|
|
21
|
+
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
|
|
22
|
+
with:
|
|
23
|
+
coverageLocations: "coverage-report.lcov:lcov"
|
|
24
|
+
debug: true
|
|
@@ -14,9 +14,9 @@ jobs:
|
|
|
14
14
|
- uses: actions/checkout@v3
|
|
15
15
|
- uses: actions/setup-node@v3
|
|
16
16
|
with:
|
|
17
|
-
node-version:
|
|
17
|
+
node-version: 20
|
|
18
18
|
registry-url: https://registry.npmjs.org/
|
|
19
19
|
- run: npm ci
|
|
20
20
|
- run: npm publish
|
|
21
21
|
env:
|
|
22
|
-
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
|
|
22
|
+
NODE_AUTH_TOKEN: ${{ secrets.npm_token }}
|
package/lib/fork.js
CHANGED
|
@@ -5,7 +5,7 @@ const debugMasterVerbose = require("debug")("sshClientWrapper:insecure:fork_mast
|
|
|
5
5
|
const debugSendPty = require("debug")("sshClientWrapper:insecure:fork_sendpty");
|
|
6
6
|
const { spawn } = require("node-pty");
|
|
7
7
|
|
|
8
|
-
const { rePwPrompt, rePhPrompt, reNewHostPrompt, buff2String, sendPty } = require("./util.js");
|
|
8
|
+
const { rePwPrompt, rePhPrompt, reNewHostPrompt, buff2String, sendPty, watchDogTimer } = require("./util.js");
|
|
9
9
|
|
|
10
10
|
const nodePtyOpt = {
|
|
11
11
|
shell: process.platform === "win32" ? process.env.ComSpec : process.env.SHELL,
|
|
@@ -67,11 +67,19 @@ function getMasterPty(hostInfo, logger) {
|
|
|
67
67
|
hostInfo.masterPty = ptyProcess;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
async function fork(hostInfo, cmd, args, outputCallback) {
|
|
71
|
-
|
|
72
|
-
const p = new Promise((resolve, reject)=>{
|
|
70
|
+
async function fork(hostInfo, cmd, args, timeout, outputCallback) {
|
|
71
|
+
return new Promise((resolve, reject)=>{
|
|
73
72
|
debug(`cmd=${cmd}: args=${args}`);
|
|
74
|
-
ptyProcess = spawn(cmd, args, nodePtyOpt);
|
|
73
|
+
const ptyProcess = spawn(cmd, args, nodePtyOpt);
|
|
74
|
+
let ac = null;
|
|
75
|
+
if (timeout > 0) {
|
|
76
|
+
ac = watchDogTimer(timeout, cmd, ()=>{
|
|
77
|
+
ptyProcess.kill();
|
|
78
|
+
const err = new Error(`watchdog timer expired ${timeout}`);
|
|
79
|
+
err.code = "TIMEEXPIRE";
|
|
80
|
+
reject(err);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
75
83
|
ptyProcess.onData(buff2String.bind(null, debugVerbose));
|
|
76
84
|
ptyProcess.onData((data)=>{
|
|
77
85
|
const output = data.toString();
|
|
@@ -91,6 +99,10 @@ async function fork(hostInfo, cmd, args, outputCallback) {
|
|
|
91
99
|
ptyProcess.onExit(({ exitCode, signal })=>{
|
|
92
100
|
debug("onExit", exitCode, signal);
|
|
93
101
|
|
|
102
|
+
if (ac !== null) {
|
|
103
|
+
ac.abort();
|
|
104
|
+
}
|
|
105
|
+
|
|
94
106
|
if (signal > 0) {
|
|
95
107
|
const err = new Error("signal caught");
|
|
96
108
|
err.signal = signal;
|
|
@@ -104,8 +116,6 @@ async function fork(hostInfo, cmd, args, outputCallback) {
|
|
|
104
116
|
return reject(err);
|
|
105
117
|
});
|
|
106
118
|
});
|
|
107
|
-
p.pty = ptyProcess;
|
|
108
|
-
return p;
|
|
109
119
|
}
|
|
110
120
|
module.exports = {
|
|
111
121
|
fork,
|
package/lib/index.js
CHANGED
|
@@ -30,6 +30,11 @@ const { isArrayOfString, sanityCheck } = require("./util.js");
|
|
|
30
30
|
* @param {string[]} hostInfo.sshOpt - additional options for ssh
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
|
+
const logAndReject = (message)=>{
|
|
34
|
+
debug(message);
|
|
35
|
+
return Promise.reject(new Error(message));
|
|
36
|
+
};
|
|
37
|
+
|
|
33
38
|
/**
|
|
34
39
|
* Facade class.
|
|
35
40
|
* @constructor
|
|
@@ -46,18 +51,19 @@ class SshClientWrapper {
|
|
|
46
51
|
/**
|
|
47
52
|
* Execute command on remote host.
|
|
48
53
|
* @param {string} cmd - Cmdline which will be executed.
|
|
54
|
+
* @param {Integer} timeout - timeout in secconds
|
|
49
55
|
* @param { Function } outputCallback - call back routine for output
|
|
50
56
|
* @returns {Integer} - Return code of cmd.
|
|
51
57
|
*
|
|
52
58
|
* If stdout and stderr is array, last 5 line of stdout and stderr is stored in them.
|
|
53
59
|
*/
|
|
54
|
-
async exec(cmd, outputCallback) {
|
|
60
|
+
async exec(cmd, timeout = 0, outputCallback = null) {
|
|
55
61
|
debug("exec called", cmd);
|
|
56
62
|
|
|
57
|
-
if (typeof cmd !== "string") {
|
|
58
|
-
return
|
|
63
|
+
if (typeof cmd !== "string" || cmd === "") {
|
|
64
|
+
return logAndReject("cmd must be string");
|
|
59
65
|
}
|
|
60
|
-
return sshExec(this.hostInfo, cmd, outputCallback);
|
|
66
|
+
return sshExec(this.hostInfo, cmd, timeout, outputCallback);
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
|
|
@@ -65,11 +71,12 @@ class SshClientWrapper {
|
|
|
65
71
|
* execute ls command and return output
|
|
66
72
|
* @param {string} target - file or directory path to watch
|
|
67
73
|
* @param {string[]} lsOpt - optional arguments for ls
|
|
74
|
+
* @param {Integer} timeout - timeout in secconds
|
|
68
75
|
* @returns {string[]} - output from ls
|
|
69
76
|
*/
|
|
70
|
-
async ls(target, lsOpt = []) {
|
|
77
|
+
async ls(target, lsOpt = [], timeout = 0) {
|
|
71
78
|
debug("ls called", target, lsOpt);
|
|
72
|
-
return ls(this.hostInfo, target, lsOpt);
|
|
79
|
+
return ls(this.hostInfo, target, lsOpt, timeout);
|
|
73
80
|
}
|
|
74
81
|
|
|
75
82
|
/**
|
|
@@ -84,7 +91,7 @@ class SshClientWrapper {
|
|
|
84
91
|
debug("watch called", cmd, regexp, retryDelay, maxRetry);
|
|
85
92
|
|
|
86
93
|
if (typeof cmd !== "string") {
|
|
87
|
-
return
|
|
94
|
+
return logAndReject("cmd must be string");
|
|
88
95
|
}
|
|
89
96
|
if (!(regexp instanceof RegExp)) {
|
|
90
97
|
const err = new Error("illegal regexp specified");
|
|
@@ -92,7 +99,7 @@ class SshClientWrapper {
|
|
|
92
99
|
err.cmd = cmd;
|
|
93
100
|
err.retryDelay = retryDelay;
|
|
94
101
|
err.maxRetry = maxRetry;
|
|
95
|
-
return Promise.reject(err);
|
|
102
|
+
return Promise.reject(new Error("err"));
|
|
96
103
|
}
|
|
97
104
|
//not implemented yet !!!
|
|
98
105
|
return this.exec(cmd, ()=>{
|
|
@@ -104,27 +111,30 @@ class SshClientWrapper {
|
|
|
104
111
|
* @param {string[]} src - File or directory name which to be recieve.
|
|
105
112
|
* @param {string} dst - Destination path.
|
|
106
113
|
* @param {string[]} opt - option for rsync
|
|
114
|
+
* @param {Integer} timeout - timeout in secconds
|
|
107
115
|
* @returns {Promise} - Resolved with undefined when file transfer is done.
|
|
108
116
|
*/
|
|
109
|
-
async send(src, dst, opt) {
|
|
117
|
+
async send(src, dst, opt = [], timeout = 0) {
|
|
110
118
|
debug("send called", src, dst, opt);
|
|
111
119
|
|
|
112
120
|
if (!isArrayOfString(src)) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
121
|
+
return logAndReject("src must be array of string");
|
|
122
|
+
}
|
|
123
|
+
if (!src.some((e)=>{
|
|
124
|
+
return e !== "";
|
|
125
|
+
})) {
|
|
126
|
+
return logAndReject("src must contain non-empty string");
|
|
116
127
|
}
|
|
117
128
|
if (typeof dst !== "string") {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
129
|
+
return logAndReject("dst must be string");
|
|
130
|
+
}
|
|
131
|
+
if (dst === "") {
|
|
132
|
+
return logAndReject("dst must be non-empty string");
|
|
121
133
|
}
|
|
122
134
|
if (typeof opt !== "undefined" && !isArrayOfString(opt)) {
|
|
123
|
-
|
|
124
|
-
debug(err);
|
|
125
|
-
return new Error(err);
|
|
135
|
+
return logAndReject("opt must be array of string");
|
|
126
136
|
}
|
|
127
|
-
return send(this.hostInfo, src, dst, opt);
|
|
137
|
+
return send(this.hostInfo, src, dst, opt, timeout);
|
|
128
138
|
}
|
|
129
139
|
|
|
130
140
|
/**
|
|
@@ -132,27 +142,30 @@ class SshClientWrapper {
|
|
|
132
142
|
* @param {string[]} src - File or directory name which to be recieve.
|
|
133
143
|
* @param {string} dst - Destination path.
|
|
134
144
|
* @param {string[]} opt - option for rsync
|
|
145
|
+
* @param {Integer} timeout - timeout in secconds
|
|
135
146
|
* @returns {Promise} - Resolved with undefined when file transfer is done.
|
|
136
147
|
*/
|
|
137
|
-
async recv(src, dst, opt) {
|
|
148
|
+
async recv(src, dst, opt = [], timeout = 0) {
|
|
138
149
|
debug("recv called", src, dst, opt);
|
|
139
150
|
|
|
140
151
|
if (!isArrayOfString(src)) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
152
|
+
return logAndReject("src must be array of string");
|
|
153
|
+
}
|
|
154
|
+
if (!src.some((e)=>{
|
|
155
|
+
return e !== "";
|
|
156
|
+
})) {
|
|
157
|
+
return logAndReject("src must contain non-empty string");
|
|
144
158
|
}
|
|
145
159
|
if (typeof dst !== "string") {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
160
|
+
return logAndReject("dst must be string");
|
|
161
|
+
}
|
|
162
|
+
if (dst === "") {
|
|
163
|
+
return logAndReject("dst must be non-empty string");
|
|
149
164
|
}
|
|
150
165
|
if (typeof opt !== "undefined" && !isArrayOfString(opt)) {
|
|
151
|
-
|
|
152
|
-
debug(err);
|
|
153
|
-
return new Error(err);
|
|
166
|
+
return logAndReject("opt must be array of string");
|
|
154
167
|
}
|
|
155
|
-
return recv(this.hostInfo, src, dst, opt);
|
|
168
|
+
return recv(this.hostInfo, src, dst, opt, timeout);
|
|
156
169
|
}
|
|
157
170
|
|
|
158
171
|
/**
|
|
@@ -165,7 +178,7 @@ class SshClientWrapper {
|
|
|
165
178
|
debugVerbose("hostInfo=", this.hostInfo);
|
|
166
179
|
|
|
167
180
|
if (!Number.isInteger(timeout) || timeout <= 0) {
|
|
168
|
-
return
|
|
181
|
+
return logAndReject("timeout must be positive integer");
|
|
169
182
|
}
|
|
170
183
|
return canConnect(this.hostInfo, timeout);
|
|
171
184
|
}
|
package/lib/rsyncExec.js
CHANGED
|
@@ -16,10 +16,11 @@ const { sshCmd, rsyncCmd, getSshOption } = require("./util.js");
|
|
|
16
16
|
* @param {[string]} argSrc - File or directory name which to be send.
|
|
17
17
|
* @param {string} dst - Destination path.
|
|
18
18
|
* @param {[string]} rsyncOpt - optional arguments for rsync
|
|
19
|
+
* @param {Integer} timeout - timeout in secconds
|
|
19
20
|
*
|
|
20
21
|
* this function execute rsync -avv --copy-unsafe-links src rsync://[user@]host[:port]/dst
|
|
21
22
|
*/
|
|
22
|
-
async function send(hostInfo, argSrc, dst, rsyncOpt
|
|
23
|
+
async function send(hostInfo, argSrc, dst, rsyncOpt, timeout) {
|
|
23
24
|
await connect(hostInfo);
|
|
24
25
|
const promises = await Promise.allSettled(argSrc.map((e)=>{
|
|
25
26
|
return glob(e);
|
|
@@ -65,7 +66,7 @@ async function send(hostInfo, argSrc, dst, rsyncOpt = []) {
|
|
|
65
66
|
}
|
|
66
67
|
debug(`send ${src} to ${hostInfo.host}:${dst}`);
|
|
67
68
|
debugVerbose(`send: ${rsyncCmd} ${args.join(" ")}`);
|
|
68
|
-
return retryWrapper(fork.bind(null, hostInfo, rsyncCmd, args), hostInfo);
|
|
69
|
+
return retryWrapper(fork.bind(null, hostInfo, rsyncCmd, args, timeout), hostInfo);
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
/**
|
|
@@ -74,10 +75,11 @@ async function send(hostInfo, argSrc, dst, rsyncOpt = []) {
|
|
|
74
75
|
* @param {[string]} argSrc - File or directory name which to be send.
|
|
75
76
|
* @param {string} dst - Destination path.
|
|
76
77
|
* @param {[string]} rsyncOpt - optional arguments for rsync
|
|
78
|
+
* @param {Integer} timeout - timeout in secconds
|
|
77
79
|
*
|
|
78
80
|
* this function execute rsync -av --copy-unsafe-links rsync://[user@]host[:port]/src dst
|
|
79
81
|
*/
|
|
80
|
-
async function recv(hostInfo, argSrc, dst, rsyncOpt
|
|
82
|
+
async function recv(hostInfo, argSrc, dst, rsyncOpt, timeout) {
|
|
81
83
|
await connect(hostInfo);
|
|
82
84
|
const src = `${hostInfo.host}:${argSrc.join(" ")}`;
|
|
83
85
|
const sshArgs = getSshOption(hostInfo, true);
|
|
@@ -87,7 +89,7 @@ async function recv(hostInfo, argSrc, dst, rsyncOpt = []) {
|
|
|
87
89
|
|
|
88
90
|
debug(`recv ${src} to ${dst}`);
|
|
89
91
|
debugVerbose(`recv: ${rsyncCmd} ${args.join(" ")}`);
|
|
90
|
-
return retryWrapper(fork.bind(null, hostInfo, rsyncCmd, args), hostInfo);
|
|
92
|
+
return retryWrapper(fork.bind(null, hostInfo, rsyncCmd, args, timeout), hostInfo);
|
|
91
93
|
}
|
|
92
94
|
module.exports = {
|
|
93
95
|
send,
|
package/lib/sshExec.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const { setTimeout: setTimeoutPromise } = require("timers/promises");
|
|
3
2
|
const crypto = require("crypto");
|
|
4
3
|
const promiseRetry = require("promise-retry");
|
|
5
4
|
const debug = require("debug")("sshClientWrapper:debug:sshExec");
|
|
6
5
|
const debugVerbose = require("debug")("sshClientWrapper:verbose:sshExec");
|
|
7
6
|
const debugSendMasterPty = require("debug")("sshClientWrapper:insecure:sshExec_masterPty");
|
|
8
|
-
const { sendPty, sshCmd, getSshOption, sanityCheck } = require("./util.js");
|
|
7
|
+
const { sendPty, sshCmd, getSshOption, sanityCheck, watchDogTimer } = require("./util.js");
|
|
9
8
|
const { fork, getMasterPty } = require("./fork.js");
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* Execute command on remote host.
|
|
13
12
|
* @param {Object} hostInfo - hostinfo object
|
|
14
13
|
* @param {string} cmd - Cmdline which will be executed on remote host.
|
|
14
|
+
* @param {number} timeout - timeout (in sec.)
|
|
15
15
|
* @param {Function} outputCallback - Callback routine for stdout and stderr.
|
|
16
16
|
*/
|
|
17
|
-
async function sshExec(hostInfo, cmd, outputCallback) {
|
|
17
|
+
async function sshExec(hostInfo, cmd, timeout, outputCallback) {
|
|
18
18
|
await connect(hostInfo);
|
|
19
19
|
debug("exec", cmd, "on remote server");
|
|
20
20
|
const args = getSshOption(hostInfo, false);
|
|
@@ -22,7 +22,7 @@ async function sshExec(hostInfo, cmd, outputCallback) {
|
|
|
22
22
|
debugVerbose(`exec: ${sshCmd} ${args.join(" ")}`);
|
|
23
23
|
|
|
24
24
|
try {
|
|
25
|
-
const rt = await retryWrapper(fork.bind(null, hostInfo, sshCmd, args, outputCallback), hostInfo);
|
|
25
|
+
const rt = await retryWrapper(fork.bind(null, hostInfo, sshCmd, args, timeout, outputCallback), hostInfo);
|
|
26
26
|
return rt;
|
|
27
27
|
} catch (e) {
|
|
28
28
|
if (typeof e.rt === "undefined") {
|
|
@@ -64,11 +64,8 @@ async function canConnect(hostInfo, timeout = 60) {
|
|
|
64
64
|
* @param {Object} hostInfo - hostinfo object
|
|
65
65
|
* @returns {boolean} - true if master session exists
|
|
66
66
|
*/
|
|
67
|
-
async function existsMaster(hostInfo) {
|
|
67
|
+
async function existsMaster(hostInfo, argTimeout) {
|
|
68
68
|
debugVerbose("existsMaster called");
|
|
69
|
-
const ac = new AbortController();
|
|
70
|
-
const signal = ac.signal;
|
|
71
|
-
|
|
72
69
|
const args = getSshOption(hostInfo);
|
|
73
70
|
args.push("-Ocheck");
|
|
74
71
|
|
|
@@ -77,40 +74,22 @@ async function existsMaster(hostInfo) {
|
|
|
77
74
|
output += e;
|
|
78
75
|
};
|
|
79
76
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const timeout = hostInfo.ConnectTimeout || 60;
|
|
83
|
-
setTimeoutPromise(timeout * 1000, "neverUsedValue", { signal })
|
|
84
|
-
.then(()=>{
|
|
85
|
-
debug(`can not finish master session check within ${timeout} sec`);
|
|
86
|
-
|
|
87
|
-
if (p.pty) {
|
|
88
|
-
p.pty.kill();
|
|
89
|
-
}
|
|
90
|
-
rt = false;
|
|
91
|
-
})
|
|
92
|
-
.catch((err)=>{
|
|
93
|
-
if (err.name === "AbortError") {
|
|
94
|
-
debug("timeout was aborted because master session check has done");
|
|
95
|
-
} else {
|
|
96
|
-
throw err;
|
|
97
|
-
}
|
|
98
|
-
});
|
|
77
|
+
const timeout = argTimeout || hostInfo.ConnectTimeout || 60;
|
|
78
|
+
debug(`check master session: ${sshCmd} ${args.join(" ")}`);
|
|
99
79
|
|
|
100
80
|
try {
|
|
101
|
-
|
|
102
|
-
p = fork(hostInfo, sshCmd, args, cb);
|
|
103
|
-
await p;
|
|
81
|
+
await fork(hostInfo, sshCmd, args, timeout, cb);
|
|
104
82
|
} catch (err) {
|
|
83
|
+
if (err.code === "TIMEEXPIRE") {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
105
86
|
if (err.rt !== 255) {
|
|
106
87
|
debug("check master session failed", err);
|
|
107
88
|
throw err;
|
|
108
89
|
}
|
|
109
|
-
} finally {
|
|
110
|
-
ac.abort();
|
|
111
90
|
}
|
|
112
91
|
|
|
113
|
-
return
|
|
92
|
+
return !/Control socket connect.*: No such file or directory/.test(output);
|
|
114
93
|
}
|
|
115
94
|
|
|
116
95
|
/**
|
|
@@ -118,7 +97,7 @@ async function existsMaster(hostInfo) {
|
|
|
118
97
|
* @param {Object} hostInfo - hostinfo object
|
|
119
98
|
*/
|
|
120
99
|
async function connect(hostInfo, timeout = 60) {
|
|
121
|
-
if (await existsMaster(hostInfo)) {
|
|
100
|
+
if (await existsMaster(hostInfo, timeout)) {
|
|
122
101
|
debugVerbose("master connection exists");
|
|
123
102
|
return;
|
|
124
103
|
}
|
|
@@ -127,11 +106,13 @@ async function connect(hostInfo, timeout = 60) {
|
|
|
127
106
|
getMasterPty(hostInfo, debugSendMasterPty);
|
|
128
107
|
|
|
129
108
|
debug(`create master ssh session to ${hostInfo.host}`);
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
109
|
+
|
|
110
|
+
const words = crypto.randomUUID();
|
|
111
|
+
const re = new RegExp(`^${words}`);
|
|
112
|
+
|
|
113
|
+
let ac = null;
|
|
114
|
+
const p = new Promise((resolve, reject)=>{
|
|
115
|
+
ac = watchDogTimer(timeout, "make connection", ()=>{
|
|
135
116
|
sendPty(hostInfo.masterPty, "\n", debugSendMasterPty, 137);
|
|
136
117
|
sendPty(hostInfo.masterPty, "exit\n", debugSendMasterPty, 138);
|
|
137
118
|
|
|
@@ -139,19 +120,9 @@ async function connect(hostInfo, timeout = 60) {
|
|
|
139
120
|
err.host = hostInfo.host;
|
|
140
121
|
err.user = hostInfo.user;
|
|
141
122
|
err.port = hostInfo.port;
|
|
142
|
-
|
|
143
|
-
})
|
|
144
|
-
.catch((err)=>{
|
|
145
|
-
if (err.name === "AbortError") {
|
|
146
|
-
debug("timeout was aborted because connection check has done");
|
|
147
|
-
} else {
|
|
148
|
-
throw err;
|
|
149
|
-
}
|
|
123
|
+
reject(err);
|
|
150
124
|
});
|
|
151
125
|
|
|
152
|
-
const words = crypto.randomUUID();
|
|
153
|
-
const re = new RegExp(`^${words}`);
|
|
154
|
-
const p = new Promise((resolve, reject)=>{
|
|
155
126
|
let done = false;
|
|
156
127
|
hostInfo.masterPty.onData((data)=>{
|
|
157
128
|
if (done) {
|
|
@@ -190,7 +161,9 @@ async function connect(hostInfo, timeout = 60) {
|
|
|
190
161
|
await p;
|
|
191
162
|
debug(`connected to ${hostInfo.host}`);
|
|
192
163
|
} finally {
|
|
193
|
-
ac
|
|
164
|
+
if (ac !== null) {
|
|
165
|
+
ac.abort();
|
|
166
|
+
}
|
|
194
167
|
}
|
|
195
168
|
}
|
|
196
169
|
|
|
@@ -209,7 +182,7 @@ async function disconnect(hostInfo) {
|
|
|
209
182
|
};
|
|
210
183
|
try {
|
|
211
184
|
debug(`disconnect: ${sshCmd} ${args.join(" ")}`);
|
|
212
|
-
await fork(hostInfo, sshCmd, args, cb);
|
|
185
|
+
await fork(hostInfo, sshCmd, args, 0, cb);
|
|
213
186
|
} catch (err) {
|
|
214
187
|
if (err.rt === 255) {
|
|
215
188
|
return;
|
|
@@ -236,12 +209,13 @@ async function disconnect(hostInfo) {
|
|
|
236
209
|
* @param {Object} hostInfo - hostinfo object
|
|
237
210
|
* @param {string} target - file or directory path to watch
|
|
238
211
|
* @param {string[]} lsOpt - optional arguments for ls
|
|
212
|
+
* @param {number} timeout - timeout (in sec.)
|
|
239
213
|
* @returns {string[]} - output from ls
|
|
240
214
|
*/
|
|
241
|
-
async function ls(hostInfo, target, lsOpt
|
|
215
|
+
async function ls(hostInfo, target, lsOpt, timeout) {
|
|
242
216
|
const outputArray = [];
|
|
243
217
|
try {
|
|
244
|
-
await sshExec(hostInfo, `ls ${lsOpt.join(" ")} ${target}`, (data)=>{
|
|
218
|
+
await sshExec(hostInfo, `ls ${lsOpt.join(" ")} ${target}`, timeout, (data)=>{
|
|
245
219
|
outputArray.push(data);
|
|
246
220
|
});
|
|
247
221
|
} catch (e) {
|
package/lib/util.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
const { EOL } = require("os");
|
|
3
3
|
const fs = require("fs");
|
|
4
4
|
const debug = require("debug")("sshClientWrapper:debug:util");
|
|
5
|
+
const { setTimeout: setTimeoutPromise } = require("timers/promises");
|
|
5
6
|
|
|
6
7
|
const sshCmd = "ssh";
|
|
7
8
|
const rsyncCmd = "rsync";
|
|
@@ -122,6 +123,31 @@ const sendPty = async (pty, message, logger, label)=>{
|
|
|
122
123
|
pty.write(message);
|
|
123
124
|
};
|
|
124
125
|
|
|
126
|
+
/**
|
|
127
|
+
* set watch dog timer
|
|
128
|
+
* @param {number} timeout - timeout (in sec.)
|
|
129
|
+
* @param {string} label - used in both timeout and aborted message
|
|
130
|
+
* @param {Function} cb - call back function which will be called after timeout
|
|
131
|
+
* @returns {AbortController} - ac to stop watch dog timer
|
|
132
|
+
*/
|
|
133
|
+
const watchDogTimer = (timeout, label, cb)=>{
|
|
134
|
+
const ac = new AbortController();
|
|
135
|
+
const signal = ac.signal;
|
|
136
|
+
setTimeoutPromise(timeout * 1000, "neverUsedValue", { signal })
|
|
137
|
+
.then(()=>{
|
|
138
|
+
debug(`can not finish ${label} within ${timeout} sec`);
|
|
139
|
+
return typeof cb === "function" ? cb() : true;
|
|
140
|
+
})
|
|
141
|
+
.catch((err)=>{
|
|
142
|
+
if (err.name === "AbortError") {
|
|
143
|
+
debug(`timeout was aborted because ${label} has done`);
|
|
144
|
+
} else {
|
|
145
|
+
throw err;
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
return ac;
|
|
149
|
+
};
|
|
150
|
+
|
|
125
151
|
module.exports = {
|
|
126
152
|
sshCmd,
|
|
127
153
|
rsyncCmd,
|
|
@@ -132,5 +158,6 @@ module.exports = {
|
|
|
132
158
|
getSshOption,
|
|
133
159
|
sanityCheck,
|
|
134
160
|
isArrayOfString,
|
|
135
|
-
sendPty
|
|
161
|
+
sendPty,
|
|
162
|
+
watchDogTimer
|
|
136
163
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ssh-client-wrapper",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "wrapper library for open ssh client and rsync",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ssh",
|
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
"glob": "^10.3.3",
|
|
39
39
|
"node-pty": "^0.10.1",
|
|
40
40
|
"nyc": "^15.1.0",
|
|
41
|
-
"promise-retry": "^2.0.1"
|
|
41
|
+
"promise-retry": "^2.0.1",
|
|
42
|
+
"rewire": "^7.0.0"
|
|
42
43
|
},
|
|
43
44
|
"devDependencies": {
|
|
44
45
|
"chai": "^4.3.7",
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
process.on("unhandledRejection", console.dir); //eslint-disable-line no-console
|
|
3
|
+
Error.traceLimit = 100000;
|
|
4
|
+
|
|
5
|
+
//setup test framework
|
|
6
|
+
const chai = require("chai");
|
|
7
|
+
const { expect } = require("chai");
|
|
8
|
+
const sinon = require("sinon");
|
|
9
|
+
chai.use(require("sinon-chai"));
|
|
10
|
+
chai.use(require("chai-as-promised"));
|
|
11
|
+
|
|
12
|
+
const rewire = require("rewire");
|
|
13
|
+
|
|
14
|
+
//testee
|
|
15
|
+
const SshClientWrapper = rewire("../lib/index.js");
|
|
16
|
+
|
|
17
|
+
const sshExec = sinon.stub();
|
|
18
|
+
const canConnect = sinon.stub();
|
|
19
|
+
const disconnect = sinon.stub();
|
|
20
|
+
const ls = sinon.stub();
|
|
21
|
+
const send = sinon.stub();
|
|
22
|
+
const recv = sinon.stub();
|
|
23
|
+
|
|
24
|
+
//eslint-disable-next-line no-underscore-dangle
|
|
25
|
+
SshClientWrapper.__set__({
|
|
26
|
+
sshExec, canConnect, disconnect, ls, send, recv
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
describe("test for interface", ()=>{
|
|
31
|
+
describe("test for constructor", ()=>{
|
|
32
|
+
it("should throw error when instanciate with non-empty string host", ()=>{
|
|
33
|
+
expect(()=>{
|
|
34
|
+
//eslint-disable-next-line no-new
|
|
35
|
+
new SshClientWrapper({});
|
|
36
|
+
}).to.throw("host must be non-empty string");
|
|
37
|
+
expect(()=>{
|
|
38
|
+
//eslint-disable-next-line no-new
|
|
39
|
+
new SshClientWrapper({ host: 1 });
|
|
40
|
+
}).to.throw("host must be non-empty string");
|
|
41
|
+
expect(()=>{
|
|
42
|
+
//eslint-disable-next-line no-new
|
|
43
|
+
new SshClientWrapper({ host: "" });
|
|
44
|
+
}).to.throw("host must be non-empty string");
|
|
45
|
+
});
|
|
46
|
+
it("should successfully instanciate only with host prop", ()=>{
|
|
47
|
+
expect(new SshClientWrapper({ host: "hoge" })).to.have.property("exec");
|
|
48
|
+
expect(new SshClientWrapper({ host: "hoge" })).to.have.property("ls");
|
|
49
|
+
expect(new SshClientWrapper({ host: "hoge" })).to.have.property("watch");
|
|
50
|
+
expect(new SshClientWrapper({ host: "hoge" })).to.have.property("send");
|
|
51
|
+
expect(new SshClientWrapper({ host: "hoge" })).to.have.property("recv");
|
|
52
|
+
expect(new SshClientWrapper({ host: "hoge" })).to.have.property("canConnect");
|
|
53
|
+
expect(new SshClientWrapper({ host: "hoge" })).to.have.property("disconnect");
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe("test for public method", ()=>{
|
|
57
|
+
let ssh;
|
|
58
|
+
const hostInfoOrg = { host: "hoge" };
|
|
59
|
+
const hostInfo = { ...hostInfoOrg, masterPty: null };
|
|
60
|
+
beforeEach(()=>{
|
|
61
|
+
ssh = new SshClientWrapper(hostInfoOrg);
|
|
62
|
+
sshExec.reset();
|
|
63
|
+
canConnect.reset();
|
|
64
|
+
disconnect.reset();
|
|
65
|
+
ls.reset();
|
|
66
|
+
send.reset();
|
|
67
|
+
recv.reset();
|
|
68
|
+
});
|
|
69
|
+
describe("test for exec", ()=>{
|
|
70
|
+
it("should call sshExec with cmd", ()=>{
|
|
71
|
+
ssh.exec("ls");
|
|
72
|
+
expect(sshExec).to.be.calledWith(hostInfo, "ls", 0, null);
|
|
73
|
+
});
|
|
74
|
+
it("should call sshExec with cmd and timeout", ()=>{
|
|
75
|
+
ssh.exec("ls", 10);
|
|
76
|
+
expect(sshExec).to.be.calledWith(hostInfo, "ls", 10, null);
|
|
77
|
+
});
|
|
78
|
+
it("should call sshExec with cmd, timeout and call back function", ()=>{
|
|
79
|
+
const cb = ()=>{};
|
|
80
|
+
ssh.exec("ls", 10, cb);
|
|
81
|
+
expect(sshExec).to.be.calledWith(hostInfo, "ls", 10, cb);
|
|
82
|
+
});
|
|
83
|
+
it("should reject if cmd is not string (number)", ()=>{
|
|
84
|
+
return expect(ssh.exec(0)).to.be.rejectedWith("cmd must be string");
|
|
85
|
+
});
|
|
86
|
+
it("should reject if cmd is not string (null)", ()=>{
|
|
87
|
+
return expect(ssh.exec(null)).to.be.rejectedWith("cmd must be string");
|
|
88
|
+
});
|
|
89
|
+
it("should reject if cmd is empty string", ()=>{
|
|
90
|
+
return expect(ssh.exec("")).to.be.rejectedWith("cmd must be string");
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
describe("test for ls", ()=>{
|
|
94
|
+
it("should call ls without timeout", ()=>{
|
|
95
|
+
ssh.ls("hoge");
|
|
96
|
+
expect(ls).to.be.calledWith(hostInfo, "hoge", [], 0);
|
|
97
|
+
});
|
|
98
|
+
it("should call ls with option", ()=>{
|
|
99
|
+
ssh.ls("hoge", ["-l", "-r", "-t"]);
|
|
100
|
+
expect(ls).to.be.calledWith(hostInfo, "hoge", ["-l", "-r", "-t"], 0);
|
|
101
|
+
});
|
|
102
|
+
it("should call ls with option and timeout", ()=>{
|
|
103
|
+
ssh.ls("hoge", ["-l", "-r", "-t"], 10);
|
|
104
|
+
expect(ls).to.be.calledWith(hostInfo, "hoge", ["-l", "-r", "-t"], 10);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
describe("test for send", ()=>{
|
|
108
|
+
it("should call send with src and dst", ()=>{
|
|
109
|
+
ssh.send(["src"], "dst");
|
|
110
|
+
expect(send).to.be.calledWith(hostInfo, ["src"], "dst", [], 0);
|
|
111
|
+
});
|
|
112
|
+
it("should call send with src, dst, and opt", ()=>{
|
|
113
|
+
ssh.send(["src"], "dst", ["--dry-run", "--delete"]);
|
|
114
|
+
expect(send).to.be.calledWith(hostInfo, ["src"], "dst", ["--dry-run", "--delete"], 0);
|
|
115
|
+
});
|
|
116
|
+
it("should call send with src, dst, opt, and timeout", ()=>{
|
|
117
|
+
ssh.send(["src"], "dst", ["--dry-run", "--delete"], 10);
|
|
118
|
+
expect(send).to.be.calledWith(hostInfo, ["src"], "dst", ["--dry-run", "--delete"], 10);
|
|
119
|
+
});
|
|
120
|
+
it("should be rejected if src is not array of string", ()=>{
|
|
121
|
+
return expect(ssh.send(1)).to.be.rejectedWith("src must be array of string");
|
|
122
|
+
});
|
|
123
|
+
it("should be rejected if src has only empty string element", ()=>{
|
|
124
|
+
return expect(ssh.send(["", ""])).to.be.rejectedWith("src must contain non-empty string");
|
|
125
|
+
});
|
|
126
|
+
it("should be rejected if src has only empty string element", ()=>{
|
|
127
|
+
return expect(ssh.send([""])).to.be.rejectedWith("src must contain non-empty string");
|
|
128
|
+
});
|
|
129
|
+
it("should be called if src includes empty string ", ()=>{
|
|
130
|
+
ssh.send(["src", ""], "dst");
|
|
131
|
+
expect(send).to.be.calledWith(hostInfo, ["src", ""], "dst", [], 0);
|
|
132
|
+
});
|
|
133
|
+
it("should be rejected if dst is not string", ()=>{
|
|
134
|
+
return expect(ssh.send(["src"], 0)).to.be.rejectedWith("dst must be string");
|
|
135
|
+
});
|
|
136
|
+
it("should be rejected if dst is empty string", ()=>{
|
|
137
|
+
return expect(ssh.send(["src"], "")).to.be.rejectedWith("dst must be non-empty string");
|
|
138
|
+
});
|
|
139
|
+
it("should be rejected if opt is not array of string", ()=>{
|
|
140
|
+
return expect(ssh.send(["src"], "dst", [null])).to.be.rejectedWith("opt must be array of string");
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
describe("test for recv", ()=>{
|
|
144
|
+
it("should call recv with src and dst", ()=>{
|
|
145
|
+
ssh.recv(["src"], "dst");
|
|
146
|
+
expect(recv).to.be.calledWith(hostInfo, ["src"], "dst", [], 0);
|
|
147
|
+
});
|
|
148
|
+
it("should call recv with src, dst, and opt", ()=>{
|
|
149
|
+
ssh.recv(["src"], "dst", ["--dry-run", "--delete"]);
|
|
150
|
+
expect(recv).to.be.calledWith(hostInfo, ["src"], "dst", ["--dry-run", "--delete"], 0);
|
|
151
|
+
});
|
|
152
|
+
it("should call recv with src, dst, opt, and timeout", ()=>{
|
|
153
|
+
ssh.recv(["src"], "dst", ["--dry-run", "--delete"], 10);
|
|
154
|
+
expect(recv).to.be.calledWith(hostInfo, ["src"], "dst", ["--dry-run", "--delete"], 10);
|
|
155
|
+
});
|
|
156
|
+
it("should be rejected if src is not array of string", ()=>{
|
|
157
|
+
return expect(ssh.recv(1)).to.be.rejectedWith("src must be array of string");
|
|
158
|
+
});
|
|
159
|
+
it("should be rejected if src has only empty string element", ()=>{
|
|
160
|
+
return expect(ssh.recv(["", ""])).to.be.rejectedWith("src must contain non-empty string");
|
|
161
|
+
});
|
|
162
|
+
it("should be rejected if src has only empty string element", ()=>{
|
|
163
|
+
return expect(ssh.recv([""])).to.be.rejectedWith("src must contain non-empty string");
|
|
164
|
+
});
|
|
165
|
+
it("should be called if src includes empty string ", ()=>{
|
|
166
|
+
ssh.recv(["src", ""], "dst");
|
|
167
|
+
expect(recv).to.be.calledWith(hostInfo, ["src", ""], "dst", [], 0);
|
|
168
|
+
});
|
|
169
|
+
it("should be rejected if dst is not string", ()=>{
|
|
170
|
+
return expect(ssh.recv(["src"], 0)).to.be.rejectedWith("dst must be string");
|
|
171
|
+
});
|
|
172
|
+
it("should be rejected if dst is empty string", ()=>{
|
|
173
|
+
return expect(ssh.recv(["src"], "")).to.be.rejectedWith("dst must be non-empty string");
|
|
174
|
+
});
|
|
175
|
+
it("should be rejected if opt is not array of string", ()=>{
|
|
176
|
+
return expect(ssh.recv(["src"], "dst", [null])).to.be.rejectedWith("opt must be array of string");
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
});
|
package/test/rsyncExec.js
CHANGED
|
@@ -50,7 +50,7 @@ describe("test rsync exec", function() {
|
|
|
50
50
|
};
|
|
51
51
|
let remoteHome;
|
|
52
52
|
before(async ()=>{
|
|
53
|
-
await sshExec(hostInfo, "pwd", (data)=>{
|
|
53
|
+
await sshExec(hostInfo, "pwd", 0, (data)=>{
|
|
54
54
|
remoteHome = data.trim();
|
|
55
55
|
});
|
|
56
56
|
});
|
|
@@ -71,57 +71,57 @@ describe("test rsync exec", function() {
|
|
|
71
71
|
describe("#send", async ()=>{
|
|
72
72
|
describe("send single file", ()=>{
|
|
73
73
|
it("should accept relative src file and relative dst dir name", async ()=>{
|
|
74
|
-
await send(hostInfo, [localFiles[0]], remoteEmptyDir);
|
|
74
|
+
await send(hostInfo, [localFiles[0]], remoteEmptyDir, [], 0);
|
|
75
75
|
|
|
76
|
-
await sshExec(hostInfo, `ls ${remoteEmptyDir}`, output);
|
|
76
|
+
await sshExec(hostInfo, `ls ${remoteEmptyDir}`, 0, output);
|
|
77
77
|
expect(formatLsOutput(rt)).to.have.members(["foo"]);
|
|
78
78
|
});
|
|
79
79
|
it("should accept absolute src file and relative dst dir name", async ()=>{
|
|
80
|
-
await send(hostInfo, [path.resolve(localFiles[3])], remoteEmptyDir);
|
|
80
|
+
await send(hostInfo, [path.resolve(localFiles[3])], remoteEmptyDir, [], 0);
|
|
81
81
|
|
|
82
|
-
await sshExec(hostInfo, `ls ${remoteEmptyDir}`, output);
|
|
82
|
+
await sshExec(hostInfo, `ls ${remoteEmptyDir}`, 0, output);
|
|
83
83
|
expect(formatLsOutput(rt)).to.have.members(["piyo"]);
|
|
84
84
|
});
|
|
85
85
|
it("should accept relative src file and absolute dst dir name", async ()=>{
|
|
86
|
-
await send(hostInfo, [localFiles[0]], `${path.posix.join(remoteHome, remoteEmptyDir)}
|
|
86
|
+
await send(hostInfo, [localFiles[0]], `${path.posix.join(remoteHome, remoteEmptyDir)}/`, [], 0);
|
|
87
87
|
|
|
88
|
-
await sshExec(hostInfo, `ls ${remoteEmptyDir}`, output);
|
|
88
|
+
await sshExec(hostInfo, `ls ${remoteEmptyDir}`, 0, output);
|
|
89
89
|
expect(formatLsOutput(rt)).to.have.members(["foo"]);
|
|
90
90
|
});
|
|
91
91
|
it("should accept absolute src file and absolute dst dir name", async ()=>{
|
|
92
|
-
await send(hostInfo, [path.resolve(localFiles[0])], path.posix.join(remoteHome, remoteEmptyDir));
|
|
92
|
+
await send(hostInfo, [path.resolve(localFiles[0])], path.posix.join(remoteHome, remoteEmptyDir), [], 0);
|
|
93
93
|
|
|
94
|
-
await sshExec(hostInfo, `ls ${remoteEmptyDir}`, output);
|
|
94
|
+
await sshExec(hostInfo, `ls ${remoteEmptyDir}`, 0, output);
|
|
95
95
|
expect(formatLsOutput(rt)).to.have.members(["foo"]);
|
|
96
96
|
});
|
|
97
97
|
it("should accept relative src file and relative dst file name", async ()=>{
|
|
98
|
-
await send(hostInfo, [localFiles[0]], path.posix.join(remoteEmptyDir, "hoge"));
|
|
98
|
+
await send(hostInfo, [localFiles[0]], path.posix.join(remoteEmptyDir, "hoge"), [], 0);
|
|
99
99
|
|
|
100
|
-
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, "hoge")}`, output);
|
|
100
|
+
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, "hoge")}`, 0, output);
|
|
101
101
|
expect(formatLsOutput(rt.map((e)=>{
|
|
102
102
|
return path.posix.basename(e);
|
|
103
103
|
}))).to.have.members(["hoge"]);
|
|
104
104
|
});
|
|
105
105
|
it("should accept absolute src file and relative dst file name", async ()=>{
|
|
106
|
-
await send(hostInfo, [path.resolve(localFiles[0])], path.posix.join(remoteEmptyDir, "hoge"));
|
|
106
|
+
await send(hostInfo, [path.resolve(localFiles[0])], path.posix.join(remoteEmptyDir, "hoge"), [], 0);
|
|
107
107
|
|
|
108
|
-
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, "hoge")}`, output);
|
|
108
|
+
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, "hoge")}`, 0, output);
|
|
109
109
|
expect(formatLsOutput(rt.map((e)=>{
|
|
110
110
|
return path.posix.basename(e);
|
|
111
111
|
}))).to.have.members(["hoge"]);
|
|
112
112
|
});
|
|
113
113
|
it("should accept relative src file and absolute dst file name", async ()=>{
|
|
114
|
-
await send(hostInfo, [localFiles[0]], path.posix.join(remoteHome, remoteEmptyDir, "hoge"));
|
|
114
|
+
await send(hostInfo, [localFiles[0]], path.posix.join(remoteHome, remoteEmptyDir, "hoge"), [], 0);
|
|
115
115
|
|
|
116
|
-
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, "hoge")}`, output);
|
|
116
|
+
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, "hoge")}`, 0, output);
|
|
117
117
|
expect(formatLsOutput(rt.map((e)=>{
|
|
118
118
|
return path.posix.basename(e);
|
|
119
119
|
}))).to.have.members(["hoge"]);
|
|
120
120
|
});
|
|
121
121
|
it("should accept absolute src file and absolute dst file name", async ()=>{
|
|
122
|
-
await send(hostInfo, [path.resolve(localFiles[0])], path.posix.join(remoteHome, remoteEmptyDir, "hoge"));
|
|
122
|
+
await send(hostInfo, [path.resolve(localFiles[0])], path.posix.join(remoteHome, remoteEmptyDir, "hoge"), [], 0);
|
|
123
123
|
|
|
124
|
-
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, "hoge")}`, output);
|
|
124
|
+
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, "hoge")}`, 0, output);
|
|
125
125
|
expect(formatLsOutput(rt.map((e)=>{
|
|
126
126
|
return path.posix.basename(e);
|
|
127
127
|
}))).to.have.members(["hoge"]);
|
|
@@ -129,8 +129,8 @@ describe("test rsync exec", function() {
|
|
|
129
129
|
|
|
130
130
|
it("should overwrite existing file", async ()=>{
|
|
131
131
|
const target = path.posix.join(remoteEmptyDir, "hoge");
|
|
132
|
-
await send(hostInfo, [localFiles[0]], target);
|
|
133
|
-
await sshExec(hostInfo, `ls ${target}`, output);
|
|
132
|
+
await send(hostInfo, [localFiles[0]], target, [], 0);
|
|
133
|
+
await sshExec(hostInfo, `ls ${target}`, 0, output);
|
|
134
134
|
expect(formatLsOutput(rt.map((e)=>{
|
|
135
135
|
return path.posix.basename(e);
|
|
136
136
|
}))).to.have.members(["hoge"]);
|
|
@@ -139,168 +139,168 @@ describe("test rsync exec", function() {
|
|
|
139
139
|
const time = new Date();
|
|
140
140
|
time.setMinutes(time.getMinutes() + 3);
|
|
141
141
|
await fs.utimes(localFiles[1], time, time);
|
|
142
|
-
await send(hostInfo, [localFiles[1]], target);
|
|
143
|
-
await sshExec(hostInfo, `ls ${target}`, output);
|
|
142
|
+
await send(hostInfo, [localFiles[1]], target, [], 0);
|
|
143
|
+
await sshExec(hostInfo, `ls ${target}`, 0, output);
|
|
144
144
|
expect(formatLsOutput(rt.map((e)=>{
|
|
145
145
|
return path.posix.basename(e);
|
|
146
146
|
}))).to.have.members(["hoge"]);
|
|
147
147
|
|
|
148
148
|
rt.splice(0, rt.length);
|
|
149
|
-
await sshExec(hostInfo, `cat ${target}`, output);
|
|
149
|
+
await sshExec(hostInfo, `cat ${target}`, 0, output);
|
|
150
150
|
expect(formatLsOutput(rt)).to.have.members(["ARssh_testLocalDir/bar"]);
|
|
151
151
|
});
|
|
152
152
|
});
|
|
153
153
|
describe("send directory tree", ()=>{
|
|
154
154
|
it("should accept relative src dirname and relative dst dirname", async ()=>{
|
|
155
|
-
await send(hostInfo, [localRoot], remoteEmptyDir);
|
|
155
|
+
await send(hostInfo, [localRoot], remoteEmptyDir, [], 0);
|
|
156
156
|
|
|
157
|
-
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot)}`, output);
|
|
157
|
+
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot)}`, 0, output);
|
|
158
158
|
expect(formatLsOutput(rt)).to.have.members(["foo", "bar", "baz", "hoge", "huga"]);
|
|
159
159
|
|
|
160
160
|
rt.splice(0, rt.length);
|
|
161
|
-
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot, "hoge")}`, output);
|
|
161
|
+
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot, "hoge")}`, 0, output);
|
|
162
162
|
expect(formatLsOutput(rt)).to.have.members(["piyo", "puyo", "poyo"]);
|
|
163
163
|
});
|
|
164
164
|
it("should accept absolute src dirname and relative dst dirname", async ()=>{
|
|
165
|
-
await send(hostInfo, [path.resolve(localRoot)], remoteEmptyDir);
|
|
165
|
+
await send(hostInfo, [path.resolve(localRoot)], remoteEmptyDir, [], 0);
|
|
166
166
|
|
|
167
|
-
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot)}`, output);
|
|
167
|
+
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot)}`, 0, output);
|
|
168
168
|
expect(formatLsOutput(rt)).to.have.members(["foo", "bar", "baz", "hoge", "huga"]);
|
|
169
169
|
|
|
170
170
|
rt.splice(0, rt.length);
|
|
171
|
-
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot, "hoge")}`, output);
|
|
171
|
+
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot, "hoge")}`, 0, output);
|
|
172
172
|
expect(formatLsOutput(rt)).to.have.members(["piyo", "puyo", "poyo"]);
|
|
173
173
|
});
|
|
174
174
|
it("should accept relative src dirname and absolute dst dirname", async ()=>{
|
|
175
|
-
await send(hostInfo, [localRoot], path.posix.join(remoteHome, remoteEmptyDir));
|
|
175
|
+
await send(hostInfo, [localRoot], path.posix.join(remoteHome, remoteEmptyDir), [], 0);
|
|
176
176
|
|
|
177
|
-
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot)}`, output);
|
|
177
|
+
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot)}`, 0, output);
|
|
178
178
|
expect(formatLsOutput(rt)).to.have.members(["foo", "bar", "baz", "hoge", "huga"]);
|
|
179
179
|
|
|
180
180
|
rt.splice(0, rt.length);
|
|
181
|
-
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot, "hoge")}`, output);
|
|
181
|
+
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot, "hoge")}`, 0, output);
|
|
182
182
|
expect(formatLsOutput(rt)).to.have.members(["piyo", "puyo", "poyo"]);
|
|
183
183
|
});
|
|
184
184
|
it("should accept absolute src dirname and absolute dst dirname", async ()=>{
|
|
185
|
-
await send(hostInfo, [path.resolve(localRoot)], path.posix.join(remoteHome, remoteEmptyDir));
|
|
185
|
+
await send(hostInfo, [path.resolve(localRoot)], path.posix.join(remoteHome, remoteEmptyDir), [], 0);
|
|
186
186
|
|
|
187
|
-
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot)}`, output);
|
|
187
|
+
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot)}`, 0, output);
|
|
188
188
|
expect(formatLsOutput(rt)).to.have.members(["foo", "bar", "baz", "hoge", "huga"]);
|
|
189
189
|
|
|
190
190
|
rt.splice(0, rt.length);
|
|
191
|
-
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot, "hoge")}`, output);
|
|
191
|
+
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot, "hoge")}`, 0, output);
|
|
192
192
|
expect(formatLsOutput(rt)).to.have.members(["piyo", "puyo", "poyo"]);
|
|
193
193
|
});
|
|
194
194
|
//only option need special treatment in rsyncExec. it's hard to set in WHEEL
|
|
195
195
|
it.skip("[not implemented] should send directory tree if only filter matched", async ()=>{
|
|
196
|
-
await send(hostInfo, [localRoot], remoteEmptyDir, [`--include=${localRoot}`, `--include=${localRoot}/ba*`, `--include=${localRoot}/hoge/*`, "--exclude=*"]);
|
|
196
|
+
await send(hostInfo, [localRoot], remoteEmptyDir, [`--include=${localRoot}`, `--include=${localRoot}/ba*`, `--include=${localRoot}/hoge/*`, "--exclude=*"], [], 0);
|
|
197
197
|
|
|
198
|
-
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot)}`, output);
|
|
198
|
+
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot)}`, 0, output);
|
|
199
199
|
expect(formatLsOutput(rt)).to.have.members(["hoge", "bar", "baz"]);
|
|
200
200
|
|
|
201
201
|
rt.splice(0, rt.length);
|
|
202
|
-
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot, "hoge")}`, output);
|
|
202
|
+
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot, "hoge")}`, 0, output);
|
|
203
203
|
expect(formatLsOutput(rt)).to.have.members(["piyo", "puyo", "poyo"]);
|
|
204
204
|
});
|
|
205
205
|
it("should send directory tree if exclude filter not matched", async ()=>{
|
|
206
|
-
await send(hostInfo, [localRoot], remoteEmptyDir, ["--exclude=ba*", "--exclude=hoge*}"]);
|
|
206
|
+
await send(hostInfo, [localRoot], remoteEmptyDir, ["--exclude=ba*", "--exclude=hoge*}"], [], 0);
|
|
207
207
|
|
|
208
|
-
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot)}`, output);
|
|
208
|
+
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot)}`, 0, output);
|
|
209
209
|
expect(formatLsOutput(rt)).to.have.members(["foo", "hoge", "huga"]);
|
|
210
210
|
});
|
|
211
211
|
it.skip("[not implemented] should send directory tree if only filter matched but exclude filter not matched", async ()=>{
|
|
212
|
-
await send(hostInfo, [localRoot], remoteEmptyDir, "*/{ba*,hoge/*}", "**/poyo");
|
|
212
|
+
await send(hostInfo, [localRoot], remoteEmptyDir, "*/{ba*,hoge/*}", "**/poyo", [], 0);
|
|
213
213
|
|
|
214
|
-
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot)}`, output);
|
|
214
|
+
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot)}`, 0, output);
|
|
215
215
|
expect(formatLsOutput(rt)).to.have.members(["hoge", "bar", "baz"]);
|
|
216
216
|
|
|
217
217
|
rt.splice(0, rt.length);
|
|
218
|
-
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot, "hoge")}`, output);
|
|
218
|
+
await sshExec(hostInfo, `ls ${path.posix.join(remoteEmptyDir, localRoot, "hoge")}`, 0, output);
|
|
219
219
|
expect(formatLsOutput(rt)).to.have.members(["piyo", "puyo"]);
|
|
220
220
|
});
|
|
221
221
|
it("shoud not send empty directory with -m option", async ()=>{
|
|
222
|
-
await send(hostInfo, [localEmptyDir], remoteEmptyDir, ["-m"]);
|
|
222
|
+
await send(hostInfo, [localEmptyDir], remoteEmptyDir, ["-m"], [], 0);
|
|
223
223
|
|
|
224
|
-
await sshExec(hostInfo, `ls ${remoteEmptyDir}`, output);
|
|
224
|
+
await sshExec(hostInfo, `ls ${remoteEmptyDir}`, 0, output);
|
|
225
225
|
expect(rt).to.have.lengthOf(0);
|
|
226
226
|
});
|
|
227
227
|
it("should send files which match glob pattern", async ()=>{
|
|
228
|
-
await send(hostInfo, [path.join(localRoot, "b*")], remoteEmptyDir);
|
|
228
|
+
await send(hostInfo, [path.join(localRoot, "b*")], remoteEmptyDir, [], 0);
|
|
229
229
|
|
|
230
|
-
await sshExec(hostInfo, `ls ${remoteEmptyDir}`, output);
|
|
230
|
+
await sshExec(hostInfo, `ls ${remoteEmptyDir}`, 0, output);
|
|
231
231
|
expect(formatLsOutput(rt)).to.have.members(["bar", "baz"]);
|
|
232
232
|
});
|
|
233
233
|
});
|
|
234
234
|
describe("error case", ()=>{
|
|
235
235
|
it("should not send directory to existing file path", ()=>{
|
|
236
|
-
return expect(send(hostInfo, [localRoot], path.posix.join(remoteRoot, "foo"))).to.be.rejected;
|
|
236
|
+
return expect(send(hostInfo, [localRoot], path.posix.join(remoteRoot, "foo"), [], 0)).to.be.rejected;
|
|
237
237
|
});
|
|
238
238
|
});
|
|
239
239
|
});
|
|
240
240
|
describe("#recv", async ()=>{
|
|
241
241
|
describe("recieve single file", ()=>{
|
|
242
242
|
it("should accept relative src file and relative dst dir name", async ()=>{
|
|
243
|
-
await recv(hostInfo, [remoteFiles[3]], localEmptyDir);
|
|
243
|
+
await recv(hostInfo, [remoteFiles[3]], localEmptyDir, [], 0);
|
|
244
244
|
|
|
245
245
|
const rt2 = await fs.readdir(localEmptyDir);
|
|
246
246
|
expect(rt2).to.have.members(["piyo"]);
|
|
247
247
|
});
|
|
248
248
|
it("should accept absolute src file and relative dst dir name", async ()=>{
|
|
249
|
-
await recv(hostInfo, [path.posix.join(remoteHome, remoteFiles[3])], localEmptyDir);
|
|
249
|
+
await recv(hostInfo, [path.posix.join(remoteHome, remoteFiles[3])], localEmptyDir, [], 0);
|
|
250
250
|
|
|
251
251
|
const rt2 = await fs.readdir(localEmptyDir);
|
|
252
252
|
expect(rt2).to.have.members(["piyo"]);
|
|
253
253
|
});
|
|
254
254
|
it("should accept relative src file and absolute dst dir name", async ()=>{
|
|
255
|
-
await recv(hostInfo, [remoteFiles[3]], path.resolve(localEmptyDir));
|
|
255
|
+
await recv(hostInfo, [remoteFiles[3]], path.resolve(localEmptyDir), [], 0);
|
|
256
256
|
|
|
257
257
|
const rt2 = await fs.readdir(localEmptyDir);
|
|
258
258
|
expect(rt2).to.have.members(["piyo"]);
|
|
259
259
|
});
|
|
260
260
|
it("should accept absolute src file and absolute dst dir name", async ()=>{
|
|
261
|
-
await recv(hostInfo, [path.posix.join(remoteHome, remoteFiles[3])], path.resolve(localEmptyDir));
|
|
261
|
+
await recv(hostInfo, [path.posix.join(remoteHome, remoteFiles[3])], path.resolve(localEmptyDir), [], 0);
|
|
262
262
|
|
|
263
263
|
const rt2 = await fs.readdir(localEmptyDir);
|
|
264
264
|
expect(rt2).to.have.members(["piyo"]);
|
|
265
265
|
});
|
|
266
266
|
it("should accept relative src file and relative dst file name", async ()=>{
|
|
267
|
-
await recv(hostInfo, [remoteFiles[0]], path.join(localEmptyDir, "hoge"));
|
|
267
|
+
await recv(hostInfo, [remoteFiles[0]], path.join(localEmptyDir, "hoge"), [], 0);
|
|
268
268
|
|
|
269
269
|
const rt2 = await fs.readdir(localEmptyDir);
|
|
270
270
|
expect(rt2).to.have.members(["hoge"]);
|
|
271
271
|
});
|
|
272
272
|
it("should accept absolute src file and relative dst file name", async ()=>{
|
|
273
|
-
await recv(hostInfo, [path.posix.join(remoteHome, remoteFiles[0])], path.join(localEmptyDir, "hoge"));
|
|
273
|
+
await recv(hostInfo, [path.posix.join(remoteHome, remoteFiles[0])], path.join(localEmptyDir, "hoge"), [], 0);
|
|
274
274
|
|
|
275
275
|
const rt2 = await fs.readdir(localEmptyDir);
|
|
276
276
|
expect(rt2).to.have.members(["hoge"]);
|
|
277
277
|
});
|
|
278
278
|
it("should accept relative src file and absolute dst file name", async ()=>{
|
|
279
|
-
await recv(hostInfo, [remoteFiles[0]], path.resolve(localEmptyDir, "hoge"));
|
|
279
|
+
await recv(hostInfo, [remoteFiles[0]], path.resolve(localEmptyDir, "hoge"), [], 0);
|
|
280
280
|
|
|
281
281
|
const rt2 = await fs.readdir(localEmptyDir);
|
|
282
282
|
expect(rt2).to.have.members(["hoge"]);
|
|
283
283
|
});
|
|
284
284
|
it("should accept absolute src file and absolute dst file name", async ()=>{
|
|
285
|
-
await recv(hostInfo, [path.posix.join(remoteHome, remoteFiles[0])], path.resolve(localEmptyDir, "hoge"));
|
|
285
|
+
await recv(hostInfo, [path.posix.join(remoteHome, remoteFiles[0])], path.resolve(localEmptyDir, "hoge"), [], 0);
|
|
286
286
|
|
|
287
287
|
const rt2 = await fs.readdir(localEmptyDir);
|
|
288
288
|
expect(rt2).to.have.members(["hoge"]);
|
|
289
289
|
});
|
|
290
290
|
it("should accept glob pattern as src", async ()=>{
|
|
291
|
-
await recv(hostInfo, [path.posix.join(remoteRoot, "b*")], path.resolve(localEmptyDir));
|
|
291
|
+
await recv(hostInfo, [path.posix.join(remoteRoot, "b*")], path.resolve(localEmptyDir), [], 0);
|
|
292
292
|
|
|
293
293
|
const rt2 = await fs.readdir(localEmptyDir);
|
|
294
294
|
expect(rt2).to.have.members(["bar", "baz"]);
|
|
295
295
|
});
|
|
296
296
|
it("should accept glob pattern as src", async ()=>{
|
|
297
|
-
await recv(hostInfo, [path.posix.join(remoteRoot, "*/p[iu]yo")], path.resolve(localEmptyDir));
|
|
297
|
+
await recv(hostInfo, [path.posix.join(remoteRoot, "*/p[iu]yo")], path.resolve(localEmptyDir), [], 0);
|
|
298
298
|
|
|
299
299
|
const rt2 = await fs.readdir(localEmptyDir);
|
|
300
300
|
expect(rt2).to.have.members(["piyo", "puyo"]);
|
|
301
301
|
});
|
|
302
302
|
it.skip("[temporarily disabled number 23 was thrown] should accept glob pattern as src", async ()=>{
|
|
303
|
-
await recv(hostInfo, [path.posix.join(remoteRoot, "hoge/p[iu]yo"), path.posix.join(remoteRoot, "foo")], path.resolve(localEmptyDir));
|
|
303
|
+
await recv(hostInfo, [path.posix.join(remoteRoot, "hoge/p[iu]yo"), path.posix.join(remoteRoot, "foo")], path.resolve(localEmptyDir), [], 0);
|
|
304
304
|
|
|
305
305
|
const rt2 = await fs.readdir(localEmptyDir);
|
|
306
306
|
expect(rt2).to.have.members(["piyo", "puyo", "foo"]);
|
|
@@ -308,7 +308,7 @@ describe("test rsync exec", function() {
|
|
|
308
308
|
});
|
|
309
309
|
describe("recieve directory tree", ()=>{
|
|
310
310
|
it("should get directory tree which match specified glob", async ()=>{
|
|
311
|
-
await recv(hostInfo, [path.posix.join(remoteRoot, "?o*")], localEmptyDir);
|
|
311
|
+
await recv(hostInfo, [path.posix.join(remoteRoot, "?o*")], localEmptyDir, [], 0);
|
|
312
312
|
|
|
313
313
|
let rt2 = await fs.readdir(localEmptyDir);
|
|
314
314
|
expect(rt2).to.have.members(["foo", "hoge"]);
|
|
@@ -316,7 +316,7 @@ describe("test rsync exec", function() {
|
|
|
316
316
|
expect(rt2).to.have.members(["piyo", "puyo", "poyo"]);
|
|
317
317
|
});
|
|
318
318
|
it("should accept relative src dirname and relative dst dirname", async ()=>{
|
|
319
|
-
await recv(hostInfo, [remoteRoot], localEmptyDir);
|
|
319
|
+
await recv(hostInfo, [remoteRoot], localEmptyDir, [], 0);
|
|
320
320
|
|
|
321
321
|
let rt2 = await fs.readdir(path.posix.join(localEmptyDir, remoteRoot));
|
|
322
322
|
expect(rt2).to.have.members(["foo", "bar", "baz", "hoge", "huga"]);
|
|
@@ -324,7 +324,7 @@ describe("test rsync exec", function() {
|
|
|
324
324
|
expect(rt2).to.have.members(["piyo", "puyo", "poyo"]);
|
|
325
325
|
});
|
|
326
326
|
it("should accept absolute src dirname and relative dst dirname", async ()=>{
|
|
327
|
-
await recv(hostInfo, [path.posix.join(remoteHome, remoteRoot)], localEmptyDir);
|
|
327
|
+
await recv(hostInfo, [path.posix.join(remoteHome, remoteRoot)], localEmptyDir, [], 0);
|
|
328
328
|
|
|
329
329
|
let rt2 = await fs.readdir(path.posix.join(localEmptyDir, remoteRoot));
|
|
330
330
|
expect(rt2).to.have.members(["foo", "bar", "baz", "hoge", "huga"]);
|
|
@@ -332,7 +332,7 @@ describe("test rsync exec", function() {
|
|
|
332
332
|
expect(rt2).to.have.members(["piyo", "puyo", "poyo"]);
|
|
333
333
|
});
|
|
334
334
|
it("should accept relative src dirname and absolute dst dirname", async ()=>{
|
|
335
|
-
await recv(hostInfo, [remoteRoot], path.resolve(localEmptyDir));
|
|
335
|
+
await recv(hostInfo, [remoteRoot], path.resolve(localEmptyDir), [], 0);
|
|
336
336
|
|
|
337
337
|
let rt2 = await fs.readdir(path.posix.join(localEmptyDir, remoteRoot));
|
|
338
338
|
expect(rt2).to.have.members(["foo", "bar", "baz", "hoge", "huga"]);
|
|
@@ -340,7 +340,7 @@ describe("test rsync exec", function() {
|
|
|
340
340
|
expect(rt2).to.have.members(["piyo", "puyo", "poyo"]);
|
|
341
341
|
});
|
|
342
342
|
it("should accept absolute src dirname and absolute dst dirname", async ()=>{
|
|
343
|
-
await recv(hostInfo, [path.posix.join(remoteHome, remoteRoot)], path.resolve(localEmptyDir));
|
|
343
|
+
await recv(hostInfo, [path.posix.join(remoteHome, remoteRoot)], path.resolve(localEmptyDir), [], 0);
|
|
344
344
|
|
|
345
345
|
let rt2 = await fs.readdir(path.posix.join(localEmptyDir, remoteRoot));
|
|
346
346
|
expect(rt2).to.have.members(["foo", "bar", "baz", "hoge", "huga"]);
|
|
@@ -348,7 +348,7 @@ describe("test rsync exec", function() {
|
|
|
348
348
|
expect(rt2).to.have.members(["piyo", "puyo", "poyo"]);
|
|
349
349
|
});
|
|
350
350
|
it.skip("[not implemented] should recv files which matches only filter", async ()=>{
|
|
351
|
-
await recv(hostInfo, [remoteRoot], localEmptyDir, "*/{ba*,hoge/*}");
|
|
351
|
+
await recv(hostInfo, [remoteRoot], localEmptyDir, "*/{ba*,hoge/*}", [], 0);
|
|
352
352
|
|
|
353
353
|
let rt2 = await fs.readdir(path.posix.join(localEmptyDir, remoteRoot));
|
|
354
354
|
expect(rt2).to.have.members(["bar", "baz", "hoge"]);
|
|
@@ -356,13 +356,13 @@ describe("test rsync exec", function() {
|
|
|
356
356
|
expect(rt2).to.have.members(["piyo", "puyo", "poyo"]);
|
|
357
357
|
});
|
|
358
358
|
it("should not recv files which matches exclude filter", async ()=>{
|
|
359
|
-
await recv(hostInfo, [remoteRoot], localEmptyDir, ["--exclude=*/ba*", "--exclude=hoge"]);
|
|
359
|
+
await recv(hostInfo, [remoteRoot], localEmptyDir, ["--exclude=*/ba*", "--exclude=hoge"], [], 0);
|
|
360
360
|
|
|
361
361
|
const rt2 = await fs.readdir(path.posix.join(localEmptyDir, remoteRoot));
|
|
362
362
|
expect(rt2).to.have.members(["foo", "huga"]);
|
|
363
363
|
});
|
|
364
364
|
it.skip("[not implemented] should recv files which matches only filter but should not recv which matches exclude filter", async ()=>{
|
|
365
|
-
await recv(hostInfo, [remoteRoot], localEmptyDir, "*/{ba*,hoge/*}", "**/piyo");
|
|
365
|
+
await recv(hostInfo, [remoteRoot], localEmptyDir, "*/{ba*,hoge/*}", "**/piyo", [], 0);
|
|
366
366
|
|
|
367
367
|
let rt2 = await fs.readdir(path.posix.join(localEmptyDir, remoteRoot));
|
|
368
368
|
expect(rt2).to.have.members(["bar", "baz", "hoge"]);
|
|
@@ -372,7 +372,7 @@ describe("test rsync exec", function() {
|
|
|
372
372
|
});
|
|
373
373
|
describe("error case", ()=>{
|
|
374
374
|
it("should not send directory to existing file path", ()=>{
|
|
375
|
-
return expect(recv(hostInfo, [remoteRoot], localFiles[0])).to.be.rejected;
|
|
375
|
+
return expect(recv(hostInfo, [remoteRoot], localFiles[0], [], 0)).to.be.rejected;
|
|
376
376
|
});
|
|
377
377
|
});
|
|
378
378
|
});
|
package/test/sshExec.js
CHANGED
|
@@ -36,14 +36,14 @@ describe("test for ssh execution", function() {
|
|
|
36
36
|
describe("#exec", ()=>{
|
|
37
37
|
const testText = "hoge";
|
|
38
38
|
it("should execute single command with stdout", async ()=>{
|
|
39
|
-
const rt = await sshExec(hostInfo, `echo ${testText}`, sshout);
|
|
39
|
+
const rt = await sshExec(hostInfo, `echo ${testText}`, 0, sshout);
|
|
40
40
|
expect(rt).to.equal(0);
|
|
41
41
|
expect(sshout).to.be.called;
|
|
42
42
|
|
|
43
43
|
expect(sshout).to.be.calledWithMatch(/^hoge/);
|
|
44
44
|
});
|
|
45
45
|
it("should execute single command with stderr", async ()=>{
|
|
46
|
-
const rt = await sshExec(hostInfo, `echo ${testText} >&2`, sshout);
|
|
46
|
+
const rt = await sshExec(hostInfo, `echo ${testText} >&2`, 0, sshout);
|
|
47
47
|
expect(rt).to.equal(0);
|
|
48
48
|
expect(sshout).to.be.called;
|
|
49
49
|
expect(sshout).to.be.calledWithMatch(/^hoge/);
|
|
@@ -61,6 +61,9 @@ describe("test for ssh execution", function() {
|
|
|
61
61
|
const rt = await sshExec(hostInfo, `./${nonExisting}`);
|
|
62
62
|
expect(rt).to.equal(127);
|
|
63
63
|
});
|
|
64
|
+
it("should be rejected if timeout expired", async ()=>{
|
|
65
|
+
return expect(sshExec(hostInfo, "sleep 60", 1)).to.be.rejectedWith("watchdog timer expired");
|
|
66
|
+
});
|
|
64
67
|
});
|
|
65
68
|
describe("#canConnect", ()=>{
|
|
66
69
|
let hostInfo2;
|
|
@@ -106,31 +109,31 @@ describe("test for ssh execution", function() {
|
|
|
106
109
|
await createRemoteFiles(hostInfo);
|
|
107
110
|
});
|
|
108
111
|
it("should return array of file and directory names in the specified directory", async ()=>{
|
|
109
|
-
expect(await ls(hostInfo, remoteRoot)).to.have.members(["foo", "bar", "baz", "hoge", "huga"]);
|
|
112
|
+
expect(await ls(hostInfo, remoteRoot, [], 0)).to.have.members(["foo", "bar", "baz", "hoge", "huga"]);
|
|
110
113
|
});
|
|
111
114
|
it("should return array which has only specified file", async ()=>{
|
|
112
|
-
expect(await ls(hostInfo, path.posix.join(remoteRoot, "foo"))).to.eql([path.posix.join(remoteRoot, "foo")]);
|
|
115
|
+
expect(await ls(hostInfo, path.posix.join(remoteRoot, "foo"), [], 0)).to.eql([path.posix.join(remoteRoot, "foo")]);
|
|
113
116
|
});
|
|
114
117
|
it("should return 'No such file or directory' if non-existing path is specified", async ()=>{
|
|
115
|
-
expect(await ls(hostInfo, path.posix.join(remoteRoot, nonExisting))).to.match(/No such file or directory/);
|
|
118
|
+
expect(await ls(hostInfo, path.posix.join(remoteRoot, nonExisting), [], 0)).to.match(/No such file or directory/);
|
|
116
119
|
});
|
|
117
120
|
it("should return only matched filenames, if glob is specified", async ()=>{
|
|
118
|
-
expect(await ls(hostInfo, path.posix.join(remoteRoot, "b*"))).to.have.members(["bar", "baz"].map((e)=>{
|
|
121
|
+
expect(await ls(hostInfo, path.posix.join(remoteRoot, "b*"), [], 0)).to.have.members(["bar", "baz"].map((e)=>{
|
|
119
122
|
return path.posix.join(remoteRoot, e);
|
|
120
123
|
}));
|
|
121
124
|
});
|
|
122
125
|
it("should return only matched filenames src contains multipl glob pattern", async ()=>{
|
|
123
|
-
expect(await ls(hostInfo, path.posix.join(remoteRoot, "h*", "p[iu]yo"))).to.have.members(["piyo", "puyo"].map((e)=>{
|
|
126
|
+
expect(await ls(hostInfo, path.posix.join(remoteRoot, "h*", "p[iu]yo"), [], 0)).to.have.members(["piyo", "puyo"].map((e)=>{
|
|
124
127
|
return path.posix.join(remoteRoot, "hoge", e);
|
|
125
128
|
}));
|
|
126
129
|
});
|
|
127
130
|
it("should return only matched filenames, if specified glob contains /", async ()=>{
|
|
128
|
-
expect(await ls(hostInfo, path.posix.join(remoteRoot, "hoge/*yo"))).to.have.members(["piyo", "puyo", "poyo"].map((e)=>{
|
|
131
|
+
expect(await ls(hostInfo, path.posix.join(remoteRoot, "hoge/*yo"), [], 0)).to.have.members(["piyo", "puyo", "poyo"].map((e)=>{
|
|
129
132
|
return path.posix.join(remoteRoot, "hoge", e);
|
|
130
133
|
}));
|
|
131
134
|
});
|
|
132
135
|
it("should return empty array if specified target is empty directory", async ()=>{
|
|
133
|
-
expect(await ls(hostInfo, remoteEmptyDir)).to.be.an("array").that.is.empty;
|
|
136
|
+
expect(await ls(hostInfo, remoteEmptyDir, [], 0)).to.be.an("array").that.is.empty;
|
|
134
137
|
});
|
|
135
138
|
});
|
|
136
139
|
});
|