github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/transform_orphan_count.go (about) 1 package terraform 2 3 import ( 4 "log" 5 6 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 7 "github.com/hashicorp/terraform-plugin-sdk/internal/dag" 8 "github.com/hashicorp/terraform-plugin-sdk/internal/states" 9 "github.com/zclconf/go-cty/cty" 10 ) 11 12 // OrphanResourceCountTransformer is a GraphTransformer that adds orphans 13 // for an expanded count to the graph. The determination of this depends 14 // on the count argument given. 15 // 16 // Orphans are found by comparing the count to what is found in the state. 17 // This transform assumes that if an element in the state is within the count 18 // bounds given, that it is not an orphan. 19 type OrphanResourceCountTransformer struct { 20 Concrete ConcreteResourceInstanceNodeFunc 21 22 Count int // Actual count of the resource, or -1 if count is not set at all 23 ForEach map[string]cty.Value // The ForEach map on the resource 24 Addr addrs.AbsResource // Addr of the resource to look for orphans 25 State *states.State // Full global state 26 } 27 28 func (t *OrphanResourceCountTransformer) Transform(g *Graph) error { 29 rs := t.State.Resource(t.Addr) 30 if rs == nil { 31 return nil // Resource doesn't exist in state, so nothing to do! 32 } 33 34 haveKeys := make(map[addrs.InstanceKey]struct{}) 35 for key := range rs.Instances { 36 haveKeys[key] = struct{}{} 37 } 38 39 // if for_each is set, use that transformer 40 if t.ForEach != nil { 41 return t.transformForEach(haveKeys, g) 42 } 43 if t.Count < 0 { 44 return t.transformNoCount(haveKeys, g) 45 } 46 if t.Count == 0 { 47 return t.transformZeroCount(haveKeys, g) 48 } 49 return t.transformCount(haveKeys, g) 50 } 51 52 func (t *OrphanResourceCountTransformer) transformForEach(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error { 53 // If there is a NoKey node, add this to the graph first, 54 // so that we can create edges to it in subsequent (StringKey) nodes. 55 // This is because the last item determines the resource mode for the whole resource, 56 // (see SetResourceInstanceCurrent for more information) and we need to evaluate 57 // an orphaned (NoKey) resource before the in-memory state is updated 58 // to deal with a new for_each resource 59 _, hasNoKeyNode := haveKeys[addrs.NoKey] 60 var noKeyNode dag.Vertex 61 if hasNoKeyNode { 62 abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(addrs.NoKey)) 63 noKeyNode = abstract 64 if f := t.Concrete; f != nil { 65 noKeyNode = f(abstract) 66 } 67 g.Add(noKeyNode) 68 } 69 70 for key := range haveKeys { 71 // If the key is no-key, we have already added it, so skip 72 if key == addrs.NoKey { 73 continue 74 } 75 76 s, _ := key.(addrs.StringKey) 77 // If the key is present in our current for_each, carry on 78 if _, ok := t.ForEach[string(s)]; ok { 79 continue 80 } 81 82 abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key)) 83 var node dag.Vertex = abstract 84 if f := t.Concrete; f != nil { 85 node = f(abstract) 86 } 87 log.Printf("[TRACE] OrphanResourceCount(non-zero): adding %s as %T", t.Addr, node) 88 g.Add(node) 89 90 // Add edge to noKeyNode if it exists 91 if hasNoKeyNode { 92 g.Connect(dag.BasicEdge(node, noKeyNode)) 93 } 94 } 95 return nil 96 } 97 98 func (t *OrphanResourceCountTransformer) transformCount(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error { 99 // Due to the logic in Transform, we only get in here if our count is 100 // at least one. 101 102 _, have0Key := haveKeys[addrs.IntKey(0)] 103 104 for key := range haveKeys { 105 if key == addrs.NoKey && !have0Key { 106 // If we have no 0-key then we will accept a no-key instance 107 // as an alias for it. 108 continue 109 } 110 111 i, isInt := key.(addrs.IntKey) 112 if isInt && int(i) < t.Count { 113 continue 114 } 115 116 abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key)) 117 var node dag.Vertex = abstract 118 if f := t.Concrete; f != nil { 119 node = f(abstract) 120 } 121 log.Printf("[TRACE] OrphanResourceCount(non-zero): adding %s as %T", t.Addr, node) 122 g.Add(node) 123 } 124 125 return nil 126 } 127 128 func (t *OrphanResourceCountTransformer) transformZeroCount(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error { 129 // This case is easy: we need to orphan any keys we have at all. 130 131 for key := range haveKeys { 132 abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key)) 133 var node dag.Vertex = abstract 134 if f := t.Concrete; f != nil { 135 node = f(abstract) 136 } 137 log.Printf("[TRACE] OrphanResourceCount(zero): adding %s as %T", t.Addr, node) 138 g.Add(node) 139 } 140 141 return nil 142 } 143 144 func (t *OrphanResourceCountTransformer) transformNoCount(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error { 145 // Negative count indicates that count is not set at all, in which 146 // case we expect to have a single instance with no key set at all. 147 // However, we'll also accept an instance with key 0 set as an alias 148 // for it, in case the user has just deleted the "count" argument and 149 // so wants to keep the first instance in the set. 150 151 _, haveNoKey := haveKeys[addrs.NoKey] 152 _, have0Key := haveKeys[addrs.IntKey(0)] 153 keepKey := addrs.NoKey 154 if have0Key && !haveNoKey { 155 // If we don't have a no-key instance then we can use the 0-key instance 156 // instead. 157 keepKey = addrs.IntKey(0) 158 } 159 160 for key := range haveKeys { 161 if key == keepKey { 162 continue 163 } 164 165 abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key)) 166 var node dag.Vertex = abstract 167 if f := t.Concrete; f != nil { 168 node = f(abstract) 169 } 170 log.Printf("[TRACE] OrphanResourceCount(no-count): adding %s as %T", t.Addr, node) 171 g.Add(node) 172 } 173 174 return nil 175 }