github.com/opentofu/opentofu@v1.7.1/internal/tofu/transform_orphan_resource.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package tofu 7 8 import ( 9 "log" 10 11 "github.com/opentofu/opentofu/internal/configs" 12 "github.com/opentofu/opentofu/internal/dag" 13 "github.com/opentofu/opentofu/internal/states" 14 ) 15 16 // OrphanResourceInstanceTransformer is a GraphTransformer that adds orphaned 17 // resource instances to the graph. An "orphan" is an instance that is present 18 // in the state but belongs to a resource that is no longer present in the 19 // configuration. 20 // 21 // This is not the transformer that deals with "count orphans" (instances that 22 // are no longer covered by a resource's "count" or "for_each" setting); that's 23 // handled instead by OrphanResourceCountTransformer. 24 type OrphanResourceInstanceTransformer struct { 25 Concrete ConcreteResourceInstanceNodeFunc 26 27 // State is the global state. We require the global state to 28 // properly find module orphans at our path. 29 State *states.State 30 31 // Config is the root node in the configuration tree. We'll look up 32 // the appropriate note in this tree using the path in each node. 33 Config *configs.Config 34 35 // Do not apply this transformer 36 skip bool 37 } 38 39 func (t *OrphanResourceInstanceTransformer) Transform(g *Graph) error { 40 if t.skip { 41 return nil 42 } 43 44 if t.State == nil { 45 // If the entire state is nil, there can't be any orphans 46 return nil 47 } 48 if t.Config == nil { 49 // Should never happen: we can't be doing any OpenTofu operations 50 // without at least an empty configuration. 51 panic("OrphanResourceInstanceTransformer used without setting Config") 52 } 53 54 // Go through the modules and for each module transform in order 55 // to add the orphan. 56 for _, ms := range t.State.Modules { 57 if err := t.transform(g, ms); err != nil { 58 return err 59 } 60 } 61 62 return nil 63 } 64 65 func (t *OrphanResourceInstanceTransformer) transform(g *Graph, ms *states.Module) error { 66 if ms == nil { 67 return nil 68 } 69 70 moduleAddr := ms.Addr 71 72 // Get the configuration for this module. The configuration might be 73 // nil if the module was removed from the configuration. This is okay, 74 // this just means that every resource is an orphan. 75 var m *configs.Module 76 if c := t.Config.DescendentForInstance(moduleAddr); c != nil { 77 m = c.Module 78 } 79 80 // An "orphan" is a resource that is in the state but not the configuration, 81 // so we'll walk the state resources and try to correlate each of them 82 // with a configuration block. Each orphan gets a node in the graph whose 83 // type is decided by t.Concrete. 84 // 85 // We don't handle orphans related to changes in the "count" and "for_each" 86 // pseudo-arguments here. They are handled by OrphanResourceCountTransformer. 87 for _, rs := range ms.Resources { 88 if m != nil { 89 if r := m.ResourceByAddr(rs.Addr.Resource); r != nil { 90 continue 91 } 92 } 93 94 for key, inst := range rs.Instances { 95 // Instances which have no current objects (only one or more 96 // deposed objects) will be taken care of separately 97 if inst.Current == nil { 98 continue 99 } 100 101 addr := rs.Addr.Instance(key) 102 abstract := NewNodeAbstractResourceInstance(addr) 103 var node dag.Vertex = abstract 104 if f := t.Concrete; f != nil { 105 node = f(abstract) 106 } 107 log.Printf("[TRACE] OrphanResourceInstanceTransformer: adding single-instance orphan node for %s", addr) 108 g.Add(node) 109 } 110 } 111 112 return nil 113 }