-
Notifications
You must be signed in to change notification settings - Fork 67
/
client.go
142 lines (112 loc) · 2.52 KB
/
client.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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package main
import (
"encoding/json"
"errors"
"fmt"
"io"
"net"
"os"
"sync"
"github.com/sandia-minimega/minimega/v2/pkg/minicli"
log "github.com/sandia-minimega/minimega/v2/pkg/minilog"
"github.com/sandia-minimega/minimega/v2/pkg/minipager"
)
type Request struct {
Command string
}
type Response struct {
// Resp, Rendered, More are returned in response to a command
Resp minicli.Responses
Rendered string
More bool // whether there are more responses coming
}
type Conn struct {
url string
// first error encountered
err error
conn net.Conn
// lock so we don't try to use enc/dec for concurrent Runs
lock sync.Mutex
enc *json.Encoder
dec *json.Decoder
// Set the Pager to use for long output messages
Pager minipager.Pager
}
func Dial(path string) (*Conn, error) {
c := &Conn{
url: path,
}
// try to connect to the local minimega
conn, err := net.Dial("unix", c.url)
if err != nil {
return nil, err
}
c.conn = conn
c.enc = json.NewEncoder(c.conn)
c.dec = json.NewDecoder(c.conn)
return c, nil
}
func (c *Conn) Close() error {
return c.conn.Close()
}
// Run a command through a JSON pipe, hand back channel for responses.
func (c *Conn) Run(cmd string) chan *Response {
out := make(chan *Response)
if cmd == "" {
// Language spec: "Receiving from a nil channel blocks forever."
// Instead, make and immediately close the channel so that range
// doesn't block and receives no values.
close(out)
return out
}
c.lock.Lock()
err := c.enc.Encode(Request{Command: cmd})
if err != nil {
c.err = fmt.Errorf("local command gob encode: %v", err)
// see above
close(out)
return out
}
log.Debugln("encoded command:", cmd)
go func() {
defer c.lock.Unlock()
defer close(out)
for {
var r Response
if err := c.dec.Decode(&r); err != nil {
if err == io.EOF {
c.err = errors.New("server disconnected")
return
}
c.err = fmt.Errorf("local command gob decode: %v", err)
return
}
out <- &r
if !r.More {
log.Debugln("got last message")
break
} else {
log.Debugln("expecting more data")
}
}
}()
return out
}
// Run a command and print the response.
func (c *Conn) RunAndPrint(cmd string, page bool) {
for resp := range c.Run(cmd) {
if page && c.Pager != nil {
c.Pager.Page(resp.Rendered)
} else if resp.Rendered != "" {
fmt.Println(resp.Rendered)
}
if err := resp.Resp.Error(); err != "" {
fmt.Fprintln(os.Stderr, err)
}
}
}
func (c *Conn) Error() error {
c.lock.Lock()
defer c.lock.Unlock()
return c.err
}