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  }