github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/unitstate.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 "strconv" 8 9 "github.com/juju/errors" 10 "github.com/juju/mgo/v3" 11 "github.com/juju/mgo/v3/bson" 12 "github.com/juju/mgo/v3/txn" 13 14 mgoutils "github.com/juju/juju/mongo/utils" 15 ) 16 17 // unitStateDoc records the state persisted by the charm executing in the unit. 18 type unitStateDoc struct { 19 // DocID is always the same as a unit's global key. 20 DocID string `bson:"_id"` 21 TxnRevno int64 `bson:"txn-revno"` 22 23 // The following maps to UnitState: 24 25 // State encodes the unit's persisted charm state as a list of key-value pairs. 26 CharmState map[string]string `bson:"charm-state,omitempty"` 27 28 // UniterState is a serialized yaml string containing the uniters internal 29 // state for this unit. 30 UniterState string `bson:"uniter-state,omitempty"` 31 32 // RelationState is a serialized yaml string containing relation internal 33 // state for this unit from the uniter. 34 RelationState map[string]string `bson:"relation-state,omitempty"` 35 36 // StorageState is a serialized yaml string containing storage internal 37 // state for this unit from the uniter. 38 StorageState string `bson:"storage-state,omitempty"` 39 40 // SecretState is a serialized yaml string containing secret internal 41 // state for this unit from the uniter. 42 SecretState string `bson:"secret-state,omitempty"` 43 44 // MeterStatusState is a serialized yaml string containing the internal 45 // state for this unit's meter status worker. 46 MeterStatusState string `bson:"meter-status-state,omitempty"` 47 } 48 49 // charmStateMatches returns true if the State map within the unitStateDoc matches 50 // the provided st argument. 51 func (d *unitStateDoc) charmStateMatches(st bson.M) bool { 52 if len(st) != len(d.CharmState) { 53 return false 54 } 55 56 for k, v := range d.CharmState { 57 if st[k] != v { 58 return false 59 } 60 } 61 62 return true 63 } 64 65 // relationData translate the unitStateDoc's RelationState as 66 // a map[string]string to a map[int]string, as is needed. 67 // BSON does not allow ints as a map key. 68 func (d *unitStateDoc) relationData() (map[int]string, error) { 69 if d.RelationState == nil { 70 return nil, nil 71 } 72 // BSON maps cannot have an int as key. 73 rState := make(map[int]string, len(d.RelationState)) 74 for k, v := range d.RelationState { 75 kString, err := strconv.Atoi(k) 76 if err != nil { 77 return nil, err 78 } 79 rState[kString] = v 80 } 81 return rState, nil 82 } 83 84 // relationStateMatches returns true if the RelationState map within the 85 // unitStateDoc contains all of the provided newRS map. Assumes that 86 // relationStateBSONFriendly has been called first. 87 func (d *unitStateDoc) relationStateMatches(newRS map[string]string) bool { 88 if len(d.RelationState) != len(newRS) { 89 return false 90 } 91 for k, v := range newRS { 92 if d.RelationState[k] != v { 93 return false 94 } 95 } 96 return true 97 } 98 99 // removeUnitStateOp returns the operation needed to remove the unit state 100 // document associated with the given globalKey. 101 func removeUnitStateOp(mb modelBackend, globalKey string) txn.Op { 102 return txn.Op{ 103 C: unitStatesC, 104 Id: mb.docID(globalKey), 105 Remove: true, 106 } 107 } 108 109 // UnitState contains the various state saved for this unit, 110 // including from the charm itself and the uniter. 111 type UnitState struct { 112 // charmState encodes the unit's persisted charm state as a list of 113 // key-value pairs. 114 charmState map[string]string 115 charmStateSet bool 116 117 // uniterState is a serialized yaml string containing the uniters internal 118 // state for this unit. 119 uniterState string 120 uniterStateSet bool 121 122 // relationState is a serialized yaml string containing relation internal 123 // state for this unit from the uniter. 124 relationState map[int]string 125 relationStateSet bool 126 127 // storageState is a serialized yaml string containing storage internal 128 // state for this unit from the uniter. 129 storageState string 130 storageStateSet bool 131 132 // secretState is a serialized yaml string containing secret internal 133 // state for this unit from the uniter. 134 secretState string 135 secretStateSet bool 136 137 // meterStatusState is a serialized yaml string containing the internal 138 // state for the meter status worker for this unit. 139 meterStatusState string 140 meterStatusStateSet bool 141 } 142 143 // NewUnitState returns a new UnitState struct. 144 func NewUnitState() *UnitState { 145 return &UnitState{} 146 } 147 148 // Modified returns true if any of the struct have been set. 149 func (u *UnitState) Modified() bool { 150 return u.relationStateSet || 151 u.storageStateSet || 152 u.secretStateSet || 153 u.charmStateSet || 154 u.uniterStateSet || 155 u.meterStatusStateSet 156 } 157 158 // SetCharmState sets the charm state value. 159 func (u *UnitState) SetCharmState(state map[string]string) { 160 u.charmStateSet = true 161 u.charmState = state 162 } 163 164 // CharmState returns the unit's stored charm state and bool indicating 165 // whether the data was set. 166 func (u *UnitState) CharmState() (map[string]string, bool) { 167 return u.charmState, u.charmStateSet 168 } 169 170 // SetUniterState sets the uniter state value. 171 func (u *UnitState) SetUniterState(state string) { 172 u.uniterStateSet = true 173 u.uniterState = state 174 } 175 176 // UniterState returns the uniter state and bool indicating 177 // whether the data was set. 178 func (u *UnitState) UniterState() (string, bool) { 179 return u.uniterState, u.uniterStateSet 180 } 181 182 // SetRelationState sets the relation state value. 183 func (u *UnitState) SetRelationState(state map[int]string) { 184 u.relationStateSet = true 185 u.relationState = state 186 } 187 188 // RelationState returns the relation state and bool indicating 189 // whether the data was set. 190 func (u *UnitState) RelationState() (map[int]string, bool) { 191 return u.relationState, u.relationStateSet 192 } 193 194 // relationStateBSONFriendly makes a map[int]string BSON friendly by 195 // translating the int map key to a string. 196 func (u *UnitState) relationStateBSONFriendly() (map[string]string, bool) { 197 stringData := make(map[string]string, len(u.relationState)) 198 for k, v := range u.relationState { 199 stringData[strconv.Itoa(k)] = v 200 } 201 return stringData, u.relationStateSet 202 } 203 204 // SetStorageState sets the storage state value. 205 func (u *UnitState) SetStorageState(state string) { 206 u.storageStateSet = true 207 u.storageState = state 208 } 209 210 // StorageState returns the storage state and bool indicating 211 // whether the data was set. 212 func (u *UnitState) StorageState() (string, bool) { 213 return u.storageState, u.storageStateSet 214 } 215 216 // SetSecretState sets the secret state value. 217 func (u *UnitState) SetSecretState(state string) { 218 u.secretStateSet = true 219 u.secretState = state 220 } 221 222 // SecretState returns the secret state and bool indicating 223 // whether the data was set. 224 func (u *UnitState) SecretState() (string, bool) { 225 return u.secretState, u.secretStateSet 226 } 227 228 // SetMeterStatusState sets the state value for meter state. 229 func (u *UnitState) SetMeterStatusState(state string) { 230 u.meterStatusStateSet = true 231 u.meterStatusState = state 232 } 233 234 // MeterStatusState returns the meter status state and a bool to indicate 235 // whether the data was set. 236 func (u *UnitState) MeterStatusState() (string, bool) { 237 return u.meterStatusState, u.meterStatusStateSet 238 } 239 240 // SetState replaces the currently stored state for a unit with the contents 241 // of the provided UnitState. 242 // 243 // Use this for testing, otherwise use SetStateOperation. 244 func (u *Unit) SetState(unitState *UnitState, limits UnitStateSizeLimits) error { 245 modelOp := u.SetStateOperation(unitState, limits) 246 return u.st.ApplyOperation(modelOp) 247 } 248 249 // SetStateOperation returns a ModelOperation for replacing the currently 250 // stored state for a unit with the contents of the provided UnitState. 251 func (u *Unit) SetStateOperation(unitState *UnitState, limits UnitStateSizeLimits) ModelOperation { 252 return &unitSetStateOperation{u: u, newState: unitState, limits: limits} 253 } 254 255 // State returns the persisted state for a unit. 256 func (u *Unit) State() (*UnitState, error) { 257 us := NewUnitState() 258 259 // Normally this would be if Life() != Alive. However the uniter 260 // needs to read its state during the Dying period for the case of 261 // hook failures just before dying. It's unclear whether this is 262 // an actual scenario or just part of the UniterSuite unit tests. 263 // See TestUniterDyingReaction. 264 if u.Life() == Dead { 265 return us, errors.NotFoundf("unit %s", u.Name()) 266 } 267 268 coll, closer := u.st.db().GetCollection(unitStatesC) 269 defer closer() 270 271 var stDoc unitStateDoc 272 if err := coll.FindId(u.globalKey()).One(&stDoc); err != nil { 273 if err == mgo.ErrNotFound { 274 return us, nil 275 } 276 return us, errors.Trace(err) 277 } 278 279 if stDoc.RelationState != nil { 280 rState, err := stDoc.relationData() 281 if err != nil { 282 return us, errors.Trace(err) 283 } 284 us.SetRelationState(rState) 285 } 286 287 if stDoc.CharmState != nil { 288 charmState := make(map[string]string, len(stDoc.CharmState)) 289 for k, v := range stDoc.CharmState { 290 charmState[mgoutils.UnescapeKey(k)] = v 291 } 292 us.SetCharmState(charmState) 293 } 294 295 us.SetUniterState(stDoc.UniterState) 296 us.SetStorageState(stDoc.StorageState) 297 us.SetSecretState(stDoc.SecretState) 298 us.SetMeterStatusState(stDoc.MeterStatusState) 299 300 return us, nil 301 } 302 303 // UnitStateSizeLimits defines the quota limits that are enforced when updating 304 // the state (charm and uniter) of a unit. 305 type UnitStateSizeLimits struct { 306 // The maximum allowed size for the charm state. It can be set to zero 307 // to bypass the charm state quota checks. 308 // quota checks will be 309 MaxCharmStateSize int 310 311 // The maximum allowed size for the uniter's state. It can be set to 312 // zero to bypass the uniter state quota checks. 313 MaxAgentStateSize int 314 }