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 {
CountAttrs map[string]cty.Value
ForEachAttrs map[string]cty.Value
ResourceInstances map[string]cty.Value
LocalValues map[string]cty.Value
Modules map[string]cty.Value
PathAttrs map[string]cty.Value
TerraformAttrs map[string]cty.Value
InputVariables map[string]cty.Value
CountAttrs map[string]cty.Value
ForEachAttrs map[string]cty.Value
Resources map[string]cty.Value
LocalValues map[string]cty.Value
Modules map[string]cty.Value
PathAttrs map[string]cty.Value
TerraformAttrs map[string]cty.Value
InputVariables map[string]cty.Value
}
var _ Data = &dataForTests{}
@ -31,8 +31,8 @@ func (d *dataForTests) GetForEachAttr(addr addrs.ForEachAttr, rng tfdiags.Source
return d.ForEachAttrs[addr.Name], nil
}
func (d *dataForTests) GetResourceInstance(addr addrs.ResourceInstance, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
return d.ResourceInstances[addr.String()], nil
func (d *dataForTests) GetResource(addr addrs.Resource, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
return d.Resources[addr.String()], nil
}
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 {
rng := ref.SourceRange
isSelf := false
rawSubj := ref.Subject
if rawSubj == addrs.Self {
@ -226,19 +225,62 @@ func (s *Scope) evalContext(refs []*addrs.Reference, selfAddr addrs.Referenceabl
continue
}
// Treat "self" as an alias for the configured self address.
rawSubj = selfAddr
isSelf = true
// self can only be used within a resource instance
subj := selfAddr.(addrs.ResourceInstance)
if rawSubj == addrs.Self {
if selfAddr == addrs.Self {
// Programming error: the self address cannot 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
// in package addrs.
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:
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))
diags = diags.Append(valDiags)
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][r.Name] = val
if isSelf {
self = val
}
case addrs.ModuleCallInstance:
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][subj.Key] = val
if isSelf {
self = val
}
case addrs.ModuleCallOutput:
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][subj.Name] = val
if isSelf {
self = val
}
case addrs.InputVariable:
val, valDiags := normalizeRefValue(s.Data.GetInputVariable(subj, rng))
diags = diags.Append(valDiags)
inputVariables[subj.Name] = val
if isSelf {
self = val
}
case addrs.LocalValue:
val, valDiags := normalizeRefValue(s.Data.GetLocalValue(subj, rng))
diags = diags.Append(valDiags)
localValues[subj.Name] = val
if isSelf {
self = val
}
case addrs.PathAttr:
val, valDiags := normalizeRefValue(s.Data.GetPathAttr(subj, rng))
diags = diags.Append(valDiags)
pathAttrs[subj.Name] = val
if isSelf {
self = val
}
case addrs.TerraformAttr:
val, valDiags := normalizeRefValue(s.Data.GetTerraformAttr(subj, rng))
diags = diags.Append(valDiags)
terraformAttrs[subj.Name] = val
if isSelf {
self = val
}
case addrs.CountAttr:
val, valDiags := normalizeRefValue(s.Data.GetCountAttr(subj, rng))
diags = diags.Append(valDiags)
countAttrs[subj.Name] = val
if isSelf {
self = val
}
case addrs.ForEachAttr:
val, valDiags := normalizeRefValue(s.Data.GetForEachAttr(subj, rng))
diags = diags.Append(valDiags)
forEachAttrs[subj.Name] = val
if isSelf {
self = val
}
default:
// Should never happen

View File

@ -24,7 +24,7 @@ func TestScopeEvalContext(t *testing.T) {
"key": cty.StringVal("a"),
"value": cty.NumberIntVal(1),
},
ResourceInstances: map[string]cty.Value{
Resources: map[string]cty.Value{
"null_resource.foo": cty.ObjectVal(map[string]cty.Value{
"attr": cty.StringVal("bar"),
}),
@ -39,6 +39,14 @@ func TestScopeEvalContext(t *testing.T) {
"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{
"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]`,
map[string]cty.Value{
"null_resource": cty.ObjectVal(map[string]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{
"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])`,
map[string]cty.Value{
@ -215,7 +242,9 @@ func TestScopeEvalContext(t *testing.T) {
// expanded here and then copied into "self".
"null_resource": cty.ObjectVal(map[string]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{
"attr": cty.StringVal("multi1"),
}),