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