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 }