github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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  	"github.com/juju/errors"
     8  	jc "github.com/juju/testing/checkers"
     9  	gc "gopkg.in/check.v1"
    10  	"gopkg.in/mgo.v2/txn"
    11  )
    12  
    13  type SettingsSuite struct {
    14  	internalStateSuite
    15  	key        string
    16  	collection string
    17  }
    18  
    19  var _ = gc.Suite(&SettingsSuite{})
    20  
    21  func (s *SettingsSuite) SetUpTest(c *gc.C) {
    22  	s.internalStateSuite.SetUpTest(c)
    23  	s.key = "config"
    24  	s.collection = settingsC
    25  }
    26  
    27  func (s *SettingsSuite) createSettings(key string, values map[string]interface{}) (*Settings, error) {
    28  	return createSettings(s.state, s.collection, key, values)
    29  }
    30  
    31  func (s *SettingsSuite) readSettings() (*Settings, error) {
    32  	return readSettings(s.state, s.collection, s.key)
    33  }
    34  
    35  func (s *SettingsSuite) TestCreateEmptySettings(c *gc.C) {
    36  	node, err := s.createSettings(s.key, nil)
    37  	c.Assert(err, jc.ErrorIsNil)
    38  	c.Assert(node.Keys(), gc.DeepEquals, []string{})
    39  }
    40  
    41  func (s *SettingsSuite) TestCannotOverwrite(c *gc.C) {
    42  	_, err := s.createSettings(s.key, nil)
    43  	c.Assert(err, jc.ErrorIsNil)
    44  	_, err = s.createSettings(s.key, nil)
    45  	c.Assert(err, gc.ErrorMatches, "cannot overwrite existing settings")
    46  }
    47  
    48  func (s *SettingsSuite) TestCannotReadMissing(c *gc.C) {
    49  	_, err := s.readSettings()
    50  	c.Assert(err, gc.ErrorMatches, "settings not found")
    51  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    52  }
    53  
    54  func (s *SettingsSuite) TestCannotWriteMissing(c *gc.C) {
    55  	node, err := s.createSettings(s.key, nil)
    56  	c.Assert(err, jc.ErrorIsNil)
    57  
    58  	err = removeSettings(s.state, s.collection, s.key)
    59  	c.Assert(err, jc.ErrorIsNil)
    60  
    61  	node.Set("foo", "bar")
    62  	_, err = node.Write()
    63  	c.Assert(err, gc.ErrorMatches, "settings not found")
    64  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    65  }
    66  
    67  func (s *SettingsSuite) TestUpdateWithWrite(c *gc.C) {
    68  	node, err := s.createSettings(s.key, nil)
    69  	c.Assert(err, jc.ErrorIsNil)
    70  	options := map[string]interface{}{"alpha": "beta", "one": 1}
    71  	node.Update(options)
    72  	changes, err := node.Write()
    73  	c.Assert(err, jc.ErrorIsNil)
    74  	c.Assert(changes, gc.DeepEquals, []ItemChange{
    75  		{ItemAdded, "alpha", nil, "beta"},
    76  		{ItemAdded, "one", nil, 1},
    77  	})
    78  
    79  	// Check local state.
    80  	c.Assert(node.Map(), gc.DeepEquals, options)
    81  
    82  	// Check MongoDB state.
    83  	var mgoData struct {
    84  		Settings settingsMap
    85  	}
    86  	settings, closer := s.state.getCollection(settingsC)
    87  	defer closer()
    88  	err = settings.FindId(s.key).One(&mgoData)
    89  	c.Assert(err, jc.ErrorIsNil)
    90  	c.Assert(map[string]interface{}(mgoData.Settings), gc.DeepEquals, options)
    91  }
    92  
    93  func (s *SettingsSuite) TestConflictOnSet(c *gc.C) {
    94  	// Check version conflict errors.
    95  	nodeOne, err := s.createSettings(s.key, nil)
    96  	c.Assert(err, jc.ErrorIsNil)
    97  	nodeTwo, err := s.readSettings()
    98  	c.Assert(err, jc.ErrorIsNil)
    99  
   100  	optionsOld := map[string]interface{}{"alpha": "beta", "one": 1}
   101  	nodeOne.Update(optionsOld)
   102  	nodeOne.Write()
   103  
   104  	nodeTwo.Update(optionsOld)
   105  	changes, err := nodeTwo.Write()
   106  	c.Assert(err, jc.ErrorIsNil)
   107  	c.Assert(changes, gc.DeepEquals, []ItemChange{
   108  		{ItemAdded, "alpha", nil, "beta"},
   109  		{ItemAdded, "one", nil, 1},
   110  	})
   111  
   112  	// First test node one.
   113  	c.Assert(nodeOne.Map(), gc.DeepEquals, optionsOld)
   114  
   115  	// Write on node one.
   116  	optionsNew := map[string]interface{}{"alpha": "gamma", "one": "two"}
   117  	nodeOne.Update(optionsNew)
   118  	changes, err = nodeOne.Write()
   119  	c.Assert(err, jc.ErrorIsNil)
   120  	c.Assert(changes, gc.DeepEquals, []ItemChange{
   121  		{ItemModified, "alpha", "beta", "gamma"},
   122  		{ItemModified, "one", 1, "two"},
   123  	})
   124  
   125  	// Verify that node one reports as expected.
   126  	c.Assert(nodeOne.Map(), gc.DeepEquals, optionsNew)
   127  
   128  	// Verify that node two has still the old data.
   129  	c.Assert(nodeTwo.Map(), gc.DeepEquals, optionsOld)
   130  
   131  	// Now issue a Set/Write from node two. This will
   132  	// merge the data deleting 'one' and updating
   133  	// other values.
   134  	optionsMerge := map[string]interface{}{"alpha": "cappa", "new": "next"}
   135  	nodeTwo.Update(optionsMerge)
   136  	nodeTwo.Delete("one")
   137  
   138  	expected := map[string]interface{}{"alpha": "cappa", "new": "next"}
   139  	changes, err = nodeTwo.Write()
   140  	c.Assert(err, jc.ErrorIsNil)
   141  	c.Assert(changes, gc.DeepEquals, []ItemChange{
   142  		{ItemModified, "alpha", "beta", "cappa"},
   143  		{ItemAdded, "new", nil, "next"},
   144  		{ItemDeleted, "one", 1, nil},
   145  	})
   146  	c.Assert(expected, gc.DeepEquals, nodeTwo.Map())
   147  
   148  	// But node one still reflects the former data.
   149  	c.Assert(nodeOne.Map(), gc.DeepEquals, optionsNew)
   150  }
   151  
   152  func (s *SettingsSuite) TestSetItem(c *gc.C) {
   153  	// Check that Set works as expected.
   154  	node, err := s.createSettings(s.key, nil)
   155  	c.Assert(err, jc.ErrorIsNil)
   156  	options := map[string]interface{}{"alpha": "beta", "one": 1}
   157  	node.Set("alpha", "beta")
   158  	node.Set("one", 1)
   159  	changes, err := node.Write()
   160  	c.Assert(err, jc.ErrorIsNil)
   161  	c.Assert(changes, gc.DeepEquals, []ItemChange{
   162  		{ItemAdded, "alpha", nil, "beta"},
   163  		{ItemAdded, "one", nil, 1},
   164  	})
   165  	// Check local state.
   166  	c.Assert(node.Map(), gc.DeepEquals, options)
   167  	// Check MongoDB state.
   168  	var mgoData struct {
   169  		Settings settingsMap
   170  	}
   171  	settings, closer := s.state.getCollection(settingsC)
   172  	defer closer()
   173  	err = settings.FindId(s.key).One(&mgoData)
   174  	c.Assert(err, jc.ErrorIsNil)
   175  	c.Assert(map[string]interface{}(mgoData.Settings), gc.DeepEquals, options)
   176  }
   177  
   178  func (s *SettingsSuite) TestSetItemEscape(c *gc.C) {
   179  	// Check that Set works as expected.
   180  	node, err := s.createSettings(s.key, nil)
   181  	c.Assert(err, jc.ErrorIsNil)
   182  	options := map[string]interface{}{"$bar": 1, "foo.alpha": "beta"}
   183  	node.Set("foo.alpha", "beta")
   184  	node.Set("$bar", 1)
   185  	changes, err := node.Write()
   186  	c.Assert(err, jc.ErrorIsNil)
   187  	c.Assert(changes, gc.DeepEquals, []ItemChange{
   188  		{ItemAdded, "$bar", nil, 1},
   189  		{ItemAdded, "foo.alpha", nil, "beta"},
   190  	})
   191  	// Check local state.
   192  	c.Assert(node.Map(), gc.DeepEquals, options)
   193  
   194  	// Check MongoDB state.
   195  	mgoOptions := map[string]interface{}{"\uff04bar": 1, "foo\uff0ealpha": "beta"}
   196  	var mgoData struct {
   197  		Settings map[string]interface{}
   198  	}
   199  	settings, closer := s.state.getCollection(settingsC)
   200  	defer closer()
   201  	err = settings.FindId(s.key).One(&mgoData)
   202  	c.Assert(err, jc.ErrorIsNil)
   203  	c.Assert(mgoData.Settings, gc.DeepEquals, mgoOptions)
   204  
   205  	// Now get another state by reading from the database instance and
   206  	// check read state has replaced '.' and '$' after fetching from
   207  	// MongoDB.
   208  	nodeTwo, err := s.readSettings()
   209  	c.Assert(err, jc.ErrorIsNil)
   210  	c.Assert(nodeTwo.disk, gc.DeepEquals, options)
   211  	c.Assert(nodeTwo.core, gc.DeepEquals, options)
   212  }
   213  
   214  func (s *SettingsSuite) TestReplaceSettingsEscape(c *gc.C) {
   215  	// Check that replaceSettings works as expected.
   216  	node, err := s.createSettings(s.key, nil)
   217  	c.Assert(err, jc.ErrorIsNil)
   218  	node.Set("foo.alpha", "beta")
   219  	node.Set("$bar", 1)
   220  	_, err = node.Write()
   221  	c.Assert(err, jc.ErrorIsNil)
   222  
   223  	options := map[string]interface{}{"$baz": 1, "foo.bar": "beta"}
   224  	rop, settingsChanged, err := replaceSettingsOp(s.state, s.collection, s.key, options)
   225  	c.Assert(err, jc.ErrorIsNil)
   226  	ops := []txn.Op{rop}
   227  	err = node.st.runTransaction(ops)
   228  	c.Assert(err, jc.ErrorIsNil)
   229  
   230  	changed, err := settingsChanged()
   231  	c.Assert(err, jc.ErrorIsNil)
   232  	c.Assert(changed, jc.IsTrue)
   233  
   234  	// Check MongoDB state.
   235  	mgoOptions := map[string]interface{}{"\uff04baz": 1, "foo\uff0ebar": "beta"}
   236  	var mgoData struct {
   237  		Settings map[string]interface{}
   238  	}
   239  	settings, closer := s.state.getCollection(settingsC)
   240  	defer closer()
   241  	err = settings.FindId(s.key).One(&mgoData)
   242  	c.Assert(err, jc.ErrorIsNil)
   243  	c.Assert(mgoData.Settings, gc.DeepEquals, mgoOptions)
   244  }
   245  
   246  func (s *SettingsSuite) TestcreateSettingsEscape(c *gc.C) {
   247  	// Check that createSettings works as expected.
   248  	options := map[string]interface{}{"$baz": 1, "foo.bar": "beta"}
   249  	node, err := s.createSettings(s.key, options)
   250  	c.Assert(err, jc.ErrorIsNil)
   251  
   252  	// Check local state.
   253  	c.Assert(node.Map(), gc.DeepEquals, options)
   254  
   255  	// Check MongoDB state.
   256  	mgoOptions := map[string]interface{}{"\uff04baz": 1, "foo\uff0ebar": "beta"}
   257  	var mgoData struct {
   258  		Settings map[string]interface{}
   259  	}
   260  	settings, closer := s.state.getCollection(settingsC)
   261  	defer closer()
   262  
   263  	err = settings.FindId(s.key).One(&mgoData)
   264  	c.Assert(err, jc.ErrorIsNil)
   265  	c.Assert(mgoData.Settings, gc.DeepEquals, mgoOptions)
   266  }
   267  
   268  func (s *SettingsSuite) TestMultipleReads(c *gc.C) {
   269  	// Check that reads without writes always resets the data.
   270  	nodeOne, err := s.createSettings(s.key, nil)
   271  	c.Assert(err, jc.ErrorIsNil)
   272  	nodeOne.Update(map[string]interface{}{"alpha": "beta", "foo": "bar"})
   273  	value, ok := nodeOne.Get("alpha")
   274  	c.Assert(ok, jc.IsTrue)
   275  	c.Assert(value, gc.Equals, "beta")
   276  	value, ok = nodeOne.Get("foo")
   277  	c.Assert(ok, jc.IsTrue)
   278  	c.Assert(value, gc.Equals, "bar")
   279  	value, ok = nodeOne.Get("baz")
   280  	c.Assert(ok, jc.IsFalse)
   281  
   282  	// A read resets the data to the empty state.
   283  	err = nodeOne.Read()
   284  	c.Assert(err, jc.ErrorIsNil)
   285  	c.Assert(nodeOne.Map(), gc.DeepEquals, map[string]interface{}{})
   286  	nodeOne.Update(map[string]interface{}{"alpha": "beta", "foo": "bar"})
   287  	changes, err := nodeOne.Write()
   288  	c.Assert(err, jc.ErrorIsNil)
   289  	c.Assert(changes, gc.DeepEquals, []ItemChange{
   290  		{ItemAdded, "alpha", nil, "beta"},
   291  		{ItemAdded, "foo", nil, "bar"},
   292  	})
   293  
   294  	// A write retains the newly set values.
   295  	value, ok = nodeOne.Get("alpha")
   296  	c.Assert(ok, jc.IsTrue)
   297  	c.Assert(value, gc.Equals, "beta")
   298  	value, ok = nodeOne.Get("foo")
   299  	c.Assert(ok, jc.IsTrue)
   300  	c.Assert(value, gc.Equals, "bar")
   301  
   302  	// Now get another state instance and change underlying state.
   303  	nodeTwo, err := s.readSettings()
   304  	c.Assert(err, jc.ErrorIsNil)
   305  	nodeTwo.Update(map[string]interface{}{"foo": "different"})
   306  	changes, err = nodeTwo.Write()
   307  	c.Assert(err, jc.ErrorIsNil)
   308  	c.Assert(changes, gc.DeepEquals, []ItemChange{
   309  		{ItemModified, "foo", "bar", "different"},
   310  	})
   311  
   312  	// This should pull in the new state into node one.
   313  	err = nodeOne.Read()
   314  	c.Assert(err, jc.ErrorIsNil)
   315  	value, ok = nodeOne.Get("alpha")
   316  	c.Assert(ok, jc.IsTrue)
   317  	c.Assert(value, gc.Equals, "beta")
   318  	value, ok = nodeOne.Get("foo")
   319  	c.Assert(ok, jc.IsTrue)
   320  	c.Assert(value, gc.Equals, "different")
   321  }
   322  
   323  func (s *SettingsSuite) TestDeleteEmptiesState(c *gc.C) {
   324  	node, err := s.createSettings(s.key, nil)
   325  	c.Assert(err, jc.ErrorIsNil)
   326  	node.Set("a", "foo")
   327  	changes, err := node.Write()
   328  	c.Assert(err, jc.ErrorIsNil)
   329  	c.Assert(changes, gc.DeepEquals, []ItemChange{
   330  		{ItemAdded, "a", nil, "foo"},
   331  	})
   332  	node.Delete("a")
   333  	changes, err = node.Write()
   334  	c.Assert(err, jc.ErrorIsNil)
   335  	c.Assert(changes, gc.DeepEquals, []ItemChange{
   336  		{ItemDeleted, "a", "foo", nil},
   337  	})
   338  	c.Assert(node.Map(), gc.DeepEquals, map[string]interface{}{})
   339  }
   340  
   341  func (s *SettingsSuite) TestReadResync(c *gc.C) {
   342  	// Check that read pulls the data into the node.
   343  	nodeOne, err := s.createSettings(s.key, nil)
   344  	c.Assert(err, jc.ErrorIsNil)
   345  	nodeOne.Set("a", "foo")
   346  	changes, err := nodeOne.Write()
   347  	c.Assert(err, jc.ErrorIsNil)
   348  	c.Assert(changes, gc.DeepEquals, []ItemChange{
   349  		{ItemAdded, "a", nil, "foo"},
   350  	})
   351  	nodeTwo, err := s.readSettings()
   352  	c.Assert(err, jc.ErrorIsNil)
   353  	nodeTwo.Delete("a")
   354  	changes, err = nodeTwo.Write()
   355  	c.Assert(err, jc.ErrorIsNil)
   356  	c.Assert(changes, gc.DeepEquals, []ItemChange{
   357  		{ItemDeleted, "a", "foo", nil},
   358  	})
   359  	nodeTwo.Set("a", "bar")
   360  	changes, err = nodeTwo.Write()
   361  	c.Assert(err, jc.ErrorIsNil)
   362  	c.Assert(changes, gc.DeepEquals, []ItemChange{
   363  		{ItemAdded, "a", nil, "bar"},
   364  	})
   365  	// Read of node one should pick up the new value.
   366  	err = nodeOne.Read()
   367  	c.Assert(err, jc.ErrorIsNil)
   368  	value, ok := nodeOne.Get("a")
   369  	c.Assert(ok, jc.IsTrue)
   370  	c.Assert(value, gc.Equals, "bar")
   371  }
   372  
   373  func (s *SettingsSuite) TestMultipleWrites(c *gc.C) {
   374  	// Check that multiple writes only do the right changes.
   375  	node, err := s.createSettings(s.key, nil)
   376  	c.Assert(err, jc.ErrorIsNil)
   377  	node.Update(map[string]interface{}{"foo": "bar", "this": "that"})
   378  	changes, err := node.Write()
   379  	c.Assert(err, jc.ErrorIsNil)
   380  	c.Assert(changes, gc.DeepEquals, []ItemChange{
   381  		{ItemAdded, "foo", nil, "bar"},
   382  		{ItemAdded, "this", nil, "that"},
   383  	})
   384  	node.Delete("this")
   385  	node.Set("another", "value")
   386  	changes, err = node.Write()
   387  	c.Assert(err, jc.ErrorIsNil)
   388  	c.Assert(changes, gc.DeepEquals, []ItemChange{
   389  		{ItemAdded, "another", nil, "value"},
   390  		{ItemDeleted, "this", "that", nil},
   391  	})
   392  
   393  	expected := map[string]interface{}{"foo": "bar", "another": "value"}
   394  	c.Assert(expected, gc.DeepEquals, node.Map())
   395  
   396  	changes, err = node.Write()
   397  	c.Assert(err, jc.ErrorIsNil)
   398  	c.Assert(changes, gc.DeepEquals, []ItemChange{})
   399  
   400  	err = node.Read()
   401  	c.Assert(err, jc.ErrorIsNil)
   402  	c.Assert(expected, gc.DeepEquals, node.Map())
   403  
   404  	changes, err = node.Write()
   405  	c.Assert(err, jc.ErrorIsNil)
   406  	c.Assert(changes, gc.DeepEquals, []ItemChange{})
   407  }
   408  
   409  func (s *SettingsSuite) TestMultipleWritesAreStable(c *gc.C) {
   410  	node, err := s.createSettings(s.key, nil)
   411  	c.Assert(err, jc.ErrorIsNil)
   412  	node.Update(map[string]interface{}{"foo": "bar", "this": "that"})
   413  	_, err = node.Write()
   414  	c.Assert(err, jc.ErrorIsNil)
   415  
   416  	var mgoData struct {
   417  		Settings map[string]interface{}
   418  	}
   419  	settings, closer := s.state.getCollection(settingsC)
   420  	defer closer()
   421  	err = settings.FindId(s.key).One(&mgoData)
   422  	c.Assert(err, jc.ErrorIsNil)
   423  	version := mgoData.Settings["version"]
   424  	for i := 0; i < 100; i++ {
   425  		node.Set("value", i)
   426  		node.Set("foo", "bar")
   427  		node.Delete("value")
   428  		node.Set("this", "that")
   429  		_, err := node.Write()
   430  		c.Assert(err, jc.ErrorIsNil)
   431  	}
   432  	mgoData.Settings = make(map[string]interface{})
   433  	err = settings.FindId(s.key).One(&mgoData)
   434  	c.Assert(err, jc.ErrorIsNil)
   435  	newVersion := mgoData.Settings["version"]
   436  	c.Assert(version, gc.Equals, newVersion)
   437  }
   438  
   439  func (s *SettingsSuite) TestWriteTwice(c *gc.C) {
   440  	// Check the correct writing into a node by two config nodes.
   441  	nodeOne, err := s.createSettings(s.key, nil)
   442  	c.Assert(err, jc.ErrorIsNil)
   443  	nodeOne.Set("a", "foo")
   444  	changes, err := nodeOne.Write()
   445  	c.Assert(err, jc.ErrorIsNil)
   446  	c.Assert(changes, gc.DeepEquals, []ItemChange{
   447  		{ItemAdded, "a", nil, "foo"},
   448  	})
   449  
   450  	nodeTwo, err := s.readSettings()
   451  	c.Assert(err, jc.ErrorIsNil)
   452  	nodeTwo.Set("a", "bar")
   453  	changes, err = nodeTwo.Write()
   454  	c.Assert(err, jc.ErrorIsNil)
   455  	c.Assert(changes, gc.DeepEquals, []ItemChange{
   456  		{ItemModified, "a", "foo", "bar"},
   457  	})
   458  
   459  	// Shouldn't write again. Changes were already
   460  	// flushed and acted upon by other parties.
   461  	changes, err = nodeOne.Write()
   462  	c.Assert(err, jc.ErrorIsNil)
   463  	c.Assert(changes, gc.DeepEquals, []ItemChange{})
   464  
   465  	err = nodeOne.Read()
   466  	c.Assert(err, jc.ErrorIsNil)
   467  	c.Assert(nodeOne.key, gc.Equals, nodeTwo.key)
   468  	c.Assert(nodeOne.disk, gc.DeepEquals, nodeTwo.disk)
   469  	c.Assert(nodeOne.core, gc.DeepEquals, nodeTwo.core)
   470  }
   471  
   472  func (s *SettingsSuite) TestList(c *gc.C) {
   473  	_, err := s.createSettings("key#1", map[string]interface{}{"foo1": "bar1"})
   474  	c.Assert(err, jc.ErrorIsNil)
   475  	_, err = s.createSettings("key#2", map[string]interface{}{"foo2": "bar2"})
   476  	c.Assert(err, jc.ErrorIsNil)
   477  	_, err = s.createSettings("another#1", map[string]interface{}{"foo2": "bar2"})
   478  	c.Assert(err, jc.ErrorIsNil)
   479  
   480  	nodes, err := listSettings(s.state, s.collection, "key#")
   481  	c.Assert(err, jc.ErrorIsNil)
   482  	c.Assert(nodes, jc.DeepEquals, map[string]map[string]interface{}{
   483  		"key#1": {"foo1": "bar1"},
   484  		"key#2": {"foo2": "bar2"},
   485  	})
   486  }