github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/juju/conn_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package juju_test 5 6 import ( 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "path/filepath" 12 "strings" 13 stdtesting "testing" 14 15 gc "launchpad.net/gocheck" 16 17 "launchpad.net/juju-core/charm" 18 "launchpad.net/juju-core/constraints" 19 "launchpad.net/juju-core/environs" 20 "launchpad.net/juju-core/environs/bootstrap" 21 "launchpad.net/juju-core/environs/config" 22 "launchpad.net/juju-core/environs/configstore" 23 envtesting "launchpad.net/juju-core/environs/testing" 24 "launchpad.net/juju-core/errors" 25 "launchpad.net/juju-core/instance" 26 "launchpad.net/juju-core/juju" 27 "launchpad.net/juju-core/juju/osenv" 28 "launchpad.net/juju-core/juju/testing" 29 "launchpad.net/juju-core/provider/dummy" 30 "launchpad.net/juju-core/state" 31 coretesting "launchpad.net/juju-core/testing" 32 jc "launchpad.net/juju-core/testing/checkers" 33 "launchpad.net/juju-core/testing/testbase" 34 "launchpad.net/juju-core/utils" 35 "launchpad.net/juju-core/utils/set" 36 ) 37 38 func Test(t *stdtesting.T) { 39 coretesting.MgoTestPackage(t) 40 } 41 42 type NewConnSuite struct { 43 testbase.LoggingSuite 44 envtesting.ToolsFixture 45 } 46 47 var _ = gc.Suite(&NewConnSuite{}) 48 49 func (cs *NewConnSuite) SetUpTest(c *gc.C) { 50 cs.LoggingSuite.SetUpTest(c) 51 cs.ToolsFixture.SetUpTest(c) 52 } 53 54 func (cs *NewConnSuite) TearDownTest(c *gc.C) { 55 dummy.Reset() 56 cs.ToolsFixture.TearDownTest(c) 57 cs.LoggingSuite.TearDownTest(c) 58 } 59 60 func assertClose(c *gc.C, closer io.Closer) { 61 err := closer.Close() 62 c.Assert(err, gc.IsNil) 63 } 64 65 func (*NewConnSuite) TestNewConnWithoutAdminSecret(c *gc.C) { 66 cfg, err := config.New(config.NoDefaults, dummy.SampleConfig()) 67 c.Assert(err, gc.IsNil) 68 ctx := coretesting.Context(c) 69 env, err := environs.Prepare(cfg, ctx, configstore.NewMem()) 70 c.Assert(err, gc.IsNil) 71 envtesting.UploadFakeTools(c, env.Storage()) 72 err = bootstrap.Bootstrap(ctx, env, constraints.Value{}) 73 c.Assert(err, gc.IsNil) 74 75 attrs := env.Config().AllAttrs() 76 delete(attrs, "admin-secret") 77 env1, err := environs.NewFromAttrs(attrs) 78 c.Assert(err, gc.IsNil) 79 conn, err := juju.NewConn(env1) 80 c.Check(conn, gc.IsNil) 81 c.Assert(err, gc.ErrorMatches, "cannot connect without admin-secret") 82 } 83 84 func bootstrapEnv(c *gc.C, envName string, store configstore.Storage) { 85 if store == nil { 86 store = configstore.NewMem() 87 } 88 ctx := coretesting.Context(c) 89 env, err := environs.PrepareFromName(envName, ctx, store) 90 c.Assert(err, gc.IsNil) 91 envtesting.UploadFakeTools(c, env.Storage()) 92 err = bootstrap.Bootstrap(ctx, env, constraints.Value{}) 93 c.Assert(err, gc.IsNil) 94 } 95 96 func (*NewConnSuite) TestConnMultipleCloseOk(c *gc.C) { 97 defer coretesting.MakeSampleHome(c).Restore() 98 bootstrapEnv(c, "", defaultConfigStore(c)) 99 // Error return from here is tested in TestNewConnFromNameNotSetGetsDefault. 100 conn, err := juju.NewConnFromName("") 101 c.Assert(err, gc.IsNil) 102 assertClose(c, conn) 103 assertClose(c, conn) 104 assertClose(c, conn) 105 } 106 107 func (*NewConnSuite) TestNewConnFromNameNotSetGetsDefault(c *gc.C) { 108 defer coretesting.MakeSampleHome(c).Restore() 109 bootstrapEnv(c, "", defaultConfigStore(c)) 110 conn, err := juju.NewConnFromName("") 111 c.Assert(err, gc.IsNil) 112 defer assertClose(c, conn) 113 c.Assert(conn.Environ.Name(), gc.Equals, coretesting.SampleEnvName) 114 } 115 116 func (*NewConnSuite) TestNewConnFromNameNotDefault(c *gc.C) { 117 defer coretesting.MakeMultipleEnvHome(c).Restore() 118 // The default environment is "erewhemos", so make sure we get what we ask for. 119 const envName = "erewhemos-2" 120 bootstrapEnv(c, envName, defaultConfigStore(c)) 121 conn, err := juju.NewConnFromName(envName) 122 c.Assert(err, gc.IsNil) 123 defer assertClose(c, conn) 124 c.Assert(conn.Environ.Name(), gc.Equals, envName) 125 } 126 127 func (cs *NewConnSuite) TestConnStateSecretsSideEffect(c *gc.C) { 128 attrs := dummy.SampleConfig().Merge(coretesting.Attrs{ 129 "admin-secret": "side-effect secret", 130 "secret": "pork", 131 }) 132 cfg, err := config.New(config.NoDefaults, attrs) 133 c.Assert(err, gc.IsNil) 134 ctx := coretesting.Context(c) 135 env, err := environs.Prepare(cfg, ctx, configstore.NewMem()) 136 c.Assert(err, gc.IsNil) 137 envtesting.UploadFakeTools(c, env.Storage()) 138 err = bootstrap.Bootstrap(ctx, env, constraints.Value{}) 139 c.Assert(err, gc.IsNil) 140 info, _, err := env.StateInfo() 141 c.Assert(err, gc.IsNil) 142 info.Password = utils.UserPasswordHash("side-effect secret", utils.CompatSalt) 143 st, err := state.Open(info, state.DefaultDialOpts(), environs.NewStatePolicy()) 144 c.Assert(err, gc.IsNil) 145 defer assertClose(c, st) 146 147 // Verify we have secrets in the environ config already. 148 statecfg, err := st.EnvironConfig() 149 c.Assert(err, gc.IsNil) 150 c.Assert(statecfg.UnknownAttrs()["secret"], gc.Equals, "pork") 151 152 // Remove the secret from state, and then make sure it gets 153 // pushed back again. 154 attrs = statecfg.AllAttrs() 155 delete(attrs, "secret") 156 newcfg, err := config.New(config.NoDefaults, attrs) 157 c.Assert(err, gc.IsNil) 158 err = st.SetEnvironConfig(newcfg, statecfg) 159 c.Assert(err, gc.IsNil) 160 statecfg, err = st.EnvironConfig() 161 c.Assert(err, gc.IsNil) 162 c.Assert(statecfg.UnknownAttrs()["secret"], gc.IsNil) 163 164 // Make a new Conn, which will push the secrets. 165 conn, err := juju.NewConn(env) 166 c.Assert(err, gc.IsNil) 167 defer assertClose(c, conn) 168 169 statecfg, err = conn.State.EnvironConfig() 170 c.Assert(err, gc.IsNil) 171 c.Assert(statecfg.UnknownAttrs()["secret"], gc.Equals, "pork") 172 173 // Reset the admin password so the state db can be reused. 174 err = conn.State.SetAdminMongoPassword("") 175 c.Assert(err, gc.IsNil) 176 } 177 178 func (cs *NewConnSuite) TestConnStateDoesNotUpdateExistingSecrets(c *gc.C) { 179 attrs := dummy.SampleConfig().Merge(coretesting.Attrs{ 180 "secret": "pork", 181 }) 182 cfg, err := config.New(config.NoDefaults, attrs) 183 c.Assert(err, gc.IsNil) 184 ctx := coretesting.Context(c) 185 env, err := environs.Prepare(cfg, ctx, configstore.NewMem()) 186 c.Assert(err, gc.IsNil) 187 envtesting.UploadFakeTools(c, env.Storage()) 188 err = bootstrap.Bootstrap(ctx, env, constraints.Value{}) 189 c.Assert(err, gc.IsNil) 190 191 // Make a new Conn, which will push the secrets. 192 conn, err := juju.NewConn(env) 193 c.Assert(err, gc.IsNil) 194 defer assertClose(c, conn) 195 196 // Make another env with a different secret. 197 attrs = env.Config().AllAttrs() 198 attrs["secret"] = "squirrel" 199 env1, err := environs.NewFromAttrs(attrs) 200 c.Assert(err, gc.IsNil) 201 202 // Connect with the new env and check that the secret has not changed 203 conn, err = juju.NewConn(env1) 204 c.Assert(err, gc.IsNil) 205 defer assertClose(c, conn) 206 cfg, err = conn.State.EnvironConfig() 207 c.Assert(err, gc.IsNil) 208 c.Assert(cfg.UnknownAttrs()["secret"], gc.Equals, "pork") 209 210 // Reset the admin password so the state db can be reused. 211 err = conn.State.SetAdminMongoPassword("") 212 c.Assert(err, gc.IsNil) 213 } 214 215 func (cs *NewConnSuite) TestConnWithPassword(c *gc.C) { 216 attrs := dummy.SampleConfig().Merge(coretesting.Attrs{ 217 "admin-secret": "nutkin", 218 }) 219 cfg, err := config.New(config.NoDefaults, attrs) 220 c.Assert(err, gc.IsNil) 221 ctx := coretesting.Context(c) 222 env, err := environs.Prepare(cfg, ctx, configstore.NewMem()) 223 c.Assert(err, gc.IsNil) 224 envtesting.UploadFakeTools(c, env.Storage()) 225 err = bootstrap.Bootstrap(ctx, env, constraints.Value{}) 226 c.Assert(err, gc.IsNil) 227 228 // Check that Bootstrap has correctly used a hash 229 // of the admin password. 230 info, _, err := env.StateInfo() 231 c.Assert(err, gc.IsNil) 232 info.Password = utils.UserPasswordHash("nutkin", utils.CompatSalt) 233 st, err := state.Open(info, state.DefaultDialOpts(), environs.NewStatePolicy()) 234 c.Assert(err, gc.IsNil) 235 assertClose(c, st) 236 237 // Check that we can connect with the original environment. 238 conn, err := juju.NewConn(env) 239 c.Assert(err, gc.IsNil) 240 assertClose(c, conn) 241 242 // Check that the password has now been changed to the original 243 // admin password. 244 info.Password = "nutkin" 245 st1, err := state.Open(info, state.DefaultDialOpts(), environs.NewStatePolicy()) 246 c.Assert(err, gc.IsNil) 247 assertClose(c, st1) 248 249 // Check that we can still connect with the original 250 // environment. 251 conn, err = juju.NewConn(env) 252 c.Assert(err, gc.IsNil) 253 defer assertClose(c, conn) 254 255 // Reset the admin password so the state db can be reused. 256 err = conn.State.SetAdminMongoPassword("") 257 c.Assert(err, gc.IsNil) 258 } 259 260 type ConnSuite struct { 261 testbase.LoggingSuite 262 coretesting.MgoSuite 263 envtesting.ToolsFixture 264 conn *juju.Conn 265 repo *charm.LocalRepository 266 } 267 268 var _ = gc.Suite(&ConnSuite{}) 269 270 func (s *ConnSuite) SetUpTest(c *gc.C) { 271 s.LoggingSuite.SetUpTest(c) 272 s.MgoSuite.SetUpTest(c) 273 s.ToolsFixture.SetUpTest(c) 274 cfg, err := config.New(config.NoDefaults, dummy.SampleConfig()) 275 c.Assert(err, gc.IsNil) 276 ctx := coretesting.Context(c) 277 environ, err := environs.Prepare(cfg, ctx, configstore.NewMem()) 278 c.Assert(err, gc.IsNil) 279 envtesting.UploadFakeTools(c, environ.Storage()) 280 err = bootstrap.Bootstrap(ctx, environ, constraints.Value{}) 281 c.Assert(err, gc.IsNil) 282 s.conn, err = juju.NewConn(environ) 283 c.Assert(err, gc.IsNil) 284 s.repo = &charm.LocalRepository{Path: c.MkDir()} 285 } 286 287 func (s *ConnSuite) TearDownTest(c *gc.C) { 288 if s.conn == nil { 289 return 290 } 291 err := s.conn.State.SetAdminMongoPassword("") 292 c.Assert(err, gc.IsNil) 293 err = s.conn.Environ.Destroy() 294 c.Check(err, gc.IsNil) 295 assertClose(c, s.conn) 296 s.conn = nil 297 dummy.Reset() 298 s.ToolsFixture.TearDownTest(c) 299 s.MgoSuite.TearDownTest(c) 300 s.LoggingSuite.TearDownTest(c) 301 } 302 303 func (s *ConnSuite) SetUpSuite(c *gc.C) { 304 s.LoggingSuite.SetUpSuite(c) 305 s.MgoSuite.SetUpSuite(c) 306 } 307 308 func (s *ConnSuite) TearDownSuite(c *gc.C) { 309 s.LoggingSuite.TearDownSuite(c) 310 s.MgoSuite.TearDownSuite(c) 311 } 312 313 func (s *ConnSuite) TestNewConnFromState(c *gc.C) { 314 conn, err := juju.NewConnFromState(s.conn.State) 315 c.Assert(err, gc.IsNil) 316 c.Assert(conn.Environ.Name(), gc.Equals, dummy.SampleConfig()["name"]) 317 } 318 319 func (s *ConnSuite) TestPutCharmBasic(c *gc.C) { 320 curl := coretesting.Charms.ClonedURL(s.repo.Path, "quantal", "riak") 321 curl.Revision = -1 // make sure we trigger the repo.Latest logic. 322 sch, err := s.conn.PutCharm(curl, s.repo, false) 323 c.Assert(err, gc.IsNil) 324 c.Assert(sch.Meta().Summary, gc.Equals, "K/V storage engine") 325 326 sch, err = s.conn.State.Charm(sch.URL()) 327 c.Assert(err, gc.IsNil) 328 c.Assert(sch.Meta().Summary, gc.Equals, "K/V storage engine") 329 } 330 331 func (s *ConnSuite) TestPutBundledCharm(c *gc.C) { 332 // Bundle the riak charm into a charm repo directory. 333 dir := filepath.Join(s.repo.Path, "quantal") 334 err := os.Mkdir(dir, 0777) 335 c.Assert(err, gc.IsNil) 336 w, err := os.Create(filepath.Join(dir, "riak.charm")) 337 c.Assert(err, gc.IsNil) 338 defer assertClose(c, w) 339 charmDir := coretesting.Charms.Dir("riak") 340 err = charmDir.BundleTo(w) 341 c.Assert(err, gc.IsNil) 342 343 // Invent a URL that points to the bundled charm, and 344 // test putting that. 345 curl := &charm.URL{ 346 Schema: "local", 347 Series: "quantal", 348 Name: "riak", 349 Revision: -1, 350 } 351 _, err = s.conn.PutCharm(curl, s.repo, true) 352 c.Assert(err, gc.ErrorMatches, `cannot increment revision of charm "local:quantal/riak-7": not a directory`) 353 354 sch, err := s.conn.PutCharm(curl, s.repo, false) 355 c.Assert(err, gc.IsNil) 356 c.Assert(sch.Meta().Summary, gc.Equals, "K/V storage engine") 357 358 // Check that we can get the charm from the state. 359 sch, err = s.conn.State.Charm(sch.URL()) 360 c.Assert(err, gc.IsNil) 361 c.Assert(sch.Meta().Summary, gc.Equals, "K/V storage engine") 362 } 363 364 func (s *ConnSuite) TestPutCharmUpload(c *gc.C) { 365 repo := &charm.LocalRepository{c.MkDir()} 366 curl := coretesting.Charms.ClonedURL(repo.Path, "quantal", "riak") 367 368 // Put charm for the first time. 369 sch, err := s.conn.PutCharm(curl, repo, false) 370 c.Assert(err, gc.IsNil) 371 c.Assert(sch.Meta().Summary, gc.Equals, "K/V storage engine") 372 373 sch, err = s.conn.State.Charm(sch.URL()) 374 c.Assert(err, gc.IsNil) 375 sha256 := sch.BundleSha256() 376 rev := sch.Revision() 377 378 // Change the charm on disk. 379 ch, err := repo.Get(curl) 380 c.Assert(err, gc.IsNil) 381 chd := ch.(*charm.Dir) 382 err = ioutil.WriteFile(filepath.Join(chd.Path, "extra"), []byte("arble"), 0666) 383 c.Assert(err, gc.IsNil) 384 385 // Put charm again and check that it has not changed in the state. 386 sch, err = s.conn.PutCharm(curl, repo, false) 387 c.Assert(err, gc.IsNil) 388 389 sch, err = s.conn.State.Charm(sch.URL()) 390 c.Assert(err, gc.IsNil) 391 c.Assert(sch.BundleSha256(), gc.Equals, sha256) 392 c.Assert(sch.Revision(), gc.Equals, rev) 393 394 // Put charm again, with bumpRevision this time, and check that 395 // it has changed. 396 sch, err = s.conn.PutCharm(curl, repo, true) 397 c.Assert(err, gc.IsNil) 398 399 sch, err = s.conn.State.Charm(sch.URL()) 400 c.Assert(err, gc.IsNil) 401 c.Assert(sch.BundleSha256(), gc.Not(gc.Equals), sha256) 402 c.Assert(sch.Revision(), gc.Equals, rev+1) 403 } 404 405 func (s *ConnSuite) TestAddUnits(c *gc.C) { 406 curl := coretesting.Charms.ClonedURL(s.repo.Path, "quantal", "riak") 407 sch, err := s.conn.PutCharm(curl, s.repo, false) 408 c.Assert(err, gc.IsNil) 409 svc, err := s.conn.State.AddService("testriak", "user-admin", sch) 410 c.Assert(err, gc.IsNil) 411 units, err := juju.AddUnits(s.conn.State, svc, 2, "") 412 c.Assert(err, gc.IsNil) 413 c.Assert(units, gc.HasLen, 2) 414 415 id0, err := units[0].AssignedMachineId() 416 c.Assert(err, gc.IsNil) 417 id1, err := units[1].AssignedMachineId() 418 c.Assert(err, gc.IsNil) 419 c.Assert(id0, gc.Not(gc.Equals), id1) 420 421 units, err = juju.AddUnits(s.conn.State, svc, 2, "0") 422 c.Assert(err, gc.ErrorMatches, `cannot add multiple units of service "testriak" to a single machine`) 423 424 units, err = juju.AddUnits(s.conn.State, svc, 1, "0") 425 c.Assert(err, gc.IsNil) 426 id2, err := units[0].AssignedMachineId() 427 c.Assert(id2, gc.Equals, id0) 428 429 units, err = juju.AddUnits(s.conn.State, svc, 1, "lxc:0") 430 c.Assert(err, gc.IsNil) 431 id3, err := units[0].AssignedMachineId() 432 c.Assert(id3, gc.Equals, id0+"/lxc/0") 433 434 units, err = juju.AddUnits(s.conn.State, svc, 1, "lxc:"+id3) 435 c.Assert(err, gc.IsNil) 436 id4, err := units[0].AssignedMachineId() 437 c.Assert(id4, gc.Equals, id0+"/lxc/0/lxc/0") 438 439 // Check that all but the first colon is left alone. 440 _, err = juju.AddUnits(s.conn.State, svc, 1, "lxc:"+strings.Replace(id3, "/", ":", -1)) 441 c.Assert(err, gc.ErrorMatches, `invalid force machine id ".*"`) 442 } 443 444 // DeployLocalSuite uses a fresh copy of the same local dummy charm for each 445 // test, because DeployService demands that a charm already exists in state, 446 // and that's is the simplest way to get one in there. 447 type DeployLocalSuite struct { 448 testing.JujuConnSuite 449 repo charm.Repository 450 charm *state.Charm 451 oldCacheDir string 452 } 453 454 var _ = gc.Suite(&DeployLocalSuite{}) 455 456 func (s *DeployLocalSuite) SetUpSuite(c *gc.C) { 457 s.JujuConnSuite.SetUpSuite(c) 458 s.repo = &charm.LocalRepository{Path: coretesting.Charms.Path()} 459 s.oldCacheDir, charm.CacheDir = charm.CacheDir, c.MkDir() 460 } 461 462 func (s *DeployLocalSuite) TearDownSuite(c *gc.C) { 463 charm.CacheDir = s.oldCacheDir 464 s.JujuConnSuite.TearDownSuite(c) 465 } 466 467 func (s *DeployLocalSuite) SetUpTest(c *gc.C) { 468 s.JujuConnSuite.SetUpTest(c) 469 curl := charm.MustParseURL("local:quantal/dummy") 470 charm, err := s.Conn.PutCharm(curl, s.repo, false) 471 c.Assert(err, gc.IsNil) 472 s.charm = charm 473 } 474 475 func (s *DeployLocalSuite) TestDeployMinimal(c *gc.C) { 476 service, err := juju.DeployService(s.State, 477 juju.DeployServiceParams{ 478 ServiceName: "bob", 479 Charm: s.charm, 480 }) 481 c.Assert(err, gc.IsNil) 482 s.assertCharm(c, service, s.charm.URL()) 483 s.assertSettings(c, service, charm.Settings{}) 484 s.assertConstraints(c, service, constraints.Value{}) 485 s.assertMachines(c, service, constraints.Value{}) 486 } 487 488 func (s *DeployLocalSuite) TestDeploySettings(c *gc.C) { 489 service, err := juju.DeployService(s.State, 490 juju.DeployServiceParams{ 491 ServiceName: "bob", 492 Charm: s.charm, 493 ConfigSettings: charm.Settings{ 494 "title": "banana cupcakes", 495 "skill-level": 9901, 496 }, 497 }) 498 c.Assert(err, gc.IsNil) 499 s.assertSettings(c, service, charm.Settings{ 500 "title": "banana cupcakes", 501 "skill-level": int64(9901), 502 }) 503 } 504 505 func (s *DeployLocalSuite) TestDeploySettingsError(c *gc.C) { 506 _, err := juju.DeployService(s.State, 507 juju.DeployServiceParams{ 508 ServiceName: "bob", 509 Charm: s.charm, 510 ConfigSettings: charm.Settings{ 511 "skill-level": 99.01, 512 }, 513 }) 514 c.Assert(err, gc.ErrorMatches, `option "skill-level" expected int, got 99.01`) 515 _, err = s.State.Service("bob") 516 c.Assert(err, jc.Satisfies, errors.IsNotFoundError) 517 } 518 519 func (s *DeployLocalSuite) TestDeployConstraints(c *gc.C) { 520 err := s.State.SetEnvironConstraints(constraints.MustParse("mem=2G")) 521 c.Assert(err, gc.IsNil) 522 serviceCons := constraints.MustParse("cpu-cores=2") 523 service, err := juju.DeployService(s.State, 524 juju.DeployServiceParams{ 525 ServiceName: "bob", 526 Charm: s.charm, 527 Constraints: serviceCons, 528 }) 529 c.Assert(err, gc.IsNil) 530 s.assertConstraints(c, service, serviceCons) 531 } 532 533 func (s *DeployLocalSuite) TestDeployNumUnits(c *gc.C) { 534 err := s.State.SetEnvironConstraints(constraints.MustParse("mem=2G")) 535 c.Assert(err, gc.IsNil) 536 serviceCons := constraints.MustParse("cpu-cores=2") 537 service, err := juju.DeployService(s.State, 538 juju.DeployServiceParams{ 539 ServiceName: "bob", 540 Charm: s.charm, 541 Constraints: serviceCons, 542 NumUnits: 2, 543 }) 544 c.Assert(err, gc.IsNil) 545 s.assertConstraints(c, service, serviceCons) 546 s.assertMachines(c, service, constraints.MustParse("mem=2G cpu-cores=2"), "0", "1") 547 } 548 549 func (s *DeployLocalSuite) TestDeployWithForceMachineRejectsTooManyUnits(c *gc.C) { 550 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 551 c.Assert(err, gc.IsNil) 552 c.Assert(machine.Id(), gc.Equals, "0") 553 _, err = juju.DeployService(s.State, 554 juju.DeployServiceParams{ 555 ServiceName: "bob", 556 Charm: s.charm, 557 NumUnits: 2, 558 ToMachineSpec: "0", 559 }) 560 c.Assert(err, gc.ErrorMatches, "cannot use --num-units with --to") 561 } 562 563 func (s *DeployLocalSuite) TestDeployForceMachineId(c *gc.C) { 564 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 565 c.Assert(err, gc.IsNil) 566 c.Assert(machine.Id(), gc.Equals, "0") 567 err = s.State.SetEnvironConstraints(constraints.MustParse("mem=2G")) 568 c.Assert(err, gc.IsNil) 569 serviceCons := constraints.MustParse("cpu-cores=2") 570 service, err := juju.DeployService(s.State, 571 juju.DeployServiceParams{ 572 ServiceName: "bob", 573 Charm: s.charm, 574 Constraints: serviceCons, 575 NumUnits: 1, 576 ToMachineSpec: "0", 577 }) 578 c.Assert(err, gc.IsNil) 579 s.assertConstraints(c, service, serviceCons) 580 s.assertMachines(c, service, constraints.Value{}, "0") 581 } 582 583 func (s *DeployLocalSuite) TestDeployForceMachineIdWithContainer(c *gc.C) { 584 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 585 c.Assert(err, gc.IsNil) 586 c.Assert(machine.Id(), gc.Equals, "0") 587 cons := constraints.MustParse("mem=2G") 588 err = s.State.SetEnvironConstraints(cons) 589 c.Assert(err, gc.IsNil) 590 serviceCons := constraints.MustParse("cpu-cores=2") 591 service, err := juju.DeployService(s.State, 592 juju.DeployServiceParams{ 593 ServiceName: "bob", 594 Charm: s.charm, 595 Constraints: serviceCons, 596 NumUnits: 1, 597 ToMachineSpec: fmt.Sprintf("%s:0", instance.LXC), 598 }) 599 c.Assert(err, gc.IsNil) 600 s.assertConstraints(c, service, serviceCons) 601 units, err := service.AllUnits() 602 c.Assert(err, gc.IsNil) 603 c.Assert(units, gc.HasLen, 1) 604 605 // The newly created container will use the constraints. 606 id, err := units[0].AssignedMachineId() 607 c.Assert(err, gc.IsNil) 608 machine, err = s.State.Machine(id) 609 c.Assert(err, gc.IsNil) 610 expectedCons, err := machine.Constraints() 611 c.Assert(err, gc.IsNil) 612 c.Assert(cons, gc.DeepEquals, expectedCons) 613 } 614 615 func (s *DeployLocalSuite) assertCharm(c *gc.C, service *state.Service, expect *charm.URL) { 616 curl, force := service.CharmURL() 617 c.Assert(curl, gc.DeepEquals, expect) 618 c.Assert(force, gc.Equals, false) 619 } 620 621 func (s *DeployLocalSuite) assertSettings(c *gc.C, service *state.Service, expect charm.Settings) { 622 settings, err := service.ConfigSettings() 623 c.Assert(err, gc.IsNil) 624 c.Assert(settings, gc.DeepEquals, expect) 625 } 626 627 func (s *DeployLocalSuite) assertConstraints(c *gc.C, service *state.Service, expect constraints.Value) { 628 cons, err := service.Constraints() 629 c.Assert(err, gc.IsNil) 630 c.Assert(cons, gc.DeepEquals, expect) 631 } 632 633 func (s *DeployLocalSuite) assertMachines(c *gc.C, service *state.Service, expectCons constraints.Value, expectIds ...string) { 634 units, err := service.AllUnits() 635 c.Assert(err, gc.IsNil) 636 c.Assert(units, gc.HasLen, len(expectIds)) 637 unseenIds := set.NewStrings(expectIds...) 638 for _, unit := range units { 639 id, err := unit.AssignedMachineId() 640 c.Assert(err, gc.IsNil) 641 unseenIds.Remove(id) 642 machine, err := s.State.Machine(id) 643 c.Assert(err, gc.IsNil) 644 cons, err := machine.Constraints() 645 c.Assert(err, gc.IsNil) 646 c.Assert(cons, gc.DeepEquals, expectCons) 647 } 648 c.Assert(unseenIds, gc.DeepEquals, set.NewStrings()) 649 } 650 651 type InitJujuHomeSuite struct { 652 testbase.LoggingSuite 653 originalHome string 654 originalJujuHome string 655 } 656 657 var _ = gc.Suite(&InitJujuHomeSuite{}) 658 659 func (s *InitJujuHomeSuite) SetUpTest(c *gc.C) { 660 s.LoggingSuite.SetUpTest(c) 661 s.originalHome = osenv.Home() 662 s.originalJujuHome = os.Getenv("JUJU_HOME") 663 } 664 665 func (s *InitJujuHomeSuite) TearDownTest(c *gc.C) { 666 osenv.SetHome(s.originalHome) 667 os.Setenv("JUJU_HOME", s.originalJujuHome) 668 s.LoggingSuite.TearDownTest(c) 669 } 670 671 func (s *InitJujuHomeSuite) TestJujuHome(c *gc.C) { 672 jujuHome := c.MkDir() 673 os.Setenv("JUJU_HOME", jujuHome) 674 err := juju.InitJujuHome() 675 c.Assert(err, gc.IsNil) 676 c.Assert(osenv.JujuHome(), gc.Equals, jujuHome) 677 } 678 679 func (s *InitJujuHomeSuite) TestHome(c *gc.C) { 680 osHome := c.MkDir() 681 os.Setenv("JUJU_HOME", "") 682 osenv.SetHome(osHome) 683 err := juju.InitJujuHome() 684 c.Assert(err, gc.IsNil) 685 c.Assert(osenv.JujuHome(), gc.Equals, filepath.Join(osHome, ".juju")) 686 } 687 688 func (s *InitJujuHomeSuite) TestError(c *gc.C) { 689 os.Setenv("JUJU_HOME", "") 690 osenv.SetHome("") 691 err := juju.InitJujuHome() 692 c.Assert(err, gc.ErrorMatches, "cannot determine juju home.*") 693 } 694 695 func (s *InitJujuHomeSuite) TestCacheDir(c *gc.C) { 696 jujuHome := c.MkDir() 697 os.Setenv("JUJU_HOME", jujuHome) 698 c.Assert(charm.CacheDir, gc.Equals, "") 699 err := juju.InitJujuHome() 700 c.Assert(err, gc.IsNil) 701 c.Assert(charm.CacheDir, gc.Equals, filepath.Join(jujuHome, "charmcache")) 702 }