From c49f9763087e0d3d69f531ee8fdb672779937e5d Mon Sep 17 00:00:00 2001 From: James Bardin Date: Thu, 19 Sep 2019 09:13:47 -0400 Subject: [PATCH] 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. --- lang/data_test.go | 20 ++++++------ lang/eval.go | 80 ++++++++++++++++++++++++++++------------------- lang/eval_test.go | 35 +++++++++++++++++++-- 3 files changed, 90 insertions(+), 45 deletions(-) diff --git a/lang/data_test.go b/lang/data_test.go index dba6829580..fd87861fc9 100644 --- a/lang/data_test.go +++ b/lang/data_test.go @@ -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) { diff --git a/lang/eval.go b/lang/eval.go index d4c24e7d62..46baef7c0c 100644 --- a/lang/eval.go +++ b/lang/eval.go @@ -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 diff --git a/lang/eval_test.go b/lang/eval_test.go index 67feb30d01..57763770e2 100644 --- a/lang/eval_test.go +++ b/lang/eval_test.go @@ -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"), }),