diff --git a/addrs/parse_ref.go b/addrs/parse_ref.go index eac77f306a..bad463ba8d 100644 --- a/addrs/parse_ref.go +++ b/addrs/parse_ref.go @@ -106,6 +106,27 @@ func parseRef(traversal hcl.Traversal) (*Reference, tfdiags.Diagnostics) { remain := traversal[1:] // trim off "data" so we can use our shared resource reference parser return parseResourceRef(DataResourceMode, rootRange, remain) + case "resource": + // This is an alias for the normal case of just using a managed resource + // type as a top-level symbol, which will serve as an escape mechanism + // if a later edition of the Terraform language introduces a new + // reference prefix that conflicts with a resource type name in an + // existing provider. In that case, the edition upgrade tool can + // rewrite foo.bar into resource.foo.bar to ensure that "foo" remains + // interpreted as a resource type name rather than as the new reserved + // word. + if len(traversal) < 3 { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid reference", + Detail: `The "resource" object must be followed by two attribute names: the resource type and the resource name.`, + Subject: traversal.SourceRange().Ptr(), + }) + return nil, diags + } + remain := traversal[1:] // trim off "resource" so we can use our shared resource reference parser + return parseResourceRef(ManagedResourceMode, rootRange, remain) + case "local": name, rng, remain, diags := parseSingleAttrRef(traversal) return &Reference{ diff --git a/addrs/parse_ref_test.go b/addrs/parse_ref_test.go index 233bf8685a..660725e534 100644 --- a/addrs/parse_ref_test.go +++ b/addrs/parse_ref_test.go @@ -584,6 +584,27 @@ func TestParseRef(t *testing.T) { `The "var" object does not support this operation.`, }, + // the "resource" prefix forces interpreting the next name as a + // resource type name. This is an alias for just using a resource + // type name at the top level, to be used only if a later edition + // of the Terraform language introduces a new reserved word that + // overlaps with a resource type name. + { + `resource.boop_instance.foo`, + &Reference{ + Subject: Resource{ + Mode: ManagedResourceMode, + Type: "boop_instance", + Name: "foo", + }, + SourceRange: tfdiags.SourceRange{ + Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0}, + End: tfdiags.SourcePos{Line: 1, Column: 27, Byte: 26}, + }, + }, + ``, + }, + // anything else, interpreted as a managed resource reference { `boop_instance.foo`, diff --git a/lang/eval.go b/lang/eval.go index fab3c933db..f14592a75d 100644 --- a/lang/eval.go +++ b/lang/eval.go @@ -407,9 +407,16 @@ func (s *Scope) evalContext(refs []*addrs.Reference, selfAddr addrs.Referenceabl } } + // Managed resources are exposed in two different locations. The primary + // is at the top level where the resource type name is the root of the + // traversal, but we also expose them under "resource" as an escaping + // technique if we add a reserved name in a future language edition which + // conflicts with someone's existing provider. for k, v := range buildResourceObjects(managedResources) { vals[k] = v } + vals["resource"] = cty.ObjectVal(buildResourceObjects(managedResources)) + vals["data"] = cty.ObjectVal(buildResourceObjects(dataResources)) vals["module"] = cty.ObjectVal(wholeModules) vals["var"] = cty.ObjectVal(inputVariables) diff --git a/lang/eval_test.go b/lang/eval_test.go index 0ddcef193f..4b812a7eda 100644 --- a/lang/eval_test.go +++ b/lang/eval_test.go @@ -120,6 +120,13 @@ func TestScopeEvalContext(t *testing.T) { "attr": cty.StringVal("bar"), }), }), + "resource": cty.ObjectVal(map[string]cty.Value{ + "null_resource": cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }), + }), + }), }, }, { @@ -130,6 +137,13 @@ func TestScopeEvalContext(t *testing.T) { "attr": cty.StringVal("bar"), }), }), + "resource": cty.ObjectVal(map[string]cty.Value{ + "null_resource": cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }), + }), + }), }, }, { @@ -145,6 +159,18 @@ func TestScopeEvalContext(t *testing.T) { }), }), }), + "resource": cty.ObjectVal(map[string]cty.Value{ + "null_resource": cty.ObjectVal(map[string]cty.Value{ + "multi": cty.TupleVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("multi0"), + }), + cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("multi1"), + }), + }), + }), + }), }, }, { @@ -161,6 +187,18 @@ func TestScopeEvalContext(t *testing.T) { }), }), }), + "resource": cty.ObjectVal(map[string]cty.Value{ + "null_resource": cty.ObjectVal(map[string]cty.Value{ + "multi": cty.TupleVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("multi0"), + }), + cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("multi1"), + }), + }), + }), + }), }, }, { @@ -177,6 +215,18 @@ func TestScopeEvalContext(t *testing.T) { }), }), }), + "resource": cty.ObjectVal(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"), + }), + }), + }), + }), }, }, { @@ -193,6 +243,18 @@ func TestScopeEvalContext(t *testing.T) { }), }), }), + "resource": cty.ObjectVal(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"), + }), + }), + }), + }), }, }, { @@ -208,6 +270,18 @@ func TestScopeEvalContext(t *testing.T) { }), }), }), + "resource": cty.ObjectVal(map[string]cty.Value{ + "null_resource": cty.ObjectVal(map[string]cty.Value{ + "multi": cty.TupleVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("multi0"), + }), + cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("multi1"), + }), + }), + }), + }), }, }, {