-
Notifications
You must be signed in to change notification settings - Fork 4
/
sshmanager.go
118 lines (102 loc) · 3.1 KB
/
sshmanager.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package main
import (
"fmt"
"os"
"time"
"golang.org/x/crypto/ssh"
)
// SSHClientConfig structure
// Values comes from the `patroni.json` file
type SSHClientConfig struct {
Host string
Port int
User string
PrivateKey string
}
type SSHManager struct {
Config SSHClientConfig
Client *ssh.Client
}
// NewSSHManager to spawn a new SSHManager with the given configuration.
func NewSSHManager(config SSHClientConfig) *SSHManager {
return &SSHManager{Config: config}
}
// EnsureConnected ensures that the SSH client is connected
// if not (m.Client == nil) -> reconnects
func (m *SSHManager) EnsureConnected() error {
if m.Client == nil {
client, err := m.connectSSH()
if err != nil {
return err
}
m.Client = client
}
return nil
}
// connectSSH creates a new SSH client and returns its pointer
func (m *SSHManager) connectSSH() (*ssh.Client, error) {
// privateKey could be read from a file, or retrieved from another storage
// source, such as the Secret Service / GNOME Keyring
file_privateKey, err := os.ReadFile(m.Config.PrivateKey)
if err != nil {
message := "\nCould not read SSH private key file defined in "
message += patroniconfigfilename.value + ":\n"
exit1(message, err)
}
key, err := ssh.ParsePrivateKey([]byte(file_privateKey))
if err != nil {
message := "\nCould not use SSH private key file defined in "
message += patroniconfigfilename.value
message += "\nYou may have entered the public key instead the private one ?\n"
exit1(message, err)
}
// Create the SSH client config
sshConfig := &ssh.ClientConfig{
User: m.Config.User,
// https://github.com/golang/go/issues/19767
// as clientConfig is non-permissive by default
// you can set ssh.InsercureIgnoreHostKey to allow any host
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Auth: []ssh.AuthMethod{
ssh.PublicKeys(key),
},
Timeout: 5 * time.Second,
}
//JPAREM : maybe do a 1.0.2 to support both private key _and_ password
//methods?
//alternative with password
/*
Auth: []ssh.AuthMethod{
ssh.Password("PASSWORD"),
*/
// Connect to the SSH server
client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", m.Config.Host, m.Config.Port), sshConfig)
if err != nil {
message := "\nFailed to connect to SSH server: \n"
return nil, fmt.Errorf(message+"%v", err)
}
return client, nil
}
// RunCommand to run the specified command in parameter
// to the current SSH client opened remotely
func (m *SSHManager) RunCommand(command string) (string, error) {
// Ensure the SSH client is connected
if err := m.EnsureConnected(); err != nil {
return "", err
}
// Create a session
session, err := m.Client.NewSession()
if err != nil {
//return "", fmt.Errorf("Failed to create SSH session: %v", err)
message := "Failed to create SSH session: \n"
return "", fmt.Errorf(message+"%v", err)
}
defer session.Close()
// Run the command
output, err := session.CombinedOutput(command)
if err != nil {
message := "Failed to run command: \n"
return "", fmt.Errorf(message+"%v", err)
}
return string(output), nil
}