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