github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/client/application/deploy_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package application_test 5 6 import ( 7 "fmt" 8 9 "github.com/juju/charm/v12" 10 "github.com/juju/collections/set" 11 "github.com/juju/errors" 12 jc "github.com/juju/testing/checkers" 13 gc "gopkg.in/check.v1" 14 "gopkg.in/juju/environschema.v1" 15 16 "github.com/juju/juju/apiserver/facades/client/application" 17 "github.com/juju/juju/controller" 18 corecharm "github.com/juju/juju/core/charm" 19 coreconfig "github.com/juju/juju/core/config" 20 "github.com/juju/juju/core/constraints" 21 "github.com/juju/juju/core/instance" 22 "github.com/juju/juju/core/model" 23 "github.com/juju/juju/core/network" 24 "github.com/juju/juju/juju/testing" 25 "github.com/juju/juju/state" 26 "github.com/juju/juju/testcharms" 27 coretesting "github.com/juju/juju/testing" 28 ) 29 30 // DeployLocalSuite uses a fresh copy of the same local dummy charm for each 31 // test, because DeployApplication demands that a charm already exists in state, 32 // and that is the simplest way to get one in there. 33 type DeployLocalSuite struct { 34 testing.JujuConnSuite 35 charm *state.Charm 36 } 37 38 var _ = gc.Suite(&DeployLocalSuite{}) 39 40 func (s *DeployLocalSuite) SetUpSuite(c *gc.C) { 41 s.JujuConnSuite.SetUpSuite(c) 42 } 43 44 func (s *DeployLocalSuite) SetUpTest(c *gc.C) { 45 s.JujuConnSuite.SetUpTest(c) 46 curl := charm.MustParseURL("local:quantal/dummy") 47 ch := testcharms.RepoForSeries("quantal").CharmDir("dummy") 48 charm, err := testing.PutCharm(s.State, curl, ch) 49 c.Assert(err, jc.ErrorIsNil) 50 s.charm = charm 51 } 52 53 func (s *DeployLocalSuite) TestDeployControllerNotAllowed(c *gc.C) { 54 ch := s.AddTestingCharm(c, "juju-controller") 55 model, err := s.State.Model() 56 c.Assert(err, jc.ErrorIsNil) 57 _, err = application.DeployApplication(stateDeployer{s.State}, 58 model, 59 application.DeployApplicationParams{ 60 ApplicationName: "my-controller", 61 Charm: ch, 62 CharmOrigin: corecharm.Origin{Platform: corecharm.Platform{OS: "ubuntu", Channel: "22.04"}}, 63 }) 64 c.Assert(err, gc.ErrorMatches, "manual deploy of the controller charm not supported") 65 } 66 67 func (s *DeployLocalSuite) TestDeployMinimal(c *gc.C) { 68 model, err := s.State.Model() 69 c.Assert(err, jc.ErrorIsNil) 70 app, err := application.DeployApplication(stateDeployer{s.State}, 71 model, 72 application.DeployApplicationParams{ 73 ApplicationName: "bob", 74 Charm: s.charm, 75 CharmOrigin: corecharm.Origin{Platform: corecharm.Platform{OS: "ubuntu", Channel: "22.04"}}, 76 }) 77 c.Assert(err, jc.ErrorIsNil) 78 s.assertCharm(c, app, s.charm.URL()) 79 s.assertSettings(c, app, charm.Settings{}) 80 s.assertApplicationConfig(c, app, coreconfig.ConfigAttributes{}) 81 s.assertConstraints(c, app, constraints.MustParse("arch=amd64")) 82 s.assertMachines(c, app, constraints.Value{}) 83 } 84 85 func (s *DeployLocalSuite) TestDeployChannel(c *gc.C) { 86 var f fakeDeployer 87 88 model, err := s.State.Model() 89 c.Assert(err, jc.ErrorIsNil) 90 91 _, err = application.DeployApplication(&f, 92 model, 93 application.DeployApplicationParams{ 94 ApplicationName: "bob", 95 Charm: s.charm, 96 CharmOrigin: corecharm.Origin{Platform: corecharm.Platform{OS: "ubuntu", Channel: "22.04"}}, 97 }) 98 c.Assert(err, jc.ErrorIsNil) 99 100 c.Assert(f.args.Name, gc.Equals, "bob") 101 c.Assert(f.args.Charm, gc.DeepEquals, s.charm) 102 c.Assert(f.args.CharmOrigin, jc.DeepEquals, &state.CharmOrigin{ 103 Platform: &state.Platform{OS: "ubuntu", Channel: "22.04"}}) 104 } 105 106 func (s *DeployLocalSuite) TestDeployWithImplicitBindings(c *gc.C) { 107 wordpressCharm := s.addWordpressCharmWithExtraBindings(c) 108 109 model, err := s.State.Model() 110 c.Assert(err, jc.ErrorIsNil) 111 112 app, err := application.DeployApplication(stateDeployer{s.State}, 113 model, 114 application.DeployApplicationParams{ 115 ApplicationName: "bob", 116 Charm: wordpressCharm, 117 EndpointBindings: nil, 118 CharmOrigin: corecharm.Origin{Platform: corecharm.Platform{OS: "ubuntu", Channel: "22.04"}}, 119 }) 120 c.Assert(err, jc.ErrorIsNil) 121 122 s.assertBindings(c, app, map[string]string{ 123 "": network.AlphaSpaceId, 124 // relation names 125 "url": network.AlphaSpaceId, 126 "logging-dir": network.AlphaSpaceId, 127 "monitoring-port": network.AlphaSpaceId, 128 "db": network.AlphaSpaceId, 129 "cache": network.AlphaSpaceId, 130 "cluster": network.AlphaSpaceId, 131 // extra-bindings names 132 "db-client": network.AlphaSpaceId, 133 "admin-api": network.AlphaSpaceId, 134 "foo-bar": network.AlphaSpaceId, 135 }) 136 } 137 138 func (s *DeployLocalSuite) addWordpressCharm(c *gc.C) *state.Charm { 139 wordpressCharmURL := charm.MustParseURL("local:quantal/wordpress") 140 return s.addWordpressCharmFromURL(c, wordpressCharmURL) 141 } 142 143 func (s *DeployLocalSuite) addWordpressCharmWithExtraBindings(c *gc.C) *state.Charm { 144 wordpressCharmURL := charm.MustParseURL("local:quantal/wordpress-extra-bindings") 145 return s.addWordpressCharmFromURL(c, wordpressCharmURL) 146 } 147 148 func (s *DeployLocalSuite) addWordpressCharmFromURL(c *gc.C, charmURL *charm.URL) *state.Charm { 149 ch := testcharms.RepoForSeries("quantal").CharmDir(charmURL.Name) 150 wordpressCharm, err := testing.PutCharm(s.State, charmURL, ch) 151 c.Assert(err, jc.ErrorIsNil) 152 return wordpressCharm 153 } 154 155 func (s *DeployLocalSuite) assertBindings(c *gc.C, app application.Application, expected map[string]string) { 156 type withEndpointBindings interface { 157 EndpointBindings() (application.Bindings, error) 158 } 159 bindings, err := app.(withEndpointBindings).EndpointBindings() 160 c.Assert(err, jc.ErrorIsNil) 161 c.Assert(bindings.Map(), jc.DeepEquals, expected) 162 } 163 164 func (s *DeployLocalSuite) TestDeployWithSomeSpecifiedBindings(c *gc.C) { 165 wordpressCharm := s.addWordpressCharm(c) 166 dbSpace, err := s.State.AddSpace("db", "", nil, false) 167 c.Assert(err, jc.ErrorIsNil) 168 publicSpace, err := s.State.AddSpace("public", "", nil, false) 169 c.Assert(err, jc.ErrorIsNil) 170 171 model, err := s.State.Model() 172 c.Assert(err, jc.ErrorIsNil) 173 174 app, err := application.DeployApplication(stateDeployer{s.State}, 175 model, 176 application.DeployApplicationParams{ 177 ApplicationName: "bob", 178 Charm: wordpressCharm, 179 EndpointBindings: map[string]string{ 180 "": publicSpace.Id(), 181 "db": dbSpace.Id(), 182 }, 183 CharmOrigin: corecharm.Origin{Platform: corecharm.Platform{OS: "ubuntu", Channel: "22.04"}}, 184 }) 185 c.Assert(err, jc.ErrorIsNil) 186 187 s.assertBindings(c, app, map[string]string{ 188 // default binding 189 "": publicSpace.Id(), 190 // relation names 191 "url": publicSpace.Id(), 192 "logging-dir": publicSpace.Id(), 193 "monitoring-port": publicSpace.Id(), 194 "db": dbSpace.Id(), 195 "cache": publicSpace.Id(), 196 // extra-bindings names 197 "db-client": publicSpace.Id(), 198 "admin-api": publicSpace.Id(), 199 "foo-bar": publicSpace.Id(), 200 }) 201 } 202 203 func (s *DeployLocalSuite) TestDeployWithBoundRelationNamesAndExtraBindingsNames(c *gc.C) { 204 wordpressCharm := s.addWordpressCharmWithExtraBindings(c) 205 dbSpace, err := s.State.AddSpace("db", "", nil, false) 206 c.Assert(err, jc.ErrorIsNil) 207 publicSpace, err := s.State.AddSpace("public", "", nil, false) 208 c.Assert(err, jc.ErrorIsNil) 209 internalSpace, err := s.State.AddSpace("internal", "", nil, false) 210 c.Assert(err, jc.ErrorIsNil) 211 212 model, err := s.State.Model() 213 c.Assert(err, jc.ErrorIsNil) 214 215 app, err := application.DeployApplication(stateDeployer{s.State}, 216 model, 217 application.DeployApplicationParams{ 218 ApplicationName: "bob", 219 Charm: wordpressCharm, 220 EndpointBindings: map[string]string{ 221 "": publicSpace.Id(), 222 "db": dbSpace.Id(), 223 "db-client": dbSpace.Id(), 224 "admin-api": internalSpace.Id(), 225 }, 226 CharmOrigin: corecharm.Origin{Platform: corecharm.Platform{OS: "ubuntu", Channel: "22.04"}}, 227 }) 228 c.Assert(err, jc.ErrorIsNil) 229 230 s.assertBindings(c, app, map[string]string{ 231 "": publicSpace.Id(), 232 "url": publicSpace.Id(), 233 "logging-dir": publicSpace.Id(), 234 "monitoring-port": publicSpace.Id(), 235 "db": dbSpace.Id(), 236 "cache": publicSpace.Id(), 237 "db-client": dbSpace.Id(), 238 "admin-api": internalSpace.Id(), 239 "cluster": publicSpace.Id(), 240 "foo-bar": publicSpace.Id(), // like for relations, uses the application-default. 241 }) 242 243 } 244 245 func (s *DeployLocalSuite) TestDeployWithInvalidSpace(c *gc.C) { 246 wordpressCharm := s.addWordpressCharm(c) 247 _, err := s.State.AddSpace("db", "", nil, false) 248 c.Assert(err, jc.ErrorIsNil) 249 publicSpace, err := s.State.AddSpace("public", "", nil, false) 250 c.Assert(err, jc.ErrorIsNil) 251 252 model, err := s.State.Model() 253 c.Assert(err, jc.ErrorIsNil) 254 255 app, err := application.DeployApplication(stateDeployer{s.State}, 256 model, 257 application.DeployApplicationParams{ 258 ApplicationName: "bob", 259 Charm: wordpressCharm, 260 EndpointBindings: map[string]string{ 261 "": publicSpace.Id(), 262 "db": "42", //unknown space id 263 }, 264 CharmOrigin: corecharm.Origin{Platform: corecharm.Platform{OS: "ubuntu", Channel: "22.04"}}, 265 }) 266 c.Assert(err, gc.ErrorMatches, `cannot add application "bob": space not found`) 267 c.Check(app, gc.IsNil) 268 // The application should not have been added 269 _, err = s.State.Application("bob") 270 c.Assert(err, jc.Satisfies, errors.IsNotFound) 271 } 272 273 func (s *DeployLocalSuite) TestDeployResources(c *gc.C) { 274 var f fakeDeployer 275 276 model, err := s.State.Model() 277 c.Assert(err, jc.ErrorIsNil) 278 279 _, err = application.DeployApplication(&f, 280 model, 281 application.DeployApplicationParams{ 282 ApplicationName: "bob", 283 Charm: s.charm, 284 EndpointBindings: map[string]string{ 285 "": "public", 286 }, 287 Resources: map[string]string{"foo": "bar"}, 288 CharmOrigin: corecharm.Origin{Platform: corecharm.Platform{OS: "ubuntu", Channel: "22.04"}}, 289 }) 290 c.Assert(err, jc.ErrorIsNil) 291 292 c.Assert(f.args.Name, gc.Equals, "bob") 293 c.Assert(f.args.Charm, gc.DeepEquals, s.charm) 294 c.Assert(f.args.Resources, gc.DeepEquals, map[string]string{"foo": "bar"}) 295 } 296 297 func (s *DeployLocalSuite) TestDeploySettings(c *gc.C) { 298 model, err := s.State.Model() 299 c.Assert(err, jc.ErrorIsNil) 300 301 app, err := application.DeployApplication(stateDeployer{s.State}, 302 model, 303 application.DeployApplicationParams{ 304 ApplicationName: "bob", 305 Charm: s.charm, 306 CharmConfig: charm.Settings{ 307 "title": "banana cupcakes", 308 "skill-level": 9901, 309 }, 310 CharmOrigin: corecharm.Origin{Platform: corecharm.Platform{OS: "ubuntu", Channel: "22.04"}}, 311 }) 312 c.Assert(err, jc.ErrorIsNil) 313 s.assertSettings(c, app, charm.Settings{ 314 "title": "banana cupcakes", 315 "skill-level": int64(9901), 316 }) 317 } 318 319 func (s *DeployLocalSuite) TestDeploySettingsError(c *gc.C) { 320 model, err := s.State.Model() 321 c.Assert(err, jc.ErrorIsNil) 322 323 _, err = application.DeployApplication(stateDeployer{s.State}, 324 model, 325 application.DeployApplicationParams{ 326 ApplicationName: "bob", 327 Charm: s.charm, 328 CharmConfig: charm.Settings{ 329 "skill-level": 99.01, 330 }, 331 CharmOrigin: corecharm.Origin{Platform: corecharm.Platform{OS: "ubuntu", Channel: "22.04"}}, 332 }) 333 c.Assert(err, gc.ErrorMatches, `option "skill-level" expected int, got 99.01`) 334 _, err = s.State.Application("bob") 335 c.Assert(err, jc.Satisfies, errors.IsNotFound) 336 } 337 338 func sampleApplicationConfigSchema() environschema.Fields { 339 schema := environschema.Fields{ 340 "title": environschema.Attr{Type: environschema.Tstring}, 341 "outlook": environschema.Attr{Type: environschema.Tstring}, 342 "username": environschema.Attr{Type: environschema.Tstring}, 343 "skill-level": environschema.Attr{Type: environschema.Tint}, 344 } 345 return schema 346 } 347 348 func (s *DeployLocalSuite) TestDeployWithApplicationConfig(c *gc.C) { 349 cfg, err := coreconfig.NewConfig(map[string]interface{}{ 350 "outlook": "good", 351 "skill-level": 1, 352 }, sampleApplicationConfigSchema(), nil) 353 c.Assert(err, jc.ErrorIsNil) 354 355 model, err := s.State.Model() 356 c.Assert(err, jc.ErrorIsNil) 357 358 app, err := application.DeployApplication(stateDeployer{s.State}, 359 model, 360 application.DeployApplicationParams{ 361 ApplicationName: "bob", 362 Charm: s.charm, 363 ApplicationConfig: cfg, 364 CharmOrigin: corecharm.Origin{Platform: corecharm.Platform{OS: "ubuntu", Channel: "22.04"}}, 365 }) 366 c.Assert(err, jc.ErrorIsNil) 367 s.assertApplicationConfig(c, app, coreconfig.ConfigAttributes{ 368 "outlook": "good", 369 "skill-level": 1, 370 }) 371 } 372 373 func (s *DeployLocalSuite) TestDeployConstraints(c *gc.C) { 374 err := s.State.SetModelConstraints(constraints.MustParse("mem=2G")) 375 c.Assert(err, jc.ErrorIsNil) 376 applicationCons := constraints.MustParse("cores=2") 377 378 model, err := s.State.Model() 379 c.Assert(err, jc.ErrorIsNil) 380 381 app, err := application.DeployApplication(stateDeployer{s.State}, 382 model, 383 application.DeployApplicationParams{ 384 ApplicationName: "bob", 385 Charm: s.charm, 386 Constraints: applicationCons, 387 CharmOrigin: corecharm.Origin{Platform: corecharm.Platform{OS: "ubuntu", Channel: "22.04"}}, 388 }) 389 c.Assert(err, jc.ErrorIsNil) 390 s.assertConstraints(c, app, constraints.MustParse("cores=2 arch=amd64")) 391 } 392 393 func (s *DeployLocalSuite) TestDeployNumUnits(c *gc.C) { 394 var f fakeDeployer 395 396 model, err := s.State.Model() 397 c.Assert(err, jc.ErrorIsNil) 398 399 applicationCons := constraints.MustParse("cores=2") 400 _, err = application.DeployApplication(&f, 401 model, 402 application.DeployApplicationParams{ 403 ApplicationName: "bob", 404 Charm: s.charm, 405 Constraints: applicationCons, 406 NumUnits: 2, 407 CharmOrigin: corecharm.Origin{Platform: corecharm.Platform{OS: "ubuntu", Channel: "22.04"}}, 408 }) 409 c.Assert(err, jc.ErrorIsNil) 410 411 c.Assert(f.args.Name, gc.Equals, "bob") 412 c.Assert(f.args.Charm, gc.DeepEquals, s.charm) 413 c.Assert(f.args.Constraints, gc.DeepEquals, applicationCons) 414 c.Assert(f.args.NumUnits, gc.Equals, 2) 415 } 416 417 func (s *DeployLocalSuite) TestDeployForceMachineId(c *gc.C) { 418 var f fakeDeployer 419 420 model, err := s.State.Model() 421 c.Assert(err, jc.ErrorIsNil) 422 423 applicationCons := constraints.MustParse("cores=2") 424 _, err = application.DeployApplication(&f, 425 model, 426 application.DeployApplicationParams{ 427 ApplicationName: "bob", 428 Charm: s.charm, 429 Constraints: applicationCons, 430 NumUnits: 1, 431 Placement: []*instance.Placement{instance.MustParsePlacement("0")}, 432 CharmOrigin: corecharm.Origin{Platform: corecharm.Platform{OS: "ubuntu", Channel: "22.04"}}, 433 }) 434 c.Assert(err, jc.ErrorIsNil) 435 436 c.Assert(f.args.Name, gc.Equals, "bob") 437 c.Assert(f.args.Charm, gc.DeepEquals, s.charm) 438 c.Assert(f.args.Constraints, gc.DeepEquals, applicationCons) 439 c.Assert(f.args.NumUnits, gc.Equals, 1) 440 c.Assert(f.args.Placement, gc.HasLen, 1) 441 c.Assert(*f.args.Placement[0], gc.Equals, instance.Placement{Scope: instance.MachineScope, Directive: "0"}) 442 } 443 444 func (s *DeployLocalSuite) TestDeployForceMachineIdWithContainer(c *gc.C) { 445 var f fakeDeployer 446 447 model, err := s.State.Model() 448 c.Assert(err, jc.ErrorIsNil) 449 450 applicationCons := constraints.MustParse("cores=2") 451 _, err = application.DeployApplication(&f, 452 model, 453 application.DeployApplicationParams{ 454 ApplicationName: "bob", 455 Charm: s.charm, 456 Constraints: applicationCons, 457 NumUnits: 1, 458 Placement: []*instance.Placement{instance.MustParsePlacement(fmt.Sprintf("%s:0", instance.LXD))}, 459 CharmOrigin: corecharm.Origin{Platform: corecharm.Platform{OS: "ubuntu", Channel: "22.04"}}, 460 }) 461 c.Assert(err, jc.ErrorIsNil) 462 c.Assert(f.args.Name, gc.Equals, "bob") 463 c.Assert(f.args.Charm, gc.DeepEquals, s.charm) 464 c.Assert(f.args.Constraints, gc.DeepEquals, applicationCons) 465 c.Assert(f.args.NumUnits, gc.Equals, 1) 466 c.Assert(f.args.Placement, gc.HasLen, 1) 467 c.Assert(*f.args.Placement[0], gc.Equals, instance.Placement{Scope: string(instance.LXD), Directive: "0"}) 468 } 469 470 func (s *DeployLocalSuite) TestDeploy(c *gc.C) { 471 var f fakeDeployer 472 473 model, err := s.State.Model() 474 c.Assert(err, jc.ErrorIsNil) 475 476 applicationCons := constraints.MustParse("cores=2") 477 placement := []*instance.Placement{ 478 {Scope: s.State.ModelUUID(), Directive: "valid"}, 479 {Scope: "#", Directive: "0"}, 480 {Scope: "lxd", Directive: "1"}, 481 {Scope: "lxd", Directive: ""}, 482 } 483 _, err = application.DeployApplication(&f, 484 model, 485 application.DeployApplicationParams{ 486 ApplicationName: "bob", 487 Charm: s.charm, 488 Constraints: applicationCons, 489 NumUnits: 4, 490 Placement: placement, 491 CharmOrigin: corecharm.Origin{Platform: corecharm.Platform{OS: "ubuntu", Channel: "22.04"}}, 492 }) 493 c.Assert(err, jc.ErrorIsNil) 494 495 c.Assert(f.args.Name, gc.Equals, "bob") 496 c.Assert(f.args.Charm, gc.DeepEquals, s.charm) 497 c.Assert(f.args.Constraints, gc.DeepEquals, applicationCons) 498 c.Assert(f.args.NumUnits, gc.Equals, 4) 499 c.Assert(f.args.Placement, gc.DeepEquals, placement) 500 } 501 502 func (s *DeployLocalSuite) TestDeployWithUnmetCharmRequirements(c *gc.C) { 503 curl := charm.MustParseURL("local:focal/juju-qa-test-assumes-v2") 504 ch := testcharms.Hub.CharmDir("juju-qa-test-assumes-v2") 505 charm, err := testing.PutCharm(s.State, curl, ch) 506 c.Assert(err, jc.ErrorIsNil) 507 508 var f = fakeDeployer{} 509 510 model, err := s.State.Model() 511 c.Assert(err, jc.ErrorIsNil) 512 513 _, err = application.DeployApplication(&f, 514 model, 515 application.DeployApplicationParams{ 516 ApplicationName: "assume-metal", 517 Charm: charm, 518 NumUnits: 1, 519 CharmOrigin: corecharm.Origin{Platform: corecharm.Platform{OS: "ubuntu", Channel: "22.04"}}, 520 }) 521 c.Assert(err, gc.ErrorMatches, "(?m).*Charm feature requirements cannot be met.*") 522 } 523 524 func (s *DeployLocalSuite) TestDeployWithUnmetCharmRequirementsAndForce(c *gc.C) { 525 curl := charm.MustParseURL("local:focal/juju-qa-test-assumes-v2") 526 ch := testcharms.Hub.CharmDir("juju-qa-test-assumes-v2") 527 charm, err := testing.PutCharm(s.State, curl, ch) 528 c.Assert(err, jc.ErrorIsNil) 529 530 var f = fakeDeployer{} 531 532 model, err := s.State.Model() 533 c.Assert(err, jc.ErrorIsNil) 534 535 _, err = application.DeployApplication(&f, 536 model, 537 application.DeployApplicationParams{ 538 ApplicationName: "assume-metal", 539 Charm: charm, 540 NumUnits: 1, 541 Force: true, // bypass assumes checks 542 CharmOrigin: corecharm.Origin{Platform: corecharm.Platform{OS: "ubuntu", Channel: "22.04"}}, 543 }) 544 c.Assert(err, jc.ErrorIsNil) 545 } 546 547 func (s *DeployLocalSuite) TestDeployWithFewerPlacement(c *gc.C) { 548 var f fakeDeployer 549 550 model, err := s.State.Model() 551 c.Assert(err, jc.ErrorIsNil) 552 553 applicationCons := constraints.MustParse("cores=2") 554 placement := []*instance.Placement{{Scope: s.State.ModelUUID(), Directive: "valid"}} 555 _, err = application.DeployApplication(&f, 556 model, 557 application.DeployApplicationParams{ 558 ApplicationName: "bob", 559 Charm: s.charm, 560 Constraints: applicationCons, 561 NumUnits: 3, 562 Placement: placement, 563 CharmOrigin: corecharm.Origin{Platform: corecharm.Platform{OS: "ubuntu", Channel: "22.04"}}, 564 }) 565 c.Assert(err, jc.ErrorIsNil) 566 c.Assert(f.args.Name, gc.Equals, "bob") 567 c.Assert(f.args.Charm, gc.DeepEquals, s.charm) 568 c.Assert(f.args.Constraints, gc.DeepEquals, applicationCons) 569 c.Assert(f.args.NumUnits, gc.Equals, 3) 570 c.Assert(f.args.Placement, gc.DeepEquals, placement) 571 } 572 573 func (s *DeployLocalSuite) assertCharm(c *gc.C, app application.Application, expect string) { 574 curl, force := app.CharmURL() 575 c.Assert(*curl, gc.Equals, expect) 576 c.Assert(force, jc.IsFalse) 577 } 578 579 func (s *DeployLocalSuite) assertSettings(c *gc.C, app application.Application, _ charm.Settings) { 580 settings, err := app.CharmConfig(model.GenerationMaster) 581 c.Assert(err, jc.ErrorIsNil) 582 expected := s.charm.Config().DefaultSettings() 583 for name, value := range settings { 584 expected[name] = value 585 } 586 c.Assert(settings, gc.DeepEquals, expected) 587 } 588 589 func (s *DeployLocalSuite) assertApplicationConfig(c *gc.C, app application.Application, wantCfg coreconfig.ConfigAttributes) { 590 cfg, err := app.ApplicationConfig() 591 c.Assert(err, jc.ErrorIsNil) 592 c.Assert(cfg, gc.DeepEquals, wantCfg) 593 } 594 595 func (s *DeployLocalSuite) assertConstraints(c *gc.C, app application.Application, expect constraints.Value) { 596 cons, err := app.Constraints() 597 c.Assert(err, jc.ErrorIsNil) 598 c.Assert(cons, gc.DeepEquals, expect) 599 } 600 601 func (s *DeployLocalSuite) assertMachines(c *gc.C, app application.Application, expectCons constraints.Value, expectIds ...string) { 602 type withAssignedMachineId interface { 603 AssignedMachineId() (string, error) 604 } 605 606 units, err := app.AllUnits() 607 c.Assert(err, jc.ErrorIsNil) 608 c.Assert(units, gc.HasLen, len(expectIds)) 609 // first manually tell state to assign all the units 610 for _, unit := range units { 611 id := unit.UnitTag().Id() 612 res, err := s.State.AssignStagedUnits([]string{id}) 613 c.Assert(err, jc.ErrorIsNil) 614 c.Assert(res[0].Error, jc.ErrorIsNil) 615 c.Assert(res[0].Unit, gc.Equals, id) 616 } 617 618 // refresh the list of units from state 619 units, err = app.AllUnits() 620 c.Assert(err, jc.ErrorIsNil) 621 c.Assert(units, gc.HasLen, len(expectIds)) 622 unseenIds := set.NewStrings(expectIds...) 623 for _, unit := range units { 624 id, err := unit.(withAssignedMachineId).AssignedMachineId() 625 c.Assert(err, jc.ErrorIsNil) 626 unseenIds.Remove(id) 627 machine, err := s.State.Machine(id) 628 c.Assert(err, jc.ErrorIsNil) 629 cons, err := machine.Constraints() 630 c.Assert(err, jc.ErrorIsNil) 631 c.Assert(cons, gc.DeepEquals, expectCons) 632 } 633 c.Assert(unseenIds, gc.DeepEquals, set.NewStrings()) 634 } 635 636 type stateDeployer struct { 637 *state.State 638 } 639 640 func (d stateDeployer) AddApplication(args state.AddApplicationArgs) (application.Application, error) { 641 app, err := d.State.AddApplication(args) 642 if err != nil { 643 return nil, err 644 } 645 return application.NewStateApplication(d.State, app), nil 646 } 647 648 type fakeDeployer struct { 649 args state.AddApplicationArgs 650 controllerCfg *controller.Config 651 } 652 653 func (f *fakeDeployer) ControllerConfig() (controller.Config, error) { 654 if f.controllerCfg != nil { 655 return *f.controllerCfg, nil 656 } 657 return controller.NewConfig(coretesting.ControllerTag.Id(), coretesting.CACert, map[string]interface{}{}) 658 } 659 660 func (f *fakeDeployer) AddApplication(args state.AddApplicationArgs) (application.Application, error) { 661 f.args = args 662 return nil, nil 663 }