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  }