package main import ( "fmt" "os" "time" "golang.org/x/crypto/ssh" ) type SSHClient struct { Config HostConfig client *ssh.Client } func NewSSHClient(config HostConfig) (*SSHClient, error) { key, err := os.ReadFile(config.KeyPath) if err != nil { return nil, fmt.Errorf("unable to read private key: %v", err) } signer, err := ssh.ParsePrivateKey(key) if err != nil { return nil, fmt.Errorf("unable to parse private key: %v", err) } sshConfig := &ssh.ClientConfig{ User: config.User, Auth: []ssh.AuthMethod{ ssh.PublicKeys(signer), }, HostKeyCallback: ssh.InsecureIgnoreHostKey(), Timeout: 10 * time.Second, } client, err := ssh.Dial("tcp", fmt.Sprintf("%s:22", config.IP), sshConfig) if err != nil { return nil, fmt.Errorf("failed to dial: %v", err) } return &SSHClient{ Config: config, client: client, }, nil } func (s *SSHClient) Close() error { if s.client != nil { return s.client.Close() } return nil } func (s *SSHClient) RunCommand(cmd string) (string, error) { session, err := s.client.NewSession() if err != nil { return "", fmt.Errorf("failed to create session: %v", err) } defer session.Close() output, err := session.CombinedOutput(cmd) if err != nil { return string(output), fmt.Errorf("failed to run command '%s': %v (output: %s)", cmd, err, string(output)) } return string(output), nil } func (s *SSHClient) RunCommandBackground(cmd string) error { session, err := s.client.NewSession() if err != nil { return fmt.Errorf("failed to create session: %v", err) } err = session.Start(cmd) if err != nil { session.Close() return fmt.Errorf("failed to start command: %v", err) } go func() { session.Wait() session.Close() }() return nil }