github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/client/modelupgrader/upgrader_test.go (about)

     1  // Copyright 2020 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package modelupgrader_test
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/names/v5"
     9  	"github.com/juju/replicaset/v3"
    10  	jujutesting "github.com/juju/testing"
    11  	jc "github.com/juju/testing/checkers"
    12  	"github.com/juju/utils/v3"
    13  	"github.com/juju/version/v2"
    14  	"go.uber.org/mock/gomock"
    15  	gc "gopkg.in/check.v1"
    16  
    17  	"github.com/juju/juju/apiserver/common"
    18  	"github.com/juju/juju/apiserver/facades/client/modelupgrader"
    19  	"github.com/juju/juju/apiserver/facades/client/modelupgrader/mocks"
    20  	apiservertesting "github.com/juju/juju/apiserver/testing"
    21  	"github.com/juju/juju/controller"
    22  	coreos "github.com/juju/juju/core/os"
    23  	"github.com/juju/juju/core/os/ostype"
    24  	"github.com/juju/juju/docker"
    25  	"github.com/juju/juju/docker/registry"
    26  	"github.com/juju/juju/docker/registry/image"
    27  	registrymocks "github.com/juju/juju/docker/registry/mocks"
    28  	"github.com/juju/juju/environs"
    29  	environscloudspec "github.com/juju/juju/environs/cloudspec"
    30  	"github.com/juju/juju/environs/context"
    31  	envtools "github.com/juju/juju/environs/tools"
    32  	"github.com/juju/juju/provider/lxd"
    33  	"github.com/juju/juju/rpc/params"
    34  	"github.com/juju/juju/state"
    35  	coretesting "github.com/juju/juju/testing"
    36  	coretools "github.com/juju/juju/tools"
    37  	"github.com/juju/juju/upgrades/upgradevalidation"
    38  	upgradevalidationmocks "github.com/juju/juju/upgrades/upgradevalidation/mocks"
    39  )
    40  
    41  var winVersions = []string{
    42  	"win2008r2", "win2012", "win2012hv", "win2012hvr2", "win2012r2", "win2012r2",
    43  	"win2016", "win2016hv", "win2019", "win7", "win8", "win81", "win10",
    44  }
    45  
    46  var ubuntuVersions = []string{
    47  	"12.04",
    48  	"12.10",
    49  	"13.04",
    50  	"13.10",
    51  	"14.04",
    52  	"14.10",
    53  	"15.04",
    54  	"15.10",
    55  	"16.04",
    56  	"16.10",
    57  	"17.04",
    58  	"17.10",
    59  	"18.04",
    60  	"18.10",
    61  	"19.04",
    62  	"19.10",
    63  	"20.10",
    64  	"21.04",
    65  	"21.10",
    66  	"22.10",
    67  	"23.04",
    68  	"23.10",
    69  	"24.04",
    70  }
    71  
    72  var controllerCfg = controller.Config{
    73  	controller.ControllerUUIDKey: coretesting.ControllerTag.Id(),
    74  	controller.CAASImageRepo: `
    75  {
    76      "serveraddress": "quay.io",
    77      "auth": "xxxxx==",
    78      "repository": "test-account"
    79  }
    80  `[1:],
    81  }
    82  
    83  func makeBases(os string, vers []string) []state.Base {
    84  	bases := make([]state.Base, len(vers))
    85  	for i, vers := range vers {
    86  		bases[i] = state.Base{OS: os, Channel: vers}
    87  	}
    88  	return bases
    89  }
    90  
    91  type modelUpgradeSuite struct {
    92  	jujutesting.IsolationSuite
    93  
    94  	adminUser   names.UserTag
    95  	authoriser  apiservertesting.FakeAuthorizer
    96  	callContext context.ProviderCallContext
    97  
    98  	statePool        *mocks.MockStatePool
    99  	toolsFinder      *mocks.MockToolsFinder
   100  	bootstrapEnviron *mocks.MockBootstrapEnviron
   101  	blockChecker     *mocks.MockBlockCheckerInterface
   102  	registryProvider *registrymocks.MockRegistry
   103  	cloudSpec        lxd.CloudSpec
   104  }
   105  
   106  var _ = gc.Suite(&modelUpgradeSuite{})
   107  
   108  func (s *modelUpgradeSuite) SetUpTest(c *gc.C) {
   109  	s.IsolationSuite.SetUpTest(c)
   110  
   111  	adminUser := "admin"
   112  	s.adminUser = names.NewUserTag(adminUser)
   113  
   114  	s.authoriser = apiservertesting.FakeAuthorizer{
   115  		Tag: s.adminUser,
   116  	}
   117  
   118  	s.callContext = context.NewEmptyCloudCallContext()
   119  	s.cloudSpec = lxd.CloudSpec{CloudSpec: environscloudspec.CloudSpec{Type: "lxd"}}
   120  }
   121  
   122  func (s *modelUpgradeSuite) getModelUpgraderAPI(c *gc.C) (*gomock.Controller, *modelupgrader.ModelUpgraderAPI) {
   123  	ctrl := gomock.NewController(c)
   124  	s.statePool = mocks.NewMockStatePool(ctrl)
   125  	s.toolsFinder = mocks.NewMockToolsFinder(ctrl)
   126  	s.bootstrapEnviron = mocks.NewMockBootstrapEnviron(ctrl)
   127  	s.blockChecker = mocks.NewMockBlockCheckerInterface(ctrl)
   128  	s.registryProvider = registrymocks.NewMockRegistry(ctrl)
   129  
   130  	api, err := modelupgrader.NewModelUpgraderAPI(
   131  		coretesting.ControllerTag,
   132  		s.statePool,
   133  		s.toolsFinder,
   134  		func() (environs.BootstrapEnviron, error) {
   135  			return s.bootstrapEnviron, nil
   136  		},
   137  		s.blockChecker, s.authoriser, s.callContext,
   138  		func(docker.ImageRepoDetails) (registry.Registry, error) {
   139  			return s.registryProvider, nil
   140  		},
   141  		func(names.ModelTag) (environscloudspec.CloudSpec, error) {
   142  			return s.cloudSpec.CloudSpec, nil
   143  		},
   144  	)
   145  	c.Assert(err, jc.ErrorIsNil)
   146  	return ctrl, api
   147  }
   148  
   149  func (s *modelUpgradeSuite) TestUpgradeModelWithInvalidModelTag(c *gc.C) {
   150  	ctrl, api := s.getModelUpgraderAPI(c)
   151  	defer ctrl.Finish()
   152  
   153  	_, err := api.UpgradeModel(params.UpgradeModelParams{ModelTag: "!!!"})
   154  	c.Assert(err, gc.ErrorMatches, `"!!!" is not a valid tag`)
   155  }
   156  
   157  func (s *modelUpgradeSuite) TestUpgradeModelWithModelWithNoPermission(c *gc.C) {
   158  	s.authoriser = apiservertesting.FakeAuthorizer{
   159  		Tag: names.NewUserTag("user"),
   160  	}
   161  	ctrl, api := s.getModelUpgraderAPI(c)
   162  	defer ctrl.Finish()
   163  
   164  	_, err := api.UpgradeModel(
   165  		params.UpgradeModelParams{
   166  			ModelTag:      coretesting.ModelTag.String(),
   167  			TargetVersion: version.MustParse("3.0.0"),
   168  		},
   169  	)
   170  	c.Assert(err, gc.ErrorMatches, `permission denied`)
   171  }
   172  
   173  func (s *modelUpgradeSuite) TestUpgradeModelWithChangeNotAllowed(c *gc.C) {
   174  	ctrl, api := s.getModelUpgraderAPI(c)
   175  	defer ctrl.Finish()
   176  
   177  	s.blockChecker.EXPECT().ChangeAllowed().Return(errors.Errorf("the operation has been blocked"))
   178  
   179  	_, err := api.UpgradeModel(
   180  		params.UpgradeModelParams{
   181  			ModelTag:      coretesting.ModelTag.String(),
   182  			TargetVersion: version.MustParse("3.0.0"),
   183  		},
   184  	)
   185  	c.Assert(err, gc.ErrorMatches, `the operation has been blocked`)
   186  }
   187  
   188  func (s *modelUpgradeSuite) assertUpgradeModelForControllerModelJuju3(c *gc.C, dryRun bool) {
   189  	ctrl, api := s.getModelUpgraderAPI(c)
   190  	defer ctrl.Finish()
   191  
   192  	s.PatchValue(&upgradevalidation.MinAgentVersions, map[int]version.Number{
   193  		3: version.MustParse("2.9.1"),
   194  	})
   195  
   196  	server := upgradevalidationmocks.NewMockServer(ctrl)
   197  	serverFactory := upgradevalidationmocks.NewMockServerFactory(ctrl)
   198  	s.PatchValue(&upgradevalidation.NewServerFactory,
   199  		func(_ lxd.NewHTTPClientFunc) lxd.ServerFactory {
   200  			return serverFactory
   201  		},
   202  	)
   203  
   204  	ctrlModelTag := coretesting.ModelTag
   205  	model1ModelUUID, err := utils.NewUUID()
   206  	c.Assert(err, jc.ErrorIsNil)
   207  	ctrlModel := mocks.NewMockModel(ctrl)
   208  	model1 := mocks.NewMockModel(ctrl)
   209  	ctrlModel.EXPECT().IsControllerModel().Return(true).AnyTimes()
   210  
   211  	ctrlState := mocks.NewMockState(ctrl)
   212  	state1 := mocks.NewMockState(ctrl)
   213  	ctrlState.EXPECT().Release().AnyTimes()
   214  	ctrlState.EXPECT().Model().Return(ctrlModel, nil)
   215  	state1.EXPECT().Release()
   216  
   217  	s.statePool.EXPECT().Get(ctrlModelTag.Id()).Return(ctrlState, nil)
   218  	s.blockChecker.EXPECT().ChangeAllowed().Return(nil)
   219  
   220  	// Decide/validate target version.
   221  	ctrlState.EXPECT().ControllerConfig().Return(controllerCfg, nil)
   222  	ctrlModel.EXPECT().Life().Return(state.Alive)
   223  	ctrlModel.EXPECT().AgentVersion().Return(version.MustParse("3.9.1"), nil)
   224  	ctrlModel.EXPECT().Type().Return(state.ModelTypeIAAS)
   225  	s.toolsFinder.EXPECT().FindAgents(common.FindAgentsParams{
   226  		Number:        version.MustParse("3.9.99"),
   227  		ControllerCfg: controllerCfg, ModelType: state.ModelTypeIAAS}).Return(
   228  		[]*coretools.Tools{
   229  			{Version: version.MustParseBinary("3.9.99-ubuntu-amd64")},
   230  		}, nil,
   231  	)
   232  
   233  	// 1. Check controller model.
   234  	// - check agent version;
   235  	ctrlModel.EXPECT().AgentVersion().Return(version.MustParse("3.9.1"), nil)
   236  	// - check mongo status;
   237  	ctrlState.EXPECT().MongoCurrentStatus().Return(&replicaset.Status{
   238  		Members: []replicaset.MemberStatus{
   239  			{
   240  				Id:      1,
   241  				Address: "1.1.1.1",
   242  				State:   replicaset.PrimaryState,
   243  			},
   244  			{
   245  				Id:      2,
   246  				Address: "2.2.2.2",
   247  				State:   replicaset.SecondaryState,
   248  			},
   249  			{
   250  				Id:      3,
   251  				Address: "3.3.3.3",
   252  				State:   replicaset.SecondaryState,
   253  			},
   254  		},
   255  	}, nil)
   256  	// - check mongo version;
   257  	s.statePool.EXPECT().MongoVersion().Return("4.4", nil)
   258  	// - check if the model has win machines;
   259  	ctrlState.EXPECT().MachineCountForBase(makeBases("windows", winVersions)).Return(nil, nil)
   260  	// - check if the model has deprecated ubuntu machines;
   261  	ctrlState.EXPECT().MachineCountForBase(makeBases("ubuntu", ubuntuVersions)).Return(nil, nil)
   262  	// - check LXD version.
   263  	// - check if model has charm store charms;
   264  	ctrlState.EXPECT().AllCharmURLs().Return(nil, errors.NotFoundf("charms"))
   265  	serverFactory.EXPECT().RemoteServer(s.cloudSpec).Return(server, nil)
   266  	server.EXPECT().ServerVersion().Return("5.2")
   267  
   268  	ctrlState.EXPECT().AllModelUUIDs().Return([]string{ctrlModelTag.Id(), model1ModelUUID.String()}, nil)
   269  
   270  	// 2. Check other models.
   271  	s.statePool.EXPECT().Get(model1ModelUUID.String()).Return(state1, nil)
   272  	state1.EXPECT().Model().Return(model1, nil)
   273  	model1.EXPECT().Life().Return(state.Alive)
   274  	// - check agent version;
   275  	model1.EXPECT().AgentVersion().Return(version.MustParse("2.9.1"), nil)
   276  	//  - check if model migration is ongoing;
   277  	model1.EXPECT().MigrationMode().Return(state.MigrationModeNone)
   278  	// - check if the model has win machines;
   279  	state1.EXPECT().MachineCountForBase(makeBases("windows", winVersions)).Return(nil, nil)
   280  	// - check if the model has deprecated ubuntu machines;
   281  	state1.EXPECT().MachineCountForBase(makeBases("ubuntu", ubuntuVersions)).Return(nil, nil)
   282  	// - check if model has charm store charms;
   283  	state1.EXPECT().AllCharmURLs().Return(nil, errors.NotFoundf("charms"))
   284  	// - check LXD version.
   285  	serverFactory.EXPECT().RemoteServer(s.cloudSpec).Return(server, nil)
   286  	server.EXPECT().ServerVersion().Return("5.2")
   287  
   288  	if !dryRun {
   289  		ctrlState.EXPECT().SetModelAgentVersion(version.MustParse("3.9.99"), nil, false).Return(nil)
   290  	}
   291  
   292  	result, err := api.UpgradeModel(
   293  		params.UpgradeModelParams{
   294  			ModelTag:      ctrlModelTag.String(),
   295  			TargetVersion: version.MustParse("3.9.99"),
   296  			AgentStream:   "",
   297  			DryRun:        dryRun,
   298  		},
   299  	)
   300  	c.Assert(err, jc.ErrorIsNil)
   301  	c.Assert(result, gc.DeepEquals, params.UpgradeModelResult{
   302  		ChosenVersion: version.MustParse("3.9.99"),
   303  	})
   304  }
   305  
   306  func (s *modelUpgradeSuite) TestUpgradeModelForControllerModelJuju3(c *gc.C) {
   307  	s.assertUpgradeModelForControllerModelJuju3(c, false)
   308  }
   309  
   310  func (s *modelUpgradeSuite) TestUpgradeModelForControllerModelJuju3DryRun(c *gc.C) {
   311  	s.assertUpgradeModelForControllerModelJuju3(c, true)
   312  }
   313  
   314  func (s *modelUpgradeSuite) TestUpgradeModelForControllerDyingHostedModelJuju3(c *gc.C) {
   315  	ctrl, api := s.getModelUpgraderAPI(c)
   316  	defer ctrl.Finish()
   317  
   318  	s.PatchValue(&upgradevalidation.MinAgentVersions, map[int]version.Number{
   319  		3: version.MustParse("2.9.1"),
   320  	})
   321  
   322  	server := upgradevalidationmocks.NewMockServer(ctrl)
   323  	serverFactory := upgradevalidationmocks.NewMockServerFactory(ctrl)
   324  	s.PatchValue(&upgradevalidation.NewServerFactory,
   325  		func(_ lxd.NewHTTPClientFunc) lxd.ServerFactory {
   326  			return serverFactory
   327  		},
   328  	)
   329  
   330  	ctrlModelTag := coretesting.ModelTag
   331  	model1ModelUUID, err := utils.NewUUID()
   332  	c.Assert(err, jc.ErrorIsNil)
   333  	ctrlModel := mocks.NewMockModel(ctrl)
   334  	model1 := mocks.NewMockModel(ctrl)
   335  	ctrlModel.EXPECT().IsControllerModel().Return(true).AnyTimes()
   336  
   337  	ctrlState := mocks.NewMockState(ctrl)
   338  	state1 := mocks.NewMockState(ctrl)
   339  	ctrlState.EXPECT().Release().AnyTimes()
   340  	ctrlState.EXPECT().Model().Return(ctrlModel, nil)
   341  	state1.EXPECT().Release()
   342  
   343  	s.statePool.EXPECT().Get(ctrlModelTag.Id()).Return(ctrlState, nil)
   344  	s.blockChecker.EXPECT().ChangeAllowed().Return(nil)
   345  
   346  	// Decide/validate target version.
   347  	ctrlState.EXPECT().ControllerConfig().Return(controllerCfg, nil)
   348  	ctrlModel.EXPECT().Life().Return(state.Alive)
   349  	ctrlModel.EXPECT().AgentVersion().Return(version.MustParse("3.9.1"), nil)
   350  	ctrlModel.EXPECT().Type().Return(state.ModelTypeIAAS)
   351  	s.toolsFinder.EXPECT().FindAgents(common.FindAgentsParams{
   352  		Number:        version.MustParse("3.9.99"),
   353  		ControllerCfg: controllerCfg, ModelType: state.ModelTypeIAAS}).Return(
   354  		[]*coretools.Tools{
   355  			{Version: version.MustParseBinary("3.9.99-ubuntu-amd64")},
   356  		}, nil,
   357  	)
   358  
   359  	// 1. Check controller model.
   360  	// - check agent version;
   361  	ctrlModel.EXPECT().AgentVersion().Return(version.MustParse("3.9.1"), nil)
   362  	// - check mongo status;
   363  	ctrlState.EXPECT().MongoCurrentStatus().Return(&replicaset.Status{
   364  		Members: []replicaset.MemberStatus{
   365  			{
   366  				Id:      1,
   367  				Address: "1.1.1.1",
   368  				State:   replicaset.PrimaryState,
   369  			},
   370  			{
   371  				Id:      2,
   372  				Address: "2.2.2.2",
   373  				State:   replicaset.SecondaryState,
   374  			},
   375  			{
   376  				Id:      3,
   377  				Address: "3.3.3.3",
   378  				State:   replicaset.SecondaryState,
   379  			},
   380  		},
   381  	}, nil)
   382  	// - check mongo version;
   383  	s.statePool.EXPECT().MongoVersion().Return("4.4", nil)
   384  	// - check if the model has win machines;
   385  	ctrlState.EXPECT().MachineCountForBase(makeBases("windows", winVersions)).Return(nil, nil)
   386  	// - check if the model has deprecated ubuntu machines;
   387  	ctrlState.EXPECT().MachineCountForBase(makeBases("ubuntu", ubuntuVersions)).Return(nil, nil)
   388  	// - check LXD version.
   389  	// - check if model has charm store charms;
   390  	ctrlState.EXPECT().AllCharmURLs().Return(nil, errors.NotFoundf("charms"))
   391  	serverFactory.EXPECT().RemoteServer(s.cloudSpec).Return(server, nil)
   392  	server.EXPECT().ServerVersion().Return("5.2")
   393  
   394  	ctrlState.EXPECT().AllModelUUIDs().Return([]string{ctrlModelTag.Id(), model1ModelUUID.String()}, nil)
   395  
   396  	// 2. Check other models.
   397  	s.statePool.EXPECT().Get(model1ModelUUID.String()).Return(state1, nil)
   398  	state1.EXPECT().Model().Return(model1, nil)
   399  	// Skip this dying model.
   400  	model1.EXPECT().Life().Return(state.Dying)
   401  
   402  	ctrlState.EXPECT().SetModelAgentVersion(version.MustParse("3.9.99"), nil, false).Return(nil)
   403  
   404  	result, err := api.UpgradeModel(
   405  		params.UpgradeModelParams{
   406  			ModelTag:      ctrlModelTag.String(),
   407  			TargetVersion: version.MustParse("3.9.99"),
   408  			AgentStream:   "",
   409  			DryRun:        false,
   410  		},
   411  	)
   412  	c.Assert(err, jc.ErrorIsNil)
   413  	c.Assert(result, gc.DeepEquals, params.UpgradeModelResult{
   414  		ChosenVersion: version.MustParse("3.9.99"),
   415  	})
   416  }
   417  
   418  func (s *modelUpgradeSuite) TestUpgradeModelForControllerModelJuju3Failed(c *gc.C) {
   419  	ctrl, api := s.getModelUpgraderAPI(c)
   420  	defer ctrl.Finish()
   421  
   422  	s.PatchValue(&upgradevalidation.MinAgentVersions, map[int]version.Number{
   423  		3: version.MustParse("2.9.2"),
   424  	})
   425  
   426  	server := upgradevalidationmocks.NewMockServer(ctrl)
   427  	serverFactory := upgradevalidationmocks.NewMockServerFactory(ctrl)
   428  	s.PatchValue(&upgradevalidation.NewServerFactory,
   429  		func(_ lxd.NewHTTPClientFunc) lxd.ServerFactory {
   430  			return serverFactory
   431  		},
   432  	)
   433  
   434  	ctrlModelTag := coretesting.ModelTag
   435  	model1ModelUUID, err := utils.NewUUID()
   436  	c.Assert(err, jc.ErrorIsNil)
   437  	ctrlModel := mocks.NewMockModel(ctrl)
   438  	model1 := mocks.NewMockModel(ctrl)
   439  	ctrlModel.EXPECT().IsControllerModel().Return(true).AnyTimes()
   440  
   441  	ctrlState := mocks.NewMockState(ctrl)
   442  	state1 := mocks.NewMockState(ctrl)
   443  	ctrlState.EXPECT().Release().AnyTimes()
   444  	ctrlState.EXPECT().Model().Return(ctrlModel, nil)
   445  	state1.EXPECT().Release()
   446  
   447  	s.statePool.EXPECT().Get(ctrlModelTag.Id()).Return(ctrlState, nil)
   448  
   449  	s.blockChecker.EXPECT().ChangeAllowed().Return(nil)
   450  
   451  	// Decide/validate target version.
   452  	ctrlState.EXPECT().ControllerConfig().Return(controllerCfg, nil)
   453  	ctrlModel.EXPECT().Life().Return(state.Alive)
   454  	ctrlModel.EXPECT().AgentVersion().Return(version.MustParse("2.9.1"), nil)
   455  	ctrlModel.EXPECT().Type().Return(state.ModelTypeIAAS)
   456  	s.toolsFinder.EXPECT().FindAgents(common.FindAgentsParams{
   457  		Number:        version.MustParse("3.9.99"),
   458  		ControllerCfg: controllerCfg, ModelType: state.ModelTypeIAAS}).Return(
   459  		[]*coretools.Tools{
   460  			{Version: version.MustParseBinary("3.9.99-ubuntu-amd64")},
   461  		}, nil,
   462  	)
   463  
   464  	// 1. Check controller model.
   465  	// - check agent version;
   466  	ctrlModel.EXPECT().AgentVersion().Return(version.MustParse("2.9.1"), nil)
   467  	// - check mongo status;
   468  	ctrlState.EXPECT().MongoCurrentStatus().Return(&replicaset.Status{
   469  		Members: []replicaset.MemberStatus{
   470  			{
   471  				Id:      1,
   472  				Address: "1.1.1.1",
   473  				State:   replicaset.FatalState,
   474  			},
   475  			{
   476  				Id:      2,
   477  				Address: "2.2.2.2",
   478  				State:   replicaset.ArbiterState,
   479  			},
   480  			{
   481  				Id:      3,
   482  				Address: "3.3.3.3",
   483  				State:   replicaset.RecoveringState,
   484  			},
   485  		},
   486  	}, nil)
   487  	// - check mongo version;
   488  	s.statePool.EXPECT().MongoVersion().Return("4.3", nil)
   489  	// - check if the model has win machines;
   490  	ctrlState.EXPECT().MachineCountForBase(makeBases("windows", winVersions)).Return(map[string]int{"win10": 1, "win7": 2}, nil)
   491  	// - check if the model has deprecated ubuntu machines;
   492  	ctrlState.EXPECT().MachineCountForBase(makeBases("ubuntu", ubuntuVersions)).Return(map[string]int{"xenial": 2}, nil)
   493  	// - check if model has charm store charms;
   494  	ctrlState.EXPECT().AllCharmURLs().Return(nil, errors.NotFoundf("charms"))
   495  	// - check LXD version.
   496  	serverFactory.EXPECT().RemoteServer(s.cloudSpec).Return(server, nil)
   497  	server.EXPECT().ServerVersion().Return("4.0")
   498  	ctrlModel.EXPECT().Owner().Return(names.NewUserTag("admin"))
   499  	ctrlModel.EXPECT().Name().Return("controller")
   500  
   501  	ctrlState.EXPECT().AllModelUUIDs().Return([]string{ctrlModelTag.Id(), model1ModelUUID.String()}, nil)
   502  	// 2. Check other models.
   503  	s.statePool.EXPECT().Get(model1ModelUUID.String()).Return(state1, nil)
   504  	state1.EXPECT().Model().Return(model1, nil)
   505  	model1.EXPECT().Life().Return(state.Alive)
   506  	// - check agent version;
   507  	model1.EXPECT().AgentVersion().Return(version.MustParse("2.9.0"), nil)
   508  	//  - check if model migration is ongoing;
   509  	model1.EXPECT().MigrationMode().Return(state.MigrationModeExporting)
   510  	// - check if the model has win machines;
   511  	state1.EXPECT().MachineCountForBase(makeBases("windows", winVersions)).Return(map[string]int{"win10": 1, "win7": 3}, nil)
   512  	// - check if the model has deprecated ubuntu machines;
   513  	state1.EXPECT().MachineCountForBase(makeBases("ubuntu", ubuntuVersions)).Return(map[string]int{
   514  		"artful": 1, "cosmic": 2, "disco": 3, "eoan": 4, "groovy": 5,
   515  		"hirsute": 6, "impish": 7, "precise": 8, "quantal": 9, "raring": 10,
   516  		"saucy": 11, "trusty": 12, "utopic": 13, "vivid": 14, "wily": 15,
   517  		"xenial": 16, "yakkety": 17, "zesty": 18,
   518  	}, nil)
   519  	// - check if model has charm store charms;
   520  	state1.EXPECT().AllCharmURLs().Return(nil, errors.NotFoundf("charms"))
   521  	// - check LXD version.
   522  	serverFactory.EXPECT().RemoteServer(s.cloudSpec).Return(server, nil)
   523  	server.EXPECT().ServerVersion().Return("4.0")
   524  	model1.EXPECT().Owner().Return(names.NewUserTag("admin"))
   525  	model1.EXPECT().Name().Return("model-1")
   526  
   527  	result, err := api.UpgradeModel(
   528  		params.UpgradeModelParams{
   529  			ModelTag:      ctrlModelTag.String(),
   530  			TargetVersion: version.MustParse("3.9.99"),
   531  		},
   532  	)
   533  	c.Assert(err, jc.ErrorIsNil)
   534  	c.Assert(result.Error.Error(), gc.Equals, `
   535  cannot upgrade to "3.9.99" due to issues with these models:
   536  "admin/controller":
   537  - upgrading a controller to a newer major.minor version 3.9 not supported
   538  - unable to upgrade, database node 1 (1.1.1.1) has state FATAL, node 2 (2.2.2.2) has state ARBITER, node 3 (3.3.3.3) has state RECOVERING
   539  - mongo version has to be "4.4" at least, but current version is "4.3"
   540  - the model hosts deprecated windows machine(s): win10(1) win7(2)
   541  - the model hosts deprecated ubuntu machine(s): xenial(2)
   542  - LXD version has to be at least "5.0.0", but current version is only "4.0.0"
   543  "admin/model-1":
   544  - current model ("2.9.0") has to be upgraded to "2.9.2" at least
   545  - model is under "exporting" mode, upgrade blocked
   546  - the model hosts deprecated windows machine(s): win10(1) win7(3)
   547  - the model hosts deprecated ubuntu machine(s): artful(1) cosmic(2) disco(3) eoan(4) groovy(5) hirsute(6) impish(7) precise(8) quantal(9) raring(10) saucy(11) trusty(12) utopic(13) vivid(14) wily(15) xenial(16) yakkety(17) zesty(18)
   548  - LXD version has to be at least "5.0.0", but current version is only "4.0.0"`[1:])
   549  }
   550  
   551  func (s *modelUpgradeSuite) assertUpgradeModelJuju3(c *gc.C, ctrlModelVers string, dryRun bool) {
   552  	ctrl, api := s.getModelUpgraderAPI(c)
   553  	defer ctrl.Finish()
   554  
   555  	server := upgradevalidationmocks.NewMockServer(ctrl)
   556  	serverFactory := upgradevalidationmocks.NewMockServerFactory(ctrl)
   557  	s.PatchValue(&upgradevalidation.NewServerFactory,
   558  		func(_ lxd.NewHTTPClientFunc) lxd.ServerFactory {
   559  			return serverFactory
   560  		},
   561  	)
   562  
   563  	modelUUID := coretesting.ModelTag.Id()
   564  	model := mocks.NewMockModel(ctrl)
   565  	st := mocks.NewMockState(ctrl)
   566  	st.EXPECT().Release().AnyTimes()
   567  
   568  	s.statePool.EXPECT().Get(modelUUID).AnyTimes().Return(st, nil)
   569  	st.EXPECT().Model().AnyTimes().Return(model, nil)
   570  	ctrlModel := mocks.NewMockModel(ctrl)
   571  
   572  	var agentStream string
   573  
   574  	s.blockChecker.EXPECT().ChangeAllowed().Return(nil)
   575  
   576  	// Decide/validate target version.
   577  	st.EXPECT().ControllerConfig().Return(controllerCfg, nil)
   578  	model.EXPECT().Life().Return(state.Alive)
   579  	model.EXPECT().AgentVersion().Return(version.MustParse("2.9.1"), nil)
   580  	model.EXPECT().IsControllerModel().Return(false).AnyTimes()
   581  	s.statePool.EXPECT().ControllerModel().Return(ctrlModel, nil)
   582  	ctrlModel.EXPECT().AgentVersion().Return(version.MustParse(ctrlModelVers), nil)
   583  	if ctrlModelVers != "3.9.99" {
   584  		model.EXPECT().Type().Return(state.ModelTypeIAAS)
   585  		s.toolsFinder.EXPECT().FindAgents(common.FindAgentsParams{
   586  			Number:        version.MustParse("3.9.99"),
   587  			ControllerCfg: controllerCfg, ModelType: state.ModelTypeIAAS}).Return(
   588  			[]*coretools.Tools{
   589  				{Version: version.MustParseBinary("3.9.99-ubuntu-amd64")},
   590  			}, nil,
   591  		)
   592  	}
   593  
   594  	// - check no upgrade series in process.
   595  	st.EXPECT().HasUpgradeSeriesLocks().Return(false, nil)
   596  	// - check if model has charm store charms;
   597  	st.EXPECT().AllCharmURLs().Return(nil, errors.NotFoundf("charms"))
   598  	// - check if the model has win machines;
   599  	st.EXPECT().MachineCountForBase(makeBases("windows", winVersions)).Return(nil, nil)
   600  	// - check if the model has deprecated ubuntu machines;
   601  	st.EXPECT().MachineCountForBase(makeBases("ubuntu", ubuntuVersions)).Return(nil, nil)
   602  	// - check LXD version.
   603  	serverFactory.EXPECT().RemoteServer(s.cloudSpec).Return(server, nil)
   604  	server.EXPECT().ServerVersion().Return("5.2")
   605  
   606  	if !dryRun {
   607  		st.EXPECT().SetModelAgentVersion(version.MustParse("3.9.99"), nil, false).Return(nil)
   608  	}
   609  
   610  	result, err := api.UpgradeModel(
   611  		params.UpgradeModelParams{
   612  			ModelTag:      coretesting.ModelTag.String(),
   613  			TargetVersion: version.MustParse("3.9.99"),
   614  			AgentStream:   agentStream,
   615  			DryRun:        dryRun,
   616  		},
   617  	)
   618  	c.Assert(err, jc.ErrorIsNil)
   619  	c.Assert(result, gc.DeepEquals, params.UpgradeModelResult{
   620  		ChosenVersion: version.MustParse("3.9.99"),
   621  	})
   622  }
   623  
   624  func (s *modelUpgradeSuite) TestUpgradeModelJuju3(c *gc.C) {
   625  	s.assertUpgradeModelJuju3(c, "3.10.0", false)
   626  }
   627  
   628  func (s *modelUpgradeSuite) TestUpgradeModelJuju3SameAsController(c *gc.C) {
   629  	s.assertUpgradeModelJuju3(c, "3.9.99", false)
   630  }
   631  
   632  func (s *modelUpgradeSuite) TestUpgradeModelJuju3DryRun(c *gc.C) {
   633  	s.assertUpgradeModelJuju3(c, "3.10.0", true)
   634  }
   635  
   636  func (s *modelUpgradeSuite) TestUpgradeModelJuju3Failed(c *gc.C) {
   637  	ctrl, api := s.getModelUpgraderAPI(c)
   638  	defer ctrl.Finish()
   639  
   640  	server := upgradevalidationmocks.NewMockServer(ctrl)
   641  	serverFactory := upgradevalidationmocks.NewMockServerFactory(ctrl)
   642  	s.PatchValue(&upgradevalidation.NewServerFactory,
   643  		func(_ lxd.NewHTTPClientFunc) lxd.ServerFactory {
   644  			return serverFactory
   645  		},
   646  	)
   647  
   648  	modelUUID := coretesting.ModelTag.Id()
   649  	model := mocks.NewMockModel(ctrl)
   650  	st := mocks.NewMockState(ctrl)
   651  	st.EXPECT().Release()
   652  
   653  	s.statePool.EXPECT().Get(modelUUID).AnyTimes().Return(st, nil)
   654  	st.EXPECT().Model().AnyTimes().Return(model, nil)
   655  
   656  	ctrlModel := mocks.NewMockModel(ctrl)
   657  
   658  	s.blockChecker.EXPECT().ChangeAllowed().Return(nil)
   659  
   660  	// Decide/validate target version.
   661  	st.EXPECT().ControllerConfig().Return(controllerCfg, nil)
   662  	model.EXPECT().Life().Return(state.Alive)
   663  	model.EXPECT().AgentVersion().Return(version.MustParse("2.9.1"), nil)
   664  	model.EXPECT().Type().Return(state.ModelTypeIAAS)
   665  	model.EXPECT().IsControllerModel().Return(false).AnyTimes()
   666  	s.statePool.EXPECT().ControllerModel().Return(ctrlModel, nil)
   667  	ctrlModel.EXPECT().AgentVersion().Return(version.MustParse("3.10.0"), nil)
   668  	s.toolsFinder.EXPECT().FindAgents(common.FindAgentsParams{
   669  		Number:        version.MustParse("3.9.99"),
   670  		ControllerCfg: controllerCfg, ModelType: state.ModelTypeIAAS}).Return(
   671  		[]*coretools.Tools{
   672  			{Version: version.MustParseBinary("3.9.99-ubuntu-amd64")},
   673  		}, nil,
   674  	)
   675  
   676  	// - check no upgrade series in process.
   677  	st.EXPECT().HasUpgradeSeriesLocks().Return(true, nil)
   678  
   679  	// - check if the model has win machines;
   680  	st.EXPECT().MachineCountForBase(makeBases("windows", winVersions)).Return(map[string]int{"win10": 1, "win7": 3}, nil)
   681  	// - check if the model has deprecated ubuntu machines;
   682  	st.EXPECT().MachineCountForBase(makeBases("ubuntu", ubuntuVersions)).Return(map[string]int{
   683  		"artful": 1, "cosmic": 2, "disco": 3, "eoan": 4, "groovy": 5,
   684  		"hirsute": 6, "impish": 7, "precise": 8, "quantal": 9, "raring": 10,
   685  		"saucy": 11, "trusty": 12, "utopic": 13, "vivid": 14, "wily": 15,
   686  		"xenial": 16, "yakkety": 17, "zesty": 18,
   687  	}, nil)
   688  	// - check if model has charm store charms;
   689  	st.EXPECT().AllCharmURLs().Return(nil, errors.NotFoundf("charms"))
   690  	// - check LXD version.
   691  	serverFactory.EXPECT().RemoteServer(s.cloudSpec).Return(server, nil)
   692  	server.EXPECT().ServerVersion().Return("4.0")
   693  	model.EXPECT().Owner().Return(names.NewUserTag("admin"))
   694  	model.EXPECT().Name().Return("model-1")
   695  
   696  	result, err := api.UpgradeModel(
   697  		params.UpgradeModelParams{
   698  			ModelTag:      coretesting.ModelTag.String(),
   699  			TargetVersion: version.MustParse("3.9.99"),
   700  		},
   701  	)
   702  	c.Assert(err, jc.ErrorIsNil)
   703  	c.Assert(result.Error.Error(), gc.Equals, `
   704  cannot upgrade to "3.9.99" due to issues with these models:
   705  "admin/model-1":
   706  - unexpected upgrade series lock found
   707  - the model hosts deprecated windows machine(s): win10(1) win7(3)
   708  - the model hosts deprecated ubuntu machine(s): artful(1) cosmic(2) disco(3) eoan(4) groovy(5) hirsute(6) impish(7) precise(8) quantal(9) raring(10) saucy(11) trusty(12) utopic(13) vivid(14) wily(15) xenial(16) yakkety(17) zesty(18)
   709  - LXD version has to be at least "5.0.0", but current version is only "4.0.0"`[1:])
   710  }
   711  
   712  func (s *modelUpgradeSuite) TestCannotUpgradePastControllerVersion(c *gc.C) {
   713  	ctrl, api := s.getModelUpgraderAPI(c)
   714  	defer ctrl.Finish()
   715  
   716  	modelUUID := coretesting.ModelTag.Id()
   717  	model := mocks.NewMockModel(ctrl)
   718  	st := mocks.NewMockState(ctrl)
   719  	st.EXPECT().Release().AnyTimes()
   720  
   721  	s.statePool.EXPECT().Get(modelUUID).AnyTimes().Return(st, nil)
   722  	st.EXPECT().Model().AnyTimes().Return(model, nil)
   723  	ctrlModel := mocks.NewMockModel(ctrl)
   724  
   725  	s.blockChecker.EXPECT().ChangeAllowed().Return(nil)
   726  
   727  	st.EXPECT().ControllerConfig().Return(controllerCfg, nil)
   728  	model.EXPECT().Life().Return(state.Alive)
   729  	model.EXPECT().AgentVersion().Return(version.MustParse("2.9.1"), nil)
   730  	model.EXPECT().IsControllerModel().Return(false)
   731  	s.statePool.EXPECT().ControllerModel().Return(ctrlModel, nil)
   732  	ctrlModel.EXPECT().AgentVersion().Return(version.MustParse("3.9.99"), nil)
   733  
   734  	_, err := api.UpgradeModel(
   735  		params.UpgradeModelParams{
   736  			ModelTag:      coretesting.ModelTag.String(),
   737  			TargetVersion: version.MustParse("3.12.0"),
   738  		},
   739  	)
   740  	c.Assert(err, gc.ErrorMatches, `cannot upgrade to a version "3.12.0" greater than that of the controller "3.9.99"`)
   741  }
   742  
   743  func (s *modelUpgradeSuite) TestAbortCurrentUpgrade(c *gc.C) {
   744  	ctrl, api := s.getModelUpgraderAPI(c)
   745  	defer ctrl.Finish()
   746  
   747  	modelUUID := coretesting.ModelTag.Id()
   748  	st := mocks.NewMockState(ctrl)
   749  	st.EXPECT().Release()
   750  
   751  	gomock.InOrder(
   752  		s.blockChecker.EXPECT().ChangeAllowed().Return(nil),
   753  		s.statePool.EXPECT().Get(modelUUID).Return(st, nil),
   754  		st.EXPECT().AbortCurrentUpgrade().Return(nil),
   755  	)
   756  	err := api.AbortModelUpgrade(params.ModelParam{ModelTag: coretesting.ModelTag.String()})
   757  	c.Assert(err, jc.ErrorIsNil)
   758  }
   759  
   760  func (s *modelUpgradeSuite) TestFindToolsIAAS(c *gc.C) {
   761  	ctrl, api := s.getModelUpgraderAPI(c)
   762  	defer ctrl.Finish()
   763  
   764  	simpleStreams := []*coretools.Tools{
   765  		{Version: version.MustParseBinary("2.9.6-ubuntu-amd64")},
   766  	}
   767  
   768  	s.toolsFinder.EXPECT().FindAgents(common.FindAgentsParams{
   769  		MajorVersion: 2, ModelType: state.ModelTypeIAAS}).Return(simpleStreams, nil)
   770  
   771  	result, err := api.FindAgents(common.FindAgentsParams{MajorVersion: 2, ModelType: state.ModelTypeIAAS})
   772  	c.Assert(err, jc.ErrorIsNil)
   773  	c.Assert(result, gc.DeepEquals, coretools.Versions{
   774  		&coretools.Tools{Version: version.MustParseBinary("2.9.6-ubuntu-amd64")},
   775  	})
   776  }
   777  
   778  func (s *modelUpgradeSuite) TestFindToolsCAASReleased(c *gc.C) {
   779  	ctrl, api := s.getModelUpgraderAPI(c)
   780  	defer ctrl.Finish()
   781  
   782  	st := mocks.NewMockState(ctrl)
   783  	model := mocks.NewMockModel(ctrl)
   784  	st.EXPECT().Model().AnyTimes().Return(model, nil)
   785  
   786  	simpleStreams := []*coretools.Tools{
   787  		{Version: version.MustParseBinary("2.9.9-ubuntu-amd64")},
   788  		{Version: version.MustParseBinary("2.9.10-ubuntu-amd64")},
   789  		{Version: version.MustParseBinary("2.9.11-ubuntu-amd64")},
   790  	}
   791  	s.PatchValue(&coreos.HostOS, func() ostype.OSType { return ostype.Ubuntu })
   792  
   793  	gomock.InOrder(
   794  		s.toolsFinder.EXPECT().FindAgents(common.FindAgentsParams{
   795  			MajorVersion: 2, MinorVersion: 9,
   796  			ModelType: state.ModelTypeCAAS,
   797  		}).Return(simpleStreams, nil),
   798  		s.registryProvider.EXPECT().Tags("jujud-operator").Return(coretools.Versions{
   799  			image.NewImageInfo(version.MustParse("2.9.8")),
   800  			image.NewImageInfo(version.MustParse("2.9.9")),
   801  			image.NewImageInfo(version.MustParse("2.9.10.1")),
   802  			image.NewImageInfo(version.MustParse("2.9.10")),
   803  			image.NewImageInfo(version.MustParse("2.9.11")),
   804  			image.NewImageInfo(version.MustParse("2.9.12")), // skip: it's not released in simplestream yet.
   805  		}, nil),
   806  		s.registryProvider.EXPECT().GetArchitecture("jujud-operator", "2.9.9").Return("amd64", nil),
   807  		s.registryProvider.EXPECT().GetArchitecture("jujud-operator", "2.9.10.1").Return("amd64", nil),
   808  		s.registryProvider.EXPECT().GetArchitecture("jujud-operator", "2.9.10").Return("amd64", nil),
   809  		s.registryProvider.EXPECT().GetArchitecture("jujud-operator", "2.9.11").Return("amd64", nil),
   810  		s.registryProvider.EXPECT().Close().Return(nil),
   811  	)
   812  
   813  	result, err := api.FindAgents(common.FindAgentsParams{MajorVersion: 2, MinorVersion: 9, ModelType: state.ModelTypeCAAS})
   814  	c.Assert(err, jc.ErrorIsNil)
   815  	c.Assert(result, gc.DeepEquals, coretools.Versions{
   816  		&coretools.Tools{Version: version.MustParseBinary("2.9.9-ubuntu-amd64")},
   817  		&coretools.Tools{Version: version.MustParseBinary("2.9.10.1-ubuntu-amd64")},
   818  		&coretools.Tools{Version: version.MustParseBinary("2.9.10-ubuntu-amd64")},
   819  		&coretools.Tools{Version: version.MustParseBinary("2.9.11-ubuntu-amd64")},
   820  	})
   821  }
   822  
   823  func (s *modelUpgradeSuite) TestFindToolsCAASReleasedExact(c *gc.C) {
   824  	ctrl, api := s.getModelUpgraderAPI(c)
   825  	defer ctrl.Finish()
   826  
   827  	st := mocks.NewMockState(ctrl)
   828  	model := mocks.NewMockModel(ctrl)
   829  	st.EXPECT().Model().AnyTimes().Return(model, nil)
   830  
   831  	simpleStreams := []*coretools.Tools{
   832  		{Version: version.MustParseBinary("2.9.10-ubuntu-amd64")},
   833  	}
   834  	s.PatchValue(&coreos.HostOS, func() ostype.OSType { return ostype.Ubuntu })
   835  
   836  	gomock.InOrder(
   837  		s.toolsFinder.EXPECT().FindAgents(common.FindAgentsParams{
   838  			Number:    version.MustParse("2.9.10"),
   839  			ModelType: state.ModelTypeCAAS,
   840  		}).Return(simpleStreams, nil),
   841  		s.registryProvider.EXPECT().Tags("jujud-operator").Return(coretools.Versions{
   842  			image.NewImageInfo(version.MustParse("2.9.8")),
   843  			image.NewImageInfo(version.MustParse("2.9.9")),
   844  			image.NewImageInfo(version.MustParse("2.9.10.1")),
   845  			image.NewImageInfo(version.MustParse("2.9.10")),
   846  			image.NewImageInfo(version.MustParse("2.9.11")),
   847  			image.NewImageInfo(version.MustParse("2.9.12")),
   848  		}, nil),
   849  		s.registryProvider.EXPECT().GetArchitecture("jujud-operator", "2.9.10").Return("amd64", nil),
   850  		s.registryProvider.EXPECT().Close().Return(nil),
   851  	)
   852  
   853  	result, err := api.FindAgents(common.FindAgentsParams{
   854  		Number: version.MustParse("2.9.10"), ModelType: state.ModelTypeCAAS})
   855  	c.Assert(err, jc.ErrorIsNil)
   856  	c.Assert(result, gc.DeepEquals, coretools.Versions{
   857  		&coretools.Tools{Version: version.MustParseBinary("2.9.10-ubuntu-amd64")},
   858  	})
   859  }
   860  
   861  func (s *modelUpgradeSuite) TestFindToolsCAASNonReleased(c *gc.C) {
   862  	ctrl, api := s.getModelUpgraderAPI(c)
   863  	defer ctrl.Finish()
   864  
   865  	st := mocks.NewMockState(ctrl)
   866  	model := mocks.NewMockModel(ctrl)
   867  	st.EXPECT().Model().AnyTimes().Return(model, nil)
   868  
   869  	simpleStreams := []*coretools.Tools{
   870  		{Version: version.MustParseBinary("2.9.9-ubuntu-amd64")},
   871  		{Version: version.MustParseBinary("2.9.10-ubuntu-amd64")},
   872  		{Version: version.MustParseBinary("2.9.11-ubuntu-amd64")},
   873  		{Version: version.MustParseBinary("2.9.12-ubuntu-amd64")},
   874  	}
   875  	s.PatchValue(&coreos.HostOS, func() ostype.OSType { return ostype.Ubuntu })
   876  
   877  	gomock.InOrder(
   878  		s.toolsFinder.EXPECT().FindAgents(common.FindAgentsParams{
   879  			MajorVersion: 2, MinorVersion: 9, AgentStream: envtools.DevelStream,
   880  			ModelType: state.ModelTypeCAAS,
   881  		}).Return(simpleStreams, nil),
   882  		s.registryProvider.EXPECT().Tags("jujud-operator").Return(coretools.Versions{
   883  			image.NewImageInfo(version.MustParse("2.9.8")), // skip: it's not released in simplestream yet.
   884  			image.NewImageInfo(version.MustParse("2.9.9")),
   885  			image.NewImageInfo(version.MustParse("2.9.10.1")),
   886  			image.NewImageInfo(version.MustParse("2.9.10")),
   887  			image.NewImageInfo(version.MustParse("2.9.11")),
   888  			image.NewImageInfo(version.MustParse("2.9.12")),
   889  			image.NewImageInfo(version.MustParse("2.9.13")), // skip: it's not released in simplestream yet.
   890  		}, nil),
   891  		s.registryProvider.EXPECT().GetArchitecture("jujud-operator", "2.9.9").Return("amd64", nil),
   892  		s.registryProvider.EXPECT().GetArchitecture("jujud-operator", "2.9.10.1").Return("amd64", nil),
   893  		s.registryProvider.EXPECT().GetArchitecture("jujud-operator", "2.9.10").Return("amd64", nil),
   894  		s.registryProvider.EXPECT().GetArchitecture("jujud-operator", "2.9.11").Return("amd64", nil),
   895  		s.registryProvider.EXPECT().GetArchitecture("jujud-operator", "2.9.12").Return("", errors.NotFoundf("2.9.12")), // This can only happen on a non-official registry account.
   896  		s.registryProvider.EXPECT().Close().Return(nil),
   897  	)
   898  
   899  	result, err := api.FindAgents(common.FindAgentsParams{
   900  		MajorVersion: 2, MinorVersion: 9, AgentStream: envtools.DevelStream,
   901  		ModelType: state.ModelTypeCAAS,
   902  	})
   903  	c.Assert(err, jc.ErrorIsNil)
   904  	c.Assert(result, gc.DeepEquals, coretools.Versions{
   905  		&coretools.Tools{Version: version.MustParseBinary("2.9.9-ubuntu-amd64")},
   906  		&coretools.Tools{Version: version.MustParseBinary("2.9.10.1-ubuntu-amd64")},
   907  		&coretools.Tools{Version: version.MustParseBinary("2.9.10-ubuntu-amd64")},
   908  		&coretools.Tools{Version: version.MustParseBinary("2.9.11-ubuntu-amd64")},
   909  	})
   910  }
   911  
   912  func (s *modelUpgradeSuite) TestDecideVersionFindToolUseAgentVersionMajorMinor(c *gc.C) {
   913  	ctrl, api := s.getModelUpgraderAPI(c)
   914  	defer ctrl.Finish()
   915  
   916  	st := mocks.NewMockState(ctrl)
   917  	model := mocks.NewMockModel(ctrl)
   918  	st.EXPECT().Model().AnyTimes().Return(model, nil)
   919  
   920  	s.toolsFinder.EXPECT().FindAgents(common.FindAgentsParams{
   921  		MajorVersion: 3, MinorVersion: 666,
   922  		ModelType: state.ModelTypeIAAS,
   923  	}).Return(nil, errors.New(`fail to exit early`))
   924  
   925  	targetVersion, err := api.DecideVersion(
   926  		version.MustParse("3.9.99"), common.FindAgentsParams{
   927  			MajorVersion: 3, MinorVersion: 666, ModelType: state.ModelTypeIAAS},
   928  	)
   929  	c.Assert(err, gc.ErrorMatches, `cannot find agents from simple streams: fail to exit early`)
   930  	c.Assert(targetVersion, gc.DeepEquals, version.Zero)
   931  }
   932  
   933  func (s *modelUpgradeSuite) TestDecideVersionFindToolUseTargetMajor(c *gc.C) {
   934  	ctrl, api := s.getModelUpgraderAPI(c)
   935  	defer ctrl.Finish()
   936  
   937  	st := mocks.NewMockState(ctrl)
   938  	model := mocks.NewMockModel(ctrl)
   939  	st.EXPECT().Model().AnyTimes().Return(model, nil)
   940  
   941  	s.toolsFinder.EXPECT().FindAgents(common.FindAgentsParams{
   942  		Number:    version.MustParse("4.9.99"),
   943  		ModelType: state.ModelTypeIAAS,
   944  	}).Return(nil, errors.New(`fail to exit early`))
   945  
   946  	targetVersion, err := api.DecideVersion(
   947  		version.MustParse("3.9.99"),
   948  		common.FindAgentsParams{Number: version.MustParse("4.9.99"), ModelType: state.ModelTypeIAAS},
   949  	)
   950  	c.Assert(err, gc.ErrorMatches, `cannot find agents from simple streams: fail to exit early`)
   951  	c.Assert(targetVersion, gc.DeepEquals, version.Zero)
   952  }
   953  
   954  func (s *modelUpgradeSuite) TestDecideVersionValidateAndUseTargetVersion(c *gc.C) {
   955  	ctrl, api := s.getModelUpgraderAPI(c)
   956  	defer ctrl.Finish()
   957  
   958  	st := mocks.NewMockState(ctrl)
   959  	model := mocks.NewMockModel(ctrl)
   960  	st.EXPECT().Model().AnyTimes().Return(model, nil)
   961  
   962  	simpleStreams := []*coretools.Tools{
   963  		{Version: version.MustParseBinary("3.9.98-ubuntu-amd64")},
   964  	}
   965  
   966  	s.toolsFinder.EXPECT().FindAgents(common.FindAgentsParams{
   967  		Number: version.MustParse("3.9.98"), ModelType: state.ModelTypeIAAS,
   968  	}).Return(simpleStreams, nil)
   969  
   970  	targetVersion, err := api.DecideVersion(
   971  		version.MustParse("2.9.99"),
   972  		common.FindAgentsParams{
   973  			Number: version.MustParse("3.9.98"), ModelType: state.ModelTypeIAAS},
   974  	)
   975  	c.Assert(err, jc.ErrorIsNil)
   976  	c.Assert(targetVersion, gc.DeepEquals, version.MustParse("3.9.98"))
   977  }
   978  
   979  func (s *modelUpgradeSuite) TestDecideVersionNewestMinor(c *gc.C) {
   980  	ctrl, api := s.getModelUpgraderAPI(c)
   981  	defer ctrl.Finish()
   982  
   983  	st := mocks.NewMockState(ctrl)
   984  	model := mocks.NewMockModel(ctrl)
   985  	st.EXPECT().Model().AnyTimes().Return(model, nil)
   986  
   987  	simpleStreams := []*coretools.Tools{
   988  		{Version: version.MustParseBinary("2.9.100-ubuntu-amd64")},
   989  		{Version: version.MustParseBinary("2.10.99-ubuntu-amd64")},
   990  		{Version: version.MustParseBinary("2.11.99-ubuntu-amd64")},
   991  		{Version: version.MustParseBinary("3.666.0-ubuntu-amd64")},
   992  	}
   993  
   994  	s.toolsFinder.EXPECT().FindAgents(common.FindAgentsParams{
   995  		MajorVersion: 2,
   996  		ModelType:    state.ModelTypeIAAS,
   997  	}).Return(simpleStreams, nil)
   998  
   999  	targetVersion, err := api.DecideVersion(
  1000  		version.MustParse("2.9.99"),
  1001  		common.FindAgentsParams{
  1002  			MajorVersion: 2, MinorVersion: 0,
  1003  			ModelType: state.ModelTypeIAAS},
  1004  	)
  1005  	c.Assert(err, jc.ErrorIsNil)
  1006  	c.Assert(targetVersion, gc.DeepEquals, version.MustParse("2.9.100"))
  1007  }
  1008  
  1009  func (s *modelUpgradeSuite) TestDecideVersionIgnoresNewerMajor(c *gc.C) {
  1010  	ctrl, api := s.getModelUpgraderAPI(c)
  1011  	defer ctrl.Finish()
  1012  
  1013  	st := mocks.NewMockState(ctrl)
  1014  	model := mocks.NewMockModel(ctrl)
  1015  	st.EXPECT().Model().AnyTimes().Return(model, nil)
  1016  
  1017  	simpleStreams := []*coretools.Tools{
  1018  		{Version: version.MustParseBinary("2.9.100-ubuntu-amd64")},
  1019  		{Version: version.MustParseBinary("3.666.0-ubuntu-amd64")},
  1020  	}
  1021  
  1022  	s.toolsFinder.EXPECT().FindAgents(common.FindAgentsParams{
  1023  		MajorVersion: 2,
  1024  		ModelType:    state.ModelTypeIAAS,
  1025  	}).Return(simpleStreams, nil)
  1026  
  1027  	targetVersion, err := api.DecideVersion(
  1028  		version.MustParse("2.9.99"),
  1029  		common.FindAgentsParams{
  1030  			MajorVersion: 2,
  1031  			ModelType:    state.ModelTypeIAAS},
  1032  	)
  1033  	c.Assert(err, jc.ErrorIsNil)
  1034  	c.Assert(targetVersion, gc.DeepEquals, version.MustParse("2.9.100"))
  1035  }