github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/terraform/eval_diff.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 ) 7 8 // EvalCompareDiff is an EvalNode implementation that compares two diffs 9 // and errors if the diffs are not equal. 10 type EvalCompareDiff struct { 11 Info *InstanceInfo 12 One, Two **InstanceDiff 13 } 14 15 // TODO: test 16 func (n *EvalCompareDiff) Eval(ctx EvalContext) (interface{}, error) { 17 one, two := *n.One, *n.Two 18 19 // If either are nil, let them be empty 20 if one == nil { 21 one = new(InstanceDiff) 22 one.init() 23 } 24 if two == nil { 25 two = new(InstanceDiff) 26 two.init() 27 } 28 oneId := one.Attributes["id"] 29 twoId := two.Attributes["id"] 30 delete(one.Attributes, "id") 31 delete(two.Attributes, "id") 32 defer func() { 33 if oneId != nil { 34 one.Attributes["id"] = oneId 35 } 36 if twoId != nil { 37 two.Attributes["id"] = twoId 38 } 39 }() 40 41 if same, reason := one.Same(two); !same { 42 log.Printf("[ERROR] %s: diffs didn't match", n.Info.Id) 43 log.Printf("[ERROR] %s: reason: %s", n.Info.Id, reason) 44 log.Printf("[ERROR] %s: diff one: %#v", n.Info.Id, one) 45 log.Printf("[ERROR] %s: diff two: %#v", n.Info.Id, two) 46 return nil, fmt.Errorf( 47 "%s: diffs didn't match during apply. This is a bug with "+ 48 "Terraform and should be reported.", n.Info.Id) 49 } 50 51 return nil, nil 52 } 53 54 // EvalDiff is an EvalNode implementation that does a refresh for 55 // a resource. 56 type EvalDiff struct { 57 Info *InstanceInfo 58 Config **ResourceConfig 59 Provider *ResourceProvider 60 State **InstanceState 61 Output **InstanceDiff 62 OutputState **InstanceState 63 } 64 65 // TODO: test 66 func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) { 67 state := *n.State 68 config := *n.Config 69 provider := *n.Provider 70 71 // Call pre-diff hook 72 err := ctx.Hook(func(h Hook) (HookAction, error) { 73 return h.PreDiff(n.Info, state) 74 }) 75 if err != nil { 76 return nil, err 77 } 78 79 // The state for the diff must never be nil 80 diffState := state 81 if diffState == nil { 82 diffState = new(InstanceState) 83 } 84 diffState.init() 85 86 // Diff! 87 diff, err := provider.Diff(n.Info, diffState, config) 88 if err != nil { 89 return nil, err 90 } 91 if diff == nil { 92 diff = new(InstanceDiff) 93 } 94 95 // Require a destroy if there is no ID and it requires new. 96 if diff.RequiresNew() && state != nil && state.ID != "" { 97 diff.Destroy = true 98 } 99 100 // If we're creating a new resource, compute its ID 101 if diff.RequiresNew() || state == nil || state.ID == "" { 102 var oldID string 103 if state != nil { 104 oldID = state.Attributes["id"] 105 } 106 107 // Add diff to compute new ID 108 diff.init() 109 diff.Attributes["id"] = &ResourceAttrDiff{ 110 Old: oldID, 111 NewComputed: true, 112 RequiresNew: true, 113 Type: DiffAttrOutput, 114 } 115 } 116 117 // Call post-refresh hook 118 err = ctx.Hook(func(h Hook) (HookAction, error) { 119 return h.PostDiff(n.Info, diff) 120 }) 121 if err != nil { 122 return nil, err 123 } 124 125 // Update our output 126 *n.Output = diff 127 128 // Update the state if we care 129 if n.OutputState != nil { 130 *n.OutputState = state 131 132 // Merge our state so that the state is updated with our plan 133 if !diff.Empty() && n.OutputState != nil { 134 *n.OutputState = state.MergeDiff(diff) 135 } 136 } 137 138 return nil, nil 139 } 140 141 // EvalDiffDestroy is an EvalNode implementation that returns a plain 142 // destroy diff. 143 type EvalDiffDestroy struct { 144 Info *InstanceInfo 145 State **InstanceState 146 Output **InstanceDiff 147 } 148 149 // TODO: test 150 func (n *EvalDiffDestroy) Eval(ctx EvalContext) (interface{}, error) { 151 state := *n.State 152 153 // If there is no state or we don't have an ID, we're already destroyed 154 if state == nil || state.ID == "" { 155 return nil, nil 156 } 157 158 // Call pre-diff hook 159 err := ctx.Hook(func(h Hook) (HookAction, error) { 160 return h.PreDiff(n.Info, state) 161 }) 162 if err != nil { 163 return nil, err 164 } 165 166 // The diff 167 diff := &InstanceDiff{Destroy: true} 168 169 // Call post-diff hook 170 err = ctx.Hook(func(h Hook) (HookAction, error) { 171 return h.PostDiff(n.Info, diff) 172 }) 173 if err != nil { 174 return nil, err 175 } 176 177 // Update our output 178 *n.Output = diff 179 180 return nil, nil 181 } 182 183 // EvalDiffDestroyModule is an EvalNode implementation that writes the diff to 184 // the full diff. 185 type EvalDiffDestroyModule struct { 186 Path []string 187 } 188 189 // TODO: test 190 func (n *EvalDiffDestroyModule) Eval(ctx EvalContext) (interface{}, error) { 191 diff, lock := ctx.Diff() 192 193 // Acquire the lock so that we can do this safely concurrently 194 lock.Lock() 195 defer lock.Unlock() 196 197 // Write the diff 198 modDiff := diff.ModuleByPath(n.Path) 199 if modDiff == nil { 200 modDiff = diff.AddModule(n.Path) 201 } 202 modDiff.Destroy = true 203 204 return nil, nil 205 } 206 207 // EvalDiffTainted is an EvalNode implementation that writes the diff to 208 // the full diff. 209 type EvalDiffTainted struct { 210 Name string 211 Diff **InstanceDiff 212 } 213 214 // TODO: test 215 func (n *EvalDiffTainted) Eval(ctx EvalContext) (interface{}, error) { 216 state, lock := ctx.State() 217 218 // Get a read lock so we can access this instance 219 lock.RLock() 220 defer lock.RUnlock() 221 222 // Look for the module state. If we don't have one, then it doesn't matter. 223 mod := state.ModuleByPath(ctx.Path()) 224 if mod == nil { 225 return nil, nil 226 } 227 228 // Look for the resource state. If we don't have one, then it is okay. 229 rs := mod.Resources[n.Name] 230 if rs == nil { 231 return nil, nil 232 } 233 234 // If we have tainted, then mark it on the diff 235 if len(rs.Tainted) > 0 { 236 (*n.Diff).DestroyTainted = true 237 } 238 239 return nil, nil 240 } 241 242 // EvalFilterDiff is an EvalNode implementation that filters the diff 243 // according to some filter. 244 type EvalFilterDiff struct { 245 // Input and output 246 Diff **InstanceDiff 247 Output **InstanceDiff 248 249 // Destroy, if true, will only include a destroy diff if it is set. 250 Destroy bool 251 } 252 253 func (n *EvalFilterDiff) Eval(ctx EvalContext) (interface{}, error) { 254 if *n.Diff == nil { 255 return nil, nil 256 } 257 258 input := *n.Diff 259 result := new(InstanceDiff) 260 261 if n.Destroy { 262 if input.Destroy || input.RequiresNew() { 263 result.Destroy = true 264 } 265 } 266 267 if n.Output != nil { 268 *n.Output = result 269 } 270 271 return nil, nil 272 } 273 274 // EvalReadDiff is an EvalNode implementation that writes the diff to 275 // the full diff. 276 type EvalReadDiff struct { 277 Name string 278 Diff **InstanceDiff 279 } 280 281 func (n *EvalReadDiff) Eval(ctx EvalContext) (interface{}, error) { 282 diff, lock := ctx.Diff() 283 284 // Acquire the lock so that we can do this safely concurrently 285 lock.Lock() 286 defer lock.Unlock() 287 288 // Write the diff 289 modDiff := diff.ModuleByPath(ctx.Path()) 290 if modDiff == nil { 291 return nil, nil 292 } 293 294 *n.Diff = modDiff.Resources[n.Name] 295 296 return nil, nil 297 } 298 299 // EvalWriteDiff is an EvalNode implementation that writes the diff to 300 // the full diff. 301 type EvalWriteDiff struct { 302 Name string 303 Diff **InstanceDiff 304 } 305 306 // TODO: test 307 func (n *EvalWriteDiff) Eval(ctx EvalContext) (interface{}, error) { 308 diff, lock := ctx.Diff() 309 310 // The diff to write, if its empty it should write nil 311 var diffVal *InstanceDiff 312 if n.Diff != nil { 313 diffVal = *n.Diff 314 } 315 if diffVal.Empty() { 316 diffVal = nil 317 } 318 319 // Acquire the lock so that we can do this safely concurrently 320 lock.Lock() 321 defer lock.Unlock() 322 323 // Write the diff 324 modDiff := diff.ModuleByPath(ctx.Path()) 325 if modDiff == nil { 326 modDiff = diff.AddModule(ctx.Path()) 327 } 328 if diffVal != nil { 329 modDiff.Resources[n.Name] = diffVal 330 } else { 331 delete(modDiff.Resources, n.Name) 332 } 333 334 return nil, nil 335 }