Skip to content

Commit 16a34fe

Browse files
authored
Accept CLI option for the number of parallel ops in a test run's plan/apply (#36323)
1 parent 8f1b79a commit 16a34fe

File tree

13 files changed

+229
-63
lines changed

13 files changed

+229
-63
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
kind: ENHANCEMENTS
2+
body: Terraform Test command now accepts a -parallelism=n option, which sets the number of parallel operations in a test run's plan/apply operation.
3+
time: 2025-01-23T10:18:38.979866+01:00
4+
custom:
5+
Issue: "34237"

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ require (
2828
github.com/hashicorp/go-plugin v1.6.0
2929
github.com/hashicorp/go-retryablehttp v0.7.7
3030
github.com/hashicorp/go-slug v0.16.3
31-
github.com/hashicorp/go-tfe v1.70.0
31+
github.com/hashicorp/go-tfe v1.74.1
3232
github.com/hashicorp/go-uuid v1.0.3
3333
github.com/hashicorp/go-version v1.7.0
3434
github.com/hashicorp/hcl v1.0.0
3535
github.com/hashicorp/hcl/v2 v2.23.1-0.20250203194505-ba0759438da2
36-
github.com/hashicorp/jsonapi v1.3.1
36+
github.com/hashicorp/jsonapi v1.3.2
3737
github.com/hashicorp/terraform-registry-address v0.2.3
3838
github.com/hashicorp/terraform-svchost v0.1.1
3939
github.com/hashicorp/terraform/internal/backend/remote-state/azure v0.0.0-00010101000000-000000000000
@@ -256,7 +256,7 @@ require (
256256
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
257257
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 // indirect
258258
golang.org/x/sync v0.10.0 // indirect
259-
golang.org/x/time v0.7.0 // indirect
259+
golang.org/x/time v0.9.0 // indirect
260260
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
261261
google.golang.org/api v0.126.0 // indirect
262262
google.golang.org/appengine v1.6.8 // indirect

go.sum

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,8 +1123,8 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX
11231123
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
11241124
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
11251125
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
1126-
github.com/hashicorp/go-tfe v1.70.0 h1:R5a9Z+jdVz6eRWtSLsl1nw+5Qe/swunZcJgeKK5NQtQ=
1127-
github.com/hashicorp/go-tfe v1.70.0/go.mod h1:2rOcdTxXwbWm0W7dCKjC3Ec8KQ+HhW165GiurXNshc4=
1126+
github.com/hashicorp/go-tfe v1.74.1 h1:I/8fOwSYox17IZV7SULIQH0ZRPNL2g/biW6hHWnOTVY=
1127+
github.com/hashicorp/go-tfe v1.74.1/go.mod h1:kGHWMZ3HHjitgqON8nBZ4kPVJ3cLbzM4JMgmNVMs9aQ=
11281128
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
11291129
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
11301130
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
@@ -1141,8 +1141,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
11411141
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
11421142
github.com/hashicorp/hcl/v2 v2.23.1-0.20250203194505-ba0759438da2 h1:JP8y98OtHTujECs4s/HxlKc5yql/RlC99Dt1Iz4R+lM=
11431143
github.com/hashicorp/hcl/v2 v2.23.1-0.20250203194505-ba0759438da2/go.mod h1:k+HgkLpoWu9OS81sy4j1XKDXaWm/rLysG33v5ibdDnc=
1144-
github.com/hashicorp/jsonapi v1.3.1 h1:GtPvnmcWgYwCuDGvYT5VZBHcUyFdq9lSyCzDjn1DdPo=
1145-
github.com/hashicorp/jsonapi v1.3.1/go.mod h1:kWfdn49yCjQvbpnvY1dxxAuAFzISwrrMDQOcu6NsFoM=
1144+
github.com/hashicorp/jsonapi v1.3.2 h1:gP3fX2ZT7qXi+PbwieptzkspIohO2kCSiBUvUTBAbMs=
1145+
github.com/hashicorp/jsonapi v1.3.2/go.mod h1:kWfdn49yCjQvbpnvY1dxxAuAFzISwrrMDQOcu6NsFoM=
11461146
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
11471147
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
11481148
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
@@ -1460,8 +1460,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
14601460
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
14611461
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
14621462
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
1463-
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
1464-
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
1463+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
1464+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
14651465
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
14661466
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.588 h1:DYtBXB7sVc3EOW5horg8j55cLZynhsLYhHrvQ/jXKKM=
14671467
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.588/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
@@ -1917,8 +1917,8 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb
19171917
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
19181918
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
19191919
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
1920-
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
1921-
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
1920+
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
1921+
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
19221922
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
19231923
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
19241924
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

internal/backend/remote-state/kubernetes/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ require (
6464
golang.org/x/sys v0.29.0 // indirect
6565
golang.org/x/term v0.28.0 // indirect
6666
golang.org/x/text v0.21.0 // indirect
67-
golang.org/x/time v0.7.0 // indirect
67+
golang.org/x/time v0.9.0 // indirect
6868
golang.org/x/tools v0.25.0 // indirect
6969
google.golang.org/protobuf v1.34.2 // indirect
7070
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect

internal/backend/remote-state/kubernetes/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -492,8 +492,8 @@ golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
492492
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
493493
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
494494
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
495-
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
496-
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
495+
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
496+
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
497497
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
498498
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
499499
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=

internal/cloud/test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ type TestSuiteRunner struct {
8282
// Verbose tells the runner to print out plan files during each test run.
8383
Verbose bool
8484

85+
// OperationParallelism is the limit Terraform places on total parallel operations
86+
// during the plan or apply command within a single test run.
87+
OperationParallelism int
88+
8589
// Filters restricts which test files will be executed.
8690
Filters []string
8791

@@ -204,6 +208,7 @@ func (runner *TestSuiteRunner) Test() (moduletest.Status, tfdiags.Diagnostics) {
204208
Filters: runner.Filters,
205209
TestDirectory: tfe.String(runner.TestingDirectory),
206210
Verbose: tfe.Bool(runner.Verbose),
211+
Parallelism: tfe.Int(runner.OperationParallelism),
207212
Variables: func() []*tfe.RunVariable {
208213
runVariables := make([]*tfe.RunVariable, 0, len(variables))
209214
for name, value := range variables {

internal/cloud/test_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,81 @@ Success! 2 passed, 0 failed.
103103
}
104104
}
105105

106+
func TestTest_Parallelism(t *testing.T) {
107+
108+
streams, _ := terminal.StreamsForTesting(t)
109+
view := views.NewTest(arguments.ViewHuman, views.NewView(streams))
110+
111+
colorize := mockColorize()
112+
colorize.Disable = true
113+
114+
mock := NewMockClient()
115+
client := &tfe.Client{
116+
ConfigurationVersions: mock.ConfigurationVersions,
117+
Organizations: mock.Organizations,
118+
RegistryModules: mock.RegistryModules,
119+
TestRuns: mock.TestRuns,
120+
}
121+
122+
if _, err := client.Organizations.Create(context.Background(), tfe.OrganizationCreateOptions{
123+
Name: tfe.String("organisation"),
124+
}); err != nil {
125+
t.Fatalf("failed to create organisation: %v", err)
126+
}
127+
128+
if _, err := client.RegistryModules.Create(context.Background(), "organisation", tfe.RegistryModuleCreateOptions{
129+
Name: tfe.String("name"),
130+
Provider: tfe.String("provider"),
131+
RegistryName: "app.terraform.io",
132+
Namespace: "organisation",
133+
}); err != nil {
134+
t.Fatalf("failed to create registry module: %v", err)
135+
}
136+
137+
runner := TestSuiteRunner{
138+
// Configuration data.
139+
ConfigDirectory: "testdata/test",
140+
TestingDirectory: "tests",
141+
Config: nil, // We don't need this for this test.
142+
Source: "app.terraform.io/organisation/name/provider",
143+
144+
// Cancellation controls, we won't be doing any cancellations in this
145+
// test.
146+
Stopped: false,
147+
Cancelled: false,
148+
StoppedCtx: context.Background(),
149+
CancelledCtx: context.Background(),
150+
151+
// Test Options, empty for this test.
152+
GlobalVariables: nil,
153+
Verbose: false,
154+
OperationParallelism: 4,
155+
Filters: nil,
156+
157+
// Outputs
158+
Renderer: &jsonformat.Renderer{
159+
Streams: streams,
160+
Colorize: colorize,
161+
RunningInAutomation: false,
162+
},
163+
View: view,
164+
Streams: streams,
165+
166+
// Networking
167+
Services: nil, // Don't need this when the client is overridden.
168+
clientOverride: client,
169+
}
170+
171+
_, diags := runner.Test()
172+
if len(diags) > 0 {
173+
t.Errorf("found diags and expected none: %s", diags.ErrWithWarnings())
174+
}
175+
176+
if mock.TestRuns.parallelism != 4 {
177+
t.Errorf("expected parallelism to be 4 but was %d", mock.TestRuns.parallelism)
178+
}
179+
}
180+
106181
func TestTest_JSON(t *testing.T) {
107182

108183
streams, done := terminal.StreamsForTesting(t)

internal/cloud/tfe_client_mock.go

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1673,9 +1673,10 @@ type MockTestRuns struct {
16731673
client *MockClient
16741674

16751675
// TestRuns and modules keep track of our tfe.TestRun objects.
1676-
TestRuns map[string]*tfe.TestRun
1677-
modules map[string][]*tfe.TestRun
1678-
logs map[string]string
1676+
TestRuns map[string]*tfe.TestRun
1677+
modules map[string][]*tfe.TestRun
1678+
logs map[string]string
1679+
parallelism int
16791680

16801681
// delayedCancel allows a mock test run to cancel an operation instead of
16811682
// completing an operation. It's used
@@ -1767,6 +1768,9 @@ func (m *MockTestRuns) Create(ctx context.Context, options tfe.TestRunCreateOpti
17671768
"test.log",
17681769
)
17691770
m.modules[tr.RegistryModule.ID] = append(m.modules[tr.RegistryModule.ID], tr)
1771+
if options.Parallelism != nil {
1772+
m.parallelism = *options.Parallelism
1773+
}
17701774

17711775
return tr, nil
17721776
}
@@ -2170,6 +2174,30 @@ func (m *MockWorkspaces) UpdateByID(ctx context.Context, workspaceID string, opt
21702174
return w, nil
21712175
}
21722176

2177+
func (m *MockWorkspaces) ListEffectiveTagBindings(ctx context.Context, workspaceID string) ([]*tfe.EffectiveTagBinding, error) {
2178+
w, ok := m.workspaceIDs[workspaceID]
2179+
if !ok {
2180+
return nil, tfe.ErrResourceNotFound
2181+
}
2182+
var effectiveTagBindings []*tfe.EffectiveTagBinding
2183+
for _, tb := range w.TagBindings {
2184+
effectiveTagBindings = append(effectiveTagBindings, &tfe.EffectiveTagBinding{
2185+
Key: tb.Key,
2186+
Value: tb.Value,
2187+
})
2188+
}
2189+
return effectiveTagBindings, nil
2190+
}
2191+
2192+
func (m *MockWorkspaces) DeleteAllTagBindings(ctx context.Context, workspaceID string) error {
2193+
w, ok := m.workspaceIDs[workspaceID]
2194+
if !ok {
2195+
return tfe.ErrResourceNotFound
2196+
}
2197+
w.TagBindings = nil
2198+
return nil
2199+
}
2200+
21732201
func updateMockWorkspaceAttributes(w *tfe.Workspace, options tfe.WorkspaceUpdateOptions) error {
21742202
// for TestCloud_setUnavailableTerraformVersion
21752203
if w.Name == "unavailable-terraform-version" && options.TerraformVersion != nil {

internal/command/arguments/test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ type Test struct {
1818
// will be executed.
1919
Filter []string
2020

21+
// OperationParallelism is the limit Terraform places on total parallel operations
22+
// during the plan or apply command within a single test run.
23+
OperationParallelism int
24+
2125
// TestDirectory allows the user to override the directory that the test
2226
// command will use to discover test files, defaults to "tests". Regardless
2327
// of the value here, test files within the configuration directory will
@@ -55,6 +59,7 @@ func ParseTest(args []string) (*Test, tfdiags.Diagnostics) {
5559
cmdFlags.BoolVar(&jsonOutput, "json", false, "json")
5660
cmdFlags.StringVar(&test.JUnitXMLFile, "junit-xml", "", "junit-xml")
5761
cmdFlags.BoolVar(&test.Verbose, "verbose", false, "verbose")
62+
cmdFlags.IntVar(&test.OperationParallelism, "parallelism", DefaultParallelism, "parallelism")
5863

5964
// TODO: Finalise the name of this flag.
6065
cmdFlags.StringVar(&test.CloudRunSource, "cloud-run", "", "cloud-run")
@@ -73,6 +78,10 @@ func ParseTest(args []string) (*Test, tfdiags.Diagnostics) {
7378
"The -junit-xml option is currently not compatible with remote test execution via the -cloud-run flag. If you are interested in JUnit XML output for remotely-executed tests please open an issue in GitHub."))
7479
}
7580

81+
if test.OperationParallelism < 1 {
82+
test.OperationParallelism = DefaultParallelism
83+
}
84+
7685
switch {
7786
case jsonOutput:
7887
test.ViewType = ViewJSON

0 commit comments

Comments
 (0)