terraform/plugin/serve.go

115 lines
3.7 KiB
Go

package plugin
import (
"github.com/hashicorp/go-plugin"
grpcplugin "github.com/hashicorp/terraform/helper/plugin"
proto "github.com/hashicorp/terraform/internal/tfplugin5"
"github.com/hashicorp/terraform/terraform"
)
// The constants below are the names of the plugins that can be dispensed
// from the plugin server.
const (
ProviderPluginName = "provider"
ProvisionerPluginName = "provisioner"
)
// Handshake is the HandshakeConfig used to configure clients and servers.
var Handshake = plugin.HandshakeConfig{
// The ProtocolVersion is the version that must match between TF core
// and TF plugins. This should be bumped whenever a change happens in
// one or the other that makes it so that they can't safely communicate.
// This could be adding a new interface value, it could be how
// helper/schema computes diffs, etc.
ProtocolVersion: 4,
// The magic cookie values should NEVER be changed.
MagicCookieKey: "TF_PLUGIN_MAGIC_COOKIE",
MagicCookieValue: "d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2",
}
type ProviderFunc func() terraform.ResourceProvider
type ProvisionerFunc func() terraform.ResourceProvisioner
type GRPCProviderFunc func() proto.ProviderServer
type GRPCProvisionerFunc func() proto.ProvisionerServer
// ServeOpts are the configurations to serve a plugin.
type ServeOpts struct {
ProviderFunc ProviderFunc
ProvisionerFunc ProvisionerFunc
// Wrapped versions of the above plugins will automatically shimmed and
// added to the GRPC functions when possible.
GRPCProviderFunc GRPCProviderFunc
GRPCProvisionerFunc GRPCProvisionerFunc
}
// Serve serves a plugin. This function never returns and should be the final
// function called in the main function of the plugin.
func Serve(opts *ServeOpts) {
// since the plugins may not yet be aware of the new protocol, we
// automatically wrap the plugins in the grpc shims.
if opts.GRPCProviderFunc == nil && opts.ProviderFunc != nil {
provider := grpcplugin.NewGRPCProviderServerShim(opts.ProviderFunc())
// this is almost always going to be a *schema.Provider, but check that
// we got back a valid provider just in case.
if provider != nil {
opts.GRPCProviderFunc = func() proto.ProviderServer {
return provider
}
}
}
if opts.GRPCProvisionerFunc == nil && opts.ProvisionerFunc != nil {
provisioner := grpcplugin.NewGRPCProvisionerServerShim(opts.ProvisionerFunc())
if provisioner != nil {
opts.GRPCProvisionerFunc = func() proto.ProvisionerServer {
return provisioner
}
}
}
plugin.Serve(&plugin.ServeConfig{
HandshakeConfig: Handshake,
VersionedPlugins: pluginSet(opts),
GRPCServer: plugin.DefaultGRPCServer,
})
}
// pluginMap returns the legacy map[string]plugin.Plugin to use for configuring
// a plugin server or client.
func legacyPluginMap(opts *ServeOpts) map[string]plugin.Plugin {
return map[string]plugin.Plugin{
"provider": &ResourceProviderPlugin{
ResourceProvider: opts.ProviderFunc,
},
"provisioner": &ResourceProvisionerPlugin{
ResourceProvisioner: opts.ProvisionerFunc,
},
}
}
func pluginSet(opts *ServeOpts) map[int]plugin.PluginSet {
// Set the legacy netrpc plugins at version 4.
// The oldest version is returned in when executed by a legacy go-plugin
// client.
plugins := map[int]plugin.PluginSet{
4: legacyPluginMap(opts),
}
// add the new protocol versions if they're configured
if opts.GRPCProviderFunc != nil || opts.GRPCProvisionerFunc != nil {
plugins[5] = plugin.PluginSet{}
if opts.GRPCProviderFunc != nil {
plugins[5]["provider"] = &GRPCProviderPlugin{
GRPCProvider: opts.GRPCProviderFunc,
}
}
if opts.GRPCProvisionerFunc != nil {
plugins[5]["provisioner"] = &GRPCProvisionerPlugin{
GRPCProvisioner: opts.GRPCProvisionerFunc,
}
}
}
return plugins
}