-
Notifications
You must be signed in to change notification settings - Fork 13
/
client.go
159 lines (128 loc) · 3.69 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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package supervisord
import (
"errors"
"net"
"net/http"
"github.com/kolo/xmlrpc"
)
type (
Client struct {
*xmlrpc.Client
}
)
var (
// Will be returned by a few functions not yet fully implemented.
FIXMENotImplementedError error
// Will be returned if the API endpoint returns false without further explanation.
ReturnedFalseError error
// Will be returned if the API endpoint returns a reply we don't know how to parse.
ReturnedMalformedReply error
)
func init() {
FIXMENotImplementedError = errors.New("Not implemented yet")
ReturnedFalseError = errors.New("Call returned false")
ReturnedMalformedReply = errors.New("Call returned malformed reply")
}
type options struct {
username, password string
transport http.RoundTripper
}
// ClientOption is used to customize the client.
type ClientOption func(*options)
// WithAuthentication sets the username and password to use when authenticating against the server.
func WithAuthentication(username, password string) ClientOption {
return func(o *options) {
o.username = username
o.password = password
}
}
// WithTransport sets the RoundTripper representing the ability to execute a single HTTP transaction, obtaining the Response for a given Request.
func WithTransport(transport http.RoundTripper) ClientOption {
return func(o *options) {
o.transport = transport
}
}
func (c *Client) stringCall(method string, args ...interface{}) (string, error) {
var str string
err := c.Call(method, args, &str)
return str, err
}
func (c *Client) boolCall(method string, args ...interface{}) error {
var result bool
err := c.Call(method, args, &result)
if err != nil {
return err
}
if !result {
return ReturnedFalseError
}
return nil
}
// Get a new client suitable for communicating with a supervisord.
// url must contain a real url to a supervisord RPC-service.
//
// Url for local supervisord should be http://127.0.0.1:9001/RPC2 by default.
func NewClient(url string, opts ...ClientOption) (*Client, error) {
opt := &options{}
for _, o := range opts {
o(opt)
}
var transport = http.DefaultTransport
if opt.transport != nil {
transport = opt.transport
}
if opt.username != "" && opt.password != "" {
transport = &basicAuthTransport{
username: opt.username,
password: opt.password,
transport: transport,
}
}
rpc, err := xmlrpc.NewClient(url, transport)
if err != nil {
return nil, err
}
return &Client{rpc}, nil
}
// NewUnixSocketClient returns a new client which connects to supervisord
// though a local unix socket
func NewUnixSocketClient(path string, opts ...ClientOption) (*Client, error) {
opt := &options{}
for _, o := range opts {
o(opt)
}
// we inject this fake dialer, it will only connect
// to the path given, and does not care about what address
// is given to it.
dialer := func(_, _ string) (conn net.Conn, err error) {
return net.Dial("unix", path)
}
var tr http.RoundTripper = &http.Transport{
Dial: dialer,
}
if opt.username != "" && opt.password != "" {
tr = &basicAuthTransport{
username: opt.username,
password: opt.password,
transport: tr,
}
}
// we pass a valid url, as this is later url.Parse()'ed
// also we need to somehow specify "/RPC2"
rpc, err := xmlrpc.NewClient("http://127.0.0.1/RPC2", tr)
if err != nil {
return nil, err
}
return &Client{rpc}, nil
}
// basicAuthTransport is a http.RoundTripper that wraps another http.RoundTripper
// and injects basic auth credentials into each request.
type basicAuthTransport struct {
transport http.RoundTripper
username string
password string
}
func (b basicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
req.SetBasicAuth(b.username, b.password)
return b.transport.RoundTrip(req)
}