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