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

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package upgrader_test
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	stdtesting "testing"
    11  	"time"
    12  
    13  	"github.com/juju/clock/testclock"
    14  	"github.com/juju/errors"
    15  	"github.com/juju/loggo"
    16  	"github.com/juju/names/v5"
    17  	"github.com/juju/testing"
    18  	jc "github.com/juju/testing/checkers"
    19  	"github.com/juju/utils/v3"
    20  	"github.com/juju/utils/v3/symlink"
    21  	"github.com/juju/version/v2"
    22  	"github.com/juju/worker/v3"
    23  	"github.com/juju/worker/v3/workertest"
    24  	gc "gopkg.in/check.v1"
    25  
    26  	"github.com/juju/juju/agent"
    27  	agenttools "github.com/juju/juju/agent/tools"
    28  	"github.com/juju/juju/api"
    29  	upgraderapi "github.com/juju/juju/api/agent/upgrader"
    30  	agenterrors "github.com/juju/juju/cmd/jujud/agent/errors"
    31  	"github.com/juju/juju/core/arch"
    32  	coreos "github.com/juju/juju/core/os"
    33  	"github.com/juju/juju/core/os/ostype"
    34  	"github.com/juju/juju/environs/simplestreams"
    35  	sstesting "github.com/juju/juju/environs/simplestreams/testing"
    36  	envtesting "github.com/juju/juju/environs/testing"
    37  	envtools "github.com/juju/juju/environs/tools"
    38  	jujutesting "github.com/juju/juju/juju/testing"
    39  	"github.com/juju/juju/state"
    40  	statetesting "github.com/juju/juju/state/testing"
    41  	coretesting "github.com/juju/juju/testing"
    42  	coretools "github.com/juju/juju/tools"
    43  	"github.com/juju/juju/upgrades"
    44  	jujuversion "github.com/juju/juju/version"
    45  	"github.com/juju/juju/worker/gate"
    46  	"github.com/juju/juju/worker/upgrader"
    47  )
    48  
    49  func TestPackage(t *stdtesting.T) {
    50  	coretesting.MgoTestPackage(t)
    51  }
    52  
    53  type UpgraderSuite struct {
    54  	jujutesting.JujuConnSuite
    55  
    56  	machine              *state.Machine
    57  	state                api.Connection
    58  	confVersion          version.Number
    59  	upgradeStepsComplete gate.Lock
    60  	initialCheckComplete gate.Lock
    61  	clock                *testclock.Clock
    62  }
    63  
    64  type AllowedTargetVersionSuite struct{}
    65  
    66  var _ = gc.Suite(&UpgraderSuite{})
    67  var _ = gc.Suite(&AllowedTargetVersionSuite{})
    68  
    69  func (s *UpgraderSuite) SetUpTest(c *gc.C) {
    70  	s.JujuConnSuite.SetUpTest(c)
    71  	// s.machine needs to have IsManager() so that it can get the actual
    72  	// current revision to upgrade to.
    73  	s.state, s.machine = s.OpenAPIAsNewMachine(c, state.JobManageModel)
    74  
    75  	// For expediency we assume that upgrade-steps have run as the default.
    76  	// Create a new locked gate for alternative test composition.
    77  	s.upgradeStepsComplete = gate.NewLock()
    78  	s.upgradeStepsComplete.Unlock()
    79  
    80  	s.initialCheckComplete = gate.NewLock()
    81  	s.clock = testclock.NewClock(time.Now())
    82  }
    83  
    84  func (s *UpgraderSuite) patchVersion(v version.Binary) {
    85  	s.PatchValue(&arch.HostArch, func() string { return v.Arch })
    86  	s.PatchValue(&coreos.HostOS, func() ostype.OSType { return ostype.Ubuntu })
    87  	vers := v.Number
    88  	vers.Build = 666
    89  	s.PatchValue(&jujuversion.Current, vers)
    90  }
    91  
    92  type mockConfig struct {
    93  	agent.Config
    94  	tag     names.Tag
    95  	datadir string
    96  }
    97  
    98  func (mock *mockConfig) Tag() names.Tag {
    99  	return mock.tag
   100  }
   101  
   102  func (mock *mockConfig) DataDir() string {
   103  	return mock.datadir
   104  }
   105  
   106  func agentConfig(tag names.Tag, datadir string) agent.Config {
   107  	return &mockConfig{
   108  		tag:     tag,
   109  		datadir: datadir,
   110  	}
   111  }
   112  
   113  func (s *UpgraderSuite) makeUpgrader(c *gc.C) *upgrader.Upgrader {
   114  	w, err := upgrader.NewAgentUpgrader(upgrader.Config{
   115  		Clock:                       s.clock,
   116  		Logger:                      loggo.GetLogger("test"),
   117  		State:                       upgraderapi.NewState(s.state),
   118  		AgentConfig:                 agentConfig(s.machine.Tag(), s.DataDir()),
   119  		OrigAgentVersion:            s.confVersion,
   120  		UpgradeStepsWaiter:          s.upgradeStepsComplete,
   121  		InitialUpgradeCheckComplete: s.initialCheckComplete,
   122  		CheckDiskSpace:              func(string, uint64) error { return nil },
   123  	})
   124  	c.Assert(err, jc.ErrorIsNil)
   125  	return w
   126  }
   127  
   128  func (s *UpgraderSuite) TestUpgraderSetsTools(c *gc.C) {
   129  	vers := version.MustParseBinary("5.4.3-ubuntu-amd64")
   130  	err := statetesting.SetAgentVersion(s.State, vers.Number)
   131  	c.Assert(err, jc.ErrorIsNil)
   132  
   133  	store := s.DefaultToolsStorage
   134  	agentTools := envtesting.PrimeTools(c, store, s.DataDir(), s.Environ.Config().AgentStream(), vers)
   135  	s.patchVersion(agentTools.Version)
   136  
   137  	ss := simplestreams.NewSimpleStreams(sstesting.TestDataSourceFactory())
   138  	err = envtools.MergeAndWriteMetadata(
   139  		ss, store, "released", "released", coretools.List{agentTools}, envtools.DoNotWriteMirrors)
   140  	c.Assert(err, jc.ErrorIsNil)
   141  
   142  	_, err = s.machine.AgentTools()
   143  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   144  
   145  	u := s.makeUpgrader(c)
   146  	s.waitForUpgradeCheck(c)
   147  	workertest.CleanKill(c, u)
   148  
   149  	err = s.machine.Refresh()
   150  	c.Assert(err, jc.ErrorIsNil)
   151  
   152  	gotTools, err := s.machine.AgentTools()
   153  
   154  	c.Assert(err, jc.ErrorIsNil)
   155  	agentTools.Version.Build = 666
   156  	envtesting.CheckTools(c, gotTools, agentTools)
   157  }
   158  
   159  func (s *UpgraderSuite) TestUpgraderSetVersion(c *gc.C) {
   160  	vers := version.MustParseBinary("5.4.3-ubuntu-amd64")
   161  	agentTools := envtesting.PrimeTools(c, s.DefaultToolsStorage, s.DataDir(), s.Environ.Config().AgentStream(), vers)
   162  	s.patchVersion(agentTools.Version)
   163  	err := os.RemoveAll(filepath.Join(s.DataDir(), "tools"))
   164  	c.Assert(err, jc.ErrorIsNil)
   165  
   166  	_, err = s.machine.AgentTools()
   167  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   168  	err = statetesting.SetAgentVersion(s.State, vers.Number)
   169  	c.Assert(err, jc.ErrorIsNil)
   170  
   171  	u := s.makeUpgrader(c)
   172  	s.waitForUpgradeCheck(c)
   173  	workertest.CleanKill(c, u)
   174  
   175  	err = s.machine.Refresh()
   176  	c.Assert(err, jc.ErrorIsNil)
   177  	gotTools, err := s.machine.AgentTools()
   178  	c.Assert(err, jc.ErrorIsNil)
   179  	vers.Build = 666
   180  	c.Assert(gotTools, gc.DeepEquals, &coretools.Tools{Version: vers})
   181  }
   182  
   183  func (s *UpgraderSuite) TestUpgraderWaitsForUpgradeStepsGate(c *gc.C) {
   184  	// Replace with a locked gate.
   185  	s.upgradeStepsComplete = gate.NewLock()
   186  
   187  	stor := s.DefaultToolsStorage
   188  
   189  	oldTools := envtesting.PrimeTools(
   190  		c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-ubuntu-amd64"))
   191  	s.patchVersion(oldTools.Version)
   192  
   193  	newTools := envtesting.AssertUploadFakeToolsVersions(
   194  		c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(),
   195  		version.MustParseBinary("5.4.5-ubuntu-amd64"))[0]
   196  	err := statetesting.SetAgentVersion(s.State, newTools.Version.Number)
   197  	c.Assert(err, jc.ErrorIsNil)
   198  
   199  	u := s.makeUpgrader(c)
   200  	workertest.CheckAlive(c, u)
   201  
   202  	s.expectInitialUpgradeCheckNotDone(c)
   203  
   204  	// No upgrade-ready error.
   205  	workertest.CleanKill(c, u)
   206  }
   207  
   208  func (s *UpgraderSuite) TestUpgraderUpgradesImmediately(c *gc.C) {
   209  	stor := s.DefaultToolsStorage
   210  
   211  	oldTools := envtesting.PrimeTools(
   212  		c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-ubuntu-amd64"))
   213  	s.patchVersion(oldTools.Version)
   214  
   215  	newTools := envtesting.AssertUploadFakeToolsVersions(
   216  		c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(),
   217  		version.MustParseBinary("5.4.5-ubuntu-amd64"))[0]
   218  	err := statetesting.SetAgentVersion(s.State, newTools.Version.Number)
   219  	c.Assert(err, jc.ErrorIsNil)
   220  
   221  	u := s.makeUpgrader(c)
   222  	err = workertest.CheckKilled(c, u)
   223  	s.expectInitialUpgradeCheckNotDone(c)
   224  
   225  	envtesting.CheckUpgraderReadyError(c, err, &agenterrors.UpgradeReadyError{
   226  		AgentName: s.machine.Tag().String(),
   227  		OldTools:  oldTools.Version,
   228  		NewTools:  newTools.Version,
   229  		DataDir:   s.DataDir(),
   230  	})
   231  	foundTools, err := agenttools.ReadTools(s.DataDir(), newTools.Version)
   232  	c.Assert(err, jc.ErrorIsNil)
   233  	newTools.URL = fmt.Sprintf("https://%s/model/%s/tools/5.4.5-ubuntu-amd64",
   234  		s.APIState.Addr(), coretesting.ModelTag.Id())
   235  	envtesting.CheckTools(c, foundTools, newTools)
   236  }
   237  
   238  func (s *UpgraderSuite) TestUpgraderRetryAndChanged(c *gc.C) {
   239  	stor := s.DefaultToolsStorage
   240  
   241  	oldTools := envtesting.PrimeTools(
   242  		c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-ubuntu-amd64"))
   243  	s.patchVersion(oldTools.Version)
   244  
   245  	newTools := envtesting.AssertUploadFakeToolsVersions(
   246  		c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(),
   247  		version.MustParseBinary("5.4.5-ubuntu-amd64"))[0]
   248  	err := statetesting.SetAgentVersion(s.State, newTools.Version.Number)
   249  	c.Assert(err, jc.ErrorIsNil)
   250  
   251  	err = stor.Remove(envtools.StorageName(newTools.Version, "released"))
   252  	c.Assert(err, jc.ErrorIsNil)
   253  
   254  	u := s.makeUpgrader(c)
   255  	defer func() { _ = workertest.CheckKilled(c, u) }()
   256  	s.expectInitialUpgradeCheckNotDone(c)
   257  
   258  	for i := 0; i < 3; i++ {
   259  		err := s.clock.WaitAdvance(5*time.Second, coretesting.LongWait, 1)
   260  		c.Assert(err, jc.ErrorIsNil)
   261  	}
   262  
   263  	// Make it upgrade to some newer tools that can be
   264  	// downloaded ok; it should stop retrying, download
   265  	// the newer tools and exit.
   266  	newerTools := envtesting.AssertUploadFakeToolsVersions(
   267  		c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(),
   268  		version.MustParseBinary("5.4.6-ubuntu-amd64"))[0]
   269  
   270  	err = statetesting.SetAgentVersion(s.State, newerTools.Version.Number)
   271  	c.Assert(err, jc.ErrorIsNil)
   272  
   273  	done := make(chan error)
   274  	go func() {
   275  		done <- u.Wait()
   276  	}()
   277  	select {
   278  	case err := <-done:
   279  		envtesting.CheckUpgraderReadyError(c, err, &agenterrors.UpgradeReadyError{
   280  			AgentName: s.machine.Tag().String(),
   281  			OldTools:  oldTools.Version,
   282  			NewTools:  newerTools.Version,
   283  			DataDir:   s.DataDir(),
   284  		})
   285  	case <-time.After(coretesting.LongWait):
   286  		c.Fatalf("upgrader did not quit after upgrading")
   287  	}
   288  }
   289  
   290  func (s *UpgraderSuite) TestChangeAgentTools(c *gc.C) {
   291  	oldTools := &coretools.Tools{Version: version.MustParseBinary("1.2.3-ubuntu-amd64")}
   292  
   293  	store := s.DefaultToolsStorage
   294  	newToolsBinary := "5.4.3-ubuntu-amd64"
   295  	newTools := envtesting.PrimeTools(
   296  		c, store, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary(newToolsBinary))
   297  	s.patchVersion(newTools.Version)
   298  
   299  	ss := simplestreams.NewSimpleStreams(sstesting.TestDataSourceFactory())
   300  	err := envtools.MergeAndWriteMetadata(
   301  		ss, store, "released", "released", coretools.List{newTools}, envtools.DoNotWriteMirrors)
   302  	c.Assert(err, jc.ErrorIsNil)
   303  
   304  	ugErr := &agenterrors.UpgradeReadyError{
   305  		AgentName: "anAgent",
   306  		OldTools:  oldTools.Version,
   307  		NewTools:  newTools.Version,
   308  		DataDir:   s.DataDir(),
   309  	}
   310  	err = ugErr.ChangeAgentTools(loggo.GetLogger("test"))
   311  	c.Assert(err, jc.ErrorIsNil)
   312  
   313  	target := agenttools.ToolsDir(s.DataDir(), newToolsBinary)
   314  	link, err := symlink.Read(agenttools.ToolsDir(s.DataDir(), "anAgent"))
   315  	c.Assert(err, jc.ErrorIsNil)
   316  	c.Assert(link, jc.SamePath, target)
   317  }
   318  
   319  func (s *UpgraderSuite) TestUsesAlreadyDownloadedToolsIfAvailable(c *gc.C) {
   320  	oldVersion := version.MustParseBinary("1.2.3-ubuntu-amd64")
   321  	s.patchVersion(oldVersion)
   322  
   323  	newVersion := version.MustParseBinary("5.4.3-ubuntu-amd64")
   324  	err := statetesting.SetAgentVersion(s.State, newVersion.Number)
   325  	c.Assert(err, jc.ErrorIsNil)
   326  
   327  	// Install tools matching the new version in the data directory
   328  	// but *not* in environment storage. The upgrader should find the
   329  	// downloaded tools without looking in environment storage.
   330  	envtesting.InstallFakeDownloadedTools(c, s.DataDir(), newVersion)
   331  
   332  	u := s.makeUpgrader(c)
   333  	err = workertest.CheckKilled(c, u)
   334  	s.expectInitialUpgradeCheckNotDone(c)
   335  
   336  	envtesting.CheckUpgraderReadyError(c, err, &agenterrors.UpgradeReadyError{
   337  		AgentName: s.machine.Tag().String(),
   338  		OldTools:  oldVersion,
   339  		NewTools:  newVersion,
   340  		DataDir:   s.DataDir(),
   341  	})
   342  }
   343  
   344  func (s *UpgraderSuite) TestUpgraderAllowsDowngradingMinorVersions(c *gc.C) {
   345  	// We allow this scenario to allow reverting upgrades by restoring
   346  	// a backup from the previous version.
   347  	stor := s.DefaultToolsStorage
   348  	origTools := envtesting.PrimeTools(
   349  		c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-ubuntu-amd64"))
   350  	s.patchVersion(origTools.Version)
   351  
   352  	downgradeTools := envtesting.AssertUploadFakeToolsVersions(
   353  		c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(),
   354  		version.MustParseBinary("5.3.3-ubuntu-amd64"))[0]
   355  	err := statetesting.SetAgentVersion(s.State, downgradeTools.Version.Number)
   356  	c.Assert(err, jc.ErrorIsNil)
   357  
   358  	u := s.makeUpgrader(c)
   359  	err = workertest.CheckKilled(c, u)
   360  	s.expectInitialUpgradeCheckNotDone(c)
   361  
   362  	envtesting.CheckUpgraderReadyError(c, err, &agenterrors.UpgradeReadyError{
   363  		AgentName: s.machine.Tag().String(),
   364  		OldTools:  origTools.Version,
   365  		NewTools:  downgradeTools.Version,
   366  		DataDir:   s.DataDir(),
   367  	})
   368  	foundTools, err := agenttools.ReadTools(s.DataDir(), downgradeTools.Version)
   369  	c.Assert(err, jc.ErrorIsNil)
   370  	downgradeTools.URL = fmt.Sprintf("https://%s/model/%s/tools/5.3.3-ubuntu-amd64",
   371  		s.APIState.Addr(), coretesting.ModelTag.Id())
   372  	envtesting.CheckTools(c, foundTools, downgradeTools)
   373  }
   374  
   375  func (s *UpgraderSuite) TestUpgraderForbidsDowngradingToMajorVersion(c *gc.C) {
   376  	stor := s.DefaultToolsStorage
   377  	origTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(),
   378  		version.MustParseBinary("2.4.3-ubuntu-amd64"))
   379  	s.patchVersion(origTools.Version)
   380  
   381  	downgradeTools := envtesting.AssertUploadFakeToolsVersions(
   382  		c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(),
   383  		version.MustParseBinary("1.25.3-ubuntu-amd64"))[0]
   384  	err := statetesting.SetAgentVersion(s.State, downgradeTools.Version.Number)
   385  	c.Assert(err, jc.ErrorIsNil)
   386  
   387  	u := s.makeUpgrader(c)
   388  	s.waitForUpgradeCheck(c)
   389  	err = worker.Stop(u)
   390  
   391  	// If the upgrade had been allowed we would get an UpgradeReadyError.
   392  	c.Assert(err, jc.ErrorIsNil)
   393  	_, err = agenttools.ReadTools(s.DataDir(), downgradeTools.Version)
   394  	// TODO: ReadTools *should* be returning some form of
   395  	// errors.NotFound, however, it just passes back a fmt.Errorf so
   396  	// we live with it c.Assert(err, jc.Satisfies, errors.IsNotFound)
   397  	c.Check(err, gc.ErrorMatches, "cannot read agent metadata in directory.*"+utils.NoSuchFileErrRegexp)
   398  }
   399  
   400  func (s *UpgraderSuite) TestUpgraderAllowsDowngradingPatchVersions(c *gc.C) {
   401  	stor := s.DefaultToolsStorage
   402  	origTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(),
   403  		version.MustParseBinary("5.4.3-ubuntu-amd64"))
   404  	s.patchVersion(origTools.Version)
   405  
   406  	downgradeTools := envtesting.AssertUploadFakeToolsVersions(
   407  		c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(),
   408  		version.MustParseBinary("5.4.2-ubuntu-amd64"))[0]
   409  	err := statetesting.SetAgentVersion(s.State, downgradeTools.Version.Number)
   410  	c.Assert(err, jc.ErrorIsNil)
   411  
   412  	u := s.makeUpgrader(c)
   413  	err = workertest.CheckKilled(c, u)
   414  	s.expectInitialUpgradeCheckNotDone(c)
   415  
   416  	envtesting.CheckUpgraderReadyError(c, err, &agenterrors.UpgradeReadyError{
   417  		AgentName: s.machine.Tag().String(),
   418  		OldTools:  origTools.Version,
   419  		NewTools:  downgradeTools.Version,
   420  		DataDir:   s.DataDir(),
   421  	})
   422  	foundTools, err := agenttools.ReadTools(s.DataDir(), downgradeTools.Version)
   423  	c.Assert(err, jc.ErrorIsNil)
   424  	downgradeTools.URL = fmt.Sprintf("https://%s/model/%s/tools/5.4.2-ubuntu-amd64",
   425  		s.APIState.Addr(), coretesting.ModelTag.Id())
   426  	envtesting.CheckTools(c, foundTools, downgradeTools)
   427  }
   428  
   429  func (s *UpgraderSuite) TestUpgraderAllowsDowngradeToPriorMinorVersion(c *gc.C) {
   430  	// We now allow this to support restoring
   431  	// a backup from a previous version.
   432  	downgradeVersion := version.MustParseBinary("5.3.0-ubuntu-amd64")
   433  	s.confVersion = downgradeVersion.Number
   434  
   435  	stor := s.DefaultToolsStorage
   436  	origTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(),
   437  		version.MustParseBinary("5.4.3-ubuntu-amd64"))
   438  	s.patchVersion(origTools.Version)
   439  
   440  	envtesting.AssertUploadFakeToolsVersions(
   441  		c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), downgradeVersion)
   442  
   443  	prevTools := envtesting.AssertUploadFakeToolsVersions(
   444  		c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), downgradeVersion)[0]
   445  
   446  	err := statetesting.SetAgentVersion(s.State, downgradeVersion.Number)
   447  	c.Assert(err, jc.ErrorIsNil)
   448  
   449  	u := s.makeUpgrader(c)
   450  	err = workertest.CheckKilled(c, u)
   451  	s.expectInitialUpgradeCheckNotDone(c)
   452  
   453  	envtesting.CheckUpgraderReadyError(c, err, &agenterrors.UpgradeReadyError{
   454  		AgentName: s.machine.Tag().String(),
   455  		OldTools:  origTools.Version,
   456  		NewTools:  prevTools.Version,
   457  		DataDir:   s.DataDir(),
   458  	})
   459  	foundTools, err := agenttools.ReadTools(s.DataDir(), prevTools.Version)
   460  	c.Assert(err, jc.ErrorIsNil)
   461  	prevTools.URL = fmt.Sprintf("https://%s/model/%s/tools/5.3.0-ubuntu-amd64",
   462  		s.APIState.Addr(), coretesting.ModelTag.Id())
   463  	envtesting.CheckTools(c, foundTools, prevTools)
   464  }
   465  
   466  func (s *UpgraderSuite) TestChecksSpaceBeforeDownloading(c *gc.C) {
   467  	stor := s.DefaultToolsStorage
   468  	oldTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(),
   469  		version.MustParseBinary("5.4.3-ubuntu-amd64"))
   470  	s.patchVersion(oldTools.Version)
   471  
   472  	newTools := envtesting.AssertUploadFakeToolsVersions(
   473  		c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(),
   474  		version.MustParseBinary("5.4.5-ubuntu-amd64"))[0]
   475  	err := statetesting.SetAgentVersion(s.State, newTools.Version.Number)
   476  	c.Assert(err, jc.ErrorIsNil)
   477  
   478  	// We want to wait for the model to settle so that we get a single event
   479  	// from the version watcher.
   480  	// If we start the worker too quickly after setting the new tools,
   481  	// it is possible to get 2 watcher changes - the guaranteed initial event
   482  	// and *then* the one for the change.
   483  	s.WaitForModelWatchersIdle(c, s.State.ModelUUID())
   484  
   485  	var diskSpaceStub testing.Stub
   486  	diskSpaceStub.SetErrors(nil, errors.Errorf("full-up"))
   487  	diskSpaceChecked := make(chan struct{}, 1)
   488  
   489  	u, err := upgrader.NewAgentUpgrader(upgrader.Config{
   490  		Clock:                       s.clock,
   491  		Logger:                      loggo.GetLogger("test"),
   492  		State:                       upgraderapi.NewState(s.state),
   493  		AgentConfig:                 agentConfig(s.machine.Tag(), s.DataDir()),
   494  		OrigAgentVersion:            s.confVersion,
   495  		UpgradeStepsWaiter:          s.upgradeStepsComplete,
   496  		InitialUpgradeCheckComplete: s.initialCheckComplete,
   497  		CheckDiskSpace: func(dir string, size uint64) error {
   498  			diskSpaceStub.AddCall("CheckDiskSpace", dir, size)
   499  
   500  			// CheckDiskSpace is called twice in checkForSpace.
   501  			// We only care that we arrived there, so if we've already buffered
   502  			// a write, just proceed.
   503  			select {
   504  			case diskSpaceChecked <- struct{}{}:
   505  			default:
   506  			}
   507  
   508  			return diskSpaceStub.NextErr()
   509  		},
   510  	})
   511  	c.Assert(err, jc.ErrorIsNil)
   512  
   513  	select {
   514  	case <-diskSpaceChecked:
   515  		workertest.CleanKill(c, u)
   516  	case <-time.After(coretesting.LongWait):
   517  		c.Fatalf("timed out waiting for disk space check.")
   518  	}
   519  
   520  	s.expectInitialUpgradeCheckNotDone(c)
   521  
   522  	c.Assert(diskSpaceStub.Calls(), gc.HasLen, 2)
   523  	diskSpaceStub.CheckCall(c, 0, "CheckDiskSpace", s.DataDir(), upgrades.MinDiskSpaceMib)
   524  	diskSpaceStub.CheckCall(c, 1, "CheckDiskSpace", os.TempDir(), upgrades.MinDiskSpaceMib)
   525  
   526  	_, err = agenttools.ReadTools(s.DataDir(), newTools.Version)
   527  	c.Assert(err, gc.ErrorMatches, `cannot read agent metadata in directory.*: no such file or directory`)
   528  }
   529  
   530  func (s *UpgraderSuite) waitForUpgradeCheck(c *gc.C) {
   531  	select {
   532  	case <-s.initialCheckComplete.Unlocked():
   533  	case <-time.After(coretesting.LongWait):
   534  		c.Fatalf("timed out waiting for initial upgrade check")
   535  	}
   536  }
   537  
   538  func (s *UpgraderSuite) expectInitialUpgradeCheckNotDone(c *gc.C) {
   539  	c.Assert(s.initialCheckComplete.IsUnlocked(), jc.IsFalse)
   540  }
   541  
   542  type allowedTest struct {
   543  	current string
   544  	target  string
   545  	allowed bool
   546  }
   547  
   548  func (s *AllowedTargetVersionSuite) TestAllowedTargetVersionSuite(c *gc.C) {
   549  	cases := []allowedTest{
   550  		{current: "2.7.4", target: "2.8.0", allowed: true},  // normal upgrade
   551  		{current: "2.8.0", target: "2.7.4", allowed: true},  // downgrade caused by restore after upgrade
   552  		{current: "3.8.0", target: "1.2.3", allowed: false}, // can't downgrade to major version 1.x
   553  		{current: "2.7.4", target: "2.7.5", allowed: true},  // point release
   554  		{current: "2.8.0", target: "2.7.4", allowed: true},  // downgrade after upgrade but before config file updated
   555  	}
   556  	for i, test := range cases {
   557  		c.Logf("test case %d, %#v", i, test)
   558  		current := version.MustParse(test.current)
   559  		target := version.MustParse(test.target)
   560  		result := upgrader.AllowedTargetVersion(current, target)
   561  		c.Check(result, gc.Equals, test.allowed)
   562  	}
   563  }