mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
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:
committed by
GitHub
parent
ad20b23178
commit
fb9fca8bc9
@@ -23,6 +23,7 @@ import (
|
||||
)
|
||||
|
||||
type executor struct {
|
||||
initMu sync.Locker
|
||||
binaryPath string
|
||||
cachePath string
|
||||
workdir string
|
||||
@@ -142,6 +143,19 @@ func (e executor) init(ctx context.Context, logr logger) error {
|
||||
"-no-color",
|
||||
"-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)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,40 +3,20 @@
|
||||
package terraform_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/provisioner/terraform"
|
||||
"github.com/coder/coder/provisionersdk"
|
||||
"github.com/coder/coder/provisionersdk/proto"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create an in-memory provisioner to communicate with.
|
||||
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))
|
||||
ctx, api := setupProvisioner(t)
|
||||
|
||||
testCases := []struct {
|
||||
Name string
|
||||
@@ -175,7 +155,8 @@ func TestParse(t *testing.T) {
|
||||
DefaultDestination: &proto.ParameterDestination{
|
||||
Scheme: proto.ParameterDestination_PROVISIONER_VARIABLE,
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -23,21 +23,25 @@ import (
|
||||
)
|
||||
|
||||
func setupProvisioner(t *testing.T) (context.Context, proto.DRPCProvisionerClient) {
|
||||
cachePath := t.TempDir()
|
||||
client, server := provisionersdk.TransportPipe()
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
serverErr := make(chan error, 1)
|
||||
t.Cleanup(func() {
|
||||
_ = client.Close()
|
||||
_ = server.Close()
|
||||
cancelFunc()
|
||||
err := <-serverErr
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
go func() {
|
||||
err := terraform.Serve(ctx, &terraform.ServeOptions{
|
||||
serverErr <- terraform.Serve(ctx, &terraform.ServeOptions{
|
||||
ServeOptions: &provisionersdk.ServeOptions{
|
||||
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))
|
||||
return ctx, api
|
||||
|
||||
@@ -3,6 +3,7 @@ package terraform
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/cli/safeexec"
|
||||
"github.com/hashicorp/go-version"
|
||||
@@ -109,15 +110,20 @@ func Serve(ctx context.Context, options *ServeOptions) error {
|
||||
}
|
||||
|
||||
type server struct {
|
||||
// initMu protects against executors running `terraform init`
|
||||
// concurrently when cache path is set.
|
||||
initMu sync.Mutex
|
||||
|
||||
binaryPath string
|
||||
cachePath string
|
||||
logger slog.Logger
|
||||
}
|
||||
|
||||
func (t server) executor(workdir string) executor {
|
||||
func (s *server) executor(workdir string) executor {
|
||||
return executor{
|
||||
binaryPath: t.binaryPath,
|
||||
cachePath: t.cachePath,
|
||||
initMu: &s.initMu,
|
||||
binaryPath: s.binaryPath,
|
||||
cachePath: s.cachePath,
|
||||
workdir: workdir,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user