Skip to content

Commit fb9fca8

Browse files
authored
fix: Ensure terraform tests have a cache path and logger (coder#3161)
* fix: Ensure terraform tests have a cache path and logger * fix: Protect against concurrent `terraform init`
1 parent ad20b23 commit fb9fca8

File tree

4 files changed

+33
-28
lines changed

4 files changed

+33
-28
lines changed

provisioner/terraform/executor.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
)
2424

2525
type executor struct {
26+
initMu sync.Locker
2627
binaryPath string
2728
cachePath string
2829
workdir string
@@ -142,6 +143,19 @@ func (e executor) init(ctx context.Context, logr logger) error {
142143
"-no-color",
143144
"-input=false",
144145
}
146+
147+
// When cache path is set, we must protect against multiple calls
148+
// to `terraform init`.
149+
//
150+
// From the Terraform documentation:
151+
// Note: The plugin cache directory is not guaranteed to be
152+
// concurrency safe. The provider installer's behavior in
153+
// environments with multiple terraform init calls is undefined.
154+
if e.cachePath != "" {
155+
e.initMu.Lock()
156+
defer e.initMu.Unlock()
157+
}
158+
145159
return e.execWriteOutput(ctx, args, e.basicEnv(), outWriter, errWriter)
146160
}
147161

provisioner/terraform/parse_test.go

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,20 @@
33
package terraform_test
44

55
import (
6-
"context"
76
"encoding/json"
87
"os"
98
"path/filepath"
109
"testing"
1110

12-
"github.com/stretchr/testify/assert"
1311
"github.com/stretchr/testify/require"
1412

15-
"github.com/coder/coder/provisioner/terraform"
16-
"github.com/coder/coder/provisionersdk"
1713
"github.com/coder/coder/provisionersdk/proto"
1814
)
1915

2016
func TestParse(t *testing.T) {
2117
t.Parallel()
2218

23-
// Create an in-memory provisioner to communicate with.
24-
client, server := provisionersdk.TransportPipe()
25-
ctx, cancelFunc := context.WithCancel(context.Background())
26-
t.Cleanup(func() {
27-
_ = client.Close()
28-
_ = server.Close()
29-
cancelFunc()
30-
})
31-
go func() {
32-
err := terraform.Serve(ctx, &terraform.ServeOptions{
33-
ServeOptions: &provisionersdk.ServeOptions{
34-
Listener: server,
35-
},
36-
})
37-
assert.NoError(t, err)
38-
}()
39-
api := proto.NewDRPCProvisionerClient(provisionersdk.Conn(client))
19+
ctx, api := setupProvisioner(t)
4020

4121
testCases := []struct {
4222
Name string
@@ -175,7 +155,8 @@ func TestParse(t *testing.T) {
175155
DefaultDestination: &proto.ParameterDestination{
176156
Scheme: proto.ParameterDestination_PROVISIONER_VARIABLE,
177157
},
178-
}},
158+
},
159+
},
179160
},
180161
},
181162
},

provisioner/terraform/provision_test.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,25 @@ import (
2323
)
2424

2525
func setupProvisioner(t *testing.T) (context.Context, proto.DRPCProvisionerClient) {
26+
cachePath := t.TempDir()
2627
client, server := provisionersdk.TransportPipe()
2728
ctx, cancelFunc := context.WithCancel(context.Background())
29+
serverErr := make(chan error, 1)
2830
t.Cleanup(func() {
2931
_ = client.Close()
3032
_ = server.Close()
3133
cancelFunc()
34+
err := <-serverErr
35+
assert.NoError(t, err)
3236
})
3337
go func() {
34-
err := terraform.Serve(ctx, &terraform.ServeOptions{
38+
serverErr <- terraform.Serve(ctx, &terraform.ServeOptions{
3539
ServeOptions: &provisionersdk.ServeOptions{
3640
Listener: server,
3741
},
38-
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
42+
CachePath: cachePath,
43+
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
3944
})
40-
assert.NoError(t, err)
4145
}()
4246
api := proto.NewDRPCProvisionerClient(provisionersdk.Conn(client))
4347
return ctx, api

provisioner/terraform/serve.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package terraform
33
import (
44
"context"
55
"path/filepath"
6+
"sync"
67

78
"github.com/cli/safeexec"
89
"github.com/hashicorp/go-version"
@@ -109,15 +110,20 @@ func Serve(ctx context.Context, options *ServeOptions) error {
109110
}
110111

111112
type server struct {
113+
// initMu protects against executors running `terraform init`
114+
// concurrently when cache path is set.
115+
initMu sync.Mutex
116+
112117
binaryPath string
113118
cachePath string
114119
logger slog.Logger
115120
}
116121

117-
func (t server) executor(workdir string) executor {
122+
func (s *server) executor(workdir string) executor {
118123
return executor{
119-
binaryPath: t.binaryPath,
120-
cachePath: t.cachePath,
124+
initMu: &s.initMu,
125+
binaryPath: s.binaryPath,
126+
cachePath: s.cachePath,
121127
workdir: workdir,
122128
}
123129
}

0 commit comments

Comments
 (0)