Skip to content
This repository has been archived by the owner on Dec 15, 2022. It is now read-only.

Commit

Permalink
Merge pull request #275 from ulucinar/grpc-configure-regex
Browse files Browse the repository at this point in the history
Start shared native provider using a magic cookie
  • Loading branch information
ulucinar authored Apr 21, 2022
2 parents 8274f23 + 508b40c commit 4f9db89
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 9 deletions.
31 changes: 27 additions & 4 deletions pkg/terraform/provider_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package terraform

import (
"bufio"
"fmt"
"os"
"regexp"
"sync"
"time"
Expand All @@ -33,9 +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"
regexReattachLine = envReattachConfig + `='(.*)'`
reattachTimeout = 1 * time.Minute
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
Expand Down Expand Up @@ -64,6 +74,8 @@ type SharedProvider struct {
nativeProviderPath string
nativeProviderArgs []string
reattachConfig string
nativeProviderName string
protocolVersion int
logger logging.Logger
executor exec.Interface
clock clock.Clock
Expand All @@ -87,12 +99,22 @@ func WithNativeProviderExecutor(e exec.Interface) 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.protocolVersion = protocolVersion
}
}

// 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, opts ...SharedGRPCRunnerOption) *SharedProvider {
sr := &SharedProvider{
logger: l,
nativeProviderPath: nativeProviderPath,
nativeProviderName: nativeProviderName,
protocolVersion: defaultProtocolVersion,
executor: exec.New(),
clock: clock.RealClock{},
mu: &sync.Mutex{},
Expand Down Expand Up @@ -133,6 +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, valMagicCookie)))
stdout, err := cmd.StdoutPipe()
if err != nil {
errCh <- err
Expand All @@ -149,7 +172,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 {
Expand Down
13 changes: 8 additions & 5 deletions pkg/terraform/provider_runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ limitations under the License.
package terraform

import (
"fmt"
"io"
"os"
"reflect"
"strings"
"sync"
Expand All @@ -35,9 +37,10 @@ import (

func TestStartSharedServer(t *testing.T) {
testPath := "path"
testName := "provider-test"
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
Expand All @@ -57,11 +60,11 @@ func TestStartSharedServer(t *testing.T) {
},
"SuccessfullyStarted": {
args: args{
runner: NewSharedProvider(logging.NewNopLogger(), testPath, WithNativeProviderArgs(testArgs...),
runner: NewSharedProvider(logging.NewNopLogger(), testPath, testName, 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": {
Expand All @@ -80,7 +83,7 @@ func TestStartSharedServer(t *testing.T) {
},
"NativeProviderError": {
args: args{
runner: NewSharedProvider(logging.NewNopLogger(), testPath,
runner: NewSharedProvider(logging.NewNopLogger(), testPath, testName,
WithNativeProviderExecutor(newExecutorWithStoutPipe(testReattachConfig1, testErr))),
},
want: want{
Expand Down

0 comments on commit 4f9db89

Please sign in to comment.