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 }