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