Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 package state | 1 package state |
2 | 2 |
3 import ( | 3 import ( |
4 "errors" | 4 "errors" |
5 "fmt" | 5 "fmt" |
6 "labix.org/v2/mgo" | 6 "labix.org/v2/mgo" |
7 "labix.org/v2/mgo/bson" | 7 "labix.org/v2/mgo/bson" |
8 "labix.org/v2/mgo/txn" | 8 "labix.org/v2/mgo/txn" |
9 "launchpad.net/juju-core/charm" | 9 "launchpad.net/juju-core/charm" |
10 "launchpad.net/juju-core/trivial" | 10 "launchpad.net/juju-core/trivial" |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
46 | 46 |
47 // Life returns whether the service is Alive, Dying or Dead. | 47 // Life returns whether the service is Alive, Dying or Dead. |
48 func (s *Service) Life() Life { | 48 func (s *Service) Life() Life { |
49 return s.doc.Life | 49 return s.doc.Life |
50 } | 50 } |
51 | 51 |
52 var errRefresh = errors.New("cannot determine relation destruction operations; p lease refresh the service") | 52 var errRefresh = errors.New("cannot determine relation destruction operations; p lease refresh the service") |
53 | 53 |
54 // Destroy ensures that the service and all its relations will be removed at | 54 // Destroy ensures that the service and all its relations will be removed at |
55 // some point; if the service has no units, and no relation involving the | 55 // some point; if the service has no units, and no relation involving the |
56 // service has any units in scope, they will all be removed immediately. | 56 // service has any units in scope, they are all removed immediately. |
57 func (s *Service) Destroy() (err error) { | 57 func (s *Service) Destroy() (err error) { |
58 defer trivial.ErrorContextf(&err, "cannot destroy service %q", s) | 58 defer trivial.ErrorContextf(&err, "cannot destroy service %q", s) |
59 defer func() { | 59 defer func() { |
60 if err != nil { | 60 if err != nil { |
61 // This is a white lie; the document might actually be r emoved. | 61 // This is a white lie; the document might actually be r emoved. |
62 s.doc.Life = Dying | 62 s.doc.Life = Dying |
63 } | 63 } |
64 }() | 64 }() |
65 svc := &Service{s.st, s.doc} | 65 svc := &Service{s.st, s.doc} |
66 for i := 0; i < 5; i++ { | 66 for i := 0; i < 5; i++ { |
67 ops, err := svc.destroyOps() | 67 ops, err := svc.destroyOps() |
68 switch { | 68 switch { |
69 case err == errRefresh: | 69 case err == errRefresh: |
70 case err == errAlreadyDying: | |
71 return nil | |
70 case err != nil: | 72 case err != nil: |
71 return err | 73 return err |
72 case len(ops) == 0: | |
73 return nil | |
74 default: | 74 default: |
75 if err := svc.st.runner.Run(ops, "", nil); err != txn.Er rAborted { | 75 if err := svc.st.runner.Run(ops, "", nil); err != txn.Er rAborted { |
76 return err | 76 return err |
77 } | 77 } |
78 } | 78 } |
79 if err := svc.Refresh(); IsNotFound(err) { | 79 if err := svc.Refresh(); IsNotFound(err) { |
80 return nil | 80 return nil |
81 } else if err != nil { | 81 } else if err != nil { |
82 return err | 82 return err |
83 } | 83 } |
84 } | 84 } |
85 return ErrExcessiveContention | 85 return ErrExcessiveContention |
86 } | 86 } |
87 | 87 |
88 // destroyOps returns the operations required to destroy the service. If it | 88 // destroyOps returns the operations required to destroy the service. If it |
89 // returns errRefresh, the service should be refreshed and the destruction | 89 // returns errRefresh, the service should be refreshed and the destruction |
90 // operations recalculated. | 90 // operations recalculated. |
91 func (s *Service) destroyOps() ([]txn.Op, error) { | 91 func (s *Service) destroyOps() ([]txn.Op, error) { |
92 if s.doc.Life == Dying { | 92 if s.doc.Life == Dying { |
93 » » return nil, nil | 93 » » return nil, errAlreadyDying |
94 } | 94 } |
95 rels, err := s.Relations() | 95 rels, err := s.Relations() |
96 if err != nil { | 96 if err != nil { |
97 return nil, err | 97 return nil, err |
98 } | 98 } |
99 if len(rels) != s.doc.RelationCount { | 99 if len(rels) != s.doc.RelationCount { |
100 // This is just an early bail out. The relations obtained may st ill | |
101 // be wrong, but that situation will be caught by a combination of | |
102 // asserts on relationcount and on each known relation, below. | |
100 return nil, errRefresh | 103 return nil, errRefresh |
101 } | 104 } |
102 var ops []txn.Op | 105 var ops []txn.Op |
103 removeCount := 0 | 106 removeCount := 0 |
104 for _, rel := range rels { | 107 for _, rel := range rels { |
105 relOps, isRemove, err := rel.destroyOps(s.doc.Name) | 108 relOps, isRemove, err := rel.destroyOps(s.doc.Name) |
106 » » if err != nil { | 109 » » if err == errAlreadyDying { |
110 » » » relOps = []txn.Op{{ | |
111 » » » » C: s.st.relations.Name, | |
112 » » » » Id: rel.doc.Key, | |
113 » » » » Assert: D{{"life", Dying}}, | |
114 » » » }} | |
115 » » } else if err != nil { | |
107 return nil, err | 116 return nil, err |
108 } | 117 } |
118 if isRemove { | |
119 removeCount++ | |
120 } | |
109 ops = append(ops, relOps...) | 121 ops = append(ops, relOps...) |
110 » » if isRemove { | 122 » } |
111 » » » removeCount += 1 | 123 » // If the service has no units, and all its known relations will be |
rog
2013/01/16 16:17:58
removeCount++
fwereade
2013/01/17 09:12:21
SGTM
| |
112 » » } | 124 » // removed, the service can also be removed. |
113 » } | |
114 » asserts := D{{"life", Alive}, {"txn-revno", s.doc.TxnRevno}} | |
115 if s.doc.UnitCount == 0 && s.doc.RelationCount == removeCount { | 125 if s.doc.UnitCount == 0 && s.doc.RelationCount == removeCount { |
116 » » return append(ops, s.removeOps(asserts)...), nil | 126 » » hasLastRefs := D{{"life", Alive}, {"unitcount", 0}, {"relationco unt", removeCount}} |
127 » » return append(ops, s.removeOps(hasLastRefs)...), nil | |
128 » } | |
129 » // If any units of the service exist, or if any known relation was not | |
130 » // removed (because it had units in scope, or because it was Dying, whic h | |
131 » // implies the same condition), service removal will be handled as a | |
132 » // consequence of the removal of the last unit or relation referencing i t. | |
133 » notLastRefs := D{ | |
134 » » {"life", Alive}, | |
135 » » {"$or", []D{ | |
136 » » » {{"unitcount", D{{"$gt", 0}}}}, | |
137 » » » {{"relationcount", s.doc.RelationCount}}, | |
138 » » }}, | |
117 } | 139 } |
118 update := D{{"$set", D{{"life", Dying}}}} | 140 update := D{{"$set", D{{"life", Dying}}}} |
119 if removeCount != 0 { | 141 if removeCount != 0 { |
120 decref := D{{"$inc", D{{"relationcount", -removeCount}}}} | 142 decref := D{{"$inc", D{{"relationcount", -removeCount}}}} |
121 update = append(update, decref...) | 143 update = append(update, decref...) |
122 } | 144 } |
123 return append(ops, txn.Op{ | 145 return append(ops, txn.Op{ |
124 C: s.st.services.Name, | 146 C: s.st.services.Name, |
125 Id: s.doc.Name, | 147 Id: s.doc.Name, |
126 » » Assert: asserts, | 148 » » Assert: notLastRefs, |
127 Update: update, | 149 Update: update, |
128 }), nil | 150 }), nil |
129 } | 151 } |
130 | 152 |
131 // removeOps returns the oprations required to remove the service. Supplied | 153 // removeOps returns the operations required to remove the service. Supplied |
132 // asserts will be applied to the service document. | 154 // asserts will be included in the operation on the service document. |
133 func (s *Service) removeOps(asserts D) []txn.Op { | 155 func (s *Service) removeOps(asserts D) []txn.Op { |
134 return []txn.Op{{ | 156 return []txn.Op{{ |
135 C: s.st.services.Name, | 157 C: s.st.services.Name, |
136 Id: s.doc.Name, | 158 Id: s.doc.Name, |
137 Assert: asserts, | 159 Assert: asserts, |
138 Remove: true, | 160 Remove: true, |
139 }, { | 161 }, { |
140 C: s.st.settings.Name, | 162 C: s.st.settings.Name, |
141 Id: s.globalKey(), | 163 Id: s.globalKey(), |
142 Remove: true, | 164 Remove: true, |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
257 // String returns the service name. | 279 // String returns the service name. |
258 func (s *Service) String() string { | 280 func (s *Service) String() string { |
259 return s.doc.Name | 281 return s.doc.Name |
260 } | 282 } |
261 | 283 |
262 // Refresh refreshes the contents of the Service from the underlying | 284 // Refresh refreshes the contents of the Service from the underlying |
263 // state. It returns a NotFoundError if the service has been removed. | 285 // state. It returns a NotFoundError if the service has been removed. |
264 func (s *Service) Refresh() error { | 286 func (s *Service) Refresh() error { |
265 err := s.st.services.FindId(s.doc.Name).One(&s.doc) | 287 err := s.st.services.FindId(s.doc.Name).One(&s.doc) |
266 if err == mgo.ErrNotFound { | 288 if err == mgo.ErrNotFound { |
267 » » return notFound("service %q", s) | 289 » » return notFoundf("service %q", s) |
268 } | 290 } |
269 if err != nil { | 291 if err != nil { |
270 return fmt.Errorf("cannot refresh service %q: %v", s, err) | 292 return fmt.Errorf("cannot refresh service %q: %v", s, err) |
271 } | 293 } |
272 return nil | 294 return nil |
273 } | 295 } |
274 | 296 |
275 // newUnitName returns the next unit name. | 297 // newUnitName returns the next unit name. |
276 func (s *Service) newUnitName() (string, error) { | 298 func (s *Service) newUnitName() (string, error) { |
277 change := mgo.Change{Update: D{{"$inc", D{{"unitseq", 1}}}}} | 299 change := mgo.Change{Update: D{{"$inc", D{{"unitseq", 1}}}}} |
278 result := serviceDoc{} | 300 result := serviceDoc{} |
279 if _, err := s.st.services.Find(D{{"_id", s.doc.Name}}).Apply(change, &r esult); err == mgo.ErrNotFound { | 301 if _, err := s.st.services.Find(D{{"_id", s.doc.Name}}).Apply(change, &r esult); err == mgo.ErrNotFound { |
280 » » return "", notFound("service %q", s) | 302 » » return "", notFoundf("service %q", s) |
281 } else if err != nil { | 303 } else if err != nil { |
282 return "", fmt.Errorf("cannot increment unit sequence: %v", err) | 304 return "", fmt.Errorf("cannot increment unit sequence: %v", err) |
283 } | 305 } |
284 name := s.doc.Name + "/" + strconv.Itoa(result.UnitSeq) | 306 name := s.doc.Name + "/" + strconv.Itoa(result.UnitSeq) |
285 return name, nil | 307 return name, nil |
286 } | 308 } |
287 | 309 |
288 // addUnitOps returns a unique name for a new unit, and a list of txn operations | 310 // addUnitOps returns a unique name for a new unit, and a list of txn operations |
289 // necessary to create that unit. The principalName param must be non-empty if | 311 // necessary to create that unit. The principalName param must be non-empty if |
290 // and only if s is a subordinate service. If s is a subordinate and strictSubor dinates | 312 // and only if s is a subordinate service. If s is a subordinate and strictSubor dinates |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
412 Update: D{{"$pull", D{{"principals", u.doc.Name}}}}, | 434 Update: D{{"$pull", D{{"principals", u.doc.Name}}}}, |
413 }) | 435 }) |
414 } | 436 } |
415 ops = append(ops, txn.Op{ | 437 ops = append(ops, txn.Op{ |
416 C: s.st.units.Name, | 438 C: s.st.units.Name, |
417 Id: u.doc.Name, | 439 Id: u.doc.Name, |
418 Assert: txn.DocExists, | 440 Assert: txn.DocExists, |
419 Remove: true, | 441 Remove: true, |
420 }) | 442 }) |
421 if s.doc.Life == Dying && s.doc.RelationCount == 0 && s.doc.UnitCount == 1 { | 443 if s.doc.Life == Dying && s.doc.RelationCount == 0 && s.doc.UnitCount == 1 { |
422 » » return append(ops, s.removeOps(nil)...) | 444 » » hasLastRef := D{{"life", Dying}, {"relationcount", 0}, {"unitcou nt", 1}} |
445 » » return append(ops, s.removeOps(hasLastRef)...) | |
423 } | 446 } |
424 svcOp := txn.Op{ | 447 svcOp := txn.Op{ |
425 C: s.st.services.Name, | 448 C: s.st.services.Name, |
426 Id: s.doc.Name, | 449 Id: s.doc.Name, |
427 Update: D{{"$inc", D{{"unitcount", -1}}}}, | 450 Update: D{{"$inc", D{{"unitcount", -1}}}}, |
428 } | 451 } |
429 if s.doc.Life == Alive { | 452 if s.doc.Life == Alive { |
430 svcOp.Assert = D{{"life", Alive}, {"unitcount", D{{"$gt", 0}}}} | 453 svcOp.Assert = D{{"life", Alive}, {"unitcount", D{{"$gt", 0}}}} |
431 } else { | 454 } else { |
432 svcOp.Assert = D{{"life", Dying}, {"unitcount", D{{"$gt", 1}}}} | 455 svcOp.Assert = D{{"life", Dying}, {"unitcount", D{{"$gt", 1}}}} |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
475 } | 498 } |
476 | 499 |
477 // Config returns the configuration node for the service. | 500 // Config returns the configuration node for the service. |
478 func (s *Service) Config() (config *Settings, err error) { | 501 func (s *Service) Config() (config *Settings, err error) { |
479 config, err = readSettings(s.st, s.globalKey()) | 502 config, err = readSettings(s.st, s.globalKey()) |
480 if err != nil { | 503 if err != nil { |
481 return nil, fmt.Errorf("cannot get configuration of service %q: %v", s, err) | 504 return nil, fmt.Errorf("cannot get configuration of service %q: %v", s, err) |
482 } | 505 } |
483 return config, nil | 506 return config, nil |
484 } | 507 } |
LEFT | RIGHT |