Merge pull request #26788 from hashicorp/jbardin/eval-self-block
allow path and terraform in self-block eval
This commit is contained in:
commit
6bb79b7714
52
lang/eval.go
52
lang/eval.go
|
@ -72,8 +72,13 @@ func (s *Scope) EvalBlock(body hcl.Body, schema *configschema.Block) (cty.Value,
|
|||
|
||||
// EvalSelfBlock evaluates the given body only within the scope of the provided
|
||||
// object and instance key data. References to the object must use self, and the
|
||||
// key data will only contain count.index or each.key.
|
||||
// key data will only contain count.index or each.key. The static values for
|
||||
// terraform and path will also be available in this context.
|
||||
func (s *Scope) EvalSelfBlock(body hcl.Body, self cty.Value, schema *configschema.Block, keyData instances.RepetitionData) (cty.Value, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
spec := schema.DecoderSpec()
|
||||
|
||||
vals := make(map[string]cty.Value)
|
||||
vals["self"] = self
|
||||
|
||||
|
@ -88,12 +93,55 @@ func (s *Scope) EvalSelfBlock(body hcl.Body, self cty.Value, schema *configschem
|
|||
})
|
||||
}
|
||||
|
||||
refs, refDiags := References(hcldec.Variables(body, spec))
|
||||
diags = diags.Append(refDiags)
|
||||
|
||||
terraformAttrs := map[string]cty.Value{}
|
||||
pathAttrs := map[string]cty.Value{}
|
||||
|
||||
// We could always load the static values for Path and Terraform values,
|
||||
// but we want to parse the references so that we can get source ranges for
|
||||
// user diagnostics.
|
||||
for _, ref := range refs {
|
||||
// we already loaded the self value
|
||||
if ref.Subject == addrs.Self {
|
||||
continue
|
||||
}
|
||||
|
||||
switch subj := ref.Subject.(type) {
|
||||
case addrs.PathAttr:
|
||||
val, valDiags := normalizeRefValue(s.Data.GetPathAttr(subj, ref.SourceRange))
|
||||
diags = diags.Append(valDiags)
|
||||
pathAttrs[subj.Name] = val
|
||||
|
||||
case addrs.TerraformAttr:
|
||||
val, valDiags := normalizeRefValue(s.Data.GetTerraformAttr(subj, ref.SourceRange))
|
||||
diags = diags.Append(valDiags)
|
||||
terraformAttrs[subj.Name] = val
|
||||
|
||||
case addrs.CountAttr, addrs.ForEachAttr:
|
||||
// each and count have already been handled.
|
||||
|
||||
default:
|
||||
// This should have been caught in validation, but point the user
|
||||
// to the correct location in case something slipped through.
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: `Invalid reference`,
|
||||
Detail: fmt.Sprintf("The reference to %q is not valid in this context", ref.Subject),
|
||||
Subject: ref.SourceRange.ToHCL().Ptr(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
vals["path"] = cty.ObjectVal(pathAttrs)
|
||||
vals["terraform"] = cty.ObjectVal(terraformAttrs)
|
||||
|
||||
ctx := &hcl.EvalContext{
|
||||
Variables: vals,
|
||||
Functions: s.Functions(),
|
||||
}
|
||||
|
||||
var diags tfdiags.Diagnostics
|
||||
val, decDiags := hcldec.Decode(body, schema.DecoderSpec(), ctx)
|
||||
diags = diags.Append(decDiags)
|
||||
return val, diags
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"github.com/hashicorp/terraform/instances"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
|
@ -642,3 +643,128 @@ func formattedJSONValue(val cty.Value) string {
|
|||
json.Indent(&buf, j, "", " ")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func TestScopeEvalSelfBlock(t *testing.T) {
|
||||
data := &dataForTests{
|
||||
PathAttrs: map[string]cty.Value{
|
||||
"module": cty.StringVal("foo/bar"),
|
||||
"cwd": cty.StringVal("/home/foo/bar"),
|
||||
"root": cty.StringVal("/home/foo"),
|
||||
},
|
||||
TerraformAttrs: map[string]cty.Value{
|
||||
"workspace": cty.StringVal("default"),
|
||||
},
|
||||
}
|
||||
schema := &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"attr": {
|
||||
Type: cty.String,
|
||||
},
|
||||
"num": {
|
||||
Type: cty.Number,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
Config string
|
||||
Self cty.Value
|
||||
KeyData instances.RepetitionData
|
||||
Want map[string]cty.Value
|
||||
}{
|
||||
{
|
||||
Config: `attr = self.foo`,
|
||||
Self: cty.ObjectVal(map[string]cty.Value{
|
||||
"foo": cty.StringVal("bar"),
|
||||
}),
|
||||
KeyData: instances.RepetitionData{
|
||||
CountIndex: cty.NumberIntVal(0),
|
||||
},
|
||||
Want: map[string]cty.Value{
|
||||
"attr": cty.StringVal("bar"),
|
||||
"num": cty.NullVal(cty.Number),
|
||||
},
|
||||
},
|
||||
{
|
||||
Config: `num = count.index`,
|
||||
KeyData: instances.RepetitionData{
|
||||
CountIndex: cty.NumberIntVal(0),
|
||||
},
|
||||
Want: map[string]cty.Value{
|
||||
"attr": cty.NullVal(cty.String),
|
||||
"num": cty.NumberIntVal(0),
|
||||
},
|
||||
},
|
||||
{
|
||||
Config: `attr = each.key`,
|
||||
KeyData: instances.RepetitionData{
|
||||
EachKey: cty.StringVal("a"),
|
||||
},
|
||||
Want: map[string]cty.Value{
|
||||
"attr": cty.StringVal("a"),
|
||||
"num": cty.NullVal(cty.Number),
|
||||
},
|
||||
},
|
||||
{
|
||||
Config: `attr = path.cwd`,
|
||||
Want: map[string]cty.Value{
|
||||
"attr": cty.StringVal("/home/foo/bar"),
|
||||
"num": cty.NullVal(cty.Number),
|
||||
},
|
||||
},
|
||||
{
|
||||
Config: `attr = path.module`,
|
||||
Want: map[string]cty.Value{
|
||||
"attr": cty.StringVal("foo/bar"),
|
||||
"num": cty.NullVal(cty.Number),
|
||||
},
|
||||
},
|
||||
{
|
||||
Config: `attr = path.root`,
|
||||
Want: map[string]cty.Value{
|
||||
"attr": cty.StringVal("/home/foo"),
|
||||
"num": cty.NullVal(cty.Number),
|
||||
},
|
||||
},
|
||||
{
|
||||
Config: `attr = terraform.workspace`,
|
||||
Want: map[string]cty.Value{
|
||||
"attr": cty.StringVal("default"),
|
||||
"num": cty.NullVal(cty.Number),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.Config, func(t *testing.T) {
|
||||
file, parseDiags := hclsyntax.ParseConfig([]byte(test.Config), "", hcl.Pos{Line: 1, Column: 1})
|
||||
if len(parseDiags) != 0 {
|
||||
t.Errorf("unexpected diagnostics during parse")
|
||||
for _, diag := range parseDiags {
|
||||
t.Errorf("- %s", diag)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
body := file.Body
|
||||
|
||||
scope := &Scope{
|
||||
Data: data,
|
||||
}
|
||||
|
||||
gotVal, ctxDiags := scope.EvalSelfBlock(body, test.Self, schema, test.KeyData)
|
||||
if ctxDiags.HasErrors() {
|
||||
t.Fatal(ctxDiags.Err())
|
||||
}
|
||||
|
||||
wantVal := cty.ObjectVal(test.Want)
|
||||
|
||||
if !gotVal.RawEquals(wantVal) {
|
||||
t.Errorf(
|
||||
"wrong result\nexpr: %s\ngot: %#v\nwant: %#v",
|
||||
test.Config, gotVal, wantVal,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue