github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/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  	gc "launchpad.net/gocheck"
    17  
    18  	"github.com/juju/juju/agent"
    19  	agenttools "github.com/juju/juju/agent/tools"
    20  	envtesting "github.com/juju/juju/environs/testing"
    21  	envtools "github.com/juju/juju/environs/tools"
    22  	jujutesting "github.com/juju/juju/juju/testing"
    23  	"github.com/juju/juju/provider/dummy"
    24  	"github.com/juju/juju/state"
    25  	"github.com/juju/juju/state/api"
    26  	statetesting "github.com/juju/juju/state/testing"
    27  	coretesting "github.com/juju/juju/testing"
    28  	coretools "github.com/juju/juju/tools"
    29  	"github.com/juju/juju/version"
    30  	"github.com/juju/juju/worker/upgrader"
    31  )
    32  
    33  func TestPackage(t *stdtesting.T) {
    34  	coretesting.MgoTestPackage(t)
    35  }
    36  
    37  type UpgraderSuite struct {
    38  	jujutesting.JujuConnSuite
    39  
    40  	machine       *state.Machine
    41  	state         *api.State
    42  	oldRetryAfter func() <-chan time.Time
    43  }
    44  
    45  type AllowedTargetVersionSuite struct{}
    46  
    47  var _ = gc.Suite(&UpgraderSuite{})
    48  var _ = gc.Suite(&AllowedTargetVersionSuite{})
    49  
    50  func (s *UpgraderSuite) SetUpTest(c *gc.C) {
    51  	s.JujuConnSuite.SetUpTest(c)
    52  	// s.machine needs to have IsManager() so that it can get the actual
    53  	// current revision to upgrade to.
    54  	s.state, s.machine = s.OpenAPIAsNewMachine(c, state.JobManageEnviron)
    55  	// Capture the value of RetryAfter, and use that captured
    56  	// value in the cleanup lambda.
    57  	oldRetryAfter := *upgrader.RetryAfter
    58  	s.AddCleanup(func(*gc.C) {
    59  		*upgrader.RetryAfter = oldRetryAfter
    60  	})
    61  }
    62  
    63  type mockConfig struct {
    64  	agent.Config
    65  	tag     string
    66  	datadir string
    67  }
    68  
    69  func (mock *mockConfig) Tag() string {
    70  	return mock.tag
    71  }
    72  
    73  func (mock *mockConfig) DataDir() string {
    74  	return mock.datadir
    75  }
    76  
    77  func agentConfig(tag, datadir string) agent.Config {
    78  	return &mockConfig{tag: tag, datadir: datadir}
    79  }
    80  
    81  func (s *UpgraderSuite) makeUpgrader() *upgrader.Upgrader {
    82  	config := agentConfig(s.machine.Tag(), s.DataDir())
    83  	return upgrader.NewUpgrader(s.state.Upgrader(), config)
    84  }
    85  
    86  func (s *UpgraderSuite) TestUpgraderSetsTools(c *gc.C) {
    87  	vers := version.MustParseBinary("5.4.3-precise-amd64")
    88  	err := statetesting.SetAgentVersion(s.State, vers.Number)
    89  	c.Assert(err, gc.IsNil)
    90  	stor := s.Conn.Environ.Storage()
    91  	agentTools := envtesting.PrimeTools(c, stor, s.DataDir(), vers)
    92  	s.PatchValue(&version.Current, agentTools.Version)
    93  	err = envtools.MergeAndWriteMetadata(stor, coretools.List{agentTools}, envtools.DoNotWriteMirrors)
    94  	_, err = s.machine.AgentTools()
    95  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    96  
    97  	u := s.makeUpgrader()
    98  	statetesting.AssertStop(c, u)
    99  	s.machine.Refresh()
   100  	gotTools, err := s.machine.AgentTools()
   101  	c.Assert(err, gc.IsNil)
   102  	envtesting.CheckTools(c, gotTools, agentTools)
   103  }
   104  
   105  func (s *UpgraderSuite) TestUpgraderSetVersion(c *gc.C) {
   106  	vers := version.MustParseBinary("5.4.3-precise-amd64")
   107  	agentTools := envtesting.PrimeTools(c, s.Conn.Environ.Storage(), s.DataDir(), vers)
   108  	s.PatchValue(&version.Current, agentTools.Version)
   109  	err := os.RemoveAll(filepath.Join(s.DataDir(), "tools"))
   110  	c.Assert(err, gc.IsNil)
   111  
   112  	_, err = s.machine.AgentTools()
   113  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   114  	err = statetesting.SetAgentVersion(s.State, vers.Number)
   115  	c.Assert(err, gc.IsNil)
   116  
   117  	u := s.makeUpgrader()
   118  	statetesting.AssertStop(c, u)
   119  	s.machine.Refresh()
   120  	gotTools, err := s.machine.AgentTools()
   121  	c.Assert(err, gc.IsNil)
   122  	c.Assert(gotTools, gc.DeepEquals, &coretools.Tools{Version: version.Current})
   123  }
   124  
   125  func (s *UpgraderSuite) TestUpgraderUpgradesImmediately(c *gc.C) {
   126  	stor := s.Conn.Environ.Storage()
   127  	oldTools := envtesting.PrimeTools(c, stor, s.DataDir(), version.MustParseBinary("5.4.3-precise-amd64"))
   128  	s.PatchValue(&version.Current, oldTools.Version)
   129  	newTools := envtesting.AssertUploadFakeToolsVersions(
   130  		c, stor, version.MustParseBinary("5.4.5-precise-amd64"))[0]
   131  	err := statetesting.SetAgentVersion(s.State, newTools.Version.Number)
   132  	c.Assert(err, gc.IsNil)
   133  
   134  	// Make the download take a while so that we verify that
   135  	// the download happens before the upgrader checks if
   136  	// it's been stopped.
   137  	dummy.SetStorageDelay(coretesting.ShortWait)
   138  
   139  	u := s.makeUpgrader()
   140  	err = u.Stop()
   141  	envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{
   142  		AgentName: s.machine.Tag(),
   143  		OldTools:  oldTools.Version,
   144  		NewTools:  newTools.Version,
   145  		DataDir:   s.DataDir(),
   146  	})
   147  	foundTools, err := agenttools.ReadTools(s.DataDir(), newTools.Version)
   148  	c.Assert(err, gc.IsNil)
   149  	envtesting.CheckTools(c, foundTools, newTools)
   150  }
   151  
   152  func (s *UpgraderSuite) TestUpgraderRetryAndChanged(c *gc.C) {
   153  	stor := s.Conn.Environ.Storage()
   154  	oldTools := envtesting.PrimeTools(c, stor, s.DataDir(), version.MustParseBinary("5.4.3-precise-amd64"))
   155  	s.PatchValue(&version.Current, oldTools.Version)
   156  	newTools := envtesting.AssertUploadFakeToolsVersions(
   157  		c, stor, version.MustParseBinary("5.4.5-precise-amd64"))[0]
   158  	err := statetesting.SetAgentVersion(s.State, newTools.Version.Number)
   159  	c.Assert(err, gc.IsNil)
   160  
   161  	retryc := make(chan time.Time)
   162  	*upgrader.RetryAfter = func() <-chan time.Time {
   163  		c.Logf("replacement retry after")
   164  		return retryc
   165  	}
   166  	dummy.Poison(s.Conn.Environ.Storage(), envtools.StorageName(newTools.Version), fmt.Errorf("a non-fatal dose"))
   167  	u := s.makeUpgrader()
   168  	defer u.Stop()
   169  
   170  	for i := 0; i < 3; i++ {
   171  		select {
   172  		case retryc <- time.Now():
   173  		case <-time.After(coretesting.LongWait):
   174  			c.Fatalf("upgrader did not retry (attempt %d)", i)
   175  		}
   176  	}
   177  
   178  	// Make it upgrade to some newer tools that can be
   179  	// downloaded ok; it should stop retrying, download
   180  	// the newer tools and exit.
   181  	newerTools := envtesting.AssertUploadFakeToolsVersions(
   182  		c, s.Conn.Environ.Storage(), version.MustParseBinary("5.4.6-precise-amd64"))[0]
   183  
   184  	err = statetesting.SetAgentVersion(s.State, newerTools.Version.Number)
   185  	c.Assert(err, gc.IsNil)
   186  
   187  	s.BackingState.StartSync()
   188  	done := make(chan error)
   189  	go func() {
   190  		done <- u.Wait()
   191  	}()
   192  	select {
   193  	case err := <-done:
   194  		envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{
   195  			AgentName: s.machine.Tag(),
   196  			OldTools:  oldTools.Version,
   197  			NewTools:  newerTools.Version,
   198  			DataDir:   s.DataDir(),
   199  		})
   200  	case <-time.After(coretesting.LongWait):
   201  		c.Fatalf("upgrader did not quit after upgrading")
   202  	}
   203  }
   204  
   205  func (s *UpgraderSuite) TestChangeAgentTools(c *gc.C) {
   206  	oldTools := &coretools.Tools{
   207  		Version: version.MustParseBinary("1.2.3-quantal-amd64"),
   208  	}
   209  	stor := s.Conn.Environ.Storage()
   210  	newTools := envtesting.PrimeTools(c, stor, s.DataDir(), version.MustParseBinary("5.4.3-precise-amd64"))
   211  	s.PatchValue(&version.Current, newTools.Version)
   212  	err := envtools.MergeAndWriteMetadata(stor, coretools.List{newTools}, envtools.DoNotWriteMirrors)
   213  	c.Assert(err, gc.IsNil)
   214  	ugErr := &upgrader.UpgradeReadyError{
   215  		AgentName: "anAgent",
   216  		OldTools:  oldTools.Version,
   217  		NewTools:  newTools.Version,
   218  		DataDir:   s.DataDir(),
   219  	}
   220  	err = ugErr.ChangeAgentTools()
   221  	c.Assert(err, gc.IsNil)
   222  	link, err := os.Readlink(agenttools.ToolsDir(s.DataDir(), "anAgent"))
   223  	c.Assert(err, gc.IsNil)
   224  	c.Assert(link, gc.Equals, newTools.Version.String())
   225  }
   226  
   227  func (s *UpgraderSuite) TestEnsureToolsChecksBeforeDownloading(c *gc.C) {
   228  	stor := s.Conn.Environ.Storage()
   229  	newTools := envtesting.PrimeTools(c, stor, s.DataDir(), version.MustParseBinary("5.4.3-precise-amd64"))
   230  	s.PatchValue(&version.Current, newTools.Version)
   231  	// We've already downloaded the tools, so change the URL to be
   232  	// something invalid and ensure we don't actually get an error, because
   233  	// it doesn't actually do an HTTP request
   234  	u := s.makeUpgrader()
   235  	newTools.URL = "http://0.1.2.3/invalid/path/tools.tgz"
   236  	err := upgrader.EnsureTools(u, newTools, utils.VerifySSLHostnames)
   237  	c.Assert(err, gc.IsNil)
   238  }
   239  
   240  func (s *UpgraderSuite) TestUpgraderRefusesToDowngradeMinorVersions(c *gc.C) {
   241  	stor := s.Conn.Environ.Storage()
   242  	origTools := envtesting.PrimeTools(c, stor, s.DataDir(), version.MustParseBinary("5.4.3-precise-amd64"))
   243  	s.PatchValue(&version.Current, origTools.Version)
   244  	downgradeTools := envtesting.AssertUploadFakeToolsVersions(
   245  		c, stor, version.MustParseBinary("5.3.3-precise-amd64"))[0]
   246  	err := statetesting.SetAgentVersion(s.State, downgradeTools.Version.Number)
   247  	c.Assert(err, gc.IsNil)
   248  
   249  	u := s.makeUpgrader()
   250  	err = u.Stop()
   251  	// If the upgrade would have triggered, we would have gotten an
   252  	// UpgradeReadyError, since it was skipped, we get no error
   253  	c.Check(err, gc.IsNil)
   254  	_, err = agenttools.ReadTools(s.DataDir(), downgradeTools.Version)
   255  	// TODO: ReadTools *should* be returning some form of errors.NotFound,
   256  	// however, it just passes back a fmt.Errorf so we live with it
   257  	// c.Assert(err, jc.Satisfies, errors.IsNotFound)
   258  	c.Check(err, gc.ErrorMatches, "cannot read tools metadata in tools directory.*no such file or directory")
   259  }
   260  
   261  func (s *UpgraderSuite) TestUpgraderAllowsDowngradingPatchVersions(c *gc.C) {
   262  	stor := s.Conn.Environ.Storage()
   263  	origTools := envtesting.PrimeTools(c, stor, s.DataDir(), version.MustParseBinary("5.4.3-precise-amd64"))
   264  	s.PatchValue(&version.Current, origTools.Version)
   265  	downgradeTools := envtesting.AssertUploadFakeToolsVersions(
   266  		c, stor, version.MustParseBinary("5.4.2-precise-amd64"))[0]
   267  	err := statetesting.SetAgentVersion(s.State, downgradeTools.Version.Number)
   268  	c.Assert(err, gc.IsNil)
   269  
   270  	dummy.SetStorageDelay(coretesting.ShortWait)
   271  
   272  	u := s.makeUpgrader()
   273  	err = u.Stop()
   274  	envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{
   275  		AgentName: s.machine.Tag(),
   276  		OldTools:  origTools.Version,
   277  		NewTools:  downgradeTools.Version,
   278  		DataDir:   s.DataDir(),
   279  	})
   280  	foundTools, err := agenttools.ReadTools(s.DataDir(), downgradeTools.Version)
   281  	c.Assert(err, gc.IsNil)
   282  	envtesting.CheckTools(c, foundTools, downgradeTools)
   283  }
   284  
   285  type allowedTest struct {
   286  	current string
   287  	target  string
   288  	allowed bool
   289  }
   290  
   291  func (s *AllowedTargetVersionSuite) TestAllowedTargetVersionSuite(c *gc.C) {
   292  	cases := []allowedTest{
   293  		{current: "1.2.3", target: "1.3.3", allowed: true},
   294  		{current: "1.2.3", target: "1.2.3", allowed: true},
   295  		{current: "1.2.3", target: "2.2.3", allowed: true},
   296  		{current: "1.2.3", target: "1.1.3", allowed: false},
   297  		{current: "1.2.3", target: "1.2.2", allowed: true},
   298  		{current: "1.2.3", target: "0.2.3", allowed: false},
   299  	}
   300  	for i, test := range cases {
   301  		c.Logf("test case %d, %#v", i, test)
   302  		current := version.MustParse(test.current)
   303  		target := version.MustParse(test.target)
   304  		c.Check(upgrader.AllowedTargetVersion(current, target), gc.Equals, test.allowed)
   305  	}
   306  }