2017-04-10 09:37:16 +00:00
|
|
|
|
package ssh
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
|
|
|
"fmt"
|
|
|
|
|
"net"
|
|
|
|
|
"time"
|
|
|
|
|
"errors"
|
|
|
|
|
)
|
|
|
|
|
|
2017-04-20 01:36:42 +00:00
|
|
|
|
type HostAuthType int8 // 认证方式
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
HostPassword = 1 // 密码认证
|
|
|
|
|
HostPublicKey = 2 // 公钥认证
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const SSHConnectTimeout = 10
|
|
|
|
|
|
|
|
|
|
|
2017-04-10 09:37:16 +00:00
|
|
|
|
type SSHConfig struct {
|
2017-04-20 01:36:42 +00:00
|
|
|
|
AuthType HostAuthType
|
2017-04-10 09:37:16 +00:00
|
|
|
|
User string
|
|
|
|
|
Password string
|
2017-04-20 01:36:42 +00:00
|
|
|
|
PrivateKey string
|
2017-04-10 09:37:16 +00:00
|
|
|
|
Host string
|
|
|
|
|
Port int
|
|
|
|
|
ExecTimeout int// 执行超时时间
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Result struct {
|
|
|
|
|
Output string
|
|
|
|
|
Err error
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-20 01:36:42 +00:00
|
|
|
|
func parseSSHConfig(sshConfig SSHConfig) (config *ssh.ClientConfig, err error) {
|
2017-05-02 07:26:59 +00:00
|
|
|
|
timeout := time.Duration(SSHConnectTimeout) * time.Second
|
2017-04-20 01:36:42 +00:00
|
|
|
|
// 密码认证
|
|
|
|
|
if sshConfig.AuthType == HostPassword {
|
|
|
|
|
config = &ssh.ClientConfig{
|
|
|
|
|
User: sshConfig.User,
|
|
|
|
|
Auth: []ssh.AuthMethod{
|
|
|
|
|
ssh.Password(sshConfig.Password),
|
|
|
|
|
},
|
|
|
|
|
Timeout: timeout,
|
|
|
|
|
HostKeyCallback:func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
|
|
|
|
return nil
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
signer, err := ssh.ParsePrivateKey([]byte(sshConfig.PrivateKey))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 公钥认证
|
|
|
|
|
config = &ssh.ClientConfig{
|
|
|
|
|
User: sshConfig.User,
|
|
|
|
|
Auth: []ssh.AuthMethod{
|
|
|
|
|
ssh.PublicKeys(signer),
|
|
|
|
|
},
|
|
|
|
|
Timeout: timeout,
|
|
|
|
|
HostKeyCallback:func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
|
|
|
|
return nil
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-04-10 09:37:16 +00:00
|
|
|
|
// 执行shell命令
|
|
|
|
|
func Exec(sshConfig SSHConfig, cmd string) (output string, err error) {
|
2017-04-16 18:01:41 +00:00
|
|
|
|
client, err := getClient(sshConfig)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
defer client.Close()
|
|
|
|
|
|
|
|
|
|
session, err := client.NewSession()
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
defer session.Close()
|
|
|
|
|
|
2017-04-21 05:50:46 +00:00
|
|
|
|
if sshConfig.ExecTimeout <= 0 {
|
|
|
|
|
outputByte, execErr := session.CombinedOutput(cmd)
|
|
|
|
|
output = string(outputByte)
|
|
|
|
|
err = execErr
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-10 09:37:16 +00:00
|
|
|
|
var resultChan chan Result = make(chan Result)
|
|
|
|
|
var timeoutChan chan bool = make(chan bool)
|
2017-04-16 18:01:41 +00:00
|
|
|
|
go func() {
|
|
|
|
|
output, err := session.CombinedOutput(cmd)
|
|
|
|
|
resultChan <- Result{string(output), err}
|
|
|
|
|
}()
|
2017-04-21 06:50:40 +00:00
|
|
|
|
// todo 等待超时后,如何停止远程正在执行的任务, 使用timeout命令,但不具有通用性
|
2017-04-10 09:37:16 +00:00
|
|
|
|
go triggerTimeout(timeoutChan, sshConfig.ExecTimeout)
|
|
|
|
|
select {
|
|
|
|
|
case result := <- resultChan:
|
|
|
|
|
output = result.Output
|
|
|
|
|
err = result.Err
|
|
|
|
|
case <- timeoutChan:
|
|
|
|
|
output = ""
|
|
|
|
|
err = errors.New("timeout")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getClient(sshConfig SSHConfig) (*ssh.Client, error) {
|
2017-04-20 01:36:42 +00:00
|
|
|
|
config, err := parseSSHConfig(sshConfig)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
2017-04-10 09:37:16 +00:00
|
|
|
|
}
|
|
|
|
|
addr := fmt.Sprintf("%s:%d", sshConfig.Host, sshConfig.Port)
|
|
|
|
|
|
|
|
|
|
return ssh.Dial("tcp", addr, config)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func triggerTimeout(ch chan bool, timeout int){
|
|
|
|
|
// 最长执行时间不能超过24小时
|
|
|
|
|
if timeout <= 0 || timeout > 86400 {
|
|
|
|
|
timeout = 86400
|
|
|
|
|
}
|
|
|
|
|
time.Sleep(time.Duration(timeout) * time.Second)
|
2017-04-12 06:12:34 +00:00
|
|
|
|
close(ch)
|
2017-04-20 01:36:42 +00:00
|
|
|
|
}
|
|
|
|
|
|