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 }