github.com/sealerio/sealer@v0.11.1-0.20240507115618-f4f89c5853ae/utils/ssh/sshcmd.go (about) 1 // Copyright © 2021 Alibaba Group Holding Ltd. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package ssh 16 17 import ( 18 "bytes" 19 "fmt" 20 "net" 21 "os/exec" 22 "strings" 23 24 "github.com/sealerio/sealer/common" 25 "github.com/sealerio/sealer/pkg/env" 26 utilsnet "github.com/sealerio/sealer/utils/net" 27 ) 28 29 const SUDO = "sudo " 30 31 func (s *SSH) Ping(host net.IP) error { 32 if utilsnet.IsLocalIP(host, s.LocalAddress) { 33 return nil 34 } 35 client, _, err := s.Connect(host) 36 if err != nil { 37 return fmt.Errorf("failed to ping node %s using ssh session: %v", host, err) 38 } 39 err = client.Close() 40 if err != nil { 41 return err 42 } 43 return nil 44 } 45 46 func (s *SSH) CmdAsync(host net.IP, hostEnv map[string]string, cmds ...string) error { 47 // force specify PATH env 48 if hostEnv == nil { 49 hostEnv = map[string]string{} 50 } 51 if hostEnv["PATH"] == "" { 52 hostEnv["PATH"] = "/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin" 53 } 54 55 var execFunc func(cmd string) error 56 57 if utilsnet.IsLocalIP(host, s.LocalAddress) { 58 execFunc = func(cmd string) error { 59 c := exec.Command("/bin/bash", "-c", cmd) 60 stdout, err := c.StdoutPipe() 61 if err != nil { 62 return err 63 } 64 65 stderr, err := c.StderrPipe() 66 if err != nil { 67 return err 68 } 69 70 if err := c.Start(); err != nil { 71 return fmt.Errorf("failed to start command %s: %v", cmd, err) 72 } 73 74 ReadPipe(stdout, stderr, s.AlsoToStdout) 75 76 err = c.Wait() 77 if err != nil { 78 return fmt.Errorf("failed to execute command(%s) on host(%s): error(%v)", cmd, host, err) 79 } 80 return nil 81 } 82 } else { 83 execFunc = func(cmd string) error { 84 client, session, err := s.Connect(host) 85 if err != nil { 86 return fmt.Errorf("failed to create ssh session for %s: %v", host, err) 87 } 88 defer client.Close() 89 defer session.Close() 90 stdout, err := session.StdoutPipe() 91 if err != nil { 92 return fmt.Errorf("failed to create stdout pipe for %s: %v", host, err) 93 } 94 stderr, err := session.StderrPipe() 95 if err != nil { 96 return fmt.Errorf("failed to create stderr pipe for %s: %v", host, err) 97 } 98 99 if err := session.Start(cmd); err != nil { 100 return fmt.Errorf("failed to start command %s on %s: %v", cmd, host, err) 101 } 102 103 ReadPipe(stdout, stderr, s.AlsoToStdout) 104 105 err = session.Wait() 106 if err != nil { 107 return fmt.Errorf("failed to execute command(%s) on host(%s): error(%v)", cmd, host, err) 108 } 109 110 return nil 111 } 112 } 113 114 for _, cmd := range cmds { 115 if cmd == "" { 116 continue 117 } 118 if s.User != common.ROOT { 119 cmd = fmt.Sprintf("sudo -E /bin/bash <<EOF\n%s\nEOF", cmd) 120 } 121 cmd = env.WrapperShell(cmd, hostEnv) 122 123 if err := execFunc(cmd); err != nil { 124 return err 125 } 126 } 127 128 return nil 129 } 130 131 func (s *SSH) Cmd(host net.IP, hostEnv map[string]string, cmd string) ([]byte, error) { 132 // force specify PATH env 133 if hostEnv == nil { 134 hostEnv = map[string]string{} 135 } 136 if hostEnv["PATH"] == "" { 137 hostEnv["PATH"] = "/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin" 138 } 139 140 if s.User != common.ROOT { 141 cmd = fmt.Sprintf("sudo -E /bin/bash <<EOF\n%s\nEOF", cmd) 142 } 143 cmd = env.WrapperShell(cmd, hostEnv) 144 145 var stdoutContent, stderrContent bytes.Buffer 146 147 if utilsnet.IsLocalIP(host, s.LocalAddress) { 148 localCmd := exec.Command("/bin/bash", "-c", cmd) 149 localCmd.Stdout = &stdoutContent 150 localCmd.Stderr = &stderrContent 151 if err := localCmd.Run(); err != nil { 152 return stdoutContent.Bytes(), fmt.Errorf("failed to execute command(%s) on host(%s): error(%v)", cmd, host, stderrContent.String()) 153 } 154 return stdoutContent.Bytes(), nil 155 } 156 157 client, session, err := s.Connect(host) 158 if err != nil { 159 return nil, fmt.Errorf("[ssh][%s] failed to create ssh session: %s", host, err) 160 } 161 defer client.Close() 162 defer session.Close() 163 164 session.Stdout = &stdoutContent 165 session.Stderr = &stderrContent 166 if err := session.Run(cmd); err != nil { 167 return stdoutContent.Bytes(), fmt.Errorf("[ssh][%s]failed to run command[%s]: %s", host, cmd, stderrContent.String()) 168 } 169 170 return stdoutContent.Bytes(), nil 171 } 172 173 // CmdToString is in host exec cmd and replace to spilt str 174 func (s *SSH) CmdToString(host net.IP, env map[string]string, cmd, split string) (string, error) { 175 data, err := s.Cmd(host, env, cmd) 176 str := string(data) 177 if err != nil { 178 return str, err 179 } 180 if data != nil { 181 str = strings.ReplaceAll(str, "\r", split) 182 str = strings.ReplaceAll(str, "\r\n", split) 183 str = strings.ReplaceAll(str, "\n", split) 184 return str, nil 185 } 186 return str, fmt.Errorf("command %s %s return nil", host, cmd) 187 }