mirror of https://github.com/docker/docs.git
309 lines
7.3 KiB
Go
309 lines
7.3 KiB
Go
package rpcdriver
|
|
|
|
import (
|
|
"fmt"
|
|
"net/rpc"
|
|
"time"
|
|
|
|
"github.com/docker/machine/libmachine/drivers"
|
|
"github.com/docker/machine/libmachine/drivers/plugin/localbinary"
|
|
"github.com/docker/machine/libmachine/log"
|
|
"github.com/docker/machine/libmachine/mcnflag"
|
|
"github.com/docker/machine/libmachine/state"
|
|
"github.com/docker/machine/libmachine/version"
|
|
)
|
|
|
|
var (
|
|
heartbeatInterval = 200 * time.Millisecond
|
|
)
|
|
|
|
type RPCClientDriver struct {
|
|
plugin localbinary.DriverPlugin
|
|
heartbeatDoneCh chan bool
|
|
Client *InternalClient
|
|
}
|
|
|
|
type RPCCall struct {
|
|
ServiceMethod string
|
|
Args interface{}
|
|
Reply interface{}
|
|
}
|
|
|
|
type InternalClient struct {
|
|
MachineName string
|
|
RPCClient *rpc.Client
|
|
}
|
|
|
|
func (ic *InternalClient) Call(serviceMethod string, args interface{}, reply interface{}) error {
|
|
if serviceMethod != "RPCServerDriver.Heartbeat" {
|
|
log.Debugf("(%s) Calling %+v", ic.MachineName, serviceMethod)
|
|
}
|
|
return ic.RPCClient.Call(serviceMethod, args, reply)
|
|
}
|
|
|
|
func NewInternalClient(rpcclient *rpc.Client) *InternalClient {
|
|
return &InternalClient{
|
|
RPCClient: rpcclient,
|
|
}
|
|
}
|
|
|
|
func NewRPCClientDriver(rawDriverData []byte, driverName string) (*RPCClientDriver, error) {
|
|
mcnName := ""
|
|
|
|
p, err := localbinary.NewPlugin(driverName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
go func() {
|
|
if err := p.Serve(); err != nil {
|
|
// TODO: Is this best approach?
|
|
log.Warn(err)
|
|
return
|
|
}
|
|
}()
|
|
|
|
addr, err := p.Address()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error attempting to get plugin server address for RPC: %s", err)
|
|
}
|
|
|
|
rpcclient, err := rpc.DialHTTP("tcp", addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
c := &RPCClientDriver{
|
|
Client: NewInternalClient(rpcclient),
|
|
heartbeatDoneCh: make(chan bool),
|
|
}
|
|
|
|
go func(c *RPCClientDriver) {
|
|
for {
|
|
select {
|
|
case <-c.heartbeatDoneCh:
|
|
return
|
|
default:
|
|
if err := c.Client.Call("RPCServerDriver.Heartbeat", struct{}{}, nil); err != nil {
|
|
log.Warnf("Error attempting heartbeat call to plugin server: %s", err)
|
|
c.Close()
|
|
return
|
|
}
|
|
time.Sleep(heartbeatInterval)
|
|
}
|
|
}
|
|
}(c)
|
|
|
|
var serverVersion int
|
|
if err := c.Client.Call("RPCServerDriver.GetVersion", struct{}{}, &serverVersion); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if serverVersion != version.APIVersion {
|
|
return nil, fmt.Errorf("Driver binary uses an incompatible API version (%d)", serverVersion)
|
|
}
|
|
log.Debug("Using API Version ", serverVersion)
|
|
|
|
if err := c.SetConfigRaw(rawDriverData); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
mcnName = c.GetMachineName()
|
|
p.MachineName = mcnName
|
|
c.Client.MachineName = mcnName
|
|
c.plugin = p
|
|
|
|
return c, nil
|
|
}
|
|
|
|
func (c *RPCClientDriver) MarshalJSON() ([]byte, error) {
|
|
return c.GetConfigRaw()
|
|
}
|
|
|
|
func (c *RPCClientDriver) UnmarshalJSON(data []byte) error {
|
|
return c.SetConfigRaw(data)
|
|
}
|
|
|
|
func (c *RPCClientDriver) Close() error {
|
|
c.heartbeatDoneCh <- true
|
|
close(c.heartbeatDoneCh)
|
|
|
|
log.Debug("Making call to close connection to plugin binary")
|
|
|
|
if err := c.plugin.Close(); err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Debug("Making call to close driver server")
|
|
|
|
if err := c.Client.Call("RPCServerDriver.Close", struct{}{}, nil); err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Debug("Successfully made call to close driver server")
|
|
|
|
return nil
|
|
}
|
|
|
|
// Helper method to make requests which take no arguments and return simply a
|
|
// string, e.g. "GetIP".
|
|
func (c *RPCClientDriver) rpcStringCall(method string) (string, error) {
|
|
var info string
|
|
|
|
if err := c.Client.Call(method, struct{}{}, &info); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return info, nil
|
|
}
|
|
|
|
func (c *RPCClientDriver) GetCreateFlags() []mcnflag.Flag {
|
|
var flags []mcnflag.Flag
|
|
|
|
if err := c.Client.Call("RPCServerDriver.GetCreateFlags", struct{}{}, &flags); err != nil {
|
|
log.Warnf("Error attempting call to get create flags: %s", err)
|
|
}
|
|
|
|
return flags
|
|
}
|
|
|
|
func (c *RPCClientDriver) SetConfigRaw(data []byte) error {
|
|
return c.Client.Call("RPCServerDriver.SetConfigRaw", data, nil)
|
|
}
|
|
|
|
func (c *RPCClientDriver) GetConfigRaw() ([]byte, error) {
|
|
var data []byte
|
|
|
|
if err := c.Client.Call("RPCServerDriver.GetConfigRaw", struct{}{}, &data); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
// DriverName returns the name of the driver
|
|
func (c *RPCClientDriver) DriverName() string {
|
|
driverName, err := c.rpcStringCall("RPCServerDriver.DriverName")
|
|
if err != nil {
|
|
log.Warnf("Error attempting call to get driver name: %s", err)
|
|
}
|
|
|
|
return driverName
|
|
}
|
|
|
|
func (c *RPCClientDriver) SetConfigFromFlags(flags drivers.DriverOptions) error {
|
|
return c.Client.Call("RPCServerDriver.SetConfigFromFlags", &flags, nil)
|
|
}
|
|
|
|
func (c *RPCClientDriver) GetURL() (string, error) {
|
|
return c.rpcStringCall("RPCServerDriver.GetURL")
|
|
}
|
|
|
|
func (c *RPCClientDriver) GetMachineName() string {
|
|
name, err := c.rpcStringCall("RPCServerDriver.GetMachineName")
|
|
if err != nil {
|
|
log.Warnf("Error attempting call to get machine name: %s", err)
|
|
}
|
|
|
|
return name
|
|
}
|
|
|
|
func (c *RPCClientDriver) GetIP() (string, error) {
|
|
return c.rpcStringCall("RPCServerDriver.GetIP")
|
|
}
|
|
|
|
func (c *RPCClientDriver) GetSSHHostname() (string, error) {
|
|
return c.rpcStringCall("RPCServerDriver.GetSSHHostname")
|
|
}
|
|
|
|
// GetSSHKeyPath returns the key path
|
|
// TODO: This method doesn't even make sense to have with RPC.
|
|
func (c *RPCClientDriver) GetSSHKeyPath() string {
|
|
path, err := c.rpcStringCall("RPCServerDriver.GetSSHKeyPath")
|
|
if err != nil {
|
|
log.Warnf("Error attempting call to get SSH key path: %s", err)
|
|
}
|
|
|
|
return path
|
|
}
|
|
|
|
func (c *RPCClientDriver) GetSSHPort() (int, error) {
|
|
var port int
|
|
|
|
if err := c.Client.Call("RPCServerDriver.GetSSHPort", struct{}{}, &port); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return port, nil
|
|
}
|
|
|
|
func (c *RPCClientDriver) GetSSHUsername() string {
|
|
username, err := c.rpcStringCall("RPCServerDriver.GetSSHUsername")
|
|
if err != nil {
|
|
log.Warnf("Error attempting call to get SSH username: %s", err)
|
|
}
|
|
|
|
return username
|
|
}
|
|
|
|
func (c *RPCClientDriver) GetState() (state.State, error) {
|
|
var s state.State
|
|
|
|
if err := c.Client.Call("RPCServerDriver.GetState", struct{}{}, &s); err != nil {
|
|
return state.Error, err
|
|
}
|
|
|
|
return s, nil
|
|
}
|
|
|
|
func (c *RPCClientDriver) PreCreateCheck() error {
|
|
return c.Client.Call("RPCServerDriver.PreCreateCheck", struct{}{}, nil)
|
|
}
|
|
|
|
func (c *RPCClientDriver) Create() error {
|
|
return c.Client.Call("RPCServerDriver.Create", struct{}{}, nil)
|
|
}
|
|
|
|
func (c *RPCClientDriver) Remove() error {
|
|
return c.Client.Call("RPCServerDriver.Remove", struct{}{}, nil)
|
|
}
|
|
|
|
func (c *RPCClientDriver) Start() error {
|
|
return c.Client.Call("RPCServerDriver.Start", struct{}{}, nil)
|
|
}
|
|
|
|
func (c *RPCClientDriver) Stop() error {
|
|
return c.Client.Call("RPCServerDriver.Stop", struct{}{}, nil)
|
|
}
|
|
|
|
func (c *RPCClientDriver) Restart() error {
|
|
return c.Client.Call("RPCServerDriver.Restart", struct{}{}, nil)
|
|
}
|
|
|
|
func (c *RPCClientDriver) Kill() error {
|
|
return c.Client.Call("RPCServerDriver.Kill", struct{}{}, nil)
|
|
}
|
|
|
|
func (c *RPCClientDriver) LocalArtifactPath(file string) string {
|
|
var path string
|
|
|
|
if err := c.Client.Call("RPCServerDriver.LocalArtifactPath", file, &path); err != nil {
|
|
log.Warnf("Error attempting call to get LocalArtifactPath: %s", err)
|
|
}
|
|
|
|
return path
|
|
}
|
|
|
|
func (c *RPCClientDriver) GlobalArtifactPath() string {
|
|
globalArtifactPath, err := c.rpcStringCall("RPCServerDriver.GlobalArtifactPath")
|
|
if err != nil {
|
|
log.Warnf("Error attempting call to get GlobalArtifactPath: %s", err)
|
|
}
|
|
|
|
return globalArtifactPath
|
|
}
|
|
|
|
func (c *RPCClientDriver) Upgrade() error {
|
|
return c.Client.Call("RPCServerDriver.Upgrade", struct{}{}, nil)
|
|
}
|