Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(478)

Delta Between Two Patch Sets: state/service.go

Issue 7095059: state: service API changes
Left Patch Set: state: service API changes Created 12 years, 2 months ago
Right Patch Set: state: service API changes Created 12 years, 2 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « state/relation.go ('k') | state/service_test.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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
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
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
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
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 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b