From baf3d33085f19f4c0a38226215e96841a28f0499 Mon Sep 17 00:00:00 2001 From: Alper Rifat Ulucinar Date: Wed, 20 Apr 2022 13:31:44 +0300 Subject: [PATCH 1/2] Start shared native provider using a magic cookie Signed-off-by: Alper Rifat Ulucinar --- pkg/terraform/provider_runner.go | 31 ++++++++++++++++++++++----- pkg/terraform/provider_runner_test.go | 14 +++++++----- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/pkg/terraform/provider_runner.go b/pkg/terraform/provider_runner.go index db8d33b..163e6c1 100644 --- a/pkg/terraform/provider_runner.go +++ b/pkg/terraform/provider_runner.go @@ -18,6 +18,8 @@ package terraform import ( "bufio" + "fmt" + "os" "regexp" "sync" "time" @@ -33,9 +35,13 @@ const ( errFmtTimeout = "timed out after %v while waiting for the reattach configuration string" // an example value would be: '{"registry.terraform.io/hashicorp/aws": {"Protocol": "grpc", "ProtocolVersion":5, "Pid":... "Addr":{"Network": "unix","String": "..."}}}' - envReattachConfig = "TF_REATTACH_PROVIDERS" - regexReattachLine = envReattachConfig + `='(.*)'` - reattachTimeout = 1 * time.Minute + fmtReattachEnv = `{"%s":{"Protocol":"grpc","ProtocolVersion":%d,"Pid":%d,"Test": true,"Addr":{"Network": "unix","String": "%s"}}}` + fmtSetEnv = "%s=%s" + envReattachConfig = "TF_REATTACH_PROVIDERS" + envMagicCookie = "TF_PLUGIN_MAGIC_COOKIE" + defaultMagicCookie = "d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2" + regexReattachLine = `.*unix\|(.*)\|grpc.*` + reattachTimeout = 1 * time.Minute ) // ProviderRunner is the interface for running @@ -64,6 +70,9 @@ type SharedProvider struct { nativeProviderPath string nativeProviderArgs []string reattachConfig string + nativeProviderName string + protocolVersion int + pluginMagicCookie string logger logging.Logger executor exec.Interface clock clock.Clock @@ -87,12 +96,23 @@ func WithNativeProviderExecutor(e exec.Interface) SharedGRPCRunnerOption { } } +// WithNativeProviderMagicCookie sets the magic cookie for +// the native provider plugin to run. +func WithNativeProviderMagicCookie(cookie string) SharedGRPCRunnerOption { + return func(sr *SharedProvider) { + sr.pluginMagicCookie = cookie + } +} + // NewSharedProvider instantiates a SharedProvider with an // OS executor using the supplied logger -func NewSharedProvider(l logging.Logger, nativeProviderPath string, opts ...SharedGRPCRunnerOption) *SharedProvider { +func NewSharedProvider(l logging.Logger, nativeProviderPath, nativeProviderName string, protocolVersion int, opts ...SharedGRPCRunnerOption) *SharedProvider { sr := &SharedProvider{ logger: l, nativeProviderPath: nativeProviderPath, + nativeProviderName: nativeProviderName, + protocolVersion: protocolVersion, + pluginMagicCookie: defaultMagicCookie, executor: exec.New(), clock: clock.RealClock{}, mu: &sync.Mutex{}, @@ -133,6 +153,7 @@ func (sr *SharedProvider) Start() (string, error) { //nolint:gocyclo }() //#nosec G204 no user input cmd := sr.executor.Command(sr.nativeProviderPath, sr.nativeProviderArgs...) + cmd.SetEnv(append(os.Environ(), fmt.Sprintf(fmtSetEnv, envMagicCookie, sr.pluginMagicCookie))) stdout, err := cmd.StdoutPipe() if err != nil { errCh <- err @@ -149,7 +170,7 @@ func (sr *SharedProvider) Start() (string, error) { //nolint:gocyclo if matches == nil { continue } - reattachCh <- matches[1] + reattachCh <- fmt.Sprintf(fmtReattachEnv, sr.nativeProviderName, sr.protocolVersion, os.Getpid(), matches[1]) break } if err := cmd.Wait(); err != nil { diff --git a/pkg/terraform/provider_runner_test.go b/pkg/terraform/provider_runner_test.go index c382695..35fdace 100644 --- a/pkg/terraform/provider_runner_test.go +++ b/pkg/terraform/provider_runner_test.go @@ -17,7 +17,9 @@ limitations under the License. package terraform import ( + "fmt" "io" + "os" "reflect" "strings" "sync" @@ -35,9 +37,11 @@ import ( func TestStartSharedServer(t *testing.T) { testPath := "path" + testName := "provider-test" + testProtocol := 5 testArgs := []string{"arg1", "arg2"} - testReattachConfig1 := `TF_REATTACH_PROVIDERS='test1'` - testReattachConfig2 := `TF_REATTACH_PROVIDERS='test2'` + testReattachConfig1 := `1|5|unix|test1|grpc|` + testReattachConfig2 := `1|5|unix|test2|grpc|` testErr := errors.New("boom") type args struct { runner ProviderRunner @@ -57,11 +61,11 @@ func TestStartSharedServer(t *testing.T) { }, "SuccessfullyStarted": { args: args{ - runner: NewSharedProvider(logging.NewNopLogger(), testPath, WithNativeProviderArgs(testArgs...), + runner: NewSharedProvider(logging.NewNopLogger(), testPath, testName, testProtocol, WithNativeProviderArgs(testArgs...), WithNativeProviderExecutor(newExecutorWithStoutPipe(testReattachConfig1, nil))), }, want: want{ - reattachConfig: "test1", + reattachConfig: fmt.Sprintf(`{"provider-test":{"Protocol":"grpc","ProtocolVersion":5,"Pid":%d,"Test": true,"Addr":{"Network": "unix","String": "test1"}}}`, os.Getpid()), }, }, "AlreadyRunning": { @@ -80,7 +84,7 @@ func TestStartSharedServer(t *testing.T) { }, "NativeProviderError": { args: args{ - runner: NewSharedProvider(logging.NewNopLogger(), testPath, + runner: NewSharedProvider(logging.NewNopLogger(), testPath, testName, testProtocol, WithNativeProviderExecutor(newExecutorWithStoutPipe(testReattachConfig1, testErr))), }, want: want{ From 508b40c56f6fe7b75746d127aa4599927c847dd4 Mon Sep 17 00:00:00 2001 From: Alper Rifat Ulucinar Date: Thu, 21 Apr 2022 03:00:13 +0300 Subject: [PATCH 2/2] Default gRPC protocol version between Terraform CLI and native provider to 5 Signed-off-by: Alper Rifat Ulucinar --- pkg/terraform/provider_runner.go | 34 ++++++++++++++------------- pkg/terraform/provider_runner_test.go | 5 ++-- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/pkg/terraform/provider_runner.go b/pkg/terraform/provider_runner.go index 163e6c1..ede107b 100644 --- a/pkg/terraform/provider_runner.go +++ b/pkg/terraform/provider_runner.go @@ -35,13 +35,17 @@ const ( errFmtTimeout = "timed out after %v while waiting for the reattach configuration string" // an example value would be: '{"registry.terraform.io/hashicorp/aws": {"Protocol": "grpc", "ProtocolVersion":5, "Pid":... "Addr":{"Network": "unix","String": "..."}}}' - fmtReattachEnv = `{"%s":{"Protocol":"grpc","ProtocolVersion":%d,"Pid":%d,"Test": true,"Addr":{"Network": "unix","String": "%s"}}}` - fmtSetEnv = "%s=%s" - envReattachConfig = "TF_REATTACH_PROVIDERS" - envMagicCookie = "TF_PLUGIN_MAGIC_COOKIE" - defaultMagicCookie = "d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2" - regexReattachLine = `.*unix\|(.*)\|grpc.*` - reattachTimeout = 1 * time.Minute + fmtReattachEnv = `{"%s":{"Protocol":"grpc","ProtocolVersion":%d,"Pid":%d,"Test": true,"Addr":{"Network": "unix","String": "%s"}}}` + fmtSetEnv = "%s=%s" + envReattachConfig = "TF_REATTACH_PROVIDERS" + envMagicCookie = "TF_PLUGIN_MAGIC_COOKIE" + // Terraform provider plugin expects this magic cookie in its environment + // (as the value of key TF_PLUGIN_MAGIC_COOKIE): + // https://github.com/hashicorp/terraform/blob/d35bc0531255b496beb5d932f185cbcdb2d61a99/internal/plugin/serve.go#L33 + valMagicCookie = "d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2" + defaultProtocolVersion = 5 + regexReattachLine = `.*unix\|(.*)\|grpc.*` + reattachTimeout = 1 * time.Minute ) // ProviderRunner is the interface for running @@ -72,7 +76,6 @@ type SharedProvider struct { reattachConfig string nativeProviderName string protocolVersion int - pluginMagicCookie string logger logging.Logger executor exec.Interface clock clock.Clock @@ -96,23 +99,22 @@ func WithNativeProviderExecutor(e exec.Interface) SharedGRPCRunnerOption { } } -// WithNativeProviderMagicCookie sets the magic cookie for -// the native provider plugin to run. -func WithNativeProviderMagicCookie(cookie string) SharedGRPCRunnerOption { +// WithProtocolVersion sets the gRPC protocol version in use between +// the Terraform CLI and the native provider. +func WithProtocolVersion(protocolVersion int) SharedGRPCRunnerOption { return func(sr *SharedProvider) { - sr.pluginMagicCookie = cookie + sr.protocolVersion = protocolVersion } } // NewSharedProvider instantiates a SharedProvider with an // OS executor using the supplied logger -func NewSharedProvider(l logging.Logger, nativeProviderPath, nativeProviderName string, protocolVersion int, opts ...SharedGRPCRunnerOption) *SharedProvider { +func NewSharedProvider(l logging.Logger, nativeProviderPath, nativeProviderName string, opts ...SharedGRPCRunnerOption) *SharedProvider { sr := &SharedProvider{ logger: l, nativeProviderPath: nativeProviderPath, nativeProviderName: nativeProviderName, - protocolVersion: protocolVersion, - pluginMagicCookie: defaultMagicCookie, + protocolVersion: defaultProtocolVersion, executor: exec.New(), clock: clock.RealClock{}, mu: &sync.Mutex{}, @@ -153,7 +155,7 @@ func (sr *SharedProvider) Start() (string, error) { //nolint:gocyclo }() //#nosec G204 no user input cmd := sr.executor.Command(sr.nativeProviderPath, sr.nativeProviderArgs...) - cmd.SetEnv(append(os.Environ(), fmt.Sprintf(fmtSetEnv, envMagicCookie, sr.pluginMagicCookie))) + cmd.SetEnv(append(os.Environ(), fmt.Sprintf(fmtSetEnv, envMagicCookie, valMagicCookie))) stdout, err := cmd.StdoutPipe() if err != nil { errCh <- err diff --git a/pkg/terraform/provider_runner_test.go b/pkg/terraform/provider_runner_test.go index 35fdace..7d12012 100644 --- a/pkg/terraform/provider_runner_test.go +++ b/pkg/terraform/provider_runner_test.go @@ -38,7 +38,6 @@ import ( func TestStartSharedServer(t *testing.T) { testPath := "path" testName := "provider-test" - testProtocol := 5 testArgs := []string{"arg1", "arg2"} testReattachConfig1 := `1|5|unix|test1|grpc|` testReattachConfig2 := `1|5|unix|test2|grpc|` @@ -61,7 +60,7 @@ func TestStartSharedServer(t *testing.T) { }, "SuccessfullyStarted": { args: args{ - runner: NewSharedProvider(logging.NewNopLogger(), testPath, testName, testProtocol, WithNativeProviderArgs(testArgs...), + runner: NewSharedProvider(logging.NewNopLogger(), testPath, testName, WithNativeProviderArgs(testArgs...), WithNativeProviderExecutor(newExecutorWithStoutPipe(testReattachConfig1, nil))), }, want: want{ @@ -84,7 +83,7 @@ func TestStartSharedServer(t *testing.T) { }, "NativeProviderError": { args: args{ - runner: NewSharedProvider(logging.NewNopLogger(), testPath, testName, testProtocol, + runner: NewSharedProvider(logging.NewNopLogger(), testPath, testName, WithNativeProviderExecutor(newExecutorWithStoutPipe(testReattachConfig1, testErr))), }, want: want{