github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/juju/service/bundle_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package service
     5  
     6  import (
     7  	"fmt"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  	"time"
    13  
    14  	jc "github.com/juju/testing/checkers"
    15  	gc "gopkg.in/check.v1"
    16  	"gopkg.in/juju/charm.v6-unstable"
    17  	"gopkg.in/juju/charmrepo.v2-unstable/csclient"
    18  
    19  	"github.com/juju/juju/api"
    20  	"github.com/juju/juju/constraints"
    21  	"github.com/juju/juju/state"
    22  	"github.com/juju/juju/state/multiwatcher"
    23  	"github.com/juju/juju/state/watcher"
    24  	"github.com/juju/juju/testcharms"
    25  	coretesting "github.com/juju/juju/testing"
    26  )
    27  
    28  // runDeployCommand executes the deploy command in order to deploy the given
    29  // charm or bundle. The deployment output and error are returned.
    30  func runDeployCommand(c *gc.C, id string, args ...string) (string, error) {
    31  	args = append([]string{id}, args...)
    32  	ctx, err := coretesting.RunCommand(c, NewDeployCommand(), args...)
    33  	return strings.Trim(coretesting.Stderr(ctx), "\n"), err
    34  }
    35  
    36  func (s *BundleDeployCharmStoreSuite) TestDeployBundleNotFoundCharmStore(c *gc.C) {
    37  	err := runDeploy(c, "bundle/no-such")
    38  	c.Assert(err, gc.ErrorMatches, `cannot resolve URL "cs:bundle/no-such": bundle not found`)
    39  }
    40  
    41  func (s *BundleDeployCharmStoreSuite) TestDeployBundleInvalidFlags(c *gc.C) {
    42  	testcharms.UploadCharm(c, s.client, "trusty/mysql-42", "mysql")
    43  	testcharms.UploadCharm(c, s.client, "trusty/wordpress-47", "wordpress")
    44  	testcharms.UploadBundle(c, s.client, "bundle/wordpress-simple-1", "wordpress-simple")
    45  	_, err := runDeployCommand(c, "bundle/wordpress-simple", "--config", "config.yaml")
    46  	c.Assert(err, gc.ErrorMatches, "Flags provided but not supported when deploying a bundle: --config.")
    47  	_, err = runDeployCommand(c, "bundle/wordpress-simple", "-n", "2")
    48  	c.Assert(err, gc.ErrorMatches, "Flags provided but not supported when deploying a bundle: -n.")
    49  	_, err = runDeployCommand(c, "bundle/wordpress-simple", "--series", "trusty", "--force")
    50  	c.Assert(err, gc.ErrorMatches, "Flags provided but not supported when deploying a bundle: --force, --series.")
    51  }
    52  
    53  func (s *BundleDeployCharmStoreSuite) TestDeployBundleSuccess(c *gc.C) {
    54  	testcharms.UploadCharm(c, s.client, "trusty/mysql-42", "mysql")
    55  	testcharms.UploadCharm(c, s.client, "trusty/wordpress-47", "wordpress")
    56  	testcharms.UploadBundle(c, s.client, "bundle/wordpress-simple-1", "wordpress-simple")
    57  	output, err := runDeployCommand(c, "bundle/wordpress-simple")
    58  	c.Assert(err, jc.ErrorIsNil)
    59  	expectedOutput := `
    60  added charm cs:trusty/mysql-42
    61  service mysql deployed (charm cs:trusty/mysql-42 with the charm series "trusty")
    62  added charm cs:trusty/wordpress-47
    63  service wordpress deployed (charm cs:trusty/wordpress-47 with the charm series "trusty")
    64  related wordpress:db and mysql:server
    65  added mysql/0 unit to new machine
    66  added wordpress/0 unit to new machine
    67  deployment of bundle "cs:bundle/wordpress-simple-1" completed`
    68  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
    69  	s.assertCharmsUploaded(c, "cs:trusty/mysql-42", "cs:trusty/wordpress-47")
    70  	s.assertServicesDeployed(c, map[string]serviceInfo{
    71  		"mysql":     {charm: "cs:trusty/mysql-42"},
    72  		"wordpress": {charm: "cs:trusty/wordpress-47"},
    73  	})
    74  	s.assertRelationsEstablished(c, "wordpress:db mysql:server")
    75  	s.assertUnitsCreated(c, map[string]string{
    76  		"mysql/0":     "0",
    77  		"wordpress/0": "1",
    78  	})
    79  }
    80  
    81  func (s *BundleDeployCharmStoreSuite) TestDeployBundleWithTermsSuccess(c *gc.C) {
    82  	testcharms.UploadCharm(c, s.client, "trusty/terms1-17", "terms1")
    83  	testcharms.UploadCharm(c, s.client, "trusty/terms2-42", "terms2")
    84  	testcharms.UploadBundle(c, s.client, "bundle/terms-simple-1", "terms-simple")
    85  	output, err := runDeployCommand(c, "bundle/terms-simple")
    86  	c.Assert(err, jc.ErrorIsNil)
    87  	expectedOutput := `
    88  added charm cs:trusty/terms1-17
    89  service terms1 deployed (charm cs:trusty/terms1-17 with the charm series "trusty")
    90  added charm cs:trusty/terms2-42
    91  service terms2 deployed (charm cs:trusty/terms2-42 with the charm series "trusty")
    92  added terms1/0 unit to new machine
    93  added terms2/0 unit to new machine
    94  deployment of bundle "cs:bundle/terms-simple-1" completed`
    95  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
    96  	s.assertCharmsUploaded(c, "cs:trusty/terms1-17", "cs:trusty/terms2-42")
    97  	s.assertServicesDeployed(c, map[string]serviceInfo{
    98  		"terms1": {charm: "cs:trusty/terms1-17"},
    99  		"terms2": {charm: "cs:trusty/terms2-42"},
   100  	})
   101  	s.assertUnitsCreated(c, map[string]string{
   102  		"terms1/0": "0",
   103  		"terms2/0": "1",
   104  	})
   105  	c.Assert(s.termsString, gc.Not(gc.Equals), "")
   106  }
   107  
   108  func (s *BundleDeployCharmStoreSuite) TestDeployBundleStorage(c *gc.C) {
   109  	testcharms.UploadCharm(c, s.client, "trusty/mysql-42", "mysql-storage")
   110  	testcharms.UploadCharm(c, s.client, "trusty/wordpress-47", "wordpress")
   111  	testcharms.UploadBundle(c, s.client, "bundle/wordpress-with-mysql-storage-1", "wordpress-with-mysql-storage")
   112  	output, err := runDeployCommand(
   113  		c, "bundle/wordpress-with-mysql-storage",
   114  		"--storage", "mysql:logs=tmpfs,10G", // override logs
   115  	)
   116  	c.Assert(err, jc.ErrorIsNil)
   117  	expectedOutput := `
   118  added charm cs:trusty/mysql-42
   119  service mysql deployed (charm cs:trusty/mysql-42 with the charm series "trusty")
   120  added charm cs:trusty/wordpress-47
   121  service wordpress deployed (charm cs:trusty/wordpress-47 with the charm series "trusty")
   122  related wordpress:db and mysql:server
   123  added mysql/0 unit to new machine
   124  added wordpress/0 unit to new machine
   125  deployment of bundle "cs:bundle/wordpress-with-mysql-storage-1" completed`
   126  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
   127  	s.assertCharmsUploaded(c, "cs:trusty/mysql-42", "cs:trusty/wordpress-47")
   128  	s.assertServicesDeployed(c, map[string]serviceInfo{
   129  		"mysql": {
   130  			charm: "cs:trusty/mysql-42",
   131  			storage: map[string]state.StorageConstraints{
   132  				"data": state.StorageConstraints{Pool: "rootfs", Size: 50 * 1024, Count: 1},
   133  				"logs": state.StorageConstraints{Pool: "tmpfs", Size: 10 * 1024, Count: 1},
   134  			},
   135  		},
   136  		"wordpress": {charm: "cs:trusty/wordpress-47"},
   137  	})
   138  	s.assertRelationsEstablished(c, "wordpress:db mysql:server")
   139  	s.assertUnitsCreated(c, map[string]string{
   140  		"mysql/0":     "0",
   141  		"wordpress/0": "1",
   142  	})
   143  }
   144  
   145  func (s *BundleDeployCharmStoreSuite) TestDeployBundleEndpointBindingsSpaceMissing(c *gc.C) {
   146  	testcharms.UploadCharm(c, s.client, "trusty/mysql-42", "mysql")
   147  	testcharms.UploadCharm(c, s.client, "trusty/wordpress-extra-bindings-47", "wordpress-extra-bindings")
   148  	testcharms.UploadBundle(c, s.client, "bundle/wordpress-with-endpoint-bindings-1", "wordpress-with-endpoint-bindings")
   149  	output, err := runDeployCommand(c, "bundle/wordpress-with-endpoint-bindings")
   150  	c.Assert(err, gc.ErrorMatches,
   151  		"cannot deploy bundle: cannot deploy service \"mysql\": "+
   152  			"cannot add service \"mysql\": unknown space \"db\" not valid")
   153  	c.Assert(output, gc.Equals, "added charm cs:trusty/mysql-42")
   154  	s.assertCharmsUploaded(c, "cs:trusty/mysql-42")
   155  	s.assertServicesDeployed(c, map[string]serviceInfo{})
   156  	s.assertUnitsCreated(c, map[string]string{})
   157  }
   158  
   159  func (s *BundleDeployCharmStoreSuite) TestDeployBundleEndpointBindingsSuccess(c *gc.C) {
   160  	_, err := s.State.AddSpace("db", "", nil, false)
   161  	c.Assert(err, jc.ErrorIsNil)
   162  	_, err = s.State.AddSpace("public", "", nil, false)
   163  	c.Assert(err, jc.ErrorIsNil)
   164  
   165  	testcharms.UploadCharm(c, s.client, "trusty/mysql-42", "mysql")
   166  	testcharms.UploadCharm(c, s.client, "trusty/wordpress-extra-bindings-47", "wordpress-extra-bindings")
   167  	testcharms.UploadBundle(c, s.client, "bundle/wordpress-with-endpoint-bindings-1", "wordpress-with-endpoint-bindings")
   168  	output, err := runDeployCommand(c, "bundle/wordpress-with-endpoint-bindings")
   169  	c.Assert(err, jc.ErrorIsNil)
   170  	expectedOutput := `
   171  added charm cs:trusty/mysql-42
   172  service mysql deployed (charm cs:trusty/mysql-42 with the charm series "trusty")
   173  added charm cs:trusty/wordpress-extra-bindings-47
   174  service wordpress-extra-bindings deployed (charm cs:trusty/wordpress-extra-bindings-47 with the charm series "trusty")
   175  related wordpress-extra-bindings:db and mysql:server
   176  added mysql/0 unit to new machine
   177  added wordpress-extra-bindings/0 unit to new machine
   178  deployment of bundle "cs:bundle/wordpress-with-endpoint-bindings-1" completed`
   179  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
   180  	s.assertCharmsUploaded(c, "cs:trusty/mysql-42", "cs:trusty/wordpress-extra-bindings-47")
   181  
   182  	s.assertServicesDeployed(c, map[string]serviceInfo{
   183  		"mysql":                    {charm: "cs:trusty/mysql-42"},
   184  		"wordpress-extra-bindings": {charm: "cs:trusty/wordpress-extra-bindings-47"},
   185  	})
   186  	s.assertDeployedServiceBindings(c, map[string]serviceInfo{
   187  		"mysql": {
   188  			endpointBindings: map[string]string{"server": "db"},
   189  		},
   190  		"wordpress-extra-bindings": {
   191  			endpointBindings: map[string]string{
   192  				"cache":           "",
   193  				"url":             "public",
   194  				"logging-dir":     "",
   195  				"monitoring-port": "",
   196  				"db":              "db",
   197  				"cluster":         "",
   198  				"db-client":       "db",
   199  				"admin-api":       "public",
   200  				"foo-bar":         "",
   201  			},
   202  		},
   203  	})
   204  	s.assertRelationsEstablished(c, "wordpress-extra-bindings:cluster", "wordpress-extra-bindings:db mysql:server")
   205  	s.assertUnitsCreated(c, map[string]string{
   206  		"mysql/0":                    "0",
   207  		"wordpress-extra-bindings/0": "1",
   208  	})
   209  }
   210  
   211  func (s *BundleDeployCharmStoreSuite) TestDeployBundleTwice(c *gc.C) {
   212  	testcharms.UploadCharm(c, s.client, "trusty/mysql-42", "mysql")
   213  	testcharms.UploadCharm(c, s.client, "trusty/wordpress-47", "wordpress")
   214  	testcharms.UploadBundle(c, s.client, "bundle/wordpress-simple-1", "wordpress-simple")
   215  	_, err := runDeployCommand(c, "bundle/wordpress-simple")
   216  	c.Assert(err, jc.ErrorIsNil)
   217  	output, err := runDeployCommand(c, "bundle/wordpress-simple")
   218  	c.Assert(err, jc.ErrorIsNil)
   219  	expectedOutput := `
   220  added charm cs:trusty/mysql-42
   221  reusing service mysql (charm: cs:trusty/mysql-42)
   222  added charm cs:trusty/wordpress-47
   223  reusing service wordpress (charm: cs:trusty/wordpress-47)
   224  wordpress:db and mysql:server are already related
   225  avoid adding new units to service mysql: 1 unit already present
   226  avoid adding new units to service wordpress: 1 unit already present
   227  deployment of bundle "cs:bundle/wordpress-simple-1" completed`
   228  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
   229  	s.assertCharmsUploaded(c, "cs:trusty/mysql-42", "cs:trusty/wordpress-47")
   230  	s.assertServicesDeployed(c, map[string]serviceInfo{
   231  		"mysql":     {charm: "cs:trusty/mysql-42"},
   232  		"wordpress": {charm: "cs:trusty/wordpress-47"},
   233  	})
   234  	s.assertRelationsEstablished(c, "wordpress:db mysql:server")
   235  	s.assertUnitsCreated(c, map[string]string{
   236  		"mysql/0":     "0",
   237  		"wordpress/0": "1",
   238  	})
   239  }
   240  
   241  func (s *BundleDeployCharmStoreSuite) TestDeployBundleGatedCharm(c *gc.C) {
   242  	testcharms.UploadCharm(c, s.client, "trusty/mysql-42", "mysql")
   243  	url, _ := testcharms.UploadCharm(c, s.client, "trusty/wordpress-47", "wordpress")
   244  	s.changeReadPerm(c, url, clientUserName)
   245  	testcharms.UploadBundle(c, s.client, "bundle/wordpress-simple-1", "wordpress-simple")
   246  	_, err := runDeployCommand(c, "bundle/wordpress-simple")
   247  	c.Assert(err, jc.ErrorIsNil)
   248  	s.assertCharmsUploaded(c, "cs:trusty/mysql-42", "cs:trusty/wordpress-47")
   249  	s.assertServicesDeployed(c, map[string]serviceInfo{
   250  		"mysql":     {charm: "cs:trusty/mysql-42"},
   251  		"wordpress": {charm: "cs:trusty/wordpress-47"},
   252  	})
   253  }
   254  
   255  func (s *BundleDeployCharmStoreSuite) TestDeployBundleLocalPath(c *gc.C) {
   256  	dir := c.MkDir()
   257  	testcharms.Repo.ClonedDir(dir, "dummy")
   258  	path := filepath.Join(dir, "mybundle")
   259  	data := `
   260          series: trusty
   261          services:
   262              dummy:
   263                  charm: ./dummy
   264                  series: xenial
   265                  num_units: 1
   266      `
   267  	err := ioutil.WriteFile(path, []byte(data), 0644)
   268  	c.Assert(err, jc.ErrorIsNil)
   269  	output, err := runDeployCommand(c, path)
   270  	c.Assert(err, jc.ErrorIsNil)
   271  	expectedOutput := fmt.Sprintf(`
   272  added charm local:xenial/dummy-1
   273  service dummy deployed (charm local:xenial/dummy-1 with the series "xenial" defined by the bundle)
   274  added dummy/0 unit to new machine
   275  deployment of bundle %q completed`, path)
   276  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
   277  	s.assertCharmsUploaded(c, "local:xenial/dummy-1")
   278  	s.assertServicesDeployed(c, map[string]serviceInfo{
   279  		"dummy": {charm: "local:xenial/dummy-1"},
   280  	})
   281  }
   282  
   283  func (s *BundleDeployCharmStoreSuite) TestDeployBundleNoSeriesInCharmURL(c *gc.C) {
   284  	testcharms.UploadCharmMultiSeries(c, s.client, "~who/multi-series", "multi-series")
   285  	dir := c.MkDir()
   286  	testcharms.Repo.ClonedDir(dir, "dummy")
   287  	path := filepath.Join(dir, "mybundle")
   288  	data := `
   289          series: trusty
   290          services:
   291              dummy:
   292                  charm: cs:~who/multi-series
   293      `
   294  	err := ioutil.WriteFile(path, []byte(data), 0644)
   295  	c.Assert(err, jc.ErrorIsNil)
   296  	output, err := runDeployCommand(c, path)
   297  	c.Assert(err, jc.ErrorIsNil)
   298  	expectedOutput := fmt.Sprintf(`
   299  added charm cs:~who/multi-series-0
   300  service dummy deployed (charm cs:~who/multi-series-0 with the series "trusty" defined by the bundle)
   301  deployment of bundle %q completed`, path)
   302  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
   303  	s.assertCharmsUploaded(c, "cs:~who/multi-series-0")
   304  	s.assertServicesDeployed(c, map[string]serviceInfo{
   305  		"dummy": {charm: "cs:~who/multi-series-0"},
   306  	})
   307  }
   308  
   309  func (s *BundleDeployCharmStoreSuite) TestDeployBundleGatedCharmUnauthorized(c *gc.C) {
   310  	testcharms.UploadCharm(c, s.client, "trusty/mysql-42", "mysql")
   311  	url, _ := testcharms.UploadCharm(c, s.client, "trusty/wordpress-47", "wordpress")
   312  	s.changeReadPerm(c, url, "who")
   313  	testcharms.UploadBundle(c, s.client, "bundle/wordpress-simple-1", "wordpress-simple")
   314  	_, err := runDeployCommand(c, "bundle/wordpress-simple")
   315  	c.Assert(err, gc.ErrorMatches, `cannot deploy bundle: .*: unauthorized: access denied for user "client-username"`)
   316  }
   317  
   318  type BundleDeployCharmStoreSuite struct {
   319  	charmStoreSuite
   320  }
   321  
   322  var _ = gc.Suite(&BundleDeployCharmStoreSuite{})
   323  
   324  func (s *BundleDeployCharmStoreSuite) SetUpSuite(c *gc.C) {
   325  	s.charmStoreSuite.SetUpSuite(c)
   326  	s.PatchValue(&watcher.Period, 10*time.Millisecond)
   327  }
   328  
   329  func (s *BundleDeployCharmStoreSuite) Client() *csclient.Client {
   330  	return s.client
   331  }
   332  
   333  // DeployBundleYAML uses the given bundle content to create a bundle in the
   334  // local repository and then deploy it. It returns the bundle deployment output
   335  // and error.
   336  func (s *BundleDeployCharmStoreSuite) DeployBundleYAML(c *gc.C, content string) (string, error) {
   337  	bundlePath := filepath.Join(c.MkDir(), "example")
   338  	c.Assert(os.Mkdir(bundlePath, 0777), jc.ErrorIsNil)
   339  	defer os.RemoveAll(bundlePath)
   340  	err := ioutil.WriteFile(filepath.Join(bundlePath, "bundle.yaml"), []byte(content), 0644)
   341  	c.Assert(err, jc.ErrorIsNil)
   342  	err = ioutil.WriteFile(filepath.Join(bundlePath, "README.md"), []byte("README"), 0644)
   343  	c.Assert(err, jc.ErrorIsNil)
   344  	return runDeployCommand(c, bundlePath)
   345  }
   346  
   347  var deployBundleErrorsTests = []struct {
   348  	about   string
   349  	content string
   350  	err     string
   351  }{{
   352  	about: "local charm not found",
   353  	content: `
   354          services:
   355              mysql:
   356                  charm: ./mysql
   357                  num_units: 1
   358      `,
   359  	err: `the provided bundle has the following errors:
   360  charm path in service "mysql" does not exist: mysql`,
   361  }, {
   362  	about: "charm store charm not found",
   363  	content: `
   364          services:
   365              rails:
   366                  charm: trusty/rails-42
   367                  num_units: 1
   368      `,
   369  	err: `cannot deploy bundle: cannot resolve URL "trusty/rails-42": cannot resolve URL "cs:trusty/rails-42": charm not found`,
   370  }, {
   371  	about:   "invalid bundle content",
   372  	content: "!",
   373  	err:     `cannot unmarshal bundle data: YAML error: .*`,
   374  }, {
   375  	about: "invalid bundle data",
   376  	content: `
   377          services:
   378              mysql:
   379                  charm: mysql
   380                  num_units: -1
   381      `,
   382  	err: `the provided bundle has the following errors:
   383  negative number of units specified on service "mysql"`,
   384  }, {
   385  	about: "invalid constraints",
   386  	content: `
   387          services:
   388              mysql:
   389                  charm: mysql
   390                  num_units: 1
   391                  constraints: bad-wolf
   392      `,
   393  	err: `the provided bundle has the following errors:
   394  invalid constraints "bad-wolf" in service "mysql": malformed constraint "bad-wolf"`,
   395  }, {
   396  	about: "multiple bundle verification errors",
   397  	content: `
   398          services:
   399              mysql:
   400                  charm: mysql
   401                  num_units: -1
   402                  constraints: bad-wolf
   403      `,
   404  	err: `the provided bundle has the following errors:
   405  invalid constraints "bad-wolf" in service "mysql": malformed constraint "bad-wolf"
   406  negative number of units specified on service "mysql"`,
   407  }, {
   408  	about: "bundle inception",
   409  	content: `
   410          services:
   411              example:
   412                  charm: local:wordpress
   413                  num_units: 1
   414      `,
   415  	err: `cannot deploy bundle: cannot resolve URL "local:wordpress": unknown schema for charm URL "local:wordpress"`,
   416  }}
   417  
   418  func (s *BundleDeployCharmStoreSuite) TestDeployBundleErrors(c *gc.C) {
   419  	for i, test := range deployBundleErrorsTests {
   420  		c.Logf("test %d: %s", i, test.about)
   421  		_, err := s.DeployBundleYAML(c, test.content)
   422  		c.Check(err, gc.ErrorMatches, test.err)
   423  	}
   424  }
   425  
   426  func (s *BundleDeployCharmStoreSuite) TestDeployBundleInvalidOptions(c *gc.C) {
   427  	testcharms.UploadCharm(c, s.client, "trusty/wordpress-42", "wordpress")
   428  	_, err := s.DeployBundleYAML(c, `
   429          services:
   430              wp:
   431                  charm: trusty/wordpress-42
   432                  num_units: 1
   433                  options:
   434                      blog-title: 42
   435      `)
   436  	c.Assert(err, gc.ErrorMatches, `cannot deploy bundle: cannot deploy service "wp": option "blog-title" expected string, got 42`)
   437  }
   438  
   439  func (s *BundleDeployCharmStoreSuite) TestDeployBundleInvalidMachineContainerType(c *gc.C) {
   440  	testcharms.UploadCharm(c, s.client, "trusty/wordpress-42", "wordpress")
   441  	_, err := s.DeployBundleYAML(c, `
   442          services:
   443              wp:
   444                  charm: trusty/wordpress
   445                  num_units: 1
   446                  to: ["bad:1"]
   447          machines:
   448              1:
   449      `)
   450  	c.Assert(err, gc.ErrorMatches, `cannot deploy bundle: cannot create machine for holding wp unit: invalid container type "bad"`)
   451  }
   452  
   453  func (s *BundleDeployCharmStoreSuite) TestDeployBundleInvalidSeries(c *gc.C) {
   454  	testcharms.UploadCharm(c, s.client, "vivid/django-0", "dummy")
   455  	_, err := s.DeployBundleYAML(c, `
   456          services:
   457              django:
   458                  charm: vivid/django
   459                  num_units: 1
   460                  to:
   461                      - 1
   462          machines:
   463              1:
   464                  series: trusty
   465      `)
   466  	c.Assert(err, gc.ErrorMatches, `cannot deploy bundle: cannot add unit for service "django": adding new machine to host unit "django/0": cannot assign unit "django/0" to machine 0: series does not match`)
   467  }
   468  
   469  func (s *BundleDeployCharmStoreSuite) TestDeployBundleWatcherTimeout(c *gc.C) {
   470  	// Inject an "AllWatcher" that never delivers a result.
   471  	ch := make(chan struct{})
   472  	defer close(ch)
   473  	watcher := mockAllWatcher{
   474  		next: func() []multiwatcher.Delta {
   475  			<-ch
   476  			return nil
   477  		},
   478  	}
   479  	s.PatchValue(&watchAll, func(*api.Client) (allWatcher, error) {
   480  		return watcher, nil
   481  	})
   482  
   483  	testcharms.UploadCharm(c, s.client, "trusty/django-0", "dummy")
   484  	testcharms.UploadCharm(c, s.client, "trusty/wordpress-0", "wordpress")
   485  	s.PatchValue(&updateUnitStatusPeriod, 0*time.Second)
   486  	_, err := s.DeployBundleYAML(c, `
   487          services:
   488              django:
   489                  charm: django
   490                  num_units: 1
   491              wordpress:
   492                  charm: wordpress
   493                  num_units: 1
   494                  to: [django]
   495      `)
   496  	c.Assert(err, gc.ErrorMatches, `cannot deploy bundle: cannot retrieve placement for "wordpress" unit: cannot resolve machine: timeout while trying to get new changes from the watcher`)
   497  }
   498  
   499  func (s *BundleDeployCharmStoreSuite) TestDeployBundleLocalDeployment(c *gc.C) {
   500  	charmsPath := c.MkDir()
   501  	mysqlPath := testcharms.Repo.ClonedDirPath(charmsPath, "mysql")
   502  	wordpressPath := testcharms.Repo.ClonedDirPath(charmsPath, "wordpress")
   503  	output, err := s.DeployBundleYAML(c, fmt.Sprintf(`
   504          series: trusty
   505          services:
   506              wordpress:
   507                  charm: %s
   508                  num_units: 1
   509              mysql:
   510                  charm: %s
   511                  num_units: 2
   512          relations:
   513              - ["wordpress:db", "mysql:server"]
   514      `, wordpressPath, mysqlPath))
   515  	c.Assert(err, jc.ErrorIsNil)
   516  	expectedOutput := `
   517  added charm local:trusty/mysql-1
   518  service mysql deployed (charm local:trusty/mysql-1 with the series "trusty" defined by the bundle)
   519  added charm local:trusty/wordpress-3
   520  service wordpress deployed (charm local:trusty/wordpress-3 with the series "trusty" defined by the bundle)
   521  related wordpress:db and mysql:server
   522  added mysql/0 unit to new machine
   523  added mysql/1 unit to new machine
   524  added wordpress/0 unit to new machine
   525  deployment of bundle "local:bundle/example-0" completed`
   526  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
   527  	s.assertCharmsUploaded(c, "local:trusty/mysql-1", "local:trusty/wordpress-3")
   528  	s.assertServicesDeployed(c, map[string]serviceInfo{
   529  		"mysql":     {charm: "local:trusty/mysql-1"},
   530  		"wordpress": {charm: "local:trusty/wordpress-3"},
   531  	})
   532  	s.assertRelationsEstablished(c, "wordpress:db mysql:server")
   533  	s.assertUnitsCreated(c, map[string]string{
   534  		"mysql/0":     "0",
   535  		"mysql/1":     "1",
   536  		"wordpress/0": "2",
   537  	})
   538  }
   539  
   540  func (s *BundleDeployCharmStoreSuite) TestDeployBundleLocalAndCharmStoreCharms(c *gc.C) {
   541  	charmsPath := c.MkDir()
   542  	testcharms.UploadCharm(c, s.client, "trusty/wordpress-42", "wordpress")
   543  	mysqlPath := testcharms.Repo.ClonedDirPath(charmsPath, "mysql")
   544  	output, err := s.DeployBundleYAML(c, fmt.Sprintf(`
   545          series: trusty
   546          services:
   547              wordpress:
   548                  charm: trusty/wordpress-42
   549                  series: trusty
   550                  num_units: 1
   551              mysql:
   552                  charm: %s
   553                  num_units: 1
   554          relations:
   555              - ["wordpress:db", "mysql:server"]
   556      `, mysqlPath))
   557  	c.Assert(err, jc.ErrorIsNil)
   558  	expectedOutput := `
   559  added charm local:trusty/mysql-1
   560  service mysql deployed (charm local:trusty/mysql-1 with the series "trusty" defined by the bundle)
   561  added charm cs:trusty/wordpress-42
   562  service wordpress deployed (charm cs:trusty/wordpress-42 with the series "trusty" defined by the bundle)
   563  related wordpress:db and mysql:server
   564  added mysql/0 unit to new machine
   565  added wordpress/0 unit to new machine
   566  deployment of bundle "local:bundle/example-0" completed`
   567  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
   568  	s.assertCharmsUploaded(c, "local:trusty/mysql-1", "cs:trusty/wordpress-42")
   569  	s.assertServicesDeployed(c, map[string]serviceInfo{
   570  		"mysql":     {charm: "local:trusty/mysql-1"},
   571  		"wordpress": {charm: "cs:trusty/wordpress-42"},
   572  	})
   573  	s.assertRelationsEstablished(c, "wordpress:db mysql:server")
   574  	s.assertUnitsCreated(c, map[string]string{
   575  		"mysql/0":     "0",
   576  		"wordpress/0": "1",
   577  	})
   578  }
   579  
   580  func (s *BundleDeployCharmStoreSuite) TestDeployBundleServiceOptions(c *gc.C) {
   581  	testcharms.UploadCharm(c, s.client, "trusty/wordpress-42", "wordpress")
   582  	testcharms.UploadCharm(c, s.client, "precise/dummy-0", "dummy")
   583  	output, err := s.DeployBundleYAML(c, `
   584          services:
   585              wordpress:
   586                  charm: wordpress
   587                  num_units: 1
   588                  options:
   589                      blog-title: these are the voyages
   590              customized:
   591                  charm: precise/dummy-0
   592                  num_units: 1
   593                  options:
   594                      username: who
   595                      skill-level: 47
   596      `)
   597  	c.Assert(err, jc.ErrorIsNil)
   598  	expectedOutput := `
   599  added charm cs:precise/dummy-0
   600  service customized deployed (charm cs:precise/dummy-0 with the series "precise" defined by the bundle)
   601  added charm cs:trusty/wordpress-42
   602  service wordpress deployed (charm cs:trusty/wordpress-42 with the charm series "trusty")
   603  added customized/0 unit to new machine
   604  added wordpress/0 unit to new machine
   605  deployment of bundle "local:bundle/example-0" completed`
   606  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
   607  	s.assertCharmsUploaded(c, "cs:precise/dummy-0", "cs:trusty/wordpress-42")
   608  	s.assertServicesDeployed(c, map[string]serviceInfo{
   609  		"customized": {
   610  			charm:  "cs:precise/dummy-0",
   611  			config: charm.Settings{"username": "who", "skill-level": int64(47)},
   612  		},
   613  		"wordpress": {
   614  			charm:  "cs:trusty/wordpress-42",
   615  			config: charm.Settings{"blog-title": "these are the voyages"},
   616  		},
   617  	})
   618  	s.assertUnitsCreated(c, map[string]string{
   619  		"wordpress/0":  "1",
   620  		"customized/0": "0",
   621  	})
   622  }
   623  
   624  func (s *BundleDeployCharmStoreSuite) TestDeployBundleServiceConstrants(c *gc.C) {
   625  	testcharms.UploadCharm(c, s.client, "trusty/wordpress-42", "wordpress")
   626  	testcharms.UploadCharm(c, s.client, "precise/dummy-0", "dummy")
   627  	output, err := s.DeployBundleYAML(c, `
   628          services:
   629              wordpress:
   630                  charm: wordpress
   631                  constraints: mem=4G cpu-cores=2
   632              customized:
   633                  charm: precise/dummy-0
   634                  num_units: 1
   635                  constraints: arch=i386
   636      `)
   637  	c.Assert(err, jc.ErrorIsNil)
   638  	expectedOutput := `
   639  added charm cs:precise/dummy-0
   640  service customized deployed (charm cs:precise/dummy-0 with the series "precise" defined by the bundle)
   641  added charm cs:trusty/wordpress-42
   642  service wordpress deployed (charm cs:trusty/wordpress-42 with the charm series "trusty")
   643  added customized/0 unit to new machine
   644  deployment of bundle "local:bundle/example-0" completed`
   645  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
   646  	s.assertCharmsUploaded(c, "cs:precise/dummy-0", "cs:trusty/wordpress-42")
   647  	s.assertServicesDeployed(c, map[string]serviceInfo{
   648  		"customized": {
   649  			charm:       "cs:precise/dummy-0",
   650  			constraints: constraints.MustParse("arch=i386"),
   651  		},
   652  		"wordpress": {
   653  			charm:       "cs:trusty/wordpress-42",
   654  			constraints: constraints.MustParse("mem=4G cpu-cores=2"),
   655  		},
   656  	})
   657  	s.assertUnitsCreated(c, map[string]string{
   658  		"customized/0": "0",
   659  	})
   660  }
   661  
   662  func (s *BundleDeployCharmStoreSuite) TestDeployBundleServiceUpgrade(c *gc.C) {
   663  	testcharms.UploadCharm(c, s.client, "trusty/wordpress-42", "wordpress")
   664  	testcharms.UploadCharm(c, s.client, "vivid/upgrade-1", "upgrade1")
   665  	testcharms.UploadCharm(c, s.client, "vivid/upgrade-2", "upgrade2")
   666  
   667  	// First deploy the bundle.
   668  	output, err := s.DeployBundleYAML(c, `
   669          services:
   670              wordpress:
   671                  charm: wordpress
   672                  num_units: 1
   673                  options:
   674                      blog-title: these are the voyages
   675                  constraints: spaces=final,frontiers mem=8000M
   676              up:
   677                  charm: vivid/upgrade-1
   678                  num_units: 1
   679      `)
   680  	c.Assert(err, jc.ErrorIsNil)
   681  	expectedOutput := `
   682  added charm cs:vivid/upgrade-1
   683  service up deployed (charm cs:vivid/upgrade-1 with the series "vivid" defined by the bundle)
   684  added charm cs:trusty/wordpress-42
   685  service wordpress deployed (charm cs:trusty/wordpress-42 with the charm series "trusty")
   686  added up/0 unit to new machine
   687  added wordpress/0 unit to new machine
   688  deployment of bundle "local:bundle/example-0" completed`
   689  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
   690  	s.assertCharmsUploaded(c, "cs:vivid/upgrade-1", "cs:trusty/wordpress-42")
   691  
   692  	// Then deploy a new bundle with modified charm revision and options.
   693  	output, err = s.DeployBundleYAML(c, `
   694          services:
   695              wordpress:
   696                  charm: wordpress
   697                  num_units: 1
   698                  options:
   699                      blog-title: new title
   700                  constraints: spaces=new cpu-cores=8
   701              up:
   702                  charm: vivid/upgrade-2
   703                  num_units: 1
   704      `)
   705  	c.Assert(err, jc.ErrorIsNil)
   706  	expectedOutput = `
   707  added charm cs:vivid/upgrade-2
   708  upgraded charm for existing service up (from cs:vivid/upgrade-1 to cs:vivid/upgrade-2)
   709  added charm cs:trusty/wordpress-42
   710  reusing service wordpress (charm: cs:trusty/wordpress-42)
   711  configuration updated for service wordpress
   712  constraints applied for service wordpress
   713  avoid adding new units to service up: 1 unit already present
   714  avoid adding new units to service wordpress: 1 unit already present
   715  deployment of bundle "local:bundle/example-0" completed`
   716  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
   717  	s.assertCharmsUploaded(c, "cs:vivid/upgrade-1", "cs:vivid/upgrade-2", "cs:trusty/wordpress-42")
   718  	s.assertServicesDeployed(c, map[string]serviceInfo{
   719  		"up": {charm: "cs:vivid/upgrade-2"},
   720  		"wordpress": {
   721  			charm:       "cs:trusty/wordpress-42",
   722  			config:      charm.Settings{"blog-title": "new title"},
   723  			constraints: constraints.MustParse("spaces=new cpu-cores=8"),
   724  		},
   725  	})
   726  	s.assertUnitsCreated(c, map[string]string{
   727  		"up/0":        "0",
   728  		"wordpress/0": "1",
   729  	})
   730  }
   731  
   732  func (s *BundleDeployCharmStoreSuite) TestDeployBundleExpose(c *gc.C) {
   733  	testcharms.UploadCharm(c, s.client, "trusty/wordpress-42", "wordpress")
   734  	content := `
   735          services:
   736              wordpress:
   737                  charm: wordpress
   738                  num_units: 1
   739                  expose: true
   740      `
   741  	expectedServices := map[string]serviceInfo{
   742  		"wordpress": {
   743  			charm:   "cs:trusty/wordpress-42",
   744  			exposed: true,
   745  		},
   746  	}
   747  
   748  	// First deploy the bundle.
   749  	output, err := s.DeployBundleYAML(c, content)
   750  	c.Assert(err, jc.ErrorIsNil)
   751  	expectedOutput := `
   752  added charm cs:trusty/wordpress-42
   753  service wordpress deployed (charm cs:trusty/wordpress-42 with the charm series "trusty")
   754  service wordpress exposed
   755  added wordpress/0 unit to new machine
   756  deployment of bundle "local:bundle/example-0" completed`
   757  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
   758  	s.assertServicesDeployed(c, expectedServices)
   759  
   760  	// Then deploy the same bundle again: no error is produced when the service
   761  	// is exposed again.
   762  	output, err = s.DeployBundleYAML(c, content)
   763  	c.Assert(err, jc.ErrorIsNil)
   764  	expectedOutput = `
   765  added charm cs:trusty/wordpress-42
   766  reusing service wordpress (charm: cs:trusty/wordpress-42)
   767  service wordpress exposed
   768  avoid adding new units to service wordpress: 1 unit already present
   769  deployment of bundle "local:bundle/example-0" completed`
   770  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
   771  	s.assertServicesDeployed(c, expectedServices)
   772  
   773  	// Then deploy a bundle with the service unexposed, and check that the
   774  	// service is not unexposed.
   775  	output, err = s.DeployBundleYAML(c, `
   776          services:
   777              wordpress:
   778                  charm: wordpress
   779                  num_units: 1
   780                  expose: false
   781      `)
   782  	c.Assert(err, jc.ErrorIsNil)
   783  	expectedOutput = `
   784  added charm cs:trusty/wordpress-42
   785  reusing service wordpress (charm: cs:trusty/wordpress-42)
   786  avoid adding new units to service wordpress: 1 unit already present
   787  deployment of bundle "local:bundle/example-0" completed`
   788  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
   789  	s.assertServicesDeployed(c, expectedServices)
   790  }
   791  
   792  func (s *BundleDeployCharmStoreSuite) TestDeployBundleServiceUpgradeFailure(c *gc.C) {
   793  	s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   794  
   795  	// Try upgrading to a different charm name.
   796  	testcharms.UploadCharm(c, s.client, "trusty/incompatible-42", "wordpress")
   797  	_, err := s.DeployBundleYAML(c, `
   798          services:
   799              wordpress:
   800                  charm: trusty/incompatible-42
   801                  num_units: 1
   802      `)
   803  	c.Assert(err, gc.ErrorMatches, `cannot deploy bundle: cannot upgrade service "wordpress": bundle charm "cs:trusty/incompatible-42" is incompatible with existing charm "local:quantal/wordpress-3"`)
   804  
   805  	// Try upgrading to a different series.
   806  	// Note that this test comes before the next one because
   807  	// otherwise we can't resolve the charm URL because the charm's
   808  	// "base entity" is not marked as promulgated so the query by
   809  	// promulgated will find it.
   810  	testcharms.UploadCharm(c, s.client, "vivid/wordpress-42", "wordpress")
   811  	_, err = s.DeployBundleYAML(c, `
   812          services:
   813              wordpress:
   814                  charm: vivid/wordpress
   815                  num_units: 1
   816      `)
   817  	c.Assert(err, gc.ErrorMatches, `cannot deploy bundle: cannot upgrade service "wordpress": bundle charm "cs:vivid/wordpress-42" is incompatible with existing charm "local:quantal/wordpress-3"`)
   818  
   819  	// Try upgrading to a different user.
   820  	testcharms.UploadCharm(c, s.client, "~who/trusty/wordpress-42", "wordpress")
   821  	_, err = s.DeployBundleYAML(c, `
   822          services:
   823              wordpress:
   824                  charm: cs:~who/trusty/wordpress-42
   825                  num_units: 1
   826      `)
   827  	c.Assert(err, gc.ErrorMatches, `cannot deploy bundle: cannot upgrade service "wordpress": bundle charm "cs:~who/trusty/wordpress-42" is incompatible with existing charm "local:quantal/wordpress-3"`)
   828  }
   829  
   830  func (s *BundleDeployCharmStoreSuite) TestDeployBundleMultipleRelations(c *gc.C) {
   831  	testcharms.UploadCharm(c, s.client, "trusty/wordpress-0", "wordpress")
   832  	testcharms.UploadCharm(c, s.client, "trusty/mysql-1", "mysql")
   833  	testcharms.UploadCharm(c, s.client, "trusty/postgres-2", "mysql")
   834  	testcharms.UploadCharm(c, s.client, "trusty/varnish-3", "varnish")
   835  	output, err := s.DeployBundleYAML(c, `
   836          services:
   837              wp:
   838                  charm: wordpress
   839                  num_units: 1
   840              mysql:
   841                  charm: mysql
   842                  num_units: 1
   843              pgres:
   844                  charm: trusty/postgres-2
   845                  num_units: 1
   846              varnish:
   847                  charm: trusty/varnish
   848                  num_units: 1
   849          relations:
   850              - ["wp:db", "mysql:server"]
   851              - ["wp:db", "pgres:server"]
   852              - ["varnish:webcache", "wp:cache"]
   853      `)
   854  	c.Assert(err, jc.ErrorIsNil)
   855  	expectedOutput := `
   856  added charm cs:trusty/mysql-1
   857  service mysql deployed (charm cs:trusty/mysql-1 with the charm series "trusty")
   858  added charm cs:trusty/postgres-2
   859  service pgres deployed (charm cs:trusty/postgres-2 with the series "trusty" defined by the bundle)
   860  added charm cs:trusty/varnish-3
   861  service varnish deployed (charm cs:trusty/varnish-3 with the series "trusty" defined by the bundle)
   862  added charm cs:trusty/wordpress-0
   863  service wp deployed (charm cs:trusty/wordpress-0 with the charm series "trusty")
   864  related wp:db and mysql:server
   865  related wp:db and pgres:server
   866  related varnish:webcache and wp:cache
   867  added mysql/0 unit to new machine
   868  added pgres/0 unit to new machine
   869  added varnish/0 unit to new machine
   870  added wp/0 unit to new machine
   871  deployment of bundle "local:bundle/example-0" completed`
   872  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
   873  	s.assertRelationsEstablished(c, "wp:db mysql:server", "wp:db pgres:server", "wp:cache varnish:webcache")
   874  	s.assertUnitsCreated(c, map[string]string{
   875  		"mysql/0":   "0",
   876  		"pgres/0":   "1",
   877  		"varnish/0": "2",
   878  		"wp/0":      "3",
   879  	})
   880  }
   881  
   882  func (s *BundleDeployCharmStoreSuite) TestDeployBundleNewRelations(c *gc.C) {
   883  	testcharms.UploadCharm(c, s.client, "trusty/wordpress-0", "wordpress")
   884  	testcharms.UploadCharm(c, s.client, "trusty/mysql-1", "mysql")
   885  	testcharms.UploadCharm(c, s.client, "trusty/postgres-2", "mysql")
   886  	testcharms.UploadCharm(c, s.client, "trusty/varnish-3", "varnish")
   887  	_, err := s.DeployBundleYAML(c, `
   888          services:
   889              wp:
   890                  charm: wordpress
   891                  num_units: 1
   892              mysql:
   893                  charm: mysql
   894                  num_units: 1
   895              varnish:
   896                  charm: trusty/varnish
   897                  num_units: 1
   898          relations:
   899              - ["wp:db", "mysql:server"]
   900      `)
   901  	c.Assert(err, jc.ErrorIsNil)
   902  	output, err := s.DeployBundleYAML(c, `
   903          services:
   904              wp:
   905                  charm: wordpress
   906                  num_units: 1
   907              mysql:
   908                  charm: mysql
   909                  num_units: 1
   910              varnish:
   911                  charm: trusty/varnish
   912                  num_units: 1
   913          relations:
   914              - ["wp:db", "mysql:server"]
   915              - ["varnish:webcache", "wp:cache"]
   916      `)
   917  	c.Assert(err, jc.ErrorIsNil)
   918  	expectedOutput := `
   919  added charm cs:trusty/mysql-1
   920  reusing service mysql (charm: cs:trusty/mysql-1)
   921  added charm cs:trusty/varnish-3
   922  reusing service varnish (charm: cs:trusty/varnish-3)
   923  added charm cs:trusty/wordpress-0
   924  reusing service wp (charm: cs:trusty/wordpress-0)
   925  wp:db and mysql:server are already related
   926  related varnish:webcache and wp:cache
   927  avoid adding new units to service mysql: 1 unit already present
   928  avoid adding new units to service varnish: 1 unit already present
   929  avoid adding new units to service wp: 1 unit already present
   930  deployment of bundle "local:bundle/example-0" completed`
   931  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
   932  	s.assertRelationsEstablished(c, "wp:db mysql:server", "wp:cache varnish:webcache")
   933  	s.assertUnitsCreated(c, map[string]string{
   934  		"mysql/0":   "0",
   935  		"varnish/0": "1",
   936  		"wp/0":      "2",
   937  	})
   938  }
   939  
   940  func (s *BundleDeployCharmStoreSuite) TestDeployBundleMachinesUnitsPlacement(c *gc.C) {
   941  	testcharms.UploadCharm(c, s.client, "trusty/wordpress-0", "wordpress")
   942  	testcharms.UploadCharm(c, s.client, "trusty/mysql-2", "mysql")
   943  	content := `
   944          services:
   945              wp:
   946                  charm: cs:trusty/wordpress-0
   947                  num_units: 2
   948                  to:
   949                      - 1
   950                      - lxc:2
   951                  options:
   952                      blog-title: these are the voyages
   953              sql:
   954                  charm: cs:trusty/mysql
   955                  num_units: 2
   956                  to:
   957                      - lxc:wp/0
   958                      - new
   959          machines:
   960              1:
   961                  series: trusty
   962              2:
   963      `
   964  	output, err := s.DeployBundleYAML(c, content)
   965  	c.Assert(err, jc.ErrorIsNil)
   966  	expectedOutput := `
   967  added charm cs:trusty/mysql-2
   968  service sql deployed (charm cs:trusty/mysql-2 with the series "trusty" defined by the bundle)
   969  added charm cs:trusty/wordpress-0
   970  service wp deployed (charm cs:trusty/wordpress-0 with the series "trusty" defined by the bundle)
   971  created new machine 0 for holding wp unit
   972  created new machine 1 for holding wp unit
   973  added wp/0 unit to machine 0
   974  created 0/lxc/0 container in machine 0 for holding sql unit
   975  created new machine 2 for holding sql unit
   976  created 1/lxc/0 container in machine 1 for holding wp unit
   977  added sql/0 unit to machine 0/lxc/0
   978  added sql/1 unit to machine 2
   979  added wp/1 unit to machine 1/lxc/0
   980  deployment of bundle "local:bundle/example-0" completed`
   981  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
   982  	s.assertServicesDeployed(c, map[string]serviceInfo{
   983  		"sql": {charm: "cs:trusty/mysql-2"},
   984  		"wp": {
   985  			charm:  "cs:trusty/wordpress-0",
   986  			config: charm.Settings{"blog-title": "these are the voyages"},
   987  		},
   988  	})
   989  	s.assertRelationsEstablished(c)
   990  
   991  	// We explicitly pull out the map creation in the call to
   992  	// s.assertUnitsCreated() and create the map as a new variable
   993  	// because this /appears/ to tickle a bug on ppc64le using
   994  	// gccgo-4.9; the bug is that the map on the receiving side
   995  	// does not have the same contents as it does here - which is
   996  	// weird because that pattern is used elsewhere in this
   997  	// function. And just pulling the map instantiation out of the
   998  	// call is not enough; we need to do something benign with the
   999  	// variable to keep a reference beyond the call to the
  1000  	// s.assertUnitsCreated(). I have to chosen to delete a
  1001  	// non-existent key. This problem does not occur on amd64
  1002  	// using gc or gccgo-4.9. Nor does it happen using go1.6 on
  1003  	// ppc64. Once we switch to go1.6 across the board this change
  1004  	// should be reverted. See http://pad.lv/1556116.
  1005  	expectedUnits := map[string]string{
  1006  		"sql/0": "0/lxc/0",
  1007  		"sql/1": "2",
  1008  		"wp/0":  "0",
  1009  		"wp/1":  "1/lxc/0",
  1010  	}
  1011  	s.assertUnitsCreated(c, expectedUnits)
  1012  	delete(expectedUnits, "non-existent")
  1013  
  1014  	// Redeploy the same bundle again.
  1015  	output, err = s.DeployBundleYAML(c, content)
  1016  	c.Assert(err, jc.ErrorIsNil)
  1017  	expectedOutput = `
  1018  added charm cs:trusty/mysql-2
  1019  reusing service sql (charm: cs:trusty/mysql-2)
  1020  added charm cs:trusty/wordpress-0
  1021  reusing service wp (charm: cs:trusty/wordpress-0)
  1022  configuration updated for service wp
  1023  avoid creating other machines to host wp units
  1024  avoid adding new units to service wp: 2 units already present
  1025  avoid creating other machines to host sql units
  1026  avoid adding new units to service sql: 2 units already present
  1027  deployment of bundle "local:bundle/example-0" completed`
  1028  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
  1029  	s.assertUnitsCreated(c, map[string]string{
  1030  		"sql/0": "0/lxc/0",
  1031  		"sql/1": "2",
  1032  		"wp/0":  "0",
  1033  		"wp/1":  "1/lxc/0",
  1034  	})
  1035  }
  1036  
  1037  func (s *BundleDeployCharmStoreSuite) TestDeployBundleMachineAttributes(c *gc.C) {
  1038  	testcharms.UploadCharm(c, s.client, "trusty/django-42", "dummy")
  1039  	output, err := s.DeployBundleYAML(c, `
  1040          services:
  1041              django:
  1042                  charm: cs:trusty/django-42
  1043                  num_units: 2
  1044                  to:
  1045                      - 1
  1046                      - new
  1047          machines:
  1048              1:
  1049                  series: trusty
  1050                  constraints: "cpu-cores=4 mem=4G"
  1051                  annotations:
  1052                      foo: bar
  1053      `)
  1054  	c.Assert(err, jc.ErrorIsNil)
  1055  	expectedOutput := `
  1056  added charm cs:trusty/django-42
  1057  service django deployed (charm cs:trusty/django-42 with the series "trusty" defined by the bundle)
  1058  created new machine 0 for holding django unit
  1059  annotations set for machine 0
  1060  added django/0 unit to machine 0
  1061  created new machine 1 for holding django unit
  1062  added django/1 unit to machine 1
  1063  deployment of bundle "local:bundle/example-0" completed`
  1064  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
  1065  	s.assertServicesDeployed(c, map[string]serviceInfo{
  1066  		"django": {charm: "cs:trusty/django-42"},
  1067  	})
  1068  	s.assertRelationsEstablished(c)
  1069  	s.assertUnitsCreated(c, map[string]string{
  1070  		"django/0": "0",
  1071  		"django/1": "1",
  1072  	})
  1073  	m, err := s.State.Machine("0")
  1074  	c.Assert(err, jc.ErrorIsNil)
  1075  	c.Assert(m.Series(), gc.Equals, "trusty")
  1076  	cons, err := m.Constraints()
  1077  	c.Assert(err, jc.ErrorIsNil)
  1078  	expectedCons, err := constraints.Parse("cpu-cores=4 mem=4G")
  1079  	c.Assert(err, jc.ErrorIsNil)
  1080  	c.Assert(cons, jc.DeepEquals, expectedCons)
  1081  	ann, err := s.State.Annotations(m)
  1082  	c.Assert(err, jc.ErrorIsNil)
  1083  	c.Assert(ann, jc.DeepEquals, map[string]string{"foo": "bar"})
  1084  }
  1085  
  1086  func (s *BundleDeployCharmStoreSuite) TestDeployBundleTwiceScaleUp(c *gc.C) {
  1087  	testcharms.UploadCharm(c, s.client, "trusty/django-42", "dummy")
  1088  	_, err := s.DeployBundleYAML(c, `
  1089          services:
  1090              django:
  1091                  charm: cs:trusty/django-42
  1092                  num_units: 2
  1093      `)
  1094  	c.Assert(err, jc.ErrorIsNil)
  1095  	output, err := s.DeployBundleYAML(c, `
  1096          services:
  1097              django:
  1098                  charm: cs:trusty/django-42
  1099                  num_units: 5
  1100      `)
  1101  	c.Assert(err, jc.ErrorIsNil)
  1102  	expectedOutput := `
  1103  added charm cs:trusty/django-42
  1104  reusing service django (charm: cs:trusty/django-42)
  1105  added django/2 unit to new machine
  1106  added django/3 unit to new machine
  1107  added django/4 unit to new machine
  1108  avoid adding new units to service django: 5 units already present
  1109  deployment of bundle "local:bundle/example-0" completed`
  1110  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
  1111  	s.assertUnitsCreated(c, map[string]string{
  1112  		"django/0": "0",
  1113  		"django/1": "1",
  1114  		"django/2": "2",
  1115  		"django/3": "3",
  1116  		"django/4": "4",
  1117  	})
  1118  }
  1119  
  1120  func (s *BundleDeployCharmStoreSuite) TestDeployBundleUnitPlacedInService(c *gc.C) {
  1121  	testcharms.UploadCharm(c, s.client, "trusty/django-42", "dummy")
  1122  	testcharms.UploadCharm(c, s.client, "trusty/wordpress-0", "wordpress")
  1123  	output, err := s.DeployBundleYAML(c, `
  1124          services:
  1125              wordpress:
  1126                  charm: wordpress
  1127                  num_units: 3
  1128              django:
  1129                  charm: cs:trusty/django-42
  1130                  num_units: 2
  1131                  to: [wordpress]
  1132      `)
  1133  	c.Assert(err, jc.ErrorIsNil)
  1134  	expectedOutput := `
  1135  added charm cs:trusty/django-42
  1136  service django deployed (charm cs:trusty/django-42 with the series "trusty" defined by the bundle)
  1137  added charm cs:trusty/wordpress-0
  1138  service wordpress deployed (charm cs:trusty/wordpress-0 with the charm series "trusty")
  1139  added wordpress/0 unit to new machine
  1140  added wordpress/1 unit to new machine
  1141  added wordpress/2 unit to new machine
  1142  added django/0 unit to machine 0
  1143  added django/1 unit to machine 1
  1144  deployment of bundle "local:bundle/example-0" completed`
  1145  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
  1146  	s.assertUnitsCreated(c, map[string]string{
  1147  		"django/0":    "0",
  1148  		"django/1":    "1",
  1149  		"wordpress/0": "0",
  1150  		"wordpress/1": "1",
  1151  		"wordpress/2": "2",
  1152  	})
  1153  }
  1154  
  1155  func (s *BundleDeployCharmStoreSuite) TestDeployBundleUnitColocationWithUnit(c *gc.C) {
  1156  	testcharms.UploadCharm(c, s.client, "trusty/django-42", "dummy")
  1157  	testcharms.UploadCharm(c, s.client, "trusty/mem-47", "dummy")
  1158  	testcharms.UploadCharm(c, s.client, "trusty/rails-0", "dummy")
  1159  	output, err := s.DeployBundleYAML(c, `
  1160          services:
  1161              memcached:
  1162                  charm: cs:trusty/mem-47
  1163                  num_units: 3
  1164                  to: [1, new]
  1165              django:
  1166                  charm: cs:trusty/django-42
  1167                  num_units: 5
  1168                  to:
  1169                      - memcached/0
  1170                      - lxc:memcached/1
  1171                      - lxc:memcached/2
  1172                      - kvm:ror
  1173              ror:
  1174                  charm: rails
  1175                  num_units: 2
  1176                  to:
  1177                      - new
  1178                      - 1
  1179          machines:
  1180              1:
  1181                  series: trusty
  1182      `)
  1183  	c.Assert(err, jc.ErrorIsNil)
  1184  	expectedOutput := `
  1185  added charm cs:trusty/django-42
  1186  service django deployed (charm cs:trusty/django-42 with the series "trusty" defined by the bundle)
  1187  added charm cs:trusty/mem-47
  1188  service memcached deployed (charm cs:trusty/mem-47 with the series "trusty" defined by the bundle)
  1189  added charm cs:trusty/rails-0
  1190  service ror deployed (charm cs:trusty/rails-0 with the charm series "trusty")
  1191  created new machine 0 for holding memcached and ror units
  1192  added memcached/0 unit to machine 0
  1193  added ror/0 unit to machine 0
  1194  created 0/kvm/0 container in machine 0 for holding django unit
  1195  created new machine 1 for holding memcached unit
  1196  created new machine 2 for holding memcached unit
  1197  created new machine 3 for holding ror unit
  1198  added django/0 unit to machine 0
  1199  added django/1 unit to machine 0/kvm/0
  1200  added memcached/1 unit to machine 1
  1201  added memcached/2 unit to machine 2
  1202  added ror/1 unit to machine 3
  1203  created 1/lxc/0 container in machine 1 for holding django unit
  1204  created 2/lxc/0 container in machine 2 for holding django unit
  1205  created 3/kvm/0 container in machine 3 for holding django unit
  1206  added django/2 unit to machine 1/lxc/0
  1207  added django/3 unit to machine 2/lxc/0
  1208  added django/4 unit to machine 3/kvm/0
  1209  deployment of bundle "local:bundle/example-0" completed`
  1210  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
  1211  	s.assertUnitsCreated(c, map[string]string{
  1212  		"django/0":    "0",
  1213  		"django/1":    "0/kvm/0",
  1214  		"django/2":    "1/lxc/0",
  1215  		"django/3":    "2/lxc/0",
  1216  		"django/4":    "3/kvm/0",
  1217  		"memcached/0": "0",
  1218  		"memcached/1": "1",
  1219  		"memcached/2": "2",
  1220  		"ror/0":       "0",
  1221  		"ror/1":       "3",
  1222  	})
  1223  }
  1224  
  1225  func (s *BundleDeployCharmStoreSuite) TestDeployBundleUnitPlacedToMachines(c *gc.C) {
  1226  	testcharms.UploadCharm(c, s.client, "trusty/django-42", "dummy")
  1227  	output, err := s.DeployBundleYAML(c, `
  1228          services:
  1229              django:
  1230                  charm: cs:django
  1231                  num_units: 7
  1232                  to:
  1233                      - new
  1234                      - 4
  1235                      - kvm:8
  1236                      - lxc:4
  1237                      - lxc:4
  1238                      - lxc:new
  1239          machines:
  1240              4:
  1241              8:
  1242      `)
  1243  	c.Assert(err, jc.ErrorIsNil)
  1244  	expectedOutput := `
  1245  added charm cs:trusty/django-42
  1246  service django deployed (charm cs:trusty/django-42 with the charm series "trusty")
  1247  created new machine 0 for holding django unit
  1248  created new machine 1 for holding django unit
  1249  added django/0 unit to machine 0
  1250  created new machine 2 for holding django unit
  1251  created 1/kvm/0 container in machine 1 for holding django unit
  1252  created 0/lxc/0 container in machine 0 for holding django unit
  1253  created 0/lxc/1 container in machine 0 for holding django unit
  1254  created 3/lxc/0 container in new machine for holding django unit
  1255  created 4/lxc/0 container in new machine for holding django unit
  1256  added django/1 unit to machine 2
  1257  added django/2 unit to machine 1/kvm/0
  1258  added django/3 unit to machine 0/lxc/0
  1259  added django/4 unit to machine 0/lxc/1
  1260  added django/5 unit to machine 3/lxc/0
  1261  added django/6 unit to machine 4/lxc/0
  1262  deployment of bundle "local:bundle/example-0" completed`
  1263  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
  1264  	s.assertUnitsCreated(c, map[string]string{
  1265  		"django/0": "0",       // Machine "4" in the bundle.
  1266  		"django/1": "2",       // Machine "new" in the bundle.
  1267  		"django/2": "1/kvm/0", // The KVM container in bundle machine "8".
  1268  		"django/3": "0/lxc/0", // First LXC container in bundle machine "4".
  1269  		"django/4": "0/lxc/1", // Second LXC container in bundle machine "4".
  1270  		"django/5": "3/lxc/0", // First LXC in new machine.
  1271  		"django/6": "4/lxc/0", // Second LXC in new machine.
  1272  	})
  1273  }
  1274  
  1275  func (s *BundleDeployCharmStoreSuite) TestDeployBundleMassiveUnitColocation(c *gc.C) {
  1276  	testcharms.UploadCharm(c, s.client, "trusty/django-42", "dummy")
  1277  	testcharms.UploadCharm(c, s.client, "trusty/mem-47", "dummy")
  1278  	testcharms.UploadCharm(c, s.client, "trusty/rails-0", "dummy")
  1279  	output, err := s.DeployBundleYAML(c, `
  1280          services:
  1281              memcached:
  1282                  charm: cs:trusty/mem-47
  1283                  num_units: 3
  1284                  to: [1, 2, 3]
  1285              django:
  1286                  charm: cs:trusty/django-42
  1287                  num_units: 4
  1288                  to:
  1289                      - 1
  1290                      - lxc:memcached
  1291              ror:
  1292                  charm: rails
  1293                  num_units: 3
  1294                  to:
  1295                      - 1
  1296                      - kvm:3
  1297          machines:
  1298              1:
  1299              2:
  1300              3:
  1301      `)
  1302  	c.Assert(err, jc.ErrorIsNil)
  1303  	expectedOutput := `
  1304  added charm cs:trusty/django-42
  1305  service django deployed (charm cs:trusty/django-42 with the series "trusty" defined by the bundle)
  1306  added charm cs:trusty/mem-47
  1307  service memcached deployed (charm cs:trusty/mem-47 with the series "trusty" defined by the bundle)
  1308  added charm cs:trusty/rails-0
  1309  service ror deployed (charm cs:trusty/rails-0 with the charm series "trusty")
  1310  created new machine 0 for holding django, memcached and ror units
  1311  created new machine 1 for holding memcached unit
  1312  created new machine 2 for holding memcached and ror units
  1313  added django/0 unit to machine 0
  1314  added memcached/0 unit to machine 0
  1315  added memcached/1 unit to machine 1
  1316  added memcached/2 unit to machine 2
  1317  added ror/0 unit to machine 0
  1318  created 0/lxc/0 container in machine 0 for holding django unit
  1319  created 1/lxc/0 container in machine 1 for holding django unit
  1320  created 2/lxc/0 container in machine 2 for holding django unit
  1321  created 2/kvm/0 container in machine 2 for holding ror unit
  1322  created 2/kvm/1 container in machine 2 for holding ror unit
  1323  added django/1 unit to machine 0/lxc/0
  1324  added django/2 unit to machine 1/lxc/0
  1325  added django/3 unit to machine 2/lxc/0
  1326  added ror/1 unit to machine 2/kvm/0
  1327  added ror/2 unit to machine 2/kvm/1
  1328  deployment of bundle "local:bundle/example-0" completed`
  1329  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
  1330  	s.assertUnitsCreated(c, map[string]string{
  1331  		"django/0":    "0",
  1332  		"django/1":    "0/lxc/0",
  1333  		"django/2":    "1/lxc/0",
  1334  		"django/3":    "2/lxc/0",
  1335  		"memcached/0": "0",
  1336  		"memcached/1": "1",
  1337  		"memcached/2": "2",
  1338  		"ror/0":       "0",
  1339  		"ror/1":       "2/kvm/0",
  1340  		"ror/2":       "2/kvm/1",
  1341  	})
  1342  
  1343  	// Redeploy a very similar bundle with another service unit. The new unit
  1344  	// is placed on machine 1 because that's the least crowded machine.
  1345  	content := `
  1346          services:
  1347              memcached:
  1348                  charm: cs:trusty/mem-47
  1349                  num_units: 3
  1350                  to: [1, 2, 3]
  1351              django:
  1352                  charm: cs:trusty/django-42
  1353                  num_units: 4
  1354                  to:
  1355                      - 1
  1356                      - lxc:memcached
  1357              node:
  1358                  charm: cs:trusty/django-42
  1359                  num_units: 1
  1360                  to:
  1361                      - lxc:memcached
  1362          machines:
  1363              1:
  1364              2:
  1365              3:
  1366      `
  1367  	output, err = s.DeployBundleYAML(c, content)
  1368  	c.Assert(err, jc.ErrorIsNil)
  1369  	expectedOutput = `
  1370  added charm cs:trusty/django-42
  1371  reusing service django (charm: cs:trusty/django-42)
  1372  added charm cs:trusty/mem-47
  1373  reusing service memcached (charm: cs:trusty/mem-47)
  1374  service node deployed (charm cs:trusty/django-42 with the series "trusty" defined by the bundle)
  1375  avoid creating other machines to host django and memcached units
  1376  avoid adding new units to service django: 4 units already present
  1377  avoid adding new units to service memcached: 3 units already present
  1378  created 1/lxc/1 container in machine 1 for holding node unit
  1379  added node/0 unit to machine 1/lxc/1
  1380  deployment of bundle "local:bundle/example-0" completed`
  1381  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
  1382  
  1383  	// Redeploy the same bundle again and check that nothing happens.
  1384  	output, err = s.DeployBundleYAML(c, content)
  1385  	c.Assert(err, jc.ErrorIsNil)
  1386  	expectedOutput = `
  1387  added charm cs:trusty/django-42
  1388  reusing service django (charm: cs:trusty/django-42)
  1389  added charm cs:trusty/mem-47
  1390  reusing service memcached (charm: cs:trusty/mem-47)
  1391  reusing service node (charm: cs:trusty/django-42)
  1392  avoid creating other machines to host django and memcached units
  1393  avoid adding new units to service django: 4 units already present
  1394  avoid adding new units to service memcached: 3 units already present
  1395  avoid creating other machines to host node units
  1396  avoid adding new units to service node: 1 unit already present
  1397  deployment of bundle "local:bundle/example-0" completed`
  1398  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
  1399  	s.assertUnitsCreated(c, map[string]string{
  1400  		"django/0":    "0",
  1401  		"django/1":    "0/lxc/0",
  1402  		"django/2":    "1/lxc/0",
  1403  		"django/3":    "2/lxc/0",
  1404  		"memcached/0": "0",
  1405  		"memcached/1": "1",
  1406  		"memcached/2": "2",
  1407  		"node/0":      "1/lxc/1",
  1408  		"ror/0":       "0",
  1409  		"ror/1":       "2/kvm/0",
  1410  		"ror/2":       "2/kvm/1",
  1411  	})
  1412  }
  1413  
  1414  func (s *BundleDeployCharmStoreSuite) TestDeployBundleAnnotations(c *gc.C) {
  1415  	testcharms.UploadCharm(c, s.client, "trusty/django-42", "dummy")
  1416  	testcharms.UploadCharm(c, s.client, "trusty/mem-47", "dummy")
  1417  	output, err := s.DeployBundleYAML(c, `
  1418          services:
  1419              django:
  1420                  charm: cs:django
  1421                  num_units: 1
  1422                  annotations:
  1423                      key1: value1
  1424                      key2: value2
  1425                  to: [1]
  1426              memcached:
  1427                  charm: trusty/mem-47
  1428                  num_units: 1
  1429          machines:
  1430              1:
  1431                  annotations: {foo: bar}
  1432      `)
  1433  	c.Assert(err, jc.ErrorIsNil)
  1434  	expectedOutput := `
  1435  added charm cs:trusty/django-42
  1436  service django deployed (charm cs:trusty/django-42 with the charm series "trusty")
  1437  annotations set for service django
  1438  added charm cs:trusty/mem-47
  1439  service memcached deployed (charm cs:trusty/mem-47 with the series "trusty" defined by the bundle)
  1440  created new machine 0 for holding django unit
  1441  annotations set for machine 0
  1442  added django/0 unit to machine 0
  1443  added memcached/0 unit to new machine
  1444  deployment of bundle "local:bundle/example-0" completed`
  1445  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
  1446  	svc, err := s.State.Service("django")
  1447  	c.Assert(err, jc.ErrorIsNil)
  1448  	ann, err := s.State.Annotations(svc)
  1449  	c.Assert(err, jc.ErrorIsNil)
  1450  	c.Assert(ann, jc.DeepEquals, map[string]string{
  1451  		"key1": "value1",
  1452  		"key2": "value2",
  1453  	})
  1454  	m, err := s.State.Machine("0")
  1455  	c.Assert(err, jc.ErrorIsNil)
  1456  	ann, err = s.State.Annotations(m)
  1457  	c.Assert(err, jc.ErrorIsNil)
  1458  	c.Assert(ann, jc.DeepEquals, map[string]string{"foo": "bar"})
  1459  
  1460  	// Update the annotations and deploy the bundle again.
  1461  	output, err = s.DeployBundleYAML(c, `
  1462          services:
  1463              django:
  1464                  charm: cs:django
  1465                  num_units: 1
  1466                  annotations:
  1467                      key1: new value!
  1468                      key2: value2
  1469                  to: [1]
  1470          machines:
  1471              1:
  1472                  annotations: {answer: 42}
  1473      `)
  1474  	c.Assert(err, jc.ErrorIsNil)
  1475  	expectedOutput = `
  1476  added charm cs:trusty/django-42
  1477  reusing service django (charm: cs:trusty/django-42)
  1478  annotations set for service django
  1479  avoid creating other machines to host django units
  1480  annotations set for machine 0
  1481  avoid adding new units to service django: 1 unit already present
  1482  deployment of bundle "local:bundle/example-0" completed`
  1483  	c.Assert(output, gc.Equals, strings.TrimSpace(expectedOutput))
  1484  	ann, err = s.State.Annotations(svc)
  1485  	c.Assert(err, jc.ErrorIsNil)
  1486  	c.Assert(ann, jc.DeepEquals, map[string]string{
  1487  		"key1": "new value!",
  1488  		"key2": "value2",
  1489  	})
  1490  	ann, err = s.State.Annotations(m)
  1491  	c.Assert(err, jc.ErrorIsNil)
  1492  	c.Assert(ann, jc.DeepEquals, map[string]string{
  1493  		"foo":    "bar",
  1494  		"answer": "42",
  1495  	})
  1496  }
  1497  
  1498  type mockAllWatcher struct {
  1499  	next func() []multiwatcher.Delta
  1500  }
  1501  
  1502  func (w mockAllWatcher) Next() ([]multiwatcher.Delta, error) {
  1503  	return w.next(), nil
  1504  }
  1505  
  1506  func (mockAllWatcher) Stop() error {
  1507  	return nil
  1508  }