core: ResourceAddress supports data resources
The ResourceAddress struct grows a new "Mode" field to match with Resource, and its parser learns to recognize the "data." prefix so it can set that field. Allows -target to be applied to data sources, although that is arguably not a very useful thing to do. Other future uses of resource addressing, like the state plumbing commands, may be better uses of this.
This commit is contained in:
parent
afc7ec5ac0
commit
61ab8bf39a
|
@ -219,6 +219,7 @@ func (n *GraphNodeConfigResource) ResourceAddress() *ResourceAddress {
|
|||
InstanceType: TypePrimary,
|
||||
Name: n.Resource.Name,
|
||||
Type: n.Resource.Type,
|
||||
Mode: n.Resource.Mode,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ import (
|
|||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
)
|
||||
|
||||
// ResourceAddress is a way of identifying an individual resource (or,
|
||||
|
@ -22,6 +24,7 @@ type ResourceAddress struct {
|
|||
InstanceTypeSet bool
|
||||
Name string
|
||||
Type string
|
||||
Mode config.ResourceMode // significant only if InstanceTypeSet
|
||||
}
|
||||
|
||||
// Copy returns a copy of this ResourceAddress
|
||||
|
@ -32,6 +35,7 @@ func (r *ResourceAddress) Copy() *ResourceAddress {
|
|||
InstanceType: r.InstanceType,
|
||||
Name: r.Name,
|
||||
Type: r.Type,
|
||||
Mode: r.Mode,
|
||||
}
|
||||
for _, p := range r.Path {
|
||||
n.Path = append(n.Path, p)
|
||||
|
@ -46,6 +50,15 @@ func (r *ResourceAddress) String() string {
|
|||
result = append(result, "module", p)
|
||||
}
|
||||
|
||||
switch r.Mode {
|
||||
case config.ManagedResourceMode:
|
||||
// nothing to do
|
||||
case config.DataResourceMode:
|
||||
result = append(result, "data")
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported resource mode %s", r.Mode))
|
||||
}
|
||||
|
||||
if r.Type != "" {
|
||||
result = append(result, r.Type)
|
||||
}
|
||||
|
@ -77,6 +90,10 @@ func ParseResourceAddress(s string) (*ResourceAddress, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mode := config.ManagedResourceMode
|
||||
if matches["data_prefix"] != "" {
|
||||
mode = config.DataResourceMode
|
||||
}
|
||||
resourceIndex, err := ParseResourceIndex(matches["index"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -87,6 +104,11 @@ func ParseResourceAddress(s string) (*ResourceAddress, error) {
|
|||
}
|
||||
path := ParseResourcePath(matches["path"])
|
||||
|
||||
// not allowed to say "data." without a type following
|
||||
if mode == config.DataResourceMode && matches["type"] == "" {
|
||||
return nil, fmt.Errorf("must target specific data instance")
|
||||
}
|
||||
|
||||
return &ResourceAddress{
|
||||
Path: path,
|
||||
Index: resourceIndex,
|
||||
|
@ -94,6 +116,7 @@ func ParseResourceAddress(s string) (*ResourceAddress, error) {
|
|||
InstanceTypeSet: matches["instance_type"] != "",
|
||||
Name: matches["name"],
|
||||
Type: matches["type"],
|
||||
Mode: mode,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -118,11 +141,17 @@ func (addr *ResourceAddress) Equals(raw interface{}) bool {
|
|||
other.Type == "" ||
|
||||
addr.Type == other.Type
|
||||
|
||||
// mode is significant only when type is set
|
||||
modeMatch := addr.Type == "" ||
|
||||
other.Type == "" ||
|
||||
addr.Mode == other.Mode
|
||||
|
||||
return pathMatch &&
|
||||
indexMatch &&
|
||||
addr.InstanceType == other.InstanceType &&
|
||||
nameMatch &&
|
||||
typeMatch
|
||||
typeMatch &&
|
||||
modeMatch
|
||||
}
|
||||
|
||||
func ParseResourceIndex(s string) (int, error) {
|
||||
|
@ -168,6 +197,8 @@ func tokenizeResourceAddress(s string) (map[string]string, error) {
|
|||
re := regexp.MustCompile(`\A` +
|
||||
// "module.foo.module.bar" (optional)
|
||||
`(?P<path>(?:module\.[^.]+\.?)*)` +
|
||||
// possibly "data.", if targeting is a data resource
|
||||
`(?P<data_prefix>(?:data\.)?)` +
|
||||
// "aws_instance.web" (optional when module path specified)
|
||||
`(?:(?P<type>[^.]+)\.(?P<name>[^.[]+))?` +
|
||||
// "tainted" (optional, omission implies: "primary")
|
||||
|
|
|
@ -3,6 +3,8 @@ package terraform
|
|||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
)
|
||||
|
||||
func TestParseResourceAddress(t *testing.T) {
|
||||
|
@ -11,9 +13,21 @@ func TestParseResourceAddress(t *testing.T) {
|
|||
Expected *ResourceAddress
|
||||
Output string
|
||||
}{
|
||||
"implicit primary, no specific index": {
|
||||
"implicit primary managed instance, no specific index": {
|
||||
"aws_instance.foo",
|
||||
&ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
Index: -1,
|
||||
},
|
||||
"",
|
||||
},
|
||||
"implicit primary data instance, no specific index": {
|
||||
"data.aws_instance.foo",
|
||||
&ResourceAddress{
|
||||
Mode: config.DataResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
|
@ -24,6 +38,7 @@ func TestParseResourceAddress(t *testing.T) {
|
|||
"implicit primary, explicit index": {
|
||||
"aws_instance.foo[2]",
|
||||
&ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
|
@ -34,6 +49,7 @@ func TestParseResourceAddress(t *testing.T) {
|
|||
"implicit primary, explicit index over ten": {
|
||||
"aws_instance.foo[12]",
|
||||
&ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
|
@ -44,6 +60,7 @@ func TestParseResourceAddress(t *testing.T) {
|
|||
"explicit primary, explicit index": {
|
||||
"aws_instance.foo.primary[2]",
|
||||
&ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
|
@ -55,6 +72,7 @@ func TestParseResourceAddress(t *testing.T) {
|
|||
"tainted": {
|
||||
"aws_instance.foo.tainted",
|
||||
&ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypeTainted,
|
||||
|
@ -66,6 +84,7 @@ func TestParseResourceAddress(t *testing.T) {
|
|||
"deposed": {
|
||||
"aws_instance.foo.deposed",
|
||||
&ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypeDeposed,
|
||||
|
@ -77,6 +96,7 @@ func TestParseResourceAddress(t *testing.T) {
|
|||
"with a hyphen": {
|
||||
"aws_instance.foo-bar",
|
||||
&ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo-bar",
|
||||
InstanceType: TypePrimary,
|
||||
|
@ -84,10 +104,23 @@ func TestParseResourceAddress(t *testing.T) {
|
|||
},
|
||||
"",
|
||||
},
|
||||
"in a module": {
|
||||
"managed in a module": {
|
||||
"module.child.aws_instance.foo",
|
||||
&ResourceAddress{
|
||||
Path: []string{"child"},
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
Index: -1,
|
||||
},
|
||||
"",
|
||||
},
|
||||
"data in a module": {
|
||||
"module.child.data.aws_instance.foo",
|
||||
&ResourceAddress{
|
||||
Path: []string{"child"},
|
||||
Mode: config.DataResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
|
@ -99,6 +132,7 @@ func TestParseResourceAddress(t *testing.T) {
|
|||
"module.a.module.b.module.forever.aws_instance.foo",
|
||||
&ResourceAddress{
|
||||
Path: []string{"a", "b", "forever"},
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
|
@ -133,7 +167,7 @@ func TestParseResourceAddress(t *testing.T) {
|
|||
for tn, tc := range cases {
|
||||
out, err := ParseResourceAddress(tc.Input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %#v", err)
|
||||
t.Fatalf("%s: unexpected err: %#v", tn, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(out, tc.Expected) {
|
||||
|
@ -158,12 +192,14 @@ func TestResourceAddressEquals(t *testing.T) {
|
|||
}{
|
||||
"basic match": {
|
||||
Address: &ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
Index: 0,
|
||||
},
|
||||
Other: &ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
|
@ -173,12 +209,14 @@ func TestResourceAddressEquals(t *testing.T) {
|
|||
},
|
||||
"address does not set index": {
|
||||
Address: &ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
Index: -1,
|
||||
},
|
||||
Other: &ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
|
@ -188,12 +226,14 @@ func TestResourceAddressEquals(t *testing.T) {
|
|||
},
|
||||
"other does not set index": {
|
||||
Address: &ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
Index: 3,
|
||||
},
|
||||
Other: &ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
|
@ -203,12 +243,14 @@ func TestResourceAddressEquals(t *testing.T) {
|
|||
},
|
||||
"neither sets index": {
|
||||
Address: &ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
Index: -1,
|
||||
},
|
||||
Other: &ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
|
@ -218,12 +260,14 @@ func TestResourceAddressEquals(t *testing.T) {
|
|||
},
|
||||
"index over ten": {
|
||||
Address: &ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
Index: 1,
|
||||
},
|
||||
Other: &ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
|
@ -233,12 +277,14 @@ func TestResourceAddressEquals(t *testing.T) {
|
|||
},
|
||||
"different type": {
|
||||
Address: &ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
Index: 0,
|
||||
},
|
||||
Other: &ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_vpc",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
|
@ -246,14 +292,33 @@ func TestResourceAddressEquals(t *testing.T) {
|
|||
},
|
||||
Expect: false,
|
||||
},
|
||||
"different mode": {
|
||||
Address: &ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
Index: 0,
|
||||
},
|
||||
Other: &ResourceAddress{
|
||||
Mode: config.DataResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
Index: 0,
|
||||
},
|
||||
Expect: false,
|
||||
},
|
||||
"different name": {
|
||||
Address: &ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
Index: 0,
|
||||
},
|
||||
Other: &ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "bar",
|
||||
InstanceType: TypePrimary,
|
||||
|
@ -263,12 +328,14 @@ func TestResourceAddressEquals(t *testing.T) {
|
|||
},
|
||||
"different instance type": {
|
||||
Address: &ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
Index: 0,
|
||||
},
|
||||
Other: &ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypeTainted,
|
||||
|
@ -278,12 +345,14 @@ func TestResourceAddressEquals(t *testing.T) {
|
|||
},
|
||||
"different index": {
|
||||
Address: &ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
Index: 0,
|
||||
},
|
||||
Other: &ResourceAddress{
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
|
@ -291,7 +360,7 @@ func TestResourceAddressEquals(t *testing.T) {
|
|||
},
|
||||
Expect: false,
|
||||
},
|
||||
"module address matches address of resource inside module": {
|
||||
"module address matches address of managed resource inside module": {
|
||||
Address: &ResourceAddress{
|
||||
Path: []string{"a", "b"},
|
||||
Type: "",
|
||||
|
@ -301,6 +370,7 @@ func TestResourceAddressEquals(t *testing.T) {
|
|||
},
|
||||
Other: &ResourceAddress{
|
||||
Path: []string{"a", "b"},
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
|
@ -308,7 +378,25 @@ func TestResourceAddressEquals(t *testing.T) {
|
|||
},
|
||||
Expect: true,
|
||||
},
|
||||
"module address doesn't match resource outside module": {
|
||||
"module address matches address of data resource inside module": {
|
||||
Address: &ResourceAddress{
|
||||
Path: []string{"a", "b"},
|
||||
Type: "",
|
||||
Name: "",
|
||||
InstanceType: TypePrimary,
|
||||
Index: -1,
|
||||
},
|
||||
Other: &ResourceAddress{
|
||||
Path: []string{"a", "b"},
|
||||
Mode: config.DataResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
Index: 0,
|
||||
},
|
||||
Expect: true,
|
||||
},
|
||||
"module address doesn't match managed resource outside module": {
|
||||
Address: &ResourceAddress{
|
||||
Path: []string{"a", "b"},
|
||||
Type: "",
|
||||
|
@ -318,6 +406,25 @@ func TestResourceAddressEquals(t *testing.T) {
|
|||
},
|
||||
Other: &ResourceAddress{
|
||||
Path: []string{"a"},
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
Index: 0,
|
||||
},
|
||||
Expect: false,
|
||||
},
|
||||
"module address doesn't match data resource outside module": {
|
||||
Address: &ResourceAddress{
|
||||
Path: []string{"a", "b"},
|
||||
Type: "",
|
||||
Name: "",
|
||||
InstanceType: TypePrimary,
|
||||
Index: -1,
|
||||
},
|
||||
Other: &ResourceAddress{
|
||||
Path: []string{"a"},
|
||||
Mode: config.DataResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
|
@ -328,6 +435,7 @@ func TestResourceAddressEquals(t *testing.T) {
|
|||
"nil path vs empty path should match": {
|
||||
Address: &ResourceAddress{
|
||||
Path: []string{},
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
|
@ -335,6 +443,7 @@ func TestResourceAddressEquals(t *testing.T) {
|
|||
},
|
||||
Other: &ResourceAddress{
|
||||
Path: nil,
|
||||
Mode: config.ManagedResourceMode,
|
||||
Type: "aws_instance",
|
||||
Name: "foo",
|
||||
InstanceType: TypePrimary,
|
||||
|
|
|
@ -175,6 +175,7 @@ func (n *graphNodeOrphanResource) ResourceAddress() *ResourceAddress {
|
|||
Name: n.ResourceKey.Name,
|
||||
Path: n.Path[1:],
|
||||
Type: n.ResourceKey.Type,
|
||||
Mode: n.ResourceKey.Mode,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -119,6 +119,7 @@ func (n *graphNodeExpandedResource) ResourceAddress() *ResourceAddress {
|
|||
InstanceType: TypePrimary,
|
||||
Name: n.Resource.Name,
|
||||
Type: n.Resource.Type,
|
||||
Mode: n.Resource.Mode,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue