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

Commit ebd5fe9

Browse files
committed
Continue to develop SSH client
1 parent 574bb9c commit ebd5fe9

File tree

3 files changed

+138
-4
lines changed

3 files changed

+138
-4
lines changed

internal/monitor_server_widget.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66

77
type monitorServerWidget struct {
88
tui *Tui
9-
client platform.SSH
9+
client *platform.SSH
1010
}
1111

1212
func NewMonitorServerWidget(username, addr string) (*monitorServerWidget, error) {
@@ -23,3 +23,13 @@ func NewMonitorServerWidget(username, addr string) (*monitorServerWidget, error)
2323
func (ms *monitorServerWidget) CreateWidgets(widget Widget, tui *Tui) (f func() error, err error) {
2424
return nil, nil
2525
}
26+
27+
func (ms *monitorServerWidget) GetMemory(widget Widget, tui *Tui) (f func() error, err error) {
28+
// headers MemTotal, MemFree, Buffers, Cached, SwapTotal, SwapFree
29+
return func() error {
30+
return nil
31+
}, nil
32+
}
33+
34+
// func (ms *monitorServerWidget) table(widget Widget, firstHeader string) (f func() error, err error) {
35+
// }

internal/platform/ssh.go

+88-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package platform
22

33
import (
4+
"bufio"
5+
"bytes"
46
"net"
57
"os"
8+
"strconv"
9+
"strings"
610

711
"github.com/pkg/errors"
812
"golang.org/x/crypto/ssh"
@@ -18,12 +22,12 @@ type SSH struct {
1822
}
1923

2024
// TODO accept more method of connection than only via ssh-agent
21-
func NewSSHClient(username, addr string) (SSH, error) {
25+
func NewSSHClient(username, addr string) (*SSH, error) {
2226
sshClient, err := createAgentAuth(username, addr)
2327
if err != nil {
24-
return SSH{}, err
28+
return nil, err
2529
}
26-
return SSH{
30+
return &SSH{
2731
client: sshClient,
2832
}, nil
2933
}
@@ -48,3 +52,84 @@ func createAgentAuth(username, addr string) (*ssh.Client, error) {
4852

4953
return ssh.Dial("tcp", addr, config)
5054
}
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+
}

internal/platform/ssh_test.go

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package platform
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func Test_formatToTable(t *testing.T) {
9+
testCases := []struct {
10+
name string
11+
expected [][]string
12+
headers []string
13+
data string
14+
}{
15+
{
16+
name: "happy case",
17+
expected: [][]string{
18+
{
19+
"data1", "data2", "data3", "data4",
20+
},
21+
{
22+
"data5", "data6", "data7", "data8",
23+
},
24+
},
25+
headers: []string{"dataHeader1", "dataHeader2", "dataHeader3", "dataHeader4"},
26+
data: "data1,data2,data3,data4,data5,data6,data7,data8",
27+
},
28+
}
29+
30+
for _, tc := range testCases {
31+
t.Run(tc.name, func(t *testing.T) {
32+
actual := formatToTable(tc.headers, tc.data)
33+
34+
if !reflect.DeepEqual(actual, tc.expected) {
35+
t.Errorf("Expected %v, actual %v", tc.expected, actual)
36+
}
37+
})
38+
}
39+
}

0 commit comments

Comments
 (0)