diff --git a/vendor/github.com/hashicorp/go-plugin/client.go b/vendor/github.com/hashicorp/go-plugin/client.go index e559f713eee..3692736dae9 100644 --- a/vendor/github.com/hashicorp/go-plugin/client.go +++ b/vendor/github.com/hashicorp/go-plugin/client.go @@ -243,8 +243,16 @@ func (c *Client) Kill() { return } - // Kill the process - c.process.Kill() + // Close the client to cleanly exit the process + client, err := c.Client() + if err == nil { + err = client.Close() + } + if err != nil { + // If something went wrong somewhere gracefully quitting the + // plugin, we just force kill it. + c.process.Kill() + } // Wait for the client to finish logging so we have a complete log <-c.doneLogging diff --git a/vendor/github.com/hashicorp/go-plugin/rpc_client.go b/vendor/github.com/hashicorp/go-plugin/rpc_client.go index e6d613bc61d..29f9bf063e7 100644 --- a/vendor/github.com/hashicorp/go-plugin/rpc_client.go +++ b/vendor/github.com/hashicorp/go-plugin/rpc_client.go @@ -76,6 +76,13 @@ func (c *RPCClient) SyncStreams(stdout io.Writer, stderr io.Writer) error { // Close closes the connection. The client is no longer usable after this // is called. func (c *RPCClient) Close() error { + // Call the control channel and ask it to gracefully exit. If this + // errors, then we save it so that we always return an error but we + // want to try to close the other channels anyways. + var empty struct{} + returnErr := c.control.Call("Control.Quit", true, &empty) + + // Close the other streams we have if err := c.control.Close(); err != nil { return err } @@ -85,8 +92,14 @@ func (c *RPCClient) Close() error { if err := c.stderr.Close(); err != nil { return err } + if err := c.broker.Close(); err != nil { + return err + } - return c.broker.Close() + // Return back the error we got from Control.Quit. This is very important + // since we MUST return non-nil error if this fails so that Client.Kill + // will properly try a process.Kill. + return returnErr } func (c *RPCClient) Dispense(name string) (interface{}, error) { diff --git a/vendor/github.com/hashicorp/go-plugin/rpc_server.go b/vendor/github.com/hashicorp/go-plugin/rpc_server.go index 714b047dc5f..33601e207aa 100644 --- a/vendor/github.com/hashicorp/go-plugin/rpc_server.go +++ b/vendor/github.com/hashicorp/go-plugin/rpc_server.go @@ -22,6 +22,10 @@ type RPCServer struct { // make our own custom one we pipe across. Stdout io.Reader Stderr io.Reader + + // DoneCh should be set to a non-nil channel that will be closed + // when the control requests the RPC server to end. + DoneCh chan<- struct{} } // Accept accepts connections on a listener and serves requests for @@ -84,6 +88,9 @@ func (s *RPCServer) ServeConn(conn io.ReadWriteCloser) { // Use the control connection to build the dispenser and serve the // connection. server := rpc.NewServer() + server.RegisterName("Control", &controlServer{ + server: s, + }) server.RegisterName("Dispenser", &dispenseServer{ broker: broker, plugins: s.Plugins, @@ -91,6 +98,31 @@ func (s *RPCServer) ServeConn(conn io.ReadWriteCloser) { server.ServeConn(control) } +// done is called internally by the control server to trigger the +// doneCh to close which is listened to by the main process to cleanly +// exit. +func (s *RPCServer) done() { + if s.DoneCh != nil { + close(s.DoneCh) + } +} + +// dispenseServer dispenses variousinterface implementations for Terraform. +type controlServer struct { + server *RPCServer +} + +func (c *controlServer) Quit( + null bool, response *struct{}) error { + // End the server + c.server.done() + + // Always return true + *response = struct{}{} + + return nil +} + // dispenseServer dispenses variousinterface implementations for Terraform. type dispenseServer struct { broker *MuxBroker diff --git a/vendor/github.com/hashicorp/go-plugin/server.go b/vendor/github.com/hashicorp/go-plugin/server.go index 9d0de871f1e..b5c5270a7d8 100644 --- a/vendor/github.com/hashicorp/go-plugin/server.go +++ b/vendor/github.com/hashicorp/go-plugin/server.go @@ -98,11 +98,15 @@ func Serve(opts *ServeConfig) { } defer listener.Close() + // Create the channel to tell us when we're done + doneCh := make(chan struct{}) + // Create the RPC server to dispense server := &RPCServer{ Plugins: opts.Plugins, Stdout: stdout_r, Stderr: stderr_r, + DoneCh: doneCh, } // Output the address and service name to stdout so that core can bring it up. @@ -134,7 +138,10 @@ func Serve(opts *ServeConfig) { os.Stderr = stderr_w // Serve - server.Accept(listener) + go server.Accept(listener) + + // Wait for the graceful exit + <-doneCh } func serverListener() (net.Listener, error) { diff --git a/vendor/vendor.json b/vendor/vendor.json index 5fdb65f305b..ab2c284b5da 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -461,10 +461,10 @@ "revision": "d30f09973e19c1dfcd120b2d9c4f168e68d6b5d5" }, { - "checksumSHA1": "FK7oNTTXthoohAwDgVo/nmf1w9A=", + "checksumSHA1": "Jh6jdEjDeajnpEG8xlrLrnYw210=", "path": "github.com/hashicorp/go-plugin", - "revision": "6691772c4e872547b2e6cfc54c853a6d61bc0edb", - "revisionTime": "2016-06-02T23:01:53Z" + "revision": "8cf118f7a2f0c7ef1c82f66d4f6ac77c7e27dc12", + "revisionTime": "2016-06-08T02:21:58Z" }, { "path": "github.com/hashicorp/go-syslog",