github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/cmd/juju/commands/deploy_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package commands 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "io/ioutil" 10 "net/http" 11 "net/http/httptest" 12 "net/url" 13 "strings" 14 15 "github.com/juju/errors" 16 jujutesting "github.com/juju/testing" 17 jc "github.com/juju/testing/checkers" 18 "github.com/juju/utils" 19 gc "gopkg.in/check.v1" 20 "gopkg.in/juju/charm.v5" 21 "gopkg.in/juju/charm.v5/charmrepo" 22 "gopkg.in/juju/charmstore.v4" 23 "gopkg.in/juju/charmstore.v4/charmstoretesting" 24 "gopkg.in/juju/charmstore.v4/csclient" 25 "gopkg.in/macaroon-bakery.v0/bakery/checkers" 26 "gopkg.in/macaroon-bakery.v0/bakerytest" 27 28 "github.com/juju/juju/api" 29 "github.com/juju/juju/cmd/envcmd" 30 "github.com/juju/juju/cmd/juju/service" 31 "github.com/juju/juju/constraints" 32 "github.com/juju/juju/environs/config" 33 "github.com/juju/juju/instance" 34 "github.com/juju/juju/juju/testing" 35 "github.com/juju/juju/state" 36 "github.com/juju/juju/storage/poolmanager" 37 "github.com/juju/juju/storage/provider" 38 "github.com/juju/juju/testcharms" 39 coretesting "github.com/juju/juju/testing" 40 ) 41 42 type DeploySuite struct { 43 testing.RepoSuite 44 CmdBlockHelper 45 } 46 47 func (s *DeploySuite) SetUpTest(c *gc.C) { 48 s.RepoSuite.SetUpTest(c) 49 s.CmdBlockHelper = NewCmdBlockHelper(s.APIState) 50 c.Assert(s.CmdBlockHelper, gc.NotNil) 51 s.AddCleanup(func(*gc.C) { s.CmdBlockHelper.Close() }) 52 } 53 54 var _ = gc.Suite(&DeploySuite{}) 55 56 func runDeploy(c *gc.C, args ...string) error { 57 _, err := coretesting.RunCommand(c, envcmd.Wrap(&DeployCommand{}), args...) 58 return err 59 } 60 61 var initErrorTests = []struct { 62 args []string 63 err string 64 }{ 65 { 66 args: nil, 67 err: `no charm specified`, 68 }, { 69 args: []string{"charm-name", "service-name", "hotdog"}, 70 err: `unrecognized args: \["hotdog"\]`, 71 }, { 72 args: []string{"craz~ness"}, 73 err: `invalid charm name "craz~ness"`, 74 }, { 75 args: []string{"craziness", "burble-1"}, 76 err: `invalid service name "burble-1"`, 77 }, { 78 args: []string{"craziness", "burble1", "-n", "0"}, 79 err: `--num-units must be a positive integer`, 80 }, { 81 args: []string{"craziness", "burble1", "--to", "bigglesplop"}, 82 err: `invalid --to parameter "bigglesplop"`, 83 }, { 84 args: []string{"craziness", "burble1", "-n", "2", "--to", "123"}, 85 err: `cannot use --num-units > 1 with --to`, 86 }, { 87 args: []string{"craziness", "burble1", "--constraints", "gibber=plop"}, 88 err: `invalid value "gibber=plop" for flag --constraints: unknown constraint "gibber"`, 89 }, 90 } 91 92 func (s *DeploySuite) TestInitErrors(c *gc.C) { 93 for i, t := range initErrorTests { 94 c.Logf("test %d", i) 95 err := coretesting.InitCommand(envcmd.Wrap(&DeployCommand{}), t.args) 96 c.Assert(err, gc.ErrorMatches, t.err) 97 } 98 } 99 100 func (s *DeploySuite) TestNoCharm(c *gc.C) { 101 err := runDeploy(c, "local:unknown-123") 102 c.Assert(err, gc.ErrorMatches, `charm not found in ".*": local:trusty/unknown-123`) 103 } 104 105 func (s *DeploySuite) TestBlockDeploy(c *gc.C) { 106 // Block operation 107 s.BlockAllChanges(c, "TestBlockDeploy") 108 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 109 err := runDeploy(c, "local:dummy", "some-service-name") 110 s.AssertBlocked(c, err, ".*TestBlockDeploy.*") 111 } 112 113 func (s *DeploySuite) TestCharmDir(c *gc.C) { 114 testcharms.Repo.ClonedDirPath(s.SeriesPath, "dummy") 115 err := runDeploy(c, "local:dummy") 116 c.Assert(err, jc.ErrorIsNil) 117 curl := charm.MustParseURL("local:trusty/dummy-1") 118 s.AssertService(c, "dummy", curl, 1, 0) 119 } 120 121 func (s *DeploySuite) TestUpgradeReportsDeprecated(c *gc.C) { 122 testcharms.Repo.ClonedDirPath(s.SeriesPath, "dummy") 123 ctx, err := coretesting.RunCommand(c, envcmd.Wrap(&DeployCommand{}), "local:dummy", "-u") 124 c.Assert(err, jc.ErrorIsNil) 125 126 c.Assert(coretesting.Stdout(ctx), gc.Equals, "") 127 output := strings.Split(coretesting.Stderr(ctx), "\n") 128 c.Check(output[0], gc.Matches, `Added charm ".*" to the environment.`) 129 c.Check(output[1], gc.Equals, "--upgrade (or -u) is deprecated and ignored; charms are always deployed with a unique revision.") 130 } 131 132 func (s *DeploySuite) TestUpgradeCharmDir(c *gc.C) { 133 // Add the charm, so the url will exist and a new revision will be 134 // picked in ServiceDeploy. 135 dummyCharm := s.AddTestingCharm(c, "dummy") 136 137 dirPath := testcharms.Repo.ClonedDirPath(s.SeriesPath, "dummy") 138 err := runDeploy(c, "local:quantal/dummy") 139 c.Assert(err, jc.ErrorIsNil) 140 upgradedRev := dummyCharm.Revision() + 1 141 curl := dummyCharm.URL().WithRevision(upgradedRev) 142 s.AssertService(c, "dummy", curl, 1, 0) 143 // Check the charm dir was left untouched. 144 ch, err := charm.ReadCharmDir(dirPath) 145 c.Assert(err, jc.ErrorIsNil) 146 c.Assert(ch.Revision(), gc.Equals, 1) 147 } 148 149 func (s *DeploySuite) TestCharmBundle(c *gc.C) { 150 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 151 err := runDeploy(c, "local:dummy", "some-service-name") 152 c.Assert(err, jc.ErrorIsNil) 153 curl := charm.MustParseURL("local:trusty/dummy-1") 154 s.AssertService(c, "some-service-name", curl, 1, 0) 155 } 156 157 func (s *DeploySuite) TestSubordinateCharm(c *gc.C) { 158 testcharms.Repo.CharmArchivePath(s.SeriesPath, "logging") 159 err := runDeploy(c, "local:logging") 160 c.Assert(err, jc.ErrorIsNil) 161 curl := charm.MustParseURL("local:trusty/logging-1") 162 s.AssertService(c, "logging", curl, 0, 0) 163 } 164 165 func (s *DeploySuite) TestConfig(c *gc.C) { 166 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 167 path := setupConfigFile(c, c.MkDir()) 168 err := runDeploy(c, "local:dummy", "dummy-service", "--config", path) 169 c.Assert(err, jc.ErrorIsNil) 170 service, err := s.State.Service("dummy-service") 171 c.Assert(err, jc.ErrorIsNil) 172 settings, err := service.ConfigSettings() 173 c.Assert(err, jc.ErrorIsNil) 174 c.Assert(settings, gc.DeepEquals, charm.Settings{ 175 "skill-level": int64(9000), 176 "username": "admin001", 177 }) 178 } 179 180 func (s *DeploySuite) TestRelativeConfigPath(c *gc.C) { 181 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 182 // Putting a config file in home is okay as $HOME is set to a tempdir 183 setupConfigFile(c, utils.Home()) 184 err := runDeploy(c, "local:dummy", "dummy-service", "--config", "~/testconfig.yaml") 185 c.Assert(err, jc.ErrorIsNil) 186 } 187 188 func (s *DeploySuite) TestConfigError(c *gc.C) { 189 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 190 path := setupConfigFile(c, c.MkDir()) 191 err := runDeploy(c, "local:dummy", "other-service", "--config", path) 192 c.Assert(err, gc.ErrorMatches, `no settings found for "other-service"`) 193 _, err = s.State.Service("other-service") 194 c.Assert(err, jc.Satisfies, errors.IsNotFound) 195 } 196 197 func (s *DeploySuite) TestConstraints(c *gc.C) { 198 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 199 err := runDeploy(c, "local:dummy", "--constraints", "mem=2G cpu-cores=2 networks=net1,^net2") 200 c.Assert(err, jc.ErrorIsNil) 201 curl := charm.MustParseURL("local:trusty/dummy-1") 202 service, _ := s.AssertService(c, "dummy", curl, 1, 0) 203 cons, err := service.Constraints() 204 c.Assert(err, jc.ErrorIsNil) 205 c.Assert(cons, jc.DeepEquals, constraints.MustParse("mem=2G cpu-cores=2 networks=net1,^net2")) 206 } 207 208 func (s *DeploySuite) TestNetworks(c *gc.C) { 209 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 210 err := runDeploy(c, "local:dummy", "--networks", ", net1, net2 , ", "--constraints", "mem=2G cpu-cores=2 networks=net1,net0,^net3,^net4") 211 c.Assert(err, jc.ErrorIsNil) 212 curl := charm.MustParseURL("local:trusty/dummy-1") 213 service, _ := s.AssertService(c, "dummy", curl, 1, 0) 214 networks, err := service.Networks() 215 c.Assert(err, jc.ErrorIsNil) 216 c.Assert(networks, jc.DeepEquals, []string{"net1", "net2"}) 217 cons, err := service.Constraints() 218 c.Assert(err, jc.ErrorIsNil) 219 c.Assert(cons, jc.DeepEquals, constraints.MustParse("mem=2G cpu-cores=2 networks=net1,net0,^net3,^net4")) 220 } 221 222 // TODO(wallyworld) - add another test that deploy with storage fails for older environments 223 // (need deploy client to be refactored to use API stub) 224 func (s *DeploySuite) TestStorage(c *gc.C) { 225 pm := poolmanager.New(state.NewStateSettings(s.State)) 226 _, err := pm.Create("loop-pool", provider.LoopProviderType, map[string]interface{}{"foo": "bar"}) 227 c.Assert(err, jc.ErrorIsNil) 228 229 testcharms.Repo.CharmArchivePath(s.SeriesPath, "storage-block") 230 err = runDeploy(c, "local:storage-block", "--storage", "data=loop-pool,1G") 231 c.Assert(err, jc.ErrorIsNil) 232 curl := charm.MustParseURL("local:trusty/storage-block-1") 233 service, _ := s.AssertService(c, "storage-block", curl, 1, 0) 234 235 cons, err := service.StorageConstraints() 236 c.Assert(err, jc.ErrorIsNil) 237 c.Assert(cons, jc.DeepEquals, map[string]state.StorageConstraints{ 238 "data": { 239 Pool: "loop-pool", 240 Count: 1, 241 Size: 1024, 242 }, 243 "allecto": { 244 Pool: "loop", 245 Count: 0, 246 Size: 1024, 247 }, 248 }) 249 } 250 251 func (s *DeploySuite) TestSubordinateConstraints(c *gc.C) { 252 testcharms.Repo.CharmArchivePath(s.SeriesPath, "logging") 253 err := runDeploy(c, "local:logging", "--constraints", "mem=1G") 254 c.Assert(err, gc.ErrorMatches, "cannot use --constraints with subordinate service") 255 } 256 257 func (s *DeploySuite) TestNumUnits(c *gc.C) { 258 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 259 err := runDeploy(c, "local:dummy", "-n", "13") 260 c.Assert(err, jc.ErrorIsNil) 261 curl := charm.MustParseURL("local:trusty/dummy-1") 262 s.AssertService(c, "dummy", curl, 13, 0) 263 } 264 265 func (s *DeploySuite) TestNumUnitsSubordinate(c *gc.C) { 266 testcharms.Repo.CharmArchivePath(s.SeriesPath, "logging") 267 err := runDeploy(c, "--num-units", "3", "local:logging") 268 c.Assert(err, gc.ErrorMatches, "cannot use --num-units or --to with subordinate service") 269 _, err = s.State.Service("dummy") 270 c.Assert(err, gc.ErrorMatches, `service "dummy" not found`) 271 } 272 273 func (s *DeploySuite) assertForceMachine(c *gc.C, machineId string) { 274 svc, err := s.State.Service("portlandia") 275 c.Assert(err, jc.ErrorIsNil) 276 units, err := svc.AllUnits() 277 c.Assert(err, jc.ErrorIsNil) 278 c.Assert(units, gc.HasLen, 1) 279 mid, err := units[0].AssignedMachineId() 280 c.Assert(err, jc.ErrorIsNil) 281 c.Assert(mid, gc.Equals, machineId) 282 } 283 284 func (s *DeploySuite) TestForceMachine(c *gc.C) { 285 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 286 machine, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits) 287 c.Assert(err, jc.ErrorIsNil) 288 err = runDeploy(c, "--to", machine.Id(), "local:dummy", "portlandia") 289 c.Assert(err, jc.ErrorIsNil) 290 s.assertForceMachine(c, machine.Id()) 291 } 292 293 func (s *DeploySuite) TestForceMachineExistingContainer(c *gc.C) { 294 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 295 template := state.MachineTemplate{ 296 Series: coretesting.FakeDefaultSeries, 297 Jobs: []state.MachineJob{state.JobHostUnits}, 298 } 299 container, err := s.State.AddMachineInsideNewMachine(template, template, instance.LXC) 300 c.Assert(err, jc.ErrorIsNil) 301 err = runDeploy(c, "--to", container.Id(), "local:dummy", "portlandia") 302 c.Assert(err, jc.ErrorIsNil) 303 s.assertForceMachine(c, container.Id()) 304 machines, err := s.State.AllMachines() 305 c.Assert(err, jc.ErrorIsNil) 306 c.Assert(machines, gc.HasLen, 2) 307 } 308 309 func (s *DeploySuite) TestForceMachineNewContainer(c *gc.C) { 310 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 311 machine, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits) 312 c.Assert(err, jc.ErrorIsNil) 313 err = runDeploy(c, "--to", "lxc:"+machine.Id(), "local:dummy", "portlandia") 314 c.Assert(err, jc.ErrorIsNil) 315 s.assertForceMachine(c, machine.Id()+"/lxc/0") 316 machines, err := s.State.AllMachines() 317 c.Assert(err, jc.ErrorIsNil) 318 c.Assert(machines, gc.HasLen, 2) 319 } 320 321 func (s *DeploySuite) TestForceMachineNotFound(c *gc.C) { 322 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 323 err := runDeploy(c, "--to", "42", "local:dummy", "portlandia") 324 c.Assert(err, gc.ErrorMatches, `cannot deploy "portlandia" to machine 42: machine 42 not found`) 325 _, err = s.State.Service("portlandia") 326 c.Assert(err, gc.ErrorMatches, `service "portlandia" not found`) 327 } 328 329 func (s *DeploySuite) TestForceMachineSubordinate(c *gc.C) { 330 machine, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits) 331 c.Assert(err, jc.ErrorIsNil) 332 testcharms.Repo.CharmArchivePath(s.SeriesPath, "logging") 333 err = runDeploy(c, "--to", machine.Id(), "local:logging") 334 c.Assert(err, gc.ErrorMatches, "cannot use --num-units or --to with subordinate service") 335 _, err = s.State.Service("dummy") 336 c.Assert(err, gc.ErrorMatches, `service "dummy" not found`) 337 } 338 339 func (s *DeploySuite) TestNonLocalCannotHostUnits(c *gc.C) { 340 err := runDeploy(c, "--to", "0", "local:dummy", "portlandia") 341 c.Assert(err, gc.Not(gc.ErrorMatches), "machine 0 is the state server for a local environment and cannot host units") 342 } 343 344 type DeployLocalSuite struct { 345 testing.RepoSuite 346 } 347 348 var _ = gc.Suite(&DeployLocalSuite{}) 349 350 func (s *DeployLocalSuite) SetUpTest(c *gc.C) { 351 s.RepoSuite.SetUpTest(c) 352 353 // override provider type 354 s.PatchValue(&service.GetClientConfig, func(client service.ServiceAddUnitAPI) (*config.Config, error) { 355 attrs, err := client.EnvironmentGet() 356 if err != nil { 357 return nil, err 358 } 359 attrs["type"] = "local" 360 361 return config.New(config.NoDefaults, attrs) 362 }) 363 } 364 365 func (s *DeployLocalSuite) TestLocalCannotHostUnits(c *gc.C) { 366 err := runDeploy(c, "--to", "0", "local:dummy", "portlandia") 367 c.Assert(err, gc.ErrorMatches, "machine 0 is the state server for a local environment and cannot host units") 368 } 369 370 // setupConfigFile creates a configuration file for testing set 371 // with the --config argument specifying a configuration file. 372 func setupConfigFile(c *gc.C, dir string) string { 373 ctx := coretesting.ContextForDir(c, dir) 374 path := ctx.AbsPath("testconfig.yaml") 375 content := []byte("dummy-service:\n skill-level: 9000\n username: admin001\n\n") 376 err := ioutil.WriteFile(path, content, 0666) 377 c.Assert(err, jc.ErrorIsNil) 378 return path 379 } 380 381 type DeployCharmStoreSuite struct { 382 charmStoreSuite 383 } 384 385 var _ = gc.Suite(&DeployCharmStoreSuite{}) 386 387 var deployAuthorizationTests = []struct { 388 about string 389 uploadURL string 390 deployURL string 391 readPermUser string 392 expectError string 393 expectOutput string 394 }{{ 395 about: "public charm, success", 396 uploadURL: "cs:~bob/trusty/wordpress1-10", 397 deployURL: "cs:~bob/trusty/wordpress1", 398 expectOutput: `Added charm "cs:~bob/trusty/wordpress1-10" to the environment.`, 399 }, { 400 about: "public charm, fully resolved, success", 401 uploadURL: "cs:~bob/trusty/wordpress2-10", 402 deployURL: "cs:~bob/trusty/wordpress2-10", 403 expectOutput: `Added charm "cs:~bob/trusty/wordpress2-10" to the environment.`, 404 }, { 405 about: "non-public charm, success", 406 uploadURL: "cs:~bob/trusty/wordpress3-10", 407 deployURL: "cs:~bob/trusty/wordpress3", 408 readPermUser: clientUserName, 409 expectOutput: `Added charm "cs:~bob/trusty/wordpress3-10" to the environment.`, 410 }, { 411 about: "non-public charm, fully resolved, success", 412 uploadURL: "cs:~bob/trusty/wordpress4-10", 413 deployURL: "cs:~bob/trusty/wordpress4-10", 414 readPermUser: clientUserName, 415 expectOutput: `Added charm "cs:~bob/trusty/wordpress4-10" to the environment.`, 416 }, { 417 about: "non-public charm, access denied", 418 uploadURL: "cs:~bob/trusty/wordpress5-10", 419 deployURL: "cs:~bob/trusty/wordpress5", 420 readPermUser: "bob", 421 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"`, 422 }, { 423 about: "non-public charm, fully resolved, access denied", 424 uploadURL: "cs:~bob/trusty/wordpress6-47", 425 deployURL: "cs:~bob/trusty/wordpress6-47", 426 readPermUser: "bob", 427 expectError: `cannot retrieve charm "cs:~bob/trusty/wordpress6-47": cannot get archive: unauthorized: access denied for user "client-username"`, 428 }} 429 430 func (s *DeployCharmStoreSuite) TestDeployAuthorization(c *gc.C) { 431 for i, test := range deployAuthorizationTests { 432 c.Logf("test %d: %s", i, test.about) 433 url, _ := s.uploadCharm(c, test.uploadURL, "wordpress") 434 if test.readPermUser != "" { 435 s.changeReadPerm(c, url, test.readPermUser) 436 } 437 ctx, err := coretesting.RunCommand(c, envcmd.Wrap(&DeployCommand{}), test.deployURL, fmt.Sprintf("wordpress%d", i)) 438 if test.expectError != "" { 439 c.Assert(err, gc.ErrorMatches, test.expectError) 440 continue 441 } 442 c.Assert(err, jc.ErrorIsNil) 443 output := strings.Trim(coretesting.Stderr(ctx), "\n") 444 c.Assert(output, gc.Equals, test.expectOutput) 445 } 446 } 447 448 const ( 449 // clientUserCookie is the name of the cookie which is 450 // used to signal to the charmStoreSuite macaroon discharger 451 // that the client is a juju client rather than the juju environment. 452 clientUserCookie = "client" 453 454 // clientUserName is the name chosen for the juju client 455 // when it has authorized. 456 clientUserName = "client-username" 457 ) 458 459 // charmStoreSuite is a suite fixture that puts the machinery in 460 // place to allow testing code that calls addCharmViaAPI. 461 type charmStoreSuite struct { 462 testing.JujuConnSuite 463 srv *charmstoretesting.Server 464 discharger *bakerytest.Discharger 465 } 466 467 func (s *charmStoreSuite) SetUpTest(c *gc.C) { 468 s.JujuConnSuite.SetUpTest(c) 469 470 // Set up the third party discharger. 471 s.discharger = bakerytest.NewDischarger(nil, func(req *http.Request, cond string, arg string) ([]checkers.Caveat, error) { 472 cookie, err := req.Cookie(clientUserCookie) 473 if err != nil { 474 return nil, errors.New("discharge denied to non-clients") 475 } 476 return []checkers.Caveat{ 477 checkers.DeclaredCaveat("username", cookie.Value), 478 }, nil 479 }) 480 481 // Set up the charm store testing server. 482 s.srv = charmstoretesting.OpenServer(c, s.Session, charmstore.ServerParams{ 483 IdentityLocation: s.discharger.Location(), 484 PublicKeyLocator: s.discharger, 485 }) 486 487 // Initialize the charm cache dir. 488 s.PatchValue(&charmrepo.CacheDir, c.MkDir()) 489 490 // Point the CLI to the charm store testing server. 491 original := newCharmStoreClient 492 s.PatchValue(&newCharmStoreClient, func() (*csClient, error) { 493 csclient, err := original() 494 if err != nil { 495 return nil, err 496 } 497 csclient.params.URL = s.srv.URL() 498 // Add a cookie so that the discharger can detect whether the 499 // HTTP client is the juju environment or the juju client. 500 lurl, err := url.Parse(s.discharger.Location()) 501 if err != nil { 502 panic(err) 503 } 504 csclient.params.HTTPClient.Jar.SetCookies(lurl, []*http.Cookie{{ 505 Name: clientUserCookie, 506 Value: clientUserName, 507 }}) 508 return csclient, nil 509 }) 510 511 // Point the Juju API server to the charm store testing server. 512 s.PatchValue(&csclient.ServerURL, s.srv.URL()) 513 } 514 515 func (s *charmStoreSuite) TearDownTest(c *gc.C) { 516 s.discharger.Close() 517 s.srv.Close() 518 s.JujuConnSuite.TearDownTest(c) 519 } 520 521 // uploadCharm adds a charm with the given URL and name to the charm store. 522 func (s *charmStoreSuite) uploadCharm(c *gc.C, url, name string) (*charm.URL, charm.Charm) { 523 id := charm.MustParseReference(url) 524 promulgated := false 525 if id.User == "" { 526 id.User = "who" 527 promulgated = true 528 } 529 ch := testcharms.Repo.CharmArchive(c.MkDir(), name) 530 id = s.srv.UploadCharm(c, ch, id, promulgated) 531 return (*charm.URL)(id), ch 532 } 533 534 // changeReadPerm changes the read permission of the given charm URL. 535 // The charm must be present in the testing charm store. 536 func (s *charmStoreSuite) changeReadPerm(c *gc.C, url *charm.URL, perms ...string) { 537 err := s.srv.NewClient().Put("/"+url.Path()+"/meta/perm/read", perms) 538 c.Assert(err, jc.ErrorIsNil) 539 } 540 541 type testMetricCredentialsSetter struct { 542 assert func(string, []byte) 543 } 544 545 func (t *testMetricCredentialsSetter) SetMetricCredentials(serviceName string, data []byte) error { 546 t.assert(serviceName, data) 547 return nil 548 } 549 550 func (t *testMetricCredentialsSetter) Close() error { 551 return nil 552 } 553 554 func (s *DeploySuite) TestAddMetricCredentialsDefault(c *gc.C) { 555 var called bool 556 setter := &testMetricCredentialsSetter{ 557 assert: func(serviceName string, data []byte) { 558 called = true 559 c.Assert(serviceName, gc.DeepEquals, "metered") 560 var b []byte 561 err := json.Unmarshal(data, &b) 562 c.Assert(err, gc.IsNil) 563 c.Assert(string(b), gc.Equals, "hello registration") 564 }, 565 } 566 567 cleanup := jujutesting.PatchValue(&getMetricCredentialsAPI, func(_ *api.State) (metricCredentialsAPI, error) { 568 return setter, nil 569 }) 570 defer cleanup() 571 572 handler := &testMetricsRegistrationHandler{} 573 server := httptest.NewServer(handler) 574 defer server.Close() 575 576 testcharms.Repo.ClonedDirPath(s.SeriesPath, "metered") 577 _, err := coretesting.RunCommand(c, envcmd.Wrap(&DeployCommand{RegisterURL: server.URL}), "local:quantal/metered-1") 578 c.Assert(err, jc.ErrorIsNil) 579 curl := charm.MustParseURL("local:quantal/metered-1") 580 s.AssertService(c, "metered", curl, 1, 0) 581 c.Assert(called, jc.IsTrue) 582 } 583 584 func (s *DeploySuite) TestAddMetricCredentialsDefaultForUnmeteredCharm(c *gc.C) { 585 var called bool 586 setter := &testMetricCredentialsSetter{ 587 assert: func(serviceName string, data []byte) { 588 called = true 589 c.Assert(serviceName, gc.DeepEquals, "dummy") 590 c.Assert(data, gc.DeepEquals, []byte{}) 591 }, 592 } 593 594 cleanup := jujutesting.PatchValue(&getMetricCredentialsAPI, func(_ *api.State) (metricCredentialsAPI, error) { 595 return setter, nil 596 }) 597 defer cleanup() 598 599 testcharms.Repo.ClonedDirPath(s.SeriesPath, "dummy") 600 err := runDeploy(c, "local:dummy") 601 c.Assert(err, jc.ErrorIsNil) 602 curl := charm.MustParseURL("local:trusty/dummy-1") 603 s.AssertService(c, "dummy", curl, 1, 0) 604 c.Assert(called, jc.IsFalse) 605 } 606 607 func (s *DeploySuite) TestAddMetricCredentialsHttp(c *gc.C) { 608 handler := &testMetricsRegistrationHandler{} 609 server := httptest.NewServer(handler) 610 defer server.Close() 611 612 var called bool 613 setter := &testMetricCredentialsSetter{ 614 assert: func(serviceName string, data []byte) { 615 called = true 616 c.Assert(serviceName, gc.DeepEquals, "metered") 617 var b []byte 618 err := json.Unmarshal(data, &b) 619 c.Assert(err, gc.IsNil) 620 c.Assert(string(b), gc.Equals, "hello registration") 621 }, 622 } 623 624 cleanup := jujutesting.PatchValue(&getMetricCredentialsAPI, func(_ *api.State) (metricCredentialsAPI, error) { 625 return setter, nil 626 }) 627 defer cleanup() 628 629 testcharms.Repo.ClonedDirPath(s.SeriesPath, "metered") 630 _, err := coretesting.RunCommand(c, envcmd.Wrap(&DeployCommand{RegisterURL: server.URL}), "local:quantal/metered-1") 631 c.Assert(err, jc.ErrorIsNil) 632 curl := charm.MustParseURL("local:quantal/metered-1") 633 s.AssertService(c, "metered", curl, 1, 0) 634 c.Assert(called, jc.IsTrue) 635 636 c.Assert(handler.registrationCalls, gc.HasLen, 1) 637 c.Assert(handler.registrationCalls[0].CharmURL, gc.DeepEquals, "local:quantal/metered-1") 638 c.Assert(handler.registrationCalls[0].ServiceName, gc.DeepEquals, "metered") 639 }