github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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 key string 98 99 // disk holds the values in the config node before 100 // any keys have been changed. It is reset on Read and Write 101 // operations. 102 disk map[string]interface{} 103 104 // cache holds the current values in the config node. 105 // The difference between disk and core 106 // determines the delta to be applied when Settings.Write 107 // is called. 108 core map[string]interface{} 109 110 // version is the version corresponding to "disk"; i.e. 111 // the value of the version field in the status document 112 // when it was read. 113 version int64 114 } 115 116 // Keys returns the current keys in alphabetical order. 117 func (c *Settings) Keys() []string { 118 keys := []string{} 119 for key := range c.core { 120 keys = append(keys, key) 121 } 122 sort.Strings(keys) 123 return keys 124 } 125 126 // Get returns the value of key and whether it was found. 127 func (c *Settings) Get(key string) (value interface{}, found bool) { 128 value, found = c.core[key] 129 return 130 } 131 132 // Map returns all keys and values of the node. 133 func (c *Settings) Map() map[string]interface{} { 134 return copyMap(c.core, nil) 135 } 136 137 // Set sets key to value 138 func (c *Settings) Set(key string, value interface{}) { 139 c.core[key] = value 140 } 141 142 // Update sets multiple key/value pairs. 143 func (c *Settings) Update(kv map[string]interface{}) { 144 for key, value := range kv { 145 c.core[key] = value 146 } 147 } 148 149 // Delete removes key. 150 func (c *Settings) Delete(key string) { 151 delete(c.core, key) 152 } 153 154 // cacheKeys returns the keys of all caches as a key=>true map. 155 func cacheKeys(caches ...map[string]interface{}) map[string]bool { 156 keys := make(map[string]bool) 157 for _, cache := range caches { 158 for key := range cache { 159 keys[key] = true 160 } 161 } 162 return keys 163 } 164 165 // Write writes changes made to c back onto its node. Changes are written 166 // as a delta applied on top of the latest version of the node, to prevent 167 // overwriting unrelated changes made to the node since it was last read. 168 func (c *Settings) Write() ([]ItemChange, error) { 169 changes := []ItemChange{} 170 updates := bson.M{} 171 deletions := bson.M{} 172 for key := range cacheKeys(c.disk, c.core) { 173 old, ondisk := c.disk[key] 174 new, incore := c.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: settingsC, 201 Id: c.key, 202 Assert: txn.DocExists, 203 Update: setUnsetUpdateSettings(updates, deletions), 204 }} 205 err := c.st.runTransaction(ops) 206 if err == txn.ErrAborted { 207 return nil, errors.NotFoundf("settings") 208 } 209 if err != nil { 210 return nil, fmt.Errorf("cannot write settings: %v", err) 211 } 212 c.disk = copyMap(c.core, nil) 213 return changes, nil 214 } 215 216 func newSettings(st *State, key string) *Settings { 217 return &Settings{ 218 st: st, 219 key: key, 220 core: make(map[string]interface{}), 221 } 222 } 223 224 func newSettingsWithDoc(st *State, key string, doc *settingsDoc) *Settings { 225 return &Settings{ 226 st: st, 227 key: key, 228 version: doc.Version, 229 disk: doc.Settings, 230 core: copyMap(doc.Settings, nil), 231 } 232 } 233 234 // replaceKeys will modify the provided map in place by replacing keys with 235 // their replacement if they have been modified. 236 func replaceKeys(m map[string]interface{}, replace func(string) string) { 237 for key, value := range m { 238 if newKey := replace(key); newKey != key { 239 delete(m, key) 240 m[newKey] = value 241 } 242 } 243 return 244 } 245 246 // copyMap copies the keys and values of one map into a new one. If replace 247 // is non-nil, for each old key k, the new key will be replace(k). 248 func copyMap(in map[string]interface{}, replace func(string) string) (out map[string]interface{}) { 249 out = make(map[string]interface{}) 250 for key, value := range in { 251 if replace != nil { 252 key = replace(key) 253 } 254 out[key] = value 255 } 256 return 257 } 258 259 // Read (re)reads the node data into c. 260 func (c *Settings) Read() error { 261 doc, err := readSettingsDoc(c.st, c.key) 262 if errors.IsNotFound(err) { 263 c.disk = nil 264 c.core = make(map[string]interface{}) 265 return err 266 } 267 if err != nil { 268 return errors.Annotate(err, "cannot read settings") 269 } 270 c.version = doc.Version 271 c.disk = doc.Settings 272 c.core = copyMap(c.disk, nil) 273 return nil 274 } 275 276 // readSettingsDoc reads the settings doc with the given key. 277 func readSettingsDoc(st *State, key string) (*settingsDoc, error) { 278 var doc settingsDoc 279 if err := readSettingsDocInto(st, key, &doc); err != nil { 280 return nil, errors.Trace(err) 281 } 282 return &doc, nil 283 } 284 285 // readSettingsDocInto reads the settings doc with the given key 286 // into the provided output structure. 287 func readSettingsDocInto(st *State, key string, out interface{}) error { 288 settings, closer := st.getRawCollection(settingsC) 289 defer closer() 290 291 err := settings.FindId(st.docID(key)).One(out) 292 293 // This is required to allow loading of environ settings before the 294 // model UUID migration has been applied to the settings collection. 295 // Without this, an agent's version cannot be read, blocking the upgrade. 296 if err == mgo.ErrNotFound && key == modelGlobalKey { 297 err = settings.FindId(modelGlobalKey).One(out) 298 } 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(key string) (*Settings, error) { 307 return readSettings(st, key) 308 } 309 310 // readSettings returns the Settings for key. 311 func readSettings(st *State, key string) (*Settings, error) { 312 s := newSettings(st, key) 313 if err := s.Read(); err != nil { 314 return nil, err 315 } 316 return s, nil 317 } 318 319 var errSettingsExist = fmt.Errorf("cannot overwrite existing settings") 320 321 func createSettingsOp(key string, values map[string]interface{}) txn.Op { 322 newValues := copyMap(values, escapeReplacer.Replace) 323 return txn.Op{ 324 C: settingsC, 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, key string, values map[string]interface{}) (*Settings, error) { 335 s := newSettings(st, key) 336 s.core = copyMap(values, nil) 337 ops := []txn.Op{createSettingsOp(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, key string) error { 350 err := st.runTransaction([]txn.Op{removeSettingsOp(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(key string) txn.Op { 360 return txn.Op{ 361 C: settingsC, 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, keyPrefix string) (map[string]map[string]interface{}, error) { 370 settings, closer := st.getRawCollection(settingsC) 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, key string, values map[string]interface{}) (txn.Op, func() (bool, error), error) { 390 s, err := readSettings(st, 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, 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: settingsC, 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 settingsC 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 } 458 459 // NewStateSettings creates a StateSettings from state. 460 func NewStateSettings(st *State) *StateSettings { 461 return &StateSettings{st} 462 } 463 464 // CreateSettings exposes createSettings on state for use outside the state package. 465 func (s *StateSettings) CreateSettings(key string, settings map[string]interface{}) error { 466 _, err := createSettings(s.st, key, settings) 467 return err 468 } 469 470 // ReadSettings exposes readSettings on state for use outside the state package. 471 func (s *StateSettings) ReadSettings(key string) (map[string]interface{}, error) { 472 if settings, err := readSettings(s.st, key); err != nil { 473 return nil, err 474 } else { 475 return settings.Map(), nil 476 } 477 } 478 479 // RemoveSettings exposes removeSettings on state for use outside the state package. 480 func (s *StateSettings) RemoveSettings(key string) error { 481 return removeSettings(s.st, key) 482 } 483 484 // ListSettings exposes listSettings on state for use outside the state package. 485 func (s *StateSettings) ListSettings(keyPrefix string) (map[string]map[string]interface{}, error) { 486 return listSettings(s.st, keyPrefix) 487 }