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 }