Files
ppgo_job/service/scheduler/ssh.go
T
2026-07-10 11:28:16 +08:00

88 lines
1.9 KiB
Go

package scheduler
import (
"bytes"
"fmt"
"net"
"os"
"time"
"golang.org/x/crypto/ssh"
)
// SSHClient SSH 连接客户端
type SSHClient struct {
client *ssh.Client
}
// Connect 建立 SSH 连接
func (s *SSHClient) Connect(host string, port int, user, password, privateKeySrc string, authType int) error {
addr := net.JoinHostPort(host, fmt.Sprintf("%d", port))
config := &ssh.ClientConfig{
User: user,
Timeout: 10 * time.Second,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
if authType == 0 {
// 密码认证
config.Auth = []ssh.AuthMethod{ssh.Password(password)}
} else {
// 密钥认证
keyData, err := os.ReadFile(privateKeySrc)
if err != nil {
return fmt.Errorf("读取密钥文件失败: %w", err)
}
signer, err := ssh.ParsePrivateKey(keyData)
if err != nil {
return fmt.Errorf("解析密钥失败: %w", err)
}
config.Auth = []ssh.AuthMethod{ssh.PublicKeys(signer)}
}
client, err := ssh.Dial("tcp", addr, config)
if err != nil {
return fmt.Errorf("SSH连接失败: %w", err)
}
s.client = client
return nil
}
// Exec 执行远程命令
func (s *SSHClient) Exec(command string, timeoutSec int) (stdout, stderr string, err error) {
session, err := s.client.NewSession()
if err != nil {
return "", "", fmt.Errorf("创建SSH会话失败: %w", err)
}
defer session.Close()
var outBuf, errBuf bytes.Buffer
session.Stdout = &outBuf
session.Stderr = &errBuf
done := make(chan error, 1)
go func() {
done <- session.Run(command)
}()
timeout := time.Duration(timeoutSec) * time.Second
if timeoutSec <= 0 {
timeout = 24 * time.Hour
}
select {
case err := <-done:
return outBuf.String(), errBuf.String(), err
case <-time.After(timeout):
session.Signal(ssh.SIGKILL)
return "", fmt.Sprintf("任务执行超过%d秒,已强制终止", timeoutSec), fmt.Errorf("timeout")
}
}
// Close 关闭连接
func (s *SSHClient) Close() {
if s.client != nil {
s.client.Close()
}
}