mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Introduce separate testing scope for reference validation (#33339)
This commit is contained in:
parent
dfc26c2ac4
commit
212ae6c4ba
@ -16,10 +16,11 @@ import (
|
||||
// that is defining it.
|
||||
//
|
||||
// This is related to but separate from ModuleCallOutput, which represents
|
||||
// a module output from the perspective of its parent module. Since output
|
||||
// values cannot be represented from the module where they are defined,
|
||||
// OutputValue is not Referenceable, while ModuleCallOutput is.
|
||||
// a module output from the perspective of its parent module. Outputs are
|
||||
// referencable from the testing scope, in general terraform operation users
|
||||
// will be referencing ModuleCallOutput.
|
||||
type OutputValue struct {
|
||||
referenceable
|
||||
Name string
|
||||
}
|
||||
|
||||
@ -27,6 +28,16 @@ func (v OutputValue) String() string {
|
||||
return "output." + v.Name
|
||||
}
|
||||
|
||||
func (v OutputValue) Equal(o OutputValue) bool {
|
||||
return v.Name == o.Name
|
||||
}
|
||||
|
||||
func (v OutputValue) UniqueKey() UniqueKey {
|
||||
return v // An OutputValue is its own UniqueKey
|
||||
}
|
||||
|
||||
func (v OutputValue) uniqueKeySigil() {}
|
||||
|
||||
// Absolute converts the receiver into an absolute address within the given
|
||||
// module instance.
|
||||
func (v OutputValue) Absolute(m ModuleInstance) AbsOutputValue {
|
||||
@ -82,7 +93,7 @@ func (v AbsOutputValue) String() string {
|
||||
}
|
||||
|
||||
func (v AbsOutputValue) Equal(o AbsOutputValue) bool {
|
||||
return v.OutputValue == o.OutputValue && v.Module.Equal(o.Module)
|
||||
return v.OutputValue.Equal(o.OutputValue) && v.Module.Equal(o.Module)
|
||||
}
|
||||
|
||||
func (v AbsOutputValue) ConfigOutputValue() ConfigOutputValue {
|
||||
|
@ -9,8 +9,9 @@ import (
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
)
|
||||
|
||||
// Reference describes a reference to an address with source location
|
||||
@ -82,6 +83,47 @@ func ParseRef(traversal hcl.Traversal) (*Reference, tfdiags.Diagnostics) {
|
||||
return ref, diags
|
||||
}
|
||||
|
||||
// ParseRefFromTestingScope adds check blocks and outputs into the available
|
||||
// references returned by ParseRef.
|
||||
//
|
||||
// The testing files and functionality have a slightly expanded referencing
|
||||
// scope and so should use this function to retrieve references.
|
||||
func ParseRefFromTestingScope(traversal hcl.Traversal) (*Reference, tfdiags.Diagnostics) {
|
||||
root := traversal.RootName()
|
||||
|
||||
var diags tfdiags.Diagnostics
|
||||
var reference *Reference
|
||||
|
||||
switch root {
|
||||
case "output":
|
||||
name, rng, remain, outputDiags := parseSingleAttrRef(traversal)
|
||||
reference = &Reference{
|
||||
Subject: OutputValue{Name: name},
|
||||
SourceRange: tfdiags.SourceRangeFromHCL(rng),
|
||||
Remaining: remain,
|
||||
}
|
||||
diags = outputDiags
|
||||
case "check":
|
||||
name, rng, remain, checkDiags := parseSingleAttrRef(traversal)
|
||||
reference = &Reference{
|
||||
Subject: Check{Name: name},
|
||||
SourceRange: tfdiags.SourceRangeFromHCL(rng),
|
||||
Remaining: remain,
|
||||
}
|
||||
diags = checkDiags
|
||||
}
|
||||
|
||||
if reference != nil {
|
||||
if len(reference.Remaining) == 0 {
|
||||
reference.Remaining = nil
|
||||
}
|
||||
return reference, diags
|
||||
}
|
||||
|
||||
// If it's not an output or a check block, then just parse it as normal.
|
||||
return ParseRef(traversal)
|
||||
}
|
||||
|
||||
// ParseRefStr is a helper wrapper around ParseRef that takes a string
|
||||
// and parses it with the HCL native syntax traversal parser before
|
||||
// interpreting it.
|
||||
@ -111,6 +153,22 @@ func ParseRefStr(str string) (*Reference, tfdiags.Diagnostics) {
|
||||
return ref, diags
|
||||
}
|
||||
|
||||
// ParseRefStrFromTestingScope matches ParseRefStr except it supports the
|
||||
// references supported by ParseRefFromTestingScope.
|
||||
func ParseRefStrFromTestingScope(str string) (*Reference, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1})
|
||||
diags = diags.Append(parseDiags)
|
||||
if parseDiags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
ref, targetDiags := ParseRefFromTestingScope(traversal)
|
||||
diags = diags.Append(targetDiags)
|
||||
return ref, diags
|
||||
}
|
||||
|
||||
func parseRef(traversal hcl.Traversal) (*Reference, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
|
@ -9,10 +9,117 @@ import (
|
||||
"github.com/go-test/deep"
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
)
|
||||
|
||||
func TestParseRefInTestingScope(t *testing.T) {
|
||||
tests := []struct {
|
||||
Input string
|
||||
Want *Reference
|
||||
WantErr string
|
||||
}{
|
||||
{
|
||||
`output.value`,
|
||||
&Reference{
|
||||
Subject: OutputValue{
|
||||
Name: "value",
|
||||
},
|
||||
SourceRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
||||
End: tfdiags.SourcePos{Line: 1, Column: 13, Byte: 12},
|
||||
},
|
||||
},
|
||||
``,
|
||||
},
|
||||
{
|
||||
`output`,
|
||||
nil,
|
||||
`The "output" object cannot be accessed directly. Instead, access one of its attributes.`,
|
||||
},
|
||||
{
|
||||
`output["foo"]`,
|
||||
nil,
|
||||
`The "output" object does not support this operation.`,
|
||||
},
|
||||
|
||||
{
|
||||
`check.health`,
|
||||
&Reference{
|
||||
Subject: Check{
|
||||
Name: "health",
|
||||
},
|
||||
SourceRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
||||
End: tfdiags.SourcePos{Line: 1, Column: 13, Byte: 12},
|
||||
},
|
||||
},
|
||||
``,
|
||||
},
|
||||
{
|
||||
`check`,
|
||||
nil,
|
||||
`The "check" object cannot be accessed directly. Instead, access one of its attributes.`,
|
||||
},
|
||||
{
|
||||
`check["foo"]`,
|
||||
nil,
|
||||
`The "check" object does not support this operation.`,
|
||||
},
|
||||
|
||||
// Sanity check at least one of the others works to verify it does
|
||||
// fall through to the core function.
|
||||
{
|
||||
`count.index`,
|
||||
&Reference{
|
||||
Subject: CountAttr{
|
||||
Name: "index",
|
||||
},
|
||||
SourceRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
||||
End: tfdiags.SourcePos{Line: 1, Column: 12, Byte: 11},
|
||||
},
|
||||
},
|
||||
``,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.Input, func(t *testing.T) {
|
||||
traversal, travDiags := hclsyntax.ParseTraversalAbs([]byte(test.Input), "", hcl.Pos{Line: 1, Column: 1})
|
||||
if travDiags.HasErrors() {
|
||||
t.Fatal(travDiags.Error())
|
||||
}
|
||||
|
||||
got, diags := ParseRefFromTestingScope(traversal)
|
||||
|
||||
switch len(diags) {
|
||||
case 0:
|
||||
if test.WantErr != "" {
|
||||
t.Fatalf("succeeded; want error: %s", test.WantErr)
|
||||
}
|
||||
case 1:
|
||||
if test.WantErr == "" {
|
||||
t.Fatalf("unexpected diagnostics: %s", diags.Err())
|
||||
}
|
||||
if got, want := diags[0].Description().Detail, test.WantErr; got != want {
|
||||
t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
default:
|
||||
t.Fatalf("too many diagnostics: %s", diags.Err())
|
||||
}
|
||||
|
||||
if diags.HasErrors() {
|
||||
return
|
||||
}
|
||||
|
||||
for _, problem := range deep.Equal(got, test.Want) {
|
||||
t.Errorf(problem)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRef(t *testing.T) {
|
||||
tests := []struct {
|
||||
Input string
|
||||
@ -719,6 +826,38 @@ func TestParseRef(t *testing.T) {
|
||||
nil,
|
||||
`A reference to a resource type must be followed by at least one attribute access, specifying the resource name.`,
|
||||
},
|
||||
|
||||
// Should interpret checks and outputs as resource types.
|
||||
{
|
||||
`output.value`,
|
||||
&Reference{
|
||||
Subject: Resource{
|
||||
Mode: ManagedResourceMode,
|
||||
Type: "output",
|
||||
Name: "value",
|
||||
},
|
||||
SourceRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
||||
End: tfdiags.SourcePos{Line: 1, Column: 13, Byte: 12},
|
||||
},
|
||||
},
|
||||
``,
|
||||
},
|
||||
{
|
||||
`check.health`,
|
||||
&Reference{
|
||||
Subject: Resource{
|
||||
Mode: ManagedResourceMode,
|
||||
Type: "check",
|
||||
Name: "health",
|
||||
},
|
||||
SourceRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
||||
End: tfdiags.SourcePos{Line: 1, Column: 13, Byte: 12},
|
||||
},
|
||||
},
|
||||
``,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
@ -10,12 +10,13 @@ import (
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
ctyjson "github.com/zclconf/go-cty/cty/json"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/lang"
|
||||
"github.com/hashicorp/terraform/internal/lang/blocktoattr"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
ctyjson "github.com/zclconf/go-cty/cty/json"
|
||||
)
|
||||
|
||||
// expression represents any unparsed expression
|
||||
@ -47,7 +48,7 @@ func marshalExpression(ex hcl.Expression) expression {
|
||||
ret.ConstantValue = valJSON
|
||||
}
|
||||
|
||||
refs, _ := lang.ReferencesInExpr(ex)
|
||||
refs, _ := lang.ReferencesInExpr(addrs.ParseRef, ex)
|
||||
if len(refs) > 0 {
|
||||
var varString []string
|
||||
for _, ref := range refs {
|
||||
|
@ -53,7 +53,7 @@ func (cr *CheckRule) validateSelfReferences(checkType string, addr addrs.Resourc
|
||||
if expr == nil {
|
||||
continue
|
||||
}
|
||||
refs, _ := lang.References(expr.Variables())
|
||||
refs, _ := lang.References(addrs.ParseRef, expr.Variables())
|
||||
for _, ref := range refs {
|
||||
var refAddr addrs.Resource
|
||||
|
||||
|
@ -568,7 +568,7 @@ func decodeReplaceTriggeredBy(expr hcl.Expression) ([]hcl.Expression, hcl.Diagno
|
||||
exprs[i] = expr
|
||||
}
|
||||
|
||||
refs, refDiags := lang.ReferencesInExpr(expr)
|
||||
refs, refDiags := lang.ReferencesInExpr(addrs.ParseRef, expr)
|
||||
for _, diag := range refDiags {
|
||||
severity := hcl.DiagError
|
||||
if diag.Severity() == tfdiags.Warning {
|
||||
|
@ -4,9 +4,10 @@
|
||||
package lang
|
||||
|
||||
import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// Data is an interface whose implementations can provide cty.Value
|
||||
@ -33,4 +34,6 @@ type Data interface {
|
||||
GetPathAttr(addrs.PathAttr, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics)
|
||||
GetTerraformAttr(addrs.TerraformAttr, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics)
|
||||
GetInputVariable(addrs.InputVariable, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics)
|
||||
GetOutput(addrs.OutputValue, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics)
|
||||
GetCheckBlock(addrs.Check, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics)
|
||||
}
|
||||
|
@ -4,9 +4,10 @@
|
||||
package lang
|
||||
|
||||
import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
type dataForTests struct {
|
||||
@ -14,10 +15,12 @@ type dataForTests struct {
|
||||
ForEachAttrs map[string]cty.Value
|
||||
Resources map[string]cty.Value
|
||||
LocalValues map[string]cty.Value
|
||||
OutputValues map[string]cty.Value
|
||||
Modules map[string]cty.Value
|
||||
PathAttrs map[string]cty.Value
|
||||
TerraformAttrs map[string]cty.Value
|
||||
InputVariables map[string]cty.Value
|
||||
CheckBlocks map[string]cty.Value
|
||||
}
|
||||
|
||||
var _ Data = &dataForTests{}
|
||||
@ -63,3 +66,11 @@ func (d *dataForTests) GetPathAttr(addr addrs.PathAttr, rng tfdiags.SourceRange)
|
||||
func (d *dataForTests) GetTerraformAttr(addr addrs.TerraformAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
|
||||
return d.TerraformAttrs[addr.Name], nil
|
||||
}
|
||||
|
||||
func (d *dataForTests) GetOutput(addr addrs.OutputValue, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
|
||||
return d.OutputValues[addr.Name], nil
|
||||
}
|
||||
|
||||
func (d *dataForTests) GetCheckBlock(addr addrs.Check, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
|
||||
return d.CheckBlocks[addr.Name], nil
|
||||
}
|
||||
|
@ -9,13 +9,14 @@ import (
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/ext/dynblock"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/instances"
|
||||
"github.com/hashicorp/terraform/internal/lang/blocktoattr"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
)
|
||||
|
||||
// ExpandBlock expands any "dynamic" blocks present in the given body. The
|
||||
@ -28,7 +29,7 @@ func (s *Scope) ExpandBlock(body hcl.Body, schema *configschema.Block) (hcl.Body
|
||||
spec := schema.DecoderSpec()
|
||||
|
||||
traversals := dynblock.ExpandVariablesHCLDec(body, spec)
|
||||
refs, diags := References(traversals)
|
||||
refs, diags := References(s.ParseRef, traversals)
|
||||
|
||||
ctx, ctxDiags := s.EvalContext(refs)
|
||||
diags = diags.Append(ctxDiags)
|
||||
@ -49,7 +50,7 @@ func (s *Scope) ExpandBlock(body hcl.Body, schema *configschema.Block) (hcl.Body
|
||||
func (s *Scope) EvalBlock(body hcl.Body, schema *configschema.Block) (cty.Value, tfdiags.Diagnostics) {
|
||||
spec := schema.DecoderSpec()
|
||||
|
||||
refs, diags := ReferencesInBlock(body, schema)
|
||||
refs, diags := ReferencesInBlock(s.ParseRef, body, schema)
|
||||
|
||||
ctx, ctxDiags := s.EvalContext(refs)
|
||||
diags = diags.Append(ctxDiags)
|
||||
@ -96,7 +97,7 @@ func (s *Scope) EvalSelfBlock(body hcl.Body, self cty.Value, schema *configschem
|
||||
})
|
||||
}
|
||||
|
||||
refs, refDiags := References(hcldec.Variables(body, spec))
|
||||
refs, refDiags := References(s.ParseRef, hcldec.Variables(body, spec))
|
||||
diags = diags.Append(refDiags)
|
||||
|
||||
terraformAttrs := map[string]cty.Value{}
|
||||
@ -161,7 +162,7 @@ func (s *Scope) EvalSelfBlock(body hcl.Body, self cty.Value, schema *configschem
|
||||
// If the returned diagnostics contains errors then the result may be
|
||||
// incomplete, but will always be of the requested type.
|
||||
func (s *Scope) EvalExpr(expr hcl.Expression, wantType cty.Type) (cty.Value, tfdiags.Diagnostics) {
|
||||
refs, diags := ReferencesInExpr(expr)
|
||||
refs, diags := ReferencesInExpr(s.ParseRef, expr)
|
||||
|
||||
ctx, ctxDiags := s.EvalContext(refs)
|
||||
diags = diags.Append(ctxDiags)
|
||||
@ -281,10 +282,12 @@ func (s *Scope) evalContext(refs []*addrs.Reference, selfAddr addrs.Referenceabl
|
||||
wholeModules := map[string]cty.Value{}
|
||||
inputVariables := map[string]cty.Value{}
|
||||
localValues := map[string]cty.Value{}
|
||||
outputValues := map[string]cty.Value{}
|
||||
pathAttrs := map[string]cty.Value{}
|
||||
terraformAttrs := map[string]cty.Value{}
|
||||
countAttrs := map[string]cty.Value{}
|
||||
forEachAttrs := map[string]cty.Value{}
|
||||
checkBlocks := map[string]cty.Value{}
|
||||
var self cty.Value
|
||||
|
||||
for _, ref := range refs {
|
||||
@ -405,6 +408,16 @@ func (s *Scope) evalContext(refs []*addrs.Reference, selfAddr addrs.Referenceabl
|
||||
diags = diags.Append(valDiags)
|
||||
forEachAttrs[subj.Name] = val
|
||||
|
||||
case addrs.OutputValue:
|
||||
val, valDiags := normalizeRefValue(s.Data.GetOutput(subj, rng))
|
||||
diags = diags.Append(valDiags)
|
||||
outputValues[subj.Name] = val
|
||||
|
||||
case addrs.Check:
|
||||
val, valDiags := normalizeRefValue(s.Data.GetCheckBlock(subj, rng))
|
||||
diags = diags.Append(valDiags)
|
||||
outputValues[subj.Name] = val
|
||||
|
||||
default:
|
||||
// Should never happen
|
||||
panic(fmt.Errorf("Scope.buildEvalContext cannot handle address type %T", rawSubj))
|
||||
@ -429,6 +442,17 @@ func (s *Scope) evalContext(refs []*addrs.Reference, selfAddr addrs.Referenceabl
|
||||
vals["terraform"] = cty.ObjectVal(terraformAttrs)
|
||||
vals["count"] = cty.ObjectVal(countAttrs)
|
||||
vals["each"] = cty.ObjectVal(forEachAttrs)
|
||||
|
||||
// Checks and outputs are conditionally included in the available scope, so
|
||||
// we'll only write out their values if we actually have something for them.
|
||||
if len(checkBlocks) > 0 {
|
||||
vals["check"] = cty.ObjectVal(checkBlocks)
|
||||
}
|
||||
|
||||
if len(outputValues) > 0 {
|
||||
vals["output"] = cty.ObjectVal(outputValues)
|
||||
}
|
||||
|
||||
if self != cty.NilVal {
|
||||
vals["self"] = self
|
||||
}
|
||||
|
@ -367,13 +367,14 @@ func TestScopeEvalContext(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
refs, refsDiags := ReferencesInExpr(expr)
|
||||
refs, refsDiags := ReferencesInExpr(addrs.ParseRef, expr)
|
||||
if refsDiags.HasErrors() {
|
||||
t.Fatal(refsDiags.Err())
|
||||
}
|
||||
|
||||
scope := &Scope{
|
||||
Data: data,
|
||||
ParseRef: addrs.ParseRef,
|
||||
|
||||
// "self" will just be an arbitrary one of the several resource
|
||||
// instances we have in our test dataset.
|
||||
@ -681,6 +682,7 @@ func TestScopeExpandEvalBlock(t *testing.T) {
|
||||
body := file.Body
|
||||
scope := &Scope{
|
||||
Data: data,
|
||||
ParseRef: addrs.ParseRef,
|
||||
}
|
||||
|
||||
body, expandDiags := scope.ExpandBlock(body, schema)
|
||||
@ -827,6 +829,7 @@ func TestScopeEvalSelfBlock(t *testing.T) {
|
||||
|
||||
scope := &Scope{
|
||||
Data: data,
|
||||
ParseRef: addrs.ParseRef,
|
||||
}
|
||||
|
||||
gotVal, ctxDiags := scope.EvalSelfBlock(body, test.Self, schema, test.KeyData)
|
||||
|
@ -5,12 +5,13 @@ package globalref
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/lang"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
"github.com/zclconf/go-cty/cty/gocty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/lang"
|
||||
)
|
||||
|
||||
// MetaReferences inspects the configuration to find the references contained
|
||||
@ -117,7 +118,7 @@ func (a *Analyzer) metaReferencesInputVariable(calleeAddr addrs.ModuleInstance,
|
||||
if attr == nil {
|
||||
return nil
|
||||
}
|
||||
refs, _ := lang.ReferencesInExpr(attr.Expr)
|
||||
refs, _ := lang.ReferencesInExpr(addrs.ParseRef, attr.Expr)
|
||||
return absoluteRefs(callerAddr, refs)
|
||||
}
|
||||
|
||||
@ -137,7 +138,7 @@ func (a *Analyzer) metaReferencesOutputValue(callerAddr addrs.ModuleInstance, ad
|
||||
|
||||
// We don't check for errors here because we'll make a best effort to
|
||||
// analyze whatever partial result HCL is able to extract.
|
||||
refs, _ := lang.ReferencesInExpr(oc.Expr)
|
||||
refs, _ := lang.ReferencesInExpr(addrs.ParseRef, oc.Expr)
|
||||
return absoluteRefs(calleeAddr, refs)
|
||||
}
|
||||
|
||||
@ -154,7 +155,7 @@ func (a *Analyzer) metaReferencesLocalValue(moduleAddr addrs.ModuleInstance, add
|
||||
|
||||
// We don't check for errors here because we'll make a best effort to
|
||||
// analyze whatever partial result HCL is able to extract.
|
||||
refs, _ := lang.ReferencesInExpr(local.Expr)
|
||||
refs, _ := lang.ReferencesInExpr(addrs.ParseRef, local.Expr)
|
||||
return absoluteRefs(moduleAddr, refs)
|
||||
}
|
||||
|
||||
@ -388,12 +389,12 @@ Steps:
|
||||
|
||||
var refs []*addrs.Reference
|
||||
for _, expr := range exprs {
|
||||
moreRefs, _ := lang.ReferencesInExpr(expr)
|
||||
moreRefs, _ := lang.ReferencesInExpr(addrs.ParseRef, expr)
|
||||
refs = append(refs, moreRefs...)
|
||||
}
|
||||
if schema != nil {
|
||||
for _, body := range bodies {
|
||||
moreRefs, _ := lang.ReferencesInBlock(body, schema)
|
||||
moreRefs, _ := lang.ReferencesInBlock(addrs.ParseRef, body, schema)
|
||||
refs = append(refs, moreRefs...)
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ func (a *Analyzer) ReferencesFromOutputValue(addr addrs.AbsOutputValue) []Refere
|
||||
if oc == nil {
|
||||
return nil
|
||||
}
|
||||
refs, _ := lang.ReferencesInExpr(oc.Expr)
|
||||
refs, _ := lang.ReferencesInExpr(addrs.ParseRef, oc.Expr)
|
||||
return absoluteRefs(addr.Module, refs)
|
||||
}
|
||||
|
||||
@ -79,10 +79,10 @@ func (a *Analyzer) ReferencesFromResourceRepetition(addr addrs.AbsResource) []Re
|
||||
|
||||
switch {
|
||||
case rc.ForEach != nil:
|
||||
refs, _ := lang.ReferencesInExpr(rc.ForEach)
|
||||
refs, _ := lang.ReferencesInExpr(addrs.ParseRef, rc.ForEach)
|
||||
return absoluteRefs(addr.Module, refs)
|
||||
case rc.Count != nil:
|
||||
refs, _ := lang.ReferencesInExpr(rc.Count)
|
||||
refs, _ := lang.ReferencesInExpr(addrs.ParseRef, rc.Count)
|
||||
return absoluteRefs(addr.Module, refs)
|
||||
default:
|
||||
return nil
|
||||
|
@ -5,6 +5,7 @@ package lang
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/lang/blocktoattr"
|
||||
@ -24,7 +25,7 @@ import (
|
||||
// incomplete or invalid. Otherwise, the returned slice has one reference per
|
||||
// given traversal, though it is not guaranteed that the references will
|
||||
// appear in the same order as the given traversals.
|
||||
func References(traversals []hcl.Traversal) ([]*addrs.Reference, tfdiags.Diagnostics) {
|
||||
func References(parseRef ParseRef, traversals []hcl.Traversal) ([]*addrs.Reference, tfdiags.Diagnostics) {
|
||||
if len(traversals) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@ -33,7 +34,7 @@ func References(traversals []hcl.Traversal) ([]*addrs.Reference, tfdiags.Diagnos
|
||||
refs := make([]*addrs.Reference, 0, len(traversals))
|
||||
|
||||
for _, traversal := range traversals {
|
||||
ref, refDiags := addrs.ParseRef(traversal)
|
||||
ref, refDiags := parseRef(traversal)
|
||||
diags = diags.Append(refDiags)
|
||||
if ref == nil {
|
||||
continue
|
||||
@ -50,7 +51,7 @@ func References(traversals []hcl.Traversal) ([]*addrs.Reference, tfdiags.Diagnos
|
||||
//
|
||||
// A block schema must be provided so that this function can determine where in
|
||||
// the body variables are expected.
|
||||
func ReferencesInBlock(body hcl.Body, schema *configschema.Block) ([]*addrs.Reference, tfdiags.Diagnostics) {
|
||||
func ReferencesInBlock(parseRef ParseRef, body hcl.Body, schema *configschema.Block) ([]*addrs.Reference, tfdiags.Diagnostics) {
|
||||
if body == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@ -69,16 +70,16 @@ func ReferencesInBlock(body hcl.Body, schema *configschema.Block) ([]*addrs.Refe
|
||||
// in a better position to test this due to having mock providers etc
|
||||
// available.
|
||||
traversals := blocktoattr.ExpandedVariables(body, schema)
|
||||
return References(traversals)
|
||||
return References(parseRef, traversals)
|
||||
}
|
||||
|
||||
// ReferencesInExpr is a helper wrapper around References that first searches
|
||||
// the given expression for traversals, before converting those traversals
|
||||
// to references.
|
||||
func ReferencesInExpr(expr hcl.Expression) ([]*addrs.Reference, tfdiags.Diagnostics) {
|
||||
func ReferencesInExpr(parseRef ParseRef, expr hcl.Expression) ([]*addrs.Reference, tfdiags.Diagnostics) {
|
||||
if expr == nil {
|
||||
return nil, nil
|
||||
}
|
||||
traversals := expr.Variables()
|
||||
return References(traversals)
|
||||
return References(parseRef, traversals)
|
||||
}
|
||||
|
@ -7,12 +7,16 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/experiments"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
)
|
||||
|
||||
type ParseRef func(traversal hcl.Traversal) (*addrs.Reference, tfdiags.Diagnostics)
|
||||
|
||||
// Scope is the main type in this package, allowing dynamic evaluation of
|
||||
// blocks and expressions based on some contextual information that informs
|
||||
// which variables and functions will be available.
|
||||
@ -20,6 +24,14 @@ type Scope struct {
|
||||
// Data is used to resolve references in expressions.
|
||||
Data Data
|
||||
|
||||
// ParseRef is a function that the scope uses to extract references from
|
||||
// a hcl.Traversal. This controls the type of references the scope currently
|
||||
// supports. As an example, the testing scope can reference outputs directly
|
||||
// while the main Terraform context scope can not. This means that this
|
||||
// function for the testing scope will happily return outputs, while the
|
||||
// main context scope would fail if a user attempts to reference an output.
|
||||
ParseRef ParseRef
|
||||
|
||||
// SelfAddr is the address that the "self" object should be an alias of,
|
||||
// or nil if the "self" object should not be available at all.
|
||||
SelfAddr addrs.Referenceable
|
||||
|
@ -73,9 +73,9 @@ type checkResult struct {
|
||||
func validateCheckRule(typ addrs.CheckRuleType, rule *configs.CheckRule, ctx EvalContext, self addrs.Checkable, keyData instances.RepetitionData) (string, *hcl.EvalContext, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
refs, moreDiags := lang.ReferencesInExpr(rule.Condition)
|
||||
refs, moreDiags := lang.ReferencesInExpr(addrs.ParseRef, rule.Condition)
|
||||
diags = diags.Append(moreDiags)
|
||||
moreRefs, moreDiags := lang.ReferencesInExpr(rule.ErrorMessage)
|
||||
moreRefs, moreDiags := lang.ReferencesInExpr(addrs.ParseRef, rule.ErrorMessage)
|
||||
diags = diags.Append(moreDiags)
|
||||
refs = append(refs, moreRefs...)
|
||||
|
||||
|
@ -7,10 +7,12 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/lang"
|
||||
"github.com/hashicorp/terraform/internal/lang/marks"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// evaluateForEachExpression is our standard mechanism for interpreting an
|
||||
@ -44,7 +46,7 @@ func evaluateForEachExpressionValue(expr hcl.Expression, ctx EvalContext, allowU
|
||||
return nullMap, diags
|
||||
}
|
||||
|
||||
refs, moreDiags := lang.ReferencesInExpr(expr)
|
||||
refs, moreDiags := lang.ReferencesInExpr(addrs.ParseRef, expr)
|
||||
diags = diags.Append(moreDiags)
|
||||
scope := ctx.EvaluationScope(nil, nil, EvalDataForNoInstanceKey)
|
||||
var hclCtx *hcl.EvalContext
|
||||
|
@ -77,6 +77,7 @@ type Evaluator struct {
|
||||
func (e *Evaluator) Scope(data lang.Data, self addrs.Referenceable, source addrs.Referenceable) *lang.Scope {
|
||||
return &lang.Scope{
|
||||
Data: data,
|
||||
ParseRef: addrs.ParseRef,
|
||||
SelfAddr: self,
|
||||
SourceAddr: source,
|
||||
PureOnly: e.Operation != walkApply && e.Operation != walkDestroy && e.Operation != walkEval,
|
||||
@ -940,6 +941,70 @@ func (d *evaluationStateData) GetTerraformAttr(addr addrs.TerraformAttr, rng tfd
|
||||
}
|
||||
}
|
||||
|
||||
func (d *evaluationStateData) GetOutput(addr addrs.OutputValue, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
// First we'll make sure the requested value is declared in configuration,
|
||||
// so we can produce a nice message if not.
|
||||
moduleConfig := d.Evaluator.Config.DescendentForInstance(d.ModulePath)
|
||||
if moduleConfig == nil {
|
||||
// should never happen, since we can't be evaluating in a module
|
||||
// that wasn't mentioned in configuration.
|
||||
panic(fmt.Sprintf("output value read from %s, which has no configuration", d.ModulePath))
|
||||
}
|
||||
|
||||
config := moduleConfig.Module.Outputs[addr.Name]
|
||||
if config == nil {
|
||||
var suggestions []string
|
||||
for k := range moduleConfig.Module.Outputs {
|
||||
suggestions = append(suggestions, k)
|
||||
}
|
||||
suggestion := didyoumean.NameSuggestion(addr.Name, suggestions)
|
||||
if suggestion != "" {
|
||||
suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
|
||||
}
|
||||
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: `Reference to undeclared output value`,
|
||||
Detail: fmt.Sprintf(`An output value with the name %q has not been declared.%s`, addr.Name, suggestion),
|
||||
Subject: rng.ToHCL().Ptr(),
|
||||
})
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
|
||||
output := d.Evaluator.State.OutputValue(addr.Absolute(d.ModulePath))
|
||||
|
||||
val := output.Value
|
||||
if val == cty.NilVal {
|
||||
// Not evaluated yet?
|
||||
val = cty.DynamicVal
|
||||
}
|
||||
|
||||
if output.Sensitive {
|
||||
val = val.Mark(marks.Sensitive)
|
||||
}
|
||||
|
||||
return val, diags
|
||||
}
|
||||
|
||||
func (d *evaluationStateData) GetCheckBlock(addr addrs.Check, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
|
||||
// For now, check blocks don't contain any meaningful data and can only
|
||||
// be referenced from the testing scope within an expect_failures attribute.
|
||||
//
|
||||
// We've added them into the scope explicitly since they are referencable,
|
||||
// but we'll actually just return an error message saying they can't be
|
||||
// referenced in this context.
|
||||
var diags tfdiags.Diagnostics
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Reference to \"check\" in invalid context",
|
||||
Detail: "The \"check\" object can only be referenced from an \"expect_failures\" attribute within a Terraform testing \"run\" block.",
|
||||
Subject: rng.ToHCL().Ptr(),
|
||||
})
|
||||
return cty.NilVal, diags
|
||||
}
|
||||
|
||||
// moduleDisplayAddr returns a string describing the given module instance
|
||||
// address that is appropriate for returning to users in situations where the
|
||||
// root module is possible. Specifically, it returns "the root module" if the
|
||||
|
@ -87,6 +87,70 @@ func TestEvaluatorGetPathAttr(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestEvaluatorGetOutputValue(t *testing.T) {
|
||||
evaluator := &Evaluator{
|
||||
Meta: &ContextMeta{
|
||||
Env: "foo",
|
||||
},
|
||||
Config: &configs.Config{
|
||||
Module: &configs.Module{
|
||||
Outputs: map[string]*configs.Output{
|
||||
"some_output": {
|
||||
Name: "some_output",
|
||||
Sensitive: true,
|
||||
},
|
||||
"some_other_output": {
|
||||
Name: "some_other_output",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
State: states.BuildState(func(state *states.SyncState) {
|
||||
state.SetOutputValue(addrs.AbsOutputValue{
|
||||
Module: addrs.RootModuleInstance,
|
||||
OutputValue: addrs.OutputValue{
|
||||
Name: "some_output",
|
||||
},
|
||||
}, cty.StringVal("first"), true)
|
||||
state.SetOutputValue(addrs.AbsOutputValue{
|
||||
Module: addrs.RootModuleInstance,
|
||||
OutputValue: addrs.OutputValue{
|
||||
Name: "some_other_output",
|
||||
},
|
||||
}, cty.StringVal("second"), false)
|
||||
}).SyncWrapper(),
|
||||
}
|
||||
|
||||
data := &evaluationStateData{
|
||||
Evaluator: evaluator,
|
||||
}
|
||||
scope := evaluator.Scope(data, nil, nil)
|
||||
|
||||
want := cty.StringVal("first").Mark(marks.Sensitive)
|
||||
got, diags := scope.Data.GetOutput(addrs.OutputValue{
|
||||
Name: "some_output",
|
||||
}, tfdiags.SourceRange{})
|
||||
|
||||
if len(diags) != 0 {
|
||||
t.Errorf("unexpected diagnostics %s", spew.Sdump(diags))
|
||||
}
|
||||
if !got.RawEquals(want) {
|
||||
t.Errorf("wrong result %#v; want %#v", got, want)
|
||||
}
|
||||
|
||||
want = cty.StringVal("second")
|
||||
got, diags = scope.Data.GetOutput(addrs.OutputValue{
|
||||
Name: "some_other_output",
|
||||
}, tfdiags.SourceRange{})
|
||||
|
||||
if len(diags) != 0 {
|
||||
t.Errorf("unexpected diagnostics %s", spew.Sdump(diags))
|
||||
}
|
||||
if !got.RawEquals(want) {
|
||||
t.Errorf("wrong result %#v; want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// This particularly tests that a sensitive attribute in config
|
||||
// results in a value that has a "sensitive" cty Mark
|
||||
func TestEvaluatorGetInputVariable(t *testing.T) {
|
||||
|
@ -115,7 +115,7 @@ For example, to correlate with indices of a referring resource, use:
|
||||
t.Fatal(hclDiags.Error())
|
||||
}
|
||||
|
||||
refs, diags := lang.References([]hcl.Traversal{traversal})
|
||||
refs, diags := lang.References(addrs.ParseRef, []hcl.Traversal{traversal})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
@ -99,8 +99,8 @@ func (n *nodeExpandCheck) References() []*addrs.Reference {
|
||||
for _, assert := range n.config.Asserts {
|
||||
// Check blocks reference anything referenced by conditions or messages
|
||||
// in their check rules.
|
||||
condition, _ := lang.ReferencesInExpr(assert.Condition)
|
||||
message, _ := lang.ReferencesInExpr(assert.ErrorMessage)
|
||||
condition, _ := lang.ReferencesInExpr(addrs.ParseRef, assert.Condition)
|
||||
message, _ := lang.ReferencesInExpr(addrs.ParseRef, assert.ErrorMessage)
|
||||
refs = append(refs, condition...)
|
||||
refs = append(refs, message...)
|
||||
}
|
||||
|
@ -8,12 +8,13 @@ import (
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs"
|
||||
"github.com/hashicorp/terraform/internal/dag"
|
||||
"github.com/hashicorp/terraform/internal/lang"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// nodeExpandLocal represents a named local value in a configuration module,
|
||||
@ -61,7 +62,7 @@ func (n *nodeExpandLocal) ReferenceableAddrs() []addrs.Referenceable {
|
||||
|
||||
// GraphNodeReferencer
|
||||
func (n *nodeExpandLocal) References() []*addrs.Reference {
|
||||
refs, _ := lang.ReferencesInExpr(n.Config.Expr)
|
||||
refs, _ := lang.ReferencesInExpr(addrs.ParseRef, n.Config.Expr)
|
||||
return refs
|
||||
}
|
||||
|
||||
@ -124,7 +125,7 @@ func (n *NodeLocal) ReferenceableAddrs() []addrs.Referenceable {
|
||||
|
||||
// GraphNodeReferencer
|
||||
func (n *NodeLocal) References() []*addrs.Reference {
|
||||
refs, _ := lang.ReferencesInExpr(n.Config.Expr)
|
||||
refs, _ := lang.ReferencesInExpr(addrs.ParseRef, n.Config.Expr)
|
||||
return refs
|
||||
}
|
||||
|
||||
@ -138,7 +139,7 @@ func (n *NodeLocal) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Di
|
||||
|
||||
// We ignore diags here because any problems we might find will be found
|
||||
// again in EvaluateExpr below.
|
||||
refs, _ := lang.ReferencesInExpr(expr)
|
||||
refs, _ := lang.ReferencesInExpr(addrs.ParseRef, expr)
|
||||
for _, ref := range refs {
|
||||
if ref.Subject == addr {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
|
@ -64,11 +64,11 @@ func (n *nodeExpandModule) References() []*addrs.Reference {
|
||||
// child module instances we might expand to during our evaluation.
|
||||
|
||||
if n.ModuleCall.Count != nil {
|
||||
countRefs, _ := lang.ReferencesInExpr(n.ModuleCall.Count)
|
||||
countRefs, _ := lang.ReferencesInExpr(addrs.ParseRef, n.ModuleCall.Count)
|
||||
refs = append(refs, countRefs...)
|
||||
}
|
||||
if n.ModuleCall.ForEach != nil {
|
||||
forEachRefs, _ := lang.ReferencesInExpr(n.ModuleCall.ForEach)
|
||||
forEachRefs, _ := lang.ReferencesInExpr(addrs.ParseRef, n.ModuleCall.ForEach)
|
||||
refs = append(refs, forEachRefs...)
|
||||
}
|
||||
return refs
|
||||
|
@ -8,13 +8,14 @@ import (
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs"
|
||||
"github.com/hashicorp/terraform/internal/dag"
|
||||
"github.com/hashicorp/terraform/internal/instances"
|
||||
"github.com/hashicorp/terraform/internal/lang"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// nodeExpandModuleVariable is the placeholder for an variable that has not yet had
|
||||
@ -90,7 +91,7 @@ func (n *nodeExpandModuleVariable) References() []*addrs.Reference {
|
||||
// where our associated variable was declared, which is correct because
|
||||
// our value expression is assigned within a "module" block in the parent
|
||||
// module.
|
||||
refs, _ := lang.ReferencesInExpr(n.Expr)
|
||||
refs, _ := lang.ReferencesInExpr(addrs.ParseRef, n.Expr)
|
||||
return refs
|
||||
}
|
||||
|
||||
|
@ -282,16 +282,16 @@ func (n *NodeApplyableOutput) ReferenceableAddrs() []addrs.Referenceable {
|
||||
func referencesForOutput(c *configs.Output) []*addrs.Reference {
|
||||
var refs []*addrs.Reference
|
||||
|
||||
impRefs, _ := lang.ReferencesInExpr(c.Expr)
|
||||
expRefs, _ := lang.References(c.DependsOn)
|
||||
impRefs, _ := lang.ReferencesInExpr(addrs.ParseRef, c.Expr)
|
||||
expRefs, _ := lang.References(addrs.ParseRef, c.DependsOn)
|
||||
|
||||
refs = append(refs, impRefs...)
|
||||
refs = append(refs, expRefs...)
|
||||
|
||||
for _, check := range c.Preconditions {
|
||||
condRefs, _ := lang.ReferencesInExpr(check.Condition)
|
||||
condRefs, _ := lang.ReferencesInExpr(addrs.ParseRef, check.Condition)
|
||||
refs = append(refs, condRefs...)
|
||||
errRefs, _ := lang.ReferencesInExpr(check.ErrorMessage)
|
||||
errRefs, _ := lang.ReferencesInExpr(addrs.ParseRef, check.ErrorMessage)
|
||||
refs = append(refs, errRefs...)
|
||||
}
|
||||
|
||||
|
@ -152,25 +152,25 @@ func (n *NodeAbstractResource) References() []*addrs.Reference {
|
||||
log.Printf("[WARN] no schema is attached to %s, so config references cannot be detected", n.Name())
|
||||
}
|
||||
|
||||
refs, _ := lang.ReferencesInExpr(c.Count)
|
||||
refs, _ := lang.ReferencesInExpr(addrs.ParseRef, c.Count)
|
||||
result = append(result, refs...)
|
||||
refs, _ = lang.ReferencesInExpr(c.ForEach)
|
||||
refs, _ = lang.ReferencesInExpr(addrs.ParseRef, c.ForEach)
|
||||
result = append(result, refs...)
|
||||
|
||||
for _, expr := range c.TriggersReplacement {
|
||||
refs, _ = lang.ReferencesInExpr(expr)
|
||||
refs, _ = lang.ReferencesInExpr(addrs.ParseRef, expr)
|
||||
result = append(result, refs...)
|
||||
}
|
||||
|
||||
// ReferencesInBlock() requires a schema
|
||||
if n.Schema != nil {
|
||||
refs, _ = lang.ReferencesInBlock(c.Config, n.Schema)
|
||||
refs, _ = lang.ReferencesInBlock(addrs.ParseRef, c.Config, n.Schema)
|
||||
result = append(result, refs...)
|
||||
}
|
||||
|
||||
if c.Managed != nil {
|
||||
if c.Managed.Connection != nil {
|
||||
refs, _ = lang.ReferencesInBlock(c.Managed.Connection.Config, connectionBlockSupersetSchema)
|
||||
refs, _ = lang.ReferencesInBlock(addrs.ParseRef, c.Managed.Connection.Config, connectionBlockSupersetSchema)
|
||||
result = append(result, refs...)
|
||||
}
|
||||
|
||||
@ -179,7 +179,7 @@ func (n *NodeAbstractResource) References() []*addrs.Reference {
|
||||
continue
|
||||
}
|
||||
if p.Connection != nil {
|
||||
refs, _ = lang.ReferencesInBlock(p.Connection.Config, connectionBlockSupersetSchema)
|
||||
refs, _ = lang.ReferencesInBlock(addrs.ParseRef, p.Connection.Config, connectionBlockSupersetSchema)
|
||||
result = append(result, refs...)
|
||||
}
|
||||
|
||||
@ -187,21 +187,21 @@ func (n *NodeAbstractResource) References() []*addrs.Reference {
|
||||
if schema == nil {
|
||||
log.Printf("[WARN] no schema for provisioner %q is attached to %s, so provisioner block references cannot be detected", p.Type, n.Name())
|
||||
}
|
||||
refs, _ = lang.ReferencesInBlock(p.Config, schema)
|
||||
refs, _ = lang.ReferencesInBlock(addrs.ParseRef, p.Config, schema)
|
||||
result = append(result, refs...)
|
||||
}
|
||||
}
|
||||
|
||||
for _, check := range c.Preconditions {
|
||||
refs, _ := lang.ReferencesInExpr(check.Condition)
|
||||
refs, _ := lang.ReferencesInExpr(addrs.ParseRef, check.Condition)
|
||||
result = append(result, refs...)
|
||||
refs, _ = lang.ReferencesInExpr(check.ErrorMessage)
|
||||
refs, _ = lang.ReferencesInExpr(addrs.ParseRef, check.ErrorMessage)
|
||||
result = append(result, refs...)
|
||||
}
|
||||
for _, check := range c.Postconditions {
|
||||
refs, _ := lang.ReferencesInExpr(check.Condition)
|
||||
refs, _ := lang.ReferencesInExpr(addrs.ParseRef, check.Condition)
|
||||
result = append(result, refs...)
|
||||
refs, _ = lang.ReferencesInExpr(check.ErrorMessage)
|
||||
refs, _ = lang.ReferencesInExpr(addrs.ParseRef, check.ErrorMessage)
|
||||
result = append(result, refs...)
|
||||
}
|
||||
|
||||
|
@ -95,9 +95,9 @@ func (n *NodeApplyableResource) References() []*addrs.Reference {
|
||||
// Since this node type only updates resource-level metadata, we only
|
||||
// need to worry about the parts of the configuration that affect
|
||||
// our "each mode": the count and for_each meta-arguments.
|
||||
refs, _ := lang.ReferencesInExpr(n.Config.Count)
|
||||
refs, _ := lang.ReferencesInExpr(addrs.ParseRef, n.Config.Count)
|
||||
result = append(result, refs...)
|
||||
refs, _ = lang.ReferencesInExpr(n.Config.ForEach)
|
||||
refs, _ = lang.ReferencesInExpr(addrs.ParseRef, n.Config.ForEach)
|
||||
result = append(result, refs...)
|
||||
|
||||
return result
|
||||
|
@ -8,6 +8,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
@ -17,7 +19,6 @@ import (
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
"github.com/hashicorp/terraform/internal/provisioners"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// NodeValidatableResource represents a resource that is used for validation
|
||||
@ -469,7 +470,7 @@ func (n *NodeValidatableResource) validateResource(ctx EvalContext) tfdiags.Diag
|
||||
func (n *NodeValidatableResource) evaluateExpr(ctx EvalContext, expr hcl.Expression, wantTy cty.Type, self addrs.Referenceable, keyData instances.RepetitionData) (cty.Value, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
refs, refDiags := lang.ReferencesInExpr(expr)
|
||||
refs, refDiags := lang.ReferencesInExpr(addrs.ParseRef, expr)
|
||||
diags = diags.Append(refDiags)
|
||||
|
||||
scope := ctx.EvaluationScope(self, nil, keyData)
|
||||
|
@ -100,9 +100,9 @@ func (ctx *TestContext) evaluate(state *states.SyncState, changes *plans.Changes
|
||||
|
||||
// Now validate all the assertions within this run block.
|
||||
for _, rule := range run.Config.CheckRules {
|
||||
refs, moreDiags := lang.ReferencesInExpr(rule.Condition)
|
||||
refs, moreDiags := lang.ReferencesInExpr(addrs.ParseRefFromTestingScope, rule.Condition)
|
||||
run.Diagnostics = run.Diagnostics.Append(moreDiags)
|
||||
moreRefs, moreDiags := lang.ReferencesInExpr(rule.ErrorMessage)
|
||||
moreRefs, moreDiags := lang.ReferencesInExpr(addrs.ParseRefFromTestingScope, rule.ErrorMessage)
|
||||
run.Diagnostics = run.Diagnostics.Append(moreDiags)
|
||||
refs = append(refs, moreRefs...)
|
||||
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"sort"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/dag"
|
||||
@ -555,6 +556,6 @@ func ReferencesFromConfig(body hcl.Body, schema *configschema.Block) []*addrs.Re
|
||||
if body == nil {
|
||||
return nil
|
||||
}
|
||||
refs, _ := lang.ReferencesInBlock(body, schema)
|
||||
refs, _ := lang.ReferencesInBlock(addrs.ParseRef, body, schema)
|
||||
return refs
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ func validateSelfRef(addr addrs.Referenceable, config hcl.Body, providerSchema *
|
||||
return diags
|
||||
}
|
||||
|
||||
refs, _ := lang.ReferencesInBlock(config, schema)
|
||||
refs, _ := lang.ReferencesInBlock(addrs.ParseRef, config, schema)
|
||||
for _, ref := range refs {
|
||||
for _, addrStr := range addrStrs {
|
||||
if ref.Subject.String() == addrStr {
|
||||
|
Loading…
Reference in New Issue
Block a user