github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/upgrades_test.go (about) 1 // Copyright 2014 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" 11 "gopkg.in/mgo.v2/bson" 12 "gopkg.in/mgo.v2/txn" 13 14 "github.com/juju/juju/network" 15 "github.com/juju/juju/permission" 16 "github.com/juju/juju/status" 17 "github.com/juju/juju/testing" 18 ) 19 20 type upgradesSuite struct { 21 internalStateSuite 22 } 23 24 var _ = gc.Suite(&upgradesSuite{}) 25 26 func (s *upgradesSuite) addLegacyDoc(c *gc.C, collName string, legacyDoc bson.M) { 27 ops := []txn.Op{{ 28 C: collName, 29 Id: legacyDoc["_id"], 30 Assert: txn.DocMissing, 31 Insert: legacyDoc, 32 }} 33 err := s.state.runRawTransaction(ops) 34 c.Assert(err, jc.ErrorIsNil) 35 } 36 37 func (s *upgradesSuite) FindId(c *gc.C, coll *mgo.Collection, id interface{}, doc interface{}) { 38 err := coll.FindId(id).One(doc) 39 c.Assert(err, jc.ErrorIsNil) 40 } 41 42 func (s *upgradesSuite) removePreferredAddressFields(c *gc.C, machine *Machine) { 43 machinesCol, closer := s.state.getRawCollection(machinesC) 44 defer closer() 45 46 err := machinesCol.Update( 47 bson.D{{"_id", s.state.docID(machine.Id())}}, 48 bson.D{{"$unset", bson.D{{"preferredpublicaddress", ""}}}}, 49 ) 50 c.Assert(err, jc.ErrorIsNil) 51 err = machinesCol.Update( 52 bson.D{{"_id", s.state.docID(machine.Id())}}, 53 bson.D{{"$unset", bson.D{{"preferredprivateaddress", ""}}}}, 54 ) 55 c.Assert(err, jc.ErrorIsNil) 56 } 57 58 func (s *upgradesSuite) setPreferredAddressFields(c *gc.C, machine *Machine, addr string) { 59 machinesCol, closer := s.state.getRawCollection(machinesC) 60 defer closer() 61 62 stateAddr := fromNetworkAddress(network.NewAddress(addr), OriginUnknown) 63 err := machinesCol.Update( 64 bson.D{{"_id", s.state.docID(machine.Id())}}, 65 bson.D{{"$set", bson.D{{"preferredpublicaddress", stateAddr}}}}, 66 ) 67 c.Assert(err, jc.ErrorIsNil) 68 err = machinesCol.Update( 69 bson.D{{"_id", s.state.docID(machine.Id())}}, 70 bson.D{{"$set", bson.D{{"preferredprivateaddress", stateAddr}}}}, 71 ) 72 c.Assert(err, jc.ErrorIsNil) 73 } 74 75 func assertMachineAddresses(c *gc.C, machine *Machine, publicAddress, privateAddress string) { 76 err := machine.Refresh() 77 c.Assert(err, jc.ErrorIsNil) 78 addr, err := machine.PublicAddress() 79 if publicAddress != "" { 80 c.Assert(err, jc.ErrorIsNil) 81 } else { 82 c.Assert(err, jc.Satisfies, network.IsNoAddressError) 83 } 84 c.Assert(addr.Value, gc.Equals, publicAddress) 85 privAddr, err := machine.PrivateAddress() 86 if privateAddress != "" { 87 c.Assert(err, jc.ErrorIsNil) 88 } else { 89 c.Assert(err, jc.Satisfies, network.IsNoAddressError) 90 } 91 c.Assert(privAddr.Value, gc.Equals, privateAddress) 92 } 93 94 func (s *upgradesSuite) createMachinesWithAddresses(c *gc.C) []*Machine { 95 _, err := s.state.AddMachine("quantal", JobManageModel) 96 c.Assert(err, jc.ErrorIsNil) 97 _, err = s.state.AddMachines([]MachineTemplate{ 98 {Series: "quantal", Jobs: []MachineJob{JobHostUnits}}, 99 {Series: "quantal", Jobs: []MachineJob{JobHostUnits}}, 100 {Series: "quantal", Jobs: []MachineJob{JobHostUnits}}, 101 }...) 102 c.Assert(err, jc.ErrorIsNil) 103 machines, err := s.state.AllMachines() 104 c.Assert(err, jc.ErrorIsNil) 105 c.Assert(machines, gc.HasLen, 4) 106 107 m1 := machines[0] 108 m2 := machines[1] 109 m4 := machines[3] 110 err = m1.SetProviderAddresses(network.NewAddress("8.8.8.8")) 111 c.Assert(err, jc.ErrorIsNil) 112 err = m2.SetMachineAddresses(network.NewAddress("10.0.0.1")) 113 c.Assert(err, jc.ErrorIsNil) 114 err = m2.SetProviderAddresses(network.NewAddress("10.0.0.2"), network.NewAddress("8.8.4.4")) 115 c.Assert(err, jc.ErrorIsNil) 116 117 // Attempting to set the addresses of a dead machine will fail, so we 118 // include a dead machine to make sure the upgrade step can cope. 119 err = m4.SetProviderAddresses(network.NewAddress("8.8.8.8")) 120 c.Assert(err, jc.ErrorIsNil) 121 err = m4.EnsureDead() 122 c.Assert(err, jc.ErrorIsNil) 123 124 // Delete the preferred address fields. 125 for _, machine := range machines { 126 s.removePreferredAddressFields(c, machine) 127 } 128 return machines 129 } 130 131 func (s *upgradesSuite) TestAddPreferredAddressesToMachines(c *gc.C) { 132 machines := s.createMachinesWithAddresses(c) 133 m1 := machines[0] 134 m2 := machines[1] 135 m3 := machines[2] 136 137 err := AddPreferredAddressesToMachines(s.state) 138 c.Assert(err, jc.ErrorIsNil) 139 140 assertMachineAddresses(c, m1, "8.8.8.8", "8.8.8.8") 141 assertMachineAddresses(c, m2, "8.8.4.4", "10.0.0.2") 142 assertMachineAddresses(c, m3, "", "") 143 } 144 145 func (s *upgradesSuite) TestAddPreferredAddressesToMachinesIdempotent(c *gc.C) { 146 machines := s.createMachinesWithAddresses(c) 147 m1 := machines[0] 148 m2 := machines[1] 149 m3 := machines[2] 150 151 err := AddPreferredAddressesToMachines(s.state) 152 c.Assert(err, jc.ErrorIsNil) 153 154 assertMachineAddresses(c, m1, "8.8.8.8", "8.8.8.8") 155 assertMachineAddresses(c, m2, "8.8.4.4", "10.0.0.2") 156 assertMachineAddresses(c, m3, "", "") 157 158 err = AddPreferredAddressesToMachines(s.state) 159 c.Assert(err, jc.ErrorIsNil) 160 161 assertMachineAddresses(c, m1, "8.8.8.8", "8.8.8.8") 162 assertMachineAddresses(c, m2, "8.8.4.4", "10.0.0.2") 163 assertMachineAddresses(c, m3, "", "") 164 } 165 166 func (s *upgradesSuite) TestAddPreferredAddressesToMachinesUpdatesExistingFields(c *gc.C) { 167 machines := s.createMachinesWithAddresses(c) 168 m1 := machines[0] 169 m2 := machines[1] 170 m3 := machines[2] 171 s.setPreferredAddressFields(c, m1, "1.1.2.2") 172 s.setPreferredAddressFields(c, m2, "1.1.2.2") 173 s.setPreferredAddressFields(c, m3, "1.1.2.2") 174 175 assertMachineInitial := func(m *Machine) { 176 err := m.Refresh() 177 c.Assert(err, jc.ErrorIsNil) 178 addr, err := m.PublicAddress() 179 c.Assert(err, jc.ErrorIsNil) 180 c.Assert(addr.Value, gc.Equals, "1.1.2.2") 181 addr, err = m.PrivateAddress() 182 c.Assert(err, jc.ErrorIsNil) 183 c.Assert(addr.Value, gc.Equals, "1.1.2.2") 184 } 185 assertMachineInitial(m1) 186 assertMachineInitial(m2) 187 assertMachineInitial(m3) 188 189 err := AddPreferredAddressesToMachines(s.state) 190 c.Assert(err, jc.ErrorIsNil) 191 192 assertMachineAddresses(c, m1, "8.8.8.8", "8.8.8.8") 193 assertMachineAddresses(c, m2, "8.8.4.4", "10.0.0.2") 194 assertMachineAddresses(c, m3, "", "") 195 } 196 197 func (s *upgradesSuite) readDocIDs(c *gc.C, coll, regex string) []string { 198 settings, closer := s.state.getRawCollection(coll) 199 defer closer() 200 var docs []bson.M 201 err := settings.Find(bson.D{{"_id", bson.D{{"$regex", regex}}}}).All(&docs) 202 c.Assert(err, jc.ErrorIsNil) 203 var actualDocIDs []string 204 for _, doc := range docs { 205 actualDocIDs = append(actualDocIDs, doc["_id"].(string)) 206 } 207 return actualDocIDs 208 } 209 210 func (s *upgradesSuite) getDocMap(c *gc.C, docID, collection string) (map[string]interface{}, error) { 211 docMap := map[string]interface{}{} 212 coll, closer := s.state.getRawCollection(collection) 213 defer closer() 214 err := coll.Find(bson.D{{"_id", docID}}).One(&docMap) 215 return docMap, err 216 } 217 218 func unsetField(st *State, id, collection, field string) error { 219 return st.runTransaction( 220 []txn.Op{{ 221 C: collection, 222 Id: id, 223 Update: bson.D{{"$unset", bson.D{{field, nil}}}}, 224 }, 225 }) 226 } 227 228 func setupMachineBoundStorageTests(c *gc.C, st *State) (*Machine, Volume, Filesystem, func() error) { 229 // Make an unprovisioned machine with storage for tests to use. 230 // TODO(axw) extend testing/factory to allow creating unprovisioned 231 // machines. 232 m, err := st.AddOneMachine(MachineTemplate{ 233 Series: "quantal", 234 Jobs: []MachineJob{JobHostUnits}, 235 Volumes: []MachineVolumeParams{ 236 {Volume: VolumeParams{Pool: "loop", Size: 2048}}, 237 }, 238 Filesystems: []MachineFilesystemParams{ 239 {Filesystem: FilesystemParams{Pool: "rootfs", Size: 2048}}, 240 }, 241 }) 242 c.Assert(err, jc.ErrorIsNil) 243 244 va, err := m.VolumeAttachments() 245 c.Assert(err, jc.ErrorIsNil) 246 c.Assert(va, gc.HasLen, 1) 247 v, err := st.Volume(va[0].Volume()) 248 c.Assert(err, jc.ErrorIsNil) 249 250 fa, err := st.MachineFilesystemAttachments(m.MachineTag()) 251 c.Assert(err, jc.ErrorIsNil) 252 c.Assert(fa, gc.HasLen, 1) 253 f, err := st.Filesystem(fa[0].Filesystem()) 254 c.Assert(err, jc.ErrorIsNil) 255 256 return m, v, f, m.Destroy 257 } 258 259 func (s *upgradesSuite) TestAddFilesystemStatus(c *gc.C) { 260 _, _, filesystem, cleanup := setupMachineBoundStorageTests(c, s.state) 261 defer cleanup() 262 263 removeStatusDoc(c, s.state, filesystem) 264 _, err := filesystem.Status() 265 c.Assert(err, jc.Satisfies, errors.IsNotFound) 266 s.assertAddFilesystemStatus(c, filesystem, status.Pending) 267 } 268 269 func (s *upgradesSuite) TestAddFilesystemStatusDoesNotOverwrite(c *gc.C) { 270 _, _, filesystem, cleanup := setupMachineBoundStorageTests(c, s.state) 271 defer cleanup() 272 273 now := testing.ZeroTime() 274 sInfo := status.StatusInfo{ 275 Status: status.Destroying, 276 Message: "", 277 Since: &now, 278 } 279 err := filesystem.SetStatus(sInfo) 280 c.Assert(err, jc.ErrorIsNil) 281 s.assertAddFilesystemStatus(c, filesystem, status.Destroying) 282 } 283 284 func (s *upgradesSuite) TestAddFilesystemStatusProvisioned(c *gc.C) { 285 _, _, filesystem, cleanup := setupMachineBoundStorageTests(c, s.state) 286 defer cleanup() 287 288 err := s.state.SetFilesystemInfo(filesystem.FilesystemTag(), FilesystemInfo{ 289 FilesystemId: "fs", 290 }) 291 c.Assert(err, jc.ErrorIsNil) 292 removeStatusDoc(c, s.state, filesystem) 293 s.assertAddFilesystemStatus(c, filesystem, status.Attaching) 294 } 295 296 func (s *upgradesSuite) TestAddFilesystemStatusAttached(c *gc.C) { 297 machine, _, filesystem, cleanup := setupMachineBoundStorageTests(c, s.state) 298 defer cleanup() 299 300 err := machine.SetProvisioned("fake", "fake", nil) 301 c.Assert(err, jc.ErrorIsNil) 302 303 err = s.state.SetFilesystemInfo(filesystem.FilesystemTag(), FilesystemInfo{ 304 FilesystemId: "fs", 305 }) 306 c.Assert(err, jc.ErrorIsNil) 307 308 err = s.state.SetFilesystemAttachmentInfo( 309 machine.MachineTag(), 310 filesystem.FilesystemTag(), 311 FilesystemAttachmentInfo{}, 312 ) 313 c.Assert(err, jc.ErrorIsNil) 314 315 removeStatusDoc(c, s.state, filesystem) 316 s.assertAddFilesystemStatus(c, filesystem, status.Attached) 317 } 318 319 func (s *upgradesSuite) assertAddFilesystemStatus(c *gc.C, filesystem Filesystem, expect status.Status) { 320 err := AddFilesystemStatus(s.state) 321 c.Assert(err, jc.ErrorIsNil) 322 323 info, err := filesystem.Status() 324 c.Assert(err, jc.ErrorIsNil) 325 c.Assert(info.Status, gc.Equals, expect) 326 } 327 328 func removeStatusDoc(c *gc.C, st *State, g GlobalEntity) { 329 op := removeStatusOp(st, g.globalKey()) 330 err := st.runTransaction([]txn.Op{op}) 331 c.Assert(err, jc.ErrorIsNil) 332 } 333 334 func (s *upgradesSuite) TestMigrateSettingsSchema(c *gc.C) { 335 // Insert test documents. 336 settingsColl, closer := s.state.getRawCollection(settingsC) 337 defer closer() 338 err := settingsColl.Insert( 339 bson.D{ 340 // Post-model-uuid migration, with no settings. 341 {"_id", "1"}, 342 {"model-uuid", "model-uuid"}, 343 {"txn-revno", int64(99)}, 344 {"txn-queue", []string{}}, 345 }, 346 bson.D{ 347 // Post-model-uuid migration, with settings. One 348 // of the settings is called "settings", and 349 // one "version". 350 {"_id", "2"}, 351 {"model-uuid", "model-uuid"}, 352 {"txn-revno", int64(99)}, 353 {"txn-queue", []string{}}, 354 {"settings", int64(123)}, 355 {"version", "onetwothree"}, 356 }, 357 bson.D{ 358 // Pre-model-uuid migration, with no settings. 359 {"_id", "3"}, 360 {"txn-revno", int64(99)}, 361 {"txn-queue", []string{}}, 362 }, 363 bson.D{ 364 // Pre-model-uuid migration, with settings. 365 {"_id", "4"}, 366 {"txn-revno", int64(99)}, 367 {"txn-queue", []string{}}, 368 {"settings", int64(123)}, 369 {"version", "onetwothree"}, 370 }, 371 bson.D{ 372 // Already migrated, with no settings. 373 {"_id", "5"}, 374 {"model-uuid", "model-uuid"}, 375 {"txn-revno", int64(99)}, 376 {"txn-queue", []string{}}, 377 {"version", int64(98)}, 378 {"settings", map[string]interface{}{}}, 379 }, 380 bson.D{ 381 // Already migrated, with settings. 382 {"_id", "6"}, 383 {"model-uuid", "model-uuid"}, 384 {"txn-revno", int64(99)}, 385 {"txn-queue", []string{}}, 386 {"version", int64(98)}, 387 {"settings", bson.D{ 388 {"settings", int64(123)}, 389 {"version", "onetwothree"}, 390 }}, 391 }, 392 ) 393 c.Assert(err, jc.ErrorIsNil) 394 395 // Expected docs, excluding txn-queu which we cannot predict. 396 expected := []bson.M{{ 397 "_id": "1", 398 "model-uuid": "model-uuid", 399 "txn-revno": int64(100), 400 "settings": bson.M{}, 401 "version": int64(99), 402 }, { 403 "_id": "2", 404 "model-uuid": "model-uuid", 405 "txn-revno": int64(101), 406 "settings": bson.M{ 407 "settings": int64(123), 408 "version": "onetwothree", 409 }, 410 "version": int64(99), 411 }, { 412 "_id": "3", 413 "txn-revno": int64(100), 414 "settings": bson.M{}, 415 "version": int64(99), 416 }, { 417 "_id": "4", 418 "txn-revno": int64(101), 419 "settings": bson.M{ 420 "settings": int64(123), 421 "version": "onetwothree", 422 }, 423 "version": int64(99), 424 }, { 425 "_id": "5", 426 "model-uuid": "model-uuid", 427 "txn-revno": int64(99), 428 "version": int64(98), 429 "settings": bson.M{}, 430 }, { 431 "_id": "6", 432 "model-uuid": "model-uuid", 433 "txn-revno": int64(99), 434 "version": int64(98), 435 "settings": bson.M{ 436 "settings": int64(123), 437 "version": "onetwothree", 438 }, 439 }} 440 441 // Two rounds to check idempotency. 442 for i := 0; i < 2; i++ { 443 err = MigrateSettingsSchema(s.state) 444 c.Assert(err, jc.ErrorIsNil) 445 446 var docs []bson.M 447 err = settingsColl.Find( 448 bson.D{{"model-uuid", bson.D{{"$ne", s.state.ModelUUID()}}}}, 449 ).Sort("_id").Select(bson.M{"txn-queue": 0}).All(&docs) 450 c.Assert(err, jc.ErrorIsNil) 451 c.Assert(docs, jc.DeepEquals, expected) 452 } 453 } 454 455 func (s *upgradesSuite) setupAddDefaultEndpointBindingsToServices(c *gc.C) []*Application { 456 // Add an owner user. 457 stateOwner, err := s.state.AddUser("bob", "notused", "notused", "bob") 458 c.Assert(err, jc.ErrorIsNil) 459 ownerTag := stateOwner.UserTag() 460 _, err = s.state.AddModelUser(s.state.ModelUUID(), UserAccessSpec{ 461 User: ownerTag, 462 CreatedBy: ownerTag, 463 DisplayName: "", 464 Access: permission.ReadAccess, 465 }) 466 c.Assert(err, jc.ErrorIsNil) 467 468 // Add a couple of test spaces 469 _, err = s.state.AddSpace("db", "", nil, false) 470 c.Assert(err, jc.ErrorIsNil) 471 _, err = s.state.AddSpace("apps", "", nil, true) 472 c.Assert(err, jc.ErrorIsNil) 473 474 // Add some testing charms for the services. 475 charms := []*Charm{ 476 AddTestingCharm(c, s.state, "wordpress"), 477 AddTestingCharm(c, s.state, "mysql"), 478 } 479 480 // Add a few services using the charms above: with no bindings, with just 481 // defaults, and with explicitly given bindings. For the first case we need 482 // to manually remove the added default bindings. 483 wpBindings := map[string]string{ 484 "db": "db", 485 "url": "apps", 486 } 487 msBindings := map[string]string{ 488 "server": "db", 489 } 490 services := []*Application{ 491 AddTestingService(c, s.state, "wp-no-bindings", charms[0]), 492 AddTestingService(c, s.state, "ms-no-bindings", charms[1]), 493 494 AddTestingService(c, s.state, "wp-default-bindings", charms[0]), 495 AddTestingService(c, s.state, "ms-default-bindings", charms[1]), 496 497 AddTestingServiceWithBindings(c, s.state, "wp-given-bindings", charms[0], wpBindings), 498 AddTestingServiceWithBindings(c, s.state, "ms-given-bindings", charms[1], msBindings), 499 } 500 501 // Drop the added endpoint bindings doc directly for the first two services. 502 ops := []txn.Op{ 503 removeEndpointBindingsOp(services[0].globalKey()), 504 removeEndpointBindingsOp(services[1].globalKey()), 505 } 506 err = s.state.runTransaction(ops) 507 c.Assert(err, jc.ErrorIsNil) 508 509 return services 510 } 511 512 func (s *upgradesSuite) getServicesBindings(c *gc.C, services []*Application) map[string]map[string]string { 513 currentBindings := make(map[string]map[string]string, len(services)) 514 for i := range services { 515 applicationname := services[i].Name() 516 serviceBindings, err := services[i].EndpointBindings() 517 if err != nil { 518 c.Fatalf("unexpected error getting service %q bindings: %v", applicationname, err) 519 } 520 currentBindings[applicationname] = serviceBindings 521 } 522 return currentBindings 523 } 524 525 func (s *upgradesSuite) testAddDefaultEndpointBindingsToServices(c *gc.C, runTwice bool) { 526 services := s.setupAddDefaultEndpointBindingsToServices(c) 527 initialBindings := s.getServicesBindings(c, services) 528 wpAllDefaults := map[string]string{ 529 // relation names 530 "url": "", 531 "logging-dir": "", 532 "monitoring-port": "", 533 "db": "", 534 "cache": "", 535 // extra-bindings 536 "db-client": "", 537 "admin-api": "", 538 "foo-bar": "", 539 } 540 msAllDefaults := map[string]string{ 541 "server": "", 542 } 543 expectedInitialAndFinal := map[string]map[string]string{ 544 "wp-no-bindings": wpAllDefaults, 545 "wp-default-bindings": wpAllDefaults, 546 "wp-given-bindings": map[string]string{ 547 "url": "apps", 548 "logging-dir": "", 549 "monitoring-port": "", 550 "db": "db", 551 "cache": "", 552 "db-client": "", 553 "admin-api": "", 554 "foo-bar": "", 555 }, 556 557 "ms-no-bindings": msAllDefaults, 558 "ms-default-bindings": msAllDefaults, 559 "ms-given-bindings": map[string]string{ 560 "server": "db", 561 }, 562 } 563 c.Assert(initialBindings, jc.DeepEquals, expectedInitialAndFinal) 564 565 assertFinalBindings := func() { 566 finalBindings := s.getServicesBindings(c, services) 567 c.Assert(finalBindings, jc.DeepEquals, expectedInitialAndFinal) 568 } 569 err := AddDefaultEndpointBindingsToServices(s.state) 570 c.Assert(err, jc.ErrorIsNil) 571 assertFinalBindings() 572 573 if runTwice { 574 err = AddDefaultEndpointBindingsToServices(s.state) 575 c.Assert(err, jc.ErrorIsNil, gc.Commentf("idempotency check failed!")) 576 assertFinalBindings() 577 } 578 } 579 580 func (s *upgradesSuite) TestAddDefaultEndpointBindingsToServices(c *gc.C) { 581 s.testAddDefaultEndpointBindingsToServices(c, false) 582 } 583 584 func (s *upgradesSuite) TestAddDefaultEndpointBindingsToServicesIdempotent(c *gc.C) { 585 s.testAddDefaultEndpointBindingsToServices(c, true) 586 }