1
// Copyright 2012, 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
7
"github.com/juju/errors"
8
jc "github.com/juju/testing/checkers"
13
type SettingsSuite struct {
19
var _ = gc.Suite(&SettingsSuite{})
21
func (s *SettingsSuite) SetUpTest(c *gc.C) {
22
s.internalStateSuite.SetUpTest(c)
24
s.collection = settingsC
27
func (s *SettingsSuite) createSettings(key string, values map[string]interface{}) (*Settings, error) {
28
return createSettings(s.state, s.collection, key, values)
31
func (s *SettingsSuite) readSettings() (*Settings, error) {
32
return readSettings(s.state, s.collection, s.key)
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{})
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")
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)
54
func (s *SettingsSuite) TestCannotWriteMissing(c *gc.C) {
55
node, err := s.createSettings(s.key, nil)
56
c.Assert(err, jc.ErrorIsNil)
58
err = removeSettings(s.state, s.collection, s.key)
59
c.Assert(err, jc.ErrorIsNil)
61
node.Set("foo", "bar")
63
c.Assert(err, gc.ErrorMatches, "settings not found")
64
c.Assert(err, jc.Satisfies, errors.IsNotFound)
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}
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},
80
c.Assert(node.Map(), gc.DeepEquals, options)
82
// Check MongoDB state.
86
settings, closer := s.state.getCollection(settingsC)
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)
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)
100
optionsOld := map[string]interface{}{"alpha": "beta", "one": 1}
101
nodeOne.Update(optionsOld)
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},
112
// First test node one.
113
c.Assert(nodeOne.Map(), gc.DeepEquals, optionsOld)
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"},
125
// Verify that node one reports as expected.
126
c.Assert(nodeOne.Map(), gc.DeepEquals, optionsNew)
128
// Verify that node two has still the old data.
129
c.Assert(nodeTwo.Map(), gc.DeepEquals, optionsOld)
131
// Now issue a Set/Write from node two. This will
132
// merge the data deleting 'one' and updating
134
optionsMerge := map[string]interface{}{"alpha": "cappa", "new": "next"}
135
nodeTwo.Update(optionsMerge)
136
nodeTwo.Delete("one")
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},
146
c.Assert(expected, gc.DeepEquals, nodeTwo.Map())
148
// But node one still reflects the former data.
149
c.Assert(nodeOne.Map(), gc.DeepEquals, optionsNew)
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")
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},
165
// Check local state.
166
c.Assert(node.Map(), gc.DeepEquals, options)
167
// Check MongoDB state.
171
settings, closer := s.state.getCollection(settingsC)
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)
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")
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"},
191
// Check local state.
192
c.Assert(node.Map(), gc.DeepEquals, options)
194
// Check MongoDB state.
195
mgoOptions := map[string]interface{}{"\uff04bar": 1, "foo\uff0ealpha": "beta"}
197
Settings map[string]interface{}
199
settings, closer := s.state.getCollection(settingsC)
201
err = settings.FindId(s.key).One(&mgoData)
202
c.Assert(err, jc.ErrorIsNil)
203
c.Assert(mgoData.Settings, gc.DeepEquals, mgoOptions)
205
// Now get another state by reading from the database instance and
206
// check read state has replaced '.' and '$' after fetching from
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)
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")
220
_, err = node.Write()
221
c.Assert(err, jc.ErrorIsNil)
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)
227
err = node.st.runTransaction(ops)
228
c.Assert(err, jc.ErrorIsNil)
230
changed, err := settingsChanged()
231
c.Assert(err, jc.ErrorIsNil)
232
c.Assert(changed, jc.IsTrue)
234
// Check MongoDB state.
235
mgoOptions := map[string]interface{}{"\uff04baz": 1, "foo\uff0ebar": "beta"}
237
Settings map[string]interface{}
239
settings, closer := s.state.getCollection(settingsC)
241
err = settings.FindId(s.key).One(&mgoData)
242
c.Assert(err, jc.ErrorIsNil)
243
c.Assert(mgoData.Settings, gc.DeepEquals, mgoOptions)
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)
252
// Check local state.
253
c.Assert(node.Map(), gc.DeepEquals, options)
255
// Check MongoDB state.
256
mgoOptions := map[string]interface{}{"\uff04baz": 1, "foo\uff0ebar": "beta"}
258
Settings map[string]interface{}
260
settings, closer := s.state.getCollection(settingsC)
263
err = settings.FindId(s.key).One(&mgoData)
264
c.Assert(err, jc.ErrorIsNil)
265
c.Assert(mgoData.Settings, gc.DeepEquals, mgoOptions)
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)
282
// A read resets the data to the empty state.
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"},
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")
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"},
312
// This should pull in the new state into node one.
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")
323
func (s *SettingsSuite) TestDeleteEmptiesState(c *gc.C) {
324
node, err := s.createSettings(s.key, nil)
325
c.Assert(err, jc.ErrorIsNil)
327
changes, err := node.Write()
328
c.Assert(err, jc.ErrorIsNil)
329
c.Assert(changes, gc.DeepEquals, []ItemChange{
330
{ItemAdded, "a", nil, "foo"},
333
changes, err = node.Write()
334
c.Assert(err, jc.ErrorIsNil)
335
c.Assert(changes, gc.DeepEquals, []ItemChange{
336
{ItemDeleted, "a", "foo", nil},
338
c.Assert(node.Map(), gc.DeepEquals, map[string]interface{}{})
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"},
351
nodeTwo, err := s.readSettings()
352
c.Assert(err, jc.ErrorIsNil)
354
changes, err = nodeTwo.Write()
355
c.Assert(err, jc.ErrorIsNil)
356
c.Assert(changes, gc.DeepEquals, []ItemChange{
357
{ItemDeleted, "a", "foo", nil},
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"},
365
// Read of node one should pick up the new value.
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")
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"},
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},
393
expected := map[string]interface{}{"foo": "bar", "another": "value"}
394
c.Assert(expected, gc.DeepEquals, node.Map())
396
changes, err = node.Write()
397
c.Assert(err, jc.ErrorIsNil)
398
c.Assert(changes, gc.DeepEquals, []ItemChange{})
401
c.Assert(err, jc.ErrorIsNil)
402
c.Assert(expected, gc.DeepEquals, node.Map())
404
changes, err = node.Write()
405
c.Assert(err, jc.ErrorIsNil)
406
c.Assert(changes, gc.DeepEquals, []ItemChange{})
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)
417
Settings map[string]interface{}
419
settings, closer := s.state.getCollection(settingsC)
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++ {
426
node.Set("foo", "bar")
428
node.Set("this", "that")
429
_, err := node.Write()
430
c.Assert(err, jc.ErrorIsNil)
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)
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"},
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"},
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{})
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)
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)
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"},