Bump reva deps (#8412)
* bump dependencies Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * bump reva and add config options Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> --------- Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
+287
-103
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
@@ -23,6 +26,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-plugin/internal/cmdrunner"
|
||||
"github.com/hashicorp/go-plugin/internal/grpcmux"
|
||||
"github.com/hashicorp/go-plugin/runner"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
@@ -41,7 +47,7 @@ var managedClientsLock sync.Mutex
|
||||
var (
|
||||
// ErrProcessNotFound is returned when a client is instantiated to
|
||||
// reattach to an existing process and it isn't found.
|
||||
ErrProcessNotFound = errors.New("Reattachment process not found")
|
||||
ErrProcessNotFound = cmdrunner.ErrProcessNotFound
|
||||
|
||||
// ErrChecksumsDoNotMatch is returned when binary's checksum doesn't match
|
||||
// the one provided in the SecureConfig.
|
||||
@@ -58,8 +64,18 @@ var (
|
||||
// ErrSecureConfigAndReattach is returned when both Reattach and
|
||||
// SecureConfig are set.
|
||||
ErrSecureConfigAndReattach = errors.New("only one of Reattach or SecureConfig can be set")
|
||||
|
||||
// ErrGRPCBrokerMuxNotSupported is returned when the client requests
|
||||
// multiplexing over the gRPC broker, but the plugin does not support the
|
||||
// feature. In most cases, this should be resolvable by updating and
|
||||
// rebuilding the plugin, or restarting the plugin with
|
||||
// ClientConfig.GRPCBrokerMultiplex set to false.
|
||||
ErrGRPCBrokerMuxNotSupported = errors.New("client requested gRPC broker multiplexing but plugin does not support the feature")
|
||||
)
|
||||
|
||||
// defaultPluginLogBufferSize is the default size of the buffer used to read from stderr for plugin log lines.
|
||||
const defaultPluginLogBufferSize = 64 * 1024
|
||||
|
||||
// Client handles the lifecycle of a plugin application. It launches
|
||||
// plugins, connects to them, dispenses interface implementations, and handles
|
||||
// killing the process.
|
||||
@@ -76,7 +92,7 @@ type Client struct {
|
||||
exited bool
|
||||
l sync.Mutex
|
||||
address net.Addr
|
||||
process *os.Process
|
||||
runner runner.AttachedRunner
|
||||
client ClientProtocol
|
||||
protocol Protocol
|
||||
logger hclog.Logger
|
||||
@@ -95,6 +111,11 @@ type Client struct {
|
||||
// processKilled is used for testing only, to flag when the process was
|
||||
// forcefully killed.
|
||||
processKilled bool
|
||||
|
||||
unixSocketCfg UnixSocketConfig
|
||||
|
||||
grpcMuxerOnce sync.Once
|
||||
grpcMuxer *grpcmux.GRPCClientMuxer
|
||||
}
|
||||
|
||||
// NegotiatedVersion returns the protocol version negotiated with the server.
|
||||
@@ -103,6 +124,19 @@ func (c *Client) NegotiatedVersion() int {
|
||||
return c.negotiatedVersion
|
||||
}
|
||||
|
||||
// ID returns a unique ID for the running plugin. By default this is the process
|
||||
// ID (pid), but it could take other forms if RunnerFunc was provided.
|
||||
func (c *Client) ID() string {
|
||||
c.l.Lock()
|
||||
defer c.l.Unlock()
|
||||
|
||||
if c.runner != nil {
|
||||
return c.runner.ID()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// ClientConfig is the configuration used to initialize a new
|
||||
// plugin client. After being used to initialize a plugin client,
|
||||
// that configuration must not be modified again.
|
||||
@@ -130,6 +164,13 @@ type ClientConfig struct {
|
||||
Cmd *exec.Cmd
|
||||
Reattach *ReattachConfig
|
||||
|
||||
// RunnerFunc allows consumers to provide their own implementation of
|
||||
// runner.Runner and control the context within which a plugin is executed.
|
||||
// The cmd argument will have been copied from the config and populated with
|
||||
// environment variables that a go-plugin server expects to read such as
|
||||
// AutoMTLS certs and the magic cookie key.
|
||||
RunnerFunc func(l hclog.Logger, cmd *exec.Cmd, tmpDir string) (runner.Runner, error)
|
||||
|
||||
// SecureConfig is configuration for verifying the integrity of the
|
||||
// executable. It can not be used with Reattach.
|
||||
SecureConfig *SecureConfig
|
||||
@@ -182,6 +223,10 @@ type ClientConfig struct {
|
||||
// it will default to hclog's default logger.
|
||||
Logger hclog.Logger
|
||||
|
||||
// PluginLogBufferSize is the buffer size(bytes) to read from stderr for plugin log lines.
|
||||
// If this is 0, then the default of 64KB is used.
|
||||
PluginLogBufferSize int
|
||||
|
||||
// AutoMTLS has the client and server automatically negotiate mTLS for
|
||||
// transport authentication. This ensures that only the original client will
|
||||
// be allowed to connect to the server, and all other connections will be
|
||||
@@ -209,6 +254,44 @@ type ClientConfig struct {
|
||||
// to create gRPC connections. This only affects plugins using the gRPC
|
||||
// protocol.
|
||||
GRPCDialOptions []grpc.DialOption
|
||||
|
||||
// GRPCBrokerMultiplex turns on multiplexing for the gRPC broker. The gRPC
|
||||
// broker will multiplex all brokered gRPC servers over the plugin's original
|
||||
// listener socket instead of making a new listener for each server. The
|
||||
// go-plugin library currently only includes a Go implementation for the
|
||||
// server (i.e. plugin) side of gRPC broker multiplexing.
|
||||
//
|
||||
// Does not support reattaching.
|
||||
//
|
||||
// Multiplexed gRPC streams MUST be established sequentially, i.e. after
|
||||
// calling AcceptAndServe from one side, wait for the other side to Dial
|
||||
// before calling AcceptAndServe again.
|
||||
GRPCBrokerMultiplex bool
|
||||
|
||||
// SkipHostEnv allows plugins to run without inheriting the parent process'
|
||||
// environment variables.
|
||||
SkipHostEnv bool
|
||||
|
||||
// UnixSocketConfig configures additional options for any Unix sockets
|
||||
// that are created. Not normally required. Not supported on Windows.
|
||||
UnixSocketConfig *UnixSocketConfig
|
||||
}
|
||||
|
||||
type UnixSocketConfig struct {
|
||||
// If set, go-plugin will change the owner of any Unix sockets created to
|
||||
// this group, and set them as group-writable. Can be a name or gid. The
|
||||
// client process must be a member of this group or chown will fail.
|
||||
Group string
|
||||
|
||||
// TempDir specifies the base directory to use when creating a plugin-specific
|
||||
// temporary directory. It is expected to already exist and be writable. If
|
||||
// not set, defaults to the directory chosen by os.MkdirTemp.
|
||||
TempDir string
|
||||
|
||||
// The directory to create Unix sockets in. Internally created and managed
|
||||
// by go-plugin and deleted when the plugin is killed. Will be created
|
||||
// inside TempDir if specified.
|
||||
socketDir string
|
||||
}
|
||||
|
||||
// ReattachConfig is used to configure a client to reattach to an
|
||||
@@ -220,6 +303,11 @@ type ReattachConfig struct {
|
||||
Addr net.Addr
|
||||
Pid int
|
||||
|
||||
// ReattachFunc allows consumers to provide their own implementation of
|
||||
// runner.AttachedRunner and attach to something other than a plain process.
|
||||
// At least one of Pid or ReattachFunc must be set.
|
||||
ReattachFunc runner.ReattachFunc
|
||||
|
||||
// Test is set to true if this is reattaching to to a plugin in "test mode"
|
||||
// (see ServeConfig.Test). In this mode, client.Kill will NOT kill the
|
||||
// process and instead will rely on the plugin to terminate itself. This
|
||||
@@ -295,11 +383,11 @@ func CleanupClients() {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// Creates a new plugin client which manages the lifecycle of an external
|
||||
// NewClient creates a new plugin client which manages the lifecycle of an external
|
||||
// plugin and gets the address for the RPC connection.
|
||||
//
|
||||
// The client must be cleaned up at some point by calling Kill(). If
|
||||
// the client is a managed client (created with NewManagedClient) you
|
||||
// the client is a managed client (created with ClientConfig.Managed) you
|
||||
// can just call CleanupClients at the end of your program and they will
|
||||
// be properly cleaned.
|
||||
func NewClient(config *ClientConfig) (c *Client) {
|
||||
@@ -317,10 +405,10 @@ func NewClient(config *ClientConfig) (c *Client) {
|
||||
}
|
||||
|
||||
if config.SyncStdout == nil {
|
||||
config.SyncStdout = ioutil.Discard
|
||||
config.SyncStdout = io.Discard
|
||||
}
|
||||
if config.SyncStderr == nil {
|
||||
config.SyncStderr = ioutil.Discard
|
||||
config.SyncStderr = io.Discard
|
||||
}
|
||||
|
||||
if config.AllowedProtocols == nil {
|
||||
@@ -335,6 +423,10 @@ func NewClient(config *ClientConfig) (c *Client) {
|
||||
})
|
||||
}
|
||||
|
||||
if config.PluginLogBufferSize == 0 {
|
||||
config.PluginLogBufferSize = defaultPluginLogBufferSize
|
||||
}
|
||||
|
||||
c = &Client{
|
||||
config: config,
|
||||
logger: config.Logger,
|
||||
@@ -407,12 +499,13 @@ func (c *Client) killed() bool {
|
||||
func (c *Client) Kill() {
|
||||
// Grab a lock to read some private fields.
|
||||
c.l.Lock()
|
||||
process := c.process
|
||||
runner := c.runner
|
||||
addr := c.address
|
||||
hostSocketDir := c.unixSocketCfg.socketDir
|
||||
c.l.Unlock()
|
||||
|
||||
// If there is no process, there is nothing to kill.
|
||||
if process == nil {
|
||||
// If there is no runner or ID, there is nothing to kill.
|
||||
if runner == nil || runner.ID() == "" {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -420,10 +513,14 @@ func (c *Client) Kill() {
|
||||
// Wait for the all client goroutines to finish.
|
||||
c.clientWaitGroup.Wait()
|
||||
|
||||
if hostSocketDir != "" {
|
||||
os.RemoveAll(hostSocketDir)
|
||||
}
|
||||
|
||||
// Make sure there is no reference to the old process after it has been
|
||||
// killed.
|
||||
c.l.Lock()
|
||||
c.process = nil
|
||||
c.runner = nil
|
||||
c.l.Unlock()
|
||||
}()
|
||||
|
||||
@@ -466,14 +563,16 @@ func (c *Client) Kill() {
|
||||
|
||||
// If graceful exiting failed, just kill it
|
||||
c.logger.Warn("plugin failed to exit gracefully")
|
||||
process.Kill()
|
||||
if err := runner.Kill(context.Background()); err != nil {
|
||||
c.logger.Debug("error killing plugin", "error", err)
|
||||
}
|
||||
|
||||
c.l.Lock()
|
||||
c.processKilled = true
|
||||
c.l.Unlock()
|
||||
}
|
||||
|
||||
// Starts the underlying subprocess, communicating with it to negotiate
|
||||
// Start the underlying subprocess, communicating with it to negotiate
|
||||
// a port for RPC connections, and returning the address to connect via RPC.
|
||||
//
|
||||
// This method is safe to call multiple times. Subsequent calls have no effect.
|
||||
@@ -491,16 +590,27 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
||||
// this in a {} for scoping reasons, and hopeful that the escape
|
||||
// analysis will pop the stack here.
|
||||
{
|
||||
cmdSet := c.config.Cmd != nil
|
||||
attachSet := c.config.Reattach != nil
|
||||
secureSet := c.config.SecureConfig != nil
|
||||
if cmdSet == attachSet {
|
||||
return nil, fmt.Errorf("Only one of Cmd or Reattach must be set")
|
||||
var mutuallyExclusiveOptions int
|
||||
if c.config.Cmd != nil {
|
||||
mutuallyExclusiveOptions += 1
|
||||
}
|
||||
if c.config.Reattach != nil {
|
||||
mutuallyExclusiveOptions += 1
|
||||
}
|
||||
if c.config.RunnerFunc != nil {
|
||||
mutuallyExclusiveOptions += 1
|
||||
}
|
||||
if mutuallyExclusiveOptions != 1 {
|
||||
return nil, fmt.Errorf("exactly one of Cmd, or Reattach, or RunnerFunc must be set")
|
||||
}
|
||||
|
||||
if secureSet && attachSet {
|
||||
if c.config.SecureConfig != nil && c.config.Reattach != nil {
|
||||
return nil, ErrSecureConfigAndReattach
|
||||
}
|
||||
|
||||
if c.config.GRPCBrokerMultiplex && c.config.Reattach != nil {
|
||||
return nil, fmt.Errorf("gRPC broker multiplexing is not supported with Reattach config")
|
||||
}
|
||||
}
|
||||
|
||||
if c.config.Reattach != nil {
|
||||
@@ -532,24 +642,24 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
||||
fmt.Sprintf("PLUGIN_MAX_PORT=%d", c.config.MaxPort),
|
||||
fmt.Sprintf("PLUGIN_PROTOCOL_VERSIONS=%s", strings.Join(versionStrings, ",")),
|
||||
}
|
||||
if c.config.GRPCBrokerMultiplex {
|
||||
env = append(env, fmt.Sprintf("%s=true", envMultiplexGRPC))
|
||||
}
|
||||
|
||||
cmd := c.config.Cmd
|
||||
cmd.Env = append(cmd.Env, os.Environ()...)
|
||||
if cmd == nil {
|
||||
// It's only possible to get here if RunnerFunc is non-nil, but we'll
|
||||
// still use cmd as a spec to populate metadata for the external
|
||||
// implementation to consume.
|
||||
cmd = exec.Command("")
|
||||
}
|
||||
if !c.config.SkipHostEnv {
|
||||
cmd.Env = append(cmd.Env, os.Environ()...)
|
||||
}
|
||||
cmd.Env = append(cmd.Env, env...)
|
||||
cmd.Stdin = os.Stdin
|
||||
|
||||
cmdStdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmdStderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c.config.SecureConfig == nil {
|
||||
c.logger.Warn("plugin configured with a nil SecureConfig")
|
||||
} else {
|
||||
if c.config.SecureConfig != nil {
|
||||
if ok, err := c.config.SecureConfig.Check(cmd.Path); err != nil {
|
||||
return nil, fmt.Errorf("error verifying checksum: %s", err)
|
||||
} else if !ok {
|
||||
@@ -582,26 +692,62 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
c.logger.Debug("starting plugin", "path", cmd.Path, "args", cmd.Args)
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return
|
||||
if c.config.UnixSocketConfig != nil {
|
||||
c.unixSocketCfg = *c.config.UnixSocketConfig
|
||||
}
|
||||
|
||||
// Set the process
|
||||
c.process = cmd.Process
|
||||
c.logger.Debug("plugin started", "path", cmd.Path, "pid", c.process.Pid)
|
||||
if c.unixSocketCfg.Group != "" {
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", EnvUnixSocketGroup, c.unixSocketCfg.Group))
|
||||
}
|
||||
|
||||
var runner runner.Runner
|
||||
switch {
|
||||
case c.config.RunnerFunc != nil:
|
||||
c.unixSocketCfg.socketDir, err = os.MkdirTemp(c.unixSocketCfg.TempDir, "plugin-dir")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// os.MkdirTemp creates folders with 0o700, so if we have a group
|
||||
// configured we need to make it group-writable.
|
||||
if c.unixSocketCfg.Group != "" {
|
||||
err = setGroupWritable(c.unixSocketCfg.socketDir, c.unixSocketCfg.Group, 0o770)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", EnvUnixSocketDir, c.unixSocketCfg.socketDir))
|
||||
c.logger.Trace("created temporary directory for unix sockets", "dir", c.unixSocketCfg.socketDir)
|
||||
|
||||
runner, err = c.config.RunnerFunc(c.logger, cmd, c.unixSocketCfg.socketDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
runner, err = cmdrunner.NewCmdRunner(c.logger, cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
c.runner = runner
|
||||
startCtx, startCtxCancel := context.WithTimeout(context.Background(), c.config.StartTimeout)
|
||||
defer startCtxCancel()
|
||||
err = runner.Start(startCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Make sure the command is properly cleaned up if there is an error
|
||||
defer func() {
|
||||
r := recover()
|
||||
rErr := recover()
|
||||
|
||||
if err != nil || r != nil {
|
||||
cmd.Process.Kill()
|
||||
if err != nil || rErr != nil {
|
||||
runner.Kill(context.Background())
|
||||
}
|
||||
|
||||
if r != nil {
|
||||
panic(r)
|
||||
if rErr != nil {
|
||||
panic(rErr)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -612,7 +758,7 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
||||
c.clientWaitGroup.Add(1)
|
||||
c.stderrWaitGroup.Add(1)
|
||||
// logStderr calls Done()
|
||||
go c.logStderr(cmdStderr)
|
||||
go c.logStderr(runner.Name(), runner.Stderr())
|
||||
|
||||
c.clientWaitGroup.Add(1)
|
||||
go func() {
|
||||
@@ -621,29 +767,17 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
||||
|
||||
defer c.clientWaitGroup.Done()
|
||||
|
||||
// get the cmd info early, since the process information will be removed
|
||||
// in Kill.
|
||||
pid := c.process.Pid
|
||||
path := cmd.Path
|
||||
|
||||
// wait to finish reading from stderr since the stderr pipe reader
|
||||
// will be closed by the subsequent call to cmd.Wait().
|
||||
c.stderrWaitGroup.Wait()
|
||||
|
||||
// Wait for the command to end.
|
||||
err := cmd.Wait()
|
||||
|
||||
msgArgs := []interface{}{
|
||||
"path", path,
|
||||
"pid", pid,
|
||||
}
|
||||
err := runner.Wait(context.Background())
|
||||
if err != nil {
|
||||
msgArgs = append(msgArgs,
|
||||
[]interface{}{"error", err.Error()}...)
|
||||
c.logger.Error("plugin process exited", msgArgs...)
|
||||
c.logger.Error("plugin process exited", "plugin", runner.Name(), "id", runner.ID(), "error", err.Error())
|
||||
} else {
|
||||
// Log and make sure to flush the logs right away
|
||||
c.logger.Info("plugin process exited", msgArgs...)
|
||||
c.logger.Info("plugin process exited", "plugin", runner.Name(), "id", runner.ID())
|
||||
}
|
||||
|
||||
os.Stderr.Sync()
|
||||
@@ -662,10 +796,13 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
||||
defer c.clientWaitGroup.Done()
|
||||
defer close(linesCh)
|
||||
|
||||
scanner := bufio.NewScanner(cmdStdout)
|
||||
scanner := bufio.NewScanner(runner.Stdout())
|
||||
for scanner.Scan() {
|
||||
linesCh <- scanner.Text()
|
||||
}
|
||||
if scanner.Err() != nil {
|
||||
c.logger.Error("error encountered while scanning stdout", "error", scanner.Err())
|
||||
}
|
||||
}()
|
||||
|
||||
// Make sure after we exit we read the lines from stdout forever
|
||||
@@ -685,22 +822,27 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
||||
timeout := time.After(c.config.StartTimeout)
|
||||
|
||||
// Start looking for the address
|
||||
c.logger.Debug("waiting for RPC address", "path", cmd.Path)
|
||||
c.logger.Debug("waiting for RPC address", "plugin", runner.Name())
|
||||
select {
|
||||
case <-timeout:
|
||||
err = errors.New("timeout while waiting for plugin to start")
|
||||
case <-c.doneCtx.Done():
|
||||
err = errors.New("plugin exited before we could connect")
|
||||
case line := <-linesCh:
|
||||
case line, ok := <-linesCh:
|
||||
// Trim the line and split by "|" in order to get the parts of
|
||||
// the output.
|
||||
line = strings.TrimSpace(line)
|
||||
parts := strings.SplitN(line, "|", 6)
|
||||
parts := strings.Split(line, "|")
|
||||
if len(parts) < 4 {
|
||||
err = fmt.Errorf(
|
||||
"Unrecognized remote plugin message: %s\n\n"+
|
||||
"This usually means that the plugin is either invalid or simply\n"+
|
||||
"needs to be recompiled to support the latest protocol.", line)
|
||||
errText := fmt.Sprintf("Unrecognized remote plugin message: %s", line)
|
||||
if !ok {
|
||||
errText += "\n" + "Failed to read any lines from plugin's stdout"
|
||||
}
|
||||
additionalNotes := runner.Diagnose(context.Background())
|
||||
if additionalNotes != "" {
|
||||
errText += "\n" + additionalNotes
|
||||
}
|
||||
err = errors.New(errText)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -735,13 +877,18 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
||||
c.negotiatedVersion = version
|
||||
c.logger.Debug("using plugin", "version", version)
|
||||
|
||||
switch parts[2] {
|
||||
network, address, err := runner.PluginToHost(parts[2], parts[3])
|
||||
if err != nil {
|
||||
return addr, err
|
||||
}
|
||||
|
||||
switch network {
|
||||
case "tcp":
|
||||
addr, err = net.ResolveTCPAddr("tcp", parts[3])
|
||||
addr, err = net.ResolveTCPAddr("tcp", address)
|
||||
case "unix":
|
||||
addr, err = net.ResolveUnixAddr("unix", parts[3])
|
||||
addr, err = net.ResolveUnixAddr("unix", address)
|
||||
default:
|
||||
err = fmt.Errorf("Unknown address type: %s", parts[3])
|
||||
err = fmt.Errorf("Unknown address type: %s", address)
|
||||
}
|
||||
|
||||
// If we have a server type, then record that. We default to net/rpc
|
||||
@@ -773,6 +920,18 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
||||
return nil, fmt.Errorf("error parsing server cert: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if c.config.GRPCBrokerMultiplex && c.protocol == ProtocolGRPC {
|
||||
if len(parts) <= 6 {
|
||||
return nil, fmt.Errorf("%w; for Go plugins, you will need to update the "+
|
||||
"github.com/hashicorp/go-plugin dependency and recompile", ErrGRPCBrokerMuxNotSupported)
|
||||
}
|
||||
if muxSupported, err := strconv.ParseBool(parts[6]); err != nil {
|
||||
return nil, fmt.Errorf("error parsing %q as a boolean for gRPC broker multiplexing support", parts[6])
|
||||
} else if !muxSupported {
|
||||
return nil, ErrGRPCBrokerMuxNotSupported
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.address = addr
|
||||
@@ -802,39 +961,30 @@ func (c *Client) loadServerCert(cert string) error {
|
||||
}
|
||||
|
||||
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 {
|
||||
// On Unix systems, FindProcess never returns an error.
|
||||
// On Windows, for non-existent pids it returns:
|
||||
// os.SyscallError - 'OpenProcess: the paremter is incorrect'
|
||||
return nil, ErrProcessNotFound
|
||||
reattachFunc := c.config.Reattach.ReattachFunc
|
||||
// For backwards compatibility default to cmdrunner.ReattachFunc
|
||||
if reattachFunc == nil {
|
||||
reattachFunc = cmdrunner.ReattachFunc(c.config.Reattach.Pid, c.config.Reattach.Addr)
|
||||
}
|
||||
|
||||
// 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())
|
||||
r, err := reattachFunc()
|
||||
if err != nil {
|
||||
p.Kill()
|
||||
return nil, ErrProcessNotFound
|
||||
return nil, err
|
||||
}
|
||||
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) {
|
||||
go func(r runner.AttachedRunner) {
|
||||
defer c.clientWaitGroup.Done()
|
||||
|
||||
// ensure the context is cancelled when we're done
|
||||
defer c.ctxCancel()
|
||||
|
||||
// Wait for the process to die
|
||||
pidWait(pid)
|
||||
r.Wait(context.Background())
|
||||
|
||||
// Log so we can see it
|
||||
c.logger.Debug("reattached plugin process exited")
|
||||
@@ -843,7 +993,7 @@ func (c *Client) reattach() (net.Addr, error) {
|
||||
c.l.Lock()
|
||||
defer c.l.Unlock()
|
||||
c.exited = true
|
||||
}(p.Pid)
|
||||
}(r)
|
||||
|
||||
// Set the address and protocol
|
||||
c.address = c.config.Reattach.Addr
|
||||
@@ -855,13 +1005,12 @@ func (c *Client) reattach() (net.Addr, error) {
|
||||
|
||||
if c.config.Reattach.Test {
|
||||
c.negotiatedVersion = c.config.Reattach.ProtocolVersion
|
||||
}
|
||||
|
||||
// If we're in test mode, we do NOT set the process. This avoids the
|
||||
// process being killed (the only purpose we have for c.process), since
|
||||
// in test mode the process is responsible for exiting on its own.
|
||||
if !c.config.Reattach.Test {
|
||||
c.process = p
|
||||
} else {
|
||||
// If we're in test mode, we do NOT set the runner. This avoids the
|
||||
// runner being killed (the only purpose we have for setting c.runner
|
||||
// when reattaching), since in test mode the process is responsible for
|
||||
// exiting on its own.
|
||||
c.runner = r
|
||||
}
|
||||
|
||||
return c.address, nil
|
||||
@@ -900,6 +1049,9 @@ func (c *Client) checkProtoVersion(protoVersion string) (int, PluginSet, error)
|
||||
//
|
||||
// If this returns nil then the process hasn't been started yet. Please
|
||||
// call Start or Client before calling this.
|
||||
//
|
||||
// Clients who specified a RunnerFunc will need to populate their own
|
||||
// ReattachFunc in the returned ReattachConfig before it can be used.
|
||||
func (c *Client) ReattachConfig() *ReattachConfig {
|
||||
c.l.Lock()
|
||||
defer c.l.Unlock()
|
||||
@@ -917,11 +1069,16 @@ func (c *Client) ReattachConfig() *ReattachConfig {
|
||||
return c.config.Reattach
|
||||
}
|
||||
|
||||
return &ReattachConfig{
|
||||
reattach := &ReattachConfig{
|
||||
Protocol: c.protocol,
|
||||
Addr: c.address,
|
||||
Pid: c.config.Cmd.Process.Pid,
|
||||
}
|
||||
|
||||
if c.config.Cmd != nil && c.config.Cmd.Process != nil {
|
||||
reattach.Pid = c.config.Cmd.Process.Pid
|
||||
}
|
||||
|
||||
return reattach
|
||||
}
|
||||
|
||||
// Protocol returns the protocol of server on the remote end. This will
|
||||
@@ -957,11 +1114,24 @@ func netAddrDialer(addr net.Addr) func(string, time.Duration) (net.Conn, error)
|
||||
// dialer is compatible with grpc.WithDialer and creates the connection
|
||||
// to the plugin.
|
||||
func (c *Client) dialer(_ string, timeout time.Duration) (net.Conn, error) {
|
||||
conn, err := netAddrDialer(c.address)("", timeout)
|
||||
muxer, err := c.getGRPCMuxer(c.address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var conn net.Conn
|
||||
if muxer.Enabled() {
|
||||
conn, err = muxer.Dial()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
conn, err = netAddrDialer(c.address)("", timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a TLS config we wrap our connection. We only do this
|
||||
// for net/rpc since gRPC uses its own mechanism for TLS.
|
||||
if c.protocol == ProtocolNetRPC && c.config.TLSConfig != nil {
|
||||
@@ -971,14 +1141,28 @@ func (c *Client) dialer(_ string, timeout time.Duration) (net.Conn, error) {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
var stdErrBufferSize = 64 * 1024
|
||||
func (c *Client) getGRPCMuxer(addr net.Addr) (*grpcmux.GRPCClientMuxer, error) {
|
||||
if c.protocol != ProtocolGRPC || !c.config.GRPCBrokerMultiplex {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *Client) logStderr(r io.Reader) {
|
||||
var err error
|
||||
c.grpcMuxerOnce.Do(func() {
|
||||
c.grpcMuxer, err = grpcmux.NewGRPCClientMuxer(c.logger, addr)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.grpcMuxer, nil
|
||||
}
|
||||
|
||||
func (c *Client) logStderr(name string, r io.Reader) {
|
||||
defer c.clientWaitGroup.Done()
|
||||
defer c.stderrWaitGroup.Done()
|
||||
l := c.logger.Named(filepath.Base(c.config.Cmd.Path))
|
||||
l := c.logger.Named(filepath.Base(name))
|
||||
|
||||
reader := bufio.NewReaderSize(r, stdErrBufferSize)
|
||||
reader := bufio.NewReaderSize(r, c.config.PluginLogBufferSize)
|
||||
// continuation indicates the previous line was a prefix
|
||||
continuation := false
|
||||
|
||||
|
||||
Reference in New Issue
Block a user