github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/plans/changes_sync.go (about) 1 package plans 2 3 import ( 4 "fmt" 5 "sync" 6 7 "github.com/hashicorp/terraform/internal/addrs" 8 "github.com/hashicorp/terraform/internal/states" 9 ) 10 11 // ChangesSync is a wrapper around a Changes that provides a concurrency-safe 12 // interface to insert new changes and retrieve copies of existing changes. 13 // 14 // Each ChangesSync is independent of all others, so all concurrent writers 15 // to a particular Changes must share a single ChangesSync. Behavior is 16 // undefined if any other caller makes changes to the underlying Changes 17 // object or its nested objects concurrently with any of the methods of a 18 // particular ChangesSync. 19 type ChangesSync struct { 20 lock sync.Mutex 21 changes *Changes 22 } 23 24 // IsFullDestroy returns true if the set of changes indicates we are doing a 25 // destroy of all resources. 26 func (cs *ChangesSync) IsFullDestroy() bool { 27 if cs == nil { 28 panic("FullDestroy on nil ChangesSync") 29 } 30 cs.lock.Lock() 31 defer cs.lock.Unlock() 32 33 for _, c := range cs.changes.Resources { 34 if c.Action != Delete { 35 return false 36 } 37 } 38 39 return true 40 } 41 42 // AppendResourceInstanceChange records the given resource instance change in 43 // the set of planned resource changes. 44 // 45 // The caller must ensure that there are no concurrent writes to the given 46 // change while this method is running, but it is safe to resume mutating 47 // it after this method returns without affecting the saved change. 48 func (cs *ChangesSync) AppendResourceInstanceChange(changeSrc *ResourceInstanceChangeSrc) { 49 if cs == nil { 50 panic("AppendResourceInstanceChange on nil ChangesSync") 51 } 52 cs.lock.Lock() 53 defer cs.lock.Unlock() 54 55 s := changeSrc.DeepCopy() 56 cs.changes.Resources = append(cs.changes.Resources, s) 57 } 58 59 // GetResourceInstanceChange searches the set of resource instance changes for 60 // one matching the given address and generation, returning it if it exists. 61 // 62 // If no such change exists, nil is returned. 63 // 64 // The returned object is a deep copy of the change recorded in the plan, so 65 // callers may mutate it although it's generally better (less confusing) to 66 // treat planned changes as immutable after they've been initially constructed. 67 func (cs *ChangesSync) GetResourceInstanceChange(addr addrs.AbsResourceInstance, gen states.Generation) *ResourceInstanceChangeSrc { 68 if cs == nil { 69 panic("GetResourceInstanceChange on nil ChangesSync") 70 } 71 cs.lock.Lock() 72 defer cs.lock.Unlock() 73 74 if gen == states.CurrentGen { 75 return cs.changes.ResourceInstance(addr).DeepCopy() 76 } 77 if dk, ok := gen.(states.DeposedKey); ok { 78 return cs.changes.ResourceInstanceDeposed(addr, dk).DeepCopy() 79 } 80 panic(fmt.Sprintf("unsupported generation value %#v", gen)) 81 } 82 83 // GetChangesForConfigResource searches the set of resource instance 84 // changes and returns all changes related to a given configuration address. 85 // This is be used to find possible changes related to a configuration 86 // reference. 87 // 88 // If no such changes exist, nil is returned. 89 // 90 // The returned objects are a deep copy of the change recorded in the plan, so 91 // callers may mutate them although it's generally better (less confusing) to 92 // treat planned changes as immutable after they've been initially constructed. 93 func (cs *ChangesSync) GetChangesForConfigResource(addr addrs.ConfigResource) []*ResourceInstanceChangeSrc { 94 if cs == nil { 95 panic("GetChangesForConfigResource on nil ChangesSync") 96 } 97 cs.lock.Lock() 98 defer cs.lock.Unlock() 99 var changes []*ResourceInstanceChangeSrc 100 for _, c := range cs.changes.InstancesForConfigResource(addr) { 101 changes = append(changes, c.DeepCopy()) 102 } 103 return changes 104 } 105 106 // GetChangesForAbsResource searches the set of resource instance 107 // changes and returns all changes related to a given configuration address. 108 // 109 // If no such changes exist, nil is returned. 110 // 111 // The returned objects are a deep copy of the change recorded in the plan, so 112 // callers may mutate them although it's generally better (less confusing) to 113 // treat planned changes as immutable after they've been initially constructed. 114 func (cs *ChangesSync) GetChangesForAbsResource(addr addrs.AbsResource) []*ResourceInstanceChangeSrc { 115 if cs == nil { 116 panic("GetChangesForAbsResource on nil ChangesSync") 117 } 118 cs.lock.Lock() 119 defer cs.lock.Unlock() 120 var changes []*ResourceInstanceChangeSrc 121 for _, c := range cs.changes.InstancesForAbsResource(addr) { 122 changes = append(changes, c.DeepCopy()) 123 } 124 return changes 125 } 126 127 // RemoveResourceInstanceChange searches the set of resource instance changes 128 // for one matching the given address and generation, and removes it from the 129 // set if it exists. 130 func (cs *ChangesSync) RemoveResourceInstanceChange(addr addrs.AbsResourceInstance, gen states.Generation) { 131 if cs == nil { 132 panic("RemoveResourceInstanceChange on nil ChangesSync") 133 } 134 cs.lock.Lock() 135 defer cs.lock.Unlock() 136 137 dk := states.NotDeposed 138 if realDK, ok := gen.(states.DeposedKey); ok { 139 dk = realDK 140 } 141 142 addrStr := addr.String() 143 for i, r := range cs.changes.Resources { 144 if r.Addr.String() != addrStr || r.DeposedKey != dk { 145 continue 146 } 147 copy(cs.changes.Resources[i:], cs.changes.Resources[i+1:]) 148 cs.changes.Resources = cs.changes.Resources[:len(cs.changes.Resources)-1] 149 return 150 } 151 } 152 153 // AppendOutputChange records the given output value change in the set of 154 // planned value changes. 155 // 156 // The caller must ensure that there are no concurrent writes to the given 157 // change while this method is running, but it is safe to resume mutating 158 // it after this method returns without affecting the saved change. 159 func (cs *ChangesSync) AppendOutputChange(changeSrc *OutputChangeSrc) { 160 if cs == nil { 161 panic("AppendOutputChange on nil ChangesSync") 162 } 163 cs.lock.Lock() 164 defer cs.lock.Unlock() 165 166 s := changeSrc.DeepCopy() 167 cs.changes.Outputs = append(cs.changes.Outputs, s) 168 } 169 170 // GetOutputChange searches the set of output value changes for one matching 171 // the given address, returning it if it exists. 172 // 173 // If no such change exists, nil is returned. 174 // 175 // The returned object is a deep copy of the change recorded in the plan, so 176 // callers may mutate it although it's generally better (less confusing) to 177 // treat planned changes as immutable after they've been initially constructed. 178 func (cs *ChangesSync) GetOutputChange(addr addrs.AbsOutputValue) *OutputChangeSrc { 179 if cs == nil { 180 panic("GetOutputChange on nil ChangesSync") 181 } 182 cs.lock.Lock() 183 defer cs.lock.Unlock() 184 185 return cs.changes.OutputValue(addr) 186 } 187 188 // GetRootOutputChanges searches the set of output changes for any that reside 189 // the root module. If no such changes exist, nil is returned. 190 // 191 // The returned objects are a deep copy of the change recorded in the plan, so 192 // callers may mutate them although it's generally better (less confusing) to 193 // treat planned changes as immutable after they've been initially constructed. 194 func (cs *ChangesSync) GetRootOutputChanges() []*OutputChangeSrc { 195 if cs == nil { 196 panic("GetRootOutputChanges on nil ChangesSync") 197 } 198 cs.lock.Lock() 199 defer cs.lock.Unlock() 200 201 return cs.changes.RootOutputValues() 202 } 203 204 // GetOutputChanges searches the set of output changes for any that reside in 205 // module instances beneath the given module. If no changes exist, nil 206 // is returned. 207 // 208 // The returned objects are a deep copy of the change recorded in the plan, so 209 // callers may mutate them although it's generally better (less confusing) to 210 // treat planned changes as immutable after they've been initially constructed. 211 func (cs *ChangesSync) GetOutputChanges(parent addrs.ModuleInstance, module addrs.ModuleCall) []*OutputChangeSrc { 212 if cs == nil { 213 panic("GetOutputChange on nil ChangesSync") 214 } 215 cs.lock.Lock() 216 defer cs.lock.Unlock() 217 218 return cs.changes.OutputValues(parent, module) 219 } 220 221 // RemoveOutputChange searches the set of output value changes for one matching 222 // the given address, and removes it from the set if it exists. 223 func (cs *ChangesSync) RemoveOutputChange(addr addrs.AbsOutputValue) { 224 if cs == nil { 225 panic("RemoveOutputChange on nil ChangesSync") 226 } 227 cs.lock.Lock() 228 defer cs.lock.Unlock() 229 230 addrStr := addr.String() 231 232 for i, o := range cs.changes.Outputs { 233 if o.Addr.String() != addrStr { 234 continue 235 } 236 copy(cs.changes.Outputs[i:], cs.changes.Outputs[i+1:]) 237 cs.changes.Outputs = cs.changes.Outputs[:len(cs.changes.Outputs)-1] 238 return 239 } 240 }