mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-30 10:47:14 -06:00
02b25e7057
This "kitchen sink" commit is mainly focused on supporting "targets" as a new sub-category of addresses, for use-case like the -target CLI option, but also includes some other functionality to get closer to replacing terraform.ResourceAddress and fill out some missing parts for representing various other address types that are currently represented as strings in the "terraform" package.
344 lines
7.3 KiB
Go
344 lines
7.3 KiB
Go
package addrs
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/go-test/deep"
|
|
"github.com/hashicorp/hcl2/hcl"
|
|
"github.com/hashicorp/hcl2/hcl/hclsyntax"
|
|
"github.com/hashicorp/terraform/tfdiags"
|
|
)
|
|
|
|
func TestParseTarget(t *testing.T) {
|
|
tests := []struct {
|
|
Input string
|
|
Want *Target
|
|
WantErr string
|
|
}{
|
|
{
|
|
`module.foo`,
|
|
&Target{
|
|
Subject: ModuleInstance{
|
|
{
|
|
Name: "foo",
|
|
},
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 11, Byte: 10},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`module.foo[2]`,
|
|
&Target{
|
|
Subject: ModuleInstance{
|
|
{
|
|
Name: "foo",
|
|
InstanceKey: IntKey(2),
|
|
},
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 14, Byte: 13},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`module.foo[2].module.bar`,
|
|
&Target{
|
|
Subject: ModuleInstance{
|
|
{
|
|
Name: "foo",
|
|
InstanceKey: IntKey(2),
|
|
},
|
|
{
|
|
Name: "bar",
|
|
},
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 25, Byte: 24},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`aws_instance.foo`,
|
|
&Target{
|
|
Subject: AbsResource{
|
|
Resource: Resource{
|
|
Mode: ManagedResourceMode,
|
|
Type: "aws_instance",
|
|
Name: "foo",
|
|
},
|
|
Module: RootModuleInstance,
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 17, Byte: 16},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`aws_instance.foo[1]`,
|
|
&Target{
|
|
Subject: AbsResourceInstance{
|
|
Resource: ResourceInstance{
|
|
Resource: Resource{
|
|
Mode: ManagedResourceMode,
|
|
Type: "aws_instance",
|
|
Name: "foo",
|
|
},
|
|
Key: IntKey(1),
|
|
},
|
|
Module: RootModuleInstance,
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 20, Byte: 19},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`data.aws_instance.foo`,
|
|
&Target{
|
|
Subject: AbsResource{
|
|
Resource: Resource{
|
|
Mode: DataResourceMode,
|
|
Type: "aws_instance",
|
|
Name: "foo",
|
|
},
|
|
Module: RootModuleInstance,
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 22, Byte: 21},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`data.aws_instance.foo[1]`,
|
|
&Target{
|
|
Subject: AbsResourceInstance{
|
|
Resource: ResourceInstance{
|
|
Resource: Resource{
|
|
Mode: DataResourceMode,
|
|
Type: "aws_instance",
|
|
Name: "foo",
|
|
},
|
|
Key: IntKey(1),
|
|
},
|
|
Module: RootModuleInstance,
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 25, Byte: 24},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`module.foo.aws_instance.bar`,
|
|
&Target{
|
|
Subject: AbsResource{
|
|
Resource: Resource{
|
|
Mode: ManagedResourceMode,
|
|
Type: "aws_instance",
|
|
Name: "bar",
|
|
},
|
|
Module: ModuleInstance{
|
|
{Name: "foo"},
|
|
},
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 28, Byte: 27},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`module.foo.module.bar.aws_instance.baz`,
|
|
&Target{
|
|
Subject: AbsResource{
|
|
Resource: Resource{
|
|
Mode: ManagedResourceMode,
|
|
Type: "aws_instance",
|
|
Name: "baz",
|
|
},
|
|
Module: ModuleInstance{
|
|
{Name: "foo"},
|
|
{Name: "bar"},
|
|
},
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 39, Byte: 38},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`module.foo.module.bar.aws_instance.baz["hello"]`,
|
|
&Target{
|
|
Subject: AbsResourceInstance{
|
|
Resource: ResourceInstance{
|
|
Resource: Resource{
|
|
Mode: ManagedResourceMode,
|
|
Type: "aws_instance",
|
|
Name: "baz",
|
|
},
|
|
Key: StringKey("hello"),
|
|
},
|
|
Module: ModuleInstance{
|
|
{Name: "foo"},
|
|
{Name: "bar"},
|
|
},
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 48, Byte: 47},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`module.foo.data.aws_instance.bar`,
|
|
&Target{
|
|
Subject: AbsResource{
|
|
Resource: Resource{
|
|
Mode: DataResourceMode,
|
|
Type: "aws_instance",
|
|
Name: "bar",
|
|
},
|
|
Module: ModuleInstance{
|
|
{Name: "foo"},
|
|
},
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 33, Byte: 32},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`module.foo.module.bar.data.aws_instance.baz`,
|
|
&Target{
|
|
Subject: AbsResource{
|
|
Resource: Resource{
|
|
Mode: DataResourceMode,
|
|
Type: "aws_instance",
|
|
Name: "baz",
|
|
},
|
|
Module: ModuleInstance{
|
|
{Name: "foo"},
|
|
{Name: "bar"},
|
|
},
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 44, Byte: 43},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`module.foo.module.bar.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"},
|
|
{Name: "bar"},
|
|
},
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 53, Byte: 52},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
|
|
{
|
|
`aws_instance`,
|
|
nil,
|
|
`Resource specification must include a resource type and name.`,
|
|
},
|
|
{
|
|
`module`,
|
|
nil,
|
|
`Prefix "module." must be followed by a module name.`,
|
|
},
|
|
{
|
|
`module["baz"]`,
|
|
nil,
|
|
`Prefix "module." must be followed by a module name.`,
|
|
},
|
|
{
|
|
`module.baz.bar`,
|
|
nil,
|
|
`Resource specification must include a resource type and name.`,
|
|
},
|
|
{
|
|
`aws_instance.foo.bar`,
|
|
nil,
|
|
`Resource instance key must be given in square brackets.`,
|
|
},
|
|
{
|
|
`aws_instance.foo[1].baz`,
|
|
nil,
|
|
`Unexpected extra operators after address.`,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.Input, func(t *testing.T) {
|
|
traversal, travDiags := hclsyntax.ParseTraversalAbs([]byte(test.Input), "", hcl.Pos{Line: 1, Column: 1})
|
|
if travDiags.HasErrors() {
|
|
t.Fatal(travDiags.Error())
|
|
}
|
|
|
|
got, diags := ParseTarget(traversal)
|
|
|
|
switch len(diags) {
|
|
case 0:
|
|
if test.WantErr != "" {
|
|
t.Fatalf("succeeded; want error: %s", test.WantErr)
|
|
}
|
|
case 1:
|
|
if test.WantErr == "" {
|
|
t.Fatalf("unexpected diagnostics: %s", diags.Err())
|
|
}
|
|
if got, want := diags[0].Description().Detail, test.WantErr; got != want {
|
|
t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
default:
|
|
t.Fatalf("too many diagnostics: %s", diags.Err())
|
|
}
|
|
|
|
if diags.HasErrors() {
|
|
return
|
|
}
|
|
|
|
for _, problem := range deep.Equal(got, test.Want) {
|
|
t.Errorf(problem)
|
|
}
|
|
})
|
|
}
|
|
}
|