Skip to content
This repository was archived by the owner on Dec 9, 2023. It is now read-only.

Commit 7a3daa2

Browse files
committed
Make SSH connection work + rename new widget + "isolate" ssh client
1 parent e926236 commit 7a3daa2

File tree

8 files changed

+327
-158
lines changed

8 files changed

+327
-158
lines changed

internal/monitor_server_widget.go

-35
This file was deleted.

internal/platform/remote_host.go

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package platform
2+
3+
import (
4+
"bufio"
5+
"bytes"
6+
"strconv"
7+
"strings"
8+
"time"
9+
10+
"github.com/pkg/errors"
11+
"golang.org/x/crypto/ssh"
12+
)
13+
14+
const (
15+
sshAgentEnv = "SSH_AUTH_SOCK"
16+
)
17+
18+
type RemoteHost struct {
19+
sshClient *ssh.Client
20+
}
21+
22+
// TODO accept more method of connection than only via ssh-agent
23+
func NewRemoteHost(username, addr string) (*RemoteHost, error) {
24+
sshClient, err := sshAgentAuth(username, addr)
25+
if err != nil {
26+
return nil, err
27+
}
28+
return &RemoteHost{
29+
sshClient: sshClient,
30+
}, nil
31+
}
32+
33+
// Run a command on remote server via SSH
34+
func (s *RemoteHost) run(command string) (string, error) {
35+
session, err := s.sshClient.NewSession()
36+
if err != nil {
37+
return "", errors.Wrapf(err, "can't create session with SSH client for command %s", command)
38+
}
39+
defer session.Close()
40+
41+
var buf bytes.Buffer
42+
session.Stdout = &buf
43+
err = session.Run(command)
44+
if err != nil {
45+
return "", errors.Wrapf(err, "can't run command %s on remote server", command)
46+
}
47+
48+
return string(buf.Bytes()), nil
49+
}
50+
51+
func (s *RemoteHost) Uptime() (int64, error) {
52+
command := "/bin/cat /proc/uptime"
53+
uptime, err := s.run(command)
54+
if err != nil {
55+
return 0, err
56+
}
57+
58+
d := strings.Fields(uptime)
59+
if len(d) == 0 {
60+
return 0, errors.Errorf("command %s return nothing", command)
61+
}
62+
63+
var secs float64
64+
secs, err = strconv.ParseFloat(d[0], 64)
65+
if err != nil {
66+
return 0, err
67+
}
68+
69+
return int64(time.Duration(secs * 1e9)), nil
70+
}
71+
72+
// TODO Doesn't really make sense to have memory info with headers on top
73+
// it's more headers on the left and one value on the right
74+
// Simple textbox for each of them would be enough?
75+
// Also, stack bar for total memory / memory free | total swap / swap free could be nice
76+
func (s *RemoteHost) getMemoryInfo(headers []string, metrics []string) (cells [][]string, err error) {
77+
lines, err := s.run("/bin/cat /proc/meminfo")
78+
if err != nil {
79+
return nil, err
80+
}
81+
82+
scanner := bufio.NewScanner(strings.NewReader(lines))
83+
var data string
84+
for scanner.Scan() {
85+
line := scanner.Text()
86+
parts := strings.Fields(line)
87+
88+
if len(parts) == 3 {
89+
val, err := strconv.ParseUint(parts[1], 10, 64)
90+
val = incSizeMetric(val)
91+
if err != nil {
92+
data += "unknown" + ","
93+
continue
94+
}
95+
96+
for _, v := range metrics {
97+
if parts[0] == v {
98+
data += strconv.FormatInt(int64(val), 10) + ","
99+
}
100+
}
101+
}
102+
}
103+
104+
cells = append(cells, headers)
105+
for _, v := range formatToTable(headers, data) {
106+
cells = append(cells, v)
107+
}
108+
109+
return cells, nil
110+
}
111+
112+
// TODO no need to specify headers
113+
func (s *RemoteHost) Table(command string, headers []string, metrics []string) (cells [][]string, err error) {
114+
lines, err := s.run(command)
115+
if err != nil {
116+
return nil, err
117+
}
118+
119+
scanner := bufio.NewScanner(strings.NewReader(lines))
120+
data := ""
121+
for scanner.Scan() {
122+
line := scanner.Text()
123+
data += line
124+
}
125+
126+
return formatToTable(headers, data), nil
127+
}
128+
129+
// formatToTable display.
130+
// The string needs to have this:
131+
// Info needs to be splitted with comma
132+
// Depending on number of columns (headers)
133+
// TODO improve this comment :D
134+
func formatToTable(headers []string, data string) (cells [][]string) {
135+
col := len(headers)
136+
c := strings.Split(data, ",")
137+
lenCells := len(c)
138+
for i := 0; i < lenCells; i += col {
139+
cells = append(cells, c[i:min(i+col, lenCells)])
140+
}
141+
142+
return cells
143+
}
144+
145+
func min(a, b int) int {
146+
if a <= b {
147+
return a
148+
}
149+
return b
150+
}
151+
152+
func incSizeMetric(val uint64) uint64 {
153+
return val / 1024
154+
}
File renamed without changes.

internal/platform/ssh.go

+4-106
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,15 @@
11
package platform
22

33
import (
4-
"bufio"
5-
"bytes"
64
"net"
75
"os"
8-
"strconv"
9-
"strings"
106

117
"github.com/pkg/errors"
128
"golang.org/x/crypto/ssh"
139
"golang.org/x/crypto/ssh/agent"
1410
)
1511

16-
const (
17-
sshAgentEnv = "SSH_AUTH_SOCK"
18-
)
19-
20-
type SSH struct {
21-
client *ssh.Client
22-
}
23-
24-
// TODO accept more method of connection than only via ssh-agent
25-
func NewSSHClient(username, addr string) (*SSH, error) {
26-
sshClient, err := createAgentAuth(username, addr)
27-
if err != nil {
28-
return nil, err
29-
}
30-
return &SSH{
31-
client: sshClient,
32-
}, nil
33-
}
34-
35-
func createAgentAuth(username, addr string) (*ssh.Client, error) {
12+
func sshAgentAuth(username, addr string) (*ssh.Client, error) {
3613
s := os.Getenv(sshAgentEnv)
3714
if s == "" {
3815
return nil, errors.Errorf("%s environment varible empty", sshAgentEnv)
@@ -44,92 +21,13 @@ func createAgentAuth(username, addr string) (*ssh.Client, error) {
4421
}
4522

4623
auth := ssh.PublicKeysCallback(agent.NewClient(agentConn).Signers)
47-
4824
config := &ssh.ClientConfig{
4925
User: username,
5026
Auth: []ssh.AuthMethod{auth},
27+
HostKeyCallback: func(string, net.Addr, ssh.PublicKey) error {
28+
return nil
29+
},
5130
}
5231

5332
return ssh.Dial("tcp", addr, config)
5433
}
55-
56-
// Run a command on remote server via SSH
57-
func (s *SSH) Run(command string) (string, error) {
58-
session, err := s.client.NewSession()
59-
if err != nil {
60-
return "", errors.Wrapf(err, "can't create session with SSH client for command %s", command)
61-
}
62-
defer session.Close()
63-
64-
var buf bytes.Buffer
65-
session.Stdout = &buf
66-
err = session.Run(command)
67-
if err != nil {
68-
return "", errors.Wrapf(err, "can't run command %s on remote server", command)
69-
}
70-
71-
return string(buf.Bytes()), nil
72-
}
73-
74-
func (s *SSH) getMemoryInfo(headers []string, metrics []string) (cells [][]string, err error) {
75-
lines, err := s.Run("/bin/cat /proc/meminfo")
76-
if err != nil {
77-
return nil, err
78-
}
79-
80-
scanner := bufio.NewScanner(strings.NewReader(lines))
81-
var data string
82-
for scanner.Scan() {
83-
line := scanner.Text()
84-
parts := strings.Fields(line)
85-
86-
if len(parts) == 3 {
87-
val, err := strconv.ParseUint(parts[1], 10, 64)
88-
val = incSizeMetric(val)
89-
if err != nil {
90-
data += "unknown" + ","
91-
continue
92-
}
93-
94-
for _, v := range metrics {
95-
if parts[0] == v {
96-
data += strconv.FormatInt(int64(val), 10) + ","
97-
}
98-
}
99-
}
100-
}
101-
102-
cells = append(cells, headers)
103-
for _, v := range formatToTable(headers, data) {
104-
cells = append(cells, v)
105-
}
106-
107-
return cells, nil
108-
}
109-
110-
// formatToTable display.
111-
// The string needs to have this:
112-
// Info needs to be splitted with comma
113-
// Depending on number of columns (headers)
114-
// TODO improve this comment :D
115-
func formatToTable(headers []string, data string) (cells [][]string) {
116-
col := len(headers)
117-
c := strings.Split(data, ",")
118-
lenCells := len(c)
119-
for i := 0; i < lenCells; i += col {
120-
cells = append(cells, c[i:min(i+col, lenCells)])
121-
}
122-
123-
return cells
124-
}
125-
126-
func min(a, b int) int {
127-
if a <= b {
128-
return a
129-
}
130-
return b
131-
}
132-
133-
func incSizeMetric(val uint64) uint64 {
134-
return val / 1024
135-
}

0 commit comments

Comments
 (0)