diff --git a/pkg/cluster/executor/ssh.go b/pkg/cluster/executor/ssh.go index d8873dad4e..ec224c8961 100644 --- a/pkg/cluster/executor/ssh.go +++ b/pkg/cluster/executor/ssh.go @@ -301,7 +301,17 @@ func (e *NativeSSHExecutor) Execute(cmd string, sudo bool, timeout ...time.Durat defer cancel() } - args := []string{"ssh", "-o", "StrictHostKeyChecking=no"} + ssh := "ssh" + + if val := os.Getenv(localdata.EnvNameSSHPath); val != "" { + if isExec := utils.IsExecBinary(val); !isExec { + return nil, nil, fmt.Errorf("specified SSH in the environment variable `%s` does not exist or is not executable", localdata.EnvNameSSHPath) + } + ssh = val + } + + args := []string{ssh, "-o", "StrictHostKeyChecking=no"} + args = e.configArgs(args) // prefix and postfix args args = append(args, fmt.Sprintf("%s@%s", e.Config.User, e.Config.Host), cmd) @@ -348,7 +358,16 @@ func (e *NativeSSHExecutor) Transfer(src string, dst string, download bool) erro return e.ConnectionTestResult } - args := []string{"scp", "-r", "-o", "StrictHostKeyChecking=no"} + scp := "scp" + + if val := os.Getenv(localdata.EnvNameSCPPath); val != "" { + if isExec := utils.IsExecBinary(val); !isExec { + return fmt.Errorf("specified SCP in the environment variable `%s` does not exist or is not executable", localdata.EnvNameSCPPath) + } + scp = val + } + + args := []string{scp, "-r", "-o", "StrictHostKeyChecking=no"} args = e.configArgs(args) // prefix and postfix args if download { diff --git a/pkg/localdata/constant.go b/pkg/localdata/constant.go index 2e7456d820..f875c2c8e3 100644 --- a/pkg/localdata/constant.go +++ b/pkg/localdata/constant.go @@ -67,9 +67,15 @@ const ( // EnvNameSSHPassPrompt is the variable name by which user specific the password prompt for sshpass EnvNameSSHPassPrompt = "TIUP_SSHPASS_PROMPT" - // EnvNameNativeSSHClient is the variable name by which user can specific use natiive ssh client or not + // EnvNameNativeSSHClient is the variable name by which user can specific use native ssh client or not EnvNameNativeSSHClient = "TIUP_NATIVE_SSH" + // EnvNameSSHPath is the variable name by which user can specific the executable ssh binary path + EnvNameSSHPath = "TIUP_SSH_PATH" + + // EnvNameSCPPath is the variable name by which user can specific the executable scp binary path + EnvNameSCPPath = "TIUP_SCP_PATH" + // MetaFilename represents the process meta file name MetaFilename = "tiup_process_meta" ) diff --git a/pkg/utils/ioutil.go b/pkg/utils/ioutil.go index 84342a0acb..3b8d2a3373 100644 --- a/pkg/utils/ioutil.go +++ b/pkg/utils/ioutil.go @@ -62,6 +62,15 @@ func IsEmptyDir(path string) (bool, error) { return false, err } +// IsExecBinary check whether a path is a valid executable +func IsExecBinary(path string) bool { + info, err := os.Stat(path) + if err != nil { + return false + } + return !info.IsDir() && info.Mode()&0111 == 0111 +} + // Untar decompresses the tarball func Untar(reader io.Reader, to string) error { gr, err := gzip.NewReader(reader) diff --git a/pkg/utils/ioutil_test.go b/pkg/utils/ioutil_test.go index 6d32097f60..4cba2b121d 100644 --- a/pkg/utils/ioutil_test.go +++ b/pkg/utils/ioutil_test.go @@ -23,10 +23,14 @@ func (s *TestIOUtilSuite) TestIOUtil(c *C) {} func (s *TestIOUtilSuite) SetUpSuite(c *C) { os.RemoveAll(path.Join(currentDir(), "testdata", "parent")) + os.RemoveAll(path.Join(currentDir(), "testdata", "ssh-exec")) + os.RemoveAll(path.Join(currentDir(), "testdata", "nop-nop")) } func (s *TestIOUtilSuite) TearDownSuite(c *C) { os.RemoveAll(path.Join(currentDir(), "testdata", "parent")) + os.RemoveAll(path.Join(currentDir(), "testdata", "ssh-exec")) + os.RemoveAll(path.Join(currentDir(), "testdata", "nop-nop")) } func (s *TestIOUtilSuite) TestIsExist(c *C) { @@ -39,6 +43,22 @@ func (s *TestIOUtilSuite) TestIsNotExist(c *C) { c.Assert(IsNotExist("/tmp/"+uuid.New().String()), IsTrue) } +func (s *TestIOUtilSuite) TestIsExecBinary(c *C) { + c.Assert(IsExecBinary("/tmp"), IsFalse) + + e := path.Join(currentDir(), "testdata", "ssh-exec") + f, err := os.OpenFile(e, os.O_CREATE, 0777) + c.Assert(err, IsNil) + defer f.Close() + c.Assert(IsExecBinary(e), IsTrue) + + e = path.Join(currentDir(), "testdata", "nop-nop") + f, err = os.OpenFile(e, os.O_CREATE, 0666) + c.Assert(err, IsNil) + defer f.Close() + c.Assert(IsExecBinary(e), IsFalse) +} + func (s *TestIOUtilSuite) TestUntar(c *C) { c.Assert(IsNotExist(path.Join(currentDir(), "testdata", "parent")), IsTrue) f, err := os.Open(path.Join(currentDir(), "testdata", "test.tar.gz"))