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  }