github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/cmd/juju/commands/upgradecharm_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package commands
     5  
     6  import (
     7  	"bytes"
     8  	"io/ioutil"
     9  	"os"
    10  	"path"
    11  
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  	"gopkg.in/juju/charm.v5"
    15  	"gopkg.in/juju/charm.v5/charmrepo"
    16  	"gopkg.in/juju/charmstore.v4"
    17  	"gopkg.in/juju/charmstore.v4/charmstoretesting"
    18  
    19  	"github.com/juju/juju/cmd/envcmd"
    20  	jujutesting "github.com/juju/juju/juju/testing"
    21  	"github.com/juju/juju/state"
    22  	"github.com/juju/juju/testcharms"
    23  	"github.com/juju/juju/testing"
    24  )
    25  
    26  type UpgradeCharmErrorsSuite struct {
    27  	jujutesting.RepoSuite
    28  	srv *charmstoretesting.Server
    29  }
    30  
    31  func (s *UpgradeCharmErrorsSuite) SetUpTest(c *gc.C) {
    32  	s.RepoSuite.SetUpTest(c)
    33  	s.srv = charmstoretesting.OpenServer(c, s.Session, charmstore.ServerParams{})
    34  	s.PatchValue(&charmrepo.CacheDir, c.MkDir())
    35  	original := newCharmStoreClient
    36  	s.PatchValue(&newCharmStoreClient, func() (*csClient, error) {
    37  		csclient, err := original()
    38  		c.Assert(err, jc.ErrorIsNil)
    39  		csclient.params.URL = s.srv.URL()
    40  		return csclient, nil
    41  	})
    42  }
    43  
    44  func (s *UpgradeCharmErrorsSuite) TearDownTest(c *gc.C) {
    45  	s.srv.Close()
    46  	s.RepoSuite.TearDownTest(c)
    47  }
    48  
    49  var _ = gc.Suite(&UpgradeCharmErrorsSuite{})
    50  
    51  func runUpgradeCharm(c *gc.C, args ...string) error {
    52  	_, err := testing.RunCommand(c, envcmd.Wrap(&UpgradeCharmCommand{}), args...)
    53  	return err
    54  }
    55  
    56  func (s *UpgradeCharmErrorsSuite) TestInvalidArgs(c *gc.C) {
    57  	err := runUpgradeCharm(c)
    58  	c.Assert(err, gc.ErrorMatches, "no service specified")
    59  	err = runUpgradeCharm(c, "invalid:name")
    60  	c.Assert(err, gc.ErrorMatches, `invalid service name "invalid:name"`)
    61  	err = runUpgradeCharm(c, "foo", "bar")
    62  	c.Assert(err, gc.ErrorMatches, `unrecognized args: \["bar"\]`)
    63  }
    64  
    65  func (s *UpgradeCharmErrorsSuite) TestWithInvalidRepository(c *gc.C) {
    66  	testcharms.Repo.ClonedDirPath(s.SeriesPath, "riak")
    67  	err := runDeploy(c, "local:riak", "riak")
    68  	c.Assert(err, jc.ErrorIsNil)
    69  
    70  	err = runUpgradeCharm(c, "riak", "--repository=blah")
    71  	c.Assert(err, gc.ErrorMatches, `no repository found at ".*blah"`)
    72  	// Reset JUJU_REPOSITORY explicitly, because repoSuite.SetUpTest
    73  	// overwrites it (TearDownTest will revert it again).
    74  	os.Setenv("JUJU_REPOSITORY", "")
    75  	err = runUpgradeCharm(c, "riak", "--repository=")
    76  	c.Assert(err, gc.ErrorMatches, `charm not found in ".*": local:trusty/riak`)
    77  }
    78  
    79  func (s *UpgradeCharmErrorsSuite) TestInvalidService(c *gc.C) {
    80  	err := runUpgradeCharm(c, "phony")
    81  	c.Assert(err, gc.ErrorMatches, `service "phony" not found`)
    82  }
    83  
    84  func (s *UpgradeCharmErrorsSuite) deployService(c *gc.C) {
    85  	testcharms.Repo.ClonedDirPath(s.SeriesPath, "riak")
    86  	err := runDeploy(c, "local:riak", "riak")
    87  	c.Assert(err, jc.ErrorIsNil)
    88  }
    89  
    90  func (s *UpgradeCharmErrorsSuite) TestInvalidSwitchURL(c *gc.C) {
    91  	s.deployService(c)
    92  	err := runUpgradeCharm(c, "riak", "--switch=blah")
    93  	c.Assert(err, gc.ErrorMatches, `cannot resolve charm URL "cs:trusty/blah": charm not found`)
    94  	err = runUpgradeCharm(c, "riak", "--switch=cs:missing/one")
    95  	c.Assert(err, gc.ErrorMatches, `cannot resolve charm URL "cs:missing/one": charm not found`)
    96  	// TODO(dimitern): add tests with incompatible charms
    97  }
    98  
    99  func (s *UpgradeCharmErrorsSuite) TestSwitchAndRevisionFails(c *gc.C) {
   100  	s.deployService(c)
   101  	err := runUpgradeCharm(c, "riak", "--switch=riak", "--revision=2")
   102  	c.Assert(err, gc.ErrorMatches, "--switch and --revision are mutually exclusive")
   103  }
   104  
   105  func (s *UpgradeCharmErrorsSuite) TestInvalidRevision(c *gc.C) {
   106  	s.deployService(c)
   107  	err := runUpgradeCharm(c, "riak", "--revision=blah")
   108  	c.Assert(err, gc.ErrorMatches, `invalid value "blah" for flag --revision: strconv.ParseInt: parsing "blah": invalid syntax`)
   109  }
   110  
   111  type UpgradeCharmSuccessSuite struct {
   112  	jujutesting.RepoSuite
   113  	CmdBlockHelper
   114  	path string
   115  	riak *state.Service
   116  }
   117  
   118  var _ = gc.Suite(&UpgradeCharmSuccessSuite{})
   119  
   120  func (s *UpgradeCharmSuccessSuite) SetUpTest(c *gc.C) {
   121  	s.RepoSuite.SetUpTest(c)
   122  	s.path = testcharms.Repo.ClonedDirPath(s.SeriesPath, "riak")
   123  	err := runDeploy(c, "local:riak", "riak")
   124  	c.Assert(err, jc.ErrorIsNil)
   125  	s.riak, err = s.State.Service("riak")
   126  	c.Assert(err, jc.ErrorIsNil)
   127  	ch, forced, err := s.riak.Charm()
   128  	c.Assert(err, jc.ErrorIsNil)
   129  	c.Assert(ch.Revision(), gc.Equals, 7)
   130  	c.Assert(forced, jc.IsFalse)
   131  
   132  	s.CmdBlockHelper = NewCmdBlockHelper(s.APIState)
   133  	c.Assert(s.CmdBlockHelper, gc.NotNil)
   134  	s.AddCleanup(func(*gc.C) { s.CmdBlockHelper.Close() })
   135  }
   136  
   137  func (s *UpgradeCharmSuccessSuite) assertUpgraded(c *gc.C, revision int, forced bool) *charm.URL {
   138  	err := s.riak.Refresh()
   139  	c.Assert(err, jc.ErrorIsNil)
   140  	ch, force, err := s.riak.Charm()
   141  	c.Assert(err, jc.ErrorIsNil)
   142  	c.Assert(ch.Revision(), gc.Equals, revision)
   143  	c.Assert(force, gc.Equals, forced)
   144  	s.AssertCharmUploaded(c, ch.URL())
   145  	return ch.URL()
   146  }
   147  
   148  func (s *UpgradeCharmSuccessSuite) assertLocalRevision(c *gc.C, revision int, path string) {
   149  	dir, err := charm.ReadCharmDir(path)
   150  	c.Assert(err, jc.ErrorIsNil)
   151  	c.Assert(dir.Revision(), gc.Equals, revision)
   152  }
   153  
   154  func (s *UpgradeCharmSuccessSuite) TestLocalRevisionUnchanged(c *gc.C) {
   155  	err := runUpgradeCharm(c, "riak")
   156  	c.Assert(err, jc.ErrorIsNil)
   157  	s.assertUpgraded(c, 8, false)
   158  	// Even though the remote revision is bumped, the local one should
   159  	// be unchanged.
   160  	s.assertLocalRevision(c, 7, s.path)
   161  }
   162  
   163  func (s *UpgradeCharmSuccessSuite) TestBlockUpgradeCharm(c *gc.C) {
   164  	// Block operation
   165  	s.BlockAllChanges(c, "TestBlockUpgradeCharm")
   166  	err := runUpgradeCharm(c, "riak")
   167  	s.AssertBlocked(c, err, ".*TestBlockUpgradeCharm.*")
   168  }
   169  
   170  func (s *UpgradeCharmSuccessSuite) TestRespectsLocalRevisionWhenPossible(c *gc.C) {
   171  	dir, err := charm.ReadCharmDir(s.path)
   172  	c.Assert(err, jc.ErrorIsNil)
   173  	err = dir.SetDiskRevision(42)
   174  	c.Assert(err, jc.ErrorIsNil)
   175  
   176  	err = runUpgradeCharm(c, "riak")
   177  	c.Assert(err, jc.ErrorIsNil)
   178  	s.assertUpgraded(c, 42, false)
   179  	s.assertLocalRevision(c, 42, s.path)
   180  }
   181  
   182  func (s *UpgradeCharmSuccessSuite) TestUpgradesWithBundle(c *gc.C) {
   183  	dir, err := charm.ReadCharmDir(s.path)
   184  	c.Assert(err, jc.ErrorIsNil)
   185  	dir.SetRevision(42)
   186  	buf := &bytes.Buffer{}
   187  	err = dir.ArchiveTo(buf)
   188  	c.Assert(err, jc.ErrorIsNil)
   189  	bundlePath := path.Join(s.SeriesPath, "riak.charm")
   190  	err = ioutil.WriteFile(bundlePath, buf.Bytes(), 0644)
   191  	c.Assert(err, jc.ErrorIsNil)
   192  
   193  	err = runUpgradeCharm(c, "riak")
   194  	c.Assert(err, jc.ErrorIsNil)
   195  	s.assertUpgraded(c, 42, false)
   196  	s.assertLocalRevision(c, 7, s.path)
   197  }
   198  
   199  func (s *UpgradeCharmSuccessSuite) TestBlockUpgradesWithBundle(c *gc.C) {
   200  	dir, err := charm.ReadCharmDir(s.path)
   201  	c.Assert(err, jc.ErrorIsNil)
   202  	dir.SetRevision(42)
   203  	buf := &bytes.Buffer{}
   204  	err = dir.ArchiveTo(buf)
   205  	c.Assert(err, jc.ErrorIsNil)
   206  	bundlePath := path.Join(s.SeriesPath, "riak.charm")
   207  	err = ioutil.WriteFile(bundlePath, buf.Bytes(), 0644)
   208  	c.Assert(err, jc.ErrorIsNil)
   209  
   210  	// Block operation
   211  	s.BlockAllChanges(c, "TestBlockUpgradesWithBundle")
   212  	err = runUpgradeCharm(c, "riak")
   213  	s.AssertBlocked(c, err, ".*TestBlockUpgradesWithBundle.*")
   214  }
   215  
   216  func (s *UpgradeCharmSuccessSuite) TestForcedUpgrade(c *gc.C) {
   217  	err := runUpgradeCharm(c, "riak", "--force")
   218  	c.Assert(err, jc.ErrorIsNil)
   219  	s.assertUpgraded(c, 8, true)
   220  	// Local revision is not changed.
   221  	s.assertLocalRevision(c, 7, s.path)
   222  }
   223  
   224  func (s *UpgradeCharmSuccessSuite) TestBlockForcedUpgrade(c *gc.C) {
   225  	// Block operation
   226  	s.BlockAllChanges(c, "TestBlockForcedUpgrade")
   227  	err := runUpgradeCharm(c, "riak", "--force")
   228  	c.Assert(err, jc.ErrorIsNil)
   229  	s.assertUpgraded(c, 8, true)
   230  	// Local revision is not changed.
   231  	s.assertLocalRevision(c, 7, s.path)
   232  }
   233  
   234  var myriakMeta = []byte(`
   235  name: myriak
   236  summary: "K/V storage engine"
   237  description: "Scalable K/V Store in Erlang with Clocks :-)"
   238  provides:
   239    endpoint:
   240      interface: http
   241    admin:
   242      interface: http
   243  peers:
   244    ring:
   245      interface: riak
   246  `)
   247  
   248  func (s *UpgradeCharmSuccessSuite) TestSwitch(c *gc.C) {
   249  	myriakPath := testcharms.Repo.RenamedClonedDirPath(s.SeriesPath, "riak", "myriak")
   250  	err := ioutil.WriteFile(path.Join(myriakPath, "metadata.yaml"), myriakMeta, 0644)
   251  	c.Assert(err, jc.ErrorIsNil)
   252  
   253  	// Test with local repo and no explicit revsion.
   254  	err = runUpgradeCharm(c, "riak", "--switch=local:myriak")
   255  	c.Assert(err, jc.ErrorIsNil)
   256  	curl := s.assertUpgraded(c, 7, false)
   257  	c.Assert(curl.String(), gc.Equals, "local:trusty/myriak-7")
   258  	s.assertLocalRevision(c, 7, myriakPath)
   259  
   260  	// Now try the same with explicit revision - should fail.
   261  	err = runUpgradeCharm(c, "riak", "--switch=local:myriak-7")
   262  	c.Assert(err, gc.ErrorMatches, `already running specified charm "local:trusty/myriak-7"`)
   263  
   264  	// Change the revision to 42 and upgrade to it with explicit revision.
   265  	err = ioutil.WriteFile(path.Join(myriakPath, "revision"), []byte("42"), 0644)
   266  	c.Assert(err, jc.ErrorIsNil)
   267  	err = runUpgradeCharm(c, "riak", "--switch=local:myriak-42")
   268  	c.Assert(err, jc.ErrorIsNil)
   269  	curl = s.assertUpgraded(c, 42, false)
   270  	c.Assert(curl.String(), gc.Equals, "local:trusty/myriak-42")
   271  	s.assertLocalRevision(c, 42, myriakPath)
   272  }
   273  
   274  type UpgradeCharmCharmStoreSuite struct {
   275  	charmStoreSuite
   276  }
   277  
   278  var _ = gc.Suite(&UpgradeCharmCharmStoreSuite{})
   279  
   280  var upgradeCharmAuthorizationTests = []struct {
   281  	about        string
   282  	uploadURL    string
   283  	switchURL    string
   284  	readPermUser string
   285  	expectError  string
   286  }{{
   287  	about:     "public charm, success",
   288  	uploadURL: "cs:~bob/trusty/wordpress1-10",
   289  	switchURL: "cs:~bob/trusty/wordpress1",
   290  }, {
   291  	about:     "public charm, fully resolved, success",
   292  	uploadURL: "cs:~bob/trusty/wordpress2-10",
   293  	switchURL: "cs:~bob/trusty/wordpress2-10",
   294  }, {
   295  	about:        "non-public charm, success",
   296  	uploadURL:    "cs:~bob/trusty/wordpress3-10",
   297  	switchURL:    "cs:~bob/trusty/wordpress3",
   298  	readPermUser: clientUserName,
   299  }, {
   300  	about:        "non-public charm, fully resolved, success",
   301  	uploadURL:    "cs:~bob/trusty/wordpress4-10",
   302  	switchURL:    "cs:~bob/trusty/wordpress4-10",
   303  	readPermUser: clientUserName,
   304  }, {
   305  	about:        "non-public charm, access denied",
   306  	uploadURL:    "cs:~bob/trusty/wordpress5-10",
   307  	switchURL:    "cs:~bob/trusty/wordpress5",
   308  	readPermUser: "bob",
   309  	expectError:  `cannot resolve charm URL "cs:~bob/trusty/wordpress5": cannot get "/~bob/trusty/wordpress5/meta/any\?include=id": unauthorized: access denied for user "client-username"`,
   310  }, {
   311  	about:        "non-public charm, fully resolved, access denied",
   312  	uploadURL:    "cs:~bob/trusty/wordpress6-47",
   313  	switchURL:    "cs:~bob/trusty/wordpress6-47",
   314  	readPermUser: "bob",
   315  	expectError:  `cannot retrieve charm "cs:~bob/trusty/wordpress6-47": cannot get archive: unauthorized: access denied for user "client-username"`,
   316  }}
   317  
   318  func (s *UpgradeCharmCharmStoreSuite) TestUpgradeCharmAuthorization(c *gc.C) {
   319  	s.uploadCharm(c, "cs:~other/trusty/wordpress-0", "wordpress")
   320  	err := runDeploy(c, "cs:~other/trusty/wordpress-0")
   321  	c.Assert(err, jc.ErrorIsNil)
   322  	for i, test := range upgradeCharmAuthorizationTests {
   323  		c.Logf("test %d: %s", i, test.about)
   324  		url, _ := s.uploadCharm(c, test.uploadURL, "wordpress")
   325  		if test.readPermUser != "" {
   326  			s.changeReadPerm(c, url, test.readPermUser)
   327  		}
   328  		err := runUpgradeCharm(c, "wordpress", "--switch", test.switchURL)
   329  		if test.expectError != "" {
   330  			c.Assert(err, gc.ErrorMatches, test.expectError)
   331  			continue
   332  		}
   333  		c.Assert(err, jc.ErrorIsNil)
   334  	}
   335  }