Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 106 additions & 118 deletions internal/terraform/context_apply_action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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{
Expand Down Expand Up @@ -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" {}
Expand Down Expand Up @@ -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" {}
Expand Down Expand Up @@ -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" {}
Expand All @@ -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.failure]
events = [before_create]
actions = [action.act_unlinked.hello, action.act_unlinked.failure, action.act_unlinked.goodbye]
}
}
}
`,
},
expectInvokeActionCalled: true,
events: func(req providers.InvokeActionRequest) []providers.InvokeActionEvent {
if !req.PlannedActionData.IsNull() && req.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" {}
Expand Down Expand Up @@ -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
Expand All @@ -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" {}
Expand Down Expand Up @@ -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{{
Expand Down Expand Up @@ -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)
}
Expand Down
Loading