terraform: use ProtocolVersion from unmanaged providers' reattachConfig to chose the correct PluginClient (#28190)
* add/use ProtocolVersion with unmanaged providers reattach config
This commit is contained in:
parent
bd4f8d7dea
commit
4928e1dd01
|
@ -13,9 +13,12 @@ import (
|
|||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/terraform/internal/e2e"
|
||||
"github.com/hashicorp/terraform/internal/grpcwrap"
|
||||
tfplugin "github.com/hashicorp/terraform/internal/plugin"
|
||||
simple "github.com/hashicorp/terraform/internal/provider-simple"
|
||||
proto "github.com/hashicorp/terraform/internal/tfplugin5"
|
||||
tfplugin5 "github.com/hashicorp/terraform/internal/plugin"
|
||||
tfplugin "github.com/hashicorp/terraform/internal/plugin6"
|
||||
simple5 "github.com/hashicorp/terraform/internal/provider-simple"
|
||||
simple "github.com/hashicorp/terraform/internal/provider-simple-v6"
|
||||
proto5 "github.com/hashicorp/terraform/internal/tfplugin5"
|
||||
proto "github.com/hashicorp/terraform/internal/tfplugin6"
|
||||
)
|
||||
|
||||
// The tests in this file are for the "unmanaged provider workflow", which
|
||||
|
@ -29,10 +32,11 @@ import (
|
|||
// checked for correctness of the operations themselves.
|
||||
|
||||
type reattachConfig struct {
|
||||
Protocol string
|
||||
Pid int
|
||||
Test bool
|
||||
Addr reattachConfigAddr
|
||||
Protocol string
|
||||
ProtocolVersion int
|
||||
Pid int
|
||||
Test bool
|
||||
Addr reattachConfigAddr
|
||||
}
|
||||
|
||||
type reattachConfigAddr struct {
|
||||
|
@ -89,6 +93,55 @@ func (p *providerServer) ResetApplyResourceChangeCalled() {
|
|||
p.applyResourceChangeCalled = false
|
||||
}
|
||||
|
||||
type providerServer5 struct {
|
||||
sync.Mutex
|
||||
proto5.ProviderServer
|
||||
planResourceChangeCalled bool
|
||||
applyResourceChangeCalled bool
|
||||
}
|
||||
|
||||
func (p *providerServer5) PlanResourceChange(ctx context.Context, req *proto5.PlanResourceChange_Request) (*proto5.PlanResourceChange_Response, error) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
p.planResourceChangeCalled = true
|
||||
return p.ProviderServer.PlanResourceChange(ctx, req)
|
||||
}
|
||||
|
||||
func (p *providerServer5) ApplyResourceChange(ctx context.Context, req *proto5.ApplyResourceChange_Request) (*proto5.ApplyResourceChange_Response, error) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
p.applyResourceChangeCalled = true
|
||||
return p.ProviderServer.ApplyResourceChange(ctx, req)
|
||||
}
|
||||
|
||||
func (p *providerServer5) PlanResourceChangeCalled() bool {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
return p.planResourceChangeCalled
|
||||
}
|
||||
func (p *providerServer5) ResetPlanResourceChangeCalled() {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
p.planResourceChangeCalled = false
|
||||
}
|
||||
|
||||
func (p *providerServer5) ApplyResourceChangeCalled() bool {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
return p.applyResourceChangeCalled
|
||||
}
|
||||
func (p *providerServer5) ResetApplyResourceChangeCalled() {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
p.applyResourceChangeCalled = false
|
||||
}
|
||||
|
||||
func TestUnmanagedSeparatePlan(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -99,7 +152,7 @@ func TestUnmanagedSeparatePlan(t *testing.T) {
|
|||
reattachCh := make(chan *plugin.ReattachConfig)
|
||||
closeCh := make(chan struct{})
|
||||
provider := &providerServer{
|
||||
ProviderServer: grpcwrap.Provider(simple.Provider()),
|
||||
ProviderServer: grpcwrap.Provider6(simple.Provider()),
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
@ -116,7 +169,7 @@ func TestUnmanagedSeparatePlan(t *testing.T) {
|
|||
},
|
||||
GRPCServer: plugin.DefaultGRPCServer,
|
||||
VersionedPlugins: map[int]plugin.PluginSet{
|
||||
5: plugin.PluginSet{
|
||||
6: {
|
||||
"provider": &tfplugin.GRPCProviderPlugin{
|
||||
GRPCProvider: func() proto.ProviderServer {
|
||||
return provider
|
||||
|
@ -130,10 +183,117 @@ func TestUnmanagedSeparatePlan(t *testing.T) {
|
|||
t.Fatalf("no reattach config received")
|
||||
}
|
||||
reattachStr, err := json.Marshal(map[string]reattachConfig{
|
||||
"hashicorp/test": reattachConfig{
|
||||
Protocol: string(config.Protocol),
|
||||
Pid: config.Pid,
|
||||
Test: true,
|
||||
"hashicorp/test": {
|
||||
Protocol: string(config.Protocol),
|
||||
ProtocolVersion: 6,
|
||||
Pid: config.Pid,
|
||||
Test: true,
|
||||
Addr: reattachConfigAddr{
|
||||
Network: config.Addr.Network(),
|
||||
String: config.Addr.String(),
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tf.AddEnv("TF_REATTACH_PROVIDERS=" + string(reattachStr))
|
||||
|
||||
//// INIT
|
||||
stdout, stderr, err := tf.Run("init")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected init error: %s\nstderr:\n%s", err, stderr)
|
||||
}
|
||||
|
||||
// Make sure we didn't download the binary
|
||||
if strings.Contains(stdout, "Installing hashicorp/test v") {
|
||||
t.Errorf("test provider download message is present in init output:\n%s", stdout)
|
||||
}
|
||||
if tf.FileExists(filepath.Join(".terraform", "plugins", "registry.terraform.io", "hashicorp", "test")) {
|
||||
t.Errorf("test provider binary found in .terraform dir")
|
||||
}
|
||||
|
||||
//// PLAN
|
||||
_, stderr, err = tf.Run("plan", "-out=tfplan")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected plan error: %s\nstderr:\n%s", err, stderr)
|
||||
}
|
||||
|
||||
if !provider.PlanResourceChangeCalled() {
|
||||
t.Error("PlanResourceChange not called on un-managed provider")
|
||||
}
|
||||
|
||||
//// APPLY
|
||||
_, stderr, err = tf.Run("apply", "tfplan")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected apply error: %s\nstderr:\n%s", err, stderr)
|
||||
}
|
||||
|
||||
if !provider.ApplyResourceChangeCalled() {
|
||||
t.Error("ApplyResourceChange not called on un-managed provider")
|
||||
}
|
||||
provider.ResetApplyResourceChangeCalled()
|
||||
|
||||
//// DESTROY
|
||||
_, stderr, err = tf.Run("destroy", "-auto-approve")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected destroy error: %s\nstderr:\n%s", err, stderr)
|
||||
}
|
||||
|
||||
if !provider.ApplyResourceChangeCalled() {
|
||||
t.Error("ApplyResourceChange (destroy) not called on in-process provider")
|
||||
}
|
||||
cancel()
|
||||
<-closeCh
|
||||
}
|
||||
|
||||
func TestUnmanagedSeparatePlan_proto5(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fixturePath := filepath.Join("testdata", "test-provider")
|
||||
tf := e2e.NewBinary(terraformBin, fixturePath)
|
||||
defer tf.Close()
|
||||
|
||||
reattachCh := make(chan *plugin.ReattachConfig)
|
||||
closeCh := make(chan struct{})
|
||||
provider := &providerServer5{
|
||||
ProviderServer: grpcwrap.Provider(simple5.Provider()),
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
go plugin.Serve(&plugin.ServeConfig{
|
||||
Logger: hclog.New(&hclog.LoggerOptions{
|
||||
Name: "plugintest",
|
||||
Level: hclog.Trace,
|
||||
Output: ioutil.Discard,
|
||||
}),
|
||||
Test: &plugin.ServeTestConfig{
|
||||
Context: ctx,
|
||||
ReattachConfigCh: reattachCh,
|
||||
CloseCh: closeCh,
|
||||
},
|
||||
GRPCServer: plugin.DefaultGRPCServer,
|
||||
VersionedPlugins: map[int]plugin.PluginSet{
|
||||
5: {
|
||||
"provider": &tfplugin5.GRPCProviderPlugin{
|
||||
GRPCProvider: func() proto5.ProviderServer {
|
||||
return provider
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
config := <-reattachCh
|
||||
if config == nil {
|
||||
t.Fatalf("no reattach config received")
|
||||
}
|
||||
reattachStr, err := json.Marshal(map[string]reattachConfig{
|
||||
"hashicorp/test": {
|
||||
Protocol: string(config.Protocol),
|
||||
ProtocolVersion: 5,
|
||||
Pid: config.Pid,
|
||||
Test: true,
|
||||
Addr: reattachConfigAddr{
|
||||
Network: config.Addr.Network(),
|
||||
String: config.Addr.String(),
|
||||
|
@ -145,7 +305,6 @@ func TestUnmanagedSeparatePlan(t *testing.T) {
|
|||
}
|
||||
|
||||
tf.AddEnv("TF_REATTACH_PROVIDERS=" + string(reattachStr))
|
||||
tf.AddEnv("PLUGIN_PROTOCOL_VERSION=5")
|
||||
|
||||
//// INIT
|
||||
stdout, stderr, err := tf.Run("init")
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
@ -401,7 +402,6 @@ func devOverrideProviderFactory(provider addrs.Provider, localDir getproviders.P
|
|||
// running, and implements providers.Interface against it.
|
||||
func unmanagedProviderFactory(provider addrs.Provider, reattach *plugin.ReattachConfig) providers.Factory {
|
||||
return func() (providers.Interface, error) {
|
||||
|
||||
config := &plugin.ClientConfig{
|
||||
HandshakeConfig: tfplugin.Handshake,
|
||||
Logger: logging.NewProviderLogger("unmanaged."),
|
||||
|
@ -411,12 +411,20 @@ func unmanagedProviderFactory(provider addrs.Provider, reattach *plugin.Reattach
|
|||
SyncStdout: logging.PluginOutputMonitor(fmt.Sprintf("%s:stdout", provider)),
|
||||
SyncStderr: logging.PluginOutputMonitor(fmt.Sprintf("%s:stderr", provider)),
|
||||
}
|
||||
// TODO: we probably shouldn't hardcode the protocol version
|
||||
// here, but it'll do for now, because only one protocol
|
||||
// version is supported. Eventually, we'll probably want to
|
||||
// sneak it into the JSON ReattachConfigs.
|
||||
if plugins, ok := tfplugin.VersionedPlugins[5]; !ok {
|
||||
return nil, fmt.Errorf("no supported plugins for protocol 5")
|
||||
|
||||
if reattach.ProtocolVersion == 0 {
|
||||
// As of the 0.15 release, sdk.v2 doesn't include the protocol
|
||||
// version in the ReattachConfig (only recently added to
|
||||
// go-plugin), so client.NegotiatedVersion() always returns 0. We
|
||||
// assume that an unmanaged provider reporting protocol version 0 is
|
||||
// actually using proto v5 for backwards compatibility.
|
||||
if defaultPlugins, ok := tfplugin.VersionedPlugins[5]; ok {
|
||||
config.Plugins = defaultPlugins
|
||||
} else {
|
||||
return nil, errors.New("no supported plugins for protocol 0")
|
||||
}
|
||||
} else if plugins, ok := tfplugin.VersionedPlugins[reattach.ProtocolVersion]; !ok {
|
||||
return nil, fmt.Errorf("no supported plugins for protocol %d", reattach.ProtocolVersion)
|
||||
} else {
|
||||
config.Plugins = plugins
|
||||
}
|
||||
|
@ -432,8 +440,25 @@ func unmanagedProviderFactory(provider addrs.Provider, reattach *plugin.Reattach
|
|||
return nil, err
|
||||
}
|
||||
|
||||
p := raw.(*tfplugin.GRPCProvider)
|
||||
return p, nil
|
||||
// store the client so that the plugin can kill the child process
|
||||
protoVer := client.NegotiatedVersion()
|
||||
switch protoVer {
|
||||
case 0, 5:
|
||||
// As of the 0.15 release, sdk.v2 doesn't include the protocol
|
||||
// version in the ReattachConfig (only recently added to
|
||||
// go-plugin), so client.NegotiatedVersion() always returns 0. We
|
||||
// assume that an unmanaged provider reporting protocol version 0 is
|
||||
// actually using proto v5 for backwards compatibility.
|
||||
p := raw.(*tfplugin.GRPCProvider)
|
||||
p.PluginClient = client
|
||||
return p, nil
|
||||
case 6:
|
||||
p := raw.(*tfplugin6.GRPCProvider)
|
||||
p.PluginClient = client
|
||||
return p, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported protocol version %d", protoVer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
14
main.go
14
main.go
|
@ -450,8 +450,9 @@ func parseReattachProviders(in string) (map[addrs.Provider]*plugin.ReattachConfi
|
|||
unmanagedProviders := map[addrs.Provider]*plugin.ReattachConfig{}
|
||||
if in != "" {
|
||||
type reattachConfig struct {
|
||||
Protocol string
|
||||
Addr struct {
|
||||
Protocol string
|
||||
ProtocolVersion int
|
||||
Addr struct {
|
||||
Network string
|
||||
String string
|
||||
}
|
||||
|
@ -484,10 +485,11 @@ func parseReattachProviders(in string) (map[addrs.Provider]*plugin.ReattachConfi
|
|||
return unmanagedProviders, fmt.Errorf("Unknown address type %q for %q", c.Addr.Network, p)
|
||||
}
|
||||
unmanagedProviders[a] = &plugin.ReattachConfig{
|
||||
Protocol: plugin.Protocol(c.Protocol),
|
||||
Pid: c.Pid,
|
||||
Test: c.Test,
|
||||
Addr: addr,
|
||||
Protocol: plugin.Protocol(c.Protocol),
|
||||
ProtocolVersion: c.ProtocolVersion,
|
||||
Pid: c.Pid,
|
||||
Test: c.Test,
|
||||
Addr: addr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue