github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/migration_import_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state_test 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 "github.com/juju/names" 11 jc "github.com/juju/testing/checkers" 12 "github.com/juju/utils" 13 "github.com/juju/version" 14 gc "gopkg.in/check.v1" 15 16 "github.com/juju/juju/constraints" 17 "github.com/juju/juju/core/description" 18 "github.com/juju/juju/network" 19 "github.com/juju/juju/state" 20 "github.com/juju/juju/status" 21 "github.com/juju/juju/testing/factory" 22 ) 23 24 type MigrationImportSuite struct { 25 MigrationSuite 26 } 27 28 var _ = gc.Suite(&MigrationImportSuite{}) 29 30 func (s *MigrationImportSuite) checkStatusHistory(c *gc.C, exported, imported status.StatusHistoryGetter, size int) { 31 exportedHistory, err := exported.StatusHistory(size) 32 c.Assert(err, jc.ErrorIsNil) 33 importedHistory, err := imported.StatusHistory(size) 34 c.Assert(err, jc.ErrorIsNil) 35 for i := 0; i < size; i++ { 36 c.Check(importedHistory[i].Status, gc.Equals, exportedHistory[i].Status) 37 c.Check(importedHistory[i].Message, gc.Equals, exportedHistory[i].Message) 38 c.Check(importedHistory[i].Data, jc.DeepEquals, exportedHistory[i].Data) 39 c.Check(importedHistory[i].Since, jc.DeepEquals, exportedHistory[i].Since) 40 } 41 } 42 43 func (s *MigrationImportSuite) TestExisting(c *gc.C) { 44 out, err := s.State.Export() 45 c.Assert(err, jc.ErrorIsNil) 46 47 _, _, err = s.State.Import(out) 48 c.Assert(err, jc.Satisfies, errors.IsAlreadyExists) 49 } 50 51 func (s *MigrationImportSuite) importModel(c *gc.C) (*state.Model, *state.State) { 52 out, err := s.State.Export() 53 c.Assert(err, jc.ErrorIsNil) 54 55 uuid := utils.MustNewUUID().String() 56 in := newModel(out, uuid, "new") 57 58 newModel, newSt, err := s.State.Import(in) 59 c.Assert(err, jc.ErrorIsNil) 60 return newModel, newSt 61 } 62 63 func (s *MigrationImportSuite) assertAnnotations(c *gc.C, newSt *state.State, entity state.GlobalEntity) { 64 annotations, err := newSt.Annotations(entity) 65 c.Assert(err, jc.ErrorIsNil) 66 c.Assert(annotations, jc.DeepEquals, testAnnotations) 67 } 68 69 func (s *MigrationImportSuite) TestNewModel(c *gc.C) { 70 cons := constraints.MustParse("arch=amd64 mem=8G") 71 latestTools := version.MustParse("2.0.1") 72 s.setLatestTools(c, latestTools) 73 c.Assert(s.State.SetModelConstraints(cons), jc.ErrorIsNil) 74 machineSeq := s.setRandSequenceValue(c, "machine") 75 fooSeq := s.setRandSequenceValue(c, "service-foo") 76 s.State.SwitchBlockOn(state.ChangeBlock, "locked down") 77 78 original, err := s.State.Model() 79 c.Assert(err, jc.ErrorIsNil) 80 81 err = s.State.SetAnnotations(original, testAnnotations) 82 c.Assert(err, jc.ErrorIsNil) 83 84 out, err := s.State.Export() 85 c.Assert(err, jc.ErrorIsNil) 86 87 uuid := utils.MustNewUUID().String() 88 in := newModel(out, uuid, "new") 89 90 newModel, newSt, err := s.State.Import(in) 91 c.Assert(err, jc.ErrorIsNil) 92 defer newSt.Close() 93 94 c.Assert(newModel.Owner(), gc.Equals, original.Owner()) 95 c.Assert(newModel.LatestToolsVersion(), gc.Equals, latestTools) 96 c.Assert(newModel.MigrationMode(), gc.Equals, state.MigrationModeImporting) 97 s.assertAnnotations(c, newSt, newModel) 98 99 originalConfig, err := original.Config() 100 c.Assert(err, jc.ErrorIsNil) 101 originalAttrs := originalConfig.AllAttrs() 102 103 newConfig, err := newModel.Config() 104 c.Assert(err, jc.ErrorIsNil) 105 newAttrs := newConfig.AllAttrs() 106 107 c.Assert(newAttrs["uuid"], gc.Equals, uuid) 108 c.Assert(newAttrs["name"], gc.Equals, "new") 109 110 // Now drop the uuid and name and the rest of the attributes should match. 111 delete(newAttrs, "uuid") 112 delete(newAttrs, "name") 113 delete(originalAttrs, "uuid") 114 delete(originalAttrs, "name") 115 c.Assert(newAttrs, jc.DeepEquals, originalAttrs) 116 117 newCons, err := newSt.ModelConstraints() 118 c.Assert(err, jc.ErrorIsNil) 119 // Can't test the constraints directly, so go through the string repr. 120 c.Assert(newCons.String(), gc.Equals, cons.String()) 121 122 seq, err := state.Sequence(newSt, "machine") 123 c.Assert(err, jc.ErrorIsNil) 124 c.Assert(seq, gc.Equals, machineSeq) 125 seq, err = state.Sequence(newSt, "service-foo") 126 c.Assert(err, jc.ErrorIsNil) 127 c.Assert(seq, gc.Equals, fooSeq) 128 129 blocks, err := newSt.AllBlocks() 130 c.Assert(err, jc.ErrorIsNil) 131 c.Assert(blocks, gc.HasLen, 1) 132 c.Assert(blocks[0].Type(), gc.Equals, state.ChangeBlock) 133 c.Assert(blocks[0].Message(), gc.Equals, "locked down") 134 } 135 136 func (s *MigrationImportSuite) newModelUser(c *gc.C, name string, readOnly bool, lastConnection time.Time) *state.ModelUser { 137 access := state.ModelAdminAccess 138 if readOnly { 139 access = state.ModelReadAccess 140 } 141 user, err := s.State.AddModelUser(state.ModelUserSpec{ 142 User: names.NewUserTag(name), 143 CreatedBy: s.Owner, 144 Access: access, 145 }) 146 c.Assert(err, jc.ErrorIsNil) 147 if !lastConnection.IsZero() { 148 err = state.UpdateModelUserLastConnection(user, lastConnection) 149 c.Assert(err, jc.ErrorIsNil) 150 } 151 return user 152 } 153 154 func (s *MigrationImportSuite) AssertUserEqual(c *gc.C, newUser, oldUser *state.ModelUser) { 155 c.Assert(newUser.UserName(), gc.Equals, oldUser.UserName()) 156 c.Assert(newUser.DisplayName(), gc.Equals, oldUser.DisplayName()) 157 c.Assert(newUser.CreatedBy(), gc.Equals, oldUser.CreatedBy()) 158 c.Assert(newUser.DateCreated(), gc.Equals, oldUser.DateCreated()) 159 c.Assert(newUser.ReadOnly(), gc.Equals, oldUser.ReadOnly()) 160 161 connTime, err := oldUser.LastConnection() 162 if state.IsNeverConnectedError(err) { 163 _, err := newUser.LastConnection() 164 // The new user should also return an error for last connection. 165 c.Assert(err, jc.Satisfies, state.IsNeverConnectedError) 166 } else { 167 c.Assert(err, jc.ErrorIsNil) 168 newTime, err := newUser.LastConnection() 169 c.Assert(err, jc.ErrorIsNil) 170 c.Assert(newTime, gc.Equals, connTime) 171 } 172 } 173 174 func (s *MigrationImportSuite) TestModelUsers(c *gc.C) { 175 // To be sure with this test, we create three env users, and remove 176 // the owner. 177 err := s.State.RemoveModelUser(s.Owner) 178 c.Assert(err, jc.ErrorIsNil) 179 180 lastConnection := state.NowToTheSecond() 181 182 bravo := s.newModelUser(c, "bravo@external", false, lastConnection) 183 charlie := s.newModelUser(c, "charlie@external", true, lastConnection) 184 delta := s.newModelUser(c, "delta@external", true, time.Time{}) 185 186 newModel, newSt := s.importModel(c) 187 defer newSt.Close() 188 189 // Check the import values of the users. 190 for _, user := range []*state.ModelUser{bravo, charlie, delta} { 191 newUser, err := newSt.ModelUser(user.UserTag()) 192 c.Assert(err, jc.ErrorIsNil) 193 s.AssertUserEqual(c, newUser, user) 194 } 195 196 // Also make sure that there aren't any more. 197 allUsers, err := newModel.Users() 198 c.Assert(err, jc.ErrorIsNil) 199 c.Assert(allUsers, gc.HasLen, 3) 200 } 201 202 func (s *MigrationImportSuite) AssertMachineEqual(c *gc.C, newMachine, oldMachine *state.Machine) { 203 c.Assert(newMachine.Id(), gc.Equals, oldMachine.Id()) 204 c.Assert(newMachine.Principals(), jc.DeepEquals, oldMachine.Principals()) 205 c.Assert(newMachine.Series(), gc.Equals, oldMachine.Series()) 206 c.Assert(newMachine.ContainerType(), gc.Equals, oldMachine.ContainerType()) 207 newHardware, err := newMachine.HardwareCharacteristics() 208 c.Assert(err, jc.ErrorIsNil) 209 oldHardware, err := oldMachine.HardwareCharacteristics() 210 c.Assert(err, jc.ErrorIsNil) 211 c.Assert(newHardware, jc.DeepEquals, oldHardware) 212 c.Assert(newMachine.Jobs(), jc.DeepEquals, oldMachine.Jobs()) 213 c.Assert(newMachine.Life(), gc.Equals, oldMachine.Life()) 214 newTools, err := newMachine.AgentTools() 215 c.Assert(err, jc.ErrorIsNil) 216 oldTools, err := oldMachine.AgentTools() 217 c.Assert(err, jc.ErrorIsNil) 218 c.Assert(newTools, jc.DeepEquals, oldTools) 219 } 220 221 func (s *MigrationImportSuite) TestMachines(c *gc.C) { 222 // Let's add a machine with an LXC container. 223 cons := constraints.MustParse("arch=amd64 mem=8G") 224 machine1 := s.Factory.MakeMachine(c, &factory.MachineParams{ 225 Constraints: cons, 226 }) 227 err := s.State.SetAnnotations(machine1, testAnnotations) 228 c.Assert(err, jc.ErrorIsNil) 229 s.primeStatusHistory(c, machine1, status.StatusStarted, 5) 230 231 // machine1 should have some instance data. 232 hardware, err := machine1.HardwareCharacteristics() 233 c.Assert(err, jc.ErrorIsNil) 234 c.Assert(hardware, gc.NotNil) 235 236 _ = s.Factory.MakeMachineNested(c, machine1.Id(), nil) 237 238 allMachines, err := s.State.AllMachines() 239 c.Assert(err, jc.ErrorIsNil) 240 c.Assert(allMachines, gc.HasLen, 2) 241 242 _, newSt := s.importModel(c) 243 defer newSt.Close() 244 245 importedMachines, err := newSt.AllMachines() 246 c.Assert(err, jc.ErrorIsNil) 247 c.Assert(importedMachines, gc.HasLen, 2) 248 249 // AllMachines returns the machines in the same order, yay us. 250 for i, newMachine := range importedMachines { 251 s.AssertMachineEqual(c, newMachine, allMachines[i]) 252 } 253 254 // And a few extra checks. 255 parent := importedMachines[0] 256 container := importedMachines[1] 257 containers, err := parent.Containers() 258 c.Assert(err, jc.ErrorIsNil) 259 c.Assert(containers, jc.DeepEquals, []string{container.Id()}) 260 parentId, isContainer := container.ParentId() 261 c.Assert(parentId, gc.Equals, parent.Id()) 262 c.Assert(isContainer, jc.IsTrue) 263 264 s.assertAnnotations(c, newSt, parent) 265 s.checkStatusHistory(c, machine1, parent, 5) 266 267 newCons, err := parent.Constraints() 268 c.Assert(err, jc.ErrorIsNil) 269 // Can't test the constraints directly, so go through the string repr. 270 c.Assert(newCons.String(), gc.Equals, cons.String()) 271 } 272 273 func (s *MigrationImportSuite) TestServices(c *gc.C) { 274 // Add a service with both settings and leadership settings. 275 cons := constraints.MustParse("arch=amd64 mem=8G") 276 service := s.Factory.MakeService(c, &factory.ServiceParams{ 277 Settings: map[string]interface{}{ 278 "foo": "bar", 279 }, 280 Constraints: cons, 281 }) 282 err := service.UpdateLeaderSettings(&goodToken{}, map[string]string{ 283 "leader": "true", 284 }) 285 c.Assert(err, jc.ErrorIsNil) 286 err = service.SetMetricCredentials([]byte("sekrit")) 287 c.Assert(err, jc.ErrorIsNil) 288 // Expose the service. 289 c.Assert(service.SetExposed(), jc.ErrorIsNil) 290 err = s.State.SetAnnotations(service, testAnnotations) 291 c.Assert(err, jc.ErrorIsNil) 292 s.primeStatusHistory(c, service, status.StatusActive, 5) 293 294 allServices, err := s.State.AllServices() 295 c.Assert(err, jc.ErrorIsNil) 296 c.Assert(allServices, gc.HasLen, 1) 297 298 _, newSt := s.importModel(c) 299 defer newSt.Close() 300 301 importedServices, err := newSt.AllServices() 302 c.Assert(err, jc.ErrorIsNil) 303 c.Assert(importedServices, gc.HasLen, 1) 304 305 exported := allServices[0] 306 imported := importedServices[0] 307 308 c.Assert(imported.ServiceTag(), gc.Equals, exported.ServiceTag()) 309 c.Assert(imported.Series(), gc.Equals, exported.Series()) 310 c.Assert(imported.IsExposed(), gc.Equals, exported.IsExposed()) 311 c.Assert(imported.MetricCredentials(), jc.DeepEquals, exported.MetricCredentials()) 312 313 exportedConfig, err := exported.ConfigSettings() 314 c.Assert(err, jc.ErrorIsNil) 315 importedConfig, err := imported.ConfigSettings() 316 c.Assert(err, jc.ErrorIsNil) 317 c.Assert(importedConfig, jc.DeepEquals, exportedConfig) 318 319 exportedLeaderSettings, err := exported.LeaderSettings() 320 c.Assert(err, jc.ErrorIsNil) 321 importedLeaderSettings, err := imported.LeaderSettings() 322 c.Assert(err, jc.ErrorIsNil) 323 c.Assert(importedLeaderSettings, jc.DeepEquals, exportedLeaderSettings) 324 325 s.assertAnnotations(c, newSt, imported) 326 s.checkStatusHistory(c, service, imported, 5) 327 328 newCons, err := imported.Constraints() 329 c.Assert(err, jc.ErrorIsNil) 330 // Can't test the constraints directly, so go through the string repr. 331 c.Assert(newCons.String(), gc.Equals, cons.String()) 332 } 333 334 func (s *MigrationImportSuite) TestServiceLeaders(c *gc.C) { 335 s.makeServiceWithLeader(c, "mysql", 2, 1) 336 s.makeServiceWithLeader(c, "wordpress", 4, 2) 337 338 _, newSt := s.importModel(c) 339 defer newSt.Close() 340 341 leaders := make(map[string]string) 342 for key, value := range state.LeadershipLeases(newSt) { 343 leaders[key] = value.Holder 344 } 345 c.Assert(leaders, jc.DeepEquals, map[string]string{ 346 "mysql": "mysql/1", 347 "wordpress": "wordpress/2", 348 }) 349 } 350 351 func (s *MigrationImportSuite) TestUnits(c *gc.C) { 352 cons := constraints.MustParse("arch=amd64 mem=8G") 353 exported, pwd := s.Factory.MakeUnitReturningPassword(c, &factory.UnitParams{ 354 Constraints: cons, 355 }) 356 err := exported.SetMeterStatus("GREEN", "some info") 357 c.Assert(err, jc.ErrorIsNil) 358 err = s.State.SetAnnotations(exported, testAnnotations) 359 c.Assert(err, jc.ErrorIsNil) 360 s.primeStatusHistory(c, exported, status.StatusActive, 5) 361 s.primeStatusHistory(c, exported.Agent(), status.StatusIdle, 5) 362 363 _, newSt := s.importModel(c) 364 defer newSt.Close() 365 366 importedServices, err := newSt.AllServices() 367 c.Assert(err, jc.ErrorIsNil) 368 c.Assert(importedServices, gc.HasLen, 1) 369 370 importedUnits, err := importedServices[0].AllUnits() 371 c.Assert(err, jc.ErrorIsNil) 372 c.Assert(importedUnits, gc.HasLen, 1) 373 imported := importedUnits[0] 374 375 c.Assert(imported.UnitTag(), gc.Equals, exported.UnitTag()) 376 c.Assert(imported.PasswordValid(pwd), jc.IsTrue) 377 378 exportedMachineId, err := exported.AssignedMachineId() 379 c.Assert(err, jc.ErrorIsNil) 380 importedMachineId, err := imported.AssignedMachineId() 381 c.Assert(err, jc.ErrorIsNil) 382 c.Assert(importedMachineId, gc.Equals, exportedMachineId) 383 384 // Confirm machine Principals are set. 385 exportedMachine, err := s.State.Machine(exportedMachineId) 386 c.Assert(err, jc.ErrorIsNil) 387 importedMachine, err := newSt.Machine(importedMachineId) 388 c.Assert(err, jc.ErrorIsNil) 389 s.AssertMachineEqual(c, importedMachine, exportedMachine) 390 391 meterStatus, err := imported.GetMeterStatus() 392 c.Assert(err, jc.ErrorIsNil) 393 c.Assert(meterStatus, gc.Equals, state.MeterStatus{state.MeterGreen, "some info"}) 394 s.assertAnnotations(c, newSt, imported) 395 s.checkStatusHistory(c, exported, imported, 5) 396 s.checkStatusHistory(c, exported.Agent(), imported.Agent(), 5) 397 398 newCons, err := imported.Constraints() 399 c.Assert(err, jc.ErrorIsNil) 400 // Can't test the constraints directly, so go through the string repr. 401 c.Assert(newCons.String(), gc.Equals, cons.String()) 402 } 403 404 func (s *MigrationImportSuite) TestRelations(c *gc.C) { 405 // Need to remove owner from service. 406 ignored := s.Owner 407 wordpress := state.AddTestingService(c, s.State, "wordpress", state.AddTestingCharm(c, s.State, "wordpress"), ignored) 408 state.AddTestingService(c, s.State, "mysql", state.AddTestingCharm(c, s.State, "mysql"), ignored) 409 eps, err := s.State.InferEndpoints("mysql", "wordpress") 410 c.Assert(err, jc.ErrorIsNil) 411 rel, err := s.State.AddRelation(eps...) 412 c.Assert(err, jc.ErrorIsNil) 413 wordpress_0 := s.Factory.MakeUnit(c, &factory.UnitParams{Service: wordpress}) 414 415 ru, err := rel.Unit(wordpress_0) 416 c.Assert(err, jc.ErrorIsNil) 417 relSettings := map[string]interface{}{ 418 "name": "wordpress/0", 419 } 420 err = ru.EnterScope(relSettings) 421 c.Assert(err, jc.ErrorIsNil) 422 423 _, newSt := s.importModel(c) 424 defer newSt.Close() 425 426 newWordpress, err := newSt.Service("wordpress") 427 c.Assert(err, jc.ErrorIsNil) 428 c.Assert(state.RelationCount(newWordpress), gc.Equals, 1) 429 rels, err := newWordpress.Relations() 430 c.Assert(err, jc.ErrorIsNil) 431 c.Assert(rels, gc.HasLen, 1) 432 units, err := newWordpress.AllUnits() 433 c.Assert(err, jc.ErrorIsNil) 434 c.Assert(units, gc.HasLen, 1) 435 436 ru, err = rels[0].Unit(units[0]) 437 c.Assert(err, jc.ErrorIsNil) 438 439 settings, err := ru.Settings() 440 c.Assert(err, jc.ErrorIsNil) 441 c.Assert(settings.Map(), gc.DeepEquals, relSettings) 442 } 443 444 func (s *MigrationImportSuite) TestUnitsOpenPorts(c *gc.C) { 445 unit := s.Factory.MakeUnit(c, nil) 446 err := unit.OpenPorts("tcp", 1234, 2345) 447 c.Assert(err, jc.ErrorIsNil) 448 449 _, newSt := s.importModel(c) 450 defer newSt.Close() 451 452 // Even though the opened ports document is stored with the 453 // machine, the only way to easily access it is through the units. 454 imported, err := newSt.Unit(unit.Name()) 455 c.Assert(err, jc.ErrorIsNil) 456 457 ports, err := imported.OpenedPorts() 458 c.Assert(err, jc.ErrorIsNil) 459 c.Assert(ports, gc.HasLen, 1) 460 c.Assert(ports[0], gc.Equals, network.PortRange{ 461 FromPort: 1234, 462 ToPort: 2345, 463 Protocol: "tcp", 464 }) 465 } 466 467 func (s *MigrationImportSuite) TestDestroyEmptyModel(c *gc.C) { 468 newModel, newSt := s.importModel(c) 469 defer newSt.Close() 470 s.assertDestroyModelAdvancesLife(c, newModel, state.Dead) 471 } 472 473 func (s *MigrationImportSuite) TestDestroyModelWithMachine(c *gc.C) { 474 s.Factory.MakeMachine(c, nil) 475 newModel, newSt := s.importModel(c) 476 defer newSt.Close() 477 s.assertDestroyModelAdvancesLife(c, newModel, state.Dying) 478 } 479 480 func (s *MigrationImportSuite) TestDestroyModelWithService(c *gc.C) { 481 s.Factory.MakeService(c, nil) 482 newModel, newSt := s.importModel(c) 483 defer newSt.Close() 484 s.assertDestroyModelAdvancesLife(c, newModel, state.Dying) 485 } 486 487 func (s *MigrationImportSuite) assertDestroyModelAdvancesLife(c *gc.C, m *state.Model, life state.Life) { 488 err := m.Destroy() 489 c.Assert(err, jc.ErrorIsNil) 490 err = m.Refresh() 491 c.Assert(err, jc.ErrorIsNil) 492 c.Assert(m.Life(), gc.Equals, life) 493 } 494 495 // newModel replaces the uuid and name of the config attributes so we 496 // can use all the other data to validate imports. An owner and name of the 497 // model are unique together in a controller. 498 func newModel(m description.Model, uuid, name string) description.Model { 499 return &mockModel{m, uuid, name} 500 } 501 502 type mockModel struct { 503 description.Model 504 uuid string 505 name string 506 } 507 508 func (m *mockModel) Tag() names.ModelTag { 509 return names.NewModelTag(m.uuid) 510 } 511 512 func (m *mockModel) Config() map[string]interface{} { 513 c := m.Model.Config() 514 c["uuid"] = m.uuid 515 c["name"] = m.name 516 return c 517 }