diff --git a/server/app/core/sshManager.js b/server/app/core/sshManager.js index 79a956952..e848956b1 100644 --- a/server/app/core/sshManager.js +++ b/server/app/core/sshManager.js @@ -247,5 +247,6 @@ module.exports = { getSshHostinfo, removeSsh, askPassword, - createSsh + createSsh, + isVerboseSshEnabled }; diff --git a/server/app/db/version.json b/server/app/db/version.json index a9ad41afd..e44604107 100644 --- a/server/app/db/version.json +++ b/server/app/db/version.json @@ -1 +1 @@ -{"version": "2026-0605-211714" } \ No newline at end of file +{"version": "2026-0605-221627" } \ No newline at end of file diff --git a/server/app/handlers/tryToConnect.js b/server/app/handlers/tryToConnect.js index bc62636f5..229c24255 100644 --- a/server/app/handlers/tryToConnect.js +++ b/server/app/handlers/tryToConnect.js @@ -8,18 +8,19 @@ const SshClientWrapper = require("ssh-client-wrapper"); const { getLogger } = require("../logSettings"); const logger = getLogger(); const { remoteHost } = require("../db/db"); -const { askPassword } = require("../core/sshManager.js"); +const { askPassword, isVerboseSshEnabled } = require("../core/sshManager.js"); /** * try to connect remote host via ssh * @param {string} clientID - socketIO client's ID string * @param {object} hostInfo - target host's information * @param {Function} cb - call back function called with string "success" or "error" + * @returns {Promise} */ async function onTryToConnect(clientID, hostInfo, cb) { hostInfo.password = askPassword.bind(null, clientID, hostInfo.name, "password", null); hostInfo.passphrase = askPassword.bind(null, clientID, hostInfo.name, "passphrase", null); - if (process.env.WHEEL_VERBOSE_SSH) { + if (isVerboseSshEnabled()) { hostInfo.sshOpt = ["-vvv"]; } const ssh = new SshClientWrapper(hostInfo); @@ -38,6 +39,14 @@ async function onTryToConnect(clientID, hostInfo, cb) { ssh.disconnect(); return cb("success"); } + +/** + * try to connect remote host via ssh with host id + * @param {string} clientID - socketIO client's ID string + * @param {string} id - remote host id + * @param {Function} cb - call back function called with string "success" or "error" + * @returns {Promise} + */ async function onTryToConnectById(clientID, id, cb) { const hostInfo = remoteHost.get(id); await onTryToConnect(clientID, hostInfo, cb); diff --git a/server/test/app/handlers/tryToConnect.js b/server/test/app/handlers/tryToConnect.js new file mode 100644 index 000000000..7702b9bef --- /dev/null +++ b/server/test/app/handlers/tryToConnect.js @@ -0,0 +1,108 @@ +"use strict"; +const os = require("os"); +const path = require("path"); +const fs = require("fs-extra"); + +const chai = require("chai"); +const expect = chai.expect; +const sinon = require("sinon"); +chai.use(require("sinon-chai")); +const rewire = require("rewire"); + +describe("tryToConnect UT", ()=>{ + const originalHome = process.env.HOME; + const originalWheelUseHttp = process.env.WHEEL_USE_HTTP; + const originalWheelVerboseSsh = process.env.WHEEL_VERBOSE_SSH; + let tempHome; + let tryToConnect; + let onTryToConnect; + let SshClientWrapperMock; + let canConnectMock; + let disconnectMock; + let askPasswordMock; + let cb; + + before(async ()=>{ + tempHome = await fs.mkdtemp(path.join(os.tmpdir(), "wheel-try-connect-")); + process.env.HOME = tempHome; + process.env.WHEEL_USE_HTTP = "1"; + await fs.ensureDir(path.join(tempHome, ".wheel")); + await Promise.all([ + fs.writeJson(path.join(tempHome, ".wheel", "remotehost.json"), []), + fs.writeJson(path.join(tempHome, ".wheel", "projectList.json"), []), + fs.writeJson(path.join(tempHome, ".wheel", "jobScriptTemplate.json"), []) + ]); + }); + + beforeEach(()=>{ + cb = sinon.stub(); + canConnectMock = sinon.stub().resolves(); + disconnectMock = sinon.stub(); + askPasswordMock = sinon.stub(); + + tryToConnect = rewire("../../../app/handlers/tryToConnect.js"); + SshClientWrapperMock = sinon.stub().callsFake((hostInfo)=>{ + return { + hostInfo, + canConnect: canConnectMock, + disconnect: disconnectMock + }; + }); + + tryToConnect.__set__({ + SshClientWrapper: SshClientWrapperMock, + askPassword: askPasswordMock, + logger: { + debug: sinon.stub(), + info: sinon.stub(), + error: sinon.stub() + } + }); + onTryToConnect = tryToConnect.__get__("onTryToConnect"); + }); + + afterEach(()=>{ + sinon.restore(); + if (originalWheelVerboseSsh !== undefined) { + process.env.WHEEL_VERBOSE_SSH = originalWheelVerboseSsh; + } else { + delete process.env.WHEEL_VERBOSE_SSH; + } + }); + + after(async ()=>{ + if (originalHome !== undefined) { + process.env.HOME = originalHome; + } else { + delete process.env.HOME; + } + if (originalWheelUseHttp !== undefined) { + process.env.WHEEL_USE_HTTP = originalWheelUseHttp; + } else { + delete process.env.WHEEL_USE_HTTP; + } + await fs.remove(tempHome); + }); + + it("should set sshOpt when WHEEL_VERBOSE_SSH is truthy", async ()=>{ + process.env.WHEEL_VERBOSE_SSH = "true"; + const hostInfo = { name: "testHost", host: "localhost", port: 22, user: "test" }; + + await onTryToConnect("client-1", hostInfo, cb); + + expect(hostInfo.sshOpt).to.deep.equal(["-vvv"]); + expect(SshClientWrapperMock).to.have.been.calledOnceWithExactly(hostInfo); + expect(cb).to.have.been.calledOnceWithExactly("success"); + }); + + it("should not set sshOpt when WHEEL_VERBOSE_SSH is false-like", async ()=>{ + process.env.WHEEL_VERBOSE_SSH = "false"; + const hostInfo = { name: "testHost", host: "localhost", port: 22, user: "test" }; + + await onTryToConnect("client-1", hostInfo, cb); + + expect(hostInfo).to.not.have.property("sshOpt"); + expect(SshClientWrapperMock).to.have.been.calledOnceWithExactly(hostInfo); + expect(cb).to.have.been.calledOnceWithExactly("success"); + }); +});