diff --git a/commands/commands.go b/commands/commands.go index 338ab4a250..2ba2032e58 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -10,7 +10,10 @@ import ( "github.com/docker/machine/cli" "github.com/docker/machine/commands/mcndirs" + "github.com/docker/machine/drivers/errdriver" "github.com/docker/machine/libmachine/cert" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/drivers/plugin/localbinary" "github.com/docker/machine/libmachine/drivers/rpc" "github.com/docker/machine/libmachine/host" "github.com/docker/machine/libmachine/log" @@ -23,9 +26,13 @@ var ( ErrExpectedOneMachine = errors.New("Error: Expected one machine name as an argument") ) -func newPluginDriver(driverName string, rawContent []byte) (*rpcdriver.RpcClientDriver, error) { +func newPluginDriver(driverName string, rawContent []byte) (drivers.Driver, error) { d, err := rpcdriver.NewRpcClientDriver(rawContent, driverName) if err != nil { + // Not being able to find a driver binary is a "known error" + if _, ok := err.(localbinary.ErrPluginBinaryNotFound); ok { + return errdriver.NewDriver(driverName), nil + } return nil, err } diff --git a/commands/create.go b/commands/create.go index 7e89088382..75a571d96d 100644 --- a/commands/create.go +++ b/commands/create.go @@ -14,6 +14,7 @@ import ( "github.com/docker/machine/cli" "github.com/docker/machine/commands/mcndirs" + "github.com/docker/machine/drivers/errdriver" "github.com/docker/machine/libmachine" "github.com/docker/machine/libmachine/auth" "github.com/docker/machine/libmachine/drivers" @@ -295,6 +296,10 @@ func cmdCreateOuter(c *cli.Context) error { return fmt.Errorf("Error loading driver %q: %s", driverName, err) } + if _, ok := driver.(*errdriver.Driver); ok { + return errdriver.ErrDriverNotLoadable{driverName} + } + // TODO: So much flag manipulation and voodoo here, it seems to be // asking for trouble. // @@ -316,8 +321,10 @@ func cmdCreateOuter(c *cli.Context) error { } } - if err := driver.Close(); err != nil { - return err + if rpcd, ok := driver.(*rpcdriver.RpcClientDriver); ok { + if err := rpcd.Close(); err != nil { + return err + } } return c.App.Run(os.Args) diff --git a/drivers/errdriver/error.go b/drivers/errdriver/error.go new file mode 100644 index 0000000000..ff8836010e --- /dev/null +++ b/drivers/errdriver/error.go @@ -0,0 +1,103 @@ +package errdriver + +import ( + "fmt" + + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/mcnflag" + "github.com/docker/machine/libmachine/state" +) + +type Driver struct { + Name string +} + +type ErrDriverNotLoadable struct { + Name string +} + +func (e ErrDriverNotLoadable) Error() string { + return fmt.Sprintf("Driver %q not found. Do you have the plugin binary accessible in your PATH?", e.Name) +} + +func NewDriver(Name string) drivers.Driver { + return &Driver{ + Name: Name, + } +} + +func (d *Driver) DriverName() string { + return "not-found" +} + +func (d *Driver) PreCreateCheck() error { + return nil +} + +func (d *Driver) GetCreateFlags() []mcnflag.Flag { + return nil +} + +func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { + return ErrDriverNotLoadable{d.Name} +} + +func (d *Driver) GetURL() (string, error) { + return "", ErrDriverNotLoadable{d.Name} +} + +func (d *Driver) GetMachineName() string { + return "" +} + +func (d *Driver) GetIP() (string, error) { + return "1.2.3.4", ErrDriverNotLoadable{d.Name} +} + +func (d *Driver) GetSSHHostname() (string, error) { + return "", ErrDriverNotLoadable{d.Name} +} + +func (d *Driver) GetSSHKeyPath() string { + return "" +} + +func (d *Driver) GetSSHPort() (int, error) { + return 0, ErrDriverNotLoadable{d.Name} +} + +func (d *Driver) GetSSHUsername() string { + return "" +} + +func (d *Driver) GetState() (state.State, error) { + return state.Error, ErrDriverNotLoadable{d.Name} +} + +func (d *Driver) Create() error { + return ErrDriverNotLoadable{d.Name} +} + +func (d *Driver) Remove() error { + return ErrDriverNotLoadable{d.Name} +} + +func (d *Driver) Start() error { + return ErrDriverNotLoadable{d.Name} +} + +func (d *Driver) Stop() error { + return ErrDriverNotLoadable{d.Name} +} + +func (d *Driver) Restart() error { + return ErrDriverNotLoadable{d.Name} +} + +func (d *Driver) Kill() error { + return ErrDriverNotLoadable{d.Name} +} + +func (d *Driver) Upgrade() error { + return ErrDriverNotLoadable{d.Name} +} diff --git a/libmachine/drivers/plugin/localbinary/plugin.go b/libmachine/drivers/plugin/localbinary/plugin.go index 2498db38b6..776943f4f2 100644 --- a/libmachine/drivers/plugin/localbinary/plugin.go +++ b/libmachine/drivers/plugin/localbinary/plugin.go @@ -73,29 +73,41 @@ type LocalBinaryPlugin struct { type LocalBinaryExecutor struct { pluginStdout, pluginStderr io.ReadCloser DriverName string + binaryPath string } -func NewLocalBinaryPlugin(driverName string) *LocalBinaryPlugin { +type ErrPluginBinaryNotFound struct { + driverName string +} + +func (e ErrPluginBinaryNotFound) Error() string { + return fmt.Sprintf("Driver %q not found. Do you have the plugin binary accessible in your PATH?", e.driverName) +} + +func NewLocalBinaryPlugin(driverName string) (*LocalBinaryPlugin, error) { + binaryPath, err := exec.LookPath(fmt.Sprintf("docker-machine-driver-%s", driverName)) + if err != nil { + return nil, ErrPluginBinaryNotFound{driverName} + } + + log.Debugf("Found binary path at %s", binaryPath) + return &LocalBinaryPlugin{ stopCh: make(chan bool), addrCh: make(chan string, 1), Executor: &LocalBinaryExecutor{ DriverName: driverName, + binaryPath: binaryPath, }, - } + }, nil } func (lbe *LocalBinaryExecutor) Start() (*bufio.Scanner, *bufio.Scanner, error) { + var err error + log.Debugf("Launching plugin server for driver %s", lbe.DriverName) - binaryPath, err := exec.LookPath(fmt.Sprintf("docker-machine-driver-%s", lbe.DriverName)) - if err != nil { - return nil, nil, fmt.Errorf("Driver %q not found. Do you have the plugin binary accessible in your PATH?", lbe.DriverName) - } - - log.Debugf("Found binary path at %s", binaryPath) - - cmd := exec.Command(binaryPath) + cmd := exec.Command(lbe.binaryPath) lbe.pluginStdout, err = cmd.StdoutPipe() if err != nil { diff --git a/libmachine/drivers/rpc/client_driver.go b/libmachine/drivers/rpc/client_driver.go index 0e79591d94..37165e1897 100644 --- a/libmachine/drivers/rpc/client_driver.go +++ b/libmachine/drivers/rpc/client_driver.go @@ -45,16 +45,20 @@ func NewInternalClient(rpcclient *rpc.Client) *InternalClient { RpcClient: rpcclient, } } + func NewRpcClientDriver(rawDriverData []byte, driverName string) (*RpcClientDriver, error) { mcnName := "" - p := localbinary.NewLocalBinaryPlugin(driverName) + p, err := localbinary.NewLocalBinaryPlugin(driverName) + if err != nil { + return nil, err + } go func() { if err := p.Serve(); err != nil { - // If we can't safely load the server, best to just - // bail. - log.Fatal(err) + // TODO: Is this best approach? + log.Warn(err) + return } }()