6 "github.com/hashicorp/terraform/configs"
7 "github.com/hashicorp/terraform/dag"
8 "github.com/hashicorp/terraform/states"
11 // OrphanResourceInstanceTransformer is a GraphTransformer that adds orphaned
12 // resource instances to the graph. An "orphan" is an instance that is present
13 // in the state but belongs to a resource that is no longer present in the
16 // This is not the transformer that deals with "count orphans" (instances that
17 // are no longer covered by a resource's "count" or "for_each" setting); that's
18 // handled instead by OrphanResourceCountTransformer.
19 type OrphanResourceInstanceTransformer struct {
20 Concrete ConcreteResourceInstanceNodeFunc
22 // State is the global state. We require the global state to
23 // properly find module orphans at our path.
26 // Config is the root node in the configuration tree. We'll look up
27 // the appropriate note in this tree using the path in each node.
28 Config *configs.Config
31 func (t *OrphanResourceInstanceTransformer) Transform(g *Graph) error {
33 // If the entire state is nil, there can't be any orphans
37 // Should never happen: we can't be doing any Terraform operations
38 // without at least an empty configuration.
39 panic("OrphanResourceInstanceTransformer used without setting Config")
42 // Go through the modules and for each module transform in order
44 for _, ms := range t.State.Modules {
45 if err := t.transform(g, ms); err != nil {
53 func (t *OrphanResourceInstanceTransformer) transform(g *Graph, ms *states.Module) error {
60 // Get the configuration for this module. The configuration might be
61 // nil if the module was removed from the configuration. This is okay,
62 // this just means that every resource is an orphan.
64 if c := t.Config.DescendentForInstance(moduleAddr); c != nil {
68 // An "orphan" is a resource that is in the state but not the configuration,
69 // so we'll walk the state resources and try to correlate each of them
70 // with a configuration block. Each orphan gets a node in the graph whose
71 // type is decided by t.Concrete.
73 // We don't handle orphans related to changes in the "count" and "for_each"
74 // pseudo-arguments here. They are handled by OrphanResourceCountTransformer.
75 for _, rs := range ms.Resources {
77 if r := m.ResourceByAddr(rs.Addr); r != nil {
82 for key := range rs.Instances {
83 addr := rs.Addr.Instance(key).Absolute(moduleAddr)
84 abstract := NewNodeAbstractResourceInstance(addr)
85 var node dag.Vertex = abstract
86 if f := t.Concrete; f != nil {
89 log.Printf("[TRACE] OrphanResourceInstanceTransformer: adding single-instance orphan node for %s", addr)
97 // OrphanResourceTransformer is a GraphTransformer that adds orphaned
98 // resources to the graph. An "orphan" is a resource that is present in
99 // the state but no longer present in the config.
101 // This is separate to OrphanResourceInstanceTransformer in that it deals with
102 // whole resources, rather than individual instances of resources. Orphan
103 // resource nodes are only used during apply to clean up leftover empty
104 // resource state skeletons, after all of the instances inside have been
107 // This transformer will also create edges in the graph to any pre-existing
108 // node that creates or destroys the entire orphaned resource or any of its
109 // instances, to ensure that the "orphan-ness" of a resource is always dealt
110 // with after all other aspects of it.
111 type OrphanResourceTransformer struct {
112 Concrete ConcreteResourceNodeFunc
114 // State is the global state.
117 // Config is the root node in the configuration tree.
118 Config *configs.Config
121 func (t *OrphanResourceTransformer) Transform(g *Graph) error {
123 // If the entire state is nil, there can't be any orphans
127 // Should never happen: we can't be doing any Terraform operations
128 // without at least an empty configuration.
129 panic("OrphanResourceTransformer used without setting Config")
132 // We'll first collect up the existing nodes for each resource so we can
133 // create dependency edges for any new nodes we create.
134 deps := map[string][]dag.Vertex{}
135 for _, v := range g.Vertices() {
136 switch tv := v.(type) {
137 case GraphNodeResourceInstance:
138 k := tv.ResourceInstanceAddr().ContainingResource().String()
139 deps[k] = append(deps[k], v)
140 case GraphNodeResource:
141 k := tv.ResourceAddr().String()
142 deps[k] = append(deps[k], v)
143 case GraphNodeDestroyer:
144 k := tv.DestroyAddr().ContainingResource().String()
145 deps[k] = append(deps[k], v)
149 for _, ms := range t.State.Modules {
150 moduleAddr := ms.Addr
152 mc := t.Config.DescendentForInstance(moduleAddr) // might be nil if whole module has been removed
154 for _, rs := range ms.Resources {
156 if r := mc.Module.ResourceByAddr(rs.Addr); r != nil {
157 // It's in the config, so nothing to do for this one.
162 addr := rs.Addr.Absolute(moduleAddr)
163 abstract := NewNodeAbstractResource(addr)
164 var node dag.Vertex = abstract
165 if f := t.Concrete; f != nil {
168 log.Printf("[TRACE] OrphanResourceTransformer: adding whole-resource orphan node for %s", addr)
170 for _, dn := range deps[addr.String()] {
171 log.Printf("[TRACE] OrphanResourceTransformer: node %q depends on %q", dag.VertexName(node), dag.VertexName(dn))
172 g.Connect(dag.BasicEdge(node, dn))