mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-14 01:13:59 -06:00
Merge pull request #4574 from hashicorp/phinze/orphan-addressing
Orphan addressing / targeting
This commit is contained in:
commit
8d8b28717e
@ -3359,6 +3359,60 @@ aws_instance.bar:
|
|||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/hashicorp/terraform/issues/4462
|
||||||
|
func TestContext2Apply_targetedDestroyModule(t *testing.T) {
|
||||||
|
m := testModule(t, "apply-targeted-module")
|
||||||
|
p := testProvider("aws")
|
||||||
|
p.ApplyFn = testApplyFn
|
||||||
|
p.DiffFn = testDiffFn
|
||||||
|
ctx := testContext2(t, &ContextOpts{
|
||||||
|
Module: m,
|
||||||
|
Providers: map[string]ResourceProviderFactory{
|
||||||
|
"aws": testProviderFuncFixed(p),
|
||||||
|
},
|
||||||
|
State: &State{
|
||||||
|
Modules: []*ModuleState{
|
||||||
|
&ModuleState{
|
||||||
|
Path: rootModulePath,
|
||||||
|
Resources: map[string]*ResourceState{
|
||||||
|
"aws_instance.foo": resourceState("aws_instance", "i-bcd345"),
|
||||||
|
"aws_instance.bar": resourceState("aws_instance", "i-abc123"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&ModuleState{
|
||||||
|
Path: []string{"root", "child"},
|
||||||
|
Resources: map[string]*ResourceState{
|
||||||
|
"aws_instance.foo": resourceState("aws_instance", "i-bcd345"),
|
||||||
|
"aws_instance.bar": resourceState("aws_instance", "i-abc123"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Targets: []string{"module.child.aws_instance.foo"},
|
||||||
|
Destroy: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
if _, err := ctx.Plan(); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
state, err := ctx.Apply()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkStateString(t, state, `
|
||||||
|
aws_instance.bar:
|
||||||
|
ID = i-abc123
|
||||||
|
aws_instance.foo:
|
||||||
|
ID = i-bcd345
|
||||||
|
|
||||||
|
module.child:
|
||||||
|
aws_instance.bar:
|
||||||
|
ID = i-abc123
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
func TestContext2Apply_targetedDestroyCountIndex(t *testing.T) {
|
func TestContext2Apply_targetedDestroyCountIndex(t *testing.T) {
|
||||||
m := testModule(t, "apply-targeted-count")
|
m := testModule(t, "apply-targeted-count")
|
||||||
p := testProvider("aws")
|
p := testProvider("aws")
|
||||||
|
@ -1670,6 +1670,12 @@ func TestContext2Plan_targetedOrphan(t *testing.T) {
|
|||||||
ID: "i-789xyz",
|
ID: "i-789xyz",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"aws_instance.nottargeted": &ResourceState{
|
||||||
|
Type: "aws_instance",
|
||||||
|
Primary: &InstanceState{
|
||||||
|
ID: "i-abc123",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1690,8 +1696,150 @@ DESTROY: aws_instance.orphan
|
|||||||
|
|
||||||
STATE:
|
STATE:
|
||||||
|
|
||||||
|
aws_instance.nottargeted:
|
||||||
|
ID = i-abc123
|
||||||
aws_instance.orphan:
|
aws_instance.orphan:
|
||||||
ID = i-789xyz`)
|
ID = i-789xyz
|
||||||
|
`)
|
||||||
|
if actual != expected {
|
||||||
|
t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/hashicorp/terraform/issues/2538
|
||||||
|
func TestContext2Plan_targetedModuleOrphan(t *testing.T) {
|
||||||
|
m := testModule(t, "plan-targeted-module-orphan")
|
||||||
|
p := testProvider("aws")
|
||||||
|
p.DiffFn = testDiffFn
|
||||||
|
ctx := testContext2(t, &ContextOpts{
|
||||||
|
Module: m,
|
||||||
|
Providers: map[string]ResourceProviderFactory{
|
||||||
|
"aws": testProviderFuncFixed(p),
|
||||||
|
},
|
||||||
|
State: &State{
|
||||||
|
Modules: []*ModuleState{
|
||||||
|
&ModuleState{
|
||||||
|
Path: []string{"root", "child"},
|
||||||
|
Resources: map[string]*ResourceState{
|
||||||
|
"aws_instance.orphan": &ResourceState{
|
||||||
|
Type: "aws_instance",
|
||||||
|
Primary: &InstanceState{
|
||||||
|
ID: "i-789xyz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"aws_instance.nottargeted": &ResourceState{
|
||||||
|
Type: "aws_instance",
|
||||||
|
Primary: &InstanceState{
|
||||||
|
ID: "i-abc123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Destroy: true,
|
||||||
|
Targets: []string{"module.child.aws_instance.orphan"},
|
||||||
|
})
|
||||||
|
|
||||||
|
plan, err := ctx.Plan()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := strings.TrimSpace(plan.String())
|
||||||
|
expected := strings.TrimSpace(`DIFF:
|
||||||
|
|
||||||
|
module.child:
|
||||||
|
DESTROY: aws_instance.orphan
|
||||||
|
|
||||||
|
STATE:
|
||||||
|
|
||||||
|
module.child:
|
||||||
|
aws_instance.nottargeted:
|
||||||
|
ID = i-abc123
|
||||||
|
aws_instance.orphan:
|
||||||
|
ID = i-789xyz
|
||||||
|
`)
|
||||||
|
if actual != expected {
|
||||||
|
t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/hashicorp/terraform/issues/4515
|
||||||
|
func TestContext2Plan_targetedOverTen(t *testing.T) {
|
||||||
|
m := testModule(t, "plan-targeted-over-ten")
|
||||||
|
p := testProvider("aws")
|
||||||
|
p.DiffFn = testDiffFn
|
||||||
|
|
||||||
|
resources := make(map[string]*ResourceState)
|
||||||
|
var expectedState []string
|
||||||
|
for i := 0; i < 13; i++ {
|
||||||
|
key := fmt.Sprintf("aws_instance.foo.%d", i)
|
||||||
|
id := fmt.Sprintf("i-abc%d", i)
|
||||||
|
resources[key] = &ResourceState{
|
||||||
|
Type: "aws_instance",
|
||||||
|
Primary: &InstanceState{ID: id},
|
||||||
|
}
|
||||||
|
expectedState = append(expectedState,
|
||||||
|
fmt.Sprintf("%s:\n ID = %s\n", key, id))
|
||||||
|
}
|
||||||
|
ctx := testContext2(t, &ContextOpts{
|
||||||
|
Module: m,
|
||||||
|
Providers: map[string]ResourceProviderFactory{
|
||||||
|
"aws": testProviderFuncFixed(p),
|
||||||
|
},
|
||||||
|
State: &State{
|
||||||
|
Modules: []*ModuleState{
|
||||||
|
&ModuleState{
|
||||||
|
Path: rootModulePath,
|
||||||
|
Resources: resources,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Targets: []string{"aws_instance.foo[1]"},
|
||||||
|
})
|
||||||
|
|
||||||
|
plan, err := ctx.Plan()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := strings.TrimSpace(plan.String())
|
||||||
|
sort.Strings(expectedState)
|
||||||
|
expected := strings.TrimSpace(`
|
||||||
|
DIFF:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
STATE:
|
||||||
|
|
||||||
|
aws_instance.foo.0:
|
||||||
|
ID = i-abc0
|
||||||
|
aws_instance.foo.1:
|
||||||
|
ID = i-abc1
|
||||||
|
aws_instance.foo.10:
|
||||||
|
ID = i-abc10
|
||||||
|
aws_instance.foo.11:
|
||||||
|
ID = i-abc11
|
||||||
|
aws_instance.foo.12:
|
||||||
|
ID = i-abc12
|
||||||
|
aws_instance.foo.2:
|
||||||
|
ID = i-abc2
|
||||||
|
aws_instance.foo.3:
|
||||||
|
ID = i-abc3
|
||||||
|
aws_instance.foo.4:
|
||||||
|
ID = i-abc4
|
||||||
|
aws_instance.foo.5:
|
||||||
|
ID = i-abc5
|
||||||
|
aws_instance.foo.6:
|
||||||
|
ID = i-abc6
|
||||||
|
aws_instance.foo.7:
|
||||||
|
ID = i-abc7
|
||||||
|
aws_instance.foo.8:
|
||||||
|
ID = i-abc8
|
||||||
|
aws_instance.foo.9:
|
||||||
|
ID = i-abc9
|
||||||
|
`)
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
|
t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
|
||||||
}
|
}
|
||||||
|
@ -163,9 +163,8 @@ func (n *GraphNodeConfigResource) DynamicExpand(ctx EvalContext) (*Graph, error)
|
|||||||
// expand orphans, which have all the same semantics in a destroy
|
// expand orphans, which have all the same semantics in a destroy
|
||||||
// as a primary.
|
// as a primary.
|
||||||
steps = append(steps, &OrphanTransformer{
|
steps = append(steps, &OrphanTransformer{
|
||||||
State: state,
|
State: state,
|
||||||
View: n.Resource.Id(),
|
View: n.Resource.Id(),
|
||||||
Targets: n.Targets,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
steps = append(steps, &DeposedTransformer{
|
steps = append(steps, &DeposedTransformer{
|
||||||
@ -181,6 +180,12 @@ func (n *GraphNodeConfigResource) DynamicExpand(ctx EvalContext) (*Graph, error)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We always want to apply targeting
|
||||||
|
steps = append(steps, &TargetsTransformer{
|
||||||
|
ParsedTargets: n.Targets,
|
||||||
|
Destroy: n.DestroyMode != DestroyNone,
|
||||||
|
})
|
||||||
|
|
||||||
// Always end with the root being added
|
// Always end with the root being added
|
||||||
steps = append(steps, &RootTransformer{})
|
steps = append(steps, &RootTransformer{})
|
||||||
|
|
||||||
|
@ -28,6 +28,15 @@ func TestParseResourceAddress(t *testing.T) {
|
|||||||
Index: 2,
|
Index: 2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"implicit primary, explicit index over ten": {
|
||||||
|
Input: "aws_instance.foo[12]",
|
||||||
|
Expected: &ResourceAddress{
|
||||||
|
Type: "aws_instance",
|
||||||
|
Name: "foo",
|
||||||
|
InstanceType: TypePrimary,
|
||||||
|
Index: 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
"explicit primary, explicit index": {
|
"explicit primary, explicit index": {
|
||||||
Input: "aws_instance.foo.primary[2]",
|
Input: "aws_instance.foo.primary[2]",
|
||||||
Expected: &ResourceAddress{
|
Expected: &ResourceAddress{
|
||||||
@ -184,6 +193,21 @@ func TestResourceAddressEquals(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Expect: true,
|
Expect: true,
|
||||||
},
|
},
|
||||||
|
"index over ten": {
|
||||||
|
Address: &ResourceAddress{
|
||||||
|
Type: "aws_instance",
|
||||||
|
Name: "foo",
|
||||||
|
InstanceType: TypePrimary,
|
||||||
|
Index: 1,
|
||||||
|
},
|
||||||
|
Other: &ResourceAddress{
|
||||||
|
Type: "aws_instance",
|
||||||
|
Name: "foo",
|
||||||
|
InstanceType: TypePrimary,
|
||||||
|
Index: 13,
|
||||||
|
},
|
||||||
|
Expect: false,
|
||||||
|
},
|
||||||
"different type": {
|
"different type": {
|
||||||
Address: &ResourceAddress{
|
Address: &ResourceAddress{
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config"
|
"github.com/hashicorp/terraform/config"
|
||||||
@ -661,6 +662,65 @@ func (m *ModuleState) String() string {
|
|||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResourceStateKey is a structured representation of the key used for the
|
||||||
|
// ModuleState.Resources mapping
|
||||||
|
type ResourceStateKey struct {
|
||||||
|
Name string
|
||||||
|
Type string
|
||||||
|
Index int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal determines whether two ResourceStateKeys are the same
|
||||||
|
func (rsk *ResourceStateKey) Equal(other *ResourceStateKey) bool {
|
||||||
|
if rsk == nil || other == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if rsk.Type != other.Type {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if rsk.Name != other.Name {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if rsk.Index != other.Index {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rsk *ResourceStateKey) String() string {
|
||||||
|
if rsk == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if rsk.Index == -1 {
|
||||||
|
return fmt.Sprintf("%s.%s", rsk.Type, rsk.Name)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s.%s.%d", rsk.Type, rsk.Name, rsk.Index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseResourceStateKey accepts a key in the format used by
|
||||||
|
// ModuleState.Resources and returns a resource name and resource index. In the
|
||||||
|
// state, a resource has the format "type.name.index" or "type.name". In the
|
||||||
|
// latter case, the index is returned as -1.
|
||||||
|
func ParseResourceStateKey(k string) (*ResourceStateKey, error) {
|
||||||
|
parts := strings.Split(k, ".")
|
||||||
|
if len(parts) < 2 || len(parts) > 3 {
|
||||||
|
return nil, fmt.Errorf("Malformed resource state key: %s", k)
|
||||||
|
}
|
||||||
|
rsk := &ResourceStateKey{
|
||||||
|
Type: parts[0],
|
||||||
|
Name: parts[1],
|
||||||
|
Index: -1,
|
||||||
|
}
|
||||||
|
if len(parts) == 3 {
|
||||||
|
index, err := strconv.Atoi(parts[2])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Malformed resource state key index: %s", k)
|
||||||
|
}
|
||||||
|
rsk.Index = index
|
||||||
|
}
|
||||||
|
return rsk, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ResourceState holds the state of a resource that is used so that
|
// ResourceState holds the state of a resource that is used so that
|
||||||
// a provider can find and manage an existing resource as well as for
|
// a provider can find and manage an existing resource as well as for
|
||||||
// storing attributes that are used to populate variables of child
|
// storing attributes that are used to populate variables of child
|
||||||
|
@ -895,3 +895,57 @@ func TestUpgradeV1State(t *testing.T) {
|
|||||||
t.Fatalf("bad: %#v", bt)
|
t.Fatalf("bad: %#v", bt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseResourceStateKey(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
Input string
|
||||||
|
Expected *ResourceStateKey
|
||||||
|
ExpectedErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Input: "aws_instance.foo.3",
|
||||||
|
Expected: &ResourceStateKey{
|
||||||
|
Type: "aws_instance",
|
||||||
|
Name: "foo",
|
||||||
|
Index: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Input: "aws_instance.foo.0",
|
||||||
|
Expected: &ResourceStateKey{
|
||||||
|
Type: "aws_instance",
|
||||||
|
Name: "foo",
|
||||||
|
Index: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Input: "aws_instance.foo",
|
||||||
|
Expected: &ResourceStateKey{
|
||||||
|
Type: "aws_instance",
|
||||||
|
Name: "foo",
|
||||||
|
Index: -1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Input: "aws_instance.foo.malformed",
|
||||||
|
ExpectedErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Input: "aws_instance.foo.malformedwithnumber.123",
|
||||||
|
ExpectedErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Input: "malformed",
|
||||||
|
ExpectedErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
rsk, err := ParseResourceStateKey(tc.Input)
|
||||||
|
if rsk != nil && tc.Expected != nil && !rsk.Equal(tc.Expected) {
|
||||||
|
t.Fatalf("%s: expected %s, got %s", tc.Input, tc.Expected, rsk)
|
||||||
|
}
|
||||||
|
if (err != nil) != tc.ExpectedErr {
|
||||||
|
t.Fatalf("%s: expected err: %t, got %s", tc.Input, tc.ExpectedErr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
# Once opon a time, there was a child module here
|
||||||
|
/*
|
||||||
|
module "child" {
|
||||||
|
source = "./child"
|
||||||
|
}
|
||||||
|
*/
|
3
terraform/test-fixtures/plan-targeted-over-ten/main.tf
Normal file
3
terraform/test-fixtures/plan-targeted-over-ten/main.tf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
resource "aws_instance" "foo" {
|
||||||
|
count = 13
|
||||||
|
}
|
@ -2,7 +2,6 @@ package terraform
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config"
|
"github.com/hashicorp/terraform/config"
|
||||||
"github.com/hashicorp/terraform/config/module"
|
"github.com/hashicorp/terraform/config/module"
|
||||||
@ -26,11 +25,6 @@ type OrphanTransformer struct {
|
|||||||
// using the graph path.
|
// using the graph path.
|
||||||
Module *module.Tree
|
Module *module.Tree
|
||||||
|
|
||||||
// Targets are user-specified resources to target. We need to be aware of
|
|
||||||
// these so we don't improperly identify orphans when they've just been
|
|
||||||
// filtered out of the graph via targeting.
|
|
||||||
Targets []ResourceAddress
|
|
||||||
|
|
||||||
// View, if non-nil will set a view on the module state.
|
// View, if non-nil will set a view on the module state.
|
||||||
View string
|
View string
|
||||||
}
|
}
|
||||||
@ -68,22 +62,6 @@ func (t *OrphanTransformer) Transform(g *Graph) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resourceOrphans := state.Orphans(config)
|
resourceOrphans := state.Orphans(config)
|
||||||
if len(t.Targets) > 0 {
|
|
||||||
var targetedOrphans []string
|
|
||||||
for _, o := range resourceOrphans {
|
|
||||||
targeted := false
|
|
||||||
for _, t := range t.Targets {
|
|
||||||
prefix := fmt.Sprintf("%s.%s.%d", t.Type, t.Name, t.Index)
|
|
||||||
if strings.HasPrefix(o, prefix) {
|
|
||||||
targeted = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if targeted {
|
|
||||||
targetedOrphans = append(targetedOrphans, o)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resourceOrphans = targetedOrphans
|
|
||||||
}
|
|
||||||
|
|
||||||
resourceVertexes = make([]dag.Vertex, len(resourceOrphans))
|
resourceVertexes = make([]dag.Vertex, len(resourceOrphans))
|
||||||
for i, k := range resourceOrphans {
|
for i, k := range resourceOrphans {
|
||||||
@ -95,11 +73,15 @@ func (t *OrphanTransformer) Transform(g *Graph) error {
|
|||||||
|
|
||||||
rs := state.Resources[k]
|
rs := state.Resources[k]
|
||||||
|
|
||||||
|
rsk, err := ParseResourceStateKey(k)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
resourceVertexes[i] = g.Add(&graphNodeOrphanResource{
|
resourceVertexes[i] = g.Add(&graphNodeOrphanResource{
|
||||||
ResourceName: k,
|
Path: g.Path,
|
||||||
ResourceType: rs.Type,
|
ResourceKey: rsk,
|
||||||
Provider: rs.Provider,
|
Provider: rs.Provider,
|
||||||
dependentOn: rs.Dependencies,
|
dependentOn: rs.Dependencies,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,15 +157,25 @@ func (n *graphNodeOrphanModule) Expand(b GraphBuilder) (GraphNodeSubgraph, error
|
|||||||
|
|
||||||
// graphNodeOrphanResource is the graph vertex representing an orphan resource..
|
// graphNodeOrphanResource is the graph vertex representing an orphan resource..
|
||||||
type graphNodeOrphanResource struct {
|
type graphNodeOrphanResource struct {
|
||||||
ResourceName string
|
Path []string
|
||||||
ResourceType string
|
ResourceKey *ResourceStateKey
|
||||||
Provider string
|
Provider string
|
||||||
|
|
||||||
dependentOn []string
|
dependentOn []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *graphNodeOrphanResource) ConfigType() GraphNodeConfigType {
|
||||||
|
return GraphNodeConfigTypeResource
|
||||||
|
}
|
||||||
|
|
||||||
func (n *graphNodeOrphanResource) ResourceAddress() *ResourceAddress {
|
func (n *graphNodeOrphanResource) ResourceAddress() *ResourceAddress {
|
||||||
return n.ResourceAddress()
|
return &ResourceAddress{
|
||||||
|
Index: n.ResourceKey.Index,
|
||||||
|
InstanceType: TypePrimary,
|
||||||
|
Name: n.ResourceKey.Name,
|
||||||
|
Path: n.Path[1:],
|
||||||
|
Type: n.ResourceKey.Type,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *graphNodeOrphanResource) DependableName() []string {
|
func (n *graphNodeOrphanResource) DependableName() []string {
|
||||||
@ -202,11 +194,11 @@ func (n *graphNodeOrphanResource) Flatten(p []string) (dag.Vertex, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *graphNodeOrphanResource) Name() string {
|
func (n *graphNodeOrphanResource) Name() string {
|
||||||
return fmt.Sprintf("%s (orphan)", n.ResourceName)
|
return fmt.Sprintf("%s (orphan)", n.ResourceKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *graphNodeOrphanResource) ProvidedBy() []string {
|
func (n *graphNodeOrphanResource) ProvidedBy() []string {
|
||||||
return []string{resourceProvider(n.ResourceName, n.Provider)}
|
return []string{resourceProvider(n.ResourceKey.Type, n.Provider)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeEvalable impl.
|
// GraphNodeEvalable impl.
|
||||||
@ -217,7 +209,7 @@ func (n *graphNodeOrphanResource) EvalTree() EvalNode {
|
|||||||
seq := &EvalSequence{Nodes: make([]EvalNode, 0, 5)}
|
seq := &EvalSequence{Nodes: make([]EvalNode, 0, 5)}
|
||||||
|
|
||||||
// Build instance info
|
// Build instance info
|
||||||
info := &InstanceInfo{Id: n.ResourceName, Type: n.ResourceType}
|
info := &InstanceInfo{Id: n.ResourceKey.String(), Type: n.ResourceKey.Type}
|
||||||
seq.Nodes = append(seq.Nodes, &EvalInstanceInfo{Info: info})
|
seq.Nodes = append(seq.Nodes, &EvalInstanceInfo{Info: info})
|
||||||
|
|
||||||
// Refresh the resource
|
// Refresh the resource
|
||||||
@ -230,7 +222,7 @@ func (n *graphNodeOrphanResource) EvalTree() EvalNode {
|
|||||||
Output: &provider,
|
Output: &provider,
|
||||||
},
|
},
|
||||||
&EvalReadState{
|
&EvalReadState{
|
||||||
Name: n.ResourceName,
|
Name: n.ResourceKey.String(),
|
||||||
Output: &state,
|
Output: &state,
|
||||||
},
|
},
|
||||||
&EvalRefresh{
|
&EvalRefresh{
|
||||||
@ -240,8 +232,8 @@ func (n *graphNodeOrphanResource) EvalTree() EvalNode {
|
|||||||
Output: &state,
|
Output: &state,
|
||||||
},
|
},
|
||||||
&EvalWriteState{
|
&EvalWriteState{
|
||||||
Name: n.ResourceName,
|
Name: n.ResourceKey.String(),
|
||||||
ResourceType: n.ResourceType,
|
ResourceType: n.ResourceKey.Type,
|
||||||
Provider: n.Provider,
|
Provider: n.Provider,
|
||||||
Dependencies: n.DependentOn(),
|
Dependencies: n.DependentOn(),
|
||||||
State: &state,
|
State: &state,
|
||||||
@ -257,7 +249,7 @@ func (n *graphNodeOrphanResource) EvalTree() EvalNode {
|
|||||||
Node: &EvalSequence{
|
Node: &EvalSequence{
|
||||||
Nodes: []EvalNode{
|
Nodes: []EvalNode{
|
||||||
&EvalReadState{
|
&EvalReadState{
|
||||||
Name: n.ResourceName,
|
Name: n.ResourceKey.String(),
|
||||||
Output: &state,
|
Output: &state,
|
||||||
},
|
},
|
||||||
&EvalDiffDestroy{
|
&EvalDiffDestroy{
|
||||||
@ -266,7 +258,7 @@ func (n *graphNodeOrphanResource) EvalTree() EvalNode {
|
|||||||
Output: &diff,
|
Output: &diff,
|
||||||
},
|
},
|
||||||
&EvalWriteDiff{
|
&EvalWriteDiff{
|
||||||
Name: n.ResourceName,
|
Name: n.ResourceKey.String(),
|
||||||
Diff: &diff,
|
Diff: &diff,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -280,7 +272,7 @@ func (n *graphNodeOrphanResource) EvalTree() EvalNode {
|
|||||||
Node: &EvalSequence{
|
Node: &EvalSequence{
|
||||||
Nodes: []EvalNode{
|
Nodes: []EvalNode{
|
||||||
&EvalReadDiff{
|
&EvalReadDiff{
|
||||||
Name: n.ResourceName,
|
Name: n.ResourceKey.String(),
|
||||||
Diff: &diff,
|
Diff: &diff,
|
||||||
},
|
},
|
||||||
&EvalGetProvider{
|
&EvalGetProvider{
|
||||||
@ -288,7 +280,7 @@ func (n *graphNodeOrphanResource) EvalTree() EvalNode {
|
|||||||
Output: &provider,
|
Output: &provider,
|
||||||
},
|
},
|
||||||
&EvalReadState{
|
&EvalReadState{
|
||||||
Name: n.ResourceName,
|
Name: n.ResourceKey.String(),
|
||||||
Output: &state,
|
Output: &state,
|
||||||
},
|
},
|
||||||
&EvalApply{
|
&EvalApply{
|
||||||
@ -300,8 +292,8 @@ func (n *graphNodeOrphanResource) EvalTree() EvalNode {
|
|||||||
Error: &err,
|
Error: &err,
|
||||||
},
|
},
|
||||||
&EvalWriteState{
|
&EvalWriteState{
|
||||||
Name: n.ResourceName,
|
Name: n.ResourceKey.String(),
|
||||||
ResourceType: n.ResourceType,
|
ResourceType: n.ResourceKey.Type,
|
||||||
Provider: n.Provider,
|
Provider: n.Provider,
|
||||||
Dependencies: n.DependentOn(),
|
Dependencies: n.DependentOn(),
|
||||||
State: &state,
|
State: &state,
|
||||||
@ -320,7 +312,7 @@ func (n *graphNodeOrphanResource) EvalTree() EvalNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *graphNodeOrphanResource) dependableName() string {
|
func (n *graphNodeOrphanResource) dependableName() string {
|
||||||
return n.ResourceName
|
return n.ResourceKey.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeDestroyable impl.
|
// GraphNodeDestroyable impl.
|
||||||
|
@ -333,17 +333,18 @@ func TestGraphNodeOrphanResource_impl(t *testing.T) {
|
|||||||
var _ dag.Vertex = new(graphNodeOrphanResource)
|
var _ dag.Vertex = new(graphNodeOrphanResource)
|
||||||
var _ dag.NamedVertex = new(graphNodeOrphanResource)
|
var _ dag.NamedVertex = new(graphNodeOrphanResource)
|
||||||
var _ GraphNodeProviderConsumer = new(graphNodeOrphanResource)
|
var _ GraphNodeProviderConsumer = new(graphNodeOrphanResource)
|
||||||
|
var _ GraphNodeAddressable = new(graphNodeOrphanResource)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGraphNodeOrphanResource_ProvidedBy(t *testing.T) {
|
func TestGraphNodeOrphanResource_ProvidedBy(t *testing.T) {
|
||||||
n := &graphNodeOrphanResource{ResourceName: "aws_instance.foo"}
|
n := &graphNodeOrphanResource{ResourceKey: &ResourceStateKey{Type: "aws_instance"}}
|
||||||
if v := n.ProvidedBy(); v[0] != "aws" {
|
if v := n.ProvidedBy(); v[0] != "aws" {
|
||||||
t.Fatalf("bad: %#v", v)
|
t.Fatalf("bad: %#v", v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGraphNodeOrphanResource_ProvidedBy_alias(t *testing.T) {
|
func TestGraphNodeOrphanResource_ProvidedBy_alias(t *testing.T) {
|
||||||
n := &graphNodeOrphanResource{ResourceName: "aws_instance.foo", Provider: "aws.bar"}
|
n := &graphNodeOrphanResource{ResourceKey: &ResourceStateKey{Type: "aws_instance"}, Provider: "aws.bar"}
|
||||||
if v := n.ProvidedBy(); v[0] != "aws.bar" {
|
if v := n.ProvidedBy(); v[0] != "aws.bar" {
|
||||||
t.Fatalf("bad: %#v", v)
|
t.Fatalf("bad: %#v", v)
|
||||||
}
|
}
|
||||||
|
@ -13,20 +13,25 @@ type TargetsTransformer struct {
|
|||||||
// List of targeted resource names specified by the user
|
// List of targeted resource names specified by the user
|
||||||
Targets []string
|
Targets []string
|
||||||
|
|
||||||
|
// List of parsed targets, provided by callers like ResourceCountTransform
|
||||||
|
// that already have the targets parsed
|
||||||
|
ParsedTargets []ResourceAddress
|
||||||
|
|
||||||
// Set to true when we're in a `terraform destroy` or a
|
// Set to true when we're in a `terraform destroy` or a
|
||||||
// `terraform plan -destroy`
|
// `terraform plan -destroy`
|
||||||
Destroy bool
|
Destroy bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TargetsTransformer) Transform(g *Graph) error {
|
func (t *TargetsTransformer) Transform(g *Graph) error {
|
||||||
if len(t.Targets) > 0 {
|
if len(t.Targets) > 0 && len(t.ParsedTargets) == 0 {
|
||||||
// TODO: duplicated in OrphanTransformer; pull up parsing earlier
|
|
||||||
addrs, err := t.parseTargetAddresses()
|
addrs, err := t.parseTargetAddresses()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
t.ParsedTargets = addrs
|
||||||
targetedNodes, err := t.selectTargetedNodes(g, addrs)
|
}
|
||||||
|
if len(t.ParsedTargets) > 0 {
|
||||||
|
targetedNodes, err := t.selectTargetedNodes(g, t.ParsedTargets)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user