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