update go-plugin

All our changes have been merged, so this moved the dependency back to
master
This commit is contained in:
James Bardin 2018-12-12 11:33:13 -05:00
parent 8ab5698e2a
commit a5a5e1aed5
4 changed files with 105 additions and 71 deletions

2
go.mod
View File

@ -63,7 +63,7 @@ require (
github.com/hashicorp/go-immutable-radix v0.0.0-20180129170900-7f3cd4390caa // indirect
github.com/hashicorp/go-msgpack v0.0.0-20150518234257-fa3f63826f7c // indirect
github.com/hashicorp/go-multierror v1.0.0
github.com/hashicorp/go-plugin v0.0.0-20181205205220-20341d70f4ff
github.com/hashicorp/go-plugin v0.0.0-20181212150838-f444068e8f5a
github.com/hashicorp/go-retryablehttp v0.5.0
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90
github.com/hashicorp/go-safetemp v0.0.0-20180326211150-b1a1dbde6fdc // indirect

2
go.sum
View File

@ -139,6 +139,8 @@ github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uP
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-plugin v0.0.0-20181205205220-20341d70f4ff h1:z9Nk32P4kDgdYMZU4OGX1Nfpm2q9E++10TSQ6sltD/k=
github.com/hashicorp/go-plugin v0.0.0-20181205205220-20341d70f4ff/go.mod h1:Ft7ju2vWzhO0ETMKUVo12XmXmII6eSUS4rsPTkY/siA=
github.com/hashicorp/go-plugin v0.0.0-20181212150838-f444068e8f5a h1:z9eTtDWoxYrJvtAD+xAepmTEfEmYgouWUytJ84UWAr8=
github.com/hashicorp/go-plugin v0.0.0-20181212150838-f444068e8f5a/go.mod h1:Ft7ju2vWzhO0ETMKUVo12XmXmII6eSUS4rsPTkY/siA=
github.com/hashicorp/go-retryablehttp v0.5.0 h1:aVN0FYnPwAgZI/hVzqwfMiM86ttcHTlQKbBVeVmXPIs=
github.com/hashicorp/go-retryablehttp v0.5.0/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 h1:9HVkPxOpo+yO93Ah4yrO67d/qh0fbLLWbKqhYjyHq9A=

View File

@ -74,7 +74,6 @@ var (
type Client struct {
config *ClientConfig
exited bool
doneLogging chan struct{}
l sync.Mutex
address net.Addr
process *os.Process
@ -82,7 +81,16 @@ type Client struct {
protocol Protocol
logger hclog.Logger
doneCtx context.Context
ctxCancel context.CancelFunc
negotiatedVersion int
// clientWaitGroup is used to manage the lifecycle of the plugin management
// goroutines.
clientWaitGroup sync.WaitGroup
// processKilled is used for testing only, to flag when the process was
// forcefully killed.
processKilled bool
}
// NegotiatedVersion returns the protocol version negotiated with the server.
@ -369,6 +377,14 @@ func (c *Client) Exited() bool {
return c.exited
}
// killed is used in tests to check if a process failed to exit gracefully, and
// needed to be killed.
func (c *Client) killed() bool {
c.l.Lock()
defer c.l.Unlock()
return c.processKilled
}
// End the executing subprocess (if it is running) and perform any cleanup
// tasks necessary such as capturing any remaining logs and so on.
//
@ -380,7 +396,6 @@ func (c *Client) Kill() {
c.l.Lock()
process := c.process
addr := c.address
doneCh := c.doneLogging
c.l.Unlock()
// If there is no process, there is nothing to kill.
@ -389,11 +404,14 @@ func (c *Client) Kill() {
}
defer func() {
// Wait for the all client goroutines to finish.
c.clientWaitGroup.Wait()
// Make sure there is no reference to the old process after it has been
// killed.
c.l.Lock()
defer c.l.Unlock()
c.process = nil
c.l.Unlock()
}()
// We need to check for address here. It is possible that the plugin
@ -416,6 +434,8 @@ func (c *Client) Kill() {
// kill in a moment anyways.
c.logger.Warn("error closing client during Kill", "err", err)
}
} else {
c.logger.Error("client", "error", err)
}
}
@ -424,19 +444,20 @@ func (c *Client) Kill() {
// doneCh which would be closed if the process exits.
if graceful {
select {
case <-doneCh:
case <-c.doneCtx.Done():
c.logger.Debug("plugin exited")
return
case <-time.After(1500 * time.Millisecond):
c.logger.Warn("plugin failed to exit gracefully")
case <-time.After(2 * time.Second):
}
}
// If graceful exiting failed, just kill it
c.logger.Warn("plugin failed to exit gracefully")
process.Kill()
// Wait for the client to finish logging so we have a complete log
<-doneCh
c.l.Lock()
c.processKilled = true
c.l.Unlock()
}
// Starts the underlying subprocess, communicating with it to negotiate
@ -455,7 +476,7 @@ func (c *Client) Start() (addr net.Addr, err error) {
// If one of cmd or reattach isn't set, then it is an error. We wrap
// this in a {} for scoping reasons, and hopeful that the escape
// analysis will pop the stock here.
// analysis will pop the stack here.
{
cmdSet := c.config.Cmd != nil
attachSet := c.config.Reattach != nil
@ -469,59 +490,8 @@ func (c *Client) Start() (addr net.Addr, err error) {
}
}
// Create the logging channel for when we kill
c.doneLogging = make(chan struct{})
// Create a context for when we kill
var ctxCancel context.CancelFunc
c.doneCtx, ctxCancel = context.WithCancel(context.Background())
if c.config.Reattach != nil {
// Verify the process still exists. If not, then it is an error
p, err := os.FindProcess(c.config.Reattach.Pid)
if err != nil {
return nil, err
}
// Attempt to connect to the addr since on Unix systems FindProcess
// doesn't actually return an error if it can't find the process.
conn, err := net.Dial(
c.config.Reattach.Addr.Network(),
c.config.Reattach.Addr.String())
if err != nil {
p.Kill()
return nil, ErrProcessNotFound
}
conn.Close()
// Goroutine to mark exit status
go func(pid int) {
// ensure the context is cancelled when we're done
defer ctxCancel()
// Wait for the process to die
pidWait(pid)
// Log so we can see it
c.logger.Debug("reattached plugin process exited")
// Mark it
c.l.Lock()
defer c.l.Unlock()
c.exited = true
// Close the logging channel since that doesn't work on reattach
close(c.doneLogging)
}(p.Pid)
// Set the address and process
c.address = c.config.Reattach.Addr
c.process = p
c.protocol = c.config.Reattach.Protocol
if c.protocol == "" {
// Default the protocol to net/rpc for backwards compatibility
c.protocol = ProtocolNetRPC
}
return c.address, nil
return c.reattach()
}
if c.config.VersionedPlugins == nil {
@ -618,11 +588,15 @@ func (c *Client) Start() (addr net.Addr, err error) {
}
}()
// Start goroutine to wait for process to exit
exitCh := make(chan struct{})
// Create a context for when we kill
c.doneCtx, c.ctxCancel = context.WithCancel(context.Background())
c.clientWaitGroup.Add(1)
go func() {
// ensure the context is cancelled when we're done
defer ctxCancel()
defer c.ctxCancel()
defer c.clientWaitGroup.Done()
// get the cmd info early, since the process information will be removed
// in Kill.
@ -645,9 +619,6 @@ func (c *Client) Start() (addr net.Addr, err error) {
c.logger.Debug("plugin process exited", debugMsgArgs...)
os.Stderr.Sync()
// Mark that we exited
close(exitCh)
// Set that we exited, which takes a lock
c.l.Lock()
defer c.l.Unlock()
@ -655,12 +626,16 @@ func (c *Client) Start() (addr net.Addr, err error) {
}()
// Start goroutine that logs the stderr
c.clientWaitGroup.Add(1)
// logStderr calls Done()
go c.logStderr(cmdStderr)
// Start a goroutine that is going to be reading the lines
// out of stdout
linesCh := make(chan string)
c.clientWaitGroup.Add(1)
go func() {
defer c.clientWaitGroup.Done()
defer close(linesCh)
scanner := bufio.NewScanner(cmdStdout)
@ -671,8 +646,12 @@ func (c *Client) Start() (addr net.Addr, err error) {
// Make sure after we exit we read the lines from stdout forever
// so they don't block since it is a pipe.
// The scanner goroutine above will close this, but track it with a wait
// group for completeness.
c.clientWaitGroup.Add(1)
defer func() {
go func() {
defer c.clientWaitGroup.Done()
for range linesCh {
}
}()
@ -686,7 +665,7 @@ func (c *Client) Start() (addr net.Addr, err error) {
select {
case <-timeout:
err = errors.New("timeout while waiting for plugin to start")
case <-exitCh:
case <-c.doneCtx.Done():
err = errors.New("plugin exited before we could connect")
case line := <-linesCh:
// Trim the line and split by "|" in order to get the parts of
@ -797,6 +776,59 @@ func (c *Client) loadServerCert(cert string) error {
return nil
}
func (c *Client) reattach() (net.Addr, error) {
// Verify the process still exists. If not, then it is an error
p, err := os.FindProcess(c.config.Reattach.Pid)
if err != nil {
return nil, err
}
// Attempt to connect to the addr since on Unix systems FindProcess
// doesn't actually return an error if it can't find the process.
conn, err := net.Dial(
c.config.Reattach.Addr.Network(),
c.config.Reattach.Addr.String())
if err != nil {
p.Kill()
return nil, ErrProcessNotFound
}
conn.Close()
// Create a context for when we kill
c.doneCtx, c.ctxCancel = context.WithCancel(context.Background())
c.clientWaitGroup.Add(1)
// Goroutine to mark exit status
go func(pid int) {
defer c.clientWaitGroup.Done()
// ensure the context is cancelled when we're done
defer c.ctxCancel()
// Wait for the process to die
pidWait(pid)
// Log so we can see it
c.logger.Debug("reattached plugin process exited")
// Mark it
c.l.Lock()
defer c.l.Unlock()
c.exited = true
}(p.Pid)
// Set the address and process
c.address = c.config.Reattach.Addr
c.process = p
c.protocol = c.config.Reattach.Protocol
if c.protocol == "" {
// Default the protocol to net/rpc for backwards compatibility
c.protocol = ProtocolNetRPC
}
return c.address, nil
}
// checkProtoVersion returns the negotiated version and PluginSet.
// This returns an error if the server returned an incompatible protocol
// version, or an invalid handshake response.
@ -902,7 +934,7 @@ func (c *Client) dialer(_ string, timeout time.Duration) (net.Conn, error) {
}
func (c *Client) logStderr(r io.Reader) {
defer close(c.doneLogging)
defer c.clientWaitGroup.Done()
scanner := bufio.NewScanner(r)
l := c.logger.Named(filepath.Base(c.config.Cmd.Path))

2
vendor/modules.txt vendored
View File

@ -316,7 +316,7 @@ github.com/hashicorp/go-getter/helper/url
github.com/hashicorp/go-hclog
# github.com/hashicorp/go-multierror v1.0.0
github.com/hashicorp/go-multierror
# github.com/hashicorp/go-plugin v0.0.0-20181205205220-20341d70f4ff
# github.com/hashicorp/go-plugin v0.0.0-20181212150838-f444068e8f5a
github.com/hashicorp/go-plugin
github.com/hashicorp/go-plugin/internal/proto
# github.com/hashicorp/go-retryablehttp v0.5.0