github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/upgradesteps/worker_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package upgradesteps
     5  
     6  import (
     7  	"fmt"
     8  	"time"
     9  
    10  	"github.com/juju/clock"
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	"github.com/juju/names/v5"
    14  	"github.com/juju/retry"
    15  	jc "github.com/juju/testing/checkers"
    16  	"github.com/juju/version/v2"
    17  	gc "gopkg.in/check.v1"
    18  
    19  	"github.com/juju/juju/agent"
    20  	agenterrors "github.com/juju/juju/cmd/jujud/agent/errors"
    21  	"github.com/juju/juju/core/constraints"
    22  	"github.com/juju/juju/core/instance"
    23  	"github.com/juju/juju/core/status"
    24  	"github.com/juju/juju/state"
    25  	"github.com/juju/juju/state/stateenvirons"
    26  	statetesting "github.com/juju/juju/state/testing"
    27  	coretesting "github.com/juju/juju/testing"
    28  	"github.com/juju/juju/testing/factory"
    29  	"github.com/juju/juju/upgrades"
    30  	jujuversion "github.com/juju/juju/version"
    31  	"github.com/juju/juju/worker/gate"
    32  )
    33  
    34  // TODO(mjs) - these tests are too tightly coupled to the
    35  // implementation. They needn't be internal tests.
    36  
    37  type UpgradeSuite struct {
    38  	statetesting.StateSuite
    39  
    40  	oldVersion      version.Binary
    41  	logWriter       loggo.TestWriter
    42  	connectionDead  bool
    43  	preUpgradeError bool
    44  }
    45  
    46  var _ = gc.Suite(&UpgradeSuite{})
    47  
    48  const fails = true
    49  const succeeds = false
    50  
    51  func (s *UpgradeSuite) SetUpTest(c *gc.C) {
    52  	s.StateSuite.SetUpTest(c)
    53  
    54  	s.preUpgradeError = false
    55  	// Most of these tests normally finish sub-second on a fast machine.
    56  	// If any given test hits a minute, we have almost certainly become
    57  	// wedged, so dump the logs.
    58  	coretesting.DumpTestLogsAfter(time.Minute, c, s)
    59  
    60  	s.oldVersion = coretesting.CurrentVersion()
    61  	s.oldVersion.Major = 1
    62  	s.oldVersion.Minor = 16
    63  
    64  	// Don't wait so long in tests.
    65  	s.PatchValue(&UpgradeStartTimeoutController, time.Millisecond*50)
    66  
    67  	// Allow tests to make the API connection appear to be dead.
    68  	s.connectionDead = false
    69  	s.PatchValue(&agenterrors.ConnectionIsDead, func(loggo.Logger, agenterrors.Breakable) bool {
    70  		return s.connectionDead
    71  	})
    72  }
    73  
    74  func (s *UpgradeSuite) captureLogs(c *gc.C) {
    75  	c.Assert(loggo.RegisterWriter("upgrade-tests", &s.logWriter), gc.IsNil)
    76  	s.AddCleanup(func(*gc.C) {
    77  		loggo.RemoveWriter("upgrade-tests")
    78  		s.logWriter.Clear()
    79  	})
    80  }
    81  
    82  func (s *UpgradeSuite) countUpgradeAttempts(upgradeErr error) *int {
    83  	count := 0
    84  	s.PatchValue(&PerformUpgrade, func(version.Number, []upgrades.Target, upgrades.Context) error {
    85  		count++
    86  		return upgradeErr
    87  	})
    88  	return &count
    89  }
    90  
    91  func (s *UpgradeSuite) TestNewChannelWhenNoUpgradeRequired(c *gc.C) {
    92  	// Set the agent's upgradedToVersion to version.Current,
    93  	// to simulate the upgrade steps having been run already.
    94  	initialVersion := jujuversion.Current
    95  	config := NewFakeConfigSetter(names.NewMachineTag("0"), initialVersion)
    96  
    97  	lock := NewLock(config)
    98  
    99  	// Upgrade steps have already been run.
   100  	c.Assert(lock.IsUnlocked(), jc.IsTrue)
   101  }
   102  
   103  func (s *UpgradeSuite) TestNewChannelWhenUpgradeRequired(c *gc.C) {
   104  	// Set the agent's upgradedToVersion so that upgrade steps are required.
   105  	initialVersion := version.MustParse("1.16.0")
   106  	config := NewFakeConfigSetter(names.NewMachineTag("0"), initialVersion)
   107  
   108  	lock := NewLock(config)
   109  
   110  	c.Assert(lock.IsUnlocked(), jc.IsFalse)
   111  	// The agent's version should NOT have been updated.
   112  	c.Assert(config.Version, gc.Equals, initialVersion)
   113  }
   114  
   115  func (s *UpgradeSuite) TestNoUpgradeNecessary(c *gc.C) {
   116  	attemptsP := s.countUpgradeAttempts(nil)
   117  	s.captureLogs(c)
   118  	s.oldVersion.Number = jujuversion.Current // nothing to do
   119  
   120  	workerErr, config, _, doneLock := s.runUpgradeWorker(c, false)
   121  
   122  	c.Check(workerErr, gc.IsNil)
   123  	c.Check(*attemptsP, gc.Equals, 0)
   124  	c.Check(config.Version, gc.Equals, jujuversion.Current)
   125  	c.Check(doneLock.IsUnlocked(), jc.IsTrue)
   126  }
   127  
   128  func (s *UpgradeSuite) TestNoUpgradeNecessaryIgnoresBuildNumbers(c *gc.C) {
   129  	attemptsP := s.countUpgradeAttempts(nil)
   130  	s.captureLogs(c)
   131  	s.oldVersion.Number = jujuversion.Current
   132  	s.oldVersion.Build = 1 // Ensure there's a build number mismatch.
   133  
   134  	workerErr, config, _, doneLock := s.runUpgradeWorker(c, false)
   135  
   136  	c.Check(workerErr, gc.IsNil)
   137  	c.Check(*attemptsP, gc.Equals, 0)
   138  	c.Check(config.Version, gc.Equals, s.oldVersion.Number)
   139  	c.Check(doneLock.IsUnlocked(), jc.IsTrue)
   140  }
   141  
   142  func (s *UpgradeSuite) TestUpgradeStepsFailure(c *gc.C) {
   143  	// This test checks what happens when every upgrade attempt fails.
   144  	// A number of retries should be observed and the agent should end
   145  	// up in a state where it is is still running but is reporting an
   146  	// error and the upgrade is not flagged as having completed (which
   147  	// prevents most of the agent's workers from running and keeps the
   148  	// API in restricted mode).
   149  
   150  	attemptsP := s.countUpgradeAttempts(errors.New("boom"))
   151  	s.captureLogs(c)
   152  
   153  	workerErr, config, statusCalls, doneLock := s.runUpgradeWorker(c, false)
   154  
   155  	// The worker shouldn't return an error so that the worker and
   156  	// agent keep running.
   157  	c.Check(workerErr, gc.IsNil)
   158  
   159  	c.Check(*attemptsP, gc.Equals, maxUpgradeRetries)
   160  	c.Check(config.Version, gc.Equals, s.oldVersion.Number) // Upgrade didn't finish
   161  	c.Assert(statusCalls, jc.DeepEquals,
   162  		s.makeExpectedStatusCalls(maxUpgradeRetries-1, fails, "boom"))
   163  	c.Assert(s.logWriter.Log(), jc.LogMatches,
   164  		s.makeExpectedUpgradeLogs(maxUpgradeRetries-1, "hostMachine", fails, "boom"))
   165  	c.Assert(doneLock.IsUnlocked(), jc.IsFalse)
   166  }
   167  
   168  func (s *UpgradeSuite) TestUpgradeStepsRetries(c *gc.C) {
   169  	// This test checks what happens when the first upgrade attempt
   170  	// fails but the following on succeeds. The final state should be
   171  	// the same as a successful upgrade which worked first go.
   172  	attempts := 0
   173  	fail := true
   174  	fakePerformUpgrade := func(version.Number, []upgrades.Target, upgrades.Context) error {
   175  		attempts++
   176  		if fail {
   177  			fail = false
   178  			return errors.New("boom")
   179  		} else {
   180  			return nil
   181  		}
   182  	}
   183  	s.PatchValue(&PerformUpgrade, fakePerformUpgrade)
   184  	s.captureLogs(c)
   185  
   186  	workerErr, config, statusCalls, doneLock := s.runUpgradeWorker(c, false)
   187  
   188  	c.Check(workerErr, gc.IsNil)
   189  	c.Check(attempts, gc.Equals, 2)
   190  	c.Check(config.Version, gc.Equals, jujuversion.Current) // Upgrade finished
   191  	c.Assert(statusCalls, jc.DeepEquals, s.makeExpectedStatusCalls(1, succeeds, "boom"))
   192  	c.Assert(s.logWriter.Log(), jc.LogMatches, s.makeExpectedUpgradeLogs(1, "hostMachine", succeeds, "boom"))
   193  	c.Check(doneLock.IsUnlocked(), jc.IsTrue)
   194  }
   195  
   196  func (s *UpgradeSuite) TestOtherUpgradeRunFailure(c *gc.C) {
   197  	// This test checks what happens something other than the upgrade
   198  	// steps themselves fails, ensuring the something is logged and
   199  	// the agent status is updated.
   200  
   201  	m := s.Factory.MakeMachine(c, &factory.MachineParams{
   202  		Jobs: []state.MachineJob{state.JobManageModel},
   203  	})
   204  	s.captureLogs(c)
   205  
   206  	// Simulate the upgrade-database worker having run successfully.
   207  	info, err := s.State.EnsureUpgradeInfo(m.Id(), s.oldVersion.Number, jujuversion.Current)
   208  	c.Assert(err, jc.ErrorIsNil)
   209  	err = info.SetStatus(state.UpgradeDBComplete)
   210  	c.Assert(err, jc.ErrorIsNil)
   211  
   212  	fakePerformUpgrade := func(version.Number, []upgrades.Target, upgrades.Context) error {
   213  		// Violate the state-machine rules so that finaliseUpgrade() will fail.
   214  		// Recreating the upgrade doc will put us into status "pending" without
   215  		// any recorded controller ready/completed entries.
   216  		if err := s.State.ClearUpgradeInfo(); err != nil {
   217  			return err
   218  		}
   219  		info, err = s.State.EnsureUpgradeInfo(m.Id(), s.oldVersion.Number, jujuversion.Current)
   220  		return err
   221  	}
   222  	s.PatchValue(&PerformUpgrade, fakePerformUpgrade)
   223  
   224  	workerErr, config, statusCalls, doneLock := s.runUpgradeWorker(c, true)
   225  	c.Check(workerErr, gc.IsNil)
   226  
   227  	c.Check(config.Version, gc.Equals, jujuversion.Current) // Upgrade almost finished
   228  
   229  	failReason := `upgrade done but failed to synchronise: cannot complete upgrade: upgrade has not yet run`
   230  	c.Assert(statusCalls, jc.DeepEquals, s.makeExpectedStatusCalls(0, fails, failReason))
   231  	c.Assert(s.logWriter.Log(), jc.LogMatches, s.makeExpectedUpgradeLogs(0, "databaseMaster", fails, failReason))
   232  	c.Assert(doneLock.IsUnlocked(), jc.IsFalse)
   233  }
   234  
   235  func (s *UpgradeSuite) TestAPIConnectionFailure(c *gc.C) {
   236  	// This test checks what happens when an upgrade fails because the
   237  	// connection to mongo has gone away. This will happen when the
   238  	// mongo master changes. In this case we want the upgrade worker
   239  	// to return immediately without further retries. The error should
   240  	// be returned by the worker so that the agent will restart.
   241  
   242  	attemptsP := s.countUpgradeAttempts(errors.New("boom"))
   243  	s.connectionDead = true // Make the connection to state appear to be dead
   244  	s.captureLogs(c)
   245  
   246  	workerErr, config, _, doneLock := s.runUpgradeWorker(c, false)
   247  
   248  	c.Check(workerErr, gc.ErrorMatches, "API connection lost during upgrade: boom")
   249  	c.Check(*attemptsP, gc.Equals, 1)
   250  	c.Check(config.Version, gc.Equals, s.oldVersion.Number) // Upgrade didn't finish
   251  	c.Assert(doneLock.IsUnlocked(), jc.IsFalse)
   252  }
   253  
   254  func (s *UpgradeSuite) TestAbortWhenOtherControllerDoesNotStartUpgrade(c *gc.C) {
   255  	// This test checks when a controller is upgrading and one of
   256  	// the other controllers doesn't signal it is ready in time.
   257  
   258  	err := s.State.SetModelAgentVersion(jujuversion.Current, nil, false)
   259  	c.Assert(err, jc.ErrorIsNil)
   260  
   261  	s.create3Controllers(c)
   262  	s.captureLogs(c)
   263  	attemptsP := s.countUpgradeAttempts(nil)
   264  
   265  	workerErr, config, statusCalls, doneLock := s.runUpgradeWorker(c, true)
   266  
   267  	c.Check(workerErr, gc.IsNil)
   268  	c.Check(*attemptsP, gc.Equals, 0)
   269  	c.Check(config.Version, gc.Equals, s.oldVersion.Number) // Upgrade didn't happen
   270  	c.Assert(doneLock.IsUnlocked(), jc.IsFalse)
   271  
   272  	// The environment agent-version should still be the new version.
   273  	// It's up to the master to trigger the rollback.
   274  	s.assertEnvironAgentVersion(c, jujuversion.Current)
   275  
   276  	causeMsg := " timed out after 50ms"
   277  	c.Assert(s.logWriter.Log(), jc.LogMatches, []jc.SimpleMessage{
   278  		{loggo.INFO, "waiting for other controllers to be ready for upgrade"},
   279  		{loggo.ERROR, "aborted wait for other controllers: timed out after 50ms"},
   280  		{loggo.ERROR, `upgrade from .+ to .+ for "machine-0" failed \(giving up\): ` +
   281  			"aborted wait for other controllers:" + causeMsg},
   282  	})
   283  	c.Assert(statusCalls, jc.DeepEquals, []StatusCall{{
   284  		status.Error,
   285  		fmt.Sprintf(
   286  			"upgrade to %s failed (giving up): aborted wait for other controllers:"+causeMsg,
   287  			jujuversion.Current),
   288  	}})
   289  }
   290  
   291  func (s *UpgradeSuite) TestSuccessLeadingController(c *gc.C) {
   292  	// This test checks what happens when an upgrade works on the first
   293  	// attempt, on the first controller to set the status to "running".
   294  	info := s.checkSuccess(c, "databaseMaster", func(i *state.UpgradeInfo) {
   295  		err := i.SetStatus(state.UpgradeDBComplete)
   296  		c.Assert(err, jc.ErrorIsNil)
   297  	})
   298  	c.Assert(info.Status(), gc.Equals, state.UpgradeRunning)
   299  }
   300  
   301  func (s *UpgradeSuite) TestSuccessFollowingController(c *gc.C) {
   302  	// This test checks what happens when an upgrade works on the a controller
   303  	// following a controller having already set the status to "running".
   304  	s.checkSuccess(c, "controller", func(info *state.UpgradeInfo) {
   305  		// Indicate that the master is done
   306  		err := info.SetStatus(state.UpgradeDBComplete)
   307  		c.Assert(err, jc.ErrorIsNil)
   308  		err = info.SetStatus(state.UpgradeRunning)
   309  		c.Assert(err, jc.ErrorIsNil)
   310  	})
   311  }
   312  
   313  func (s *UpgradeSuite) checkSuccess(c *gc.C, target string, mungeInfo func(*state.UpgradeInfo)) *state.UpgradeInfo {
   314  	_, machineIdB, machineIdC := s.create3Controllers(c)
   315  
   316  	// Indicate that machine B and C are ready to upgrade
   317  	vPrevious := s.oldVersion.Number
   318  	vNext := jujuversion.Current
   319  	info, err := s.State.EnsureUpgradeInfo(machineIdB, vPrevious, vNext)
   320  	c.Assert(err, jc.ErrorIsNil)
   321  	_, err = s.State.EnsureUpgradeInfo(machineIdC, vPrevious, vNext)
   322  	c.Assert(err, jc.ErrorIsNil)
   323  
   324  	mungeInfo(info)
   325  
   326  	attemptsP := s.countUpgradeAttempts(nil)
   327  	s.captureLogs(c)
   328  
   329  	workerErr, config, statusCalls, doneLock := s.runUpgradeWorker(c, true)
   330  
   331  	c.Check(workerErr, gc.IsNil)
   332  	c.Check(*attemptsP, gc.Equals, 1)
   333  	c.Check(config.Version, gc.Equals, jujuversion.Current) // Upgrade finished
   334  	c.Assert(statusCalls, jc.DeepEquals, s.makeExpectedStatusCalls(0, succeeds, ""))
   335  	c.Assert(s.logWriter.Log(), jc.LogMatches, s.makeExpectedUpgradeLogs(0, target, succeeds, ""))
   336  	c.Check(doneLock.IsUnlocked(), jc.IsTrue)
   337  
   338  	err = info.Refresh()
   339  	c.Assert(err, jc.ErrorIsNil)
   340  	c.Assert(info.ControllersDone(), jc.DeepEquals, []string{"0"})
   341  	return info
   342  }
   343  
   344  func (s *UpgradeSuite) TestJobsToTargets(c *gc.C) {
   345  	c.Assert(upgradeTargets(false), jc.DeepEquals, []upgrades.Target{upgrades.HostMachine})
   346  	c.Assert(upgradeTargets(true), jc.SameContents, []upgrades.Target{upgrades.HostMachine, upgrades.Controller})
   347  }
   348  
   349  func (s *UpgradeSuite) TestPreUpgradeFail(c *gc.C) {
   350  	s.preUpgradeError = true
   351  	s.captureLogs(c)
   352  
   353  	workerErr, config, statusCalls, doneLock := s.runUpgradeWorker(c, false)
   354  
   355  	c.Check(workerErr, jc.ErrorIsNil)
   356  	c.Check(config.Version, gc.Equals, s.oldVersion.Number) // Upgrade didn't finish
   357  	c.Assert(doneLock.IsUnlocked(), jc.IsFalse)
   358  
   359  	causeMessage := `machine 0 cannot be upgraded: preupgrade error`
   360  	failMessage := fmt.Sprintf(
   361  		`upgrade from %s to %s for "machine-0" failed \(giving up\): %s`,
   362  		s.oldVersion.Number, jujuversion.Current, causeMessage)
   363  	c.Assert(s.logWriter.Log(), jc.LogMatches, []jc.SimpleMessage{
   364  		{loggo.INFO, "checking that upgrade can proceed"},
   365  		{loggo.ERROR, failMessage},
   366  	})
   367  
   368  	statusMessage := fmt.Sprintf(
   369  		`upgrade to %s failed (giving up): %s`, jujuversion.Current, causeMessage)
   370  	c.Assert(statusCalls, jc.DeepEquals, []StatusCall{{
   371  		status.Error, statusMessage,
   372  	}})
   373  }
   374  
   375  // Run just the upgradeSteps worker with a fake machine agent and
   376  // fake agent config.
   377  func (s *UpgradeSuite) runUpgradeWorker(c *gc.C, isController bool) (
   378  	error, *fakeConfigSetter, []StatusCall, gate.Lock,
   379  ) {
   380  	config := s.makeFakeConfig()
   381  	agent := NewFakeAgent(config)
   382  	doneLock := NewLock(config)
   383  	machineStatus := &testStatusSetter{}
   384  	testRetryStrategy := retry.CallArgs{
   385  		Clock:    clock.WallClock,
   386  		Delay:    time.Millisecond,
   387  		Attempts: maxUpgradeRetries,
   388  	}
   389  	worker, err := NewWorker(
   390  		doneLock,
   391  		agent,
   392  		nil,
   393  		isController,
   394  		s.openStateForUpgrade,
   395  		s.preUpgradeSteps,
   396  		testRetryStrategy,
   397  		machineStatus,
   398  		false,
   399  	)
   400  	c.Assert(err, jc.ErrorIsNil)
   401  	return worker.Wait(), config, machineStatus.Calls, doneLock
   402  }
   403  
   404  func (s *UpgradeSuite) openStateForUpgrade() (*state.StatePool, error) {
   405  	newPolicy := stateenvirons.GetNewPolicyFunc()
   406  	pool, err := state.OpenStatePool(state.OpenParams{
   407  		Clock:              clock.WallClock,
   408  		ControllerTag:      s.State.ControllerTag(),
   409  		ControllerModelTag: s.Model.ModelTag(),
   410  		MongoSession:       s.State.MongoSession(),
   411  		NewPolicy:          newPolicy,
   412  	})
   413  	if err != nil {
   414  		return nil, err
   415  	}
   416  	return pool, nil
   417  }
   418  
   419  func (s *UpgradeSuite) preUpgradeSteps(_ *state.StatePool, _ agent.Config, _, _ bool) error {
   420  	if s.preUpgradeError {
   421  		return errors.New("preupgrade error")
   422  	}
   423  	return nil
   424  }
   425  
   426  func (s *UpgradeSuite) makeFakeConfig() *fakeConfigSetter {
   427  	return NewFakeConfigSetter(names.NewMachineTag("0"), s.oldVersion.Number)
   428  }
   429  
   430  func (s *UpgradeSuite) create3Controllers(c *gc.C) (machineIdA, machineIdB, machineIdC string) {
   431  	machine0 := s.Factory.MakeMachine(c, &factory.MachineParams{
   432  		Jobs: []state.MachineJob{state.JobManageModel},
   433  	})
   434  	machineIdA = machine0.Id()
   435  
   436  	changes, err := s.State.EnableHA(3, constraints.Value{}, state.UbuntuBase("12.10"), nil)
   437  	c.Assert(err, jc.ErrorIsNil)
   438  	c.Assert(len(changes.Added), gc.Equals, 2)
   439  
   440  	machineIdB = changes.Added[0]
   441  	s.setMachineProvisioned(c, machineIdB)
   442  
   443  	machineIdC = changes.Added[1]
   444  	s.setMachineProvisioned(c, machineIdC)
   445  
   446  	return
   447  }
   448  
   449  func (s *UpgradeSuite) setMachineProvisioned(c *gc.C, id string) {
   450  	machine, err := s.State.Machine(id)
   451  	c.Assert(err, jc.ErrorIsNil)
   452  	err = machine.SetProvisioned(instance.Id(id+"-inst"), "", "nonce", nil)
   453  	c.Assert(err, jc.ErrorIsNil)
   454  }
   455  
   456  const maxUpgradeRetries = 3
   457  
   458  func (s *UpgradeSuite) makeExpectedStatusCalls(retryCount int, expectFail bool, failReason string) []StatusCall {
   459  	calls := []StatusCall{{
   460  		status.Started,
   461  		fmt.Sprintf("upgrading to %s", jujuversion.Current),
   462  	}}
   463  	for i := 0; i < retryCount; i++ {
   464  		calls = append(calls, StatusCall{
   465  			status.Error,
   466  			fmt.Sprintf("upgrade to %s failed (will retry): %s", jujuversion.Current, failReason),
   467  		})
   468  	}
   469  	if expectFail {
   470  		calls = append(calls, StatusCall{
   471  			status.Error,
   472  			fmt.Sprintf("upgrade to %s failed (giving up): %s", jujuversion.Current, failReason),
   473  		})
   474  	} else {
   475  		calls = append(calls, StatusCall{status.Started, ""})
   476  	}
   477  	return calls
   478  }
   479  
   480  func (s *UpgradeSuite) makeExpectedUpgradeLogs(retryCount int, target string, expectFail bool, failReason string) []jc.SimpleMessage {
   481  	outLogs := []jc.SimpleMessage{}
   482  
   483  	if target == "databaseMaster" || target == "controller" {
   484  		outLogs = append(outLogs, jc.SimpleMessage{
   485  			Level: loggo.INFO, Message: "waiting for other controllers to be ready for upgrade",
   486  		})
   487  		outLogs = append(outLogs, jc.SimpleMessage{
   488  			Level:   loggo.INFO,
   489  			Message: "finished waiting - all controllers are ready to run upgrade steps",
   490  		})
   491  	}
   492  
   493  	outLogs = append(outLogs, jc.SimpleMessage{
   494  		Level: loggo.INFO, Message: fmt.Sprintf(
   495  			`starting upgrade from %s to %s for "machine-0"`,
   496  			s.oldVersion.Number, jujuversion.Current),
   497  	})
   498  
   499  	failMessage := fmt.Sprintf(
   500  		`upgrade from %s to %s for "machine-0" failed \(%%s\): %s`,
   501  		s.oldVersion.Number, jujuversion.Current, failReason)
   502  
   503  	for i := 0; i < retryCount; i++ {
   504  		outLogs = append(outLogs, jc.SimpleMessage{Level: loggo.ERROR, Message: fmt.Sprintf(failMessage, "will retry")})
   505  	}
   506  	if expectFail {
   507  		outLogs = append(outLogs, jc.SimpleMessage{Level: loggo.ERROR, Message: fmt.Sprintf(failMessage, "giving up")})
   508  	} else {
   509  		outLogs = append(outLogs, jc.SimpleMessage{Level: loggo.INFO,
   510  			Message: fmt.Sprintf(`upgrade to %s completed successfully.`, jujuversion.Current)})
   511  	}
   512  	return outLogs
   513  }
   514  
   515  func (s *UpgradeSuite) assertEnvironAgentVersion(c *gc.C, expected version.Number) {
   516  	envConfig, err := s.Model.ModelConfig()
   517  	c.Assert(err, jc.ErrorIsNil)
   518  	agentVersion, ok := envConfig.AgentVersion()
   519  	c.Assert(ok, jc.IsTrue)
   520  	c.Assert(agentVersion, gc.Equals, expected)
   521  }
   522  
   523  // NewFakeConfigSetter returns a fakeConfigSetter which implements
   524  // just enough of the agent.ConfigSetter interface to keep the upgrade
   525  // steps worker happy.
   526  func NewFakeConfigSetter(agentTag names.Tag, initialVersion version.Number) *fakeConfigSetter {
   527  	return &fakeConfigSetter{
   528  		AgentTag: agentTag,
   529  		Version:  initialVersion,
   530  	}
   531  }
   532  
   533  type fakeConfigSetter struct {
   534  	agent.ConfigSetter
   535  	AgentTag names.Tag
   536  	Version  version.Number
   537  }
   538  
   539  func (s *fakeConfigSetter) Tag() names.Tag {
   540  	return s.AgentTag
   541  }
   542  
   543  func (s *fakeConfigSetter) UpgradedToVersion() version.Number {
   544  	return s.Version
   545  }
   546  
   547  func (s *fakeConfigSetter) SetUpgradedToVersion(newVersion version.Number) {
   548  	s.Version = newVersion
   549  }
   550  
   551  // NewFakeAgent returns a fakeAgent which implements the agent.Agent
   552  // interface. This provides enough MachineAgent functionality to
   553  // support upgrades.
   554  func NewFakeAgent(confSetter agent.ConfigSetter) *fakeAgent {
   555  	return &fakeAgent{
   556  		config: confSetter,
   557  	}
   558  }
   559  
   560  type fakeAgent struct {
   561  	config agent.ConfigSetter
   562  }
   563  
   564  func (a *fakeAgent) CurrentConfig() agent.Config {
   565  	return a.config
   566  }
   567  
   568  func (a *fakeAgent) ChangeConfig(mutate agent.ConfigMutator) error {
   569  	return mutate(a.config)
   570  }
   571  
   572  type StatusCall struct {
   573  	Status status.Status
   574  	Info   string
   575  }
   576  
   577  type testStatusSetter struct {
   578  	Calls []StatusCall
   579  }
   580  
   581  func (s *testStatusSetter) SetStatus(status status.Status, info string, _ map[string]interface{}) error {
   582  	s.Calls = append(s.Calls, StatusCall{status, info})
   583  	return nil
   584  }