8
8
9
9
"github.com/hashicorp/terraform/internal/addrs"
10
10
"github.com/hashicorp/terraform/internal/configs/configschema"
11
+ "github.com/hashicorp/terraform/internal/lang/marks"
11
12
"github.com/hashicorp/terraform/internal/plans"
12
13
"github.com/hashicorp/terraform/internal/providers"
13
14
testing_provider "github.com/hashicorp/terraform/internal/providers/testing"
@@ -23,8 +24,10 @@ func TestContext2Apply_actions(t *testing.T) {
23
24
prevRunState * states.State
24
25
events []providers.InvokeActionEvent
25
26
callingInvokeReturnsDiagnostics tfdiags.Diagnostics
27
+ planOpts * PlanOpts
26
28
27
29
expectInvokeActionCalled bool
30
+ expectInvokeActionCalls []providers.InvokeActionRequest
28
31
29
32
expectDiagnostics tfdiags.Diagnostics
30
33
}{
@@ -207,6 +210,110 @@ resource "test_object" "a" {
207
210
),
208
211
},
209
212
},
213
+
214
+ "action with configuration" : {
215
+ module : map [string ]string {
216
+ "main.tf" : `
217
+ resource "test_object" "a" {
218
+ name = "foo"
219
+ }
220
+ action "test_unlinked" "hello" {
221
+ config {
222
+ attr = resource.test_object.a.name
223
+ }
224
+ }
225
+ resource "test_object" "b" {
226
+ lifecycle {
227
+ action_trigger {
228
+ events = [before_create]
229
+ actions = [action.test_unlinked.hello]
230
+ }
231
+ }
232
+ }
233
+ ` ,
234
+ },
235
+ expectInvokeActionCalled : true ,
236
+ expectInvokeActionCalls : []providers.InvokeActionRequest {{
237
+ ActionType : "test_unlinked" ,
238
+ PlannedActionData : cty .ObjectVal (map [string ]cty.Value {
239
+ "attr" : cty .StringVal ("foo" ),
240
+ }),
241
+ }},
242
+ },
243
+
244
+ // Providers can handle unknown values in the configuration
245
+ "action with unknown configuration" : {
246
+ module : map [string ]string {
247
+ "main.tf" : `
248
+ variable "unknown_value" {
249
+ type = string
250
+ }
251
+ action "test_unlinked" "hello" {
252
+ config {
253
+ attr = var.unknown_value
254
+ }
255
+ }
256
+ resource "test_object" "b" {
257
+ lifecycle {
258
+ action_trigger {
259
+ events = [before_create]
260
+ actions = [action.test_unlinked.hello]
261
+ }
262
+ }
263
+ }
264
+ ` ,
265
+ },
266
+ planOpts : SimplePlanOpts (plans .NormalMode , InputValues {
267
+ "unknown_value" : & InputValue {
268
+ Value : cty .UnknownVal (cty .String ),
269
+ },
270
+ }),
271
+
272
+ expectInvokeActionCalled : true ,
273
+ expectInvokeActionCalls : []providers.InvokeActionRequest {{
274
+ ActionType : "test_unlinked" ,
275
+ PlannedActionData : cty .ObjectVal (map [string ]cty.Value {
276
+ "attr" : cty .UnknownVal (cty .String ),
277
+ }),
278
+ }},
279
+ },
280
+
281
+ "action with secrets in configuration" : {
282
+ module : map [string ]string {
283
+ "main.tf" : `
284
+ variable "secret_value" {
285
+ type = string
286
+ sensitive = true
287
+ }
288
+ action "test_unlinked" "hello" {
289
+ config {
290
+ attr = var.secret_value
291
+ }
292
+ }
293
+ resource "test_object" "b" {
294
+ lifecycle {
295
+ action_trigger {
296
+ events = [before_create]
297
+ actions = [action.test_unlinked.hello]
298
+ }
299
+ }
300
+ }
301
+ ` ,
302
+ },
303
+ planOpts : SimplePlanOpts (plans .NormalMode , InputValues {
304
+ "secret_value" : & InputValue {
305
+ Value : cty .StringVal ("psst, I'm secret" ).Mark (marks .Sensitive ), // Not sure if we need the mark here, but it doesn't hurt
306
+ },
307
+ }),
308
+
309
+ expectInvokeActionCalled : true ,
310
+ expectInvokeActionCalls : []providers.InvokeActionRequest {{
311
+ ActionType : "test_unlinked" ,
312
+ PlannedActionData : cty .ObjectVal (map [string ]cty.Value {
313
+ "attr" : cty .StringVal ("psst, I'm secret" ),
314
+ }),
315
+ }},
316
+ },
210
317
} {
211
318
t .Run (name , func (t * testing.T ) {
212
319
m := testModuleInline (t , tc .module )
@@ -286,7 +393,12 @@ resource "test_object" "a" {
286
393
diags := ctx .Validate (m , & ValidateOpts {})
287
394
tfdiags .AssertNoDiagnostics (t , diags )
288
395
289
- plan , diags := ctx .Plan (m , tc .prevRunState , SimplePlanOpts (plans .NormalMode , InputValues {}))
396
+ planOpts := SimplePlanOpts (plans .NormalMode , InputValues {})
397
+ if tc .planOpts != nil {
398
+ planOpts = tc .planOpts
399
+ }
400
+
401
+ plan , diags := ctx .Plan (m , tc .prevRunState , planOpts )
290
402
tfdiags .AssertNoDiagnostics (t , diags )
291
403
292
404
_ , diags = ctx .Apply (plan , m , nil )
@@ -299,6 +411,19 @@ resource "test_object" "a" {
299
411
if tc .expectInvokeActionCalled && len (invokeActionCalls ) == 0 {
300
412
t .Fatalf ("expected invoke action to be called, but it was not" )
301
413
}
414
+
415
+ if len (tc .expectInvokeActionCalls ) > 0 && len (invokeActionCalls ) != len (tc .expectInvokeActionCalls ) {
416
+ t .Fatalf ("expected %d invoke action calls, got %d" , len (tc .expectInvokeActionCalls ), len (invokeActionCalls ))
417
+ }
418
+ for i , expectedCall := range tc .expectInvokeActionCalls {
419
+ actualCall := invokeActionCalls [i ]
420
+ if actualCall .ActionType != expectedCall .ActionType {
421
+ t .Fatalf ("expected invoke action call %d ActionType to be %s, got %s" , i , expectedCall .ActionType , actualCall .ActionType )
422
+ }
423
+ if ! actualCall .PlannedActionData .RawEquals (expectedCall .PlannedActionData ) {
424
+ t .Fatalf ("expected invoke action call %d PlannedActionData to be %s, got %s" , i , expectedCall .PlannedActionData , actualCall .PlannedActionData )
425
+ }
426
+ }
302
427
})
303
428
}
304
429
}
0 commit comments