github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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/status" 16 "github.com/juju/juju/storage/provider" 17 "github.com/juju/juju/storage/provider/registry" 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.IsNoAddress) 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.IsNoAddress) 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 registry.RegisterEnvironStorageProviders("someprovider", provider.LoopProviderType, provider.RootfsProviderType) 230 // Make an unprovisioned machine with storage for tests to use. 231 // TODO(axw) extend testing/factory to allow creating unprovisioned 232 // machines. 233 m, err := st.AddOneMachine(MachineTemplate{ 234 Series: "quantal", 235 Jobs: []MachineJob{JobHostUnits}, 236 Volumes: []MachineVolumeParams{ 237 {Volume: VolumeParams{Pool: "loop", Size: 2048}}, 238 }, 239 Filesystems: []MachineFilesystemParams{ 240 {Filesystem: FilesystemParams{Pool: "rootfs", Size: 2048}}, 241 }, 242 }) 243 c.Assert(err, jc.ErrorIsNil) 244 245 va, err := m.VolumeAttachments() 246 c.Assert(err, jc.ErrorIsNil) 247 c.Assert(va, gc.HasLen, 1) 248 v, err := st.Volume(va[0].Volume()) 249 c.Assert(err, jc.ErrorIsNil) 250 251 fa, err := st.MachineFilesystemAttachments(m.MachineTag()) 252 c.Assert(err, jc.ErrorIsNil) 253 c.Assert(fa, gc.HasLen, 1) 254 f, err := st.Filesystem(fa[0].Filesystem()) 255 c.Assert(err, jc.ErrorIsNil) 256 257 return m, v, f, m.Destroy 258 } 259 260 func (s *upgradesSuite) TestAddFilesystemStatus(c *gc.C) { 261 _, _, filesystem, cleanup := setupMachineBoundStorageTests(c, s.state) 262 defer cleanup() 263 264 removeStatusDoc(c, s.state, filesystem) 265 _, err := filesystem.Status() 266 c.Assert(err, jc.Satisfies, errors.IsNotFound) 267 s.assertAddFilesystemStatus(c, filesystem, status.StatusPending) 268 } 269 270 func (s *upgradesSuite) TestAddFilesystemStatusDoesNotOverwrite(c *gc.C) { 271 _, _, filesystem, cleanup := setupMachineBoundStorageTests(c, s.state) 272 defer cleanup() 273 274 err := filesystem.SetStatus(status.StatusDestroying, "", nil) 275 c.Assert(err, jc.ErrorIsNil) 276 s.assertAddFilesystemStatus(c, filesystem, status.StatusDestroying) 277 } 278 279 func (s *upgradesSuite) TestAddFilesystemStatusProvisioned(c *gc.C) { 280 _, _, filesystem, cleanup := setupMachineBoundStorageTests(c, s.state) 281 defer cleanup() 282 283 err := s.state.SetFilesystemInfo(filesystem.FilesystemTag(), FilesystemInfo{ 284 FilesystemId: "fs", 285 }) 286 c.Assert(err, jc.ErrorIsNil) 287 removeStatusDoc(c, s.state, filesystem) 288 s.assertAddFilesystemStatus(c, filesystem, status.StatusAttaching) 289 } 290 291 func (s *upgradesSuite) TestAddFilesystemStatusAttached(c *gc.C) { 292 machine, _, filesystem, cleanup := setupMachineBoundStorageTests(c, s.state) 293 defer cleanup() 294 295 err := machine.SetProvisioned("fake", "fake", nil) 296 c.Assert(err, jc.ErrorIsNil) 297 298 err = s.state.SetFilesystemInfo(filesystem.FilesystemTag(), FilesystemInfo{ 299 FilesystemId: "fs", 300 }) 301 c.Assert(err, jc.ErrorIsNil) 302 303 err = s.state.SetFilesystemAttachmentInfo( 304 machine.MachineTag(), 305 filesystem.FilesystemTag(), 306 FilesystemAttachmentInfo{}, 307 ) 308 c.Assert(err, jc.ErrorIsNil) 309 310 removeStatusDoc(c, s.state, filesystem) 311 s.assertAddFilesystemStatus(c, filesystem, status.StatusAttached) 312 } 313 314 func (s *upgradesSuite) assertAddFilesystemStatus(c *gc.C, filesystem Filesystem, expect status.Status) { 315 err := AddFilesystemStatus(s.state) 316 c.Assert(err, jc.ErrorIsNil) 317 318 info, err := filesystem.Status() 319 c.Assert(err, jc.ErrorIsNil) 320 c.Assert(info.Status, gc.Equals, expect) 321 } 322 323 func removeStatusDoc(c *gc.C, st *State, g GlobalEntity) { 324 op := removeStatusOp(st, g.globalKey()) 325 err := st.runTransaction([]txn.Op{op}) 326 c.Assert(err, jc.ErrorIsNil) 327 } 328 329 func (s *upgradesSuite) TestMigrateSettingsSchema(c *gc.C) { 330 // Insert test documents. 331 settingsColl, closer := s.state.getRawCollection(settingsC) 332 defer closer() 333 err := settingsColl.Insert( 334 bson.D{ 335 // Post-model-uuid migration, with no settings. 336 {"_id", "1"}, 337 {"model-uuid", "model-uuid"}, 338 {"txn-revno", int64(99)}, 339 {"txn-queue", []string{}}, 340 }, 341 bson.D{ 342 // Post-model-uuid migration, with settings. One 343 // of the settings is called "settings", and 344 // one "version". 345 {"_id", "2"}, 346 {"model-uuid", "model-uuid"}, 347 {"txn-revno", int64(99)}, 348 {"txn-queue", []string{}}, 349 {"settings", int64(123)}, 350 {"version", "onetwothree"}, 351 }, 352 bson.D{ 353 // Pre-model-uuid migration, with no settings. 354 {"_id", "3"}, 355 {"txn-revno", int64(99)}, 356 {"txn-queue", []string{}}, 357 }, 358 bson.D{ 359 // Pre-model-uuid migration, with settings. 360 {"_id", "4"}, 361 {"txn-revno", int64(99)}, 362 {"txn-queue", []string{}}, 363 {"settings", int64(123)}, 364 {"version", "onetwothree"}, 365 }, 366 bson.D{ 367 // Already migrated, with no settings. 368 {"_id", "5"}, 369 {"model-uuid", "model-uuid"}, 370 {"txn-revno", int64(99)}, 371 {"txn-queue", []string{}}, 372 {"version", int64(98)}, 373 {"settings", map[string]interface{}{}}, 374 }, 375 bson.D{ 376 // Already migrated, with settings. 377 {"_id", "6"}, 378 {"model-uuid", "model-uuid"}, 379 {"txn-revno", int64(99)}, 380 {"txn-queue", []string{}}, 381 {"version", int64(98)}, 382 {"settings", bson.D{ 383 {"settings", int64(123)}, 384 {"version", "onetwothree"}, 385 }}, 386 }, 387 ) 388 c.Assert(err, jc.ErrorIsNil) 389 390 // Expected docs, excluding txn-queu which we cannot predict. 391 expected := []bson.M{{ 392 "_id": "1", 393 "model-uuid": "model-uuid", 394 "txn-revno": int64(100), 395 "settings": bson.M{}, 396 "version": int64(99), 397 }, { 398 "_id": "2", 399 "model-uuid": "model-uuid", 400 "txn-revno": int64(101), 401 "settings": bson.M{ 402 "settings": int64(123), 403 "version": "onetwothree", 404 }, 405 "version": int64(99), 406 }, { 407 "_id": "3", 408 "txn-revno": int64(100), 409 "settings": bson.M{}, 410 "version": int64(99), 411 }, { 412 "_id": "4", 413 "txn-revno": int64(101), 414 "settings": bson.M{ 415 "settings": int64(123), 416 "version": "onetwothree", 417 }, 418 "version": int64(99), 419 }, { 420 "_id": "5", 421 "model-uuid": "model-uuid", 422 "txn-revno": int64(99), 423 "version": int64(98), 424 "settings": bson.M{}, 425 }, { 426 "_id": "6", 427 "model-uuid": "model-uuid", 428 "txn-revno": int64(99), 429 "version": int64(98), 430 "settings": bson.M{ 431 "settings": int64(123), 432 "version": "onetwothree", 433 }, 434 }} 435 436 // Two rounds to check idempotency. 437 for i := 0; i < 2; i++ { 438 err = MigrateSettingsSchema(s.state) 439 c.Assert(err, jc.ErrorIsNil) 440 441 var docs []bson.M 442 err = settingsColl.Find( 443 bson.D{{"model-uuid", bson.D{{"$ne", s.state.ModelUUID()}}}}, 444 ).Sort("_id").Select(bson.M{"txn-queue": 0}).All(&docs) 445 c.Assert(err, jc.ErrorIsNil) 446 c.Assert(docs, jc.DeepEquals, expected) 447 } 448 } 449 450 func (s *upgradesSuite) setupAddDefaultEndpointBindingsToServices(c *gc.C) []*Service { 451 // Add an owner user. 452 stateOwner, err := s.state.AddUser("bob", "notused", "notused", "bob") 453 c.Assert(err, jc.ErrorIsNil) 454 ownerTag := stateOwner.UserTag() 455 _, err = s.state.AddModelUser(ModelUserSpec{ 456 User: ownerTag, 457 CreatedBy: ownerTag, 458 DisplayName: "", 459 }) 460 c.Assert(err, jc.ErrorIsNil) 461 462 // Add a couple of test spaces 463 _, err = s.state.AddSpace("db", "", nil, false) 464 c.Assert(err, jc.ErrorIsNil) 465 _, err = s.state.AddSpace("apps", "", nil, true) 466 c.Assert(err, jc.ErrorIsNil) 467 468 // Add some testing charms for the services. 469 charms := []*Charm{ 470 AddTestingCharm(c, s.state, "wordpress"), 471 AddTestingCharm(c, s.state, "mysql"), 472 } 473 474 // Add a few services using the charms above: with no bindings, with just 475 // defaults, and with explicitly given bindings. For the first case we need 476 // to manually remove the added default bindings. 477 wpBindings := map[string]string{ 478 "db": "db", 479 "url": "apps", 480 } 481 msBindings := map[string]string{ 482 "server": "db", 483 } 484 services := []*Service{ 485 AddTestingService(c, s.state, "wp-no-bindings", charms[0], ownerTag), 486 AddTestingService(c, s.state, "ms-no-bindings", charms[1], ownerTag), 487 488 AddTestingService(c, s.state, "wp-default-bindings", charms[0], ownerTag), 489 AddTestingService(c, s.state, "ms-default-bindings", charms[1], ownerTag), 490 491 AddTestingServiceWithBindings(c, s.state, "wp-given-bindings", charms[0], ownerTag, wpBindings), 492 AddTestingServiceWithBindings(c, s.state, "ms-given-bindings", charms[1], ownerTag, msBindings), 493 } 494 495 // Drop the added endpoint bindings doc directly for the first two services. 496 ops := []txn.Op{ 497 removeEndpointBindingsOp(services[0].globalKey()), 498 removeEndpointBindingsOp(services[1].globalKey()), 499 } 500 err = s.state.runTransaction(ops) 501 c.Assert(err, jc.ErrorIsNil) 502 503 return services 504 } 505 506 func (s *upgradesSuite) getServicesBindings(c *gc.C, services []*Service) map[string]map[string]string { 507 currentBindings := make(map[string]map[string]string, len(services)) 508 for i := range services { 509 serviceName := services[i].Name() 510 serviceBindings, err := services[i].EndpointBindings() 511 if err != nil { 512 c.Fatalf("unexpected error getting service %q bindings: %v", serviceName, err) 513 } 514 currentBindings[serviceName] = serviceBindings 515 } 516 return currentBindings 517 } 518 519 func (s *upgradesSuite) testAddDefaultEndpointBindingsToServices(c *gc.C, runTwice bool) { 520 services := s.setupAddDefaultEndpointBindingsToServices(c) 521 initialBindings := s.getServicesBindings(c, services) 522 wpAllDefaults := map[string]string{ 523 // relation names 524 "url": "", 525 "logging-dir": "", 526 "monitoring-port": "", 527 "db": "", 528 "cache": "", 529 // extra-bindings 530 "db-client": "", 531 "admin-api": "", 532 "foo-bar": "", 533 } 534 msAllDefaults := map[string]string{ 535 "server": "", 536 } 537 expectedInitialAndFinal := map[string]map[string]string{ 538 "wp-no-bindings": wpAllDefaults, 539 "wp-default-bindings": wpAllDefaults, 540 "wp-given-bindings": map[string]string{ 541 "url": "apps", 542 "logging-dir": "", 543 "monitoring-port": "", 544 "db": "db", 545 "cache": "", 546 "db-client": "", 547 "admin-api": "", 548 "foo-bar": "", 549 }, 550 551 "ms-no-bindings": msAllDefaults, 552 "ms-default-bindings": msAllDefaults, 553 "ms-given-bindings": map[string]string{ 554 "server": "db", 555 }, 556 } 557 c.Assert(initialBindings, jc.DeepEquals, expectedInitialAndFinal) 558 559 assertFinalBindings := func() { 560 finalBindings := s.getServicesBindings(c, services) 561 c.Assert(finalBindings, jc.DeepEquals, expectedInitialAndFinal) 562 } 563 err := AddDefaultEndpointBindingsToServices(s.state) 564 c.Assert(err, jc.ErrorIsNil) 565 assertFinalBindings() 566 567 if runTwice { 568 err = AddDefaultEndpointBindingsToServices(s.state) 569 c.Assert(err, jc.ErrorIsNil, gc.Commentf("idempotency check failed!")) 570 assertFinalBindings() 571 } 572 } 573 574 func (s *upgradesSuite) TestAddDefaultEndpointBindingsToServices(c *gc.C) { 575 s.testAddDefaultEndpointBindingsToServices(c, false) 576 } 577 578 func (s *upgradesSuite) TestAddDefaultEndpointBindingsToServicesIdempotent(c *gc.C) { 579 s.testAddDefaultEndpointBindingsToServices(c, true) 580 }