This repository was archived by the owner on Aug 15, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 605
Expand file tree
/
Copy pathklient.go
More file actions
335 lines (276 loc) · 8.75 KB
/
Copy pathklient.go
File metadata and controls
335 lines (276 loc) · 8.75 KB
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
// A library for easing the interaction with klient.
package klient
import (
"errors"
"io/ioutil"
"strings"
"time"
konfig "koding/kites/config"
"koding/klient/client"
"koding/klient/command"
"koding/klient/fs"
"koding/klient/remote/req"
"koding/klientctl/config"
"koding/klientctl/list"
"github.com/koding/kite"
"github.com/koding/kite/dnode"
)
// defaultKlientTimeout is a general timeout for klient communications.
const defaultKlientTimeout = 5 * time.Second
// Klient implements methods that klientctl uses when calling Klient, unmarshalling
// automatically into the proper methods.
type Klient struct {
// The Teller is Klient's main "transport" for communication with the internal
// Client.
//
// Why an interface here? Rather than Requiring a kite.Client specifically, the
// Tell() method is the only thing required. As such, the Klient struct itself,
// some older Transport structs, and pretty much any struct that talks to Kites
// has this Tell method and Satisfies the interface.
Teller interface {
Tell(string, ...interface{}) (*dnode.Partial, error)
}
// Client is exposed (and via GetClient() mainly to allow the Klient struct
// to be backwards compatible with non-Klient using methods.
//
// All actual communication is done via the Teller - this field purely exists
// for the GetClient() method.
Client *kite.Client
}
// NewKlient creates a Klient instance from the given kite.Client.
func NewKlient(c *kite.Client) *Klient {
return &Klient{
Client: c,
Teller: c,
}
}
// KlientOptions contains various fields for connecting to a klient.
type KlientOptions struct {
// Address is the path to the Klient.
Address string
// KiteKeyPath is the full path to kite.key, which will be loaded and used
// to authorize kdbin requests to Klient.
KiteKeyPath string
// KiteKey is a content of kite.key, which is used for
// authenticating kd with other klients.
//
// If not empty, this fields is used instead of KiteKeyPath.
KiteKey string
// Name, as passed to the first argument in `kite.New()`.
Name string
// Version, as passed to the second argument to `kite.New()`.
Version string
// Environment for the kite.Config.Environemnt.
Environment string
}
// NewKlientOptions returns KlientOptions initialized to default values.
func NewKlientOptions() KlientOptions {
return KlientOptions{
Address: config.Konfig.Endpoints.Klient.Private.String(),
KiteKeyPath: config.Konfig.KiteKeyFile,
KiteKey: config.Konfig.KiteKey,
Name: config.Name,
Version: config.KiteVersion,
Environment: config.Environment,
}
}
// CreateKlientClient creates a kite with default KlientOptions and returns a
// Kite Client to talk to that Klient.
func CreateKlientWithDefaultOpts() (*kite.Client, error) {
return CreateKlientClient(NewKlientOptions())
}
// CreateKlientClient creates a kite to the klient specified by KlientOptions.
// In most cases CreateKlientWithDefaultOpts should be used instead of this, ie
// this should be used only if you want to override KlientOptions.
func CreateKlientClient(opts KlientOptions) (*kite.Client, error) {
if opts.Version == "" {
return nil, errors.New("CreateKlientClient: Version is required")
}
if opts.Address == "" {
return nil, errors.New("CreateKlientClient: Address is required")
}
k := kite.NewWithConfig(opts.Name, opts.Version, konfig.NewKiteConfig(false))
k.Config.Environment = opts.Environment
c := k.NewClient(opts.Address)
if opts.KiteKey != "" {
c.Auth = &kite.Auth{
Type: "kiteKey",
Key: opts.KiteKey,
}
} else if opts.KiteKeyPath != "" {
// If a key path is declared, load it and setup auth.
data, err := ioutil.ReadFile(opts.KiteKeyPath)
if err != nil {
return nil, err
}
c.Auth = &kite.Auth{
Type: "kiteKey",
Key: strings.TrimSpace(string(data)),
}
}
return c, nil
}
// NewDefaultDialedKlient creates a pre-dialed Klient instance using default
// klient options.
func NewDefaultDialedKlient() (*Klient, error) {
return NewDialedKlient(NewKlientOptions())
}
// NewDialedKlient creates a pre-dialed Klient instance. In most cases
// NewDefaultDialedKlient should be used instead of this, ie this should be used
// only if you want to override KlientOptions.
func NewDialedKlient(opts KlientOptions) (*Klient, error) {
c, err := CreateKlientClient(opts)
if err != nil {
return nil, err
}
if err := c.Dial(); err != nil {
return nil, err
}
return NewKlient(c), nil
}
func (k *Klient) Tell(methodName string, reqs ...interface{}) (*dnode.Partial, error) {
if k.Teller == nil {
return nil, errors.New("Missing Teller on Klient struct")
}
return k.Teller.Tell(methodName, reqs...)
}
// GetClient is a utility function for getting the underlying kite Client
// back from a Klient struct hidden behind an interface. Used mainly to
// interact with legacy code.
func (k *Klient) GetClient() *kite.Client {
return k.Client
}
// RemoteList the current machines.
func (k *Klient) RemoteList() (list.KiteInfos, error) {
res, err := k.Client.TellWithTimeout("remote.list", defaultKlientTimeout)
if err != nil {
return nil, err
}
var infos []list.KiteInfo
if err := res.Unmarshal(&infos); err != nil {
return nil, err
}
return infos, nil
}
// RemoteCache calls klient's remote.cache method.
//
// Note that due to how the remote/req library is setup, this function needs to
// take the callback as a separate argument for now. This will be improved
// in the future, in one way or another.
func (k *Klient) RemoteCache(r req.Cache, cb func(par *dnode.Partial)) error {
cacheReq := struct {
req.Cache
Progress dnode.Function `json:"progress"`
}{
Cache: r,
}
if cb != nil {
cacheReq.Progress = dnode.Callback(cb)
}
// No response from cacheFolder currently.
_, err := k.Tell("remote.cacheFolder", cacheReq)
return err
}
// RemoteMountFolder calls klient's remote.mountFolder method. If there are
// any warnings, those are returned here.
func (k *Klient) RemoteMountFolder(r req.MountFolder) (string, error) {
resp, err := k.Tell("remote.mountFolder", r)
if err != nil {
return "", err
}
var warning string
// TODO: Ignore the nil unmarshal error, but return others.
resp.Unmarshal(&warning)
return warning, nil
}
// RemoteStatus calls klients remote.status method.
func (k *Klient) RemoteStatus(r req.Status) error {
_, err := k.Tell("remote.status", r)
return err
}
// RemoteMountInfo calls klients remote.mountInfo method.
func (k *Klient) RemoteMountInfo(mountName string) (req.MountInfoResponse, error) {
r := req.MountInfo{MountName: mountName}
resp, err := k.Tell("remote.mountInfo", r)
if err != nil {
return req.MountInfoResponse{}, err
}
var mountInfo req.MountInfoResponse
// TODO: Ignore the nil unmarshal error, but return others.
resp.Unmarshal(&mountInfo)
return mountInfo, nil
}
// RemoteRemount calls klient's remote.remount method.
func (k *Klient) RemoteRemount(mountName string) error {
r := req.Remount{MountName: mountName}
_, err := k.Tell("remote.remount", r)
return err
}
// RemoteExec calls the `remote.exec` method.
func (k *Klient) RemoteExec(machineName, c string) (command.Output, error) {
var res command.Output
req := req.Exec{Machine: machineName, Command: c}
kRes, err := k.Tell("remote.exec", req)
if err != nil {
return res, err
}
return res, kRes.Unmarshal(&res)
}
func (k *Klient) RemoteReadDirectory(mountName, remotePath string) ([]fs.FileEntry, error) {
r := req.ReadDirectoryOptions{
Machine: mountName,
ReadDirectoryOptions: fs.ReadDirectoryOptions{
Path: remotePath,
},
}
res, err := k.Tell("remote.readDirectory", r)
if err != nil {
return nil, err
}
resMap, err := res.Map()
if err != nil {
return nil, err
}
partial, ok := resMap["files"]
if !ok {
return nil, nil
}
filePartials, err := partial.Slice()
if err != nil {
return nil, err
}
files := make([]fs.FileEntry, len(filePartials))
for i, p := range filePartials {
var fe fs.FileEntry
if err := p.Unmarshal(&fe); err != nil {
return nil, err
}
files[i] = fe
}
return files, nil
}
// RemoteCurrentUsername calls klient's `remote.currentUsername` method
func (k *Klient) RemoteCurrentUsername(opts req.CurrentUsernameOptions) (string, error) {
var username string
kRes, err := k.Tell("remote.currentUsername", opts)
if err != nil {
return username, err
}
return username, kRes.Unmarshal(&username)
}
// RemoteGetPathSize calls the klient's `remote.getPathSize` method
func (k *Klient) RemoteGetPathSize(opts req.GetPathSizeOptions) (uint64, error) {
var size uint64
kRes, err := k.Tell("remote.getPathSize", opts)
if err != nil {
return size, err
}
return size, kRes.Unmarshal(&size)
}
func (k *Klient) LocalOpenFiles(files ...string) error {
_, err := k.Tell("client.Publish", FilesEvent{
PublishRequest: client.PublishRequest{EventName: "openFiles"},
Files: files,
})
return err
}