diff --git a/lang/data_test.go b/lang/data_test.go index dba682958..fd87861fc 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 d4c24e7d6..46baef7c0 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 67feb30d0..57763770e 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"), }),