mirror of
https://github.com/coder/coder.git
synced 2025-07-15 22:20:27 +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 {
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
}},
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user