171 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			171 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package main
 | 
						|
 | 
						|
import (
 | 
						|
	"io"
 | 
						|
	"os"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/silenceper/pool"
 | 
						|
	log "github.com/sirupsen/logrus"
 | 
						|
	"golang.org/x/crypto/ssh"
 | 
						|
)
 | 
						|
 | 
						|
const SshDialTimeout = time.Duration(10 * time.Second)
 | 
						|
const SshInactivityTimeout = time.Duration(time.Minute)
 | 
						|
 | 
						|
type Ssh struct {
 | 
						|
	name    string
 | 
						|
	signer  ssh.Signer
 | 
						|
	config  *ssh.ClientConfig
 | 
						|
	client  *ssh.Client
 | 
						|
	session *ssh.Session
 | 
						|
	in      io.WriteCloser
 | 
						|
	out     io.Reader
 | 
						|
	err     io.Reader
 | 
						|
}
 | 
						|
 | 
						|
func NewSsh(name, addr, user, key string) (*Ssh, error) {
 | 
						|
	log.WithFields(log.Fields{"name": name, "addr": addr, "user": user, "key": key}).Debugf("starting")
 | 
						|
	defer log.WithFields(log.Fields{"name": name, "addr": addr, "user": user, "key": key}).Debugf("done")
 | 
						|
 | 
						|
	s := &Ssh{
 | 
						|
		name: name,
 | 
						|
	}
 | 
						|
 | 
						|
	k, err := os.ReadFile(key)
 | 
						|
	if err != nil {
 | 
						|
		log.WithFields(log.Fields{"name": name, "addr": addr, "user": user, "key": key, "call": "os.ReadFile", "error": err}).Errorf("")
 | 
						|
		return s, err
 | 
						|
	}
 | 
						|
 | 
						|
	parsedKey, err := ssh.ParseRawPrivateKey(k)
 | 
						|
	if err != nil {
 | 
						|
		log.WithFields(log.Fields{"name": name, "addr": addr, "user": user, "key": key, "call": "ssh.ParseRawPrivateKey", "error": err}).Errorf("")
 | 
						|
		return s, err
 | 
						|
	}
 | 
						|
 | 
						|
	s.signer, err = ssh.NewSignerFromKey(parsedKey)
 | 
						|
	if err != nil {
 | 
						|
		log.WithFields(log.Fields{"name": name, "addr": addr, "user": user, "key": key, "call": "ssh.NewSignerFromKey", "error": err}).Errorf("")
 | 
						|
		return s, err
 | 
						|
	}
 | 
						|
 | 
						|
	s.config = &ssh.ClientConfig{
 | 
						|
		User: user,
 | 
						|
		Auth: []ssh.AuthMethod{
 | 
						|
			ssh.PublicKeys(s.signer),
 | 
						|
		},
 | 
						|
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
 | 
						|
		Timeout:         SshDialTimeout,
 | 
						|
	}
 | 
						|
 | 
						|
	s.client, err = ssh.Dial("tcp", addr, s.config)
 | 
						|
	if err != nil {
 | 
						|
		log.WithFields(log.Fields{"name": name, "addr": addr, "user": user, "key": key, "call": "ssh.Dial", "error": err}).Errorf("")
 | 
						|
		return s, err
 | 
						|
	}
 | 
						|
 | 
						|
	return s, nil
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
func (s *Ssh) Close() error {
 | 
						|
	log.WithFields(log.Fields{"name": s.name}).Debugf("starting")
 | 
						|
	defer log.WithFields(log.Fields{"name": s.name}).Debugf("done")
 | 
						|
 | 
						|
	return s.client.Close()
 | 
						|
}
 | 
						|
 | 
						|
func NewSshPool(name, addr, user, key string) (pool.Pool, error) {
 | 
						|
	log.WithFields(log.Fields{"name": name, "addr": addr, "user": user, "key": key}).Debugf("starting")
 | 
						|
	defer log.WithFields(log.Fields{"name": name, "addr": addr, "user": user, "key": key}).Debugf("done")
 | 
						|
 | 
						|
	//factory Specify the method to create the connection
 | 
						|
	factory := func() (interface{}, error) { return NewSsh(name, addr, user, key) }
 | 
						|
 | 
						|
	// close Specify the method to close the connection
 | 
						|
	close := func(v interface{}) error { return v.(*Ssh).Close() }
 | 
						|
 | 
						|
	// Create a connection pool: Initialize the number of connections to 0, the maximum idle connection is 2, and the maximum concurrent connection is 25
 | 
						|
	poolConfig := &pool.Config{
 | 
						|
		InitialCap: 0,
 | 
						|
		MaxIdle:    2,
 | 
						|
		MaxCap:     25,
 | 
						|
		Factory:    factory,
 | 
						|
		Close:      close,
 | 
						|
		//Ping:       ping,
 | 
						|
		//The maximum idle time of the connection, the connection exceeding this time will be closed, which can avoid the problem of automatic failure when connecting to EOF when idle
 | 
						|
		IdleTimeout: SshInactivityTimeout,
 | 
						|
	}
 | 
						|
 | 
						|
	return pool.NewChannelPool(poolConfig)
 | 
						|
}
 | 
						|
 | 
						|
func (s *Ssh) Exec(cmd string) (string, error) {
 | 
						|
	log.WithFields(log.Fields{"name": s.name, "cmd": cmd}).Debugf("starting")
 | 
						|
	defer log.WithFields(log.Fields{"name": s.name, "cmd": cmd}).Debugf("done")
 | 
						|
 | 
						|
	if err := s.ExecPipe(cmd); err != nil {
 | 
						|
		log.WithFields(log.Fields{"name": s.name, "cmd": cmd, "call": "ssh.ExecPipe", "error": err}).Errorf("")
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
	defer s.session.Close()
 | 
						|
 | 
						|
	if err := s.session.Wait(); err != nil {
 | 
						|
		log.WithFields(log.Fields{"name": s.name, "cmd": cmd, "call": "session.Setenv", "error": err}).Errorf("")
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
 | 
						|
	buf, err := io.ReadAll(s.out)
 | 
						|
	if err != nil {
 | 
						|
		log.WithFields(log.Fields{"name": s.name, "cmd": cmd, "call": "io.ReadAll", "error": err}).Errorf("")
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
 | 
						|
	return string(buf), nil
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
func (s *Ssh) ExecPipe(cmd string) error {
 | 
						|
	log.WithFields(log.Fields{"name": s.name, "cmd": cmd}).Debugf("starting")
 | 
						|
	defer log.WithFields(log.Fields{"name": s.name, "cmd": cmd}).Debugf("done")
 | 
						|
 | 
						|
	session, err := s.client.NewSession()
 | 
						|
	if err != nil {
 | 
						|
		log.WithFields(log.Fields{"name": s.name, "cmd": cmd, "call": "client.NewSession", "error": err}).Errorf("")
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	s.session = session
 | 
						|
 | 
						|
	if err = s.session.Setenv("TZ", cfg.Timezone); err != nil {
 | 
						|
		log.WithFields(log.Fields{"name": s.name, "cmd": cmd, "call": "session.Setenv", "attr": cfg.Timezone, "error": err}).Errorf("")
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	if s.in, err = s.session.StdinPipe(); err != nil {
 | 
						|
		log.WithFields(log.Fields{"name": s.name, "cmd": cmd, "call": "session.StdinPipe", "error": err}).Errorf("")
 | 
						|
		s.session.Close()
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	if s.out, err = s.session.StdoutPipe(); err != nil {
 | 
						|
		log.WithFields(log.Fields{"name": s.name, "cmd": cmd, "call": "session.StdoutPipe", "error": err}).Errorf("")
 | 
						|
		s.session.Close()
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	if s.err, err = s.session.StderrPipe(); err != nil {
 | 
						|
		log.WithFields(log.Fields{"name": s.name, "cmd": cmd, "call": "session.StderrPipe", "error": err}).Errorf("")
 | 
						|
		s.session.Close()
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	if err = s.session.Start(cmd); err != nil {
 | 
						|
		log.WithFields(log.Fields{"name": s.name, "cmd": cmd, "call": "session.Start", "attr": cmd, "error": err}).Errorf("")
 | 
						|
		s.session.Close()
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 |