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