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"]`,
|
||||
&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
|
||||
// resource lifecycle has a slightly different address format.
|
||||
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