Skip to content

Commit 1aef3b7

Browse files
committed
Add extended action validation during config parsing
- Verify that actions and linked_resources are in the current module - Verify that given traversals reference the right types (action, resource) - Add some validation tests Things not captured in config validation: - is this action referencing a resource defined in this configuration - is this resource referencing an action defined in this configuration Todo: - Verify actions and resources are specific instances - no containers/expansion allowed in referenfces for now
1 parent 42449aa commit 1aef3b7

File tree

2 files changed

+146
-17
lines changed

2 files changed

+146
-17
lines changed

internal/configs/action.go

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ func decodeActionTriggerBlock(block *hcl.Block) (*ActionTrigger, hcl.Diagnostics
123123
Detail: "The \"event\" argument supports the following values: before_create, after_create, before_update, after_update, before_destroy, after_destroy.",
124124
Subject: expr.Range().Ptr(),
125125
})
126+
continue
126127
}
127128

128129
if event == BeforeDestroy || event == AfterDestroy {
@@ -132,8 +133,8 @@ func decodeActionTriggerBlock(block *hcl.Block) (*ActionTrigger, hcl.Diagnostics
132133
Detail: "The destroy events (before_destroy, after_destroy) are not supported as of right now. They will be supported in a future release.",
133134
Subject: expr.Range().Ptr(),
134135
})
136+
continue
135137
}
136-
137138
events = append(events, event)
138139
}
139140
a.Events = events
@@ -146,25 +147,29 @@ func decodeActionTriggerBlock(block *hcl.Block) (*ActionTrigger, hcl.Diagnostics
146147
for _, expr := range exprs {
147148
traversal, travDiags := hcl.AbsTraversalForExpr(expr)
148149
diags = append(diags, travDiags...)
149-
// verify that the traversal is an action
150-
if traversal.RootName() != "action" {
151-
diags = append(diags, &hcl.Diagnostic{
152-
Severity: hcl.DiagError,
153-
Summary: "Invalid actions argument inside action_triggers",
154-
Detail: "action_triggers.actions accepts a list of one or more actions",
155-
Subject: block.DefRange.Ptr(),
156-
})
157-
continue
158-
}
159150

160-
if len(traversal) != 0 {
161-
actionRef := ActionRef{
162-
Traversal: traversal,
163-
Range: expr.Range(),
151+
if len(traversal) > 0 {
152+
// verify that the traversal is an action
153+
ref, refDiags := addrs.ParseRef(traversal)
154+
diags = append(diags, refDiags.ToHCL()...)
155+
156+
switch ref.Subject.(type) {
157+
case addrs.ActionInstance, addrs.Action:
158+
actionRef := ActionRef{
159+
Traversal: traversal,
160+
Range: expr.Range(),
161+
}
162+
actions = append(actions, actionRef)
163+
default:
164+
diags = append(diags, &hcl.Diagnostic{
165+
Severity: hcl.DiagError,
166+
Summary: "Invalid actions argument inside action_triggers",
167+
Detail: "action_triggers.actions accepts a list of one or more actions, which must be in the current module.",
168+
Subject: expr.Range().Ptr(),
169+
})
170+
continue
164171
}
165-
actions = append(actions, actionRef)
166172
}
167-
168173
}
169174
a.Actions = actions
170175
}

internal/configs/action_test.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/hashicorp/hcl/v2"
1010
"github.com/hashicorp/hcl/v2/hcltest"
11+
"github.com/zclconf/go-cty/cty"
1112
)
1213

1314
func TestDecodeActionBlock(t *testing.T) {
@@ -125,6 +126,129 @@ func TestDecodeActionBlock(t *testing.T) {
125126
}
126127
}
127128

129+
func TestDecodeActionTriggerBlock(t *testing.T) {
130+
conditionExpr := hcltest.MockExprLiteral(cty.True)
131+
eventsListExpr := hcltest.MockExprList([]hcl.Expression{hcltest.MockExprTraversalSrc("after_create"), hcltest.MockExprTraversalSrc("after_update")})
132+
133+
fooActionExpr := hcltest.MockExprTraversalSrc("action.action_type.foo")
134+
barActionExpr := hcltest.MockExprTraversalSrc("action.action_type.bar")
135+
fooAndBarExpr := hcltest.MockExprList([]hcl.Expression{fooActionExpr, barActionExpr})
136+
137+
// bad inputs!
138+
moduleActionExpr := hcltest.MockExprTraversalSrc("module.foo.action.action_type.bar")
139+
fooDataSourceExpr := hcltest.MockExprTraversalSrc("data.example.foo")
140+
141+
tests := map[string]struct {
142+
input *hcl.Block
143+
want *ActionTrigger
144+
expectDiags []string
145+
}{
146+
"simple example": {
147+
&hcl.Block{
148+
Type: "action_trigger",
149+
Body: hcltest.MockBody(&hcl.BodyContent{
150+
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
151+
"condition": conditionExpr,
152+
"events": eventsListExpr,
153+
"actions": fooAndBarExpr,
154+
}),
155+
}),
156+
},
157+
&ActionTrigger{
158+
Condition: conditionExpr,
159+
Events: []ActionTriggerEvent{AfterCreate, AfterUpdate},
160+
Actions: []ActionRef{
161+
{
162+
mustAbsTraversalForExpr(fooActionExpr),
163+
fooActionExpr.Range(),
164+
},
165+
{
166+
mustAbsTraversalForExpr(barActionExpr),
167+
barActionExpr.Range(),
168+
},
169+
},
170+
},
171+
nil,
172+
},
173+
"error - referencing actions in other modules": {
174+
&hcl.Block{
175+
Type: "action_trigger",
176+
Body: hcltest.MockBody(&hcl.BodyContent{
177+
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
178+
"condition": conditionExpr,
179+
"events": eventsListExpr,
180+
"actions": hcltest.MockExprList([]hcl.Expression{moduleActionExpr}),
181+
}),
182+
}),
183+
},
184+
&ActionTrigger{
185+
Condition: conditionExpr,
186+
Events: []ActionTriggerEvent{AfterCreate, AfterUpdate},
187+
Actions: []ActionRef{},
188+
},
189+
[]string{
190+
"MockExprTraversal:0,0-33: Invalid actions argument inside action_triggers; action_triggers.actions accepts a list of one or more actions, which must be in the current module.",
191+
":0,0-0: No actions specified; At least one action must be specified for an action_trigger.",
192+
},
193+
},
194+
"error - action is not an action": {
195+
&hcl.Block{
196+
Type: "action_trigger",
197+
Body: hcltest.MockBody(&hcl.BodyContent{
198+
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
199+
"condition": conditionExpr,
200+
"events": eventsListExpr,
201+
"actions": hcltest.MockExprList([]hcl.Expression{fooDataSourceExpr}),
202+
}),
203+
}),
204+
},
205+
&ActionTrigger{
206+
Condition: conditionExpr,
207+
Events: []ActionTriggerEvent{AfterCreate, AfterUpdate},
208+
Actions: []ActionRef{},
209+
},
210+
[]string{
211+
"MockExprTraversal:0,0-16: Invalid actions argument inside action_triggers; action_triggers.actions accepts a list of one or more actions, which must be in the current module.",
212+
":0,0-0: No actions specified; At least one action must be specified for an action_trigger.",
213+
},
214+
},
215+
"error - invalid event": {
216+
&hcl.Block{
217+
Type: "action_trigger",
218+
Body: hcltest.MockBody(&hcl.BodyContent{
219+
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
220+
"condition": conditionExpr,
221+
"events": hcltest.MockExprList([]hcl.Expression{hcltest.MockExprTraversalSrc("not_an_event")}),
222+
"actions": hcltest.MockExprList([]hcl.Expression{fooActionExpr}),
223+
}),
224+
}),
225+
},
226+
&ActionTrigger{
227+
Condition: conditionExpr,
228+
Events: []ActionTriggerEvent{},
229+
Actions: []ActionRef{
230+
{
231+
mustAbsTraversalForExpr(fooActionExpr),
232+
fooActionExpr.Range(),
233+
},
234+
},
235+
},
236+
[]string{
237+
"MockExprTraversal:0,0-12: Invalid \"event\" value not_an_event; The \"event\" argument supports the following values: before_create, after_create, before_update, after_update, before_destroy, after_destroy.",
238+
":0,0-0: No events specified; At least one event must be specified for an action_trigger.",
239+
},
240+
},
241+
}
242+
243+
for name, test := range tests {
244+
t.Run(name, func(t *testing.T) {
245+
got, diags := decodeActionTriggerBlock(test.input)
246+
assertExactDiagnostics(t, diags, test.expectDiags)
247+
assertResultDeepEqual(t, got, test.want)
248+
})
249+
}
250+
}
251+
128252
func mustAbsTraversalForExpr(expr hcl.Expression) hcl.Traversal {
129253
trav, diags := hcl.AbsTraversalForExpr(expr)
130254
if diags.HasErrors() {

0 commit comments

Comments
 (0)