2021-10-16 15:39:54 +02:00
package main
import (
2023-08-21 14:37:47 +02:00
"io"
2023-06-29 22:58:24 +02:00
"os"
"time"
2021-10-16 15:39:54 +02:00
2023-06-29 22:58:24 +02:00
"github.com/silenceper/pool"
log "github.com/sirupsen/logrus"
2021-10-16 15:39:54 +02:00
"golang.org/x/crypto/ssh"
)
2023-06-29 22:58:24 +02:00
const SshDialTimeout = time . Duration ( 10 * time . Second )
const SshInactivityTimeout = time . Duration ( time . Minute )
type Ssh struct {
2023-08-21 14:37:47 +02:00
name string
signer ssh . Signer
config * ssh . ClientConfig
client * ssh . Client
session * ssh . Session
in io . WriteCloser
out io . Reader
err io . Reader
2021-10-16 15:39:54 +02:00
}
2023-06-29 22:58:24 +02:00
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
2021-10-16 15:39:54 +02:00
}
2023-06-29 22:58:24 +02:00
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" )
2023-08-21 14:37:47 +02:00
if err := s . ExecPipe ( cmd ) ; err != nil {
log . WithFields ( log . Fields { "name" : s . name , "cmd" : cmd , "call" : "ssh.ExecPipe" , "error" : err } ) . Errorf ( "" )
2023-06-29 22:58:24 +02:00
return "" , err
2021-10-16 15:39:54 +02:00
}
2023-08-21 14:37:47 +02:00
defer s . session . Close ( )
2021-10-16 15:39:54 +02:00
2023-08-21 14:37:47 +02:00
if err := s . session . Wait ( ) ; err != nil {
2023-08-20 16:57:53 +02:00
log . WithFields ( log . Fields { "name" : s . name , "cmd" : cmd , "call" : "session.Setenv" , "error" : err } ) . Errorf ( "" )
return "" , err
}
2023-08-21 14:37:47 +02:00
buf , err := io . ReadAll ( s . out )
2021-10-16 15:39:54 +02:00
if err != nil {
2023-08-21 14:37:47 +02:00
log . WithFields ( log . Fields { "name" : s . name , "cmd" : cmd , "call" : "io.ReadAll" , "error" : err } ) . Errorf ( "" )
2023-06-29 22:58:24 +02:00
return "" , err
2021-10-16 15:39:54 +02:00
}
2023-08-21 14:37:47 +02:00
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 s . session . Setenv ( "TZ" , cfg . Timezone ) ; err != nil {
log . WithFields ( log . Fields { "name" : s . name , "cmd" : cmd , "call" : "session.Setenv" , "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" , "error" : err } ) . Errorf ( "" )
s . session . Close ( )
return err
}
return nil
2021-10-16 15:39:54 +02:00
}