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