implement addrs.ConfigResource
Core needs a way to address resources through unexpanded modules, as they are present in the configuration. There are already some cases of paring `addrs.Module` with `addrs.Resource` for this purpose, but it is going to be helpful to have a single type to describe that pair, as well as have the ability to use TargetContains.
This commit is contained in:
parent
482ae66e18
commit
9054716caf
|
@ -249,6 +249,51 @@ func TestParseTarget(t *testing.T) {
|
||||||
},
|
},
|
||||||
``,
|
``,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
`module.foo.module.bar[0].data.aws_instance.baz`,
|
||||||
|
&Target{
|
||||||
|
Subject: AbsResource{
|
||||||
|
Resource: Resource{
|
||||||
|
Mode: DataResourceMode,
|
||||||
|
Type: "aws_instance",
|
||||||
|
Name: "baz",
|
||||||
|
},
|
||||||
|
Module: ModuleInstance{
|
||||||
|
{Name: "foo", InstanceKey: NoKey},
|
||||||
|
{Name: "bar", InstanceKey: IntKey(0)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SourceRange: tfdiags.SourceRange{
|
||||||
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
||||||
|
End: tfdiags.SourcePos{Line: 1, Column: 47, Byte: 46},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
``,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`module.foo.module.bar["a"].data.aws_instance.baz["hello"]`,
|
||||||
|
&Target{
|
||||||
|
Subject: AbsResourceInstance{
|
||||||
|
Resource: ResourceInstance{
|
||||||
|
Resource: Resource{
|
||||||
|
Mode: DataResourceMode,
|
||||||
|
Type: "aws_instance",
|
||||||
|
Name: "baz",
|
||||||
|
},
|
||||||
|
Key: StringKey("hello"),
|
||||||
|
},
|
||||||
|
Module: ModuleInstance{
|
||||||
|
{Name: "foo", InstanceKey: NoKey},
|
||||||
|
{Name: "bar", InstanceKey: StringKey("a")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SourceRange: tfdiags.SourceRange{
|
||||||
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
||||||
|
End: tfdiags.SourcePos{Line: 1, Column: 58, Byte: 57},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
``,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
`module.foo.module.bar.data.aws_instance.baz["hello"]`,
|
`module.foo.module.bar.data.aws_instance.baz["hello"]`,
|
||||||
&Target{
|
&Target{
|
||||||
|
|
|
@ -253,6 +253,61 @@ func (r AbsResourceInstance) Less(o AbsResourceInstance) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConfigResource is an address for a resource within a configuration.
|
||||||
|
type ConfigResource struct {
|
||||||
|
targetable
|
||||||
|
Module Module
|
||||||
|
Resource Resource
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resource returns the address of a particular resource within the module.
|
||||||
|
func (m Module) Resource(mode ResourceMode, typeName string, name string) ConfigResource {
|
||||||
|
return ConfigResource{
|
||||||
|
Module: m,
|
||||||
|
Resource: Resource{
|
||||||
|
Mode: mode,
|
||||||
|
Type: typeName,
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Absolute produces the address for the receiver within a specific module instance.
|
||||||
|
func (r ConfigResource) Absolute(module ModuleInstance) AbsResource {
|
||||||
|
return AbsResource{
|
||||||
|
Module: module,
|
||||||
|
Resource: r.Resource,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TargetContains implements Targetable by returning true if the given other
|
||||||
|
// address is either equal to the receiver or is an instance of the
|
||||||
|
// receiver.
|
||||||
|
func (r ConfigResource) TargetContains(other Targetable) bool {
|
||||||
|
switch to := other.(type) {
|
||||||
|
case ConfigResource:
|
||||||
|
// We'll use our stringification as a cheat-ish way to test for equality.
|
||||||
|
return to.String() == r.String()
|
||||||
|
case AbsResource:
|
||||||
|
return r.TargetContains(ConfigResource{Module: to.Module.Module(), Resource: to.Resource})
|
||||||
|
case AbsResourceInstance:
|
||||||
|
return r.TargetContains(to.ContainingResource())
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r ConfigResource) String() string {
|
||||||
|
if len(r.Module) == 0 {
|
||||||
|
return r.Resource.String()
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s.%s", r.Module.String(), r.Resource.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r ConfigResource) Equal(o ConfigResource) bool {
|
||||||
|
return r.String() == o.String()
|
||||||
|
}
|
||||||
|
|
||||||
// ResourceMode defines which lifecycle applies to a given resource. Each
|
// ResourceMode defines which lifecycle applies to a given resource. Each
|
||||||
// resource lifecycle has a slightly different address format.
|
// resource lifecycle has a slightly different address format.
|
||||||
type ResourceMode rune
|
type ResourceMode rune
|
||||||
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
package addrs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTargetContains(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
addr, other Targetable
|
||||||
|
expect bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
mustParseTarget("module.foo"),
|
||||||
|
mustParseTarget("module.bar"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mustParseTarget("module.foo"),
|
||||||
|
mustParseTarget("module.foo"),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// module.foo is an unkeyed module instance here, so it cannot
|
||||||
|
// contain another instance
|
||||||
|
mustParseTarget("module.foo"),
|
||||||
|
mustParseTarget("module.foo[0]"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RootModuleInstance,
|
||||||
|
mustParseTarget("module.foo"),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mustParseTarget("module.foo"),
|
||||||
|
RootModuleInstance,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mustParseTarget("module.foo"),
|
||||||
|
mustParseTarget("module.foo.module.bar[0]"),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mustParseTarget("module.foo"),
|
||||||
|
mustParseTarget("module.foo.module.bar[0]"),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mustParseTarget("module.foo[2]"),
|
||||||
|
mustParseTarget("module.foo[2].module.bar[0]"),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mustParseTarget("module.foo"),
|
||||||
|
mustParseTarget("module.foo.test_resource.bar"),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mustParseTarget("module.foo"),
|
||||||
|
mustParseTarget("module.foo.test_resource.bar[0]"),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Resources
|
||||||
|
{
|
||||||
|
mustParseTarget("test_resource.foo"),
|
||||||
|
mustParseTarget("test_resource.foo[\"bar\"]"),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mustParseTarget(`test_resource.foo["bar"]`),
|
||||||
|
mustParseTarget(`test_resource.foo["bar"]`),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mustParseTarget("test_resource.foo"),
|
||||||
|
mustParseTarget("test_resource.foo[2]"),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mustParseTarget("test_resource.foo"),
|
||||||
|
mustParseTarget("module.bar.test_resource.foo[2]"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mustParseTarget("module.bar.test_resource.foo"),
|
||||||
|
mustParseTarget("module.bar.test_resource.foo[2]"),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mustParseTarget("module.bar.test_resource.foo"),
|
||||||
|
mustParseTarget("module.bar[0].test_resource.foo[2]"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Config paths, while never returned from parsing a target, must still be targetable
|
||||||
|
{
|
||||||
|
ConfigResource{
|
||||||
|
Module: []string{"bar"},
|
||||||
|
Resource: Resource{
|
||||||
|
Mode: ManagedResourceMode,
|
||||||
|
Type: "test_resource",
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mustParseTarget("module.bar.test_resource.foo[2]"),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ConfigResource{
|
||||||
|
Resource: Resource{
|
||||||
|
Mode: ManagedResourceMode,
|
||||||
|
Type: "test_resource",
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mustParseTarget("module.bar.test_resource.foo[2]"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ConfigResource{
|
||||||
|
Module: []string{"bar"},
|
||||||
|
Resource: Resource{
|
||||||
|
Mode: ManagedResourceMode,
|
||||||
|
Type: "test_resource",
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mustParseTarget("module.bar[0].test_resource.foo"),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(fmt.Sprintf("%s-in-%s", test.other, test.addr), func(t *testing.T) {
|
||||||
|
got := test.addr.TargetContains(test.other)
|
||||||
|
if got != test.expect {
|
||||||
|
t.Fatalf("expected %q.TargetContains(%q) == %t", test.addr, test.other, test.expect)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResourceContains(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
in, other Targetable
|
||||||
|
expect bool
|
||||||
|
}{} {
|
||||||
|
t.Run(fmt.Sprintf("%s-in-%s", test.other, test.in), func(t *testing.T) {
|
||||||
|
got := test.in.TargetContains(test.other)
|
||||||
|
if got != test.expect {
|
||||||
|
t.Fatalf("expected %q.TargetContains(%q) == %t", test.in, test.other, test.expect)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustParseTarget(str string) Targetable {
|
||||||
|
t, diags := ParseTargetStr(str)
|
||||||
|
if diags != nil {
|
||||||
|
panic(fmt.Sprintf("%s: %s", str, diags.ErrWithWarnings()))
|
||||||
|
}
|
||||||
|
return t.Subject
|
||||||
|
}
|
Loading…
Reference in New Issue