change lang eval to also only lookup resources

Continue only evaluating resource at a whole and push the indexing of
the resource down into the expression evaluation.

The exception here is that `self` must be an instance which must be
extracted from the resource. We now also add the entire resource to the
context, which was previously only partially populated with the self
referenced instance.
This commit is contained in:
James Bardin 2019-09-19 09:13:47 -04:00
parent da8ec6b483
commit c49f976308
3 changed files with 90 additions and 45 deletions

View File

@ -7,14 +7,14 @@ import (
) )
type dataForTests struct { type dataForTests struct {
CountAttrs map[string]cty.Value CountAttrs map[string]cty.Value
ForEachAttrs map[string]cty.Value ForEachAttrs map[string]cty.Value
ResourceInstances map[string]cty.Value Resources map[string]cty.Value
LocalValues map[string]cty.Value LocalValues map[string]cty.Value
Modules map[string]cty.Value Modules map[string]cty.Value
PathAttrs map[string]cty.Value PathAttrs map[string]cty.Value
TerraformAttrs map[string]cty.Value TerraformAttrs map[string]cty.Value
InputVariables map[string]cty.Value InputVariables map[string]cty.Value
} }
var _ Data = &dataForTests{} var _ Data = &dataForTests{}
@ -31,8 +31,8 @@ func (d *dataForTests) GetForEachAttr(addr addrs.ForEachAttr, rng tfdiags.Source
return d.ForEachAttrs[addr.Name], nil return d.ForEachAttrs[addr.Name], nil
} }
func (d *dataForTests) GetResourceInstance(addr addrs.ResourceInstance, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) { func (d *dataForTests) GetResource(addr addrs.Resource, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
return d.ResourceInstances[addr.String()], nil return d.Resources[addr.String()], nil
} }
func (d *dataForTests) GetInputVariable(addr addrs.InputVariable, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) { func (d *dataForTests) GetInputVariable(addr addrs.InputVariable, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {

View File

@ -208,7 +208,6 @@ func (s *Scope) evalContext(refs []*addrs.Reference, selfAddr addrs.Referenceabl
for _, ref := range refs { for _, ref := range refs {
rng := ref.SourceRange rng := ref.SourceRange
isSelf := false
rawSubj := ref.Subject rawSubj := ref.Subject
if rawSubj == addrs.Self { if rawSubj == addrs.Self {
@ -226,19 +225,62 @@ func (s *Scope) evalContext(refs []*addrs.Reference, selfAddr addrs.Referenceabl
continue continue
} }
// Treat "self" as an alias for the configured self address. // self can only be used within a resource instance
rawSubj = selfAddr subj := selfAddr.(addrs.ResourceInstance)
isSelf = true
if rawSubj == addrs.Self { if selfAddr == addrs.Self {
// Programming error: the self address cannot alias itself. // Programming error: the self address cannot alias itself.
panic("scope SelfAddr attempting to alias itself") panic("scope SelfAddr attempting to alias itself")
} }
val, valDiags := normalizeRefValue(s.Data.GetResource(subj.ContainingResource(), rng))
diags = diags.Append(valDiags)
// Self is an exception in that it must always resolve to a
// particular instance. We will still insert the full resource into
// the context below.
switch k := subj.Key.(type) {
case addrs.IntKey:
self = val.Index(cty.NumberIntVal(int64(k)))
case addrs.StringKey:
self = val.Index(cty.StringVal(string(k)))
default:
self = val
}
r := subj.Resource
if managedResources[r.Type] == nil {
managedResources[r.Type] = make(map[string]cty.Value)
}
managedResources[r.Type][r.Name] = val
continue
} }
// This type switch must cover all of the "Referenceable" implementations // This type switch must cover all of the "Referenceable" implementations
// in package addrs. // in package addrs.
switch subj := rawSubj.(type) { switch subj := rawSubj.(type) {
case addrs.Resource:
panic("RESOURCE REFERENCES DON'T HIT THIS")
var into map[string]map[string]cty.Value
switch subj.Mode {
case addrs.ManagedResourceMode:
into = managedResources
case addrs.DataResourceMode:
into = dataResources
default:
panic(fmt.Errorf("unsupported ResourceMode %s", subj.Mode))
}
val, valDiags := normalizeRefValue(s.Data.GetResource(subj, rng))
diags = diags.Append(valDiags)
r := subj
if into[r.Type] == nil {
into[r.Type] = make(map[string]cty.Value)
}
into[r.Type][r.Name] = val
case addrs.ResourceInstance: case addrs.ResourceInstance:
var into map[string]map[string]cty.Value var into map[string]map[string]cty.Value
@ -252,6 +294,7 @@ func (s *Scope) evalContext(refs []*addrs.Reference, selfAddr addrs.Referenceabl
} }
val, valDiags := normalizeRefValue(s.Data.GetResource(subj.ContainingResource(), rng)) val, valDiags := normalizeRefValue(s.Data.GetResource(subj.ContainingResource(), rng))
diags = diags.Append(valDiags) diags = diags.Append(valDiags)
r := subj.Resource r := subj.Resource
@ -259,9 +302,6 @@ func (s *Scope) evalContext(refs []*addrs.Reference, selfAddr addrs.Referenceabl
into[r.Type] = make(map[string]cty.Value) into[r.Type] = make(map[string]cty.Value)
} }
into[r.Type][r.Name] = val into[r.Type][r.Name] = val
if isSelf {
self = val
}
case addrs.ModuleCallInstance: case addrs.ModuleCallInstance:
val, valDiags := normalizeRefValue(s.Data.GetModuleInstance(subj, rng)) val, valDiags := normalizeRefValue(s.Data.GetModuleInstance(subj, rng))
@ -271,9 +311,6 @@ func (s *Scope) evalContext(refs []*addrs.Reference, selfAddr addrs.Referenceabl
wholeModules[subj.Call.Name] = make(map[addrs.InstanceKey]cty.Value) wholeModules[subj.Call.Name] = make(map[addrs.InstanceKey]cty.Value)
} }
wholeModules[subj.Call.Name][subj.Key] = val wholeModules[subj.Call.Name][subj.Key] = val
if isSelf {
self = val
}
case addrs.ModuleCallOutput: case addrs.ModuleCallOutput:
val, valDiags := normalizeRefValue(s.Data.GetModuleInstanceOutput(subj, rng)) val, valDiags := normalizeRefValue(s.Data.GetModuleInstanceOutput(subj, rng))
@ -288,57 +325,36 @@ func (s *Scope) evalContext(refs []*addrs.Reference, selfAddr addrs.Referenceabl
moduleOutputs[callName][callKey] = make(map[string]cty.Value) moduleOutputs[callName][callKey] = make(map[string]cty.Value)
} }
moduleOutputs[callName][callKey][subj.Name] = val moduleOutputs[callName][callKey][subj.Name] = val
if isSelf {
self = val
}
case addrs.InputVariable: case addrs.InputVariable:
val, valDiags := normalizeRefValue(s.Data.GetInputVariable(subj, rng)) val, valDiags := normalizeRefValue(s.Data.GetInputVariable(subj, rng))
diags = diags.Append(valDiags) diags = diags.Append(valDiags)
inputVariables[subj.Name] = val inputVariables[subj.Name] = val
if isSelf {
self = val
}
case addrs.LocalValue: case addrs.LocalValue:
val, valDiags := normalizeRefValue(s.Data.GetLocalValue(subj, rng)) val, valDiags := normalizeRefValue(s.Data.GetLocalValue(subj, rng))
diags = diags.Append(valDiags) diags = diags.Append(valDiags)
localValues[subj.Name] = val localValues[subj.Name] = val
if isSelf {
self = val
}
case addrs.PathAttr: case addrs.PathAttr:
val, valDiags := normalizeRefValue(s.Data.GetPathAttr(subj, rng)) val, valDiags := normalizeRefValue(s.Data.GetPathAttr(subj, rng))
diags = diags.Append(valDiags) diags = diags.Append(valDiags)
pathAttrs[subj.Name] = val pathAttrs[subj.Name] = val
if isSelf {
self = val
}
case addrs.TerraformAttr: case addrs.TerraformAttr:
val, valDiags := normalizeRefValue(s.Data.GetTerraformAttr(subj, rng)) val, valDiags := normalizeRefValue(s.Data.GetTerraformAttr(subj, rng))
diags = diags.Append(valDiags) diags = diags.Append(valDiags)
terraformAttrs[subj.Name] = val terraformAttrs[subj.Name] = val
if isSelf {
self = val
}
case addrs.CountAttr: case addrs.CountAttr:
val, valDiags := normalizeRefValue(s.Data.GetCountAttr(subj, rng)) val, valDiags := normalizeRefValue(s.Data.GetCountAttr(subj, rng))
diags = diags.Append(valDiags) diags = diags.Append(valDiags)
countAttrs[subj.Name] = val countAttrs[subj.Name] = val
if isSelf {
self = val
}
case addrs.ForEachAttr: case addrs.ForEachAttr:
val, valDiags := normalizeRefValue(s.Data.GetForEachAttr(subj, rng)) val, valDiags := normalizeRefValue(s.Data.GetForEachAttr(subj, rng))
diags = diags.Append(valDiags) diags = diags.Append(valDiags)
forEachAttrs[subj.Name] = val forEachAttrs[subj.Name] = val
if isSelf {
self = val
}
default: default:
// Should never happen // Should never happen

View File

@ -24,7 +24,7 @@ func TestScopeEvalContext(t *testing.T) {
"key": cty.StringVal("a"), "key": cty.StringVal("a"),
"value": cty.NumberIntVal(1), "value": cty.NumberIntVal(1),
}, },
ResourceInstances: map[string]cty.Value{ Resources: map[string]cty.Value{
"null_resource.foo": cty.ObjectVal(map[string]cty.Value{ "null_resource.foo": cty.ObjectVal(map[string]cty.Value{
"attr": cty.StringVal("bar"), "attr": cty.StringVal("bar"),
}), }),
@ -39,6 +39,14 @@ func TestScopeEvalContext(t *testing.T) {
"attr": cty.StringVal("multi1"), "attr": cty.StringVal("multi1"),
}), }),
}), }),
"null_resource.each": cty.ObjectVal(map[string]cty.Value{
"each0": cty.ObjectVal(map[string]cty.Value{
"attr": cty.StringVal("each0"),
}),
"each1": cty.ObjectVal(map[string]cty.Value{
"attr": cty.StringVal("each1"),
}),
}),
"null_resource.multi[1]": cty.ObjectVal(map[string]cty.Value{ "null_resource.multi[1]": cty.ObjectVal(map[string]cty.Value{
"attr": cty.StringVal("multi1"), "attr": cty.StringVal("multi1"),
}), }),
@ -139,11 +147,14 @@ func TestScopeEvalContext(t *testing.T) {
}, },
}, },
{ {
// at this level, all instance references return the entire resource
`null_resource.multi[1]`, `null_resource.multi[1]`,
map[string]cty.Value{ map[string]cty.Value{
"null_resource": cty.ObjectVal(map[string]cty.Value{ "null_resource": cty.ObjectVal(map[string]cty.Value{
"multi": cty.TupleVal([]cty.Value{ "multi": cty.TupleVal([]cty.Value{
cty.DynamicVal, cty.ObjectVal(map[string]cty.Value{
"attr": cty.StringVal("multi0"),
}),
cty.ObjectVal(map[string]cty.Value{ cty.ObjectVal(map[string]cty.Value{
"attr": cty.StringVal("multi1"), "attr": cty.StringVal("multi1"),
}), }),
@ -151,6 +162,22 @@ func TestScopeEvalContext(t *testing.T) {
}), }),
}, },
}, },
{
// at this level, all instance references return the entire resource
`null_resource.each["each1"]`,
map[string]cty.Value{
"null_resource": cty.ObjectVal(map[string]cty.Value{
"each": cty.ObjectVal(map[string]cty.Value{
"each0": cty.ObjectVal(map[string]cty.Value{
"attr": cty.StringVal("each0"),
}),
"each1": cty.ObjectVal(map[string]cty.Value{
"attr": cty.StringVal("each1"),
}),
}),
}),
},
},
{ {
`foo(null_resource.multi, null_resource.multi[1])`, `foo(null_resource.multi, null_resource.multi[1])`,
map[string]cty.Value{ map[string]cty.Value{
@ -215,7 +242,9 @@ func TestScopeEvalContext(t *testing.T) {
// expanded here and then copied into "self". // expanded here and then copied into "self".
"null_resource": cty.ObjectVal(map[string]cty.Value{ "null_resource": cty.ObjectVal(map[string]cty.Value{
"multi": cty.TupleVal([]cty.Value{ "multi": cty.TupleVal([]cty.Value{
cty.DynamicVal, cty.ObjectVal(map[string]cty.Value{
"attr": cty.StringVal("multi0"),
}),
cty.ObjectVal(map[string]cty.Value{ cty.ObjectVal(map[string]cty.Value{
"attr": cty.StringVal("multi1"), "attr": cty.StringVal("multi1"),
}), }),