github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/upgrades/upgrade_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package upgrades_test
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"path/filepath"
    10  	"strings"
    11  	stdtesting "testing"
    12  
    13  	"github.com/juju/names"
    14  	jc "github.com/juju/testing/checkers"
    15  	gc "gopkg.in/check.v1"
    16  
    17  	"github.com/juju/juju/agent"
    18  	"github.com/juju/juju/api"
    19  	"github.com/juju/juju/apiserver/params"
    20  	"github.com/juju/juju/mongo"
    21  	"github.com/juju/juju/state"
    22  	"github.com/juju/juju/state/multiwatcher"
    23  	coretesting "github.com/juju/juju/testing"
    24  	"github.com/juju/juju/upgrades"
    25  	"github.com/juju/juju/version"
    26  )
    27  
    28  func TestPackage(t *stdtesting.T) {
    29  	coretesting.MgoTestPackage(t)
    30  }
    31  
    32  // assertStateSteps is a helper that ensures that the given
    33  // state-based upgrade steps match what is expected for that version
    34  // and that the steps have been added to the global upgrade operations
    35  // list.
    36  func assertStateSteps(c *gc.C, ver version.Number, expectedSteps []string) {
    37  	findAndCheckSteps(c, (*upgrades.StateUpgradeOperations)(), ver, expectedSteps)
    38  }
    39  
    40  // assertSteps is a helper that ensures that the given API-based
    41  // upgrade steps match what is expected for that version and that the
    42  // steps have been added to the global upgrade operations list.
    43  func assertSteps(c *gc.C, ver version.Number, expectedSteps []string) {
    44  	findAndCheckSteps(c, (*upgrades.UpgradeOperations)(), ver, expectedSteps)
    45  }
    46  
    47  func findAndCheckSteps(c *gc.C, ops []upgrades.Operation, ver version.Number, expectedSteps []string) {
    48  	for _, op := range ops {
    49  		if op.TargetVersion() == ver {
    50  			assertExpectedSteps(c, op.Steps(), expectedSteps)
    51  			return
    52  		}
    53  	}
    54  	if len(expectedSteps) > 0 {
    55  		c.Fatal("upgrade operations for this version are not hooked up")
    56  	}
    57  }
    58  
    59  // assertExpectedSteps is a helper function used to check that the upgrade steps match
    60  // what is expected for a version.
    61  func assertExpectedSteps(c *gc.C, steps []upgrades.Step, expectedSteps []string) {
    62  	c.Assert(steps, gc.HasLen, len(expectedSteps))
    63  
    64  	var stepNames = make([]string, len(steps))
    65  	for i, step := range steps {
    66  		stepNames[i] = step.Description()
    67  	}
    68  	c.Assert(stepNames, gc.DeepEquals, expectedSteps)
    69  }
    70  
    71  type upgradeSuite struct {
    72  	coretesting.BaseSuite
    73  }
    74  
    75  var _ = gc.Suite(&upgradeSuite{})
    76  
    77  type mockUpgradeOperation struct {
    78  	targetVersion version.Number
    79  	steps         []upgrades.Step
    80  }
    81  
    82  func (m *mockUpgradeOperation) TargetVersion() version.Number {
    83  	return m.targetVersion
    84  }
    85  
    86  func (m *mockUpgradeOperation) Steps() []upgrades.Step {
    87  	return m.steps
    88  }
    89  
    90  type mockUpgradeStep struct {
    91  	msg     string
    92  	targets []upgrades.Target
    93  }
    94  
    95  func (u *mockUpgradeStep) Description() string {
    96  	return u.msg
    97  }
    98  
    99  func (u *mockUpgradeStep) Targets() []upgrades.Target {
   100  	return u.targets
   101  }
   102  
   103  func (u *mockUpgradeStep) Run(ctx upgrades.Context) error {
   104  	if strings.HasSuffix(u.msg, "error") {
   105  		return errors.New("upgrade error occurred")
   106  	}
   107  	context := ctx.(*mockContext)
   108  	context.messages = append(context.messages, u.msg)
   109  	return nil
   110  }
   111  
   112  func newUpgradeStep(msg string, targets ...upgrades.Target) *mockUpgradeStep {
   113  	if len(targets) < 1 {
   114  		panic(fmt.Sprintf("step %q must have at least one target", msg))
   115  	}
   116  	return &mockUpgradeStep{
   117  		msg:     msg,
   118  		targets: targets,
   119  	}
   120  }
   121  
   122  type mockContext struct {
   123  	messages        []string
   124  	agentConfig     *mockAgentConfig
   125  	realAgentConfig agent.ConfigSetter
   126  	apiState        *api.State
   127  	state           *state.State
   128  }
   129  
   130  func (c *mockContext) APIState() *api.State {
   131  	return c.apiState
   132  }
   133  
   134  func (c *mockContext) State() *state.State {
   135  	return c.state
   136  }
   137  
   138  func (c *mockContext) AgentConfig() agent.ConfigSetter {
   139  	if c.realAgentConfig != nil {
   140  		return c.realAgentConfig
   141  	}
   142  	return c.agentConfig
   143  }
   144  
   145  func (c *mockContext) StateContext() upgrades.Context {
   146  	return c
   147  }
   148  
   149  func (c *mockContext) APIContext() upgrades.Context {
   150  	return c
   151  }
   152  
   153  type mockAgentConfig struct {
   154  	agent.ConfigSetter
   155  	dataDir      string
   156  	logDir       string
   157  	tag          names.Tag
   158  	jobs         []multiwatcher.MachineJob
   159  	apiAddresses []string
   160  	values       map[string]string
   161  	mongoInfo    *mongo.MongoInfo
   162  	servingInfo  params.StateServingInfo
   163  }
   164  
   165  func (mock *mockAgentConfig) Tag() names.Tag {
   166  	return mock.tag
   167  }
   168  
   169  func (mock *mockAgentConfig) DataDir() string {
   170  	return mock.dataDir
   171  }
   172  
   173  func (mock *mockAgentConfig) LogDir() string {
   174  	return mock.logDir
   175  }
   176  
   177  func (mock *mockAgentConfig) SystemIdentityPath() string {
   178  	return filepath.Join(mock.dataDir, agent.SystemIdentity)
   179  }
   180  
   181  func (mock *mockAgentConfig) Jobs() []multiwatcher.MachineJob {
   182  	return mock.jobs
   183  }
   184  
   185  func (mock *mockAgentConfig) APIAddresses() ([]string, error) {
   186  	return mock.apiAddresses, nil
   187  }
   188  
   189  func (mock *mockAgentConfig) Value(name string) string {
   190  	return mock.values[name]
   191  }
   192  
   193  func (mock *mockAgentConfig) MongoInfo() (*mongo.MongoInfo, bool) {
   194  	return mock.mongoInfo, true
   195  }
   196  
   197  func (mock *mockAgentConfig) StateServingInfo() (params.StateServingInfo, bool) {
   198  	return mock.servingInfo, true
   199  }
   200  
   201  func (mock *mockAgentConfig) SetStateServingInfo(info params.StateServingInfo) {
   202  	mock.servingInfo = info
   203  }
   204  
   205  func stateUpgradeOperations() []upgrades.Operation {
   206  	steps := []upgrades.Operation{
   207  		&mockUpgradeOperation{
   208  			targetVersion: version.MustParse("1.11.0"),
   209  			steps: []upgrades.Step{
   210  				newUpgradeStep("state step 1 - 1.11.0", upgrades.StateServer),
   211  				newUpgradeStep("state step 2 error", upgrades.StateServer),
   212  				newUpgradeStep("state step 3 - 1.11.0", upgrades.StateServer),
   213  			},
   214  		},
   215  		&mockUpgradeOperation{
   216  			targetVersion: version.MustParse("1.21.0"),
   217  			steps: []upgrades.Step{
   218  				newUpgradeStep("state step 1 - 1.21.0", upgrades.DatabaseMaster),
   219  				newUpgradeStep("state step 2 - 1.21.0", upgrades.StateServer),
   220  			},
   221  		},
   222  		&mockUpgradeOperation{
   223  			targetVersion: version.MustParse("1.22.0"),
   224  			steps: []upgrades.Step{
   225  				newUpgradeStep("state step 1 - 1.22.0", upgrades.DatabaseMaster),
   226  				newUpgradeStep("state step 2 - 1.22.0", upgrades.StateServer),
   227  			},
   228  		},
   229  	}
   230  	return steps
   231  }
   232  
   233  func upgradeOperations() []upgrades.Operation {
   234  	steps := []upgrades.Operation{
   235  		&mockUpgradeOperation{
   236  			targetVersion: version.MustParse("1.12.0"),
   237  			steps: []upgrades.Step{
   238  				newUpgradeStep("step 1 - 1.12.0", upgrades.AllMachines),
   239  				newUpgradeStep("step 2 error", upgrades.HostMachine),
   240  				newUpgradeStep("step 3", upgrades.HostMachine),
   241  			},
   242  		},
   243  		&mockUpgradeOperation{
   244  			targetVersion: version.MustParse("1.16.0"),
   245  			steps: []upgrades.Step{
   246  				newUpgradeStep("step 1 - 1.16.0", upgrades.HostMachine),
   247  				newUpgradeStep("step 2 - 1.16.0", upgrades.HostMachine),
   248  				newUpgradeStep("step 3 - 1.16.0", upgrades.StateServer),
   249  			},
   250  		},
   251  		&mockUpgradeOperation{
   252  			targetVersion: version.MustParse("1.17.0"),
   253  			steps: []upgrades.Step{
   254  				newUpgradeStep("step 1 - 1.17.0", upgrades.HostMachine),
   255  			},
   256  		},
   257  		&mockUpgradeOperation{
   258  			targetVersion: version.MustParse("1.17.1"),
   259  			steps: []upgrades.Step{
   260  				newUpgradeStep("step 1 - 1.17.1", upgrades.HostMachine),
   261  				newUpgradeStep("step 2 - 1.17.1", upgrades.StateServer),
   262  			},
   263  		},
   264  		&mockUpgradeOperation{
   265  			targetVersion: version.MustParse("1.18.0"),
   266  			steps: []upgrades.Step{
   267  				newUpgradeStep("step 1 - 1.18.0", upgrades.HostMachine),
   268  				newUpgradeStep("step 2 - 1.18.0", upgrades.StateServer),
   269  			},
   270  		},
   271  		&mockUpgradeOperation{
   272  			targetVersion: version.MustParse("1.20.0"),
   273  			steps: []upgrades.Step{
   274  				newUpgradeStep("step 1 - 1.20.0", upgrades.AllMachines),
   275  				newUpgradeStep("step 2 - 1.20.0", upgrades.HostMachine),
   276  				newUpgradeStep("step 3 - 1.20.0", upgrades.StateServer),
   277  			},
   278  		},
   279  		&mockUpgradeOperation{
   280  			targetVersion: version.MustParse("1.21.0"),
   281  			steps: []upgrades.Step{
   282  				newUpgradeStep("step 1 - 1.21.0", upgrades.AllMachines),
   283  			},
   284  		},
   285  		&mockUpgradeOperation{
   286  			targetVersion: version.MustParse("1.22.0"),
   287  			steps: []upgrades.Step{
   288  				// Separate targets used intentionally
   289  				newUpgradeStep("step 1 - 1.22.0", upgrades.StateServer, upgrades.HostMachine),
   290  				newUpgradeStep("step 2 - 1.22.0", upgrades.AllMachines),
   291  			},
   292  		},
   293  	}
   294  	return steps
   295  }
   296  
   297  type areUpgradesDefinedTest struct {
   298  	about       string
   299  	fromVersion string
   300  	toVersion   string
   301  	expected    bool
   302  	err         string
   303  }
   304  
   305  var areUpgradesDefinedTests = []areUpgradesDefinedTest{
   306  	{
   307  		about:       "no ops if same version",
   308  		fromVersion: "1.18.0",
   309  		expected:    false,
   310  	},
   311  	{
   312  		about:       "true when ops defined between versions",
   313  		fromVersion: "1.17.1",
   314  		expected:    true,
   315  	},
   316  	{
   317  		about:       "false when no ops defined between versions",
   318  		fromVersion: "1.13.0",
   319  		toVersion:   "1.14.1",
   320  		expected:    false,
   321  	},
   322  	{
   323  		about:       "true when just state ops defined ",
   324  		fromVersion: "1.10.0",
   325  		toVersion:   "1.11.0",
   326  		expected:    true,
   327  	},
   328  	{
   329  		about:       "from version is defaulted when not supplied",
   330  		fromVersion: "",
   331  		expected:    true,
   332  	},
   333  	{
   334  		about:       "upgrade between pre-final versions",
   335  		fromVersion: "1.21-beta4",
   336  		toVersion:   "1.21-beta5",
   337  		expected:    true,
   338  	},
   339  	{
   340  		about:       "no upgrades when version hasn't changed, even with release tags",
   341  		fromVersion: "1.21-beta5",
   342  		toVersion:   "1.21-beta5",
   343  		expected:    false,
   344  	},
   345  }
   346  
   347  func (s *upgradeSuite) TestAreUpgradesDefined(c *gc.C) {
   348  	s.PatchValue(upgrades.StateUpgradeOperations, stateUpgradeOperations)
   349  	s.PatchValue(upgrades.UpgradeOperations, upgradeOperations)
   350  	for i, test := range areUpgradesDefinedTests {
   351  		c.Logf("%d: %s", i, test.about)
   352  		fromVersion := version.Zero
   353  		if test.fromVersion != "" {
   354  			fromVersion = version.MustParse(test.fromVersion)
   355  		}
   356  		toVersion := version.MustParse("1.18.0")
   357  		if test.toVersion != "" {
   358  			toVersion = version.MustParse(test.toVersion)
   359  		}
   360  		vers := version.Current
   361  		vers.Number = toVersion
   362  		s.PatchValue(&version.Current, vers)
   363  		result := upgrades.AreUpgradesDefined(fromVersion)
   364  		c.Check(result, gc.Equals, test.expected)
   365  	}
   366  }
   367  
   368  type upgradeTest struct {
   369  	about         string
   370  	fromVersion   string
   371  	toVersion     string
   372  	targets       []upgrades.Target
   373  	expectedSteps []string
   374  	err           string
   375  }
   376  
   377  func targets(t ...upgrades.Target) []upgrades.Target {
   378  	return t
   379  }
   380  
   381  var upgradeTests = []upgradeTest{
   382  	{
   383  		about:         "from version excludes steps for same version",
   384  		fromVersion:   "1.18.0",
   385  		targets:       targets(upgrades.HostMachine),
   386  		expectedSteps: []string{},
   387  	},
   388  	{
   389  		about:         "target version excludes steps for newer version",
   390  		toVersion:     "1.17.1",
   391  		targets:       targets(upgrades.HostMachine),
   392  		expectedSteps: []string{"step 1 - 1.17.0", "step 1 - 1.17.1"},
   393  	},
   394  	{
   395  		about:         "from version excludes older steps",
   396  		fromVersion:   "1.17.0",
   397  		targets:       targets(upgrades.HostMachine),
   398  		expectedSteps: []string{"step 1 - 1.17.1", "step 1 - 1.18.0"},
   399  	},
   400  	{
   401  		about:         "incompatible targets excluded",
   402  		fromVersion:   "1.17.1",
   403  		targets:       targets(upgrades.StateServer),
   404  		expectedSteps: []string{"step 2 - 1.18.0"},
   405  	},
   406  	{
   407  		about:         "allMachines matches everything",
   408  		fromVersion:   "1.18.1",
   409  		toVersion:     "1.20.0",
   410  		targets:       targets(upgrades.HostMachine),
   411  		expectedSteps: []string{"step 1 - 1.20.0", "step 2 - 1.20.0"},
   412  	},
   413  	{
   414  		about:         "allMachines matches everything",
   415  		fromVersion:   "1.18.1",
   416  		toVersion:     "1.20.0",
   417  		targets:       targets(upgrades.StateServer),
   418  		expectedSteps: []string{"step 1 - 1.20.0", "step 3 - 1.20.0"},
   419  	},
   420  	{
   421  		about:         "state step error aborts, subsequent state steps not run",
   422  		fromVersion:   "1.10.0",
   423  		targets:       targets(upgrades.StateServer),
   424  		expectedSteps: []string{"state step 1 - 1.11.0"},
   425  		err:           "state step 2 error: upgrade error occurred",
   426  	},
   427  	{
   428  		about:         "error aborts, subsequent steps not run",
   429  		fromVersion:   "1.11.0",
   430  		targets:       targets(upgrades.HostMachine),
   431  		expectedSteps: []string{"step 1 - 1.12.0"},
   432  		err:           "step 2 error: upgrade error occurred",
   433  	},
   434  	{
   435  		about:         "default from version is 1.16",
   436  		fromVersion:   "",
   437  		targets:       targets(upgrades.StateServer),
   438  		expectedSteps: []string{"step 2 - 1.17.1", "step 2 - 1.18.0"},
   439  	},
   440  	{
   441  		about:         "state servers don't get database master",
   442  		fromVersion:   "1.20.0",
   443  		toVersion:     "1.21.0",
   444  		targets:       targets(upgrades.StateServer),
   445  		expectedSteps: []string{"state step 2 - 1.21.0", "step 1 - 1.21.0"},
   446  	},
   447  	{
   448  		about:         "database master only (not actually possible in reality)",
   449  		fromVersion:   "1.20.0",
   450  		toVersion:     "1.21.0",
   451  		targets:       targets(upgrades.DatabaseMaster),
   452  		expectedSteps: []string{"state step 1 - 1.21.0", "step 1 - 1.21.0"},
   453  	},
   454  	{
   455  		about:       "all state steps are run first",
   456  		fromVersion: "1.20.0",
   457  		toVersion:   "1.22.0",
   458  		targets:     targets(upgrades.DatabaseMaster, upgrades.StateServer),
   459  		expectedSteps: []string{
   460  			"state step 1 - 1.21.0", "state step 2 - 1.21.0",
   461  			"state step 1 - 1.22.0", "state step 2 - 1.22.0",
   462  			"step 1 - 1.21.0",
   463  			"step 1 - 1.22.0", "step 2 - 1.22.0",
   464  		},
   465  	},
   466  	{
   467  		about:         "machine with multiple targets - each step only run once",
   468  		fromVersion:   "1.20.0",
   469  		toVersion:     "1.21.0",
   470  		targets:       targets(upgrades.HostMachine, upgrades.StateServer),
   471  		expectedSteps: []string{"state step 2 - 1.21.0", "step 1 - 1.21.0"},
   472  	},
   473  	{
   474  		about:         "step with multiple targets",
   475  		fromVersion:   "1.21.0",
   476  		toVersion:     "1.22.0",
   477  		targets:       targets(upgrades.HostMachine),
   478  		expectedSteps: []string{"step 1 - 1.22.0", "step 2 - 1.22.0"},
   479  	},
   480  	{
   481  		about:         "machine and step with multiple targets - each step only run once",
   482  		fromVersion:   "1.21.0",
   483  		toVersion:     "1.22.0",
   484  		targets:       targets(upgrades.HostMachine, upgrades.StateServer),
   485  		expectedSteps: []string{"state step 2 - 1.22.0", "step 1 - 1.22.0", "step 2 - 1.22.0"},
   486  	},
   487  	{
   488  		about:         "upgrade to alpha release runs steps for final release",
   489  		fromVersion:   "1.20.0",
   490  		toVersion:     "1.21-alpha1",
   491  		targets:       targets(upgrades.HostMachine),
   492  		expectedSteps: []string{"step 1 - 1.21.0"},
   493  	},
   494  	{
   495  		about:         "upgrade to beta release runs steps for final release",
   496  		fromVersion:   "1.20.0",
   497  		toVersion:     "1.21-beta2",
   498  		targets:       targets(upgrades.HostMachine),
   499  		expectedSteps: []string{"step 1 - 1.21.0"},
   500  	},
   501  	{
   502  		about:         "starting release steps included when upgrading from an alpha release",
   503  		fromVersion:   "1.20-alpha3",
   504  		toVersion:     "1.21.0",
   505  		targets:       targets(upgrades.HostMachine),
   506  		expectedSteps: []string{"step 1 - 1.20.0", "step 2 - 1.20.0", "step 1 - 1.21.0"},
   507  	},
   508  	{
   509  		about:         "starting release steps included when upgrading from an beta release",
   510  		fromVersion:   "1.20-beta1",
   511  		toVersion:     "1.21.0",
   512  		targets:       targets(upgrades.HostMachine),
   513  		expectedSteps: []string{"step 1 - 1.20.0", "step 2 - 1.20.0", "step 1 - 1.21.0"},
   514  	},
   515  	{
   516  		about:         "nothing happens when the version hasn't changed but contains a tag",
   517  		fromVersion:   "1.21-alpha1",
   518  		toVersion:     "1.21-alpha1",
   519  		targets:       targets(upgrades.DatabaseMaster),
   520  		expectedSteps: []string{},
   521  	},
   522  	{
   523  		about:         "upgrades between pre-final versions should run steps for the final version",
   524  		fromVersion:   "1.21-beta2",
   525  		toVersion:     "1.21-beta3",
   526  		targets:       targets(upgrades.DatabaseMaster),
   527  		expectedSteps: []string{"state step 1 - 1.21.0", "step 1 - 1.21.0"},
   528  	},
   529  }
   530  
   531  func (s *upgradeSuite) TestPerformUpgrade(c *gc.C) {
   532  	s.PatchValue(upgrades.StateUpgradeOperations, stateUpgradeOperations)
   533  	s.PatchValue(upgrades.UpgradeOperations, upgradeOperations)
   534  	for i, test := range upgradeTests {
   535  		c.Logf("%d: %s", i, test.about)
   536  		var messages []string
   537  		ctx := &mockContext{
   538  			messages: messages,
   539  		}
   540  		fromVersion := version.Zero
   541  		if test.fromVersion != "" {
   542  			fromVersion = version.MustParse(test.fromVersion)
   543  		}
   544  		toVersion := version.MustParse("1.18.0")
   545  		if test.toVersion != "" {
   546  			toVersion = version.MustParse(test.toVersion)
   547  		}
   548  		vers := version.Current
   549  		vers.Number = toVersion
   550  		s.PatchValue(&version.Current, vers)
   551  		err := upgrades.PerformUpgrade(fromVersion, test.targets, ctx)
   552  		if test.err == "" {
   553  			c.Check(err, jc.ErrorIsNil)
   554  		} else {
   555  			c.Check(err, gc.ErrorMatches, test.err)
   556  		}
   557  		c.Check(ctx.messages, jc.DeepEquals, test.expectedSteps)
   558  	}
   559  }
   560  
   561  type contextStep struct {
   562  	useAPI bool
   563  }
   564  
   565  func (s *contextStep) Description() string {
   566  	return "something"
   567  }
   568  
   569  func (s *contextStep) Targets() []upgrades.Target {
   570  	return []upgrades.Target{upgrades.StateServer}
   571  }
   572  
   573  func (s *contextStep) Run(context upgrades.Context) error {
   574  	if s.useAPI {
   575  		context.APIState()
   576  	} else {
   577  		context.State()
   578  	}
   579  	return nil
   580  }
   581  
   582  func (s *upgradeSuite) TestStateStepsGetRestrictedContext(c *gc.C) {
   583  	s.PatchValue(upgrades.StateUpgradeOperations, func() []upgrades.Operation {
   584  		return []upgrades.Operation{
   585  			&mockUpgradeOperation{
   586  				targetVersion: version.MustParse("1.21.0"),
   587  				steps:         []upgrades.Step{&contextStep{useAPI: true}},
   588  			},
   589  		}
   590  	})
   591  
   592  	s.PatchValue(upgrades.UpgradeOperations,
   593  		func() []upgrades.Operation { return nil })
   594  
   595  	s.checkContextRestriction(c, "API not available from this context")
   596  }
   597  
   598  func (s *upgradeSuite) TestApiStepsGetRestrictedContext(c *gc.C) {
   599  	s.PatchValue(upgrades.StateUpgradeOperations,
   600  		func() []upgrades.Operation { return nil })
   601  
   602  	s.PatchValue(upgrades.UpgradeOperations, func() []upgrades.Operation {
   603  		return []upgrades.Operation{
   604  			&mockUpgradeOperation{
   605  				targetVersion: version.MustParse("1.21.0"),
   606  				steps:         []upgrades.Step{&contextStep{useAPI: false}},
   607  			},
   608  		}
   609  	})
   610  
   611  	s.checkContextRestriction(c, "State not available from this context")
   612  }
   613  
   614  func (s *upgradeSuite) checkContextRestriction(c *gc.C, expectedPanic string) {
   615  	fromVersion := version.MustParse("1.20.0")
   616  	type fakeAgentConfigSetter struct{ agent.ConfigSetter }
   617  	ctx := upgrades.NewContext(fakeAgentConfigSetter{}, new(api.State), new(state.State))
   618  	c.Assert(
   619  		func() { upgrades.PerformUpgrade(fromVersion, targets(upgrades.StateServer), ctx) },
   620  		gc.PanicMatches, expectedPanic,
   621  	)
   622  }
   623  
   624  func (s *upgradeSuite) TestStateStepsNotAttemptedWhenNoStateTarget(c *gc.C) {
   625  	stateCount := 0
   626  	stateUpgradeOperations := func() []upgrades.Operation {
   627  		stateCount++
   628  		return nil
   629  	}
   630  	s.PatchValue(upgrades.StateUpgradeOperations, stateUpgradeOperations)
   631  
   632  	apiCount := 0
   633  	upgradeOperations := func() []upgrades.Operation {
   634  		apiCount++
   635  		return nil
   636  	}
   637  	s.PatchValue(upgrades.UpgradeOperations, upgradeOperations)
   638  
   639  	fromVers := version.MustParse("1.18.0")
   640  	ctx := new(mockContext)
   641  	check := func(target upgrades.Target, expectedStateCallCount int) {
   642  		stateCount = 0
   643  		apiCount = 0
   644  		err := upgrades.PerformUpgrade(fromVers, targets(target), ctx)
   645  		c.Assert(err, jc.ErrorIsNil)
   646  		c.Assert(stateCount, gc.Equals, expectedStateCallCount)
   647  		c.Assert(apiCount, gc.Equals, 1)
   648  	}
   649  
   650  	check(upgrades.StateServer, 1)
   651  	check(upgrades.DatabaseMaster, 1)
   652  	check(upgrades.AllMachines, 0)
   653  	check(upgrades.HostMachine, 0)
   654  }
   655  
   656  func (s *upgradeSuite) TestUpgradeOperationsOrdered(c *gc.C) {
   657  	var previous version.Number
   658  	for i, utv := range (*upgrades.UpgradeOperations)() {
   659  		vers := utv.TargetVersion()
   660  		if i > 0 {
   661  			c.Check(previous.Compare(vers), gc.Equals, -1)
   662  		}
   663  		previous = vers
   664  	}
   665  }
   666  
   667  func (s *upgradeSuite) TestStateUpgradeOperationsVersions(c *gc.C) {
   668  	versions := extractUpgradeVersions(c, (*upgrades.StateUpgradeOperations)())
   669  	c.Assert(versions, gc.DeepEquals, []string{"1.18.0", "1.21.0", "1.22.0", "1.23.0"})
   670  }
   671  
   672  func (s *upgradeSuite) TestUpgradeOperationsVersions(c *gc.C) {
   673  	versions := extractUpgradeVersions(c, (*upgrades.UpgradeOperations)())
   674  	c.Assert(versions, gc.DeepEquals, []string{"1.18.0", "1.22.0", "1.23.0"})
   675  }
   676  
   677  func extractUpgradeVersions(c *gc.C, ops []upgrades.Operation) []string {
   678  	var versions []string
   679  	for _, utv := range ops {
   680  		vers := utv.TargetVersion()
   681  		// Upgrade steps should only be targeted at final versions (not alpha/beta).
   682  		c.Check(vers.Tag, gc.Equals, "")
   683  		versions = append(versions, vers.String())
   684  	}
   685  	return versions
   686  }