@ -4,9 +4,12 @@
package terraform
import (
"path/filepath"
"testing"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/lang/marks"
"github.com/hashicorp/terraform/internal/plans"
@ -31,7 +34,7 @@ func TestContext2Apply_actions(t *testing.T) {
expectInvokeActionCalled bool
expectInvokeActionCalls [ ] providers . InvokeActionRequest
expectDiagnostics tfdiags . Diagnostics
expectDiagnostics func ( m * configs . Config ) tfdiags . Diagnostics
} {
"unreferenced" : {
module : map [ string ] string {
@ -147,7 +150,6 @@ resource "test_object" "a" {
} ,
"before_create failing" : {
toBeImplemented : true , // We need to revisit the diagnostic enhancement
module : map [ string ] string {
"main.tf" : `
action "act_unlinked" "hello" { }
@ -176,17 +178,21 @@ resource "test_object" "a" {
}
} ,
expectDiagnostics : tfdiags . Diagnostics {
tfdiags . Sourceless (
tfdiags . Error ,
"Failed to apply actions before test_object.a" ,
"An error occured while invoking action action.act_unlinked.hello: test case for failing: this simulates a provider failing\n" ,
) ,
expectDiagnostics : func ( m * configs . Config ) tfdiags . Diagnostics {
return tfdiags . Diagnostics { } . Append ( & hcl . Diagnostic {
Severity : hcl . DiagError ,
Summary : "Error when invoking action" ,
Detail : "test case for failing: this simulates a provider failing" ,
Subject : & hcl . Range {
Filename : filepath . Join ( m . Module . SourceDir , "main.tf" ) ,
Start : hcl . Pos { Line : 7 , Column : 18 , Byte : 146 } ,
End : hcl . Pos { Line : 7 , Column : 43 , Byte : 171 } ,
} ,
} )
} ,
} ,
"before_create failing with successfully completed actions" : {
toBeImplemented : true , // We need to revisit the diagnostic enhancement
module : map [ string ] string {
"main.tf" : `
action "act_unlinked" "hello" { }
@ -227,21 +233,24 @@ resource "test_object" "a" {
}
} ,
expectDiagnostics : tfdiags . Diagnostics {
tfdiags . Sourceless (
tfdiags . Error ,
"Failed to apply actions before test_object.a" ,
` An error occured while invoking action action . act_unlinked . failure : test case for failing : this simulates a provider failing
The following actions were successfully invoked :
- action . act_unlinked . hello
- action . act_unlinked . world
As the resource did not change , these actions will be re - invoked in the next apply . ` ,
) ,
expectDiagnostics : func ( m * configs . Config ) tfdiags . Diagnostics {
return tfdiags . Diagnostics { } . Append (
& hcl . Diagnostic {
Severity : hcl . DiagError ,
Summary : "Error when invoking action" ,
Detail : ` test case for failing: this simulates a provider failing ` ,
Subject : & hcl . Range {
Filename : filepath . Join ( m . Module . SourceDir , "main.tf" ) ,
Start : hcl . Pos { Line : 13 , Column : 72 , Byte : 305 } ,
End : hcl . Pos { Line : 13 , Column : 99 , Byte : 332 } ,
} ,
} ,
)
} ,
} ,
"before_create failing when calling invoke" : {
toBeImplemented : true , // We need to revisit the diagnostic enhancement
module : map [ string ] string {
"main.tf" : `
action "act_unlinked" "hello" { }
@ -265,115 +274,87 @@ resource "test_object" "a" {
) ,
}
} ,
expectDiagnostics : tfdiags . Diagnostics {
tfdiags . Sourceless (
tfdiags . Error ,
"Failed to apply actions before test_object.a" ,
"An error occured while invoking action action.act_unlinked.hello: test case for failing: this simulates a provider failing before the action is invoked\n" ,
) ,
} ,
} ,
"after_create failing" : {
toBeImplemented : true , // We need to revisit the diagnostic enhancement
module : map [ string ] string {
"main.tf" : `
action "act_unlinked" "hello" { }
resource "test_object" "a" {
lifecycle {
action_trigger {
events = [ after_create ]
actions = [ action . act_unlinked . hello ]
}
}
}
` ,
} ,
expectInvokeActionCalled : true ,
events : func ( req providers . InvokeActionRequest ) [ ] providers . InvokeActionEvent {
return [ ] providers . InvokeActionEvent {
providers . InvokeActionEvent_Completed {
Diagnostics : tfdiags . Diagnostics {
tfdiags . Sourceless (
tfdiags . Error ,
"test case for failing" ,
"this simulates a provider failing" ,
) ,
expectDiagnostics : func ( m * configs . Config ) tfdiags . Diagnostics {
return tfdiags . Diagnostics { } . Append (
& hcl . Diagnostic {
Severity : hcl . DiagError ,
Summary : "Error when invoking action" ,
Detail : "test case for failing: this simulates a provider failing before the action is invoked" ,
Subject : & hcl . Range {
Filename : filepath . Join ( m . Module . SourceDir , "main.tf" ) ,
Start : hcl . Pos { Line : 7 , Column : 18 , Byte : 146 } ,
End : hcl . Pos { Line : 7 , Column : 43 , Byte : 171 } ,
} ,
} ,
}
} ,
expectDiagnostics : tfdiags . Diagnostics {
tfdiags . Sourceless (
tfdiags . Error ,
"Failed to apply actions after test_object.a" ,
` An error occured while invoking action action . act_unlinked . hello : test case for failing : this simulates a provider failing
The following actions were not yet invoked :
- action . act_unlinked . hello
These actions will not be triggered in the next apply , please run "terraform invoke" to invoke them . ` ,
) ,
)
} ,
} ,
"after_create failing with successfully completed actions" : {
toBeImplemented : true , // We need to revisit the diagnostic enhancement
"failing an action by action event stops next actions in list" : {
module : map [ string ] string {
"main.tf" : `
action "act_unlinked" "hello" { }
action "act_unlinked" "world" { }
action "act_unlinked" "failure" {
config {
attr = "failure"
}
}
action "act_unlinked" "goodbye" { }
resource "test_object" "a" {
lifecycle {
action_trigger {
events = [ after _create]
actions = [ action . act_unlinked . hello , action . act_unlinked . world, action . act_unlinked . failur e]
events = [ before_create ]
actions = [ action . act_unlinked . hello , action . act_unlinked . failure , action . act_unlinked . goodbye ]
}
}
}
` ,
} ,
expectInvokeActionCalled : true ,
events : func ( r eq providers . InvokeActionRequest ) [ ] providers . InvokeActionEvent {
if ! r eq . PlannedActionData . IsNull ( ) && r eq . PlannedActionData . GetAttr ( "attr" ) . AsString ( ) == "failure" {
events : func ( r providers . InvokeActionRequest ) [ ] providers . InvokeActionEvent {
if ! r . PlannedActionData . IsNull ( ) && r . PlannedActionData . GetAttr ( "attr" ) . AsString ( ) == "failure" {
return [ ] providers . InvokeActionEvent {
providers . InvokeActionEvent_Completed {
Diagnostics : tfdiags . Diagnostics {
tfdiags . Sourceless (
tfdiags . Error ,
"test case for failing" ,
"this simulates a provider failing" ,
) ,
} ,
Diagnostics : tfdiags . Diagnostics { } . Append ( tfdiags . Sourceless ( tfdiags . Error , "test case for failing" , "this simulates a provider failing" ) ) ,
} ,
}
} else {
return [ ] providers . InvokeActionEvent {
providers . InvokeActionEvent_Completed { } ,
}
}
} ,
expectDiagnostics : tfdiags . Diagnostics {
tfdiags . Sourceless (
tfdiags . Error ,
"Failed to apply actions after test_object.a" ,
` An error occured while invoking action action . act_unlinked . failure : test case for failing : this simulates a provider failing
return [ ] providers . InvokeActionEvent {
providers . InvokeActionEvent_Completed { } ,
}
The following actions were not yet invoked :
- action . act_unlinked . failure
These actions will not be triggered in the next apply , please run "terraform invoke" to invoke them . ` ,
) ,
} ,
expectDiagnostics : func ( m * configs . Config ) tfdiags . Diagnostics {
return tfdiags . Diagnostics { } . Append (
& hcl . Diagnostic {
Severity : hcl . DiagError ,
Summary : "Error when invoking action" ,
Detail : "test case for failing: this simulates a provider failing" ,
Subject : & hcl . Range {
Filename : filepath . Join ( m . Module . SourceDir , "main.tf" ) ,
Start : hcl . Pos { Line : 13 , Column : 45 , Byte : 280 } ,
End : hcl . Pos { Line : 13 , Column : 72 , Byte : 307 } ,
} ,
} ,
)
} ,
// We expect two calls but not the third one, because the second action fails
expectInvokeActionCalls : [ ] providers . InvokeActionRequest { {
ActionType : "act_unlinked" ,
PlannedActionData : cty . NullVal ( cty . Object ( map [ string ] cty . Type {
"attr" : cty . String ,
} ) ) ,
} , {
ActionType : "act_unlinked" ,
PlannedActionData : cty . ObjectVal ( map [ string ] cty . Value {
"attr" : cty . StringVal ( "failure" ) ,
} ) ,
} } ,
} ,
"failing an action stops next actions in list" : {
toBeImplemented : true , // We need to revisit the diagnostic enhancement
"failing an action during invocation stops next actions in list" : {
module : map [ string ] string {
"main.tf" : `
action "act_unlinked" "hello" { }
@ -407,15 +388,19 @@ resource "test_object" "a" {
}
return tfdiags . Diagnostics { }
} ,
expectDiagnostics : tfdiags . Diagnostics {
tfdiags . Sourceless (
tfdiags . Error ,
"Failed to apply actions before test_object.a" ,
` An error occured while invoking action action . act_unlinked . failure : test case for failing : this simulates a provider failing
The following actions were successfully invoked :
- action . act_unlinked . hello
As the resource did not change , these actions will be re - invoked in the next apply . ` ,
) ,
expectDiagnostics : func ( m * configs . Config ) tfdiags . Diagnostics {
return tfdiags . Diagnostics { } . Append (
& hcl . Diagnostic {
Severity : hcl . DiagError ,
Summary : "Error when invoking action" ,
Detail : "test case for failing: this simulates a provider failing" ,
Subject : & hcl . Range {
Filename : filepath . Join ( m . Module . SourceDir , "main.tf" ) ,
Start : hcl . Pos { Line : 13 , Column : 45 , Byte : 280 } ,
End : hcl . Pos { Line : 13 , Column : 72 , Byte : 307 } ,
} ,
} ,
)
} ,
// We expect two calls but not the third one, because the second action fails
@ -433,7 +418,6 @@ As the resource did not change, these actions will be re-invoked in the next app
} ,
"failing an action stops next action triggers" : {
toBeImplemented : true , // We need to revisit the diagnostic enhancement
module : map [ string ] string {
"main.tf" : `
action "act_unlinked" "hello" { }
@ -475,15 +459,19 @@ resource "test_object" "a" {
}
return tfdiags . Diagnostics { }
} ,
expectDiagnostics : tfdiags . Diagnostics {
tfdiags . Sourceless (
tfdiags . Error ,
"Failed to apply actions before test_object.a" ,
` An error occured while invoking action action . act_unlinked . failure : test case for failing : this simulates a provider failing
The following actions were successfully invoked :
- action . act_unlinked . hello
As the resource did not change , these actions will be re - invoked in the next apply . ` ,
) ,
expectDiagnostics : func ( m * configs . Config ) tfdiags . Diagnostics {
return tfdiags . Diagnostics { } . Append (
& hcl . Diagnostic {
Severity : hcl . DiagError ,
Summary : "Error when invoking action" ,
Detail : "test case for failing: this simulates a provider failing" ,
Subject : & hcl . Range {
Filename : filepath . Join ( m . Module . SourceDir , "main.tf" ) ,
Start : hcl . Pos { Line : 17 , Column : 18 , Byte : 355 } ,
End : hcl . Pos { Line : 17 , Column : 45 , Byte : 382 } ,
} ,
} ,
)
} ,
// We expect two calls but not the third one, because the second action fails
expectInvokeActionCalls : [ ] providers . InvokeActionRequest { {
@ -962,8 +950,8 @@ resource "test_object" "a" {
tfdiags . AssertNoDiagnostics ( t , diags )
_ , diags = ctx . Apply ( plan , m , nil )
if tc . expectDiagnostics . HasErrors ( ) {
tfdiags . AssertDiagnosticsMatch ( t , diags , tc . expectDiagnostics )
if tc . expectDiagnostics != nil {
tfdiags . AssertDiagnosticsMatch ( t , diags , tc . expectDiagnostics ( m ) )
} else {
tfdiags . AssertNoDiagnostics ( t , diags )
}