github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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/errors"
    14  	jc "github.com/juju/testing/checkers"
    15  	"github.com/juju/utils"
    16  	"github.com/juju/utils/arch"
    17  	"github.com/juju/utils/series"
    18  	"github.com/juju/utils/symlink"
    19  	"github.com/juju/version"
    20  	gc "gopkg.in/check.v1"
    21  	"gopkg.in/juju/names.v2"
    22  
    23  	"github.com/juju/juju/agent"
    24  	agenttools "github.com/juju/juju/agent/tools"
    25  	"github.com/juju/juju/api"
    26  	envtesting "github.com/juju/juju/environs/testing"
    27  	envtools "github.com/juju/juju/environs/tools"
    28  	jujutesting "github.com/juju/juju/juju/testing"
    29  	"github.com/juju/juju/state"
    30  	statetesting "github.com/juju/juju/state/testing"
    31  	coretesting "github.com/juju/juju/testing"
    32  	coretools "github.com/juju/juju/tools"
    33  	jujuversion "github.com/juju/juju/version"
    34  	"github.com/juju/juju/worker/gate"
    35  	"github.com/juju/juju/worker/upgrader"
    36  )
    37  
    38  func TestPackage(t *stdtesting.T) {
    39  	coretesting.MgoTestPackage(t)
    40  }
    41  
    42  type UpgraderSuite struct {
    43  	jujutesting.JujuConnSuite
    44  
    45  	machine              *state.Machine
    46  	state                api.Connection
    47  	oldRetryAfter        func() <-chan time.Time
    48  	confVersion          version.Number
    49  	upgradeStepsComplete gate.Lock
    50  	initialCheckComplete gate.Lock
    51  }
    52  
    53  type AllowedTargetVersionSuite struct{}
    54  
    55  var _ = gc.Suite(&UpgraderSuite{})
    56  var _ = gc.Suite(&AllowedTargetVersionSuite{})
    57  
    58  func (s *UpgraderSuite) SetUpTest(c *gc.C) {
    59  	s.JujuConnSuite.SetUpTest(c)
    60  	// s.machine needs to have IsManager() so that it can get the actual
    61  	// current revision to upgrade to.
    62  	s.state, s.machine = s.OpenAPIAsNewMachine(c, state.JobManageModel)
    63  	// Capture the value of RetryAfter, and use that captured
    64  	// value in the cleanup lambda.
    65  	oldRetryAfter := *upgrader.RetryAfter
    66  	s.AddCleanup(func(*gc.C) {
    67  		*upgrader.RetryAfter = oldRetryAfter
    68  	})
    69  	s.upgradeStepsComplete = gate.NewLock()
    70  	s.initialCheckComplete = gate.NewLock()
    71  }
    72  
    73  func (s *UpgraderSuite) patchVersion(v version.Binary) {
    74  	s.PatchValue(&arch.HostArch, func() string { return v.Arch })
    75  	s.PatchValue(&series.HostSeries, func() string { return v.Series })
    76  	s.PatchValue(&jujuversion.Current, v.Number)
    77  }
    78  
    79  type mockConfig struct {
    80  	agent.Config
    81  	tag     names.Tag
    82  	datadir string
    83  	version version.Number
    84  }
    85  
    86  func (mock *mockConfig) Tag() names.Tag {
    87  	return mock.tag
    88  }
    89  
    90  func (mock *mockConfig) DataDir() string {
    91  	return mock.datadir
    92  }
    93  
    94  func agentConfig(tag names.Tag, datadir string) agent.Config {
    95  	return &mockConfig{
    96  		tag:     tag,
    97  		datadir: datadir,
    98  	}
    99  }
   100  
   101  func (s *UpgraderSuite) makeUpgrader(c *gc.C) *upgrader.Upgrader {
   102  	w, err := upgrader.NewAgentUpgrader(
   103  		s.state.Upgrader(),
   104  		agentConfig(s.machine.Tag(), s.DataDir()),
   105  		s.confVersion,
   106  		s.upgradeStepsComplete,
   107  		s.initialCheckComplete,
   108  	)
   109  	c.Assert(err, jc.ErrorIsNil)
   110  	return w
   111  }
   112  
   113  func (s *UpgraderSuite) TestUpgraderSetsTools(c *gc.C) {
   114  	vers := version.MustParseBinary("5.4.3-precise-amd64")
   115  	err := statetesting.SetAgentVersion(s.State, vers.Number)
   116  	c.Assert(err, jc.ErrorIsNil)
   117  	stor := s.DefaultToolsStorage
   118  	agentTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), vers)
   119  	s.patchVersion(agentTools.Version)
   120  	err = envtools.MergeAndWriteMetadata(stor, "released", "released", coretools.List{agentTools}, envtools.DoNotWriteMirrors)
   121  	_, err = s.machine.AgentTools()
   122  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   123  
   124  	u := s.makeUpgrader(c)
   125  	statetesting.AssertStop(c, u)
   126  	s.expectInitialUpgradeCheckDone(c)
   127  	s.machine.Refresh()
   128  	gotTools, err := s.machine.AgentTools()
   129  	c.Assert(err, jc.ErrorIsNil)
   130  	envtesting.CheckTools(c, gotTools, agentTools)
   131  }
   132  
   133  func (s *UpgraderSuite) TestUpgraderSetVersion(c *gc.C) {
   134  	vers := version.MustParseBinary("5.4.3-precise-amd64")
   135  	agentTools := envtesting.PrimeTools(c, s.DefaultToolsStorage, s.DataDir(), s.Environ.Config().AgentStream(), vers)
   136  	s.patchVersion(agentTools.Version)
   137  	err := os.RemoveAll(filepath.Join(s.DataDir(), "tools"))
   138  	c.Assert(err, jc.ErrorIsNil)
   139  
   140  	_, err = s.machine.AgentTools()
   141  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   142  	err = statetesting.SetAgentVersion(s.State, vers.Number)
   143  	c.Assert(err, jc.ErrorIsNil)
   144  
   145  	u := s.makeUpgrader(c)
   146  	statetesting.AssertStop(c, u)
   147  	s.expectInitialUpgradeCheckDone(c)
   148  	s.machine.Refresh()
   149  	gotTools, err := s.machine.AgentTools()
   150  	c.Assert(err, jc.ErrorIsNil)
   151  	c.Assert(gotTools, gc.DeepEquals, &coretools.Tools{Version: vers})
   152  }
   153  
   154  func (s *UpgraderSuite) expectInitialUpgradeCheckDone(c *gc.C) {
   155  	c.Assert(s.initialCheckComplete.IsUnlocked(), jc.IsTrue)
   156  }
   157  
   158  func (s *UpgraderSuite) expectInitialUpgradeCheckNotDone(c *gc.C) {
   159  	c.Assert(s.initialCheckComplete.IsUnlocked(), jc.IsFalse)
   160  }
   161  
   162  func (s *UpgraderSuite) TestUpgraderUpgradesImmediately(c *gc.C) {
   163  	stor := s.DefaultToolsStorage
   164  	oldTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-precise-amd64"))
   165  	s.patchVersion(oldTools.Version)
   166  	newTools := envtesting.AssertUploadFakeToolsVersions(
   167  		c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.5-precise-amd64"))[0]
   168  	err := statetesting.SetAgentVersion(s.State, newTools.Version.Number)
   169  	c.Assert(err, jc.ErrorIsNil)
   170  
   171  	u := s.makeUpgrader(c)
   172  	err = u.Stop()
   173  	s.expectInitialUpgradeCheckNotDone(c)
   174  	envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{
   175  		AgentName: s.machine.Tag().String(),
   176  		OldTools:  oldTools.Version,
   177  		NewTools:  newTools.Version,
   178  		DataDir:   s.DataDir(),
   179  	})
   180  	foundTools, err := agenttools.ReadTools(s.DataDir(), newTools.Version)
   181  	c.Assert(err, jc.ErrorIsNil)
   182  	newTools.URL = fmt.Sprintf("https://%s/model/%s/tools/5.4.5-precise-amd64",
   183  		s.APIState.Addr(), coretesting.ModelTag.Id())
   184  	envtesting.CheckTools(c, foundTools, newTools)
   185  }
   186  
   187  func (s *UpgraderSuite) TestUpgraderRetryAndChanged(c *gc.C) {
   188  	stor := s.DefaultToolsStorage
   189  	oldTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-precise-amd64"))
   190  	s.patchVersion(oldTools.Version)
   191  	newTools := envtesting.AssertUploadFakeToolsVersions(
   192  		c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.5-precise-amd64"))[0]
   193  	err := statetesting.SetAgentVersion(s.State, newTools.Version.Number)
   194  	c.Assert(err, jc.ErrorIsNil)
   195  
   196  	retryc := make(chan time.Time)
   197  	*upgrader.RetryAfter = func() <-chan time.Time {
   198  		c.Logf("replacement retry after")
   199  		return retryc
   200  	}
   201  	err = stor.Remove(envtools.StorageName(newTools.Version, "released"))
   202  	c.Assert(err, jc.ErrorIsNil)
   203  	u := s.makeUpgrader(c)
   204  	defer u.Stop()
   205  	s.expectInitialUpgradeCheckNotDone(c)
   206  
   207  	for i := 0; i < 3; i++ {
   208  		select {
   209  		case retryc <- time.Now():
   210  		case <-time.After(coretesting.LongWait):
   211  			c.Fatalf("upgrader did not retry (attempt %d)", i)
   212  		}
   213  	}
   214  
   215  	// Make it upgrade to some newer tools that can be
   216  	// downloaded ok; it should stop retrying, download
   217  	// the newer tools and exit.
   218  	newerTools := envtesting.AssertUploadFakeToolsVersions(
   219  		c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.6-precise-amd64"))[0]
   220  
   221  	err = statetesting.SetAgentVersion(s.State, newerTools.Version.Number)
   222  	c.Assert(err, jc.ErrorIsNil)
   223  
   224  	s.BackingState.StartSync()
   225  	done := make(chan error)
   226  	go func() {
   227  		done <- u.Wait()
   228  	}()
   229  	select {
   230  	case err := <-done:
   231  		envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{
   232  			AgentName: s.machine.Tag().String(),
   233  			OldTools:  oldTools.Version,
   234  			NewTools:  newerTools.Version,
   235  			DataDir:   s.DataDir(),
   236  		})
   237  	case <-time.After(coretesting.LongWait):
   238  		c.Fatalf("upgrader did not quit after upgrading")
   239  	}
   240  }
   241  
   242  func (s *UpgraderSuite) TestChangeAgentTools(c *gc.C) {
   243  	oldTools := &coretools.Tools{
   244  		Version: version.MustParseBinary("1.2.3-quantal-amd64"),
   245  	}
   246  	stor := s.DefaultToolsStorage
   247  	newToolsBinary := "5.4.3-precise-amd64"
   248  	newTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary(newToolsBinary))
   249  	s.patchVersion(newTools.Version)
   250  	err := envtools.MergeAndWriteMetadata(stor, "released", "released", coretools.List{newTools}, envtools.DoNotWriteMirrors)
   251  	c.Assert(err, jc.ErrorIsNil)
   252  	ugErr := &upgrader.UpgradeReadyError{
   253  		AgentName: "anAgent",
   254  		OldTools:  oldTools.Version,
   255  		NewTools:  newTools.Version,
   256  		DataDir:   s.DataDir(),
   257  	}
   258  	err = ugErr.ChangeAgentTools()
   259  	c.Assert(err, jc.ErrorIsNil)
   260  	target := agenttools.ToolsDir(s.DataDir(), newToolsBinary)
   261  	link, err := symlink.Read(agenttools.ToolsDir(s.DataDir(), "anAgent"))
   262  	c.Assert(err, jc.ErrorIsNil)
   263  	c.Assert(link, jc.SamePath, target)
   264  }
   265  
   266  func (s *UpgraderSuite) TestUsesAlreadyDownloadedToolsIfAvailable(c *gc.C) {
   267  	oldVersion := version.MustParseBinary("1.2.3-quantal-amd64")
   268  	s.patchVersion(oldVersion)
   269  
   270  	newVersion := version.MustParseBinary("5.4.3-quantal-amd64")
   271  	err := statetesting.SetAgentVersion(s.State, newVersion.Number)
   272  	c.Assert(err, jc.ErrorIsNil)
   273  
   274  	// Install tools matching the new version in the data directory
   275  	// but *not* in environment storage. The upgrader should find the
   276  	// downloaded tools without looking in environment storage.
   277  	envtesting.InstallFakeDownloadedTools(c, s.DataDir(), newVersion)
   278  
   279  	u := s.makeUpgrader(c)
   280  	err = u.Stop()
   281  	s.expectInitialUpgradeCheckNotDone(c)
   282  
   283  	envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{
   284  		AgentName: s.machine.Tag().String(),
   285  		OldTools:  oldVersion,
   286  		NewTools:  newVersion,
   287  		DataDir:   s.DataDir(),
   288  	})
   289  }
   290  
   291  func (s *UpgraderSuite) TestUpgraderRefusesToDowngradeMinorVersions(c *gc.C) {
   292  	stor := s.DefaultToolsStorage
   293  	origTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-precise-amd64"))
   294  	s.patchVersion(origTools.Version)
   295  	downgradeTools := envtesting.AssertUploadFakeToolsVersions(
   296  		c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.3.3-precise-amd64"))[0]
   297  	err := statetesting.SetAgentVersion(s.State, downgradeTools.Version.Number)
   298  	c.Assert(err, jc.ErrorIsNil)
   299  
   300  	u := s.makeUpgrader(c)
   301  	err = u.Stop()
   302  	s.expectInitialUpgradeCheckDone(c)
   303  	// If the upgrade would have triggered, we would have gotten an
   304  	// UpgradeReadyError, since it was skipped, we get no error
   305  	c.Check(err, jc.ErrorIsNil)
   306  	_, err = agenttools.ReadTools(s.DataDir(), downgradeTools.Version)
   307  	// TODO: ReadTools *should* be returning some form of errors.NotFound,
   308  	// however, it just passes back a fmt.Errorf so we live with it
   309  	// c.Assert(err, jc.Satisfies, errors.IsNotFound)
   310  	c.Check(err, gc.ErrorMatches, "cannot read tools metadata in tools directory.*"+utils.NoSuchFileErrRegexp)
   311  }
   312  
   313  func (s *UpgraderSuite) TestUpgraderAllowsDowngradingPatchVersions(c *gc.C) {
   314  	stor := s.DefaultToolsStorage
   315  	origTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-precise-amd64"))
   316  	s.patchVersion(origTools.Version)
   317  	downgradeTools := envtesting.AssertUploadFakeToolsVersions(
   318  		c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.2-precise-amd64"))[0]
   319  	err := statetesting.SetAgentVersion(s.State, downgradeTools.Version.Number)
   320  	c.Assert(err, jc.ErrorIsNil)
   321  
   322  	u := s.makeUpgrader(c)
   323  	err = u.Stop()
   324  	s.expectInitialUpgradeCheckNotDone(c)
   325  	envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{
   326  		AgentName: s.machine.Tag().String(),
   327  		OldTools:  origTools.Version,
   328  		NewTools:  downgradeTools.Version,
   329  		DataDir:   s.DataDir(),
   330  	})
   331  	foundTools, err := agenttools.ReadTools(s.DataDir(), downgradeTools.Version)
   332  	c.Assert(err, jc.ErrorIsNil)
   333  	downgradeTools.URL = fmt.Sprintf("https://%s/model/%s/tools/5.4.2-precise-amd64",
   334  		s.APIState.Addr(), coretesting.ModelTag.Id())
   335  	envtesting.CheckTools(c, foundTools, downgradeTools)
   336  }
   337  
   338  func (s *UpgraderSuite) TestUpgraderAllowsDowngradeToOrigVersionIfUpgradeInProgress(c *gc.C) {
   339  	// note: otherwise illegal version jump
   340  	downgradeVersion := version.MustParseBinary("5.3.0-precise-amd64")
   341  	s.confVersion = downgradeVersion.Number
   342  
   343  	stor := s.DefaultToolsStorage
   344  	origTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-precise-amd64"))
   345  	s.patchVersion(origTools.Version)
   346  	downgradeTools := envtesting.AssertUploadFakeToolsVersions(
   347  		c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), downgradeVersion)[0]
   348  	err := statetesting.SetAgentVersion(s.State, downgradeVersion.Number)
   349  	c.Assert(err, jc.ErrorIsNil)
   350  
   351  	u := s.makeUpgrader(c)
   352  	err = u.Stop()
   353  	s.expectInitialUpgradeCheckNotDone(c)
   354  	envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{
   355  		AgentName: s.machine.Tag().String(),
   356  		OldTools:  origTools.Version,
   357  		NewTools:  downgradeVersion,
   358  		DataDir:   s.DataDir(),
   359  	})
   360  	foundTools, err := agenttools.ReadTools(s.DataDir(), downgradeTools.Version)
   361  	c.Assert(err, jc.ErrorIsNil)
   362  	downgradeTools.URL = fmt.Sprintf("https://%s/model/%s/tools/5.3.0-precise-amd64",
   363  		s.APIState.Addr(), coretesting.ModelTag.Id())
   364  	envtesting.CheckTools(c, foundTools, downgradeTools)
   365  }
   366  
   367  func (s *UpgraderSuite) TestUpgraderRefusesDowngradeToOrigVersionIfUpgradeNotInProgress(c *gc.C) {
   368  	downgradeVersion := version.MustParseBinary("5.3.0-precise-amd64")
   369  	s.confVersion = downgradeVersion.Number
   370  	s.upgradeStepsComplete.Unlock()
   371  
   372  	stor := s.DefaultToolsStorage
   373  	origTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-precise-amd64"))
   374  	s.patchVersion(origTools.Version)
   375  	envtesting.AssertUploadFakeToolsVersions(
   376  		c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), downgradeVersion)
   377  	err := statetesting.SetAgentVersion(s.State, downgradeVersion.Number)
   378  	c.Assert(err, jc.ErrorIsNil)
   379  
   380  	u := s.makeUpgrader(c)
   381  	err = u.Stop()
   382  	s.expectInitialUpgradeCheckDone(c)
   383  
   384  	// If the upgrade would have triggered, we would have gotten an
   385  	// UpgradeReadyError, since it was skipped, we get no error
   386  	c.Check(err, jc.ErrorIsNil)
   387  }
   388  
   389  type allowedTest struct {
   390  	original       string
   391  	current        string
   392  	target         string
   393  	upgradeRunning bool
   394  	allowed        bool
   395  }
   396  
   397  func (s *AllowedTargetVersionSuite) TestAllowedTargetVersionSuite(c *gc.C) {
   398  	cases := []allowedTest{
   399  		{original: "1.2.3", current: "1.2.3", upgradeRunning: false, target: "1.3.3", allowed: true},
   400  		{original: "1.2.3", current: "1.2.3", upgradeRunning: false, target: "1.2.3", allowed: true},
   401  		{original: "1.2.3", current: "1.2.3", upgradeRunning: false, target: "2.2.3", allowed: true},
   402  		{original: "1.2.3", current: "1.2.3", upgradeRunning: false, target: "1.1.3", allowed: false},
   403  		{original: "1.2.3", current: "1.2.3", upgradeRunning: false, target: "1.2.2", allowed: true}, // downgrade between builds
   404  		{original: "1.2.3", current: "1.2.3", upgradeRunning: false, target: "0.2.3", allowed: false},
   405  		{original: "0.2.3", current: "1.2.3", upgradeRunning: false, target: "0.2.3", allowed: false},
   406  		{original: "0.2.3", current: "1.2.3", upgradeRunning: true, target: "0.2.3", allowed: true}, // downgrade during upgrade
   407  	}
   408  	for i, test := range cases {
   409  		c.Logf("test case %d, %#v", i, test)
   410  		original := version.MustParse(test.original)
   411  		current := version.MustParse(test.current)
   412  		target := version.MustParse(test.target)
   413  		result := upgrader.AllowedTargetVersion(original, current, test.upgradeRunning, target)
   414  		c.Check(result, gc.Equals, test.allowed)
   415  	}
   416  }