github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/migration/precheck_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package migration_test 5 6 import ( 7 "github.com/juju/errors" 8 jc "github.com/juju/testing/checkers" 9 "github.com/juju/version" 10 gc "gopkg.in/check.v1" 11 "gopkg.in/juju/charm.v6" 12 "gopkg.in/juju/names.v2" 13 14 coremigration "github.com/juju/juju/core/migration" 15 "github.com/juju/juju/core/presence" 16 "github.com/juju/juju/core/status" 17 "github.com/juju/juju/migration" 18 "github.com/juju/juju/resource" 19 "github.com/juju/juju/resource/resourcetesting" 20 "github.com/juju/juju/state" 21 "github.com/juju/juju/testing" 22 "github.com/juju/juju/tools" 23 ) 24 25 var ( 26 modelName = "model-name" 27 modelUUID = "model-uuid" 28 modelOwner = names.NewUserTag("owner") 29 backendVersionBinary = version.MustParseBinary("1.2.3-trusty-amd64") 30 backendVersion = backendVersionBinary.Number 31 ) 32 33 type SourcePrecheckSuite struct { 34 precheckBaseSuite 35 } 36 37 var _ = gc.Suite(&SourcePrecheckSuite{}) 38 39 func sourcePrecheck(backend migration.PrecheckBackend) error { 40 return migration.SourcePrecheck(backend, allAlivePresence(), allAlivePresence()) 41 } 42 43 func (*SourcePrecheckSuite) TestSuccess(c *gc.C) { 44 backend := newHappyBackend() 45 backend.controllerBackend = newHappyBackend() 46 err := migration.SourcePrecheck(backend, allAlivePresence(), allAlivePresence()) 47 c.Assert(err, jc.ErrorIsNil) 48 } 49 50 func (*SourcePrecheckSuite) TestDyingModel(c *gc.C) { 51 backend := newFakeBackend() 52 backend.model.life = state.Dying 53 err := sourcePrecheck(backend) 54 c.Assert(err, gc.ErrorMatches, "model is dying") 55 } 56 57 func (*SourcePrecheckSuite) TestCharmUpgrades(c *gc.C) { 58 backend := &fakeBackend{ 59 apps: []migration.PrecheckApplication{ 60 &fakeApp{ 61 name: "spanner", 62 charmURL: "cs:spanner-3", 63 units: []migration.PrecheckUnit{ 64 &fakeUnit{name: "spanner/0", charmURL: "cs:spanner-3"}, 65 &fakeUnit{name: "spanner/1", charmURL: "cs:spanner-2"}, 66 }, 67 }, 68 }, 69 } 70 err := sourcePrecheck(backend) 71 c.Assert(err, gc.ErrorMatches, "unit spanner/1 is upgrading") 72 } 73 74 func (*SourcePrecheckSuite) TestPendingResources(c *gc.C) { 75 backend := newHappyBackend() 76 backend.pendingResources = []resource.Resource{ 77 resourcetesting.NewResource(c, nil, "blob", "foo", "body").Resource, 78 } 79 err := sourcePrecheck(backend) 80 // Pending resources shouldn't prevent a migration. If they exist 81 // alongside an application, they're remains of a previous failed 82 // deploy that haven't been cleaned up (see lp:1705730). If they 83 // exist without an application that indicates an impending 84 // application deployment - the migration exporter won't migrate 85 // pending resources. 86 c.Assert(err, jc.ErrorIsNil) 87 } 88 89 func (*SourcePrecheckSuite) TestImportingModel(c *gc.C) { 90 backend := newFakeBackend() 91 backend.model.migrationMode = state.MigrationModeImporting 92 err := sourcePrecheck(backend) 93 c.Assert(err, gc.ErrorMatches, "model is being imported as part of another migration") 94 } 95 96 func (*SourcePrecheckSuite) TestCleanupsError(c *gc.C) { 97 backend := newFakeBackend() 98 backend.cleanupErr = errors.New("boom") 99 err := sourcePrecheck(backend) 100 c.Assert(err, gc.ErrorMatches, "checking cleanups: boom") 101 } 102 103 func (*SourcePrecheckSuite) TestCleanupsNeeded(c *gc.C) { 104 backend := newFakeBackend() 105 backend.cleanupNeeded = true 106 err := sourcePrecheck(backend) 107 c.Assert(err, gc.ErrorMatches, "cleanup needed") 108 } 109 110 func (s *SourcePrecheckSuite) TestIsUpgradingError(c *gc.C) { 111 backend := newFakeBackend() 112 backend.controllerBackend.isUpgradingErr = errors.New("boom") 113 err := sourcePrecheck(backend) 114 c.Assert(err, gc.ErrorMatches, "controller: checking for upgrades: boom") 115 } 116 117 func (s *SourcePrecheckSuite) TestIsUpgrading(c *gc.C) { 118 backend := newFakeBackend() 119 backend.controllerBackend.isUpgrading = true 120 err := sourcePrecheck(backend) 121 c.Assert(err, gc.ErrorMatches, "controller: upgrade in progress") 122 } 123 124 func (s *SourcePrecheckSuite) TestAgentVersionError(c *gc.C) { 125 s.checkAgentVersionError(c, sourcePrecheck) 126 } 127 128 func (s *SourcePrecheckSuite) TestMachineRequiresReboot(c *gc.C) { 129 s.checkRebootRequired(c, sourcePrecheck) 130 } 131 132 func (s *SourcePrecheckSuite) TestMachineVersionsDontMatch(c *gc.C) { 133 s.checkMachineVersionsDontMatch(c, sourcePrecheck) 134 } 135 136 func (s *SourcePrecheckSuite) TestDyingMachine(c *gc.C) { 137 backend := newBackendWithDyingMachine() 138 err := sourcePrecheck(backend) 139 c.Assert(err, gc.ErrorMatches, "machine 0 is dying") 140 } 141 142 func (s *SourcePrecheckSuite) TestNonStartedMachine(c *gc.C) { 143 backend := newBackendWithDownMachine() 144 err := sourcePrecheck(backend) 145 c.Assert(err.Error(), gc.Equals, "machine 0 agent not functioning at this time (down)") 146 } 147 148 func (s *SourcePrecheckSuite) TestProvisioningMachine(c *gc.C) { 149 err := sourcePrecheck(newBackendWithProvisioningMachine()) 150 c.Assert(err.Error(), gc.Equals, "machine 0 not running (allocating)") 151 } 152 153 func (s *SourcePrecheckSuite) TestDownMachineAgentLegacy(c *gc.C) { 154 err := migration.SourcePrecheck(newBackendWithDownMachineAgent(), nil, nil) 155 c.Assert(err.Error(), gc.Equals, "machine 1 agent not functioning at this time (down)") 156 } 157 158 func (s *SourcePrecheckSuite) TestDownMachineAgent(c *gc.C) { 159 backend := newHappyBackend() 160 modelPresence := downAgentPresence("machine-1") 161 controllerPresence := allAlivePresence() 162 err := migration.SourcePrecheck(backend, modelPresence, controllerPresence) 163 c.Assert(err.Error(), gc.Equals, "machine 1 agent not functioning at this time (down)") 164 } 165 166 func (s *SourcePrecheckSuite) TestDyingApplication(c *gc.C) { 167 backend := &fakeBackend{ 168 apps: []migration.PrecheckApplication{ 169 &fakeApp{ 170 name: "foo", 171 life: state.Dying, 172 }, 173 }, 174 } 175 err := sourcePrecheck(backend) 176 c.Assert(err.Error(), gc.Equals, "application foo is dying") 177 } 178 179 func (s *SourcePrecheckSuite) TestWithPendingMinUnits(c *gc.C) { 180 backend := &fakeBackend{ 181 apps: []migration.PrecheckApplication{ 182 &fakeApp{ 183 name: "foo", 184 minunits: 2, 185 units: []migration.PrecheckUnit{&fakeUnit{name: "foo/0"}}, 186 }, 187 }, 188 } 189 err := sourcePrecheck(backend) 190 c.Assert(err.Error(), gc.Equals, "application foo is below its minimum units threshold") 191 } 192 193 func (s *SourcePrecheckSuite) TestUnitVersionsDontMatch(c *gc.C) { 194 backend := &fakeBackend{ 195 model: fakeModel{modelType: state.ModelTypeIAAS}, 196 apps: []migration.PrecheckApplication{ 197 &fakeApp{ 198 name: "foo", 199 units: []migration.PrecheckUnit{&fakeUnit{name: "foo/0"}}, 200 }, 201 &fakeApp{ 202 name: "bar", 203 units: []migration.PrecheckUnit{ 204 &fakeUnit{name: "bar/0"}, 205 &fakeUnit{name: "bar/1", version: version.MustParseBinary("1.2.4-trusty-ppc64")}, 206 }, 207 }, 208 }, 209 } 210 err := sourcePrecheck(backend) 211 c.Assert(err.Error(), gc.Equals, "unit bar/1 agent binaries don't match model (1.2.4 != 1.2.3)") 212 } 213 214 func (s *SourcePrecheckSuite) TestCAASModelNoUnitVersionCheck(c *gc.C) { 215 backend := &fakeBackend{ 216 model: fakeModel{modelType: state.ModelTypeCAAS}, 217 apps: []migration.PrecheckApplication{ 218 &fakeApp{ 219 name: "foo", 220 units: []migration.PrecheckUnit{&fakeUnit{name: "foo/0", noTools: true}}, 221 }, 222 }, 223 } 224 err := sourcePrecheck(backend) 225 c.Assert(err, jc.ErrorIsNil) 226 } 227 228 func (s *SourcePrecheckSuite) TestDeadUnit(c *gc.C) { 229 backend := &fakeBackend{ 230 apps: []migration.PrecheckApplication{ 231 &fakeApp{ 232 name: "foo", 233 units: []migration.PrecheckUnit{ 234 &fakeUnit{name: "foo/0", life: state.Dead}, 235 }, 236 }, 237 }, 238 } 239 err := sourcePrecheck(backend) 240 c.Assert(err.Error(), gc.Equals, "unit foo/0 is dead") 241 } 242 243 func (s *SourcePrecheckSuite) TestUnitExecuting(c *gc.C) { 244 backend := &fakeBackend{ 245 apps: []migration.PrecheckApplication{ 246 &fakeApp{ 247 name: "foo", 248 units: []migration.PrecheckUnit{ 249 &fakeUnit{name: "foo/0", agentStatus: status.Executing}, 250 }, 251 }, 252 }, 253 } 254 err := sourcePrecheck(backend) 255 c.Assert(err, jc.ErrorIsNil) 256 } 257 258 func (s *SourcePrecheckSuite) TestUnitNotIdle(c *gc.C) { 259 backend := &fakeBackend{ 260 apps: []migration.PrecheckApplication{ 261 &fakeApp{ 262 name: "foo", 263 units: []migration.PrecheckUnit{ 264 &fakeUnit{name: "foo/0", agentStatus: status.Failed}, 265 }, 266 }, 267 }, 268 } 269 err := sourcePrecheck(backend) 270 c.Assert(err.Error(), gc.Equals, "unit foo/0 not idle or executing (failed)") 271 } 272 273 func (s *SourcePrecheckSuite) TestUnitLostLegacy(c *gc.C) { 274 backend := &fakeBackend{ 275 apps: []migration.PrecheckApplication{ 276 &fakeApp{ 277 name: "foo", 278 units: []migration.PrecheckUnit{ 279 &fakeUnit{name: "foo/0", lost: true}, 280 }, 281 }, 282 }, 283 } 284 err := migration.SourcePrecheck(backend, nil, nil) 285 c.Assert(err.Error(), gc.Equals, "unit foo/0 not idle or executing (lost)") 286 } 287 288 func (s *SourcePrecheckSuite) TestUnitLost(c *gc.C) { 289 backend := newHappyBackend() 290 modelPresence := downAgentPresence("unit-foo-0") 291 controllerPresence := allAlivePresence() 292 err := migration.SourcePrecheck(backend, modelPresence, controllerPresence) 293 c.Assert(err.Error(), gc.Equals, "unit foo/0 not idle or executing (lost)") 294 } 295 296 func (*SourcePrecheckSuite) TestDyingControllerModel(c *gc.C) { 297 backend := newFakeBackend() 298 backend.controllerBackend.model.life = state.Dying 299 err := sourcePrecheck(backend) 300 c.Assert(err, gc.ErrorMatches, "controller: model is dying") 301 } 302 303 func (s *SourcePrecheckSuite) TestControllerAgentVersionError(c *gc.C) { 304 backend := newFakeBackend() 305 backend.controllerBackend.agentVersionErr = errors.New("boom") 306 err := sourcePrecheck(backend) 307 c.Assert(err, gc.ErrorMatches, "controller: retrieving model version: boom") 308 309 } 310 311 func (s *SourcePrecheckSuite) TestControllerMachineVersionsDontMatch(c *gc.C) { 312 backend := newFakeBackend() 313 backend.controllerBackend = newBackendWithMismatchingTools() 314 err := sourcePrecheck(backend) 315 c.Assert(err, gc.ErrorMatches, "controller: machine . agent binaries don't match model.+") 316 } 317 318 func (s *SourcePrecheckSuite) TestControllerMachineRequiresReboot(c *gc.C) { 319 backend := newFakeBackend() 320 backend.controllerBackend = newBackendWithRebootingMachine() 321 err := sourcePrecheck(backend) 322 c.Assert(err, gc.ErrorMatches, "controller: machine 0 is scheduled to reboot") 323 } 324 325 func (s *SourcePrecheckSuite) TestDyingControllerMachine(c *gc.C) { 326 backend := &fakeBackend{ 327 controllerBackend: newBackendWithDyingMachine(), 328 } 329 err := sourcePrecheck(backend) 330 c.Assert(err, gc.ErrorMatches, "controller: machine 0 is dying") 331 } 332 333 func (s *SourcePrecheckSuite) TestNonStartedControllerMachine(c *gc.C) { 334 backend := &fakeBackend{ 335 controllerBackend: newBackendWithDownMachine(), 336 } 337 err := sourcePrecheck(backend) 338 c.Assert(err.Error(), gc.Equals, "controller: machine 0 agent not functioning at this time (down)") 339 } 340 341 func (s *SourcePrecheckSuite) TestProvisioningControllerMachine(c *gc.C) { 342 backend := &fakeBackend{ 343 controllerBackend: newBackendWithProvisioningMachine(), 344 } 345 err := sourcePrecheck(backend) 346 c.Assert(err.Error(), gc.Equals, "controller: machine 0 not running (allocating)") 347 } 348 349 func (s *SourcePrecheckSuite) TestUnitsAllInScope(c *gc.C) { 350 backend := newHappyBackend() 351 backend.relations = []migration.PrecheckRelation{&fakeRelation{ 352 endpoints: []state.Endpoint{ 353 {ApplicationName: "foo"}, 354 {ApplicationName: "bar"}, 355 }, 356 relUnits: map[string]*fakeRelationUnit{ 357 "foo/0": {valid: true, inScope: true}, 358 "bar/0": {valid: true, inScope: true}, 359 "bar/1": {valid: true, inScope: true}, 360 }, 361 }} 362 err := sourcePrecheck(backend) 363 c.Assert(err, jc.ErrorIsNil) 364 } 365 366 func (s *SourcePrecheckSuite) TestSubordinatesNotYetInScope(c *gc.C) { 367 backend := newHappyBackend() 368 backend.relations = []migration.PrecheckRelation{&fakeRelation{ 369 key: "foo:db bar:db", 370 endpoints: []state.Endpoint{ 371 {ApplicationName: "foo"}, 372 {ApplicationName: "bar"}, 373 }, 374 relUnits: map[string]*fakeRelationUnit{ 375 "foo/0": {valid: true, inScope: true}, 376 "bar/0": {valid: true, inScope: true}, 377 "bar/1": {valid: true, inScope: false}, 378 }, 379 }} 380 err := sourcePrecheck(backend) 381 c.Assert(err, gc.ErrorMatches, "unit bar/1 hasn't joined relation foo:db bar:db yet") 382 } 383 384 func (s *SourcePrecheckSuite) TestSubordinatesInvalidUnitsNotYetInScope(c *gc.C) { 385 backend := newHappyBackend() 386 backend.relations = []migration.PrecheckRelation{&fakeRelation{ 387 key: "foo:db bar:db", 388 endpoints: []state.Endpoint{ 389 {ApplicationName: "foo"}, 390 {ApplicationName: "bar"}, 391 }, 392 relUnits: map[string]*fakeRelationUnit{ 393 "foo/0": {valid: true, inScope: true}, 394 "bar/0": {valid: true, inScope: true}, 395 "bar/1": {valid: false, inScope: false}, 396 }, 397 }} 398 err := sourcePrecheck(backend) 399 c.Assert(err, jc.ErrorIsNil) 400 } 401 402 func (s *SourcePrecheckSuite) TestCrossModelUnitsNotYetInScope(c *gc.C) { 403 backend := newHappyBackend() 404 backend.relations = []migration.PrecheckRelation{&fakeRelation{ 405 key: "foo:db bar:db", 406 crossModel: true, 407 endpoints: []state.Endpoint{ 408 {ApplicationName: "foo"}, 409 {ApplicationName: "remote-mysql"}, 410 }, 411 relUnits: map[string]*fakeRelationUnit{ 412 "foo/0": {valid: true, inScope: false}, 413 }, 414 }} 415 err := sourcePrecheck(backend) 416 c.Assert(err, jc.ErrorIsNil) 417 } 418 419 type TargetPrecheckSuite struct { 420 precheckBaseSuite 421 modelInfo coremigration.ModelInfo 422 } 423 424 var _ = gc.Suite(&TargetPrecheckSuite{}) 425 426 func (s *TargetPrecheckSuite) SetUpTest(c *gc.C) { 427 s.modelInfo = coremigration.ModelInfo{ 428 UUID: modelUUID, 429 Owner: modelOwner, 430 Name: modelName, 431 AgentVersion: backendVersion, 432 } 433 } 434 435 func (s *TargetPrecheckSuite) runPrecheck(backend migration.PrecheckBackend) error { 436 return migration.TargetPrecheck(backend, nil, s.modelInfo, allAlivePresence()) 437 } 438 439 func (s *TargetPrecheckSuite) TestSuccess(c *gc.C) { 440 err := s.runPrecheck(newHappyBackend()) 441 c.Assert(err, jc.ErrorIsNil) 442 } 443 444 func (s *TargetPrecheckSuite) TestModelVersionAheadOfTarget(c *gc.C) { 445 backend := newFakeBackend() 446 447 sourceVersion := backendVersion 448 sourceVersion.Patch++ 449 s.modelInfo.AgentVersion = sourceVersion 450 451 err := s.runPrecheck(backend) 452 c.Assert(err.Error(), gc.Equals, 453 `model has higher version than target controller (1.2.4 > 1.2.3)`) 454 } 455 456 func (s *TargetPrecheckSuite) TestSourceControllerMajorAhead(c *gc.C) { 457 backend := newFakeBackend() 458 459 sourceVersion := backendVersion 460 sourceVersion.Major++ 461 sourceVersion.Minor = 0 462 sourceVersion.Patch = 0 463 s.modelInfo.ControllerAgentVersion = sourceVersion 464 465 err := s.runPrecheck(backend) 466 c.Assert(err.Error(), gc.Equals, 467 `source controller has higher version than target controller (2.0.0 > 1.2.3)`) 468 } 469 470 func (s *TargetPrecheckSuite) TestSourceControllerMinorAhead(c *gc.C) { 471 backend := newFakeBackend() 472 473 sourceVersion := backendVersion 474 sourceVersion.Minor++ 475 sourceVersion.Patch = 0 476 s.modelInfo.ControllerAgentVersion = sourceVersion 477 478 err := s.runPrecheck(backend) 479 c.Assert(err.Error(), gc.Equals, 480 `source controller has higher version than target controller (1.3.0 > 1.2.3)`) 481 } 482 483 func (s *TargetPrecheckSuite) TestSourceControllerPatchAhead(c *gc.C) { 484 backend := newFakeBackend() 485 486 sourceVersion := backendVersion 487 sourceVersion.Patch++ 488 s.modelInfo.ControllerAgentVersion = sourceVersion 489 490 c.Assert(s.runPrecheck(backend), jc.ErrorIsNil) 491 } 492 493 func (s *TargetPrecheckSuite) TestSourceControllerBuildAhead(c *gc.C) { 494 backend := newFakeBackend() 495 496 sourceVersion := backendVersion 497 sourceVersion.Build++ 498 s.modelInfo.ControllerAgentVersion = sourceVersion 499 500 c.Assert(s.runPrecheck(backend), jc.ErrorIsNil) 501 } 502 503 func (s *TargetPrecheckSuite) TestSourceControllerTagMismatch(c *gc.C) { 504 backend := newFakeBackend() 505 506 sourceVersion := backendVersion 507 sourceVersion.Tag = "alpha" 508 s.modelInfo.ControllerAgentVersion = sourceVersion 509 510 c.Assert(s.runPrecheck(backend), jc.ErrorIsNil) 511 } 512 513 func (s *TargetPrecheckSuite) TestDying(c *gc.C) { 514 backend := newFakeBackend() 515 backend.model.life = state.Dying 516 err := s.runPrecheck(backend) 517 c.Assert(err, gc.ErrorMatches, "model is dying") 518 } 519 520 func (s *TargetPrecheckSuite) TestMachineRequiresReboot(c *gc.C) { 521 s.checkRebootRequired(c, s.runPrecheck) 522 } 523 524 func (s *TargetPrecheckSuite) TestAgentVersionError(c *gc.C) { 525 s.checkAgentVersionError(c, s.runPrecheck) 526 } 527 528 func (s *TargetPrecheckSuite) TestIsUpgradingError(c *gc.C) { 529 backend := &fakeBackend{ 530 isUpgradingErr: errors.New("boom"), 531 } 532 err := s.runPrecheck(backend) 533 c.Assert(err, gc.ErrorMatches, "checking for upgrades: boom") 534 } 535 536 func (s *TargetPrecheckSuite) TestIsUpgrading(c *gc.C) { 537 backend := &fakeBackend{ 538 isUpgrading: true, 539 } 540 err := s.runPrecheck(backend) 541 c.Assert(err, gc.ErrorMatches, "upgrade in progress") 542 } 543 544 func (s *TargetPrecheckSuite) TestIsMigrationActiveError(c *gc.C) { 545 backend := &fakeBackend{migrationActiveErr: errors.New("boom")} 546 err := s.runPrecheck(backend) 547 c.Assert(err, gc.ErrorMatches, "checking for active migration: boom") 548 } 549 550 func (s *TargetPrecheckSuite) TestIsMigrationActive(c *gc.C) { 551 backend := &fakeBackend{migrationActive: true} 552 err := s.runPrecheck(backend) 553 c.Assert(err, gc.ErrorMatches, "model is being migrated out of target controller") 554 } 555 556 func (s *TargetPrecheckSuite) TestMachineVersionsDontMatch(c *gc.C) { 557 s.checkMachineVersionsDontMatch(c, s.runPrecheck) 558 } 559 560 func (s *TargetPrecheckSuite) TestDyingMachine(c *gc.C) { 561 backend := newBackendWithDyingMachine() 562 err := s.runPrecheck(backend) 563 c.Assert(err, gc.ErrorMatches, "machine 0 is dying") 564 } 565 566 func (s *TargetPrecheckSuite) TestNonStartedMachine(c *gc.C) { 567 backend := newBackendWithDownMachine() 568 err := s.runPrecheck(backend) 569 c.Assert(err.Error(), gc.Equals, "machine 0 agent not functioning at this time (down)") 570 } 571 572 func (s *TargetPrecheckSuite) TestProvisioningMachine(c *gc.C) { 573 backend := newBackendWithProvisioningMachine() 574 err := s.runPrecheck(backend) 575 c.Assert(err.Error(), gc.Equals, "machine 0 not running (allocating)") 576 } 577 578 func (s *TargetPrecheckSuite) TestDownMachineAgentLegacy(c *gc.C) { 579 backend := newBackendWithDownMachineAgent() 580 err := migration.TargetPrecheck(backend, nil, s.modelInfo, nil) 581 c.Assert(err.Error(), gc.Equals, "machine 1 agent not functioning at this time (down)") 582 } 583 584 func (s *TargetPrecheckSuite) TestDownMachineAgent(c *gc.C) { 585 backend := newHappyBackend() 586 modelPresence := downAgentPresence("machine-1") 587 err := migration.TargetPrecheck(backend, nil, s.modelInfo, modelPresence) 588 c.Assert(err.Error(), gc.Equals, "machine 1 agent not functioning at this time (down)") 589 } 590 591 func (s *TargetPrecheckSuite) TestModelNameAlreadyInUse(c *gc.C) { 592 pool := &fakePool{ 593 models: []migration.PrecheckModel{ 594 &fakeModel{ 595 uuid: "uuid", 596 name: modelName, 597 modelType: state.ModelTypeIAAS, 598 owner: modelOwner, 599 }, 600 }, 601 } 602 backend := newFakeBackend() 603 backend.models = pool.uuids() 604 err := migration.TargetPrecheck(backend, pool, s.modelInfo, allAlivePresence()) 605 c.Assert(err, gc.ErrorMatches, "model named \"model-name\" already exists") 606 } 607 608 func (s *TargetPrecheckSuite) TestModelNameOverlapOkForDifferentOwner(c *gc.C) { 609 pool := &fakePool{ 610 models: []migration.PrecheckModel{ 611 &fakeModel{ 612 name: modelName, 613 modelType: state.ModelTypeIAAS, 614 owner: names.NewUserTag("someone.else"), 615 }, 616 }, 617 } 618 backend := newFakeBackend() 619 backend.models = pool.uuids() 620 err := migration.TargetPrecheck(backend, pool, s.modelInfo, allAlivePresence()) 621 c.Assert(err, jc.ErrorIsNil) 622 } 623 624 func (s *TargetPrecheckSuite) TestUUIDAlreadyExists(c *gc.C) { 625 pool := &fakePool{ 626 models: []migration.PrecheckModel{ 627 &fakeModel{uuid: modelUUID, modelType: state.ModelTypeIAAS}, 628 }, 629 } 630 backend := newFakeBackend() 631 backend.models = pool.uuids() 632 err := migration.TargetPrecheck(backend, pool, s.modelInfo, allAlivePresence()) 633 c.Assert(err.Error(), gc.Equals, "model with same UUID already exists (model-uuid)") 634 } 635 636 func (s *TargetPrecheckSuite) TestUUIDAlreadyExistsButImporting(c *gc.C) { 637 pool := &fakePool{ 638 models: []migration.PrecheckModel{ 639 &fakeModel{ 640 uuid: modelUUID, 641 modelType: state.ModelTypeIAAS, 642 migrationMode: state.MigrationModeImporting, 643 }, 644 }, 645 } 646 backend := newFakeBackend() 647 backend.models = pool.uuids() 648 err := migration.TargetPrecheck(backend, pool, s.modelInfo, allAlivePresence()) 649 c.Assert(err, jc.ErrorIsNil) 650 } 651 652 type precheckRunner func(migration.PrecheckBackend) error 653 654 type precheckBaseSuite struct { 655 testing.BaseSuite 656 } 657 658 func (*precheckBaseSuite) checkRebootRequired(c *gc.C, runPrecheck precheckRunner) { 659 err := runPrecheck(newBackendWithRebootingMachine()) 660 c.Assert(err, gc.ErrorMatches, "machine 0 is scheduled to reboot") 661 } 662 663 func (*precheckBaseSuite) checkAgentVersionError(c *gc.C, runPrecheck precheckRunner) { 664 backend := &fakeBackend{ 665 agentVersionErr: errors.New("boom"), 666 } 667 err := runPrecheck(backend) 668 c.Assert(err, gc.ErrorMatches, "retrieving model version: boom") 669 } 670 671 func (*precheckBaseSuite) checkMachineVersionsDontMatch(c *gc.C, runPrecheck precheckRunner) { 672 err := runPrecheck(newBackendWithMismatchingTools()) 673 c.Assert(err.Error(), gc.Equals, "machine 1 agent binaries don't match model (1.3.1 != 1.2.3)") 674 } 675 676 func newHappyBackend() *fakeBackend { 677 return &fakeBackend{ 678 machines: []migration.PrecheckMachine{ 679 &fakeMachine{id: "0"}, 680 &fakeMachine{id: "1"}, 681 }, 682 apps: []migration.PrecheckApplication{ 683 &fakeApp{ 684 name: "foo", 685 units: []migration.PrecheckUnit{&fakeUnit{name: "foo/0"}}, 686 }, 687 &fakeApp{ 688 name: "bar", 689 units: []migration.PrecheckUnit{ 690 &fakeUnit{name: "bar/0"}, 691 &fakeUnit{name: "bar/1"}, 692 }, 693 }, 694 }, 695 } 696 } 697 698 func newBackendWithMismatchingTools() *fakeBackend { 699 return &fakeBackend{ 700 machines: []migration.PrecheckMachine{ 701 &fakeMachine{id: "0"}, 702 &fakeMachine{id: "1", version: version.MustParseBinary("1.3.1-xenial-amd64")}, 703 }, 704 } 705 } 706 707 func newBackendWithRebootingMachine() *fakeBackend { 708 return &fakeBackend{ 709 machines: []migration.PrecheckMachine{ 710 &fakeMachine{id: "0", rebootAction: state.ShouldReboot}, 711 }, 712 } 713 } 714 715 func newBackendWithDyingMachine() *fakeBackend { 716 return &fakeBackend{ 717 machines: []migration.PrecheckMachine{ 718 &fakeMachine{id: "0", life: state.Dying}, 719 &fakeMachine{id: "1"}, 720 }, 721 } 722 } 723 724 func newBackendWithDownMachine() *fakeBackend { 725 return &fakeBackend{ 726 machines: []migration.PrecheckMachine{ 727 &fakeMachine{id: "0", status: status.Down}, 728 &fakeMachine{id: "1"}, 729 }, 730 } 731 } 732 733 func newBackendWithProvisioningMachine() *fakeBackend { 734 return &fakeBackend{ 735 machines: []migration.PrecheckMachine{ 736 &fakeMachine{id: "0", instanceStatus: status.Provisioning}, 737 &fakeMachine{id: "1"}, 738 }, 739 } 740 } 741 742 func newBackendWithDownMachineAgent() *fakeBackend { 743 return &fakeBackend{ 744 machines: []migration.PrecheckMachine{ 745 &fakeMachine{id: "0"}, 746 &fakeMachine{id: "1", lost: true}, 747 }, 748 } 749 } 750 751 func newFakeBackend() *fakeBackend { 752 return &fakeBackend{ 753 controllerBackend: &fakeBackend{}, 754 } 755 } 756 757 type fakeBackend struct { 758 agentVersionErr error 759 760 model fakeModel 761 models []string 762 763 cleanupNeeded bool 764 cleanupErr error 765 766 isUpgrading bool 767 isUpgradingErr error 768 769 migrationActive bool 770 migrationActiveErr error 771 772 machines []migration.PrecheckMachine 773 allMachinesErr error 774 775 apps []migration.PrecheckApplication 776 allAppsErr error 777 778 relations []migration.PrecheckRelation 779 allRelsErr error 780 781 credentials state.Credential 782 credentialsErr error 783 784 pendingResources []resource.Resource 785 pendingResourcesErr error 786 787 controllerBackend *fakeBackend 788 } 789 790 func (b *fakeBackend) Model() (migration.PrecheckModel, error) { 791 return &b.model, nil 792 } 793 794 func (b *fakeBackend) AllModelUUIDs() ([]string, error) { 795 return b.models, nil 796 } 797 798 func (b *fakeBackend) NeedsCleanup() (bool, error) { 799 return b.cleanupNeeded, b.cleanupErr 800 } 801 802 func (b *fakeBackend) AgentVersion() (version.Number, error) { 803 return backendVersion, b.agentVersionErr 804 } 805 806 func (b *fakeBackend) IsUpgrading() (bool, error) { 807 return b.isUpgrading, b.isUpgradingErr 808 } 809 810 func (b *fakeBackend) IsMigrationActive(string) (bool, error) { 811 return b.migrationActive, b.migrationActiveErr 812 } 813 814 func (b *fakeBackend) CloudCredential(tag names.CloudCredentialTag) (state.Credential, error) { 815 return b.credentials, b.credentialsErr 816 } 817 818 func (b *fakeBackend) AllMachines() ([]migration.PrecheckMachine, error) { 819 return b.machines, b.allMachinesErr 820 } 821 822 func (b *fakeBackend) AllApplications() ([]migration.PrecheckApplication, error) { 823 return b.apps, b.allAppsErr 824 } 825 826 func (b *fakeBackend) AllRelations() ([]migration.PrecheckRelation, error) { 827 return b.relations, b.allRelsErr 828 } 829 830 func (b *fakeBackend) ListPendingResources(app string) ([]resource.Resource, error) { 831 return b.pendingResources, b.pendingResourcesErr 832 } 833 834 func (b *fakeBackend) ControllerBackend() (migration.PrecheckBackend, error) { 835 if b.controllerBackend == nil { 836 return b, nil 837 } 838 return b.controllerBackend, nil 839 } 840 841 type fakePool struct { 842 models []migration.PrecheckModel 843 } 844 845 func (p *fakePool) uuids() []string { 846 out := make([]string, len(p.models)) 847 for i, model := range p.models { 848 out[i] = model.UUID() 849 } 850 return out 851 } 852 853 func (p *fakePool) GetModel(uuid string) (migration.PrecheckModel, func(), error) { 854 for _, model := range p.models { 855 if model.UUID() == uuid { 856 return model, func() {}, nil 857 } 858 } 859 return nil, nil, errors.NotFoundf("model %v", uuid) 860 } 861 862 type fakeModel struct { 863 uuid string 864 name string 865 owner names.UserTag 866 life state.Life 867 modelType state.ModelType 868 migrationMode state.MigrationMode 869 credential string 870 } 871 872 func (m *fakeModel) Type() state.ModelType { 873 return m.modelType 874 } 875 876 func (m *fakeModel) UUID() string { 877 return m.uuid 878 } 879 880 func (m *fakeModel) Name() string { 881 return m.name 882 } 883 884 func (m *fakeModel) Owner() names.UserTag { 885 return m.owner 886 } 887 888 func (m *fakeModel) Life() state.Life { 889 return m.life 890 } 891 892 func (m *fakeModel) MigrationMode() state.MigrationMode { 893 return m.migrationMode 894 } 895 896 func (m *fakeModel) CloudCredential() (names.CloudCredentialTag, bool) { 897 if names.IsValidCloudCredential(m.credential) { 898 return names.NewCloudCredentialTag(m.credential), true 899 } 900 return names.CloudCredentialTag{}, false 901 } 902 903 type fakeMachine struct { 904 id string 905 version version.Binary 906 life state.Life 907 status status.Status 908 instanceStatus status.Status 909 lost bool 910 rebootAction state.RebootAction 911 } 912 913 func (m *fakeMachine) Id() string { 914 return m.id 915 } 916 917 func (m *fakeMachine) Life() state.Life { 918 return m.life 919 } 920 921 func (m *fakeMachine) Status() (status.StatusInfo, error) { 922 s := m.status 923 if s == "" { 924 // Avoid the need to specify this everywhere. 925 s = status.Started 926 } 927 return status.StatusInfo{Status: s}, nil 928 } 929 930 func (m *fakeMachine) InstanceStatus() (status.StatusInfo, error) { 931 s := m.instanceStatus 932 if s == "" { 933 // Avoid the need to specify this everywhere. 934 s = status.Running 935 } 936 return status.StatusInfo{Status: s}, nil 937 } 938 939 func (m *fakeMachine) AgentPresence() (bool, error) { 940 return !m.lost, nil 941 } 942 943 func (m *fakeMachine) AgentTools() (*tools.Tools, error) { 944 // Avoid having to specify the version when it's supposed to match 945 // the model config. 946 v := m.version 947 if v.Compare(version.Zero) == 0 { 948 v = backendVersionBinary 949 } 950 return &tools.Tools{ 951 Version: v, 952 }, nil 953 } 954 955 func (m *fakeMachine) ShouldRebootOrShutdown() (state.RebootAction, error) { 956 if m.rebootAction == "" { 957 return state.ShouldDoNothing, nil 958 } 959 return m.rebootAction, nil 960 } 961 962 type fakeApp struct { 963 name string 964 life state.Life 965 charmURL string 966 units []migration.PrecheckUnit 967 minunits int 968 } 969 970 func (a *fakeApp) Name() string { 971 return a.name 972 } 973 974 func (a *fakeApp) Life() state.Life { 975 return a.life 976 } 977 978 func (a *fakeApp) CharmURL() (*charm.URL, bool) { 979 url := a.charmURL 980 if url == "" { 981 url = "cs:foo-1" 982 } 983 return charm.MustParseURL(url), false 984 } 985 986 func (a *fakeApp) AllUnits() ([]migration.PrecheckUnit, error) { 987 return a.units, nil 988 } 989 990 func (a *fakeApp) MinUnits() int { 991 return a.minunits 992 } 993 994 type fakeUnit struct { 995 name string 996 version version.Binary 997 noTools bool 998 life state.Life 999 charmURL string 1000 agentStatus status.Status 1001 lost bool 1002 } 1003 1004 func (u *fakeUnit) Name() string { 1005 return u.name 1006 } 1007 1008 func (u *fakeUnit) AgentTools() (*tools.Tools, error) { 1009 if u.noTools { 1010 return nil, errors.NotFoundf("tools") 1011 } 1012 // Avoid having to specify the version when it's supposed to match 1013 // the model config. 1014 v := u.version 1015 if v.Compare(version.Zero) == 0 { 1016 v = backendVersionBinary 1017 } 1018 return &tools.Tools{ 1019 Version: v, 1020 }, nil 1021 } 1022 1023 func (u *fakeUnit) Life() state.Life { 1024 return u.life 1025 } 1026 1027 func (u *fakeUnit) ShouldBeAssigned() bool { 1028 return true 1029 } 1030 1031 func (u *fakeUnit) CharmURL() (*charm.URL, bool) { 1032 url := u.charmURL 1033 if url == "" { 1034 url = "cs:foo-1" 1035 } 1036 return charm.MustParseURL(url), false 1037 } 1038 1039 func (u *fakeUnit) AgentStatus() (status.StatusInfo, error) { 1040 s := u.agentStatus 1041 if s == "" { 1042 // Avoid the need to specify this everywhere. 1043 s = status.Idle 1044 } 1045 return status.StatusInfo{Status: s}, nil 1046 } 1047 1048 func (u *fakeUnit) Status() (status.StatusInfo, error) { 1049 return status.StatusInfo{Status: status.Idle}, nil 1050 } 1051 1052 func (u *fakeUnit) AgentPresence() (bool, error) { 1053 return !u.lost, nil 1054 } 1055 1056 type fakeRelation struct { 1057 key string 1058 crossModel bool 1059 crossModelErr error 1060 endpoints []state.Endpoint 1061 relUnits map[string]*fakeRelationUnit 1062 unitErr error 1063 } 1064 1065 func (r *fakeRelation) String() string { 1066 return r.key 1067 } 1068 1069 func (r *fakeRelation) IsCrossModel() (bool, error) { 1070 return r.crossModel, r.crossModelErr 1071 } 1072 1073 func (r *fakeRelation) Endpoints() []state.Endpoint { 1074 return r.endpoints 1075 } 1076 1077 func (r *fakeRelation) Unit(u migration.PrecheckUnit) (migration.PrecheckRelationUnit, error) { 1078 return r.relUnits[u.Name()], r.unitErr 1079 } 1080 1081 type fakeRelationUnit struct { 1082 valid, inScope bool 1083 validErr, scopeErr error 1084 } 1085 1086 func (ru *fakeRelationUnit) Valid() (bool, error) { 1087 return ru.valid, ru.validErr 1088 } 1089 1090 func (ru *fakeRelationUnit) InScope() (bool, error) { 1091 return ru.inScope, ru.scopeErr 1092 } 1093 1094 func allAlivePresence() migration.ModelPresence { 1095 return &fakePresence{} 1096 } 1097 1098 func downAgentPresence(agent ...string) migration.ModelPresence { 1099 m := make(map[string]presence.Status) 1100 for _, a := range agent { 1101 m[a] = presence.Missing 1102 } 1103 return &fakePresence{ 1104 agentStatus: m, 1105 } 1106 } 1107 1108 type fakePresence struct { 1109 agentStatus map[string]presence.Status 1110 } 1111 1112 func (f *fakePresence) AgentStatus(agent string) (presence.Status, error) { 1113 if value, found := f.agentStatus[agent]; found { 1114 return value, nil 1115 } 1116 return presence.Alive, nil 1117 }