summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/hashicorp/go-plugin/server.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/go-plugin/server.go')
-rw-r--r--vendor/github.com/hashicorp/go-plugin/server.go112
1 files changed, 98 insertions, 14 deletions
diff --git a/vendor/github.com/hashicorp/go-plugin/server.go b/vendor/github.com/hashicorp/go-plugin/server.go
index 1e808b99e..6de90485f 100644
--- a/vendor/github.com/hashicorp/go-plugin/server.go
+++ b/vendor/github.com/hashicorp/go-plugin/server.go
@@ -11,7 +11,9 @@ import (
"os"
"os/signal"
"runtime"
+ "sort"
"strconv"
+ "strings"
"sync/atomic"
"github.com/hashicorp/go-hclog"
@@ -36,6 +38,8 @@ type HandshakeConfig struct {
// ProtocolVersion is the version that clients must match on to
// agree they can communicate. This should match the ProtocolVersion
// set on ClientConfig when using a plugin.
+ // This field is not required if VersionedPlugins are being used in the
+ // Client or Server configurations.
ProtocolVersion uint
// MagicCookieKey and value are used as a very basic verification
@@ -46,6 +50,10 @@ type HandshakeConfig struct {
MagicCookieValue string
}
+// PluginSet is a set of plugins provided to be registered in the plugin
+// server.
+type PluginSet map[string]Plugin
+
// ServeConfig configures what sorts of plugins are served.
type ServeConfig struct {
// HandshakeConfig is the configuration that must match clients.
@@ -55,7 +63,13 @@ type ServeConfig struct {
TLSProvider func() (*tls.Config, error)
// Plugins are the plugins that are served.
- Plugins map[string]Plugin
+ // The implied version of this PluginSet is the Handshake.ProtocolVersion.
+ Plugins PluginSet
+
+ // VersionedPlugins is a map of PluginSets for specific protocol versions.
+ // These can be used to negotiate a compatible version between client and
+ // server. If this is set, Handshake.ProtocolVersion is not required.
+ VersionedPlugins map[int]PluginSet
// GRPCServer should be non-nil to enable serving the plugins over
// gRPC. This is a function to create the server when needed with the
@@ -72,14 +86,80 @@ type ServeConfig struct {
Logger hclog.Logger
}
-// Protocol returns the protocol that this server should speak.
-func (c *ServeConfig) Protocol() Protocol {
- result := ProtocolNetRPC
- if c.GRPCServer != nil {
- result = ProtocolGRPC
+// protocolVersion determines the protocol version and plugin set to be used by
+// the server. In the event that there is no suitable version, the last version
+// in the config is returned leaving the client to report the incompatibility.
+func protocolVersion(opts *ServeConfig) (int, Protocol, PluginSet) {
+ protoVersion := int(opts.ProtocolVersion)
+ pluginSet := opts.Plugins
+ protoType := ProtocolNetRPC
+ // check if the client sent a list of acceptable versions
+ var clientVersions []int
+ if vs := os.Getenv("PLUGIN_PROTOCOL_VERSIONS"); vs != "" {
+ for _, s := range strings.Split(vs, ",") {
+ v, err := strconv.Atoi(s)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "server sent invalid plugin version %q", s)
+ continue
+ }
+ clientVersions = append(clientVersions, v)
+ }
+ }
+
+ // we want to iterate in reverse order, to ensure we match the newest
+ // compatible plugin version.
+ sort.Sort(sort.Reverse(sort.IntSlice(clientVersions)))
+
+ // set the old un-versioned fields as if they were versioned plugins
+ if opts.VersionedPlugins == nil {
+ opts.VersionedPlugins = make(map[int]PluginSet)
+ }
+
+ if pluginSet != nil {
+ opts.VersionedPlugins[protoVersion] = pluginSet
}
- return result
+ // sort the version to make sure we match the latest first
+ var versions []int
+ for v := range opts.VersionedPlugins {
+ versions = append(versions, v)
+ }
+
+ sort.Sort(sort.Reverse(sort.IntSlice(versions)))
+
+ // see if we have multiple versions of Plugins to choose from
+ for _, version := range versions {
+ // record each version, since we guarantee that this returns valid
+ // values even if they are not a protocol match.
+ protoVersion = version
+ pluginSet = opts.VersionedPlugins[version]
+
+ // all plugins in a set must use the same transport, so check the first
+ // for the protocol type
+ for _, p := range pluginSet {
+ switch p.(type) {
+ case GRPCPlugin:
+ protoType = ProtocolGRPC
+ default:
+ protoType = ProtocolNetRPC
+ }
+ break
+ }
+
+ for _, clientVersion := range clientVersions {
+ if clientVersion == protoVersion {
+ return protoVersion, protoType, pluginSet
+ }
+ }
+ }
+
+ // Return the lowest version as the fallback.
+ // Since we iterated over all the versions in reverse order above, these
+ // values are from the lowest version number plugins (which may be from
+ // a combination of the Handshake.ProtocolVersion and ServeConfig.Plugins
+ // fields). This allows serving the oldest version of our plugins to a
+ // legacy client that did not send a PLUGIN_PROTOCOL_VERSIONS list.
+ return protoVersion, protoType, pluginSet
}
// Serve serves the plugins given by ServeConfig.
@@ -107,6 +187,10 @@ func Serve(opts *ServeConfig) {
os.Exit(1)
}
+ // negotiate the version and plugins
+ // start with default version in the handshake config
+ protoVersion, protoType, pluginSet := protocolVersion(opts)
+
// Logging goes to the original stderr
log.SetOutput(os.Stderr)
@@ -160,7 +244,7 @@ func Serve(opts *ServeConfig) {
// Build the server type
var server ServerProtocol
- switch opts.Protocol() {
+ switch protoType {
case ProtocolNetRPC:
// If we have a TLS configuration then we wrap the listener
// ourselves and do it at that level.
@@ -170,7 +254,7 @@ func Serve(opts *ServeConfig) {
// Create the RPC server to dispense
server = &RPCServer{
- Plugins: opts.Plugins,
+ Plugins: pluginSet,
Stdout: stdout_r,
Stderr: stderr_r,
DoneCh: doneCh,
@@ -179,7 +263,7 @@ func Serve(opts *ServeConfig) {
case ProtocolGRPC:
// Create the gRPC server
server = &GRPCServer{
- Plugins: opts.Plugins,
+ Plugins: pluginSet,
Server: opts.GRPCServer,
TLS: tlsConfig,
Stdout: stdout_r,
@@ -188,7 +272,7 @@ func Serve(opts *ServeConfig) {
}
default:
- panic("unknown server protocol: " + opts.Protocol())
+ panic("unknown server protocol: " + protoType)
}
// Initialize the servers
@@ -208,13 +292,13 @@ func Serve(opts *ServeConfig) {
logger.Debug("plugin address", "network", listener.Addr().Network(), "address", listener.Addr().String())
- // Output the address and service name to stdout so that core can bring it up.
+ // Output the address and service name to stdout so that the client can bring it up.
fmt.Printf("%d|%d|%s|%s|%s%s\n",
CoreProtocolVersion,
- opts.ProtocolVersion,
+ protoVersion,
listener.Addr().Network(),
listener.Addr().String(),
- opts.Protocol(),
+ protoType,
extra)
os.Stdout.Sync()