github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/life_ns.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "github.com/juju/errors" 8 "gopkg.in/mgo.v2" 9 "gopkg.in/mgo.v2/bson" 10 "gopkg.in/mgo.v2/txn" 11 12 "github.com/juju/juju/mongo" 13 ) 14 15 // nsLife_ backs nsLife. 16 type nsLife_ struct{} 17 18 // nsLife namespaces low-level entity-life functionality. See the 19 // discussion in nsPayloads: this exists not to be the one place for 20 // life functionality (that would be a huge change), but to at least 21 // represent the parts we need for payloads in a consistent fashion. 22 // 23 // Both the namespacing and the explicit Collection->op approach seem 24 // to be good ideas, and should ideally be extended as we continue. 25 var nsLife = nsLife_{} 26 27 // destroyOp returns errNotAlive if the identified entity is not Alive; 28 // or a txn.Op that will fail if the condition no longer holds, and 29 // otherwise set Life to Dying and make any other updates supplied in 30 // update. 31 func (nsLife_) destroyOp(entities mongo.Collection, docID string, update bson.D) (txn.Op, error) { 32 op, err := nsLife.aliveOp(entities, docID) 33 if err != nil { 34 return txn.Op{}, errors.Trace(err) 35 } 36 setDying := bson.D{{"$set", bson.D{{"life", Dying}}}} 37 op.Update = append(setDying, update...) 38 return op, nil 39 } 40 41 // dieOp returns errNotDying if the identified entity is Alive, or 42 // errAlreadyDead if the entity is Dead or gone; or a txn.Op that will 43 // fail if the condition no longer holds, and otherwise set Life to 44 // Dead, and make any other updates supplied in update. 45 func (nsLife_) dieOp(entities mongo.Collection, docID string, update bson.D) (txn.Op, error) { 46 life, err := nsLife.read(entities, docID) 47 if errors.IsNotFound(err) { 48 return txn.Op{}, errAlreadyDead 49 } else if err != nil { 50 return txn.Op{}, errors.Trace(err) 51 } 52 switch life { 53 case Alive: 54 return txn.Op{}, errNotDying 55 case Dead: 56 return txn.Op{}, errAlreadyDead 57 } 58 setDead := bson.D{{"$set", bson.D{{"life", Dead}}}} 59 return txn.Op{ 60 C: entities.Name(), 61 Id: docID, 62 Assert: nsLife.dying(), 63 Update: append(setDead, update...), 64 }, nil 65 } 66 67 // aliveOp returns errNotAlive if the identified entity is not Alive; or 68 // a txn.Op that will fail if the condition no longer holds. 69 func (nsLife_) aliveOp(entities mongo.Collection, docID string) (txn.Op, error) { 70 op, err := nsLife.checkOp(entities, docID, nsLife.alive()) 71 switch errors.Cause(err) { 72 case nil: 73 case errCheckFailed: 74 return txn.Op{}, errNotAlive 75 default: 76 return txn.Op{}, errors.Trace(err) 77 } 78 return op, nil 79 } 80 81 // dyingOp returns errNotDying if the identified entity is not Dying; or 82 // a txn.Op that will fail if the condition no longer holds. 83 func (nsLife_) dyingOp(entities mongo.Collection, docID string) (txn.Op, error) { 84 op, err := nsLife.checkOp(entities, docID, nsLife.dying()) 85 switch errors.Cause(err) { 86 case nil: 87 case errCheckFailed: 88 return txn.Op{}, errNotDying 89 default: 90 return txn.Op{}, errors.Trace(err) 91 } 92 return op, nil 93 } 94 95 // notDeadOp returns errDeadOrGone if the identified entity is not Alive 96 // or Dying, or a txn.Op that will fail if the condition no longer 97 // holds. 98 func (nsLife_) notDeadOp(entities mongo.Collection, docID string) (txn.Op, error) { 99 op, err := nsLife.checkOp(entities, docID, nsLife.notDead()) 100 switch errors.Cause(err) { 101 case nil: 102 case errCheckFailed: 103 return txn.Op{}, errDeadOrGone 104 default: 105 return txn.Op{}, errors.Trace(err) 106 } 107 return op, nil 108 } 109 110 var errCheckFailed = errors.New("check failed") 111 112 func (nsLife_) checkOp(entities mongo.Collection, docID string, check bson.D) (txn.Op, error) { 113 sel := append(bson.D{{"_id", docID}}, check...) 114 count, err := entities.Find(sel).Count() 115 if err != nil { 116 return txn.Op{}, errors.Trace(err) 117 } else if count == 0 { 118 return txn.Op{}, errCheckFailed 119 } 120 return txn.Op{ 121 C: entities.Name(), 122 Id: docID, 123 Assert: check, 124 }, nil 125 } 126 127 func (nsLife_) read(entities mongo.Collection, docID string) (Life, error) { 128 var doc struct { 129 Life Life `bson:"life"` 130 } 131 err := entities.FindId(docID).One(&doc) 132 switch errors.Cause(err) { 133 case nil: 134 case mgo.ErrNotFound: 135 return Dead, errors.NotFoundf("entity") 136 default: 137 return Dead, errors.Trace(err) 138 } 139 return doc.Life, nil 140 } 141 142 // alive returns a selector that matches only documents whose life 143 // field is set to Alive. 144 func (nsLife_) alive() bson.D { 145 return bson.D{{"life", Alive}} 146 } 147 148 // dying returns a selector that matches only documents whose life 149 // field is set to Dying. 150 func (nsLife_) dying() bson.D { 151 return bson.D{{"life", Dying}} 152 } 153 154 // notDead returns a selector that matches only documents whose life 155 // field is not set to Dead. 156 func (nsLife_) notDead() bson.D { 157 return bson.D{{"life", bson.D{{"$ne", Dead}}}} 158 }