expansion resource and instance orphans

When a module instances is removed, we need to add both the instance and
resource deletion nodes from that module.
This commit is contained in:
James Bardin 2020-03-27 17:16:08 -04:00
parent 7a26fcfe84
commit 8add45b076
6 changed files with 101 additions and 14 deletions

View File

@ -6176,13 +6176,15 @@ resource "aws_instance" "foo" {
t.Fatal(diags.ErrWithWarnings())
}
if len(plan.Changes.Resources) != 1 {
t.Fatalf("expected 1 change, got %d", len(plan.Changes.Resources))
expected := map[string]plans.Action{
`module.mod[1].aws_instance.foo`: plans.Delete,
`module.mod[0].aws_instance.foo`: plans.NoOp,
}
for _, res := range plan.Changes.Resources {
if res.Action != plans.Delete {
t.Fatalf("expected Delete action, got: %s", res.Action)
want := expected[res.Addr.String()]
if res.Action != want {
t.Fatalf("expected %s action, got: %q %s", want, res.Addr, res.Action)
}
}
}

View File

@ -145,7 +145,7 @@ func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, er
// Add the count orphans. As these are orphaned refresh nodes, we add them
// directly as NodeDestroyableDataResource.
&OrphanResourceCountTransformer{
&OrphanResourceInstanceCountTransformer{
Concrete: concreteResourceDestroyable,
Addr: n.Addr,
InstanceAddrs: instanceAddrs,

View File

@ -6,6 +6,7 @@ import (
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/lang"
"github.com/hashicorp/terraform/states"
)
// nodeExpandApplyableResource handles the first layer of resource
@ -36,13 +37,47 @@ func (n *nodeExpandApplyableResource) DynamicExpand(ctx EvalContext) (*Graph, er
var g Graph
expander := ctx.InstanceExpander()
for _, module := range expander.ExpandModule(n.Addr.Module) {
moduleInstances := expander.ExpandModule(n.Addr.Module)
var resources []addrs.AbsResource
for _, module := range moduleInstances {
resAddr := n.Addr.Resource.Absolute(module)
resources = append(resources, resAddr)
g.Add(&NodeApplyableResource{
NodeAbstractResource: n.NodeAbstractResource,
Addr: n.Addr.Resource.Absolute(module),
})
}
// Lock the state while we inspect it to find any resources orphaned by
// changes in module expansion.
state := ctx.State().Lock()
defer ctx.State().Unlock()
var orphans []*states.Resource
for _, res := range state.Resources(n.Addr) {
found := false
for _, m := range moduleInstances {
if m.Equal(res.Addr.Module) {
found = true
break
}
}
// Address form state was not found in the current config
if !found {
orphans = append(orphans, res)
}
}
for _, res := range orphans {
// add the resource cleanup node to remove the resource from state
cleanupNode := &NodeDestroyResource{
NodeAbstractResource: NewNodeAbstractResource(res.Addr.Config()),
Addr: res.Addr,
}
g.Add(cleanupNode)
}
return &g, nil
}

View File

@ -5,6 +5,7 @@ import (
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/tfdiags"
)
@ -54,14 +55,63 @@ func (n *nodeExpandPlannableResource) DynamicExpand(ctx EvalContext) (*Graph, er
var g Graph
expander := ctx.InstanceExpander()
for _, module := range expander.ExpandModule(n.Addr.Module) {
var resources []addrs.AbsResource
moduleInstances := expander.ExpandModule(n.Addr.Module)
// Add the current expanded resource to the graph
for _, module := range moduleInstances {
resAddr := n.Addr.Resource.Absolute(module)
resources = append(resources, resAddr)
g.Add(&NodePlannableResource{
NodeAbstractResource: n.NodeAbstractResource,
Addr: n.Addr.Resource.Absolute(module),
Addr: resAddr,
ForceCreateBeforeDestroy: n.ForceCreateBeforeDestroy,
})
}
// Lock the state while we inspect it
state := ctx.State().Lock()
defer ctx.State().Unlock()
var orphans []*states.Resource
for _, res := range state.Resources(n.Addr) {
found := false
for _, m := range moduleInstances {
if m.Equal(res.Addr.Module) {
found = true
break
}
}
// Address form state was not found in the current config
if !found {
orphans = append(orphans, res)
}
}
// The concrete resource factory we'll use for orphans
concreteResourceOrphan := func(a *NodeAbstractResourceInstance) *NodePlannableResourceInstanceOrphan {
// Add the config and state since we don't do that via transforms
a.Config = n.Config
a.ResolvedProvider = n.ResolvedProvider
a.Schema = n.Schema
a.ProvisionerSchemas = n.ProvisionerSchemas
a.ProviderMetas = n.ProviderMetas
return &NodePlannableResourceInstanceOrphan{
NodeAbstractResourceInstance: a,
}
}
for _, res := range orphans {
for key := range res.Instances {
addr := res.Addr.Instance(key)
abs := NewNodeAbstractResourceInstance(addr)
abs.AttachResourceState(res)
n := concreteResourceOrphan(abs)
g.Add(n)
}
}
return &g, nil
}
@ -200,7 +250,7 @@ func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
},
// Add the count/for_each orphans
&OrphanResourceCountTransformer{
&OrphanResourceInstanceCountTransformer{
Concrete: concreteResourceOrphan,
Addr: n.Addr,
InstanceAddrs: instanceAddrs,

View File

@ -147,7 +147,7 @@ func (n *NodeRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph,
// Add the count orphans to make sure these resources are accounted for
// during a scale in.
&OrphanResourceCountTransformer{
&OrphanResourceInstanceCountTransformer{
Concrete: concreteResource,
Addr: n.Addr,
InstanceAddrs: instanceAddrs,

View File

@ -8,14 +8,14 @@ import (
"github.com/hashicorp/terraform/states"
)
// OrphanResourceCountTransformer is a GraphTransformer that adds orphans
// OrphanResourceInstanceCountTransformer is a GraphTransformer that adds orphans
// for an expanded count to the graph. The determination of this depends
// on the count argument given.
//
// Orphans are found by comparing the count to what is found in the state.
// This transform assumes that if an element in the state is within the count
// bounds given, that it is not an orphan.
type OrphanResourceCountTransformer struct {
type OrphanResourceInstanceCountTransformer struct {
Concrete ConcreteResourceInstanceNodeFunc
Addr addrs.AbsResource // Addr of the resource to look for orphans
@ -23,7 +23,7 @@ type OrphanResourceCountTransformer struct {
State *states.State // Full global state
}
func (t *OrphanResourceCountTransformer) Transform(g *Graph) error {
func (t *OrphanResourceInstanceCountTransformer) Transform(g *Graph) error {
rs := t.State.Resource(t.Addr)
if rs == nil {
return nil // Resource doesn't exist in state, so nothing to do!
@ -47,7 +47,7 @@ Have:
if f := t.Concrete; f != nil {
node = f(abstract)
}
log.Printf("[TRACE] OrphanResourceCountTransformer: adding %s as %T", thisAddr, node)
log.Printf("[TRACE] OrphanResourceInstanceCountTransformer: adding %s as %T", thisAddr, node)
g.Add(node)
}