1package rpcdriver
2
3import (
4	"fmt"
5	"net/rpc"
6	"sync"
7	"time"
8
9	"io"
10
11	"github.com/docker/machine/libmachine/drivers"
12	"github.com/docker/machine/libmachine/drivers/plugin/localbinary"
13	"github.com/docker/machine/libmachine/log"
14	"github.com/docker/machine/libmachine/mcnflag"
15	"github.com/docker/machine/libmachine/state"
16	"github.com/docker/machine/libmachine/version"
17)
18
19var (
20	heartbeatInterval = 5 * time.Second
21)
22
23type RPCClientDriverFactory interface {
24	NewRPCClientDriver(driverName string, rawDriver []byte) (*RPCClientDriver, error)
25	io.Closer
26}
27
28type DefaultRPCClientDriverFactory struct {
29	openedDrivers     []*RPCClientDriver
30	openedDriversLock sync.Locker
31}
32
33func NewRPCClientDriverFactory() RPCClientDriverFactory {
34	return &DefaultRPCClientDriverFactory{
35		openedDrivers:     []*RPCClientDriver{},
36		openedDriversLock: &sync.Mutex{},
37	}
38}
39
40type RPCClientDriver struct {
41	plugin          localbinary.DriverPlugin
42	heartbeatDoneCh chan bool
43	Client          *InternalClient
44}
45
46type RPCCall struct {
47	ServiceMethod string
48	Args          interface{}
49	Reply         interface{}
50}
51
52type InternalClient struct {
53	MachineName    string
54	RPCClient      *rpc.Client
55	rpcServiceName string
56}
57
58const (
59	RPCServiceNameV0 = `RpcServerDriver`
60	RPCServiceNameV1 = `RPCServerDriver`
61
62	HeartbeatMethod          = `.Heartbeat`
63	GetVersionMethod         = `.GetVersion`
64	CloseMethod              = `.Close`
65	GetCreateFlagsMethod     = `.GetCreateFlags`
66	SetConfigRawMethod       = `.SetConfigRaw`
67	GetConfigRawMethod       = `.GetConfigRaw`
68	DriverNameMethod         = `.DriverName`
69	SetConfigFromFlagsMethod = `.SetConfigFromFlags`
70	GetURLMethod             = `.GetURL`
71	GetMachineNameMethod     = `.GetMachineName`
72	GetIPMethod              = `.GetIP`
73	GetSSHHostnameMethod     = `.GetSSHHostname`
74	GetSSHKeyPathMethod      = `.GetSSHKeyPath`
75	GetSSHPortMethod         = `.GetSSHPort`
76	GetSSHUsernameMethod     = `.GetSSHUsername`
77	GetStateMethod           = `.GetState`
78	PreCreateCheckMethod     = `.PreCreateCheck`
79	CreateMethod             = `.Create`
80	RemoveMethod             = `.Remove`
81	StartMethod              = `.Start`
82	StopMethod               = `.Stop`
83	RestartMethod            = `.Restart`
84	KillMethod               = `.Kill`
85	UpgradeMethod            = `.Upgrade`
86)
87
88func (ic *InternalClient) Call(serviceMethod string, args interface{}, reply interface{}) error {
89	if serviceMethod != HeartbeatMethod {
90		log.Debugf("(%s) Calling %+v", ic.MachineName, serviceMethod)
91	}
92	return ic.RPCClient.Call(ic.rpcServiceName+serviceMethod, args, reply)
93}
94
95func (ic *InternalClient) switchToV0() {
96	ic.rpcServiceName = RPCServiceNameV0
97}
98
99func NewInternalClient(rpcclient *rpc.Client) *InternalClient {
100	return &InternalClient{
101		RPCClient:      rpcclient,
102		rpcServiceName: RPCServiceNameV1,
103	}
104}
105
106func (f *DefaultRPCClientDriverFactory) Close() error {
107	f.openedDriversLock.Lock()
108	defer f.openedDriversLock.Unlock()
109
110	for _, openedDriver := range f.openedDrivers {
111		if err := openedDriver.close(); err != nil {
112			// No need to display an error.
113			// There's nothing we can do and it doesn't add value to the user.
114		}
115	}
116	f.openedDrivers = []*RPCClientDriver{}
117
118	return nil
119}
120
121func (f *DefaultRPCClientDriverFactory) NewRPCClientDriver(driverName string, rawDriver []byte) (*RPCClientDriver, error) {
122	mcnName := ""
123
124	p, err := localbinary.NewPlugin(driverName)
125	if err != nil {
126		return nil, err
127	}
128
129	go func() {
130		if err := p.Serve(); err != nil {
131			// TODO: Is this best approach?
132			log.Warn(err)
133			return
134		}
135	}()
136
137	addr, err := p.Address()
138	if err != nil {
139		return nil, fmt.Errorf("Error attempting to get plugin server address for RPC: %s", err)
140	}
141
142	rpcclient, err := rpc.DialHTTP("tcp", addr)
143	if err != nil {
144		return nil, err
145	}
146
147	c := &RPCClientDriver{
148		Client:          NewInternalClient(rpcclient),
149		heartbeatDoneCh: make(chan bool),
150	}
151
152	f.openedDriversLock.Lock()
153	f.openedDrivers = append(f.openedDrivers, c)
154	f.openedDriversLock.Unlock()
155
156	var serverVersion int
157	if err := c.Client.Call(GetVersionMethod, struct{}{}, &serverVersion); err != nil {
158		// this is the first call we make to the server. We try to play nice with old pre 0.5.1 client,
159		// by gracefully trying old RPCServiceName, we do this only once, and keep the result for future calls.
160		log.Debugf(err.Error())
161		log.Debugf("Client (%s) with %s does not work, re-attempting with %s", c.Client.MachineName, RPCServiceNameV1, RPCServiceNameV0)
162		c.Client.switchToV0()
163		if err := c.Client.Call(GetVersionMethod, struct{}{}, &serverVersion); err != nil {
164			return nil, err
165		}
166	}
167
168	if serverVersion != version.APIVersion {
169		return nil, fmt.Errorf("Driver binary uses an incompatible API version (%d)", serverVersion)
170	}
171	log.Debug("Using API Version ", serverVersion)
172
173	go func(c *RPCClientDriver) {
174		for {
175			select {
176			case <-c.heartbeatDoneCh:
177				return
178			case <-time.After(heartbeatInterval):
179				if err := c.Client.Call(HeartbeatMethod, struct{}{}, nil); err != nil {
180					log.Warnf("Wrapper Docker Machine process exiting due to closed plugin server (%s)", err)
181					if err := c.close(); err != nil {
182						log.Warn(err)
183					}
184				}
185			}
186		}
187	}(c)
188
189	if err := c.SetConfigRaw(rawDriver); err != nil {
190		return nil, err
191	}
192
193	mcnName = c.GetMachineName()
194	p.MachineName = mcnName
195	c.Client.MachineName = mcnName
196	c.plugin = p
197
198	return c, nil
199}
200
201func (c *RPCClientDriver) MarshalJSON() ([]byte, error) {
202	return c.GetConfigRaw()
203}
204
205func (c *RPCClientDriver) UnmarshalJSON(data []byte) error {
206	return c.SetConfigRaw(data)
207}
208
209func (c *RPCClientDriver) close() error {
210	c.heartbeatDoneCh <- true
211	close(c.heartbeatDoneCh)
212
213	log.Debug("Making call to close driver server")
214
215	if err := c.Client.Call(CloseMethod, struct{}{}, nil); err != nil {
216		log.Debugf("Failed to make call to close driver server: %s", err)
217	} else {
218		log.Debug("Successfully made call to close driver server")
219	}
220
221	log.Debug("Making call to close connection to plugin binary")
222
223	return c.plugin.Close()
224}
225
226// Helper method to make requests which take no arguments and return simply a
227// string, e.g. "GetIP".
228func (c *RPCClientDriver) rpcStringCall(method string) (string, error) {
229	var info string
230
231	if err := c.Client.Call(method, struct{}{}, &info); err != nil {
232		return "", err
233	}
234
235	return info, nil
236}
237
238func (c *RPCClientDriver) GetCreateFlags() []mcnflag.Flag {
239	var flags []mcnflag.Flag
240
241	if err := c.Client.Call(GetCreateFlagsMethod, struct{}{}, &flags); err != nil {
242		log.Warnf("Error attempting call to get create flags: %s", err)
243	}
244
245	return flags
246}
247
248func (c *RPCClientDriver) SetConfigRaw(data []byte) error {
249	return c.Client.Call(SetConfigRawMethod, data, nil)
250}
251
252func (c *RPCClientDriver) GetConfigRaw() ([]byte, error) {
253	var data []byte
254
255	if err := c.Client.Call(GetConfigRawMethod, struct{}{}, &data); err != nil {
256		return nil, err
257	}
258
259	return data, nil
260}
261
262// DriverName returns the name of the driver
263func (c *RPCClientDriver) DriverName() string {
264	driverName, err := c.rpcStringCall(DriverNameMethod)
265	if err != nil {
266		log.Warnf("Error attempting call to get driver name: %s", err)
267	}
268
269	return driverName
270}
271
272func (c *RPCClientDriver) SetConfigFromFlags(flags drivers.DriverOptions) error {
273	return c.Client.Call(SetConfigFromFlagsMethod, &flags, nil)
274}
275
276func (c *RPCClientDriver) GetURL() (string, error) {
277	return c.rpcStringCall(GetURLMethod)
278}
279
280func (c *RPCClientDriver) GetMachineName() string {
281	name, err := c.rpcStringCall(GetMachineNameMethod)
282	if err != nil {
283		log.Warnf("Error attempting call to get machine name: %s", err)
284	}
285
286	return name
287}
288
289func (c *RPCClientDriver) GetIP() (string, error) {
290	return c.rpcStringCall(GetIPMethod)
291}
292
293func (c *RPCClientDriver) GetSSHHostname() (string, error) {
294	return c.rpcStringCall(GetSSHHostnameMethod)
295}
296
297// GetSSHKeyPath returns the key path
298// TODO:  This method doesn't even make sense to have with RPC.
299func (c *RPCClientDriver) GetSSHKeyPath() string {
300	path, err := c.rpcStringCall(GetSSHKeyPathMethod)
301	if err != nil {
302		log.Warnf("Error attempting call to get SSH key path: %s", err)
303	}
304
305	return path
306}
307
308func (c *RPCClientDriver) GetSSHPort() (int, error) {
309	var port int
310
311	if err := c.Client.Call(GetSSHPortMethod, struct{}{}, &port); err != nil {
312		return 0, err
313	}
314
315	return port, nil
316}
317
318func (c *RPCClientDriver) GetSSHUsername() string {
319	username, err := c.rpcStringCall(GetSSHUsernameMethod)
320	if err != nil {
321		log.Warnf("Error attempting call to get SSH username: %s", err)
322	}
323
324	return username
325}
326
327func (c *RPCClientDriver) GetState() (state.State, error) {
328	var s state.State
329
330	if err := c.Client.Call(GetStateMethod, struct{}{}, &s); err != nil {
331		return state.Error, err
332	}
333
334	return s, nil
335}
336
337func (c *RPCClientDriver) PreCreateCheck() error {
338	return c.Client.Call(PreCreateCheckMethod, struct{}{}, nil)
339}
340
341func (c *RPCClientDriver) Create() error {
342	return c.Client.Call(CreateMethod, struct{}{}, nil)
343}
344
345func (c *RPCClientDriver) Remove() error {
346	return c.Client.Call(RemoveMethod, struct{}{}, nil)
347}
348
349func (c *RPCClientDriver) Start() error {
350	return c.Client.Call(StartMethod, struct{}{}, nil)
351}
352
353func (c *RPCClientDriver) Stop() error {
354	return c.Client.Call(StopMethod, struct{}{}, nil)
355}
356
357func (c *RPCClientDriver) Restart() error {
358	return c.Client.Call(RestartMethod, struct{}{}, nil)
359}
360
361func (c *RPCClientDriver) Kill() error {
362	return c.Client.Call(KillMethod, struct{}{}, nil)
363}
364
365func (c *RPCClientDriver) Upgrade() error {
366	return c.Client.Call(UpgradeMethod, struct{}{}, nil)
367}
368