github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/cmd/juju/upgradejuju_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package main
     5  
     6  import (
     7  	"archive/tar"
     8  	"bytes"
     9  	"compress/gzip"
    10  	"io"
    11  	"io/ioutil"
    12  	"strings"
    13  
    14  	jc "github.com/juju/testing/checkers"
    15  	gc "launchpad.net/gocheck"
    16  
    17  	"github.com/juju/juju/cmd/envcmd"
    18  	"github.com/juju/juju/environs/config"
    19  	"github.com/juju/juju/environs/filestorage"
    20  	"github.com/juju/juju/environs/storage"
    21  	"github.com/juju/juju/environs/sync"
    22  	envtesting "github.com/juju/juju/environs/testing"
    23  	envtools "github.com/juju/juju/environs/tools"
    24  	toolstesting "github.com/juju/juju/environs/tools/testing"
    25  	"github.com/juju/juju/juju/testing"
    26  	coretesting "github.com/juju/juju/testing"
    27  	"github.com/juju/juju/version"
    28  )
    29  
    30  type UpgradeJujuSuite struct {
    31  	testing.JujuConnSuite
    32  	toolsDir string
    33  }
    34  
    35  var _ = gc.Suite(&UpgradeJujuSuite{})
    36  
    37  var upgradeJujuTests = []struct {
    38  	about          string
    39  	tools          []string
    40  	currentVersion string
    41  	agentVersion   string
    42  
    43  	args           []string
    44  	expectInitErr  string
    45  	expectErr      string
    46  	expectVersion  string
    47  	expectUploaded []string
    48  }{{
    49  	about:          "unwanted extra argument",
    50  	currentVersion: "1.0.0-quantal-amd64",
    51  	args:           []string{"foo"},
    52  	expectInitErr:  "unrecognized args:.*",
    53  }, {
    54  	about:          "removed arg --dev specified",
    55  	currentVersion: "1.0.0-quantal-amd64",
    56  	args:           []string{"--dev"},
    57  	expectInitErr:  "flag provided but not defined: --dev",
    58  }, {
    59  	about:          "invalid --version value",
    60  	currentVersion: "1.0.0-quantal-amd64",
    61  	args:           []string{"--version", "invalid-version"},
    62  	expectInitErr:  "invalid version .*",
    63  }, {
    64  	about:          "just major version, no minor specified",
    65  	currentVersion: "4.2.0-quantal-amd64",
    66  	args:           []string{"--version", "4"},
    67  	expectInitErr:  `invalid version "4"`,
    68  }, {
    69  	about:          "major version upgrade to incompatible version",
    70  	currentVersion: "2.0.0-quantal-amd64",
    71  	args:           []string{"--version", "5.2.0"},
    72  	expectInitErr:  "cannot upgrade to version incompatible with CLI",
    73  }, {
    74  	about:          "major version downgrade to incompatible version",
    75  	currentVersion: "4.2.0-quantal-amd64",
    76  	args:           []string{"--version", "3.2.0"},
    77  	expectInitErr:  "cannot upgrade to version incompatible with CLI",
    78  }, {
    79  	about:          "invalid --series",
    80  	currentVersion: "4.2.0-quantal-amd64",
    81  	args:           []string{"--series", "precise&quantal"},
    82  	expectInitErr:  `invalid value "precise&quantal" for flag --series: .*`,
    83  }, {
    84  	about:          "--series without --upload-tools",
    85  	currentVersion: "4.2.0-quantal-amd64",
    86  	args:           []string{"--series", "precise,quantal"},
    87  	expectInitErr:  "--series requires --upload-tools",
    88  }, {
    89  	about:          "--upload-tools with inappropriate version 1",
    90  	currentVersion: "4.2.0-quantal-amd64",
    91  	args:           []string{"--upload-tools", "--version", "3.1.0"},
    92  	expectInitErr:  "cannot upgrade to version incompatible with CLI",
    93  }, {
    94  	about:          "--upload-tools with inappropriate version 2",
    95  	currentVersion: "3.2.7-quantal-amd64",
    96  	args:           []string{"--upload-tools", "--version", "3.2.8.4"},
    97  	expectInitErr:  "cannot specify build number when uploading tools",
    98  }, {
    99  	about:          "latest supported stable release",
   100  	tools:          []string{"2.2.0-quantal-amd64", "2.2.2-quantal-i386", "2.2.3-quantal-amd64"},
   101  	currentVersion: "2.0.0-quantal-amd64",
   102  	agentVersion:   "2.0.0",
   103  	expectVersion:  "2.2.3",
   104  }, {
   105  	about:          "latest current release",
   106  	tools:          []string{"2.0.5-quantal-amd64", "2.0.1-quantal-i386", "2.3.3-quantal-amd64"},
   107  	currentVersion: "2.0.0-quantal-amd64",
   108  	agentVersion:   "2.0.0",
   109  	expectVersion:  "2.0.5",
   110  }, {
   111  	about:          "latest current release matching CLI, major version",
   112  	tools:          []string{"3.2.0-quantal-amd64"},
   113  	currentVersion: "3.2.0-quantal-amd64",
   114  	agentVersion:   "2.8.2",
   115  	expectVersion:  "3.2.0",
   116  }, {
   117  	about:          "latest current release matching CLI, major version, no matching major tools",
   118  	tools:          []string{"2.8.2-quantal-amd64"},
   119  	currentVersion: "3.2.0-quantal-amd64",
   120  	agentVersion:   "2.8.2",
   121  	expectErr:      "no matching tools available",
   122  }, {
   123  	about:          "latest current release matching CLI, major version, no matching tools",
   124  	tools:          []string{"3.3.0-quantal-amd64"},
   125  	currentVersion: "3.2.0-quantal-amd64",
   126  	agentVersion:   "2.8.2",
   127  	expectErr:      "no compatible tools available",
   128  }, {
   129  	about:          "no next supported available",
   130  	tools:          []string{"2.1.0-quantal-amd64", "2.1.5-quantal-i386", "2.3.3-quantal-amd64"},
   131  	currentVersion: "2.0.0-quantal-amd64",
   132  	agentVersion:   "2.0.0",
   133  	expectErr:      "no more recent supported versions available",
   134  }, {
   135  	about:          "latest supported stable, when client is dev",
   136  	tools:          []string{"2.1-dev1-quantal-amd64", "2.2.0-quantal-amd64", "2.3-dev0-quantal-amd64", "3.0.1-quantal-amd64"},
   137  	currentVersion: "2.1-dev0-quantal-amd64",
   138  	agentVersion:   "2.0.0",
   139  	expectVersion:  "2.2.0",
   140  }, {
   141  	about:          "latest current, when agent is dev",
   142  	tools:          []string{"2.1-dev1-quantal-amd64", "2.2.0-quantal-amd64", "2.3-dev0-quantal-amd64", "3.0.1-quantal-amd64"},
   143  	currentVersion: "2.0.0-quantal-amd64",
   144  	agentVersion:   "2.1-dev0",
   145  	expectVersion:  "2.2.0",
   146  }, {
   147  	about:          "specified version",
   148  	tools:          []string{"2.3-dev0-quantal-amd64"},
   149  	currentVersion: "2.0.0-quantal-amd64",
   150  	agentVersion:   "2.0.0",
   151  	args:           []string{"--version", "2.3-dev0"},
   152  	expectVersion:  "2.3-dev0",
   153  }, {
   154  	about:          "specified major version",
   155  	tools:          []string{"3.2.0-quantal-amd64"},
   156  	currentVersion: "3.2.0-quantal-amd64",
   157  	agentVersion:   "2.8.2",
   158  	args:           []string{"--version", "3.2.0"},
   159  	expectVersion:  "3.2.0",
   160  }, {
   161  	about:          "specified version missing, but already set",
   162  	currentVersion: "3.0.0-quantal-amd64",
   163  	agentVersion:   "3.0.0",
   164  	args:           []string{"--version", "3.0.0"},
   165  	expectVersion:  "3.0.0",
   166  }, {
   167  	about:          "specified version, no tools",
   168  	currentVersion: "3.0.0-quantal-amd64",
   169  	agentVersion:   "3.0.0",
   170  	args:           []string{"--version", "3.2.0"},
   171  	expectErr:      "no tools available",
   172  }, {
   173  	about:          "specified version, no matching major version",
   174  	tools:          []string{"4.2.0-quantal-amd64"},
   175  	currentVersion: "3.0.0-quantal-amd64",
   176  	agentVersion:   "3.0.0",
   177  	args:           []string{"--version", "3.2.0"},
   178  	expectErr:      "no matching tools available",
   179  }, {
   180  	about:          "specified version, no matching minor version",
   181  	tools:          []string{"3.4.0-quantal-amd64"},
   182  	currentVersion: "3.0.0-quantal-amd64",
   183  	agentVersion:   "3.0.0",
   184  	args:           []string{"--version", "3.2.0"},
   185  	expectErr:      "no matching tools available",
   186  }, {
   187  	about:          "specified version, no matching patch version",
   188  	tools:          []string{"3.2.5-quantal-amd64"},
   189  	currentVersion: "3.0.0-quantal-amd64",
   190  	agentVersion:   "3.0.0",
   191  	args:           []string{"--version", "3.2.0"},
   192  	expectErr:      "no matching tools available",
   193  }, {
   194  	about:          "specified version, no matching build version",
   195  	tools:          []string{"3.2.0.2-quantal-amd64"},
   196  	currentVersion: "3.0.0-quantal-amd64",
   197  	agentVersion:   "3.0.0",
   198  	args:           []string{"--version", "3.2.0"},
   199  	expectErr:      "no matching tools available",
   200  }, {
   201  	about:          "major version downgrade to incompatible version",
   202  	tools:          []string{"3.2.0-quantal-amd64"},
   203  	currentVersion: "3.2.0-quantal-amd64",
   204  	agentVersion:   "4.2.0",
   205  	args:           []string{"--version", "3.2.0"},
   206  	expectErr:      "cannot change version from 4.2.0 to 3.2.0",
   207  }, {
   208  	about:          "minor version downgrade to incompatible version",
   209  	tools:          []string{"3.2.0-quantal-amd64"},
   210  	currentVersion: "3.2.0-quantal-amd64",
   211  	agentVersion:   "3.3-dev0",
   212  	args:           []string{"--version", "3.2.0"},
   213  	expectErr:      "cannot change version from 3.3-dev0 to 3.2.0",
   214  }, {
   215  	about:          "nothing available",
   216  	currentVersion: "2.0.0-quantal-amd64",
   217  	agentVersion:   "2.0.0",
   218  	expectVersion:  "2.0.0",
   219  }, {
   220  	about:          "nothing available 2",
   221  	currentVersion: "2.0.0-quantal-amd64",
   222  	tools:          []string{"3.2.0-quantal-amd64"},
   223  	agentVersion:   "2.0.0",
   224  	expectVersion:  "2.0.0",
   225  }, {
   226  	about:          "upload with default series",
   227  	currentVersion: "2.2.0-quantal-amd64",
   228  	agentVersion:   "2.0.0",
   229  	args:           []string{"--upload-tools"},
   230  	expectVersion:  "2.2.0.1",
   231  	expectUploaded: []string{"2.2.0.1-quantal-amd64", "2.2.0.1-%LTS%-amd64", "2.2.0.1-raring-amd64"},
   232  }, {
   233  	about:          "upload with explicit version",
   234  	currentVersion: "2.2.0-quantal-amd64",
   235  	agentVersion:   "2.0.0",
   236  	args:           []string{"--upload-tools", "--version", "2.7.3"},
   237  	expectVersion:  "2.7.3.1",
   238  	expectUploaded: []string{"2.7.3.1-quantal-amd64", "2.7.3.1-%LTS%-amd64", "2.7.3.1-raring-amd64"},
   239  }, {
   240  	about:          "upload with explicit series",
   241  	currentVersion: "2.2.0-quantal-amd64",
   242  	agentVersion:   "2.0.0",
   243  	args:           []string{"--upload-tools", "--series", "raring"},
   244  	expectVersion:  "2.2.0.1",
   245  	expectUploaded: []string{"2.2.0.1-quantal-amd64", "2.2.0.1-raring-amd64"},
   246  }, {
   247  	about:          "upload dev version, currently on release version",
   248  	currentVersion: "2.1.0-quantal-amd64",
   249  	agentVersion:   "2.0.0",
   250  	args:           []string{"--upload-tools"},
   251  	expectVersion:  "2.1.0.1",
   252  	expectUploaded: []string{"2.1.0.1-quantal-amd64", "2.1.0.1-%LTS%-amd64", "2.1.0.1-raring-amd64"},
   253  }, {
   254  	about:          "upload bumps version when necessary",
   255  	tools:          []string{"2.4.6-quantal-amd64", "2.4.8-quantal-amd64"},
   256  	currentVersion: "2.4.6-quantal-amd64",
   257  	agentVersion:   "2.4.0",
   258  	args:           []string{"--upload-tools"},
   259  	expectVersion:  "2.4.6.1",
   260  	expectUploaded: []string{"2.4.6.1-quantal-amd64", "2.4.6.1-%LTS%-amd64", "2.4.6.1-raring-amd64"},
   261  }, {
   262  	about:          "upload re-bumps version when necessary",
   263  	tools:          []string{"2.4.6-quantal-amd64", "2.4.6.2-saucy-i386", "2.4.8-quantal-amd64"},
   264  	currentVersion: "2.4.6-quantal-amd64",
   265  	agentVersion:   "2.4.6.2",
   266  	args:           []string{"--upload-tools"},
   267  	expectVersion:  "2.4.6.3",
   268  	expectUploaded: []string{"2.4.6.3-quantal-amd64", "2.4.6.3-%LTS%-amd64", "2.4.6.3-raring-amd64"},
   269  }, {
   270  	about:          "upload with explicit version bumps when necessary",
   271  	currentVersion: "2.2.0-quantal-amd64",
   272  	tools:          []string{"2.7.3.1-quantal-amd64"},
   273  	agentVersion:   "2.0.0",
   274  	args:           []string{"--upload-tools", "--version", "2.7.3"},
   275  	expectVersion:  "2.7.3.2",
   276  	expectUploaded: []string{"2.7.3.2-quantal-amd64", "2.7.3.2-%LTS%-amd64", "2.7.3.2-raring-amd64"},
   277  }}
   278  
   279  func (s *UpgradeJujuSuite) TestUpgradeJuju(c *gc.C) {
   280  	oldVersion := version.Current
   281  	defer func() {
   282  		version.Current = oldVersion
   283  	}()
   284  
   285  	for i, test := range upgradeJujuTests {
   286  		c.Logf("\ntest %d: %s", i, test.about)
   287  		s.Reset(c)
   288  
   289  		// Set up apparent CLI version and initialize the command.
   290  		version.Current = version.MustParseBinary(test.currentVersion)
   291  		com := &UpgradeJujuCommand{}
   292  		if err := coretesting.InitCommand(envcmd.Wrap(com), test.args); err != nil {
   293  			if test.expectInitErr != "" {
   294  				c.Check(err, gc.ErrorMatches, test.expectInitErr)
   295  			} else {
   296  				c.Check(err, gc.IsNil)
   297  			}
   298  			continue
   299  		}
   300  
   301  		// Set up state and environ, and run the command.
   302  		toolsDir := c.MkDir()
   303  		updateAttrs := map[string]interface{}{
   304  			"agent-version":      test.agentVersion,
   305  			"tools-metadata-url": "file://" + toolsDir,
   306  		}
   307  		err := s.State.UpdateEnvironConfig(updateAttrs, nil, nil)
   308  		c.Assert(err, gc.IsNil)
   309  		versions := make([]version.Binary, len(test.tools))
   310  		for i, v := range test.tools {
   311  			versions[i] = version.MustParseBinary(v)
   312  		}
   313  		if len(versions) > 0 {
   314  			envtesting.MustUploadFakeToolsVersions(s.Conn.Environ.Storage(), versions...)
   315  			stor, err := filestorage.NewFileStorageWriter(toolsDir)
   316  			c.Assert(err, gc.IsNil)
   317  			envtesting.MustUploadFakeToolsVersions(stor, versions...)
   318  		}
   319  
   320  		err = com.Run(coretesting.Context(c))
   321  		if test.expectErr != "" {
   322  			c.Check(err, gc.ErrorMatches, test.expectErr)
   323  			continue
   324  		} else if !c.Check(err, gc.IsNil) {
   325  			continue
   326  		}
   327  
   328  		// Check expected changes to environ/state.
   329  		cfg, err := s.State.EnvironConfig()
   330  		c.Check(err, gc.IsNil)
   331  		agentVersion, ok := cfg.AgentVersion()
   332  		c.Check(ok, gc.Equals, true)
   333  		c.Check(agentVersion, gc.Equals, version.MustParse(test.expectVersion))
   334  
   335  		for _, uploaded := range test.expectUploaded {
   336  			// Substitute latest LTS for placeholder in expected series for uploaded tools
   337  			uploaded = strings.Replace(uploaded, "%LTS%", config.LatestLtsSeries(), 1)
   338  
   339  			vers := version.MustParseBinary(uploaded)
   340  			r, err := storage.Get(s.Conn.Environ.Storage(), envtools.StorageName(vers))
   341  			if !c.Check(err, gc.IsNil) {
   342  				continue
   343  			}
   344  			data, err := ioutil.ReadAll(r)
   345  			r.Close()
   346  			c.Check(err, gc.IsNil)
   347  			expectContent := version.Current
   348  			expectContent.Number = agentVersion
   349  			checkToolsContent(c, data, "jujud contents "+expectContent.String())
   350  		}
   351  	}
   352  }
   353  
   354  func checkToolsContent(c *gc.C, data []byte, uploaded string) {
   355  	zr, err := gzip.NewReader(bytes.NewReader(data))
   356  	c.Check(err, gc.IsNil)
   357  	defer zr.Close()
   358  	tr := tar.NewReader(zr)
   359  	found := false
   360  	for {
   361  		hdr, err := tr.Next()
   362  		if err == io.EOF {
   363  			break
   364  		}
   365  		c.Check(err, gc.IsNil)
   366  		if strings.ContainsAny(hdr.Name, "/\\") {
   367  			c.Fail()
   368  		}
   369  		if hdr.Typeflag != tar.TypeReg {
   370  			c.Fail()
   371  		}
   372  		content, err := ioutil.ReadAll(tr)
   373  		c.Check(err, gc.IsNil)
   374  		c.Check(string(content), gc.Equals, uploaded)
   375  		found = true
   376  	}
   377  	c.Check(found, jc.IsTrue)
   378  }
   379  
   380  // JujuConnSuite very helpfully uploads some default
   381  // tools to the environment's storage. We don't want
   382  // 'em there; but we do want a consistent default-series
   383  // in the environment state.
   384  func (s *UpgradeJujuSuite) Reset(c *gc.C) {
   385  	s.JujuConnSuite.Reset(c)
   386  	envtesting.RemoveTools(c, s.Conn.Environ.Storage())
   387  	updateAttrs := map[string]interface{}{
   388  		"default-series": "raring",
   389  		"agent-version":  "1.2.3",
   390  	}
   391  	err := s.State.UpdateEnvironConfig(updateAttrs, nil, nil)
   392  	c.Assert(err, gc.IsNil)
   393  	s.PatchValue(&sync.BuildToolsTarball, toolstesting.GetMockBuildTools(c))
   394  }
   395  
   396  func (s *UpgradeJujuSuite) TestUpgradeJujuWithRealUpload(c *gc.C) {
   397  	s.Reset(c)
   398  	_, err := coretesting.RunCommand(c, &UpgradeJujuCommand{}, "--upload-tools")
   399  	c.Assert(err, gc.IsNil)
   400  	vers := version.Current
   401  	vers.Build = 1
   402  	tools, err := envtools.FindInstanceTools(s.Conn.Environ, vers.Number, vers.Series, &vers.Arch)
   403  	c.Assert(err, gc.IsNil)
   404  	c.Assert(len(tools), gc.Equals, 1)
   405  }