github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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 "github.com/juju/mgo/v3" 9 "github.com/juju/mgo/v3/bson" 10 "github.com/juju/mgo/v3/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 // aliveOp returns errNotAlive if the identified entity is not Alive; or 42 // a txn.Op that will fail if the condition no longer holds. 43 func (nsLife_) aliveOp(entities mongo.Collection, docID string) (txn.Op, error) { 44 op, err := nsLife.checkOp(entities, docID, nsLife.alive()) 45 switch errors.Cause(err) { 46 case nil: 47 case errCheckFailed: 48 return txn.Op{}, notAliveErr 49 default: 50 return txn.Op{}, errors.Trace(err) 51 } 52 return op, nil 53 } 54 55 // notDeadOp returns errDeadOrGone if the identified entity is not Alive 56 // or Dying, or a txn.Op that will fail if the condition no longer 57 // holds. 58 func (nsLife_) notDeadOp(entities mongo.Collection, docID string) (txn.Op, error) { 59 op, err := nsLife.checkOp(entities, docID, nsLife.notDead()) 60 switch errors.Cause(err) { 61 case nil: 62 case errCheckFailed: 63 return txn.Op{}, errDeadOrGone 64 default: 65 return txn.Op{}, errors.Trace(err) 66 } 67 return op, nil 68 } 69 70 var errCheckFailed = errors.New("check failed") 71 72 func (nsLife_) checkOp(entities mongo.Collection, docID string, check bson.D) (txn.Op, error) { 73 sel := append(bson.D{{"_id", docID}}, check...) 74 count, err := entities.Find(sel).Count() 75 if err != nil { 76 return txn.Op{}, errors.Trace(err) 77 } else if count == 0 { 78 return txn.Op{}, errCheckFailed 79 } 80 return txn.Op{ 81 C: entities.Name(), 82 Id: docID, 83 Assert: check, 84 }, nil 85 } 86 87 func (nsLife_) read(entities mongo.Collection, docID string) (Life, error) { 88 var doc struct { 89 Life Life `bson:"life"` 90 } 91 err := entities.FindId(docID).One(&doc) 92 switch errors.Cause(err) { 93 case nil: 94 case mgo.ErrNotFound: 95 return Dead, errors.NotFoundf("entity") 96 default: 97 return Dead, errors.Trace(err) 98 } 99 return doc.Life, nil 100 } 101 102 // alive returns a selector that matches only documents whose life 103 // field is set to Alive. 104 func (nsLife_) alive() bson.D { 105 return bson.D{{"life", Alive}} 106 } 107 108 // notDead returns a selector that matches only documents whose life 109 // field is not set to Dead. 110 func (nsLife_) notDead() bson.D { 111 return bson.D{{"life", bson.D{{"$ne", Dead}}}} 112 }