fix: Ensure terraform tests have a cache path and logger (#3161)

* fix: Ensure terraform tests have a cache path and logger

* fix: Protect against concurrent `terraform init`
This commit is contained in:
Mathias Fredriksson
2022-08-04 20:37:07 +03:00
committed by GitHub
parent ad20b23178
commit fb9fca8bc9
4 changed files with 33 additions and 28 deletions

View File

@ -23,6 +23,7 @@ import (
) )
type executor struct { type executor struct {
initMu sync.Locker
binaryPath string binaryPath string
cachePath string cachePath string
workdir string workdir string
@ -142,6 +143,19 @@ func (e executor) init(ctx context.Context, logr logger) error {
"-no-color", "-no-color",
"-input=false", "-input=false",
} }
// When cache path is set, we must protect against multiple calls
// to `terraform init`.
//
// From the Terraform documentation:
// Note: The plugin cache directory is not guaranteed to be
// concurrency safe. The provider installer's behavior in
// environments with multiple terraform init calls is undefined.
if e.cachePath != "" {
e.initMu.Lock()
defer e.initMu.Unlock()
}
return e.execWriteOutput(ctx, args, e.basicEnv(), outWriter, errWriter) return e.execWriteOutput(ctx, args, e.basicEnv(), outWriter, errWriter)
} }

View File

@ -3,40 +3,20 @@
package terraform_test package terraform_test
import ( import (
"context"
"encoding/json" "encoding/json"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/coder/coder/provisioner/terraform"
"github.com/coder/coder/provisionersdk"
"github.com/coder/coder/provisionersdk/proto" "github.com/coder/coder/provisionersdk/proto"
) )
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
t.Parallel() t.Parallel()
// Create an in-memory provisioner to communicate with. ctx, api := setupProvisioner(t)
client, server := provisionersdk.TransportPipe()
ctx, cancelFunc := context.WithCancel(context.Background())
t.Cleanup(func() {
_ = client.Close()
_ = server.Close()
cancelFunc()
})
go func() {
err := terraform.Serve(ctx, &terraform.ServeOptions{
ServeOptions: &provisionersdk.ServeOptions{
Listener: server,
},
})
assert.NoError(t, err)
}()
api := proto.NewDRPCProvisionerClient(provisionersdk.Conn(client))
testCases := []struct { testCases := []struct {
Name string Name string
@ -175,7 +155,8 @@ func TestParse(t *testing.T) {
DefaultDestination: &proto.ParameterDestination{ DefaultDestination: &proto.ParameterDestination{
Scheme: proto.ParameterDestination_PROVISIONER_VARIABLE, Scheme: proto.ParameterDestination_PROVISIONER_VARIABLE,
}, },
}}, },
},
}, },
}, },
}, },

View File

@ -23,21 +23,25 @@ import (
) )
func setupProvisioner(t *testing.T) (context.Context, proto.DRPCProvisionerClient) { func setupProvisioner(t *testing.T) (context.Context, proto.DRPCProvisionerClient) {
cachePath := t.TempDir()
client, server := provisionersdk.TransportPipe() client, server := provisionersdk.TransportPipe()
ctx, cancelFunc := context.WithCancel(context.Background()) ctx, cancelFunc := context.WithCancel(context.Background())
serverErr := make(chan error, 1)
t.Cleanup(func() { t.Cleanup(func() {
_ = client.Close() _ = client.Close()
_ = server.Close() _ = server.Close()
cancelFunc() cancelFunc()
err := <-serverErr
assert.NoError(t, err)
}) })
go func() { go func() {
err := terraform.Serve(ctx, &terraform.ServeOptions{ serverErr <- terraform.Serve(ctx, &terraform.ServeOptions{
ServeOptions: &provisionersdk.ServeOptions{ ServeOptions: &provisionersdk.ServeOptions{
Listener: server, Listener: server,
}, },
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug), CachePath: cachePath,
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
}) })
assert.NoError(t, err)
}() }()
api := proto.NewDRPCProvisionerClient(provisionersdk.Conn(client)) api := proto.NewDRPCProvisionerClient(provisionersdk.Conn(client))
return ctx, api return ctx, api

View File

@ -3,6 +3,7 @@ package terraform
import ( import (
"context" "context"
"path/filepath" "path/filepath"
"sync"
"github.com/cli/safeexec" "github.com/cli/safeexec"
"github.com/hashicorp/go-version" "github.com/hashicorp/go-version"
@ -109,15 +110,20 @@ func Serve(ctx context.Context, options *ServeOptions) error {
} }
type server struct { type server struct {
// initMu protects against executors running `terraform init`
// concurrently when cache path is set.
initMu sync.Mutex
binaryPath string binaryPath string
cachePath string cachePath string
logger slog.Logger logger slog.Logger
} }
func (t server) executor(workdir string) executor { func (s *server) executor(workdir string) executor {
return executor{ return executor{
binaryPath: t.binaryPath, initMu: &s.initMu,
cachePath: t.cachePath, binaryPath: s.binaryPath,
cachePath: s.cachePath,
workdir: workdir, workdir: workdir,
} }
} }