From e82e59e4a1788ad9e99c19eafe0e8ecf55565834 Mon Sep 17 00:00:00 2001 From: Katy Moe Date: Fri, 7 May 2021 15:03:41 +0100 Subject: [PATCH 1/4] add ProtoV6ProviderFactories to TestCase In order to support protocol v6 providers in the test suite, we add another field to TestCase along the same lines as ProtoV5ProviderFactories. This is plumbed through as yet another argument in runProviderCommand. We hope that by the time another protocol major version is released, a new, more flexible test framework will be available. plugin.ServeOpts and plugin.DebugServe also expected all provider servers to be v5 servers, so we also add the GRPCProviderV6Func field to ServeOpts to keep it backwards compatible. If both GRPCProviderFunc and GRPCProviderV6Func are set, the v6 server will be ignored. --- helper/resource/plugin.go | 94 +++++++++++++++++++-- helper/resource/testing.go | 6 ++ helper/resource/testing_new.go | 13 +-- helper/resource/testing_new_config.go | 28 +++--- helper/resource/testing_new_import_state.go | 8 +- plugin/debug.go | 16 ++-- plugin/serve.go | 44 +++++++--- 7 files changed, 160 insertions(+), 49 deletions(-) diff --git a/helper/resource/plugin.go b/helper/resource/plugin.go index bef3a442f19..d5e5c86e3c9 100644 --- a/helper/resource/plugin.go +++ b/helper/resource/plugin.go @@ -12,13 +12,14 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/terraform-exec/tfexec" "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugintest" "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" testing "github.com/mitchellh/go-testing-interface" ) -func runProviderCommand(t testing.T, f func() error, wd *plugintest.WorkingDir, factories map[string]func() (*schema.Provider, error), v5factories map[string]func() (tfprotov5.ProviderServer, error)) error { +func runProviderCommand(t testing.T, f func() error, wd *plugintest.WorkingDir, factories map[string]func() (*schema.Provider, error), v5factories map[string]func() (tfprotov5.ProviderServer, error), v6factories map[string]func() (tfprotov6.ProviderServer, error)) error { // don't point to this as a test failure location // point to whatever called it t.Helper() @@ -94,9 +95,10 @@ func runProviderCommand(t testing.T, f func() error, wd *plugintest.WorkingDir, return fmt.Errorf("unable to serve provider %q: %w", providerName, err) } tfexecConfig := tfexec.ReattachConfig{ - Protocol: config.Protocol, - Pid: config.Pid, - Test: config.Test, + Protocol: config.Protocol, + ProtocolVersion: config.ProtocolVersion, + Pid: config.Pid, + Test: config.Test, Addr: tfexec.ReattachConfigAddr{ Network: config.Addr.Network, String: config.Addr.String, @@ -120,7 +122,7 @@ func runProviderCommand(t testing.T, f func() error, wd *plugintest.WorkingDir, } } - // Now spin up gRPC servers for every plugin-go provider factory + // Now spin up gRPC servers for every protov5 provider factory // in the same way. for providerName, factory := range v5factories { // providerName may be returned as terraform-provider-foo, and @@ -170,9 +172,85 @@ func runProviderCommand(t testing.T, f func() error, wd *plugintest.WorkingDir, return fmt.Errorf("unable to serve provider %q: %w", providerName, err) } tfexecConfig := tfexec.ReattachConfig{ - Protocol: config.Protocol, - Pid: config.Pid, - Test: config.Test, + Protocol: config.Protocol, + ProtocolVersion: config.ProtocolVersion, + Pid: config.Pid, + Test: config.Test, + Addr: tfexec.ReattachConfigAddr{ + Network: config.Addr.Network, + String: config.Addr.String, + }, + } + + // when the provider exits, remove one from the waitgroup + // so we can track when everything is done + go func(c <-chan struct{}) { + <-c + wg.Done() + }(closeCh) + + // set our provider's reattachinfo in our map, once + // for every namespace that different Terraform versions + // may expect. + for _, ns := range namespaces { + reattachString := strings.TrimSuffix(host, "/") + "/" + + strings.TrimSuffix(ns, "/") + "/" + + providerName + reattachInfo[reattachString] = tfexecConfig + } + } + + // Now spin up gRPC servers for every protov6 provider factory + // in the same way. + for providerName, factory := range v6factories { + // providerName may be returned as terraform-provider-foo, and + // we need just foo. So let's fix that. + providerName = strings.TrimPrefix(providerName, "terraform-provider-") + + // If the user has already registered this provider in + // ProviderFactories or ProtoV5ProviderFactories, they made a + // mistake and we should exit early. + for _, ns := range namespaces { + reattachString := strings.TrimSuffix(host, "/") + "/" + + strings.TrimSuffix(ns, "/") + "/" + + providerName + if _, ok := reattachInfo[reattachString]; ok { + return fmt.Errorf("Provider %s registered in both TestCase.ProtoV6ProviderFactories and either TestCase.ProviderFactories or TestCase.ProtoV5ProviderFactories: please use one of the three, or supply a muxed provider to TestCase.ProtoV5ProviderFactories.", providerName) + } + } + + provider, err := factory() + if err != nil { + return fmt.Errorf("unable to create provider %q from factory: %w", providerName, err) + } + + // keep track of the running factory, so we can make sure it's + // shut down. + wg.Add(1) + + opts := &plugin.ServeOpts{ + GRPCProviderV6Func: func() tfprotov6.ProviderServer { + return provider + }, + Logger: hclog.New(&hclog.LoggerOptions{ + Name: "plugintest", + Level: hclog.Trace, + Output: ioutil.Discard, + }), + NoLogOutputOverride: true, + } + + // let's actually start the provider server + config, closeCh, err := plugin.DebugServe(ctx, opts) + if err != nil { + return fmt.Errorf("unable to serve provider %q: %w", providerName, err) + } + + tfexecConfig := tfexec.ReattachConfig{ + Protocol: config.Protocol, + ProtocolVersion: config.ProtocolVersion, + Pid: config.Pid, + Test: config.Test, Addr: tfexec.ReattachConfigAddr{ Network: config.Addr.Network, String: config.Addr.String, diff --git a/helper/resource/testing.go b/helper/resource/testing.go index fc9644b371b..2df01bb1282 100644 --- a/helper/resource/testing.go +++ b/helper/resource/testing.go @@ -14,6 +14,7 @@ import ( testing "github.com/mitchellh/go-testing-interface" "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/logging" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/internal/addrs" @@ -305,6 +306,11 @@ type TestCase struct { // ProviderServer interface. ProtoV5ProviderFactories map[string]func() (tfprotov5.ProviderServer, error) + // ProtoV6ProviderFactories serves the same purpose as ProviderFactories, + // but for protocol v6 providers defined using the terraform-plugin-go + // ProviderServer interface. + ProtoV6ProviderFactories map[string]func() (tfprotov6.ProviderServer, error) + // Providers is the ResourceProvider that will be under test. // // Deprecated: Providers is deprecated, please use ProviderFactories diff --git a/helper/resource/testing_new.go b/helper/resource/testing_new.go index c9d90dd2159..9f4bc54a934 100644 --- a/helper/resource/testing_new.go +++ b/helper/resource/testing_new.go @@ -11,17 +11,18 @@ import ( testing "github.com/mitchellh/go-testing-interface" "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugintest" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) -func runPostTestDestroy(t testing.T, c TestCase, wd *plugintest.WorkingDir, factories map[string]func() (*schema.Provider, error), v5factories map[string]func() (tfprotov5.ProviderServer, error), statePreDestroy *terraform.State) error { +func runPostTestDestroy(t testing.T, c TestCase, wd *plugintest.WorkingDir, factories map[string]func() (*schema.Provider, error), v5factories map[string]func() (tfprotov5.ProviderServer, error), v6factories map[string]func() (tfprotov6.ProviderServer, error), statePreDestroy *terraform.State) error { t.Helper() err := runProviderCommand(t, func() error { return wd.Destroy() - }, wd, factories, v5factories) + }, wd, factories, v5factories, v6factories) if err != nil { return err } @@ -51,14 +52,14 @@ func runNewTest(t testing.T, c TestCase, helper *plugintest.Helper) { return err } return nil - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { t.Fatalf("Error retrieving state, there may be dangling resources: %s", err.Error()) return } if !stateIsEmpty(statePreDestroy) { - err := runPostTestDestroy(t, c, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, statePreDestroy) + err := runPostTestDestroy(t, c, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories, statePreDestroy) if err != nil { t.Fatalf("Error running post-test destroy, there may be dangling resources: %s", err.Error()) } @@ -78,7 +79,7 @@ func runNewTest(t testing.T, c TestCase, helper *plugintest.Helper) { } err = runProviderCommand(t, func() error { return wd.Init() - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { t.Fatalf("Error running init: %s", err.Error()) return @@ -218,7 +219,7 @@ func testIDRefresh(c TestCase, t testing.T, wd *plugintest.WorkingDir, step Test return err } return nil - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { return err } diff --git a/helper/resource/testing_new_config.go b/helper/resource/testing_new_config.go index 0e218f60217..c58a921d79d 100644 --- a/helper/resource/testing_new_config.go +++ b/helper/resource/testing_new_config.go @@ -26,7 +26,7 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step return err } return nil - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { return err } @@ -44,7 +44,7 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step // failing to do this will result in data sources not being updated err = runProviderCommand(t, func() error { return wd.Refresh() - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { return fmt.Errorf("Error running pre-apply refresh: %w", err) } @@ -59,7 +59,7 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step return wd.CreateDestroyPlan() } return wd.CreatePlan() - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { return fmt.Errorf("Error running pre-apply plan: %w", err) } @@ -74,7 +74,7 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step return err } return nil - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { return fmt.Errorf("Error retrieving pre-apply state: %w", err) } @@ -82,7 +82,7 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step // Apply the diff, creating real resources err = runProviderCommand(t, func() error { return wd.Apply() - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { if step.Destroy { return fmt.Errorf("Error running destroy: %w", err) @@ -98,7 +98,7 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step return err } return nil - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { return fmt.Errorf("Error retrieving state after apply: %w", err) } @@ -126,7 +126,7 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step return wd.CreateDestroyPlan() } return wd.CreatePlan() - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { return fmt.Errorf("Error running post-apply plan: %w", err) } @@ -136,7 +136,7 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step var err error plan, err = wd.SavedPlan() return err - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { return fmt.Errorf("Error retrieving post-apply plan: %w", err) } @@ -147,7 +147,7 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step var err error stdout, err = wd.SavedPlanRawStdout() return err - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { return fmt.Errorf("Error retrieving formatted plan output: %w", err) } @@ -158,7 +158,7 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step if !step.Destroy || (step.Destroy && !step.PreventPostDestroyRefresh) { err := runProviderCommand(t, func() error { return wd.Refresh() - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { return fmt.Errorf("Error running post-apply refresh: %w", err) } @@ -170,7 +170,7 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step return wd.CreateDestroyPlan() } return wd.CreatePlan() - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { return fmt.Errorf("Error running second post-apply plan: %w", err) } @@ -179,7 +179,7 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step var err error plan, err = wd.SavedPlan() return err - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { return fmt.Errorf("Error retrieving second post-apply plan: %w", err) } @@ -191,7 +191,7 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step var err error stdout, err = wd.SavedPlanRawStdout() return err - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { return fmt.Errorf("Error retrieving formatted second plan output: %w", err) } @@ -210,7 +210,7 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step return err } return nil - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { return err } diff --git a/helper/resource/testing_new_import_state.go b/helper/resource/testing_new_import_state.go index 52487ad85cb..502c4844309 100644 --- a/helper/resource/testing_new_import_state.go +++ b/helper/resource/testing_new_import_state.go @@ -30,7 +30,7 @@ func testStepNewImportState(t testing.T, c TestCase, helper *plugintest.Helper, return err } return nil - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { t.Fatalf("Error getting state: %s", err) } @@ -71,14 +71,14 @@ func testStepNewImportState(t testing.T, c TestCase, helper *plugintest.Helper, err = runProviderCommand(t, func() error { return importWd.Init() - }, importWd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, importWd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { t.Fatalf("Error running init: %s", err) } err = runProviderCommand(t, func() error { return importWd.Import(step.ResourceName, importId) - }, importWd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, importWd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { return err } @@ -90,7 +90,7 @@ func testStepNewImportState(t testing.T, c TestCase, helper *plugintest.Helper, return err } return nil - }, importWd, c.ProviderFactories, c.ProtoV5ProviderFactories) + }, importWd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) if err != nil { t.Fatalf("Error getting state: %s", err) } diff --git a/plugin/debug.go b/plugin/debug.go index fb4f81400f8..171451ca7f9 100644 --- a/plugin/debug.go +++ b/plugin/debug.go @@ -17,10 +17,11 @@ import ( // ReattachConfig holds the information Terraform needs to be able to attach // itself to a provider process, so it can drive the process. type ReattachConfig struct { - Protocol string - Pid int - Test bool - Addr ReattachConfigAddr + Protocol string + ProtocolVersion int + Pid int + Test bool + Addr ReattachConfigAddr } // ReattachConfigAddr is a JSON-encoding friendly version of net.Addr. @@ -56,9 +57,10 @@ func DebugServe(ctx context.Context, opts *ServeOpts) (ReattachConfig, <-chan st } return ReattachConfig{ - Protocol: string(config.Protocol), - Pid: config.Pid, - Test: config.Test, + Protocol: string(config.Protocol), + ProtocolVersion: config.ProtocolVersion, + Pid: config.Pid, + Test: config.Test, Addr: ReattachConfigAddr{ Network: config.Addr.Network(), String: config.Addr.String(), diff --git a/plugin/serve.go b/plugin/serve.go index baaab2d1d1c..127889986ea 100644 --- a/plugin/serve.go +++ b/plugin/serve.go @@ -9,6 +9,8 @@ import ( "github.com/hashicorp/terraform-plugin-go/tfprotov5" tf5server "github.com/hashicorp/terraform-plugin-go/tfprotov5/server" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + tf6server "github.com/hashicorp/terraform-plugin-go/tfprotov6/server" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -27,6 +29,7 @@ var Handshake = plugin.HandshakeConfig{ type ProviderFunc func() *schema.Provider type GRPCProviderFunc func() tfprotov5.ProviderServer +type GRPCProviderV6Func func() tfprotov6.ProviderServer // ServeOpts are the configurations to serve a plugin. type ServeOpts struct { @@ -36,6 +39,8 @@ type ServeOpts struct { // added to the GRPC functions when possible. GRPCProviderFunc GRPCProviderFunc + GRPCProviderV6Func GRPCProviderV6Func + // Logger is the logger that go-plugin will use. Logger hclog.Logger @@ -78,10 +83,19 @@ func Serve(opts *ServeOpts) { } } - provider := opts.GRPCProviderFunc() - plugin.Serve(&plugin.ServeConfig{ + serveConfig := plugin.ServeConfig{ HandshakeConfig: Handshake, - VersionedPlugins: map[int]plugin.PluginSet{ + GRPCServer: func(opts []grpc.ServerOption) *grpc.Server { + return grpc.NewServer(opts...) + }, + Logger: opts.Logger, + Test: opts.TestConfig, + } + + // assume we have either a v5 or a v6 provider + if opts.GRPCProviderFunc != nil { + provider := opts.GRPCProviderFunc() + serveConfig.VersionedPlugins = map[int]plugin.PluginSet{ 5: { ProviderPluginName: &tf5server.GRPCProviderPlugin{ GRPCProvider: func() tfprotov5.ProviderServer { @@ -89,11 +103,21 @@ func Serve(opts *ServeOpts) { }, }, }, - }, - GRPCServer: func(opts []grpc.ServerOption) *grpc.Server { - return grpc.NewServer(opts...) - }, - Logger: opts.Logger, - Test: opts.TestConfig, - }) + } + + } else if opts.GRPCProviderV6Func != nil { + provider := opts.GRPCProviderV6Func() + serveConfig.VersionedPlugins = map[int]plugin.PluginSet{ + 6: { + ProviderPluginName: &tf6server.GRPCProviderPlugin{ + GRPCProvider: func() tfprotov6.ProviderServer { + return provider + }, + }, + }, + } + + } + + plugin.Serve(&serveConfig) } From c25ea473b1ddc51945fa11a492af0bccbc731fca Mon Sep 17 00:00:00 2001 From: Katy Moe Date: Tue, 25 May 2021 17:12:22 +0100 Subject: [PATCH 2/4] bump go-plugin to v1.4.1 --- go.mod | 2 +- go.sum | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 0e5955c5086..41f3ce31423 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/go-hclog v0.15.0 github.com/hashicorp/go-multierror v1.0.0 - github.com/hashicorp/go-plugin v1.4.0 + github.com/hashicorp/go-plugin v1.4.1 github.com/hashicorp/go-uuid v1.0.1 github.com/hashicorp/go-version v1.3.0 github.com/hashicorp/hcl/v2 v2.3.0 diff --git a/go.sum b/go.sum index 7427627d394..b2de9f63678 100644 --- a/go.sum +++ b/go.sum @@ -180,8 +180,8 @@ github.com/hashicorp/go-hclog v0.15.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39 github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-plugin v1.3.0/go.mod h1:F9eH4LrE/ZsRdbwhfjs9k9HoDUwAHnYtXdgmf1AVNs0= -github.com/hashicorp/go-plugin v1.4.0 h1:b0O7rs5uiJ99Iu9HugEzsM67afboErkHUWddUSpUO3A= -github.com/hashicorp/go-plugin v1.4.0/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= +github.com/hashicorp/go-plugin v1.4.1 h1:6UltRQlLN9iZO513VveELp5xyaFxVD2+1OVylE+2E+w= +github.com/hashicorp/go-plugin v1.4.1/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -249,7 +249,6 @@ github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW1 github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= -github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= @@ -329,7 +328,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= @@ -440,7 +438,6 @@ golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492 h1:Paq34FxTluEPvVyayQqMPgHm+vTOrIifmcYxFBx9TLg= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79 h1:RX8C8PRZc2hTIod4ds8ij+/4RQX3AqhYj3uOHmyaz4E= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= From 6ba412a3148f3760508cba26547cebe7e07ad43a Mon Sep 17 00:00:00 2001 From: Katy Moe Date: Fri, 25 Jun 2021 13:59:23 +0100 Subject: [PATCH 3/4] refactor: factories struct --- helper/resource/plugin.go | 14 +++-- helper/resource/testing_new.go | 20 ++++-- helper/resource/testing_new_config.go | 70 ++++++++++++++++----- helper/resource/testing_new_import_state.go | 20 ++++-- 4 files changed, 98 insertions(+), 26 deletions(-) diff --git a/helper/resource/plugin.go b/helper/resource/plugin.go index d5e5c86e3c9..2eb08b4e4ce 100644 --- a/helper/resource/plugin.go +++ b/helper/resource/plugin.go @@ -19,7 +19,13 @@ import ( testing "github.com/mitchellh/go-testing-interface" ) -func runProviderCommand(t testing.T, f func() error, wd *plugintest.WorkingDir, factories map[string]func() (*schema.Provider, error), v5factories map[string]func() (tfprotov5.ProviderServer, error), v6factories map[string]func() (tfprotov6.ProviderServer, error)) error { +type providerFactories struct { + legacy map[string]func() (*schema.Provider, error) + protov5 map[string]func() (tfprotov5.ProviderServer, error) + protov6 map[string]func() (tfprotov6.ProviderServer, error) +} + +func runProviderCommand(t testing.T, f func() error, wd *plugintest.WorkingDir, factories providerFactories) error { // don't point to this as a test failure location // point to whatever called it t.Helper() @@ -59,7 +65,7 @@ func runProviderCommand(t testing.T, f func() error, wd *plugintest.WorkingDir, // WaitGroup to listen for all of the close channels. var wg sync.WaitGroup reattachInfo := map[string]tfexec.ReattachConfig{} - for providerName, factory := range factories { + for providerName, factory := range factories.legacy { // providerName may be returned as terraform-provider-foo, and // we need just foo. So let's fix that. providerName = strings.TrimPrefix(providerName, "terraform-provider-") @@ -124,7 +130,7 @@ func runProviderCommand(t testing.T, f func() error, wd *plugintest.WorkingDir, // Now spin up gRPC servers for every protov5 provider factory // in the same way. - for providerName, factory := range v5factories { + for providerName, factory := range factories.protov5 { // providerName may be returned as terraform-provider-foo, and // we need just foo. So let's fix that. providerName = strings.TrimPrefix(providerName, "terraform-provider-") @@ -202,7 +208,7 @@ func runProviderCommand(t testing.T, f func() error, wd *plugintest.WorkingDir, // Now spin up gRPC servers for every protov6 provider factory // in the same way. - for providerName, factory := range v6factories { + for providerName, factory := range factories.protov6 { // providerName may be returned as terraform-provider-foo, and // we need just foo. So let's fix that. providerName = strings.TrimPrefix(providerName, "terraform-provider-") diff --git a/helper/resource/testing_new.go b/helper/resource/testing_new.go index 9f4bc54a934..1dc73906b33 100644 --- a/helper/resource/testing_new.go +++ b/helper/resource/testing_new.go @@ -22,7 +22,10 @@ func runPostTestDestroy(t testing.T, c TestCase, wd *plugintest.WorkingDir, fact err := runProviderCommand(t, func() error { return wd.Destroy() - }, wd, factories, v5factories, v6factories) + }, wd, providerFactories{ + legacy: factories, + protov5: v5factories, + protov6: v6factories}) if err != nil { return err } @@ -52,7 +55,10 @@ func runNewTest(t testing.T, c TestCase, helper *plugintest.Helper) { return err } return nil - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, wd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { t.Fatalf("Error retrieving state, there may be dangling resources: %s", err.Error()) return @@ -79,7 +85,10 @@ func runNewTest(t testing.T, c TestCase, helper *plugintest.Helper) { } err = runProviderCommand(t, func() error { return wd.Init() - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, wd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { t.Fatalf("Error running init: %s", err.Error()) return @@ -219,7 +228,10 @@ func testIDRefresh(c TestCase, t testing.T, wd *plugintest.WorkingDir, step Test return err } return nil - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, wd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { return err } diff --git a/helper/resource/testing_new_config.go b/helper/resource/testing_new_config.go index c58a921d79d..9900cc3965d 100644 --- a/helper/resource/testing_new_config.go +++ b/helper/resource/testing_new_config.go @@ -26,7 +26,10 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step return err } return nil - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, wd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { return err } @@ -44,7 +47,10 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step // failing to do this will result in data sources not being updated err = runProviderCommand(t, func() error { return wd.Refresh() - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, wd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { return fmt.Errorf("Error running pre-apply refresh: %w", err) } @@ -59,7 +65,10 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step return wd.CreateDestroyPlan() } return wd.CreatePlan() - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, wd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { return fmt.Errorf("Error running pre-apply plan: %w", err) } @@ -74,7 +83,10 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step return err } return nil - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, wd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { return fmt.Errorf("Error retrieving pre-apply state: %w", err) } @@ -82,7 +94,10 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step // Apply the diff, creating real resources err = runProviderCommand(t, func() error { return wd.Apply() - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, wd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { if step.Destroy { return fmt.Errorf("Error running destroy: %w", err) @@ -98,7 +113,10 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step return err } return nil - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, wd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { return fmt.Errorf("Error retrieving state after apply: %w", err) } @@ -126,7 +144,10 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step return wd.CreateDestroyPlan() } return wd.CreatePlan() - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, wd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { return fmt.Errorf("Error running post-apply plan: %w", err) } @@ -136,7 +157,10 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step var err error plan, err = wd.SavedPlan() return err - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, wd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { return fmt.Errorf("Error retrieving post-apply plan: %w", err) } @@ -147,7 +171,10 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step var err error stdout, err = wd.SavedPlanRawStdout() return err - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, wd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { return fmt.Errorf("Error retrieving formatted plan output: %w", err) } @@ -158,7 +185,10 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step if !step.Destroy || (step.Destroy && !step.PreventPostDestroyRefresh) { err := runProviderCommand(t, func() error { return wd.Refresh() - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, wd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { return fmt.Errorf("Error running post-apply refresh: %w", err) } @@ -170,7 +200,10 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step return wd.CreateDestroyPlan() } return wd.CreatePlan() - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, wd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { return fmt.Errorf("Error running second post-apply plan: %w", err) } @@ -179,7 +212,10 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step var err error plan, err = wd.SavedPlan() return err - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, wd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { return fmt.Errorf("Error retrieving second post-apply plan: %w", err) } @@ -191,7 +227,10 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step var err error stdout, err = wd.SavedPlanRawStdout() return err - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, wd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { return fmt.Errorf("Error retrieving formatted second plan output: %w", err) } @@ -210,7 +249,10 @@ func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step return err } return nil - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, wd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { return err } diff --git a/helper/resource/testing_new_import_state.go b/helper/resource/testing_new_import_state.go index 502c4844309..a38c3c7dc42 100644 --- a/helper/resource/testing_new_import_state.go +++ b/helper/resource/testing_new_import_state.go @@ -30,7 +30,10 @@ func testStepNewImportState(t testing.T, c TestCase, helper *plugintest.Helper, return err } return nil - }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, wd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { t.Fatalf("Error getting state: %s", err) } @@ -71,14 +74,20 @@ func testStepNewImportState(t testing.T, c TestCase, helper *plugintest.Helper, err = runProviderCommand(t, func() error { return importWd.Init() - }, importWd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, importWd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { t.Fatalf("Error running init: %s", err) } err = runProviderCommand(t, func() error { return importWd.Import(step.ResourceName, importId) - }, importWd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, importWd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { return err } @@ -90,7 +99,10 @@ func testStepNewImportState(t testing.T, c TestCase, helper *plugintest.Helper, return err } return nil - }, importWd, c.ProviderFactories, c.ProtoV5ProviderFactories, c.ProtoV6ProviderFactories) + }, importWd, providerFactories{ + legacy: c.ProviderFactories, + protov5: c.ProtoV5ProviderFactories, + protov6: c.ProtoV6ProviderFactories}) if err != nil { t.Fatalf("Error getting state: %s", err) } From fe303b1e95a1421e91d25b728044ebde09578746 Mon Sep 17 00:00:00 2001 From: Katy Moe Date: Fri, 25 Jun 2021 14:10:50 +0100 Subject: [PATCH 4/4] document tf version requirement --- helper/resource/testing.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/helper/resource/testing.go b/helper/resource/testing.go index 2df01bb1282..2a03d548e8f 100644 --- a/helper/resource/testing.go +++ b/helper/resource/testing.go @@ -309,6 +309,8 @@ type TestCase struct { // ProtoV6ProviderFactories serves the same purpose as ProviderFactories, // but for protocol v6 providers defined using the terraform-plugin-go // ProviderServer interface. + // The version of Terraform used in acceptance testing must be greater + // than or equal to v0.15.4 to use ProtoV6ProviderFactories. ProtoV6ProviderFactories map[string]func() (tfprotov6.ProviderServer, error) // Providers is the ResourceProvider that will be under test.