github.com/adrian-bl/terraform@v0.7.0-rc2.0.20160705220747-de0a34fc3517/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 Diff **InstanceDiff 73 State **InstanceState 74 OutputDiff **InstanceDiff 75 OutputState **InstanceState 76 } 77 78 // TODO: test 79 func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) { 80 state := *n.State 81 config := *n.Config 82 provider := *n.Provider 83 84 // Call pre-diff hook 85 err := ctx.Hook(func(h Hook) (HookAction, error) { 86 return h.PreDiff(n.Info, state) 87 }) 88 if err != nil { 89 return nil, err 90 } 91 92 // The state for the diff must never be nil 93 diffState := state 94 if diffState == nil { 95 diffState = new(InstanceState) 96 } 97 diffState.init() 98 99 // Diff! 100 diff, err := provider.Diff(n.Info, diffState, config) 101 if err != nil { 102 return nil, err 103 } 104 if diff == nil { 105 diff = new(InstanceDiff) 106 } 107 108 // Preserve the DestroyTainted flag 109 if n.Diff != nil { 110 diff.DestroyTainted = (*n.Diff).DestroyTainted 111 } 112 113 // Require a destroy if there is an ID and it requires new. 114 if diff.RequiresNew() && state != nil && state.ID != "" { 115 diff.Destroy = true 116 } 117 118 // If we're creating a new resource, compute its ID 119 if diff.RequiresNew() || state == nil || state.ID == "" { 120 var oldID string 121 if state != nil { 122 oldID = state.Attributes["id"] 123 } 124 125 // Add diff to compute new ID 126 diff.init() 127 diff.Attributes["id"] = &ResourceAttrDiff{ 128 Old: oldID, 129 NewComputed: true, 130 RequiresNew: true, 131 Type: DiffAttrOutput, 132 } 133 } 134 135 // Call post-refresh hook 136 err = ctx.Hook(func(h Hook) (HookAction, error) { 137 return h.PostDiff(n.Info, diff) 138 }) 139 if err != nil { 140 return nil, err 141 } 142 143 // Update our output 144 *n.OutputDiff = diff 145 146 // Update the state if we care 147 if n.OutputState != nil { 148 *n.OutputState = state 149 150 // Merge our state so that the state is updated with our plan 151 if !diff.Empty() && n.OutputState != nil { 152 *n.OutputState = state.MergeDiff(diff) 153 } 154 } 155 156 return nil, nil 157 } 158 159 // EvalDiffDestroy is an EvalNode implementation that returns a plain 160 // destroy diff. 161 type EvalDiffDestroy struct { 162 Info *InstanceInfo 163 State **InstanceState 164 Output **InstanceDiff 165 } 166 167 // TODO: test 168 func (n *EvalDiffDestroy) Eval(ctx EvalContext) (interface{}, error) { 169 state := *n.State 170 171 // If there is no state or we don't have an ID, we're already destroyed 172 if state == nil || state.ID == "" { 173 return nil, nil 174 } 175 176 // Call pre-diff hook 177 err := ctx.Hook(func(h Hook) (HookAction, error) { 178 return h.PreDiff(n.Info, state) 179 }) 180 if err != nil { 181 return nil, err 182 } 183 184 // The diff 185 diff := &InstanceDiff{Destroy: true} 186 187 // Call post-diff hook 188 err = ctx.Hook(func(h Hook) (HookAction, error) { 189 return h.PostDiff(n.Info, diff) 190 }) 191 if err != nil { 192 return nil, err 193 } 194 195 // Update our output 196 *n.Output = diff 197 198 return nil, nil 199 } 200 201 // EvalDiffDestroyModule is an EvalNode implementation that writes the diff to 202 // the full diff. 203 type EvalDiffDestroyModule struct { 204 Path []string 205 } 206 207 // TODO: test 208 func (n *EvalDiffDestroyModule) Eval(ctx EvalContext) (interface{}, error) { 209 diff, lock := ctx.Diff() 210 211 // Acquire the lock so that we can do this safely concurrently 212 lock.Lock() 213 defer lock.Unlock() 214 215 // Write the diff 216 modDiff := diff.ModuleByPath(n.Path) 217 if modDiff == nil { 218 modDiff = diff.AddModule(n.Path) 219 } 220 modDiff.Destroy = true 221 222 return nil, nil 223 } 224 225 // EvalFilterDiff is an EvalNode implementation that filters the diff 226 // according to some filter. 227 type EvalFilterDiff struct { 228 // Input and output 229 Diff **InstanceDiff 230 Output **InstanceDiff 231 232 // Destroy, if true, will only include a destroy diff if it is set. 233 Destroy bool 234 } 235 236 func (n *EvalFilterDiff) Eval(ctx EvalContext) (interface{}, error) { 237 if *n.Diff == nil { 238 return nil, nil 239 } 240 241 input := *n.Diff 242 result := new(InstanceDiff) 243 244 if n.Destroy { 245 if input.Destroy || input.RequiresNew() { 246 result.Destroy = true 247 } 248 } 249 250 if n.Output != nil { 251 *n.Output = result 252 } 253 254 return nil, nil 255 } 256 257 // EvalReadDiff is an EvalNode implementation that writes the diff to 258 // the full diff. 259 type EvalReadDiff struct { 260 Name string 261 Diff **InstanceDiff 262 } 263 264 func (n *EvalReadDiff) Eval(ctx EvalContext) (interface{}, error) { 265 diff, lock := ctx.Diff() 266 267 // Acquire the lock so that we can do this safely concurrently 268 lock.Lock() 269 defer lock.Unlock() 270 271 // Write the diff 272 modDiff := diff.ModuleByPath(ctx.Path()) 273 if modDiff == nil { 274 return nil, nil 275 } 276 277 *n.Diff = modDiff.Resources[n.Name] 278 279 return nil, nil 280 } 281 282 // EvalWriteDiff is an EvalNode implementation that writes the diff to 283 // the full diff. 284 type EvalWriteDiff struct { 285 Name string 286 Diff **InstanceDiff 287 } 288 289 // TODO: test 290 func (n *EvalWriteDiff) Eval(ctx EvalContext) (interface{}, error) { 291 diff, lock := ctx.Diff() 292 293 // The diff to write, if its empty it should write nil 294 var diffVal *InstanceDiff 295 if n.Diff != nil { 296 diffVal = *n.Diff 297 } 298 if diffVal.Empty() { 299 diffVal = nil 300 } 301 302 // Acquire the lock so that we can do this safely concurrently 303 lock.Lock() 304 defer lock.Unlock() 305 306 // Write the diff 307 modDiff := diff.ModuleByPath(ctx.Path()) 308 if modDiff == nil { 309 modDiff = diff.AddModule(ctx.Path()) 310 } 311 if diffVal != nil { 312 modDiff.Resources[n.Name] = diffVal 313 } else { 314 delete(modDiff.Resources, n.Name) 315 } 316 317 return nil, nil 318 }