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