opentofu/terraform/transform_orphan_resource.go
James Bardin 0dcca1bc37 make the root node a nodeCloseModule for root
Replace the graphNodeRoot for the main graph with a nodeCloseModule for
the root module. USe a new transformer as well, so as to not change any
behavior of DynamicExpand graphs.

Closing out the root module like we do with sub modules means we no
longer need the OrphanResourceTransformer, or the NodeDestroyResource.
The old resource destroy logic has mostly moved into the instance nodes,
and the remaining resource node was just for cleanup, which need to be
done again by the module since there isn't always a NodeDestroyResource
to be evaluated.

The more-correct state caused a few tests to fail, which need to be
cleaned up to match the state without empty resource husks.
2020-04-02 16:00:36 -04:00

96 lines
2.9 KiB
Go

package terraform
import (
"log"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/states"
)
// OrphanResourceInstanceTransformer is a GraphTransformer that adds orphaned
// resource instances to the graph. An "orphan" is an instance that is present
// in the state but belongs to a resource that is no longer present in the
// configuration.
//
// This is not the transformer that deals with "count orphans" (instances that
// are no longer covered by a resource's "count" or "for_each" setting); that's
// handled instead by OrphanResourceCountTransformer.
type OrphanResourceInstanceTransformer struct {
Concrete ConcreteResourceInstanceNodeFunc
// State is the global state. We require the global state to
// properly find module orphans at our path.
State *states.State
// Config is the root node in the configuration tree. We'll look up
// the appropriate note in this tree using the path in each node.
Config *configs.Config
}
func (t *OrphanResourceInstanceTransformer) Transform(g *Graph) error {
if t.State == nil {
// If the entire state is nil, there can't be any orphans
return nil
}
if t.Config == nil {
// Should never happen: we can't be doing any Terraform operations
// without at least an empty configuration.
panic("OrphanResourceInstanceTransformer used without setting Config")
}
// Go through the modules and for each module transform in order
// to add the orphan.
for _, ms := range t.State.Modules {
if err := t.transform(g, ms); err != nil {
return err
}
}
return nil
}
func (t *OrphanResourceInstanceTransformer) transform(g *Graph, ms *states.Module) error {
if ms == nil {
return nil
}
moduleAddr := ms.Addr
// Get the configuration for this module. The configuration might be
// nil if the module was removed from the configuration. This is okay,
// this just means that every resource is an orphan.
var m *configs.Module
if c := t.Config.DescendentForInstance(moduleAddr); c != nil {
m = c.Module
}
// An "orphan" is a resource that is in the state but not the configuration,
// so we'll walk the state resources and try to correlate each of them
// with a configuration block. Each orphan gets a node in the graph whose
// type is decided by t.Concrete.
//
// We don't handle orphans related to changes in the "count" and "for_each"
// pseudo-arguments here. They are handled by OrphanResourceCountTransformer.
for _, rs := range ms.Resources {
if m != nil {
if r := m.ResourceByAddr(rs.Addr.Resource); r != nil {
continue
}
}
for key := range rs.Instances {
addr := rs.Addr.Instance(key)
abstract := NewNodeAbstractResourceInstance(addr)
var node dag.Vertex = abstract
if f := t.Concrete; f != nil {
node = f(abstract)
}
log.Printf("[TRACE] OrphanResourceInstanceTransformer: adding single-instance orphan node for %s", addr)
g.Add(node)
}
}
return nil
}