github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/application_ops.go (about) 1 // Copyright 2020 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "github.com/juju/mgo/v3/bson" 8 "github.com/juju/mgo/v3/txn" 9 jujutxn "github.com/juju/txn/v3" 10 11 "github.com/juju/juju/core/leadership" 12 mgoutils "github.com/juju/juju/mongo/utils" 13 ) 14 15 type updateLeaderSettingsOperation struct { 16 db Database 17 18 sets bson.M 19 unsets bson.M 20 21 key string 22 updateDoc bson.D 23 24 tokenAwareTxnBuilder func(int) ([]txn.Op, error) 25 } 26 27 // newApplicationUpdateLeaderSettingsOperation returns a ModelOperation for 28 // updating the leader settings for a particular application. 29 func newUpdateLeaderSettingsOperation(db Database, token leadership.Token, key string, updates map[string]interface{}) ModelOperation { 30 // We can calculate the actual update ahead of time; it's not dependent 31 // upon the current state of the document. (*Writing* it should depend 32 // on document state, but that's handled below.) 33 sets := bson.M{} 34 unsets := bson.M{} 35 for unescapedKey, value := range updates { 36 key := mgoutils.EscapeKey(unescapedKey) 37 if value == "" { 38 unsets[key] = 1 39 } else { 40 sets[key] = value 41 } 42 } 43 updateDoc := setUnsetUpdateSettings(sets, unsets) 44 45 op := &updateLeaderSettingsOperation{ 46 db: db, 47 sets: sets, 48 unsets: unsets, 49 key: key, 50 updateDoc: updateDoc, 51 } 52 53 op.tokenAwareTxnBuilder = buildTxnWithLeadership(op.buildTxn, token) 54 return op 55 } 56 57 // Build implements ModelOperation. 58 func (op *updateLeaderSettingsOperation) Build(attempt int) ([]txn.Op, error) { 59 return op.tokenAwareTxnBuilder(attempt) 60 } 61 62 func (op *updateLeaderSettingsOperation) buildTxn(_ int) ([]txn.Op, error) { 63 // Read the current document state so we can abort if there's 64 // no actual change; and the version number so we can assert 65 // on it and prevent these settings from landing late. 66 doc, err := readSettingsDoc(op.db, settingsC, op.key) 67 if err != nil { 68 return nil, err 69 } 70 if op.isNullChange(doc.Settings) { 71 return nil, jujutxn.ErrNoOperations 72 } 73 return []txn.Op{{ 74 C: settingsC, 75 Id: op.key, 76 Assert: bson.D{{"version", doc.Version}}, 77 Update: op.updateDoc, 78 }}, nil 79 } 80 81 func (op *updateLeaderSettingsOperation) isNullChange(rawMap map[string]interface{}) bool { 82 for key := range op.unsets { 83 if _, found := rawMap[key]; found { 84 return false 85 } 86 } 87 for key, value := range op.sets { 88 if current := rawMap[key]; current != value { 89 return false 90 } 91 } 92 return true 93 } 94 95 // Done implements ModelOperation. 96 func (op *updateLeaderSettingsOperation) Done(err error) error { return err }