github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/settings_test.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  	"sort"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/mgo/v3/bson"
    11  	"github.com/juju/mgo/v3/txn"
    12  	jc "github.com/juju/testing/checkers"
    13  	jujutxn "github.com/juju/txn/v3"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	coresettings "github.com/juju/juju/core/settings"
    17  )
    18  
    19  type SettingsSuite struct {
    20  	internalStateSuite
    21  	key        string
    22  	collection string
    23  }
    24  
    25  var _ = gc.Suite(&SettingsSuite{})
    26  
    27  func (s *SettingsSuite) SetUpTest(c *gc.C) {
    28  	s.internalStateSuite.SetUpTest(c)
    29  	s.key = "config"
    30  	s.collection = settingsC
    31  }
    32  
    33  func (s *SettingsSuite) createSettings(key string, values map[string]interface{}) (*Settings, error) {
    34  	return createSettings(s.state.db(), s.collection, key, values)
    35  }
    36  
    37  func (s *SettingsSuite) readSettings() (*Settings, error) {
    38  	return readSettings(s.state.db(), s.collection, s.key)
    39  }
    40  
    41  func (s *SettingsSuite) TestCreateEmptySettings(c *gc.C) {
    42  	node, err := s.createSettings(s.key, nil)
    43  	c.Assert(err, jc.ErrorIsNil)
    44  	c.Assert(node.Keys(), gc.DeepEquals, []string{})
    45  }
    46  
    47  func (s *SettingsSuite) TestCannotOverwrite(c *gc.C) {
    48  	_, err := s.createSettings(s.key, nil)
    49  	c.Assert(err, jc.ErrorIsNil)
    50  	_, err = s.createSettings(s.key, nil)
    51  	c.Assert(err, gc.ErrorMatches, "cannot overwrite existing settings")
    52  }
    53  
    54  func (s *SettingsSuite) TestCannotReadMissing(c *gc.C) {
    55  	_, err := s.readSettings()
    56  	c.Assert(err, gc.ErrorMatches, "settings not found")
    57  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    58  }
    59  
    60  func (s *SettingsSuite) TestCannotWriteMissing(c *gc.C) {
    61  	node, err := s.createSettings(s.key, nil)
    62  	c.Assert(err, jc.ErrorIsNil)
    63  
    64  	err = removeSettings(s.state.db(), s.collection, s.key)
    65  	c.Assert(err, jc.ErrorIsNil)
    66  
    67  	node.Set("foo", "bar")
    68  	_, err = node.Write()
    69  	c.Assert(err, gc.ErrorMatches, "settings not found")
    70  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    71  }
    72  
    73  func (s *SettingsSuite) TestUpdateWithWrite(c *gc.C) {
    74  	node, err := s.createSettings(s.key, nil)
    75  	c.Assert(err, jc.ErrorIsNil)
    76  	options := map[string]interface{}{"alpha": "beta", "one": 1}
    77  	node.Update(options)
    78  	changes, err := node.Write()
    79  	c.Assert(err, jc.ErrorIsNil)
    80  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges{
    81  		coresettings.MakeAddition("alpha", "beta"),
    82  		coresettings.MakeAddition("one", 1),
    83  	})
    84  
    85  	// Check local state.
    86  	c.Assert(node.Map(), gc.DeepEquals, options)
    87  
    88  	// Check MongoDB state.
    89  	var mgoData struct {
    90  		Settings settingsMap
    91  	}
    92  	settings, closer := s.state.db().GetCollection(settingsC)
    93  	defer closer()
    94  	err = settings.FindId(s.key).One(&mgoData)
    95  	c.Assert(err, jc.ErrorIsNil)
    96  	c.Assert(map[string]interface{}(mgoData.Settings), gc.DeepEquals, options)
    97  }
    98  
    99  func (s *SettingsSuite) TestConflictOnSet(c *gc.C) {
   100  	// Check version conflict errors.
   101  	nodeOne, err := s.createSettings(s.key, nil)
   102  	c.Assert(err, jc.ErrorIsNil)
   103  	nodeTwo, err := s.readSettings()
   104  	c.Assert(err, jc.ErrorIsNil)
   105  
   106  	optionsOld := map[string]interface{}{"alpha": "beta", "one": 1}
   107  	nodeOne.Update(optionsOld)
   108  	_, err = nodeOne.Write()
   109  	c.Assert(err, jc.ErrorIsNil)
   110  
   111  	nodeTwo.Update(optionsOld)
   112  	changes, err := nodeTwo.Write()
   113  	c.Assert(err, jc.ErrorIsNil)
   114  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges{
   115  		coresettings.MakeAddition("alpha", "beta"),
   116  		coresettings.MakeAddition("one", 1),
   117  	})
   118  
   119  	// First test node one.
   120  	c.Assert(nodeOne.Map(), gc.DeepEquals, optionsOld)
   121  
   122  	// Write on node one.
   123  	optionsNew := map[string]interface{}{"alpha": "gamma", "one": "two"}
   124  	nodeOne.Update(optionsNew)
   125  	changes, err = nodeOne.Write()
   126  	c.Assert(err, jc.ErrorIsNil)
   127  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges{
   128  		coresettings.MakeModification("alpha", "beta", "gamma"),
   129  		coresettings.MakeModification("one", 1, "two"),
   130  	})
   131  
   132  	// Verify that node one reports as expected.
   133  	c.Assert(nodeOne.Map(), gc.DeepEquals, optionsNew)
   134  
   135  	// Verify that node two has still the old data.
   136  	c.Assert(nodeTwo.Map(), gc.DeepEquals, optionsOld)
   137  
   138  	// Now issue a Set/Write from node two. This will
   139  	// merge the data deleting 'one' and updating
   140  	// other values.
   141  	optionsMerge := map[string]interface{}{"alpha": "cappa", "new": "next"}
   142  	nodeTwo.Update(optionsMerge)
   143  	nodeTwo.Delete("one")
   144  
   145  	expected := map[string]interface{}{"alpha": "cappa", "new": "next"}
   146  	changes, err = nodeTwo.Write()
   147  	c.Assert(err, jc.ErrorIsNil)
   148  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges{
   149  		coresettings.MakeModification("alpha", "beta", "cappa"),
   150  		coresettings.MakeAddition("new", "next"),
   151  		coresettings.MakeDeletion("one", 1),
   152  	})
   153  	c.Assert(expected, gc.DeepEquals, nodeTwo.Map())
   154  
   155  	// But node one still reflects the former data.
   156  	c.Assert(nodeOne.Map(), gc.DeepEquals, optionsNew)
   157  }
   158  
   159  func (s *SettingsSuite) TestSetItem(c *gc.C) {
   160  	// Check that Set works as expected.
   161  	node, err := s.createSettings(s.key, nil)
   162  	c.Assert(err, jc.ErrorIsNil)
   163  	options := map[string]interface{}{"alpha": "beta", "one": 1}
   164  	node.Set("alpha", "beta")
   165  	node.Set("one", 1)
   166  	changes, err := node.Write()
   167  	c.Assert(err, jc.ErrorIsNil)
   168  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges{
   169  		coresettings.MakeAddition("alpha", "beta"),
   170  		coresettings.MakeAddition("one", 1),
   171  	})
   172  	// Check local state.
   173  	c.Assert(node.Map(), gc.DeepEquals, options)
   174  	// Check MongoDB state.
   175  	var mgoData struct {
   176  		Settings settingsMap
   177  	}
   178  	settings, closer := s.state.db().GetCollection(settingsC)
   179  	defer closer()
   180  	err = settings.FindId(s.key).One(&mgoData)
   181  	c.Assert(err, jc.ErrorIsNil)
   182  	c.Assert(map[string]interface{}(mgoData.Settings), gc.DeepEquals, options)
   183  }
   184  
   185  func (s *SettingsSuite) TestSetItemEscape(c *gc.C) {
   186  	// Check that Set works as expected.
   187  	node, err := s.createSettings(s.key, nil)
   188  	c.Assert(err, jc.ErrorIsNil)
   189  	options := map[string]interface{}{"$bar": 1, "foo.alpha": "beta"}
   190  	node.Set("foo.alpha", "beta")
   191  	node.Set("$bar", 1)
   192  	changes, err := node.Write()
   193  	c.Assert(err, jc.ErrorIsNil)
   194  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges{
   195  		coresettings.MakeAddition("$bar", 1),
   196  		coresettings.MakeAddition("foo.alpha", "beta"),
   197  	})
   198  	// Check local state.
   199  	c.Assert(node.Map(), gc.DeepEquals, options)
   200  
   201  	// Check MongoDB state.
   202  	mgoOptions := map[string]interface{}{"\uff04bar": 1, "foo\uff0ealpha": "beta"}
   203  	var mgoData struct {
   204  		Settings map[string]interface{}
   205  	}
   206  	settings, closer := s.state.db().GetCollection(settingsC)
   207  	defer closer()
   208  	err = settings.FindId(s.key).One(&mgoData)
   209  	c.Assert(err, jc.ErrorIsNil)
   210  	c.Assert(mgoData.Settings, gc.DeepEquals, mgoOptions)
   211  
   212  	// Now get another state by reading from the database instance and
   213  	// check read state has replaced '.' and '$' after fetching from
   214  	// MongoDB.
   215  	nodeTwo, err := s.readSettings()
   216  	c.Assert(err, jc.ErrorIsNil)
   217  	c.Assert(nodeTwo.disk, gc.DeepEquals, options)
   218  	c.Assert(nodeTwo.core, gc.DeepEquals, options)
   219  }
   220  
   221  func (s *SettingsSuite) TestRawSettingsMapEncodeDecode(c *gc.C) {
   222  	smap := &settingsMap{
   223  		"$dollar":    1,
   224  		"dotted.key": 2,
   225  	}
   226  	asBSON, err := bson.Marshal(smap)
   227  	c.Assert(err, jc.ErrorIsNil)
   228  	var asMap map[string]interface{}
   229  	// unmarshalling into a map doesn't do the custom decoding so we get the raw escaped keys
   230  	err = bson.Unmarshal(asBSON, &asMap)
   231  	c.Assert(err, jc.ErrorIsNil)
   232  	c.Check(asMap, gc.DeepEquals, map[string]interface{}{
   233  		"\uff04dollar":    1,
   234  		"dotted\uff0ekey": 2,
   235  	})
   236  	// unmarshalling into a settingsMap will give us the right decoded keys
   237  	var asSettingsMap settingsMap
   238  	err = bson.Unmarshal(asBSON, &asSettingsMap)
   239  	c.Assert(err, jc.ErrorIsNil)
   240  	c.Check(map[string]interface{}(asSettingsMap), gc.DeepEquals, map[string]interface{}{
   241  		"$dollar":    1,
   242  		"dotted.key": 2,
   243  	})
   244  }
   245  
   246  func (s *SettingsSuite) TestReplaceSettingsEscape(c *gc.C) {
   247  	// Check that replaceSettings works as expected.
   248  	node, err := s.createSettings(s.key, nil)
   249  	c.Assert(err, jc.ErrorIsNil)
   250  	node.Set("foo.alpha", "beta")
   251  	node.Set("$bar", 1)
   252  	_, err = node.Write()
   253  	c.Assert(err, jc.ErrorIsNil)
   254  
   255  	options := map[string]interface{}{"$baz": 1, "foo.bar": "beta"}
   256  	rop, settingsChanged, err := replaceSettingsOp(s.state.db(), s.collection, s.key, options)
   257  	c.Assert(err, jc.ErrorIsNil)
   258  	ops := []txn.Op{rop}
   259  	err = node.db.RunTransaction(ops)
   260  	c.Assert(err, jc.ErrorIsNil)
   261  
   262  	changed, err := settingsChanged()
   263  	c.Assert(err, jc.ErrorIsNil)
   264  	c.Assert(changed, jc.IsTrue)
   265  
   266  	// Check MongoDB state.
   267  	mgoOptions := map[string]interface{}{"\uff04baz": 1, "foo\uff0ebar": "beta"}
   268  	var mgoData struct {
   269  		Settings map[string]interface{}
   270  	}
   271  	settings, closer := s.state.db().GetCollection(settingsC)
   272  	defer closer()
   273  	err = settings.FindId(s.key).One(&mgoData)
   274  	c.Assert(err, jc.ErrorIsNil)
   275  	c.Assert(mgoData.Settings, gc.DeepEquals, mgoOptions)
   276  }
   277  
   278  func (s *SettingsSuite) TestCreateSettingsEscape(c *gc.C) {
   279  	// Check that createSettings works as expected.
   280  	options := map[string]interface{}{"$baz": 1, "foo.bar": "beta"}
   281  	node, err := s.createSettings(s.key, options)
   282  	c.Assert(err, jc.ErrorIsNil)
   283  
   284  	// Check local state.
   285  	c.Assert(node.Map(), gc.DeepEquals, options)
   286  
   287  	// Check MongoDB state.
   288  	mgoOptions := map[string]interface{}{"\uff04baz": 1, "foo\uff0ebar": "beta"}
   289  	var mgoData struct {
   290  		Settings map[string]interface{}
   291  	}
   292  	settings, closer := s.state.db().GetCollection(settingsC)
   293  	defer closer()
   294  
   295  	err = settings.FindId(s.key).One(&mgoData)
   296  	c.Assert(err, jc.ErrorIsNil)
   297  	c.Assert(mgoData.Settings, gc.DeepEquals, mgoOptions)
   298  }
   299  
   300  func (s *SettingsSuite) TestMultipleReads(c *gc.C) {
   301  	// Check that reads without writes always resets the data.
   302  	nodeOne, err := s.createSettings(s.key, nil)
   303  	c.Assert(err, jc.ErrorIsNil)
   304  	nodeOne.Update(map[string]interface{}{"alpha": "beta", "foo": "bar"})
   305  	value, ok := nodeOne.Get("alpha")
   306  	c.Assert(ok, jc.IsTrue)
   307  	c.Assert(value, gc.Equals, "beta")
   308  	value, ok = nodeOne.Get("foo")
   309  	c.Assert(ok, jc.IsTrue)
   310  	c.Assert(value, gc.Equals, "bar")
   311  	_, ok = nodeOne.Get("baz")
   312  	c.Assert(ok, jc.IsFalse)
   313  
   314  	// A read resets the data to the empty state.
   315  	err = nodeOne.Read()
   316  	c.Assert(err, jc.ErrorIsNil)
   317  	c.Assert(nodeOne.Map(), gc.DeepEquals, map[string]interface{}{})
   318  	nodeOne.Update(map[string]interface{}{"alpha": "beta", "foo": "bar"})
   319  	changes, err := nodeOne.Write()
   320  	c.Assert(err, jc.ErrorIsNil)
   321  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges{
   322  		coresettings.MakeAddition("alpha", "beta"),
   323  		coresettings.MakeAddition("foo", "bar"),
   324  	})
   325  
   326  	// A write retains the newly set values.
   327  	value, ok = nodeOne.Get("alpha")
   328  	c.Assert(ok, jc.IsTrue)
   329  	c.Assert(value, gc.Equals, "beta")
   330  	value, ok = nodeOne.Get("foo")
   331  	c.Assert(ok, jc.IsTrue)
   332  	c.Assert(value, gc.Equals, "bar")
   333  
   334  	// Now get another state instance and change underlying state.
   335  	nodeTwo, err := s.readSettings()
   336  	c.Assert(err, jc.ErrorIsNil)
   337  	nodeTwo.Update(map[string]interface{}{"foo": "different"})
   338  	changes, err = nodeTwo.Write()
   339  	c.Assert(err, jc.ErrorIsNil)
   340  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges{
   341  		coresettings.MakeModification("foo", "bar", "different"),
   342  	})
   343  
   344  	// This should pull in the new state into node one.
   345  	err = nodeOne.Read()
   346  	c.Assert(err, jc.ErrorIsNil)
   347  	value, ok = nodeOne.Get("alpha")
   348  	c.Assert(ok, jc.IsTrue)
   349  	c.Assert(value, gc.Equals, "beta")
   350  	value, ok = nodeOne.Get("foo")
   351  	c.Assert(ok, jc.IsTrue)
   352  	c.Assert(value, gc.Equals, "different")
   353  }
   354  
   355  func (s *SettingsSuite) TestDeleteEmptiesState(c *gc.C) {
   356  	node, err := s.createSettings(s.key, nil)
   357  	c.Assert(err, jc.ErrorIsNil)
   358  	node.Set("a", "foo")
   359  	changes, err := node.Write()
   360  	c.Assert(err, jc.ErrorIsNil)
   361  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges{
   362  		coresettings.MakeAddition("a", "foo"),
   363  	})
   364  	node.Delete("a")
   365  	changes, err = node.Write()
   366  	c.Assert(err, jc.ErrorIsNil)
   367  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges{
   368  		coresettings.MakeDeletion("a", "foo"),
   369  	})
   370  	c.Assert(node.Map(), gc.DeepEquals, map[string]interface{}{})
   371  }
   372  
   373  func (s *SettingsSuite) TestReadReSync(c *gc.C) {
   374  	// Check that read pulls the data into the node.
   375  	nodeOne, err := s.createSettings(s.key, nil)
   376  	c.Assert(err, jc.ErrorIsNil)
   377  	nodeOne.Set("a", "foo")
   378  	changes, err := nodeOne.Write()
   379  	c.Assert(err, jc.ErrorIsNil)
   380  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges{
   381  		coresettings.MakeAddition("a", "foo"),
   382  	})
   383  	nodeTwo, err := s.readSettings()
   384  	c.Assert(err, jc.ErrorIsNil)
   385  	nodeTwo.Delete("a")
   386  	changes, err = nodeTwo.Write()
   387  	c.Assert(err, jc.ErrorIsNil)
   388  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges{
   389  		coresettings.MakeDeletion("a", "foo"),
   390  	})
   391  	nodeTwo.Set("a", "bar")
   392  	changes, err = nodeTwo.Write()
   393  	c.Assert(err, jc.ErrorIsNil)
   394  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges{
   395  		coresettings.MakeAddition("a", "bar"),
   396  	})
   397  	// Read of node one should pick up the new value.
   398  	err = nodeOne.Read()
   399  	c.Assert(err, jc.ErrorIsNil)
   400  	value, ok := nodeOne.Get("a")
   401  	c.Assert(ok, jc.IsTrue)
   402  	c.Assert(value, gc.Equals, "bar")
   403  }
   404  
   405  func (s *SettingsSuite) TestMultipleWrites(c *gc.C) {
   406  	// Check that multiple writes only do the right changes.
   407  	node, err := s.createSettings(s.key, nil)
   408  	c.Assert(err, jc.ErrorIsNil)
   409  	node.Update(map[string]interface{}{"foo": "bar", "this": "that"})
   410  	changes, err := node.Write()
   411  	c.Assert(err, jc.ErrorIsNil)
   412  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges{
   413  		coresettings.MakeAddition("foo", "bar"),
   414  		coresettings.MakeAddition("this", "that"),
   415  	})
   416  	node.Delete("this")
   417  	node.Set("another", "value")
   418  	changes, err = node.Write()
   419  	c.Assert(err, jc.ErrorIsNil)
   420  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges{
   421  		coresettings.MakeAddition("another", "value"),
   422  		coresettings.MakeDeletion("this", "that"),
   423  	})
   424  
   425  	expected := map[string]interface{}{"foo": "bar", "another": "value"}
   426  	c.Assert(expected, gc.DeepEquals, node.Map())
   427  
   428  	changes, err = node.Write()
   429  	c.Assert(err, jc.ErrorIsNil)
   430  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges(nil))
   431  
   432  	err = node.Read()
   433  	c.Assert(err, jc.ErrorIsNil)
   434  	c.Assert(expected, gc.DeepEquals, node.Map())
   435  
   436  	changes, err = node.Write()
   437  	c.Assert(err, jc.ErrorIsNil)
   438  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges(nil))
   439  }
   440  
   441  func (s *SettingsSuite) TestMultipleWritesAreStable(c *gc.C) {
   442  	node, err := s.createSettings(s.key, nil)
   443  	c.Assert(err, jc.ErrorIsNil)
   444  	node.Update(map[string]interface{}{"foo": "bar", "this": "that"})
   445  	_, err = node.Write()
   446  	c.Assert(err, jc.ErrorIsNil)
   447  
   448  	var mgoData struct {
   449  		Settings map[string]interface{}
   450  	}
   451  	settings, closer := s.state.db().GetCollection(settingsC)
   452  	defer closer()
   453  	err = settings.FindId(s.key).One(&mgoData)
   454  	c.Assert(err, jc.ErrorIsNil)
   455  	version := mgoData.Settings["version"]
   456  	for i := 0; i < 100; i++ {
   457  		node.Set("value", i)
   458  		node.Set("foo", "bar")
   459  		node.Delete("value")
   460  		node.Set("this", "that")
   461  		_, err := node.Write()
   462  		c.Assert(err, jc.ErrorIsNil)
   463  	}
   464  	mgoData.Settings = make(map[string]interface{})
   465  	err = settings.FindId(s.key).One(&mgoData)
   466  	c.Assert(err, jc.ErrorIsNil)
   467  	newVersion := mgoData.Settings["version"]
   468  	c.Assert(version, gc.Equals, newVersion)
   469  }
   470  
   471  func (s *SettingsSuite) TestWriteTwice(c *gc.C) {
   472  	// Check the correct writing into a node by two config nodes.
   473  	nodeOne, err := s.createSettings(s.key, nil)
   474  	c.Assert(err, jc.ErrorIsNil)
   475  	nodeOne.Set("a", "foo")
   476  	changes, err := nodeOne.Write()
   477  	c.Assert(err, jc.ErrorIsNil)
   478  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges{
   479  		coresettings.MakeAddition("a", "foo"),
   480  	})
   481  
   482  	nodeTwo, err := s.readSettings()
   483  	c.Assert(err, jc.ErrorIsNil)
   484  	nodeTwo.Set("a", "bar")
   485  	changes, err = nodeTwo.Write()
   486  	c.Assert(err, jc.ErrorIsNil)
   487  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges{
   488  		coresettings.MakeModification("a", "foo", "bar"),
   489  	})
   490  
   491  	// Shouldn't write again. Changes were already
   492  	// flushed and acted upon by other parties.
   493  	changes, err = nodeOne.Write()
   494  	c.Assert(err, jc.ErrorIsNil)
   495  	c.Assert(changes, gc.DeepEquals, coresettings.ItemChanges(nil))
   496  
   497  	err = nodeOne.Read()
   498  	c.Assert(err, jc.ErrorIsNil)
   499  	c.Assert(nodeOne.key, gc.Equals, nodeTwo.key)
   500  	c.Assert(nodeOne.disk, gc.DeepEquals, nodeTwo.disk)
   501  	c.Assert(nodeOne.core, gc.DeepEquals, nodeTwo.core)
   502  }
   503  
   504  func (s *SettingsSuite) TestWriteTwiceUsingModelOperation(c *gc.C) {
   505  	// Check the correct writing into a node by two config nodes.
   506  	nodeOne, err := s.createSettings(s.key, nil)
   507  	c.Assert(err, jc.ErrorIsNil)
   508  	nodeOne.Set("a", "foo")
   509  	err = s.state.ApplyOperation(nodeOne.WriteOperation())
   510  	c.Assert(err, jc.ErrorIsNil)
   511  
   512  	nodeTwo, err := s.readSettings()
   513  	c.Assert(err, jc.ErrorIsNil)
   514  	c.Assert(nodeTwo.Map(), gc.DeepEquals, map[string]interface{}{
   515  		"a": "foo",
   516  	}, gc.Commentf("model operation failed to update db"))
   517  	nodeTwo.Set("a", "bar")
   518  	err = s.state.ApplyOperation(nodeTwo.WriteOperation())
   519  	c.Assert(err, jc.ErrorIsNil)
   520  
   521  	// Shouldn't write again. Changes were already
   522  	// flushed and acted upon by other parties.
   523  	_, err = nodeOne.WriteOperation().Build(0)
   524  	c.Assert(err, gc.Equals, jujutxn.ErrNoOperations)
   525  
   526  	err = nodeOne.Read()
   527  	c.Assert(err, jc.ErrorIsNil)
   528  	c.Assert(nodeOne.key, gc.Equals, nodeTwo.key)
   529  	c.Assert(nodeOne.disk, gc.DeepEquals, nodeTwo.disk)
   530  	c.Assert(nodeOne.core, gc.DeepEquals, nodeTwo.core)
   531  }
   532  
   533  func (s *SettingsSuite) TestList(c *gc.C) {
   534  	_, err := s.createSettings("key#1", map[string]interface{}{"foo1": "bar1"})
   535  	c.Assert(err, jc.ErrorIsNil)
   536  	_, err = s.createSettings("key#2", map[string]interface{}{"foo2": "bar2"})
   537  	c.Assert(err, jc.ErrorIsNil)
   538  	_, err = s.createSettings("another#1", map[string]interface{}{"foo2": "bar2"})
   539  	c.Assert(err, jc.ErrorIsNil)
   540  
   541  	nodes, err := listSettings(s.state, s.collection, "key#")
   542  	c.Assert(err, jc.ErrorIsNil)
   543  	c.Assert(nodes, jc.DeepEquals, map[string]map[string]interface{}{
   544  		"key#1": {"foo1": "bar1"},
   545  		"key#2": {"foo2": "bar2"},
   546  	})
   547  }
   548  
   549  func (s *SettingsSuite) TestReplaceSettings(c *gc.C) {
   550  	_, err := s.createSettings(s.key, map[string]interface{}{"foo1": "bar1", "foo2": "bar2"})
   551  	c.Assert(err, jc.ErrorIsNil)
   552  	options := map[string]interface{}{"alpha": "beta", "foo2": "zap100"}
   553  	err = replaceSettings(s.state.db(), s.collection, s.key, options)
   554  	c.Assert(err, jc.ErrorIsNil)
   555  
   556  	// Check MongoDB state.
   557  	var mgoData struct {
   558  		Settings settingsMap
   559  	}
   560  	settings, closer := s.state.db().GetCollection(settingsC)
   561  	defer closer()
   562  	err = settings.FindId(s.key).One(&mgoData)
   563  	c.Assert(err, jc.ErrorIsNil)
   564  	c.Assert(
   565  		map[string]interface{}(mgoData.Settings),
   566  		gc.DeepEquals,
   567  		map[string]interface{}{
   568  			"alpha": "beta", "foo2": "zap100",
   569  		})
   570  }
   571  
   572  func (s *SettingsSuite) TestReplaceSettingsNotFound(c *gc.C) {
   573  	options := map[string]interface{}{"alpha": "beta", "foo2": "zap100"}
   574  	err := replaceSettings(s.state.db(), s.collection, s.key, options)
   575  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   576  }
   577  
   578  func (s *SettingsSuite) TestUpdatingInterfaceSliceValue(c *gc.C) {
   579  	// When storing config values that are coerced from schemas as
   580  	// List(Something), the value will always be a []interface{}. Make
   581  	// sure we can safely update settings with those values.
   582  	s1, err := s.createSettings(s.key, map[string]interface{}{
   583  		"foo1": []interface{}{"bar1"},
   584  	})
   585  	c.Assert(err, jc.ErrorIsNil)
   586  	_, err = s1.Write()
   587  	c.Assert(err, jc.ErrorIsNil)
   588  
   589  	s2, err := s.readSettings()
   590  	c.Assert(err, jc.ErrorIsNil)
   591  	s2.Set("foo1", []interface{}{"bar1", "bar2"})
   592  	_, err = s2.Write()
   593  	c.Assert(err, jc.ErrorIsNil)
   594  
   595  	s3, err := s.readSettings()
   596  	c.Assert(err, jc.ErrorIsNil)
   597  	value, found := s3.Get("foo1")
   598  	c.Assert(found, gc.Equals, true)
   599  	c.Assert(value, gc.DeepEquals, []interface{}{"bar1", "bar2"})
   600  }
   601  
   602  func (s *SettingsSuite) TestApplyAndRetrieveChanges(c *gc.C) {
   603  	s1, err := s.createSettings(s.key, map[string]interface{}{
   604  		"foo.dot":      "bar",
   605  		"alpha$dollar": "beta",
   606  		"number":       1,
   607  	})
   608  	c.Assert(err, jc.ErrorIsNil)
   609  	_, err = s1.Write()
   610  	c.Assert(err, jc.ErrorIsNil)
   611  
   612  	s2, err := s.readSettings()
   613  	c.Assert(err, jc.ErrorIsNil)
   614  
   615  	// Add, update, update one not present, delete, delete one not present,
   616  	// leave one alone.
   617  	s2.applyChanges(coresettings.ItemChanges{
   618  		coresettings.MakeModification("foo.dot", "no-matter", "new-bar"),
   619  		coresettings.MakeModification("make", "no-matter", "new"),
   620  		coresettings.MakeDeletion("alpha$dollar", "no-matter"),
   621  		coresettings.MakeDeletion("what", "the"),
   622  		coresettings.MakeAddition("new", "noob"),
   623  	})
   624  
   625  	// Updating one not present = addition, deleting one not present = no-op.
   626  	exp := coresettings.ItemChanges{
   627  		coresettings.MakeModification("foo.dot", "bar", "new-bar"),
   628  		coresettings.MakeAddition("make", "new"),
   629  		coresettings.MakeDeletion("alpha$dollar", "beta"),
   630  		coresettings.MakeAddition("new", "noob"),
   631  	}
   632  	sort.Sort(exp)
   633  
   634  	c.Assert(s2.changes(), gc.DeepEquals, exp)
   635  }
   636  
   637  func (s *SettingsSuite) TestDeltaOpsSuccess(c *gc.C) {
   638  	s1, err := s.createSettings(s.key, map[string]interface{}{
   639  		"foo": []interface{}{"bar"},
   640  	})
   641  	c.Assert(err, jc.ErrorIsNil)
   642  	_, err = s1.Write()
   643  	c.Assert(err, jc.ErrorIsNil)
   644  
   645  	delta := coresettings.ItemChanges{
   646  		coresettings.MakeModification("foo", "bar", "new-bar"),
   647  		coresettings.MakeAddition("new", "value"),
   648  	}
   649  
   650  	settings := s.state.NewSettings()
   651  	ops, err := settings.DeltaOps(s.key, delta)
   652  	c.Assert(err, jc.ErrorIsNil)
   653  
   654  	err = s.state.db().RunTransaction(ops)
   655  	c.Assert(err, jc.ErrorIsNil)
   656  
   657  	s1.Read()
   658  	c.Assert(s1.Map(), gc.DeepEquals, map[string]interface{}{
   659  		"foo": "new-bar",
   660  		"new": "value",
   661  	})
   662  }
   663  
   664  func (s *SettingsSuite) TestDeltaOpsNoChanges(c *gc.C) {
   665  	s1, err := s.createSettings(s.key, map[string]interface{}{
   666  		"foo": []interface{}{"bar"},
   667  	})
   668  	c.Assert(err, jc.ErrorIsNil)
   669  	_, err = s1.Write()
   670  	c.Assert(err, jc.ErrorIsNil)
   671  
   672  	settings := s.state.NewSettings()
   673  	ops, err := settings.DeltaOps(s.key, nil)
   674  	c.Assert(err, jc.ErrorIsNil)
   675  	c.Assert(ops, gc.IsNil)
   676  }
   677  
   678  func (s *SettingsSuite) TestDeltaOpsChangedError(c *gc.C) {
   679  	s1, err := s.createSettings(s.key, map[string]interface{}{
   680  		"foo": []interface{}{"bar"},
   681  	})
   682  	c.Assert(err, jc.ErrorIsNil)
   683  	_, err = s1.Write()
   684  	c.Assert(err, jc.ErrorIsNil)
   685  
   686  	settings := s.state.NewSettings()
   687  
   688  	delta := coresettings.ItemChanges{
   689  		coresettings.MakeModification("foo", "bar", "new-bar"),
   690  	}
   691  
   692  	ops, err := settings.DeltaOps(s.key, delta)
   693  	c.Assert(err, jc.ErrorIsNil)
   694  
   695  	// Change after settings above is materialised.
   696  	s1.Set("foo", "changed-bar")
   697  	_, err = s1.Write()
   698  	c.Assert(err, jc.ErrorIsNil)
   699  
   700  	err = s.state.db().RunTransaction(ops)
   701  	c.Assert(err, gc.ErrorMatches, "transaction aborted")
   702  }