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  }