LEFT | RIGHT |
1 // Copyright 2012, 2013 Canonical Ltd. | 1 // Copyright 2012, 2013 Canonical Ltd. |
2 // Licensed under the AGPLv3, see LICENCE file for details. | 2 // Licensed under the AGPLv3, see LICENCE file for details. |
3 | 3 |
4 package state | 4 package state |
5 | 5 |
6 import ( | 6 import ( |
7 stderrors "errors" | 7 stderrors "errors" |
8 "fmt" | 8 "fmt" |
9 "labix.org/v2/mgo" | 9 "labix.org/v2/mgo" |
10 "labix.org/v2/mgo/txn" | 10 "labix.org/v2/mgo/txn" |
(...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
219 // directly. | 219 // directly. |
220 func (u *Unit) Destroy() (err error) { | 220 func (u *Unit) Destroy() (err error) { |
221 defer func() { | 221 defer func() { |
222 if err == nil { | 222 if err == nil { |
223 // This is a white lie; the document might actually be r
emoved. | 223 // This is a white lie; the document might actually be r
emoved. |
224 u.doc.Life = Dying | 224 u.doc.Life = Dying |
225 } | 225 } |
226 }() | 226 }() |
227 unit := &Unit{st: u.st, doc: u.doc} | 227 unit := &Unit{st: u.st, doc: u.doc} |
228 for i := 0; i < 5; i++ { | 228 for i := 0; i < 5; i++ { |
229 » » ops, err := unit.destroyOps() | 229 » » switch ops, err := unit.destroyOps(); err { |
230 » » switch { | 230 » » case errRefresh: |
231 » » case err == errRefresh: | 231 » » case errAlreadyDying: |
232 » » case err == errAlreadyDying: | |
233 return nil | 232 return nil |
234 » » case err != nil: | 233 » » case nil: |
235 » » » return err | |
236 » » default: | |
237 if err := unit.st.runTransaction(ops); err != txn.ErrAbo
rted { | 234 if err := unit.st.runTransaction(ops); err != txn.ErrAbo
rted { |
238 return err | 235 return err |
239 } | 236 } |
| 237 default: |
| 238 return err |
240 } | 239 } |
241 if err := unit.Refresh(); errors.IsNotFoundError(err) { | 240 if err := unit.Refresh(); errors.IsNotFoundError(err) { |
242 return nil | 241 return nil |
243 } else if err != nil { | 242 } else if err != nil { |
244 return err | 243 return err |
245 } | 244 } |
246 } | 245 } |
247 return ErrExcessiveContention | 246 return ErrExcessiveContention |
248 } | 247 } |
249 | 248 |
250 // destroyOps returns the operations required to destroy the unit. If it | 249 // destroyOps returns the operations required to destroy the unit. If it |
251 // returns errRefresh, the unit should be refreshed and the destruction | 250 // returns errRefresh, the unit should be refreshed and the destruction |
252 // operations recalculated. | 251 // operations recalculated. |
253 func (u *Unit) destroyOps() ([]txn.Op, error) { | 252 func (u *Unit) destroyOps() ([]txn.Op, error) { |
254 if u.doc.Life != Alive { | 253 if u.doc.Life != Alive { |
255 return nil, errAlreadyDying | 254 return nil, errAlreadyDying |
256 } | 255 } |
257 » // In many cases, we just want to set Dying and let the agents deal with
it. | 256 |
258 » defaultOps := []txn.Op{{ | 257 » // Where possible, we'd like to be able to short-circuit unit destructio
n |
| 258 » // such that units can be removed directly rather than waiting for their |
| 259 » // agents to start, observe Dying, set Dead, and shut down; this takes a |
| 260 » // long time and is vexing to users. This turns out to be possible if an
d |
| 261 » // only if the unit agent has not yet set its status; this implies that
the |
| 262 » // most the unit could possibly have done is to run its install hook. |
| 263 » // |
| 264 » // There's no harm in removing a unit that's run its install hook only -
- |
| 265 » // or, at least, there is no more harm than there is in removing a unit |
| 266 » // that's run its stop hook, and that's the usual condition. |
| 267 » // |
| 268 » // Principals with subordinates are never eligible for this shortcut, |
| 269 » // because the unit agent must inevitably have set a status before getti
ng |
| 270 » // to the point where it can actually create its subordinate. |
| 271 » // |
| 272 » // Subordinates should be eligible for the shortcut but are not currentl
y |
| 273 » // considered, on the basis that (1) they were created by active princip
als |
| 274 » // and can be expected to be deployed pretty soon afterwards, so we don'
t |
| 275 » // lose much time and (2) by maintaining this restriction, I can reduce |
| 276 » // the number of tests that have to change and defer that improvement to |
| 277 » // its own CL. |
| 278 » minUnitsOp := minUnitsTriggerOp(u.st, u.ServiceName()) |
| 279 » setDyingOps := []txn.Op{{ |
259 C: u.st.units.Name, | 280 C: u.st.units.Name, |
260 Id: u.doc.Name, | 281 Id: u.doc.Name, |
261 Assert: isAliveDoc, | 282 Assert: isAliveDoc, |
262 Update: D{{"$set", D{{"life", Dying}}}}, | 283 Update: D{{"$set", D{{"life", Dying}}}}, |
263 » }} | 284 » }, minUnitsOp} |
264 | |
265 » // Subordinates, and principals with subordinates, are left for the agen
ts. | |
266 if u.doc.Principal != "" { | 285 if u.doc.Principal != "" { |
267 » » return defaultOps, nil | 286 » » return setDyingOps, nil |
268 } else if len(u.doc.Subordinates) != 0 { | 287 } else if len(u.doc.Subordinates) != 0 { |
269 » » return defaultOps, nil | 288 » » return setDyingOps, nil |
270 » } | 289 » } |
271 | 290 |
272 » // If the (known principal) unit has no assigned machine id, the unit ca
n | 291 » sdocId := u.globalKey() |
273 » // be removed directly. | 292 » sdoc, err := getStatus(u.st, sdocId) |
274 » asserts := D{{"machineid", u.doc.MachineId}} | |
275 » asserts = append(asserts, unitHasNoSubordinates...) | |
276 » asserts = append(asserts, isAliveDoc...) | |
277 » if u.doc.MachineId == "" { | |
278 » » return u.removeOps(asserts) | |
279 » } | |
280 | |
281 » // If the unit's machine has an instance id, leave it for the agents. | |
282 » m, err := u.st.Machine(u.doc.MachineId) | |
283 if errors.IsNotFoundError(err) { | 293 if errors.IsNotFoundError(err) { |
284 » » return nil, errRefresh | 294 » » return nil, errAlreadyDying |
285 } else if err != nil { | 295 } else if err != nil { |
286 return nil, err | 296 return nil, err |
287 } | 297 } |
288 » if _, found := m.InstanceId(); found { | 298 » if sdoc.Status != params.StatusPending { |
289 » » return defaultOps, nil | 299 » » return setDyingOps, nil |
290 » } | 300 » } |
291 | 301 » ops := []txn.Op{{ |
292 » // Units assigned to unprovisioned machines can be removed directly. | 302 » » C: u.st.statuses.Name, |
293 » ops := []txn.Op{{ | 303 » » Id: sdocId, |
294 » » C: u.st.machines.Name, | 304 » » Assert: D{{"status", params.StatusPending}}, |
295 » » Id: u.doc.MachineId, | 305 » }, minUnitsOp} |
296 » » Assert: D{{"instanceid", ""}}, | 306 » removeAsserts := append(isAliveDoc, unitHasNoSubordinates...) |
297 » }} | 307 » removeOps, err := u.removeOps(removeAsserts) |
298 » removeOps, err := u.removeOps(asserts) | 308 » if err == errAlreadyRemoved { |
299 » if err != nil { | 309 » » return nil, errAlreadyDying |
| 310 » } else if err != nil { |
300 return nil, err | 311 return nil, err |
301 } | 312 } |
302 return append(ops, removeOps...), nil | 313 return append(ops, removeOps...), nil |
303 } | 314 } |
| 315 |
| 316 var errAlreadyRemoved = stderrors.New("entity has already been removed") |
304 | 317 |
305 // removeOps returns the operations necessary to remove the unit, assuming | 318 // removeOps returns the operations necessary to remove the unit, assuming |
306 // the supplied asserts apply to the unit document. | 319 // the supplied asserts apply to the unit document. |
307 func (u *Unit) removeOps(asserts D) ([]txn.Op, error) { | 320 func (u *Unit) removeOps(asserts D) ([]txn.Op, error) { |
308 svc, err := u.st.Service(u.doc.Service) | 321 svc, err := u.st.Service(u.doc.Service) |
309 » if err != nil { | 322 » if errors.IsNotFoundError(err) { |
| 323 » » // If the service has been removed, the unit must already have b
een. |
| 324 » » return nil, errAlreadyRemoved |
| 325 » } else if err != nil { |
310 return nil, err | 326 return nil, err |
311 } | 327 } |
312 return svc.removeUnitOps(u, asserts) | 328 return svc.removeUnitOps(u, asserts) |
313 } | 329 } |
314 | 330 |
315 var ErrUnitHasSubordinates = stderrors.New("unit has subordinates") | 331 var ErrUnitHasSubordinates = stderrors.New("unit has subordinates") |
316 | 332 |
317 var unitHasNoSubordinates = D{{ | 333 var unitHasNoSubordinates = D{{ |
318 "$or", []D{ | 334 "$or", []D{ |
319 {{"subordinates", D{{"$size", 0}}}}, | 335 {{"subordinates", D{{"$size", 0}}}}, |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
351 } | 367 } |
352 | 368 |
353 // Remove removes the unit from state, and may remove its service as well, if | 369 // Remove removes the unit from state, and may remove its service as well, if |
354 // the service is Dying and no other references to it exist. It will fail if | 370 // the service is Dying and no other references to it exist. It will fail if |
355 // the unit is not Dead. | 371 // the unit is not Dead. |
356 func (u *Unit) Remove() (err error) { | 372 func (u *Unit) Remove() (err error) { |
357 defer utils.ErrorContextf(&err, "cannot remove unit %q", u) | 373 defer utils.ErrorContextf(&err, "cannot remove unit %q", u) |
358 if u.doc.Life != Dead { | 374 if u.doc.Life != Dead { |
359 return stderrors.New("unit is not dead") | 375 return stderrors.New("unit is not dead") |
360 } | 376 } |
361 svc, err := u.st.Service(u.doc.Service) | |
362 if err != nil { | |
363 return err | |
364 } | |
365 unit := &Unit{st: u.st, doc: u.doc} | 377 unit := &Unit{st: u.st, doc: u.doc} |
366 for i := 0; i < 5; i++ { | 378 for i := 0; i < 5; i++ { |
367 » » ops, err := svc.removeUnitOps(unit, isDeadDoc) | 379 » » switch ops, err := unit.removeOps(isDeadDoc); err { |
368 » » if err != nil { | 380 » » case errRefresh: |
369 » » » return err | 381 » » case errAlreadyRemoved: |
370 » » } | |
371 » » if err := svc.st.runTransaction(ops); err != txn.ErrAborted { | |
372 » » » return err | |
373 » » } | |
374 » » if err := svc.Refresh(); errors.IsNotFoundError(err) { | |
375 return nil | 382 return nil |
376 » » } else if err != nil { | 383 » » case nil: |
| 384 » » » if err := u.st.runTransaction(ops); err != txn.ErrAborte
d { |
| 385 » » » » return err |
| 386 » » » } |
| 387 » » default: |
377 return err | 388 return err |
378 } | 389 } |
379 if err := unit.Refresh(); errors.IsNotFoundError(err) { | 390 if err := unit.Refresh(); errors.IsNotFoundError(err) { |
380 return nil | 391 return nil |
381 } else if err != nil { | 392 } else if err != nil { |
382 return err | 393 return err |
383 } | 394 } |
384 } | 395 } |
385 return ErrExcessiveContention | 396 return ErrExcessiveContention |
386 } | 397 } |
(...skipping 633 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1020 return p1.Protocol < p2.Protocol | 1031 return p1.Protocol < p2.Protocol |
1021 } | 1032 } |
1022 return p1.Number < p2.Number | 1033 return p1.Number < p2.Number |
1023 } | 1034 } |
1024 | 1035 |
1025 // SortPorts sorts the given ports, first by protocol, | 1036 // SortPorts sorts the given ports, first by protocol, |
1026 // then by number. | 1037 // then by number. |
1027 func SortPorts(ports []instance.Port) { | 1038 func SortPorts(ports []instance.Port) { |
1028 sort.Sort(portSlice(ports)) | 1039 sort.Sort(portSlice(ports)) |
1029 } | 1040 } |
LEFT | RIGHT |