Merge lp:~fwereade/juju-core/unit-remove-depart-scopes into lp:~go-bot/juju-core/trunk

Proposed by William Reade
Status: Merged
Approved by: John A Meinel
Approved revision: no longer in the source branch.
Merged at revision: 2042
Proposed branch: lp:~fwereade/juju-core/unit-remove-depart-scopes
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 120 lines (+80/-3)
3 files modified
state/service.go (+7/-3)
state/unit.go (+22/-0)
state/unit_test.go (+51/-0)
To merge this branch: bzr merge lp:~fwereade/juju-core/unit-remove-depart-scopes
Reviewer Review Type Date Requested Status
John A Meinel Approve
Ian Booth Approve
Review via email: mp+194389@code.launchpad.net

Commit message

state: unit removal departs relation scopes

Fixes lp:1233457

https://codereview.appspot.com/23080045/

Description of the change

state: unit removal departs relation scopes

Fixes lp:1233457

https://codereview.appspot.com/23080045/

To post a comment you must log in.
Revision history for this message
Ian Booth (wallyworld) wrote :

Very nice. I love the well commented test.

review: Approve
Revision history for this message
William Reade (fwereade) wrote :
Download full text (5.0 KiB)

Reviewers: mp+194389_code.launchpad.net,

Message:
Please take a look.

Description:
state: unit removal departs relation scopes

Fixes lp:1233457

https://code.launchpad.net/~fwereade/juju-core/unit-remove-depart-scopes/+merge/194389

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/23080045/

Affected files (+82, -3 lines):
   A [revision details]
   M state/service.go
   M state/unit.go
   M state/unit_test.go

Index: [revision details]
=== added file '[revision details]'
--- [revision details] 2012-01-01 00:00:00 +0000
+++ [revision details] 2012-01-01 00:00:00 +0000
@@ -0,0 +1,2 @@
+Old revision: tarmac-20131107094222-rb2jpeg465spb0qa
+New revision: <email address hidden>

Index: state/service.go
=== modified file 'state/service.go'
--- state/service.go 2013-07-30 14:00:53 +0000
+++ state/service.go 2013-11-07 17:30:14 +0000
@@ -718,14 +718,18 @@

  // Relations returns a Relation for every relation the service is in.
  func (s *Service) Relations() (relations []*Relation, err error) {
- defer utils.ErrorContextf(&err, "can't get relations for service %q", s)
+ return serviceRelations(s.st, s.doc.Name)
+}
+
+func serviceRelations(st *State, name string) (relations []*Relation, err
error) {
+ defer utils.ErrorContextf(&err, "can't get relations for service %q",
name)
   docs := []relationDoc{}
- err = s.st.relations.Find(D{{"endpoints.servicename",
s.doc.Name}}).All(&docs)
+ err = st.relations.Find(D{{"endpoints.servicename", name}}).All(&docs)
   if err != nil {
    return nil, err
   }
   for _, v := range docs {
- relations = append(relations, newRelation(s.st, &v))
+ relations = append(relations, newRelation(st, &v))
   }
   return relations, nil
  }

Index: state/unit.go
=== modified file 'state/unit.go'
--- state/unit.go 2013-11-07 06:44:41 +0000
+++ state/unit.go 2013-11-07 17:30:38 +0000
@@ -418,6 +418,28 @@
   if u.doc.Life != Dead {
    return stderrors.New("unit is not dead")
   }
+
+ // Now the unit is Dead, we can be sure that it's impossible for it to
+ // enter relation scopes (once it's Dying, we can be sure of this; but
+ // EnsureDead does not require that it already be Dying, so this is the
+ // only point at which we can safely backstop lp:1233457 and mitigate
+ // the impact of unit agent bugs that leave relation scopes occupied).
+ relations, err := serviceRelations(u.st, u.doc.Service)
+ if err != nil {
+ return err
+ }
+ for _, rel := range relations {
+ ru, err := rel.Unit(u)
+ if err != nil {
+ return err
+ }
+ if err := ru.LeaveScope(); err != nil {
+ return err
+ }
+ }
+
+ // Now we're sure we haven't left any scopes occupied by this unit, we
+ // can safely remove the document.
   unit := &Unit{st: u.st, doc: u.doc}
   for i := 0; i < 5; i++ {
    switch ops, err := unit.removeOps(isDeadDoc); err {

Index: state/unit_test.go
=== modified file 'state/unit_test.go'
--- state/unit_test.go 2013-11-05 07:19:16 +0000
+++ state/unit_test.go 2013-11-07 17:30:38 +0000
@@ -1127,6 +1127,57 @@
   c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
  }

+func (s *UnitSuite) TestRemovePathologicalWithBuggyUniter(c *gc.C) {...

Read more...

Revision history for this message
John A Meinel (jameinel) wrote :

Approving this for you so that it will hopefully make the follow up patch easier to review once this has landed.

review: Approve
Revision history for this message
John A Meinel (jameinel) wrote :

FWIW, this patch is small enough that it seems like it might be something that could be backported to 1.16. I don't know if it is worthwhile right now, but maybe something to consider.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'state/service.go'
--- state/service.go 2013-07-30 14:00:53 +0000
+++ state/service.go 2013-11-07 17:37:25 +0000
@@ -718,14 +718,18 @@
718718
719// Relations returns a Relation for every relation the service is in.719// Relations returns a Relation for every relation the service is in.
720func (s *Service) Relations() (relations []*Relation, err error) {720func (s *Service) Relations() (relations []*Relation, err error) {
721 defer utils.ErrorContextf(&err, "can't get relations for service %q", s)721 return serviceRelations(s.st, s.doc.Name)
722}
723
724func serviceRelations(st *State, name string) (relations []*Relation, err error) {
725 defer utils.ErrorContextf(&err, "can't get relations for service %q", name)
722 docs := []relationDoc{}726 docs := []relationDoc{}
723 err = s.st.relations.Find(D{{"endpoints.servicename", s.doc.Name}}).All(&docs)727 err = st.relations.Find(D{{"endpoints.servicename", name}}).All(&docs)
724 if err != nil {728 if err != nil {
725 return nil, err729 return nil, err
726 }730 }
727 for _, v := range docs {731 for _, v := range docs {
728 relations = append(relations, newRelation(s.st, &v))732 relations = append(relations, newRelation(st, &v))
729 }733 }
730 return relations, nil734 return relations, nil
731}735}
732736
=== modified file 'state/unit.go'
--- state/unit.go 2013-11-07 06:44:41 +0000
+++ state/unit.go 2013-11-07 17:37:25 +0000
@@ -418,6 +418,28 @@
418 if u.doc.Life != Dead {418 if u.doc.Life != Dead {
419 return stderrors.New("unit is not dead")419 return stderrors.New("unit is not dead")
420 }420 }
421
422 // Now the unit is Dead, we can be sure that it's impossible for it to
423 // enter relation scopes (once it's Dying, we can be sure of this; but
424 // EnsureDead does not require that it already be Dying, so this is the
425 // only point at which we can safely backstop lp:1233457 and mitigate
426 // the impact of unit agent bugs that leave relation scopes occupied).
427 relations, err := serviceRelations(u.st, u.doc.Service)
428 if err != nil {
429 return err
430 }
431 for _, rel := range relations {
432 ru, err := rel.Unit(u)
433 if err != nil {
434 return err
435 }
436 if err := ru.LeaveScope(); err != nil {
437 return err
438 }
439 }
440
441 // Now we're sure we haven't left any scopes occupied by this unit, we
442 // can safely remove the document.
421 unit := &Unit{st: u.st, doc: u.doc}443 unit := &Unit{st: u.st, doc: u.doc}
422 for i := 0; i < 5; i++ {444 for i := 0; i < 5; i++ {
423 switch ops, err := unit.removeOps(isDeadDoc); err {445 switch ops, err := unit.removeOps(isDeadDoc); err {
424446
=== modified file 'state/unit_test.go'
--- state/unit_test.go 2013-11-05 07:19:16 +0000
+++ state/unit_test.go 2013-11-07 17:37:25 +0000
@@ -1127,6 +1127,57 @@
1127 c.Assert(err, jc.Satisfies, errors.IsNotFoundError)1127 c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
1128}1128}
11291129
1130func (s *UnitSuite) TestRemovePathologicalWithBuggyUniter(c *gc.C) {
1131 // Add a relation between wordpress and mysql...
1132 wordpress := s.service
1133 wordpress0 := s.unit
1134 mysql, err := s.State.AddService("mysql", s.AddTestingCharm(c, "mysql"))
1135 c.Assert(err, gc.IsNil)
1136 eps, err := s.State.InferEndpoints([]string{"wordpress", "mysql"})
1137 c.Assert(err, gc.IsNil)
1138 rel, err := s.State.AddRelation(eps...)
1139 c.Assert(err, gc.IsNil)
1140
1141 // The relation holds a reference to wordpress, but that can't keep
1142 // wordpress from being removed -- because the relation will be removed
1143 // if we destroy wordpress.
1144 // However, if a unit of the *other* service joins the relation, that
1145 // will add an additional reference and prevent the relation -- and
1146 // thus wordpress itself -- from being removed when its last unit is.
1147 mysql0, err := mysql.AddUnit()
1148 c.Assert(err, gc.IsNil)
1149 mysql0ru, err := rel.Unit(mysql0)
1150 c.Assert(err, gc.IsNil)
1151 err = mysql0ru.EnterScope(nil)
1152 c.Assert(err, gc.IsNil)
1153
1154 // Destroy wordpress, and remove its last unit.
1155 err = wordpress.Destroy()
1156 c.Assert(err, gc.IsNil)
1157 err = wordpress0.EnsureDead()
1158 c.Assert(err, gc.IsNil)
1159 err = wordpress0.Remove()
1160 c.Assert(err, gc.IsNil)
1161
1162 // Check this didn't kill the service or relation yet...
1163 err = wordpress.Refresh()
1164 c.Assert(err, gc.IsNil)
1165 err = rel.Refresh()
1166 c.Assert(err, gc.IsNil)
1167
1168 // ...and that when the malfunctioning unit agent on the other side
1169 // sets itself to dead *without* departing the relation, the unit's
1170 // removal causes the relation and the other service to be cleaned up.
1171 err = mysql0.EnsureDead()
1172 c.Assert(err, gc.IsNil)
1173 err = mysql0.Remove()
1174 c.Assert(err, gc.IsNil)
1175 err = wordpress.Refresh()
1176 c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
1177 err = rel.Refresh()
1178 c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
1179}
1180
1130func (s *UnitSuite) TestWatchSubordinates(c *gc.C) {1181func (s *UnitSuite) TestWatchSubordinates(c *gc.C) {
1131 w := s.unit.WatchSubordinateUnits()1182 w := s.unit.WatchSubordinateUnits()
1132 defer testing.AssertStop(c, w)1183 defer testing.AssertStop(c, w)

Subscribers

People subscribed via source and target branches

to status/vote changes: