-
Notifications
You must be signed in to change notification settings - Fork 9.4k
/
evaluate_triggers.go
143 lines (121 loc) · 4.38 KB
/
evaluate_triggers.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package terraform
import (
"strings"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/instances"
"github.com/hashicorp/terraform/internal/tfdiags"
"github.com/zclconf/go-cty/cty"
)
func evalReplaceTriggeredByExpr(expr hcl.Expression, keyData instances.RepetitionData) (*addrs.Reference, tfdiags.Diagnostics) {
var ref *addrs.Reference
var diags tfdiags.Diagnostics
traversal, diags := triggersExprToTraversal(expr, keyData)
if diags.HasErrors() {
return nil, diags
}
// We now have a static traversal, so we can just turn it into an addrs.Reference.
ref, ds := addrs.ParseRef(traversal)
diags = diags.Append(ds)
return ref, diags
}
// trggersExprToTraversal takes an hcl expression limited to the syntax allowed
// in replace_triggered_by, and converts it to a static traversal. The
// RepetitionData contains the data necessary to evaluate the only allowed
// variables in the expression, count.index and each.key.
func triggersExprToTraversal(expr hcl.Expression, keyData instances.RepetitionData) (hcl.Traversal, tfdiags.Diagnostics) {
var trav hcl.Traversal
var diags tfdiags.Diagnostics
switch e := expr.(type) {
case *hclsyntax.RelativeTraversalExpr:
t, d := triggersExprToTraversal(e.Source, keyData)
diags = diags.Append(d)
trav = append(trav, t...)
trav = append(trav, e.Traversal...)
case *hclsyntax.ScopeTraversalExpr:
// a static reference, we can just append the traversal
trav = append(trav, e.Traversal...)
case *hclsyntax.IndexExpr:
// Get the collection from the index expression
t, d := triggersExprToTraversal(e.Collection, keyData)
diags = diags.Append(d)
if diags.HasErrors() {
return nil, diags
}
trav = append(trav, t...)
// The index key is the only place where we could have variables that
// reference count and each, so we need to parse those independently.
idx, hclDiags := parseIndexKeyExpr(e.Key, keyData)
diags = diags.Append(hclDiags)
trav = append(trav, idx)
default:
// Something unexpected got through config validation. We're not sure
// what it is, but we'll point it out in the diagnostics for the user
// to fix.
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid replace_triggered_by expression",
Detail: "Unexpected expression found in replace_triggered_by.",
Subject: e.Range().Ptr(),
})
}
return trav, diags
}
// parseIndexKeyExpr takes an hcl.Expression and parses it as an index key, while
// evaluating any references to count.index or each.key.
func parseIndexKeyExpr(expr hcl.Expression, keyData instances.RepetitionData) (hcl.TraverseIndex, hcl.Diagnostics) {
idx := hcl.TraverseIndex{
SrcRange: expr.Range(),
}
trav, diags := hcl.RelTraversalForExpr(expr)
if diags.HasErrors() {
return idx, diags
}
keyParts := []string{}
for _, t := range trav {
attr, ok := t.(hcl.TraverseAttr)
if !ok {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid index expression",
Detail: "Only constant values, count.index or each.key are allowed in index expressions.",
Subject: expr.Range().Ptr(),
})
return idx, diags
}
keyParts = append(keyParts, attr.Name)
}
switch strings.Join(keyParts, ".") {
case "count.index":
if keyData.CountIndex == cty.NilVal {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: `Reference to "count" in non-counted context`,
Detail: `The "count" object can only be used in "resource" blocks when the "count" argument is set.`,
Subject: expr.Range().Ptr(),
})
}
idx.Key = keyData.CountIndex
case "each.key":
if keyData.EachKey == cty.NilVal {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: `Reference to "each" in context without for_each`,
Detail: `The "each" object can be used only in "resource" blocks when the "for_each" argument is set.`,
Subject: expr.Range().Ptr(),
})
}
idx.Key = keyData.EachKey
default:
// Something may have slipped through validation, probably from a json
// configuration.
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid index expression",
Detail: "Only constant values, count.index or each.key are allowed in index expressions.",
Subject: expr.Range().Ptr(),
})
}
return idx, diags
}