github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/cmd/juju/application/upgradecharm_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package application 5 6 import ( 7 "fmt" 8 "io/ioutil" 9 "net/http/httptest" 10 "os" 11 "path" 12 "path/filepath" 13 14 "github.com/juju/cmd" 15 "github.com/juju/errors" 16 "github.com/juju/testing" 17 jc "github.com/juju/testing/checkers" 18 "github.com/juju/version" 19 gc "gopkg.in/check.v1" 20 "gopkg.in/juju/charm.v6-unstable" 21 charmresource "gopkg.in/juju/charm.v6-unstable/resource" 22 "gopkg.in/juju/charmrepo.v2-unstable" 23 "gopkg.in/juju/charmrepo.v2-unstable/csclient" 24 csclientparams "gopkg.in/juju/charmrepo.v2-unstable/csclient/params" 25 charmstore "gopkg.in/juju/charmstore.v5-unstable" 26 "gopkg.in/macaroon-bakery.v1/httpbakery" 27 macaroon "gopkg.in/macaroon.v1" 28 29 "strings" 30 31 "github.com/juju/juju/api" 32 "github.com/juju/juju/api/application" 33 "github.com/juju/juju/api/base" 34 "github.com/juju/juju/api/charms" 35 jujucharmstore "github.com/juju/juju/charmstore" 36 "github.com/juju/juju/cmd/modelcmd" 37 "github.com/juju/juju/environs/config" 38 jujutesting "github.com/juju/juju/juju/testing" 39 "github.com/juju/juju/jujuclient" 40 "github.com/juju/juju/jujuclient/jujuclienttesting" 41 "github.com/juju/juju/resource/resourceadapters" 42 "github.com/juju/juju/rpc" 43 "github.com/juju/juju/state" 44 "github.com/juju/juju/storage" 45 "github.com/juju/juju/testcharms" 46 coretesting "github.com/juju/juju/testing" 47 ) 48 49 type UpgradeCharmSuite struct { 50 testing.IsolationSuite 51 testing.Stub 52 53 deployResources resourceadapters.DeployResourcesFunc 54 resolveCharm ResolveCharmFunc 55 resolvedCharmURL *charm.URL 56 apiConnection mockAPIConnection 57 charmAdder mockCharmAdder 58 charmClient mockCharmClient 59 charmUpgradeClient mockCharmUpgradeClient 60 modelConfigGetter mockModelConfigGetter 61 resourceLister mockResourceLister 62 cmd cmd.Command 63 } 64 65 var _ = gc.Suite(&UpgradeCharmSuite{}) 66 67 func (s *UpgradeCharmSuite) SetUpTest(c *gc.C) { 68 s.IsolationSuite.SetUpTest(c) 69 s.Stub.ResetCalls() 70 71 // Create persistent cookies in a temporary location. 72 cookieFile := filepath.Join(c.MkDir(), "cookies") 73 s.PatchEnvironment("JUJU_COOKIEFILE", cookieFile) 74 75 s.deployResources = func( 76 applicationID string, 77 chID jujucharmstore.CharmID, 78 csMac *macaroon.Macaroon, 79 filesAndRevisions map[string]string, 80 resources map[string]charmresource.Meta, 81 conn base.APICallCloser, 82 ) (ids map[string]string, err error) { 83 s.AddCall("DeployResources", applicationID, chID, csMac, filesAndRevisions, resources, conn) 84 return nil, s.NextErr() 85 } 86 87 s.resolveCharm = func( 88 resolveWithChannel func(*charm.URL) (*charm.URL, csclientparams.Channel, []string, error), 89 conf *config.Config, 90 url *charm.URL, 91 ) (*charm.URL, csclientparams.Channel, []string, error) { 92 s.AddCall("ResolveCharm", resolveWithChannel, conf, url) 93 if err := s.NextErr(); err != nil { 94 return nil, csclientparams.NoChannel, nil, err 95 } 96 return s.resolvedCharmURL, csclientparams.StableChannel, []string{"quantal"}, nil 97 } 98 99 currentCharmURL := charm.MustParseURL("cs:quantal/foo-1") 100 latestCharmURL := charm.MustParseURL("cs:quantal/foo-2") 101 s.resolvedCharmURL = latestCharmURL 102 103 s.apiConnection = mockAPIConnection{ 104 bestFacadeVersion: 2, 105 serverVersion: &version.Number{ 106 Major: 1, 107 Minor: 2, 108 Patch: 3, 109 }, 110 } 111 s.charmAdder = mockCharmAdder{} 112 s.charmClient = mockCharmClient{ 113 charmInfo: &charms.CharmInfo{ 114 Meta: &charm.Meta{}, 115 }, 116 } 117 s.charmUpgradeClient = mockCharmUpgradeClient{charmURL: currentCharmURL} 118 s.modelConfigGetter = mockModelConfigGetter{} 119 s.resourceLister = mockResourceLister{} 120 121 store := jujuclienttesting.NewMemStore() 122 store.CurrentControllerName = "foo" 123 store.Controllers["foo"] = jujuclient.ControllerDetails{} 124 store.Models["foo"] = &jujuclient.ControllerModels{ 125 CurrentModel: "admin/bar", 126 Models: map[string]jujuclient.ModelDetails{"admin/bar": {}}, 127 } 128 apiOpener := modelcmd.OpenFunc(func(store jujuclient.ClientStore, controller, model string) (api.Connection, error) { 129 s.AddCall("OpenAPI", store, controller, model) 130 return &s.apiConnection, nil 131 }) 132 133 s.cmd = NewUpgradeCharmCommandForTest( 134 store, 135 apiOpener, 136 s.deployResources, 137 s.resolveCharm, 138 func(conn api.Connection, bakeryClient *httpbakery.Client, channel csclientparams.Channel) CharmAdder { 139 s.AddCall("NewCharmAdder", conn, bakeryClient, channel) 140 s.PopNoErr() 141 return &s.charmAdder 142 }, 143 func(conn api.Connection) CharmClient { 144 s.AddCall("NewCharmClient", conn) 145 s.PopNoErr() 146 return &s.charmClient 147 }, 148 func(conn api.Connection) CharmUpgradeClient { 149 s.AddCall("NewCharmUpgradeClient", conn) 150 s.PopNoErr() 151 return &s.charmUpgradeClient 152 }, 153 func(conn api.Connection) ModelConfigGetter { 154 s.AddCall("NewModelConfigGetter", conn) 155 return &s.modelConfigGetter 156 }, 157 func(conn api.Connection) (ResourceLister, error) { 158 s.AddCall("NewResourceLister", conn) 159 return &s.resourceLister, s.NextErr() 160 }, 161 ) 162 } 163 164 func (s *UpgradeCharmSuite) runUpgradeCharm(c *gc.C, args ...string) (*cmd.Context, error) { 165 return coretesting.RunCommand(c, s.cmd, args...) 166 } 167 168 func (s *UpgradeCharmSuite) TestStorageConstraints(c *gc.C) { 169 _, err := s.runUpgradeCharm(c, "foo", "--storage", "bar=baz") 170 c.Assert(err, jc.ErrorIsNil) 171 s.charmUpgradeClient.CheckCallNames(c, "GetCharmURL", "SetCharm") 172 s.charmUpgradeClient.CheckCall(c, 1, "SetCharm", application.SetCharmConfig{ 173 ApplicationName: "foo", 174 CharmID: jujucharmstore.CharmID{ 175 URL: s.resolvedCharmURL, 176 Channel: csclientparams.StableChannel, 177 }, 178 StorageConstraints: map[string]storage.Constraints{ 179 "bar": {Pool: "baz", Count: 1}, 180 }, 181 }) 182 } 183 184 func (s *UpgradeCharmSuite) TestStorageConstraintsMinFacadeVersion(c *gc.C) { 185 s.apiConnection.bestFacadeVersion = 1 186 _, err := s.runUpgradeCharm(c, "foo", "--storage", "bar=baz") 187 c.Assert(err, gc.ErrorMatches, 188 "updating storage constraints at upgrade-charm time is not supported by server version 1.2.3") 189 } 190 191 func (s *UpgradeCharmSuite) TestStorageConstraintsMinFacadeVersionNoServerVersion(c *gc.C) { 192 s.apiConnection.bestFacadeVersion = 1 193 s.apiConnection.serverVersion = nil 194 _, err := s.runUpgradeCharm(c, "foo", "--storage", "bar=baz") 195 c.Assert(err, gc.ErrorMatches, 196 "updating storage constraints at upgrade-charm time is not supported by this server") 197 } 198 199 func (s *UpgradeCharmSuite) TestConfigSettings(c *gc.C) { 200 tempdir := c.MkDir() 201 configFile := filepath.Join(tempdir, "config.yaml") 202 err := ioutil.WriteFile(configFile, []byte("foo:{}"), 0644) 203 c.Assert(err, jc.ErrorIsNil) 204 205 _, err = s.runUpgradeCharm(c, "foo", "--config", configFile) 206 c.Assert(err, jc.ErrorIsNil) 207 s.charmUpgradeClient.CheckCallNames(c, "GetCharmURL", "SetCharm") 208 s.charmUpgradeClient.CheckCall(c, 1, "SetCharm", application.SetCharmConfig{ 209 ApplicationName: "foo", 210 CharmID: jujucharmstore.CharmID{ 211 URL: s.resolvedCharmURL, 212 Channel: csclientparams.StableChannel, 213 }, 214 ConfigSettingsYAML: "foo:{}", 215 }) 216 } 217 218 func (s *UpgradeCharmSuite) TestConfigSettingsMinFacadeVersion(c *gc.C) { 219 tempdir := c.MkDir() 220 configFile := filepath.Join(tempdir, "config.yaml") 221 err := ioutil.WriteFile(configFile, []byte("foo:{}"), 0644) 222 c.Assert(err, jc.ErrorIsNil) 223 224 s.apiConnection.bestFacadeVersion = 1 225 _, err = s.runUpgradeCharm(c, "foo", "--config", configFile) 226 c.Assert(err, gc.ErrorMatches, 227 "updating config at upgrade-charm time is not supported by server version 1.2.3") 228 } 229 230 type UpgradeCharmErrorsStateSuite struct { 231 jujutesting.RepoSuite 232 handler charmstore.HTTPCloseHandler 233 srv *httptest.Server 234 } 235 236 func (s *UpgradeCharmErrorsStateSuite) SetUpTest(c *gc.C) { 237 s.RepoSuite.SetUpTest(c) 238 // Set up the charm store testing server. 239 handler, err := charmstore.NewServer(s.Session.DB("juju-testing"), nil, "", charmstore.ServerParams{ 240 AuthUsername: "test-user", 241 AuthPassword: "test-password", 242 }, charmstore.V5) 243 c.Assert(err, jc.ErrorIsNil) 244 s.handler = handler 245 s.srv = httptest.NewServer(handler) 246 s.AddCleanup(func(*gc.C) { 247 s.handler.Close() 248 s.srv.Close() 249 }) 250 251 s.PatchValue(&charmrepo.CacheDir, c.MkDir()) 252 s.PatchValue(&newCharmStoreClient, func(bakeryClient *httpbakery.Client) *csclient.Client { 253 return csclient.New(csclient.Params{ 254 URL: s.srv.URL, 255 BakeryClient: bakeryClient, 256 }) 257 }) 258 } 259 260 var _ = gc.Suite(&UpgradeCharmErrorsStateSuite{}) 261 262 func runUpgradeCharm(c *gc.C, args ...string) error { 263 _, err := coretesting.RunCommand(c, NewUpgradeCharmCommand(), args...) 264 return err 265 } 266 267 func (s *UpgradeCharmErrorsStateSuite) TestInvalidArgs(c *gc.C) { 268 err := runUpgradeCharm(c) 269 c.Assert(err, gc.ErrorMatches, "no application specified") 270 err = runUpgradeCharm(c, "invalid:name") 271 c.Assert(err, gc.ErrorMatches, `invalid application name "invalid:name"`) 272 err = runUpgradeCharm(c, "foo", "bar") 273 c.Assert(err, gc.ErrorMatches, `unrecognized args: \["bar"\]`) 274 } 275 276 func (s *UpgradeCharmErrorsStateSuite) TestInvalidService(c *gc.C) { 277 err := runUpgradeCharm(c, "phony") 278 c.Assert(errors.Cause(err), gc.DeepEquals, &rpc.RequestError{ 279 Message: `application "phony" not found`, 280 Code: "not found", 281 }) 282 } 283 284 func (s *UpgradeCharmErrorsStateSuite) deployService(c *gc.C) { 285 ch := testcharms.Repo.ClonedDirPath(s.CharmsPath, "riak") 286 err := runDeploy(c, ch, "riak", "--series", "quantal") 287 c.Assert(err, jc.ErrorIsNil) 288 } 289 290 func (s *UpgradeCharmErrorsStateSuite) TestInvalidSwitchURL(c *gc.C) { 291 s.deployService(c) 292 err := runUpgradeCharm(c, "riak", "--switch=blah") 293 c.Assert(err, gc.ErrorMatches, `cannot resolve URL "cs:blah": charm or bundle not found`) 294 err = runUpgradeCharm(c, "riak", "--switch=cs:missing/one") 295 c.Assert(err, gc.ErrorMatches, `cannot resolve URL "cs:missing/one": charm not found`) 296 // TODO(dimitern): add tests with incompatible charms 297 } 298 299 func (s *UpgradeCharmErrorsStateSuite) TestNoPathFails(c *gc.C) { 300 s.deployService(c) 301 err := runUpgradeCharm(c, "riak") 302 c.Assert(err, gc.ErrorMatches, "upgrading a local charm requires either --path or --switch") 303 } 304 305 func (s *UpgradeCharmErrorsStateSuite) TestSwitchAndRevisionFails(c *gc.C) { 306 s.deployService(c) 307 err := runUpgradeCharm(c, "riak", "--switch=riak", "--revision=2") 308 c.Assert(err, gc.ErrorMatches, "--switch and --revision are mutually exclusive") 309 } 310 311 func (s *UpgradeCharmErrorsStateSuite) TestPathAndRevisionFails(c *gc.C) { 312 s.deployService(c) 313 err := runUpgradeCharm(c, "riak", "--path=foo", "--revision=2") 314 c.Assert(err, gc.ErrorMatches, "--path and --revision are mutually exclusive") 315 } 316 317 func (s *UpgradeCharmErrorsStateSuite) TestSwitchAndPathFails(c *gc.C) { 318 s.deployService(c) 319 err := runUpgradeCharm(c, "riak", "--switch=riak", "--path=foo") 320 c.Assert(err, gc.ErrorMatches, "--switch and --path are mutually exclusive") 321 } 322 323 func (s *UpgradeCharmErrorsStateSuite) TestInvalidRevision(c *gc.C) { 324 s.deployService(c) 325 err := runUpgradeCharm(c, "riak", "--revision=blah") 326 c.Assert(err, gc.ErrorMatches, `invalid value "blah" for flag --revision: strconv.(ParseInt|Atoi): parsing "blah": invalid syntax`) 327 } 328 329 type BaseUpgradeCharmStateSuite struct{} 330 331 type UpgradeCharmSuccessStateSuite struct { 332 BaseUpgradeCharmStateSuite 333 jujutesting.RepoSuite 334 coretesting.CmdBlockHelper 335 path string 336 riak *state.Application 337 } 338 339 func (s *BaseUpgradeCharmStateSuite) assertUpgraded(c *gc.C, riak *state.Application, revision int, forced bool) *charm.URL { 340 err := riak.Refresh() 341 c.Assert(err, jc.ErrorIsNil) 342 ch, force, err := riak.Charm() 343 c.Assert(err, jc.ErrorIsNil) 344 c.Assert(ch.Revision(), gc.Equals, revision) 345 c.Assert(force, gc.Equals, forced) 346 return ch.URL() 347 } 348 349 var _ = gc.Suite(&UpgradeCharmSuccessStateSuite{}) 350 351 func (s *UpgradeCharmSuccessStateSuite) SetUpTest(c *gc.C) { 352 s.RepoSuite.SetUpTest(c) 353 s.path = testcharms.Repo.ClonedDirPath(s.CharmsPath, "riak") 354 err := runDeploy(c, s.path, "--series", "quantal") 355 c.Assert(err, jc.ErrorIsNil) 356 s.riak, err = s.State.Application("riak") 357 c.Assert(err, jc.ErrorIsNil) 358 ch, forced, err := s.riak.Charm() 359 c.Assert(err, jc.ErrorIsNil) 360 c.Assert(ch.Revision(), gc.Equals, 7) 361 c.Assert(forced, jc.IsFalse) 362 363 s.CmdBlockHelper = coretesting.NewCmdBlockHelper(s.APIState) 364 c.Assert(s.CmdBlockHelper, gc.NotNil) 365 s.AddCleanup(func(*gc.C) { s.CmdBlockHelper.Close() }) 366 } 367 368 func (s *UpgradeCharmSuccessStateSuite) assertLocalRevision(c *gc.C, revision int, path string) { 369 dir, err := charm.ReadCharmDir(path) 370 c.Assert(err, jc.ErrorIsNil) 371 c.Assert(dir.Revision(), gc.Equals, revision) 372 } 373 374 func (s *UpgradeCharmSuccessStateSuite) TestLocalRevisionUnchanged(c *gc.C) { 375 err := runUpgradeCharm(c, "riak", "--path", s.path) 376 c.Assert(err, jc.ErrorIsNil) 377 curl := s.assertUpgraded(c, s.riak, 8, false) 378 s.AssertCharmUploaded(c, curl) 379 // Even though the remote revision is bumped, the local one should 380 // be unchanged. 381 s.assertLocalRevision(c, 7, s.path) 382 } 383 384 func (s *UpgradeCharmSuccessStateSuite) TestBlockUpgradeCharm(c *gc.C) { 385 // Block operation 386 s.BlockAllChanges(c, "TestBlockUpgradeCharm") 387 err := runUpgradeCharm(c, "riak", "--path", s.path) 388 s.AssertBlocked(c, err, ".*TestBlockUpgradeCharm.*") 389 } 390 391 func (s *UpgradeCharmSuccessStateSuite) TestRespectsLocalRevisionWhenPossible(c *gc.C) { 392 dir, err := charm.ReadCharmDir(s.path) 393 c.Assert(err, jc.ErrorIsNil) 394 err = dir.SetDiskRevision(42) 395 c.Assert(err, jc.ErrorIsNil) 396 397 err = runUpgradeCharm(c, "riak", "--path", s.path) 398 c.Assert(err, jc.ErrorIsNil) 399 curl := s.assertUpgraded(c, s.riak, 42, false) 400 s.AssertCharmUploaded(c, curl) 401 s.assertLocalRevision(c, 42, s.path) 402 } 403 404 func (s *UpgradeCharmSuccessStateSuite) TestForcedSeriesUpgrade(c *gc.C) { 405 path := testcharms.Repo.ClonedDirPath(c.MkDir(), "multi-series") 406 err := runDeploy(c, path, "multi-series", "--series", "precise") 407 c.Assert(err, jc.ErrorIsNil) 408 application, err := s.State.Application("multi-series") 409 c.Assert(err, jc.ErrorIsNil) 410 ch, _, err := application.Charm() 411 c.Assert(err, jc.ErrorIsNil) 412 c.Assert(ch.Revision(), gc.Equals, 1) 413 414 // Overwrite the metadata.yaml to change the supported series. 415 metadataPath := filepath.Join(path, "metadata.yaml") 416 file, err := os.OpenFile(metadataPath, os.O_TRUNC|os.O_RDWR, 0666) 417 if err != nil { 418 c.Fatal(errors.Annotate(err, "cannot open metadata.yaml for overwriting")) 419 } 420 defer file.Close() 421 422 metadata := strings.Join( 423 []string{ 424 `name: multi-series`, 425 `summary: "That's a dummy charm with multi-series."`, 426 `description: |`, 427 ` This is a longer description which`, 428 ` potentially contains multiple lines.`, 429 `series:`, 430 ` - trusty`, 431 ` - wily`, 432 }, 433 "\n", 434 ) 435 if _, err := file.WriteString(metadata); err != nil { 436 c.Fatal(errors.Annotate(err, "cannot write to metadata.yaml")) 437 } 438 439 err = runUpgradeCharm(c, "multi-series", "--path", path, "--force-series") 440 c.Assert(err, jc.ErrorIsNil) 441 442 err = application.Refresh() 443 c.Assert(err, jc.ErrorIsNil) 444 445 ch, force, err := application.Charm() 446 c.Assert(err, jc.ErrorIsNil) 447 c.Check(ch.Revision(), gc.Equals, 2) 448 c.Check(force, gc.Equals, false) 449 } 450 451 func (s *UpgradeCharmSuccessStateSuite) TestInitWithResources(c *gc.C) { 452 testcharms.Repo.CharmArchivePath(s.CharmsPath, "dummy") 453 dir := c.MkDir() 454 455 foopath := path.Join(dir, "foo") 456 barpath := path.Join(dir, "bar") 457 err := ioutil.WriteFile(foopath, []byte("foo"), 0600) 458 c.Assert(err, jc.ErrorIsNil) 459 err = ioutil.WriteFile(barpath, []byte("bar"), 0600) 460 c.Assert(err, jc.ErrorIsNil) 461 462 res1 := fmt.Sprintf("foo=%s", foopath) 463 res2 := fmt.Sprintf("bar=%s", barpath) 464 465 d := upgradeCharmCommand{} 466 args := []string{"dummy", "--resource", res1, "--resource", res2} 467 468 err = coretesting.InitCommand(modelcmd.Wrap(&d), args) 469 c.Assert(err, jc.ErrorIsNil) 470 c.Assert(d.Resources, gc.DeepEquals, map[string]string{ 471 "foo": foopath, 472 "bar": barpath, 473 }) 474 } 475 476 func (s *UpgradeCharmSuccessStateSuite) TestForcedUnitsUpgrade(c *gc.C) { 477 err := runUpgradeCharm(c, "riak", "--force-units", "--path", s.path) 478 c.Assert(err, jc.ErrorIsNil) 479 curl := s.assertUpgraded(c, s.riak, 8, true) 480 s.AssertCharmUploaded(c, curl) 481 // Local revision is not changed. 482 s.assertLocalRevision(c, 7, s.path) 483 } 484 485 func (s *UpgradeCharmSuccessStateSuite) TestBlockForcedUnitsUpgrade(c *gc.C) { 486 // Block operation 487 s.BlockAllChanges(c, "TestBlockForcedUpgrade") 488 err := runUpgradeCharm(c, "riak", "--force-units", "--path", s.path) 489 c.Assert(err, jc.ErrorIsNil) 490 curl := s.assertUpgraded(c, s.riak, 8, true) 491 s.AssertCharmUploaded(c, curl) 492 // Local revision is not changed. 493 s.assertLocalRevision(c, 7, s.path) 494 } 495 496 func (s *UpgradeCharmSuccessStateSuite) TestCharmPath(c *gc.C) { 497 myriakPath := testcharms.Repo.ClonedDirPath(c.MkDir(), "riak") 498 499 // Change the revision to 42 and upgrade to it with explicit revision. 500 err := ioutil.WriteFile(path.Join(myriakPath, "revision"), []byte("42"), 0644) 501 c.Assert(err, jc.ErrorIsNil) 502 err = runUpgradeCharm(c, "riak", "--path", myriakPath) 503 c.Assert(err, jc.ErrorIsNil) 504 curl := s.assertUpgraded(c, s.riak, 42, false) 505 c.Assert(curl.String(), gc.Equals, "local:quantal/riak-42") 506 s.assertLocalRevision(c, 42, myriakPath) 507 } 508 509 func (s *UpgradeCharmSuccessStateSuite) TestCharmPathNoRevUpgrade(c *gc.C) { 510 // Revision 7 is running to start with. 511 myriakPath := testcharms.Repo.ClonedDirPath(c.MkDir(), "riak") 512 s.assertLocalRevision(c, 7, myriakPath) 513 err := runUpgradeCharm(c, "riak", "--path", myriakPath) 514 c.Assert(err, jc.ErrorIsNil) 515 curl := s.assertUpgraded(c, s.riak, 8, false) 516 c.Assert(curl.String(), gc.Equals, "local:quantal/riak-8") 517 } 518 519 func (s *UpgradeCharmSuccessStateSuite) TestCharmPathDifferentNameFails(c *gc.C) { 520 myriakPath := testcharms.Repo.RenamedClonedDirPath(s.CharmsPath, "riak", "myriak") 521 metadataPath := filepath.Join(myriakPath, "metadata.yaml") 522 file, err := os.OpenFile(metadataPath, os.O_TRUNC|os.O_RDWR, 0666) 523 if err != nil { 524 c.Fatal(errors.Annotate(err, "cannot open metadata.yaml")) 525 } 526 defer file.Close() 527 528 // Overwrite the metadata.yaml to contain a new name. 529 newMetadata := strings.Join([]string{`name: myriak`, `summary: ""`, `description: ""`}, "\n") 530 if _, err := file.WriteString(newMetadata); err != nil { 531 c.Fatal("cannot write to metadata.yaml") 532 } 533 err = runUpgradeCharm(c, "riak", "--path", myriakPath) 534 c.Assert(err, gc.ErrorMatches, `cannot upgrade "riak" to "myriak"`) 535 } 536 537 type UpgradeCharmCharmStoreStateSuite struct { 538 BaseUpgradeCharmStateSuite 539 charmStoreSuite 540 } 541 542 var _ = gc.Suite(&UpgradeCharmCharmStoreStateSuite{}) 543 544 var upgradeCharmAuthorizationTests = []struct { 545 about string 546 uploadURL string 547 switchURL string 548 readPermUser string 549 expectError string 550 }{{ 551 about: "public charm, success", 552 uploadURL: "cs:~bob/trusty/wordpress1-10", 553 switchURL: "cs:~bob/trusty/wordpress1", 554 }, { 555 about: "public charm, fully resolved, success", 556 uploadURL: "cs:~bob/trusty/wordpress2-10", 557 switchURL: "cs:~bob/trusty/wordpress2-10", 558 }, { 559 about: "non-public charm, success", 560 uploadURL: "cs:~bob/trusty/wordpress3-10", 561 switchURL: "cs:~bob/trusty/wordpress3", 562 readPermUser: clientUserName, 563 }, { 564 about: "non-public charm, fully resolved, success", 565 uploadURL: "cs:~bob/trusty/wordpress4-10", 566 switchURL: "cs:~bob/trusty/wordpress4-10", 567 readPermUser: clientUserName, 568 }, { 569 about: "non-public charm, access denied", 570 uploadURL: "cs:~bob/trusty/wordpress5-10", 571 switchURL: "cs:~bob/trusty/wordpress5", 572 readPermUser: "bob", 573 expectError: `cannot resolve charm URL "cs:~bob/trusty/wordpress5": cannot get "/~bob/trusty/wordpress5/meta/any\?include=id&include=supported-series&include=published": unauthorized: access denied for user "client-username"`, 574 }, { 575 about: "non-public charm, fully resolved, access denied", 576 uploadURL: "cs:~bob/trusty/wordpress6-47", 577 switchURL: "cs:~bob/trusty/wordpress6-47", 578 readPermUser: "bob", 579 expectError: `cannot resolve charm URL "cs:~bob/trusty/wordpress6-47": cannot get "/~bob/trusty/wordpress6-47/meta/any\?include=id&include=supported-series&include=published": unauthorized: access denied for user "client-username"`, 580 }} 581 582 func (s *UpgradeCharmCharmStoreStateSuite) TestUpgradeCharmAuthorization(c *gc.C) { 583 testcharms.UploadCharm(c, s.client, "cs:~other/trusty/wordpress-0", "wordpress") 584 err := runDeploy(c, "cs:~other/trusty/wordpress-0") 585 c.Assert(err, jc.ErrorIsNil) 586 for i, test := range upgradeCharmAuthorizationTests { 587 c.Logf("test %d: %s", i, test.about) 588 url, _ := testcharms.UploadCharm(c, s.client, test.uploadURL, "wordpress") 589 if test.readPermUser != "" { 590 s.changeReadPerm(c, url, test.readPermUser) 591 } 592 err := runUpgradeCharm(c, "wordpress", "--switch", test.switchURL) 593 if test.expectError != "" { 594 c.Assert(err, gc.ErrorMatches, test.expectError) 595 continue 596 } 597 c.Assert(err, jc.ErrorIsNil) 598 } 599 } 600 601 func (s *UpgradeCharmCharmStoreStateSuite) TestSwitch(c *gc.C) { 602 testcharms.UploadCharm(c, s.client, "cs:~other/trusty/riak-0", "riak") 603 testcharms.UploadCharm(c, s.client, "cs:~other/trusty/anotherriak-7", "riak") 604 err := runDeploy(c, "cs:~other/trusty/riak-0") 605 c.Assert(err, jc.ErrorIsNil) 606 607 riak, err := s.State.Application("riak") 608 c.Assert(err, jc.ErrorIsNil) 609 ch, forced, err := riak.Charm() 610 c.Assert(err, jc.ErrorIsNil) 611 c.Assert(ch.Revision(), gc.Equals, 0) 612 c.Assert(forced, jc.IsFalse) 613 614 err = runUpgradeCharm(c, "riak", "--switch=cs:~other/trusty/anotherriak") 615 c.Assert(err, jc.ErrorIsNil) 616 curl := s.assertUpgraded(c, riak, 7, false) 617 c.Assert(curl.String(), gc.Equals, "cs:~other/trusty/anotherriak-7") 618 619 // Now try the same with explicit revision - should fail. 620 err = runUpgradeCharm(c, "riak", "--switch=cs:~other/trusty/anotherriak-7") 621 c.Assert(err, gc.ErrorMatches, `already running specified charm "cs:~other/trusty/anotherriak-7"`) 622 623 // Change the revision to 42 and upgrade to it with explicit revision. 624 testcharms.UploadCharm(c, s.client, "cs:~other/trusty/anotherriak-42", "riak") 625 err = runUpgradeCharm(c, "riak", "--switch=cs:~other/trusty/anotherriak-42") 626 c.Assert(err, jc.ErrorIsNil) 627 curl = s.assertUpgraded(c, riak, 42, false) 628 c.Assert(curl.String(), gc.Equals, "cs:~other/trusty/anotherriak-42") 629 } 630 631 func (s *UpgradeCharmCharmStoreStateSuite) TestUpgradeCharmWithChannel(c *gc.C) { 632 id, ch := testcharms.UploadCharm(c, s.client, "cs:~client-username/trusty/wordpress-0", "wordpress") 633 err := runDeploy(c, "cs:~client-username/trusty/wordpress-0") 634 c.Assert(err, jc.ErrorIsNil) 635 636 // Upload a new revision of the charm, but publish it 637 // only to the beta channel. 638 639 id.Revision = 1 640 err = s.client.UploadCharmWithRevision(id, ch, -1) 641 c.Assert(err, gc.IsNil) 642 643 err = s.client.Publish(id, []csclientparams.Channel{csclientparams.BetaChannel}, nil) 644 c.Assert(err, gc.IsNil) 645 646 err = runUpgradeCharm(c, "wordpress", "--channel", "beta") 647 c.Assert(err, gc.IsNil) 648 649 s.assertCharmsUploaded(c, "cs:~client-username/trusty/wordpress-0", "cs:~client-username/trusty/wordpress-1") 650 s.assertApplicationsDeployed(c, map[string]serviceInfo{ 651 "wordpress": {charm: "cs:~client-username/trusty/wordpress-1"}, 652 }) 653 } 654 655 func (s *UpgradeCharmCharmStoreStateSuite) TestUpgradeWithTermsNotSigned(c *gc.C) { 656 id, ch := testcharms.UploadCharm(c, s.client, "quantal/terms1-1", "terms1") 657 err := runDeploy(c, "quantal/terms1") 658 c.Assert(err, jc.ErrorIsNil) 659 id.Revision = id.Revision + 1 660 err = s.client.UploadCharmWithRevision(id, ch, -1) 661 c.Assert(err, gc.IsNil) 662 err = s.client.Publish(id, []csclientparams.Channel{csclientparams.StableChannel}, nil) 663 c.Assert(err, gc.IsNil) 664 s.termsDischargerError = &httpbakery.Error{ 665 Message: "term agreement required: term/1 term/2", 666 Code: "term agreement required", 667 } 668 expectedError := `Declined: please agree to the following terms term/1 term/2. Try: "juju agree term/1 term/2"` 669 err = runUpgradeCharm(c, "terms1") 670 c.Assert(err, gc.ErrorMatches, expectedError) 671 } 672 673 type mockAPIConnection struct { 674 api.Connection 675 bestFacadeVersion int 676 serverVersion *version.Number 677 } 678 679 func (m *mockAPIConnection) BestFacadeVersion(name string) int { 680 return m.bestFacadeVersion 681 } 682 683 func (m *mockAPIConnection) ServerVersion() (version.Number, bool) { 684 if m.serverVersion != nil { 685 return *m.serverVersion, true 686 } 687 return version.Number{}, false 688 } 689 690 func (*mockAPIConnection) Close() error { 691 return nil 692 } 693 694 type mockCharmAdder struct { 695 CharmAdder 696 testing.Stub 697 } 698 699 func (m *mockCharmAdder) AddCharm(curl *charm.URL, channel csclientparams.Channel) error { 700 m.MethodCall(m, "AddCharm", curl, channel) 701 return m.NextErr() 702 } 703 704 type mockCharmClient struct { 705 CharmClient 706 testing.Stub 707 charmInfo *charms.CharmInfo 708 } 709 710 func (m *mockCharmClient) CharmInfo(curl string) (*charms.CharmInfo, error) { 711 m.MethodCall(m, "CharmInfo", curl) 712 if err := m.NextErr(); err != nil { 713 return nil, err 714 } 715 return m.charmInfo, nil 716 } 717 718 type mockCharmUpgradeClient struct { 719 CharmUpgradeClient 720 testing.Stub 721 charmURL *charm.URL 722 } 723 724 func (m *mockCharmUpgradeClient) GetCharmURL(applicationName string) (*charm.URL, error) { 725 m.MethodCall(m, "GetCharmURL", applicationName) 726 return m.charmURL, m.NextErr() 727 } 728 729 func (m *mockCharmUpgradeClient) SetCharm(cfg application.SetCharmConfig) error { 730 m.MethodCall(m, "SetCharm", cfg) 731 return m.NextErr() 732 } 733 734 type mockModelConfigGetter struct { 735 ModelConfigGetter 736 testing.Stub 737 } 738 739 func (m *mockModelConfigGetter) ModelGet() (map[string]interface{}, error) { 740 m.MethodCall(m, "ModelGet") 741 return coretesting.FakeConfig(), m.NextErr() 742 } 743 744 type mockResourceLister struct { 745 ResourceLister 746 testing.Stub 747 }