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