github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/state/settings.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "fmt" 8 "sort" 9 "strings" 10 11 "github.com/juju/errors" 12 "gopkg.in/mgo.v2" 13 "gopkg.in/mgo.v2/bson" 14 "gopkg.in/mgo.v2/txn" 15 ) 16 17 // See: http://docs.mongodb.org/manual/faq/developers/#faq-dollar-sign-escaping 18 // for why we're using those replacements. 19 const ( 20 fullWidthDot = "\uff0e" 21 fullWidthDollar = "\uff04" 22 ) 23 24 var ( 25 escapeReplacer = strings.NewReplacer(".", fullWidthDot, "$", fullWidthDollar) 26 unescapeReplacer = strings.NewReplacer(fullWidthDot, ".", fullWidthDollar, "$") 27 ) 28 29 const ( 30 ItemAdded = iota 31 ItemModified 32 ItemDeleted 33 ) 34 35 // settingsDoc is the mongo document representation for 36 // a settings. 37 type settingsDoc struct { 38 DocID string `bson:"_id"` 39 ModelUUID string `bson:"model-uuid"` 40 41 // Settings contains the settings. This must not be 42 // omitempty, or migration cannot work correctly. 43 Settings settingsMap `bson:"settings"` 44 45 // Version is a version number for the settings, 46 // and is increased every time the settings change. 47 Version int64 `bson:"version"` 48 } 49 50 type settingsMap map[string]interface{} 51 52 func (m *settingsMap) SetBSON(raw bson.Raw) error { 53 rawMap := make(map[string]interface{}) 54 if err := raw.Unmarshal(rawMap); err != nil { 55 return err 56 } 57 replaceKeys(rawMap, unescapeReplacer.Replace) 58 *m = settingsMap(rawMap) 59 return nil 60 } 61 62 // ItemChange represents the change of an item in a settings. 63 type ItemChange struct { 64 Type int 65 Key string 66 OldValue interface{} 67 NewValue interface{} 68 } 69 70 // String returns the item change in a readable format. 71 func (ic *ItemChange) String() string { 72 switch ic.Type { 73 case ItemAdded: 74 return fmt.Sprintf("setting added: %v = %v", ic.Key, ic.NewValue) 75 case ItemModified: 76 return fmt.Sprintf("setting modified: %v = %v (was %v)", 77 ic.Key, ic.NewValue, ic.OldValue) 78 case ItemDeleted: 79 return fmt.Sprintf("setting deleted: %v (was %v)", ic.Key, ic.OldValue) 80 } 81 return fmt.Sprintf("unknown setting change type %d: %v = %v (was %v)", 82 ic.Type, ic.Key, ic.NewValue, ic.OldValue) 83 } 84 85 // itemChangeSlice contains a slice of item changes in a config node. 86 // It implements the sort interface to sort the items changes by key. 87 type itemChangeSlice []ItemChange 88 89 func (ics itemChangeSlice) Len() int { return len(ics) } 90 func (ics itemChangeSlice) Less(i, j int) bool { return ics[i].Key < ics[j].Key } 91 func (ics itemChangeSlice) Swap(i, j int) { ics[i], ics[j] = ics[j], ics[i] } 92 93 // A Settings manages changes to settings as a delta in memory and merges 94 // them back in the database when explicitly requested. 95 type Settings struct { 96 st *State 97 collection string 98 key string 99 100 // disk holds the values in the config node before 101 // any keys have been changed. It is reset on Read and Write 102 // operations. 103 disk map[string]interface{} 104 105 // cache holds the current values in the config node. 106 // The difference between disk and core 107 // determines the delta to be applied when Settings.Write 108 // is called. 109 core map[string]interface{} 110 111 // version is the version corresponding to "disk"; i.e. 112 // the value of the version field in the status document 113 // when it was read. 114 version int64 115 } 116 117 // Keys returns the current keys in alphabetical order. 118 func (s *Settings) Keys() []string { 119 keys := []string{} 120 for key := range s.core { 121 keys = append(keys, key) 122 } 123 sort.Strings(keys) 124 return keys 125 } 126 127 // Get returns the value of key and whether it was found. 128 func (s *Settings) Get(key string) (value interface{}, found bool) { 129 value, found = s.core[key] 130 return 131 } 132 133 // Map returns all keys and values of the node. 134 func (s *Settings) Map() map[string]interface{} { 135 return copyMap(s.core, nil) 136 } 137 138 // Set sets key to value 139 func (s *Settings) Set(key string, value interface{}) { 140 s.core[key] = value 141 } 142 143 // Update sets multiple key/value pairs. 144 func (s *Settings) Update(kv map[string]interface{}) { 145 for key, value := range kv { 146 s.core[key] = value 147 } 148 } 149 150 // Delete removes key. 151 func (s *Settings) Delete(key string) { 152 delete(s.core, key) 153 } 154 155 // cacheKeys returns the keys of all caches as a key=>true map. 156 func cacheKeys(caches ...map[string]interface{}) map[string]bool { 157 keys := make(map[string]bool) 158 for _, cache := range caches { 159 for key := range cache { 160 keys[key] = true 161 } 162 } 163 return keys 164 } 165 166 // settingsUpdateOps returns the item changes and txn ops necessary 167 // to write the changes made to c back onto its node. 168 func (s *Settings) settingsUpdateOps() ([]ItemChange, []txn.Op) { 169 changes := []ItemChange{} 170 updates := bson.M{} 171 deletions := bson.M{} 172 for key := range cacheKeys(s.disk, s.core) { 173 old, ondisk := s.disk[key] 174 new, incore := s.core[key] 175 if new == old { 176 continue 177 } 178 var change ItemChange 179 escapedKey := escapeReplacer.Replace(key) 180 switch { 181 case incore && ondisk: 182 change = ItemChange{ItemModified, key, old, new} 183 updates[escapedKey] = new 184 case incore && !ondisk: 185 change = ItemChange{ItemAdded, key, nil, new} 186 updates[escapedKey] = new 187 case ondisk && !incore: 188 change = ItemChange{ItemDeleted, key, old, nil} 189 deletions[escapedKey] = 1 190 default: 191 panic("unreachable") 192 } 193 changes = append(changes, change) 194 } 195 if len(changes) == 0 { 196 return []ItemChange{}, nil 197 } 198 sort.Sort(itemChangeSlice(changes)) 199 ops := []txn.Op{{ 200 C: s.collection, 201 Id: s.key, 202 Assert: txn.DocExists, 203 Update: setUnsetUpdateSettings(updates, deletions), 204 }} 205 return changes, ops 206 } 207 208 func (s *Settings) write(ops []txn.Op) error { 209 err := s.st.runTransaction(ops) 210 if err == txn.ErrAborted { 211 return errors.NotFoundf("settings") 212 } 213 if err != nil { 214 return fmt.Errorf("cannot write settings: %v", err) 215 } 216 s.disk = copyMap(s.core, nil) 217 return nil 218 } 219 220 // Write writes changes made to c back onto its node. Changes are written 221 // as a delta applied on top of the latest version of the node, to prevent 222 // overwriting unrelated changes made to the node since it was last read. 223 func (s *Settings) Write() ([]ItemChange, error) { 224 changes, ops := s.settingsUpdateOps() 225 err := s.write(ops) 226 if err != nil { 227 return nil, err 228 } 229 return changes, nil 230 } 231 232 func newSettings(st *State, collection, key string) *Settings { 233 return &Settings{ 234 st: st, 235 collection: collection, 236 key: key, 237 core: make(map[string]interface{}), 238 } 239 } 240 241 // replaceKeys will modify the provided map in place by replacing keys with 242 // their replacement if they have been modified. 243 func replaceKeys(m map[string]interface{}, replace func(string) string) { 244 for key, value := range m { 245 if newKey := replace(key); newKey != key { 246 delete(m, key) 247 m[newKey] = value 248 } 249 } 250 return 251 } 252 253 // copyMap copies the keys and values of one map into a new one. If replace 254 // is non-nil, for each old key k, the new key will be replace(k). 255 func copyMap(in map[string]interface{}, replace func(string) string) (out map[string]interface{}) { 256 out = make(map[string]interface{}) 257 for key, value := range in { 258 if replace != nil { 259 key = replace(key) 260 } 261 out[key] = value 262 } 263 return 264 } 265 266 // Read (re)reads the node data into c. 267 func (s *Settings) Read() error { 268 doc, err := readSettingsDoc(s.st, s.collection, s.key) 269 if errors.IsNotFound(err) { 270 s.disk = nil 271 s.core = make(map[string]interface{}) 272 return err 273 } 274 if err != nil { 275 return errors.Annotate(err, "cannot read settings") 276 } 277 s.version = doc.Version 278 s.disk = doc.Settings 279 s.core = copyMap(s.disk, nil) 280 return nil 281 } 282 283 // readSettingsDoc reads the settings doc with the given key. 284 func readSettingsDoc(st modelBackend, collection, key string) (*settingsDoc, error) { 285 var doc settingsDoc 286 if err := readSettingsDocInto(st, collection, key, &doc); err != nil { 287 return nil, errors.Trace(err) 288 } 289 return &doc, nil 290 } 291 292 // readSettingsDocInto reads the settings doc with the given key 293 // into the provided output structure. 294 func readSettingsDocInto(st modelBackend, collection, key string, out interface{}) error { 295 settings, closer := st.getCollection(collection) 296 defer closer() 297 298 err := settings.FindId(key).One(out) 299 if err == mgo.ErrNotFound { 300 err = errors.NotFoundf("settings") 301 } 302 return err 303 } 304 305 // ReadSettings returns the settings for the given key. 306 func (st *State) ReadSettings(collection, key string) (*Settings, error) { 307 return readSettings(st, collection, key) 308 } 309 310 // readSettings returns the Settings for key. 311 func readSettings(st *State, collection, key string) (*Settings, error) { 312 s := newSettings(st, collection, key) 313 if err := s.Read(); err != nil { 314 return nil, err 315 } 316 return s, nil 317 } 318 319 var errSettingsExist = errors.New("cannot overwrite existing settings") 320 321 func createSettingsOp(collection, key string, values map[string]interface{}) txn.Op { 322 newValues := copyMap(values, escapeReplacer.Replace) 323 return txn.Op{ 324 C: collection, 325 Id: key, 326 Assert: txn.DocMissing, 327 Insert: &settingsDoc{ 328 Settings: newValues, 329 }, 330 } 331 } 332 333 // createSettings writes an initial config node. 334 func createSettings(st *State, collection, key string, values map[string]interface{}) (*Settings, error) { 335 s := newSettings(st, collection, key) 336 s.core = copyMap(values, nil) 337 ops := []txn.Op{createSettingsOp(collection, key, values)} 338 err := s.st.runTransaction(ops) 339 if err == txn.ErrAborted { 340 return nil, errSettingsExist 341 } 342 if err != nil { 343 return nil, fmt.Errorf("cannot create settings: %v", err) 344 } 345 return s, nil 346 } 347 348 // removeSettings removes the Settings for key. 349 func removeSettings(st *State, collection, key string) error { 350 err := st.runTransaction([]txn.Op{removeSettingsOp(collection, key)}) 351 if err == txn.ErrAborted { 352 return errors.NotFoundf("settings") 353 } else if err != nil { 354 return errors.Trace(err) 355 } 356 return nil 357 } 358 359 func removeSettingsOp(collection, key string) txn.Op { 360 return txn.Op{ 361 C: collection, 362 Id: key, 363 Assert: txn.DocExists, 364 Remove: true, 365 } 366 } 367 368 // listSettings returns all the settings with the specified key prefix. 369 func listSettings(st *State, collection, keyPrefix string) (map[string]map[string]interface{}, error) { 370 settings, closer := st.getRawCollection(collection) 371 defer closer() 372 373 var matchingSettings []settingsDoc 374 findExpr := fmt.Sprintf("^%s.*$", st.docID(keyPrefix)) 375 if err := settings.Find(bson.D{{"_id", bson.D{{"$regex", findExpr}}}}).All(&matchingSettings); err != nil { 376 return nil, err 377 } 378 result := make(map[string]map[string]interface{}) 379 for i := range matchingSettings { 380 result[st.localID(matchingSettings[i].DocID)] = matchingSettings[i].Settings 381 } 382 return result, nil 383 } 384 385 // replaceSettingsOp returns a txn.Op that deletes the document's contents and 386 // replaces it with the supplied values, and a function that should be called on 387 // txn failure to determine whether this operation failed (due to a concurrent 388 // settings change). 389 func replaceSettingsOp(st *State, collection, key string, values map[string]interface{}) (txn.Op, func() (bool, error), error) { 390 s, err := readSettings(st, collection, key) 391 if err != nil { 392 return txn.Op{}, nil, err 393 } 394 deletes := bson.M{} 395 for k := range s.disk { 396 if _, found := values[k]; !found { 397 deletes[escapeReplacer.Replace(k)] = 1 398 } 399 } 400 newValues := copyMap(values, escapeReplacer.Replace) 401 op := s.assertUnchangedOp() 402 op.Update = setUnsetUpdateSettings(bson.M(newValues), deletes) 403 assertFailed := func() (bool, error) { 404 latest, err := readSettings(st, collection, key) 405 if err != nil { 406 return false, err 407 } 408 return latest.version != s.version, nil 409 } 410 return op, assertFailed, nil 411 } 412 413 func (s *Settings) assertUnchangedOp() txn.Op { 414 return txn.Op{ 415 C: s.collection, 416 Id: s.key, 417 Assert: bson.D{{"version", s.version}}, 418 } 419 } 420 421 func inSubdocReplacer(subdoc string) func(string) string { 422 return func(key string) string { 423 return subdoc + "." + key 424 } 425 } 426 427 func inSubdocEscapeReplacer(subdoc string) func(string) string { 428 return func(key string) string { 429 return subdoc + "." + escapeReplacer.Replace(key) 430 } 431 } 432 433 // setUnsetUpdateSettings returns a bson.D for use 434 // in a s.collection txn.Op's Update field, containing 435 // $set and $unset operators if the corresponding 436 // operands are non-empty. 437 func setUnsetUpdateSettings(set, unset bson.M) bson.D { 438 var update bson.D 439 replace := inSubdocReplacer("settings") 440 if len(set) > 0 { 441 set = bson.M(copyMap(map[string]interface{}(set), replace)) 442 update = append(update, bson.DocElem{"$set", set}) 443 } 444 if len(unset) > 0 { 445 unset = bson.M(copyMap(map[string]interface{}(unset), replace)) 446 update = append(update, bson.DocElem{"$unset", unset}) 447 } 448 if len(update) > 0 { 449 update = append(update, bson.DocElem{"$inc", bson.D{{"version", 1}}}) 450 } 451 return update 452 } 453 454 // StateSettings is used to expose various settings APIs outside of the state package. 455 type StateSettings struct { 456 st *State 457 collection string 458 } 459 460 // NewStateSettings creates a StateSettings from state. 461 func NewStateSettings(st *State) *StateSettings { 462 return &StateSettings{st, settingsC} 463 } 464 465 // CreateSettings exposes createSettings on state for use outside the state package. 466 func (s *StateSettings) CreateSettings(key string, settings map[string]interface{}) error { 467 _, err := createSettings(s.st, s.collection, key, settings) 468 return err 469 } 470 471 // ReadSettings exposes readSettings on state for use outside the state package. 472 func (s *StateSettings) ReadSettings(key string) (map[string]interface{}, error) { 473 if settings, err := readSettings(s.st, s.collection, key); err != nil { 474 return nil, err 475 } else { 476 return settings.Map(), nil 477 } 478 } 479 480 // RemoveSettings exposes removeSettings on state for use outside the state package. 481 func (s *StateSettings) RemoveSettings(key string) error { 482 return removeSettings(s.st, s.collection, key) 483 } 484 485 // ListSettings exposes listSettings on state for use outside the state package. 486 func (s *StateSettings) ListSettings(keyPrefix string) (map[string]map[string]interface{}, error) { 487 return listSettings(s.st, s.collection, keyPrefix) 488 }