ssh-client-wrapper 2.10.1 → 2.11.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.
@@ -23,7 +23,7 @@ services:
23
23
  TEST_PW: passw0rd
24
24
  TEST_HOST: testbed
25
25
  working_dir: /work/ssh-client-wrapper
26
- command: bash -c "mkdir /root/.ssh 2>/dev/null; npm run mocha"
26
+ command: bash -c "mkdir /root/.ssh 2>/dev/null; npm run mocha"
27
27
  tester-buster:
28
28
  build:
29
29
  context: .
@@ -33,4 +33,4 @@ services:
33
33
  TEST_PW: passw0rd
34
34
  TEST_HOST: testbed
35
35
  working_dir: /work/ssh-client-wrapper
36
- command: bash -c "mkdir /root/.ssh 2>/dev/null; npm run mocha"
36
+ command: bash -c "mkdir /root/.ssh 2>/dev/null; npm run mocha"
package/lib/index.js CHANGED
@@ -28,6 +28,8 @@ const { isArrayOfString, sanityCheck } = require("./util.js");
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
30
  * @param {Integer} hostInfo.retryDuration=1000 - duration time (msec) between each retry
31
+ * @param {string} hostInfo.rcfile - rcfile path which will be sourced before executing actual command
32
+ * @param {string} hostInfo.prependCmd - command string which will be executed before executing actual command
31
33
  * @param {string[]} hostInfo.sshOpt - additional options for ssh
32
34
  */
33
35
 
@@ -65,15 +67,19 @@ class SshClientWrapper {
65
67
  * @param {string} cmd - Cmdline which will be executed.
66
68
  * @param {Integer} timeout - timeout in secconds
67
69
  * @param { Function } outputCallback - call back routine for output
70
+ * @param {string} rcfile - rcfile path which will be sourced before executing actual command
71
+ * @param {string} prependCmd - command string which will be executed before executing actual command
68
72
  * @returns {Integer} - Return code of cmd.
73
+ *
74
+ * note rcfile and prependCmd override hostInfo.rcfile and hostInfo.prependCmd respectively
69
75
  */
70
- async exec (cmd, timeout = 0, outputCallback = null) {
76
+ async exec (cmd, timeout = 0, outputCallback = null, rcfile = null, prependCmd = null) {
71
77
  debug("exec called", cmd);
72
78
 
73
79
  if (typeof cmd !== "string" || cmd === "") {
74
80
  return logAndReject("cmd must be string");
75
81
  }
76
- return sshExec(this.hostInfo, cmd, timeout, outputCallback);
82
+ return sshExec(this.hostInfo, cmd, timeout, outputCallback, rcfile, prependCmd);
77
83
  }
78
84
 
79
85
  /**
package/lib/rsyncExec.js CHANGED
@@ -118,10 +118,9 @@ async function recv (hostInfo, argSrc, dst, rsyncOpt, timeout) {
118
118
  args.unshift("--old-args");
119
119
  }
120
120
 
121
- // make destination directory if multi files will be transferd
122
- if (argSrc.len > 1 || hasMagic(argSrc[0])) {
123
- await fs.mkdir(dst, { recursive: true });
124
- }
121
+ // make destination directory
122
+ const dstDir = argSrc.len > 1 || hasMagic(argSrc[0]) ? dst : path.dirname(dst);
123
+ await fs.mkdir(dstDir, { recursive: true });
125
124
 
126
125
  debug(`recv ${src} to ${dst}`);
127
126
  debugVerbose(`recv: ${rsyncCmd} ${args.join(" ")}`);
package/lib/sshExec.js CHANGED
@@ -12,13 +12,26 @@ const { fork, createMasterPty, sshLoginCallback } = require("./fork.js");
12
12
  * @param {Object} hostInfo - hostinfo object
13
13
  * @param {string} cmd - Cmdline which will be executed on remote host.
14
14
  * @param {number} timeout - timeout (in sec.)
15
+ * @param {string} rcfile - rcfile path which will be sourced before executing actual command
16
+ * @param {string} prependCmd - command string which will be executed before executing actual command
15
17
  * @param {Function} outputCallback - Callback routine for stdout and stderr.
16
18
  */
17
- async function sshExec (hostInfo, cmd, timeout, outputCallback) {
19
+ async function sshExec (hostInfo, cmd, timeout, outputCallback, rcfile, prependCmd) {
18
20
  debug("sshExec called");
19
21
  await connect(hostInfo);
20
22
  debug("exec", cmd, "on remote server");
21
23
  const args = getSshOption(hostInfo, false);
24
+ if (rcfile) {
25
+ args.push(`. ${rcfile};`);
26
+ } else if (hostInfo.rcfile) {
27
+ args.push(`. ${hostInfo.rcfile};`);
28
+ }
29
+ if (prependCmd) {
30
+ args.push(`${prependCmd};`);
31
+ } else if (hostInfo.prependCmd) {
32
+ args.push(`${hostInfo.prependCmd};`);
33
+ }
34
+
22
35
  args.push(cmd);
23
36
  debugVerbose(`exec: ${sshCmd} ${args.join(" ")}`);
24
37
 
package/lib/util.js CHANGED
@@ -40,6 +40,8 @@ const hostInfoSchema = {
40
40
  ControlPersistDir: { type: "string", pattern: "\\S+", transform: ["trim"] },
41
41
  maxRetry: { type: "number", minimum: 0, default: 3 },
42
42
  retryDuration: { type: "number", minimum: 0, default: 1000 },
43
+ rcfile: { type: "string", pattern: "\\S+", transform: ["trim"] },
44
+ prependCmd: { type: "string", pattern: "\\S+", transform: ["trim"] },
43
45
  sshOpt: { type: "array", minItems: 1, items: { type: "string", pattern: "\\S+", transform: ["trim"] } }
44
46
  },
45
47
  required: ["host"]
@@ -207,6 +209,7 @@ const getSshOption = (hostInfo, withoutDestination) => {
207
209
  return typeof e === "string";
208
210
  }));
209
211
  }
212
+
210
213
  return args;
211
214
  };
212
215
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ssh-client-wrapper",
3
- "version": "2.10.1",
3
+ "version": "2.11.0",
4
4
  "description": "wrapper library for open ssh client and rsync",
5
5
  "keywords": [
6
6
  "ssh",
package/run_test_all.sh CHANGED
@@ -1,21 +1,11 @@
1
1
  #!/bin/bash
2
-
3
2
  function cleanup(){
4
- docker compose rm --stop --force testbed
3
+ docker compose down
5
4
  }
6
-
7
5
  trap cleanup EXIT
8
6
  set -e -o pipefail
9
7
 
10
- docker compose up testbed -d
11
- export TEST_HOST=localhost
12
- export TEST_USER=testuser
13
- export TEST_PW=passw0rd
14
- export TEST_PORT=4100
15
- ssh-keygen -R '[localhost]:4100'
16
-
17
- npm run test
18
- echo local test success
8
+ ./run_test_local.sh keep
19
9
 
20
10
  docker compose run --build --rm tester
21
11
  echo test on latest ubuntu success
@@ -25,3 +15,4 @@ echo test on bullseye success
25
15
 
26
16
  docker compose run --build --rm tester-buster
27
17
  echo test on buster success
18
+ docker compose down
@@ -0,0 +1,13 @@
1
+ #!/bin/bash
2
+ docker compose up testbed -d
3
+ export TEST_HOST=localhost
4
+ export TEST_USER=testuser
5
+ export TEST_PW=passw0rd
6
+ export TEST_PORT=4100
7
+ ssh-keygen -R '[localhost]:4100'
8
+
9
+ npm run test
10
+ echo local test success
11
+ if [ x${1} != xkeep ];then
12
+ docker compose down
13
+ fi
package/test/sshExec.js CHANGED
@@ -19,17 +19,23 @@ const { sshExec, canConnect, disconnect, ls } = require("../lib/sshExec.js");
19
19
  // test helpers
20
20
  const hostInfo = require("./testUtil/hostInfo.js");
21
21
  const { clearRemoteTestFiles, clearLocalTestFiles, createRemoteFiles, nonExisting, remoteRoot, remoteEmptyDir } = require("./testUtil/testFiles.js");
22
+ const { send } = require("../lib/rsyncExec.js");
22
23
 
23
24
  describe("test for ssh execution", function () {
25
+ const rcfilePath = "/home/testuser/testrc";
24
26
  this.timeout(65000);// eslint-disable-line no-invalid-this
25
27
  beforeEach(async () => {
26
28
  sshout.reset();
27
29
  await clearRemoteTestFiles(hostInfo);
28
30
  });
31
+ before(async () => {
32
+ await send(hostInfo, [path.resolve(__dirname, "./testUtil/testrc")], rcfilePath, [], 0);
33
+ });
29
34
  after(async () => {
30
35
  if (!process.env.TEST_KEEP_FILES) {
31
36
  await clearRemoteTestFiles(hostInfo);
32
37
  await clearLocalTestFiles();
38
+ await sshExec(hostInfo, `rm ${rcfilePath}`);
33
39
  }
34
40
  await disconnect(hostInfo);
35
41
  });
@@ -48,8 +54,36 @@ describe("test for ssh execution", function () {
48
54
  expect(sshout).to.be.called;
49
55
  expect(sshout).to.be.calledWithMatch(/^hoge/);
50
56
  });
57
+ it("should execute 2 command by prependCmd ", async () => {
58
+ const rt = await sshExec(hostInfo, `echo ${testText}`, 0, sshout, null, "echo foo");
59
+ expect(rt).to.equal(0);
60
+ expect(sshout).to.be.calledOnce;
61
+ expect(sshout).to.be.calledWithMatch(/^foo\nhoge\n/);
62
+ });
63
+ it("should execute 2 command by hostInfo.prependCmd ", async () => {
64
+ hostInfo.prependCmd = "echo foo";
65
+ const rt = await sshExec(hostInfo, `echo ${testText}`, 0, sshout, null);
66
+ expect(rt).to.equal(0);
67
+ expect(sshout).to.be.calledOnce;
68
+ expect(sshout).to.be.calledWithMatch(/^foo\nhoge\n/);
69
+ delete hostInfo.prependCmd;
70
+ });
71
+ it("should execute 2 command by rcfile", async () => {
72
+ const rt = await sshExec(hostInfo, `echo ${testText}`, 0, sshout, rcfilePath);
73
+ expect(rt).to.equal(0);
74
+ expect(sshout).to.be.calledOnce;
75
+ expect(sshout).to.be.calledWithMatch(/^this is test rcfile\nhoge\n/);
76
+ });
77
+ it("should execute 2 command by hostInfo.prependCmd ", async () => {
78
+ hostInfo.rcfile = rcfilePath;
79
+ const rt = await sshExec(hostInfo, `echo ${testText}`, 0, sshout, null);
80
+ expect(rt).to.equal(0);
81
+ expect(sshout).to.be.calledOnce;
82
+ expect(sshout).to.be.calledWithMatch(/^this is test rcfile\nhoge\n/);
83
+ delete hostInfo.rcfile;
84
+ });
51
85
  // please note that exec() resolves with non-zero value
52
- // (126 permisssion deny or 127 file not found)
86
+ // (126 permission deny or 127 file not found)
53
87
  // but does not reject in following 2 cases
54
88
  it("should not execute command which do not have exec permission", async () => {
55
89
  await sshExec(hostInfo, "echo echo hoge >hoge");
@@ -0,0 +1 @@
1
+ echo this is test rcfile