actions: validate that action referenced in action_trigger exists in config during transform (#37559)

pull/37570/head
Kristin Laemmert 8 months ago committed by GitHub
parent 1cb7d1859f
commit f574c9cac9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -3257,6 +3257,35 @@ resource "test_object" "a" {
}
}
func TestContextPlan_validateActionInTriggerExists(t *testing.T) {
// this validation occurs during TransformConfig
module := `
resource "test_object" "a" {
lifecycle {
action_trigger {
events = [after_create]
actions = [action.act_unlinked.hello]
}
}
}
`
m := testModuleInline(t, map[string]string{"main.tf": module})
p := simpleMockProvider()
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
_, diags := ctx.Plan(m, nil, DefaultPlanOpts)
if !diags.HasErrors() {
t.Fatal("expected errors, got success!")
}
if diags.Err().Error() != "Configuration for triggered action does not exist: The configuration for the given action action.act_unlinked.hello does not exist. All triggered actions must have an associated configuration." {
t.Fatal("wrong error!")
}
}
func mustActionInstanceAddr(t *testing.T, address string) addrs.AbsActionInstance {
action, diags := addrs.ParseAbsActionInstanceStr(address)
if len(diags) > 0 {

@ -12,6 +12,7 @@ import (
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/dag"
"github.com/hashicorp/terraform/internal/lang/langrefs"
"github.com/hashicorp/terraform/internal/tfdiags"
)
@ -131,9 +132,14 @@ func (t *ConfigTransformer) transformSingle(g *Graph, config *configs.Config) er
}
}
// collect all the Action Declarations (configs.Actions) in this module so
// we can validate that actions referenced in a resource's ActionTriggers
// exist in this module.
allConfigActions := make(map[string]*configs.Action)
for _, a := range module.Actions {
if a != nil {
addr := a.Addr().InModule(path)
allConfigActions[addr.String()] = a
log.Printf("[TRACE] ConfigTransformer: Adding action %s", addr)
abstract := &NodeAbstractAction{
Addr: addr,
@ -162,6 +168,49 @@ func (t *ConfigTransformer) transformSingle(g *Graph, config *configs.Config) er
continue
}
// Verify that any actions referenced in the resource's ActionTriggers exist in this module
var diags tfdiags.Diagnostics
if r.Managed != nil && r.Managed.ActionTriggers != nil {
for i, at := range r.Managed.ActionTriggers {
for _, action := range at.Actions {
refs, parseRefDiags := langrefs.ReferencesInExpr(addrs.ParseRef, action.Expr)
if parseRefDiags != nil {
return parseRefDiags.Err()
}
var configAction addrs.ConfigAction
for _, ref := range refs {
switch a := ref.Subject.(type) {
case addrs.Action:
configAction = a.InModule(config.Path)
case addrs.ActionInstance:
configAction = a.Action.InModule(config.Path)
case addrs.CountAttr, addrs.ForEachAttr:
// nothing to do, these will get evaluated later
default:
// This should have been caught during validation
panic(fmt.Sprintf("unexpected action address %T", a))
}
}
_, ok := allConfigActions[configAction.String()]
if !ok {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Configuration for triggered action does not exist",
Detail: fmt.Sprintf("The configuration for the given action %s does not exist. All triggered actions must have an associated configuration.", configAction.String()),
Subject: &r.Managed.ActionTriggers[i].DeclRange,
})
}
}
}
}
if diags.HasErrors() {
return diags.Err()
}
// If any of the import targets can apply to this node's instances,
// filter them down to the applicable addresses.
var imports []*ImportTarget

Loading…
Cancel
Save