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