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