launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/cmd/jujud/machine_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "os" 8 "path/filepath" 9 "reflect" 10 "strings" 11 "time" 12 13 gc "launchpad.net/gocheck" 14 15 "launchpad.net/juju-core/agent" 16 "launchpad.net/juju-core/charm" 17 "launchpad.net/juju-core/cmd" 18 lxctesting "launchpad.net/juju-core/container/lxc/testing" 19 "launchpad.net/juju-core/environs/config" 20 envtesting "launchpad.net/juju-core/environs/testing" 21 "launchpad.net/juju-core/errors" 22 "launchpad.net/juju-core/instance" 23 "launchpad.net/juju-core/juju" 24 "launchpad.net/juju-core/juju/osenv" 25 "launchpad.net/juju-core/names" 26 "launchpad.net/juju-core/provider/dummy" 27 "launchpad.net/juju-core/state" 28 "launchpad.net/juju-core/state/api" 29 apideployer "launchpad.net/juju-core/state/api/deployer" 30 "launchpad.net/juju-core/state/api/params" 31 charmtesting "launchpad.net/juju-core/state/apiserver/charmrevisionupdater/testing" 32 statetesting "launchpad.net/juju-core/state/testing" 33 "launchpad.net/juju-core/state/watcher" 34 "launchpad.net/juju-core/testing" 35 coretesting "launchpad.net/juju-core/testing" 36 jc "launchpad.net/juju-core/testing/checkers" 37 "launchpad.net/juju-core/testing/testbase" 38 "launchpad.net/juju-core/tools" 39 "launchpad.net/juju-core/utils" 40 "launchpad.net/juju-core/utils/ssh" 41 sshtesting "launchpad.net/juju-core/utils/ssh/testing" 42 "launchpad.net/juju-core/version" 43 "launchpad.net/juju-core/worker/authenticationworker" 44 "launchpad.net/juju-core/worker/deployer" 45 "launchpad.net/juju-core/worker/instancepoller" 46 "launchpad.net/juju-core/worker/machineenvironmentworker" 47 ) 48 49 type MachineSuite struct { 50 agentSuite 51 lxctesting.TestSuite 52 } 53 54 var _ = gc.Suite(&MachineSuite{}) 55 56 func (s *MachineSuite) SetUpSuite(c *gc.C) { 57 s.agentSuite.SetUpSuite(c) 58 s.TestSuite.SetUpSuite(c) 59 restore := testbase.PatchValue(&charm.CacheDir, c.MkDir()) 60 s.AddSuiteCleanup(func(*gc.C) { restore() }) 61 } 62 63 func (s *MachineSuite) TearDownSuite(c *gc.C) { 64 s.TestSuite.TearDownSuite(c) 65 s.agentSuite.TearDownSuite(c) 66 } 67 68 func (s *MachineSuite) SetUpTest(c *gc.C) { 69 s.agentSuite.SetUpTest(c) 70 s.TestSuite.SetUpTest(c) 71 os.Remove(jujuRun) // ignore error; may not exist 72 // Fake $HOME, and ssh user to avoid touching ~ubuntu/.ssh/authorized_keys. 73 fakeHome := coretesting.MakeEmptyFakeHomeWithoutJuju(c) 74 s.AddCleanup(func(*gc.C) { fakeHome.Restore() }) 75 s.PatchValue(&authenticationworker.SSHUser, "") 76 } 77 78 func (s *MachineSuite) TearDownTest(c *gc.C) { 79 s.TestSuite.TearDownTest(c) 80 s.agentSuite.TearDownTest(c) 81 } 82 83 const initialMachinePassword = "machine-password-1234567890" 84 85 // primeAgent adds a new Machine to run the given jobs, and sets up the 86 // machine agent's directory. It returns the new machine, the 87 // agent's configuration and the tools currently running. 88 func (s *MachineSuite) primeAgent(c *gc.C, jobs ...state.MachineJob) (m *state.Machine, config agent.Config, tools *tools.Tools) { 89 m, err := s.State.AddOneMachine(state.MachineTemplate{ 90 Series: "quantal", 91 InstanceId: "ardbeg-0", 92 Nonce: state.BootstrapNonce, 93 Jobs: jobs, 94 }) 95 c.Assert(err, gc.IsNil) 96 err = m.SetPassword(initialMachinePassword) 97 c.Assert(err, gc.IsNil) 98 tag := names.MachineTag(m.Id()) 99 if m.IsManager() { 100 err = m.SetMongoPassword(initialMachinePassword) 101 c.Assert(err, gc.IsNil) 102 config, tools = s.agentSuite.primeStateAgent(c, tag, initialMachinePassword) 103 } else { 104 config, tools = s.agentSuite.primeAgent(c, tag, initialMachinePassword) 105 } 106 err = config.Write() 107 c.Assert(err, gc.IsNil) 108 return m, config, tools 109 } 110 111 // newAgent returns a new MachineAgent instance 112 func (s *MachineSuite) newAgent(c *gc.C, m *state.Machine) *MachineAgent { 113 a := &MachineAgent{} 114 s.initAgent(c, a, "--machine-id", m.Id()) 115 return a 116 } 117 118 func (s *MachineSuite) TestParseSuccess(c *gc.C) { 119 create := func() (cmd.Command, *AgentConf) { 120 a := &MachineAgent{} 121 return a, &a.Conf 122 } 123 a := CheckAgentCommand(c, create, []string{"--machine-id", "42"}) 124 c.Assert(a.(*MachineAgent).MachineId, gc.Equals, "42") 125 } 126 127 func (s *MachineSuite) TestParseNonsense(c *gc.C) { 128 for _, args := range [][]string{ 129 {}, 130 {"--machine-id", "-4004"}, 131 } { 132 err := ParseAgentCommand(&MachineAgent{}, args) 133 c.Assert(err, gc.ErrorMatches, "--machine-id option must be set, and expects a non-negative integer") 134 } 135 } 136 137 func (s *MachineSuite) TestParseUnknown(c *gc.C) { 138 a := &MachineAgent{} 139 err := ParseAgentCommand(a, []string{"--machine-id", "42", "blistering barnacles"}) 140 c.Assert(err, gc.ErrorMatches, `unrecognized args: \["blistering barnacles"\]`) 141 } 142 143 func (s *MachineSuite) TestRunInvalidMachineId(c *gc.C) { 144 c.Skip("agents don't yet distinguish between temporary and permanent errors") 145 m, _, _ := s.primeAgent(c, state.JobHostUnits) 146 err := s.newAgent(c, m).Run(nil) 147 c.Assert(err, gc.ErrorMatches, "some error") 148 } 149 150 func (s *MachineSuite) TestRunStop(c *gc.C) { 151 m, ac, _ := s.primeAgent(c, state.JobHostUnits) 152 a := s.newAgent(c, m) 153 done := make(chan error) 154 go func() { 155 done <- a.Run(nil) 156 }() 157 err := a.Stop() 158 c.Assert(err, gc.IsNil) 159 c.Assert(<-done, gc.IsNil) 160 c.Assert(charm.CacheDir, gc.Equals, filepath.Join(ac.DataDir(), "charmcache")) 161 } 162 163 func (s *MachineSuite) TestWithDeadMachine(c *gc.C) { 164 m, _, _ := s.primeAgent(c, state.JobHostUnits) 165 err := m.EnsureDead() 166 c.Assert(err, gc.IsNil) 167 a := s.newAgent(c, m) 168 err = runWithTimeout(a) 169 c.Assert(err, gc.IsNil) 170 } 171 172 func (s *MachineSuite) TestWithRemovedMachine(c *gc.C) { 173 m, _, _ := s.primeAgent(c, state.JobHostUnits) 174 err := m.EnsureDead() 175 c.Assert(err, gc.IsNil) 176 err = m.Remove() 177 c.Assert(err, gc.IsNil) 178 a := s.newAgent(c, m) 179 err = runWithTimeout(a) 180 c.Assert(err, gc.IsNil) 181 } 182 183 func (s *MachineSuite) TestDyingMachine(c *gc.C) { 184 c.Skip("Disabled as breaks test isolation somehow, see lp:1206195") 185 m, _, _ := s.primeAgent(c, state.JobHostUnits) 186 a := s.newAgent(c, m) 187 done := make(chan error) 188 go func() { 189 done <- a.Run(nil) 190 }() 191 defer func() { 192 c.Check(a.Stop(), gc.IsNil) 193 }() 194 err := m.Destroy() 195 c.Assert(err, gc.IsNil) 196 select { 197 case err := <-done: 198 c.Assert(err, gc.IsNil) 199 case <-time.After(watcher.Period * 5 / 4): 200 // TODO(rog) Fix this so it doesn't wait for so long. 201 // https://bugs.launchpad.net/juju-core/+bug/1163983 202 c.Fatalf("timed out waiting for agent to terminate") 203 } 204 err = m.Refresh() 205 c.Assert(err, gc.IsNil) 206 c.Assert(m.Life(), gc.Equals, state.Dead) 207 } 208 209 func (s *MachineSuite) TestHostUnits(c *gc.C) { 210 m, _, _ := s.primeAgent(c, state.JobHostUnits) 211 a := s.newAgent(c, m) 212 ctx, reset := patchDeployContext(c, s.BackingState) 213 defer reset() 214 go func() { c.Check(a.Run(nil), gc.IsNil) }() 215 defer func() { c.Check(a.Stop(), gc.IsNil) }() 216 217 // check that unassigned units don't trigger any deployments. 218 svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 219 u0, err := svc.AddUnit() 220 c.Assert(err, gc.IsNil) 221 u1, err := svc.AddUnit() 222 c.Assert(err, gc.IsNil) 223 ctx.waitDeployed(c) 224 225 // assign u0, check it's deployed. 226 err = u0.AssignToMachine(m) 227 c.Assert(err, gc.IsNil) 228 ctx.waitDeployed(c, u0.Name()) 229 230 // "start the agent" for u0 to prevent short-circuited remove-on-destroy; 231 // check that it's kept deployed despite being Dying. 232 err = u0.SetStatus(params.StatusStarted, "", nil) 233 c.Assert(err, gc.IsNil) 234 err = u0.Destroy() 235 c.Assert(err, gc.IsNil) 236 ctx.waitDeployed(c, u0.Name()) 237 238 // add u1 to the machine, check it's deployed. 239 err = u1.AssignToMachine(m) 240 c.Assert(err, gc.IsNil) 241 ctx.waitDeployed(c, u0.Name(), u1.Name()) 242 243 // make u0 dead; check the deployer recalls the unit and removes it from 244 // state. 245 err = u0.EnsureDead() 246 c.Assert(err, gc.IsNil) 247 ctx.waitDeployed(c, u1.Name()) 248 249 // The deployer actually removes the unit just after 250 // removing its deployment, so we need to poll here 251 // until it actually happens. 252 for attempt := coretesting.LongAttempt.Start(); attempt.Next(); { 253 err := u0.Refresh() 254 if err == nil && attempt.HasNext() { 255 continue 256 } 257 c.Assert(err, jc.Satisfies, errors.IsNotFoundError) 258 } 259 260 // short-circuit-remove u1 after it's been deployed; check it's recalled 261 // and removed from state. 262 err = u1.Destroy() 263 c.Assert(err, gc.IsNil) 264 err = u1.Refresh() 265 c.Assert(err, jc.Satisfies, errors.IsNotFoundError) 266 ctx.waitDeployed(c) 267 } 268 269 func patchDeployContext(c *gc.C, st *state.State) (*fakeContext, func()) { 270 ctx := &fakeContext{ 271 inited: make(chan struct{}), 272 } 273 orig := newDeployContext 274 newDeployContext = func(dst *apideployer.State, agentConfig agent.Config) deployer.Context { 275 ctx.st = st 276 ctx.agentConfig = agentConfig 277 close(ctx.inited) 278 return ctx 279 } 280 return ctx, func() { newDeployContext = orig } 281 } 282 283 func (s *MachineSuite) TestManageEnviron(c *gc.C) { 284 usefulVersion := version.Current 285 usefulVersion.Series = "quantal" // to match the charm created below 286 envtesting.AssertUploadFakeToolsVersions(c, s.Conn.Environ.Storage(), usefulVersion) 287 m, _, _ := s.primeAgent(c, state.JobManageEnviron) 288 err := m.SetAddresses([]instance.Address{ 289 instance.NewAddress("0.1.2.3"), 290 }) 291 c.Assert(err, gc.IsNil) 292 op := make(chan dummy.Operation, 200) 293 dummy.Listen(op) 294 295 a := s.newAgent(c, m) 296 // Make sure the agent is stopped even if the test fails. 297 defer a.Stop() 298 done := make(chan error) 299 go func() { 300 done <- a.Run(nil) 301 }() 302 303 // Check that the provisioner and firewaller are alive by doing 304 // a rudimentary check that it responds to state changes. 305 306 // Add one unit to a service; it should get allocated a machine 307 // and then its ports should be opened. 308 charm := s.AddTestingCharm(c, "dummy") 309 svc := s.AddTestingService(c, "test-service", charm) 310 err = svc.SetExposed() 311 c.Assert(err, gc.IsNil) 312 units, err := juju.AddUnits(s.State, svc, 1, "") 313 c.Assert(err, gc.IsNil) 314 c.Check(opRecvTimeout(c, s.State, op, dummy.OpStartInstance{}), gc.NotNil) 315 316 // Wait for the instance id to show up in the state. 317 s.waitProvisioned(c, units[0]) 318 err = units[0].OpenPort("tcp", 999) 319 c.Assert(err, gc.IsNil) 320 321 c.Check(opRecvTimeout(c, s.State, op, dummy.OpOpenPorts{}), gc.NotNil) 322 323 err = a.Stop() 324 c.Assert(err, gc.IsNil) 325 326 select { 327 case err := <-done: 328 c.Assert(err, gc.IsNil) 329 case <-time.After(5 * time.Second): 330 c.Fatalf("timed out waiting for agent to terminate") 331 } 332 } 333 334 func (s *MachineSuite) TestManageEnvironRunsInstancePoller(c *gc.C) { 335 defer testbase.PatchValue(&instancepoller.ShortPoll, 500*time.Millisecond).Restore() 336 usefulVersion := version.Current 337 usefulVersion.Series = "quantal" // to match the charm created below 338 envtesting.AssertUploadFakeToolsVersions(c, s.Conn.Environ.Storage(), usefulVersion) 339 m, _, _ := s.primeAgent(c, state.JobManageEnviron) 340 err := m.SetAddresses([]instance.Address{ 341 instance.NewAddress("0.1.2.3"), 342 }) 343 c.Assert(err, gc.IsNil) 344 a := s.newAgent(c, m) 345 defer a.Stop() 346 go func() { 347 c.Check(a.Run(nil), gc.IsNil) 348 }() 349 350 // Add one unit to a service; 351 charm := s.AddTestingCharm(c, "dummy") 352 svc := s.AddTestingService(c, "test-service", charm) 353 units, err := juju.AddUnits(s.State, svc, 1, "") 354 c.Assert(err, gc.IsNil) 355 356 m, instId := s.waitProvisioned(c, units[0]) 357 insts, err := s.Conn.Environ.Instances([]instance.Id{instId}) 358 c.Assert(err, gc.IsNil) 359 addrs := []instance.Address{instance.NewAddress("1.2.3.4")} 360 dummy.SetInstanceAddresses(insts[0], addrs) 361 dummy.SetInstanceStatus(insts[0], "running") 362 363 for a := coretesting.LongAttempt.Start(); a.Next(); { 364 if !a.HasNext() { 365 c.Logf("final machine addresses: %#v", m.Addresses()) 366 c.Fatalf("timed out waiting for machine to get address") 367 } 368 err := m.Refresh() 369 c.Assert(err, gc.IsNil) 370 instStatus, err := m.InstanceStatus() 371 c.Assert(err, gc.IsNil) 372 if reflect.DeepEqual(m.Addresses(), addrs) && instStatus == "running" { 373 break 374 } 375 } 376 377 } 378 379 func (s *MachineSuite) waitProvisioned(c *gc.C, unit *state.Unit) (*state.Machine, instance.Id) { 380 c.Logf("waiting for unit %q to be provisioned", unit) 381 machineId, err := unit.AssignedMachineId() 382 c.Assert(err, gc.IsNil) 383 m, err := s.State.Machine(machineId) 384 c.Assert(err, gc.IsNil) 385 w := m.Watch() 386 defer w.Stop() 387 timeout := time.After(coretesting.LongWait) 388 for { 389 select { 390 case <-timeout: 391 c.Fatalf("timed out waiting for provisioning") 392 case _, ok := <-w.Changes(): 393 c.Assert(ok, jc.IsTrue) 394 err := m.Refresh() 395 c.Assert(err, gc.IsNil) 396 if instId, err := m.InstanceId(); err == nil { 397 c.Logf("unit provisioned with instance %s", instId) 398 return m, instId 399 } else { 400 c.Check(err, jc.Satisfies, state.IsNotProvisionedError) 401 } 402 } 403 } 404 panic("watcher died") 405 } 406 407 func (s *MachineSuite) TestUpgrade(c *gc.C) { 408 m, _, currentTools := s.primeAgent(c, state.JobManageEnviron, state.JobHostUnits) 409 a := s.newAgent(c, m) 410 s.testUpgrade(c, a, m.Tag(), currentTools) 411 } 412 413 var fastDialOpts = api.DialOpts{ 414 Timeout: coretesting.LongWait, 415 RetryDelay: coretesting.ShortWait, 416 } 417 418 func (s *MachineSuite) waitStopped(c *gc.C, job state.MachineJob, a *MachineAgent, done chan error) { 419 err := a.Stop() 420 if job == state.JobManageEnviron { 421 // When shutting down, the API server can be shut down before 422 // the other workers that connect to it, so they get an error so 423 // they then die, causing Stop to return an error. It's not 424 // easy to control the actual error that's received in this 425 // circumstance so we just log it rather than asserting that it 426 // is not nil. 427 if err != nil { 428 c.Logf("error shutting down state manager: %v", err) 429 } 430 } else { 431 c.Assert(err, gc.IsNil) 432 } 433 434 select { 435 case err := <-done: 436 c.Assert(err, gc.IsNil) 437 case <-time.After(5 * time.Second): 438 c.Fatalf("timed out waiting for agent to terminate") 439 } 440 } 441 442 func (s *MachineSuite) assertJobWithAPI( 443 c *gc.C, 444 job state.MachineJob, 445 test func(agent.Config, *api.State), 446 ) { 447 stm, conf, _ := s.primeAgent(c, job) 448 a := s.newAgent(c, stm) 449 defer a.Stop() 450 451 // All state jobs currently also run an APIWorker, so no 452 // need to check for that here, like in assertJobWithState. 453 454 agentAPIs := make(chan *api.State, 1000) 455 undo := sendOpenedAPIs(agentAPIs) 456 defer undo() 457 458 done := make(chan error) 459 go func() { 460 done <- a.Run(nil) 461 }() 462 463 select { 464 case agentAPI := <-agentAPIs: 465 c.Assert(agentAPI, gc.NotNil) 466 test(conf, agentAPI) 467 case <-time.After(coretesting.LongWait): 468 c.Fatalf("API not opened") 469 } 470 471 s.waitStopped(c, job, a, done) 472 } 473 474 func (s *MachineSuite) assertJobWithState( 475 c *gc.C, 476 job state.MachineJob, 477 test func(agent.Config, *state.State), 478 ) { 479 paramsJob := job.ToParams() 480 if !paramsJob.NeedsState() { 481 c.Fatalf("%v does not use state", paramsJob) 482 } 483 stm, conf, _ := s.primeAgent(c, job) 484 a := s.newAgent(c, stm) 485 defer a.Stop() 486 487 agentStates := make(chan *state.State, 1000) 488 undo := sendOpenedStates(agentStates) 489 defer undo() 490 491 done := make(chan error) 492 go func() { 493 done <- a.Run(nil) 494 }() 495 496 select { 497 case agentState := <-agentStates: 498 c.Assert(agentState, gc.NotNil) 499 test(conf, agentState) 500 case <-time.After(coretesting.LongWait): 501 c.Fatalf("state not opened") 502 } 503 504 s.waitStopped(c, job, a, done) 505 } 506 507 // TODO(jam): 2013-09-02 http://pad.lv/1219661 508 // This test has been failing regularly on the Bot. Until someone fixes it so 509 // it doesn't crash, it isn't worth having as we can't tell when someone 510 // actually breaks something. 511 func (s *MachineSuite) TestManageEnvironServesAPI(c *gc.C) { 512 c.Skip("does not pass reliably on the bot (http://pad.lv/1219661") 513 s.assertJobWithState(c, state.JobManageEnviron, func(conf agent.Config, agentState *state.State) { 514 st, _, err := conf.OpenAPI(fastDialOpts) 515 c.Assert(err, gc.IsNil) 516 defer st.Close() 517 m, err := st.Machiner().Machine(conf.Tag()) 518 c.Assert(err, gc.IsNil) 519 c.Assert(m.Life(), gc.Equals, params.Alive) 520 }) 521 } 522 523 func (s *MachineSuite) TestManageEnvironRunsCleaner(c *gc.C) { 524 s.assertJobWithState(c, state.JobManageEnviron, func(conf agent.Config, agentState *state.State) { 525 // Create a service and unit, and destroy the service. 526 service := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 527 unit, err := service.AddUnit() 528 c.Assert(err, gc.IsNil) 529 err = service.Destroy() 530 c.Assert(err, gc.IsNil) 531 532 // Check the unit was not yet removed. 533 err = unit.Refresh() 534 c.Assert(err, gc.IsNil) 535 w := unit.Watch() 536 defer w.Stop() 537 538 // Trigger a sync on the state used by the agent, and wait 539 // for the unit to be removed. 540 agentState.StartSync() 541 timeout := time.After(coretesting.LongWait) 542 for done := false; !done; { 543 select { 544 case <-timeout: 545 c.Fatalf("unit not cleaned up") 546 case <-time.After(coretesting.ShortWait): 547 s.State.StartSync() 548 case <-w.Changes(): 549 err := unit.Refresh() 550 if errors.IsNotFoundError(err) { 551 done = true 552 } else { 553 c.Assert(err, gc.IsNil) 554 } 555 } 556 } 557 }) 558 } 559 560 func (s *MachineSuite) TestJobManageEnvironRunsMinUnitsWorker(c *gc.C) { 561 s.assertJobWithState(c, state.JobManageEnviron, func(conf agent.Config, agentState *state.State) { 562 // Ensure that the MinUnits worker is alive by doing a simple check 563 // that it responds to state changes: add a service, set its minimum 564 // number of units to one, wait for the worker to add the missing unit. 565 service := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 566 err := service.SetMinUnits(1) 567 c.Assert(err, gc.IsNil) 568 w := service.Watch() 569 defer w.Stop() 570 571 // Trigger a sync on the state used by the agent, and wait for the unit 572 // to be created. 573 agentState.StartSync() 574 timeout := time.After(coretesting.LongWait) 575 for { 576 select { 577 case <-timeout: 578 c.Fatalf("unit not created") 579 case <-time.After(coretesting.ShortWait): 580 s.State.StartSync() 581 case <-w.Changes(): 582 units, err := service.AllUnits() 583 c.Assert(err, gc.IsNil) 584 if len(units) == 1 { 585 return 586 } 587 } 588 } 589 }) 590 } 591 592 func (s *MachineSuite) TestMachineAgentRunsAuthorisedKeysWorker(c *gc.C) { 593 // Start the machine agent. 594 m, _, _ := s.primeAgent(c, state.JobHostUnits) 595 a := s.newAgent(c, m) 596 go func() { c.Check(a.Run(nil), gc.IsNil) }() 597 defer func() { c.Check(a.Stop(), gc.IsNil) }() 598 599 // Update the keys in the environment. 600 sshKey := sshtesting.ValidKeyOne.Key + " user@host" 601 err := statetesting.UpdateConfig(s.BackingState, map[string]interface{}{"authorized-keys": sshKey}) 602 c.Assert(err, gc.IsNil) 603 604 // Wait for ssh keys file to be updated. 605 s.State.StartSync() 606 timeout := time.After(coretesting.LongWait) 607 sshKeyWithCommentPrefix := sshtesting.ValidKeyOne.Key + " Juju:user@host" 608 for { 609 select { 610 case <-timeout: 611 c.Fatalf("timeout while waiting for authorised ssh keys to change") 612 case <-time.After(coretesting.ShortWait): 613 keys, err := ssh.ListKeys(authenticationworker.SSHUser, ssh.FullKeys) 614 c.Assert(err, gc.IsNil) 615 keysStr := strings.Join(keys, "\n") 616 if sshKeyWithCommentPrefix != keysStr { 617 continue 618 } 619 return 620 } 621 } 622 } 623 624 // opRecvTimeout waits for any of the given kinds of operation to 625 // be received from ops, and times out if not. 626 func opRecvTimeout(c *gc.C, st *state.State, opc <-chan dummy.Operation, kinds ...dummy.Operation) dummy.Operation { 627 st.StartSync() 628 for { 629 select { 630 case op := <-opc: 631 for _, k := range kinds { 632 if reflect.TypeOf(op) == reflect.TypeOf(k) { 633 return op 634 } 635 } 636 c.Logf("discarding unknown event %#v", op) 637 case <-time.After(15 * time.Second): 638 c.Fatalf("time out wating for operation") 639 } 640 } 641 } 642 643 func (s *MachineSuite) TestOpenStateFailsForJobHostUnitsButOpenAPIWorks(c *gc.C) { 644 m, _, _ := s.primeAgent(c, state.JobHostUnits) 645 s.testOpenAPIState(c, m, s.newAgent(c, m), initialMachinePassword) 646 s.assertJobWithAPI(c, state.JobHostUnits, func(conf agent.Config, st *api.State) { 647 s.assertCannotOpenState(c, conf.Tag(), conf.DataDir()) 648 }) 649 } 650 651 func (s *MachineSuite) TestOpenStateWorksForJobManageEnviron(c *gc.C) { 652 s.assertJobWithAPI(c, state.JobManageEnviron, func(conf agent.Config, st *api.State) { 653 s.assertCanOpenState(c, conf.Tag(), conf.DataDir()) 654 }) 655 } 656 657 func (s *MachineSuite) TestMachineAgentSymlinkJujuRun(c *gc.C) { 658 _, err := os.Stat(jujuRun) 659 c.Assert(err, jc.Satisfies, os.IsNotExist) 660 s.assertJobWithAPI(c, state.JobManageEnviron, func(conf agent.Config, st *api.State) { 661 // juju-run should have been created 662 _, err := os.Stat(jujuRun) 663 c.Assert(err, gc.IsNil) 664 }) 665 } 666 667 func (s *MachineSuite) TestMachineAgentSymlinkJujuRunExists(c *gc.C) { 668 err := os.Symlink("/nowhere/special", jujuRun) 669 c.Assert(err, gc.IsNil) 670 _, err = os.Stat(jujuRun) 671 c.Assert(err, jc.Satisfies, os.IsNotExist) 672 s.assertJobWithAPI(c, state.JobManageEnviron, func(conf agent.Config, st *api.State) { 673 // juju-run should have been recreated 674 _, err := os.Stat(jujuRun) 675 c.Assert(err, gc.IsNil) 676 link, err := os.Readlink(jujuRun) 677 c.Assert(err, gc.IsNil) 678 c.Assert(link, gc.Not(gc.Equals), "/nowhere/special") 679 }) 680 } 681 682 func (s *MachineSuite) TestMachineEnvirnWorker(c *gc.C) { 683 proxyDir := c.MkDir() 684 s.PatchValue(&machineenvironmentworker.ProxyDirectory, proxyDir) 685 s.PatchValue(&utils.AptConfFile, filepath.Join(proxyDir, "juju-apt-proxy")) 686 687 s.primeAgent(c, state.JobHostUnits) 688 // Make sure there are some proxy settings to write. 689 oldConfig, err := s.State.EnvironConfig() 690 c.Assert(err, gc.IsNil) 691 692 proxySettings := osenv.ProxySettings{ 693 Http: "http proxy", 694 Https: "https proxy", 695 Ftp: "ftp proxy", 696 } 697 698 envConfig, err := oldConfig.Apply(config.ProxyConfigMap(proxySettings)) 699 c.Assert(err, gc.IsNil) 700 701 err = s.State.SetEnvironConfig(envConfig, oldConfig) 702 c.Assert(err, gc.IsNil) 703 704 s.assertJobWithAPI(c, state.JobHostUnits, func(conf agent.Config, st *api.State) { 705 for { 706 select { 707 case <-time.After(testing.LongWait): 708 c.Fatalf("timeout while waiting for proxy settings to change") 709 case <-time.After(10 * time.Millisecond): 710 _, err := os.Stat(utils.AptConfFile) 711 if os.IsNotExist(err) { 712 continue 713 } 714 c.Assert(err, gc.IsNil) 715 return 716 } 717 } 718 }) 719 } 720 721 func (s *MachineSuite) TestMachineAgentUninstall(c *gc.C) { 722 m, ac, _ := s.primeAgent(c, state.JobHostUnits) 723 err := m.EnsureDead() 724 c.Assert(err, gc.IsNil) 725 a := s.newAgent(c, m) 726 err = runWithTimeout(a) 727 c.Assert(err, gc.IsNil) 728 // juju-run should have been removed on termination 729 _, err = os.Stat(jujuRun) 730 c.Assert(err, jc.Satisfies, os.IsNotExist) 731 // data-dir should have been removed on termination 732 _, err = os.Stat(ac.DataDir()) 733 c.Assert(err, jc.Satisfies, os.IsNotExist) 734 } 735 736 // MachineWithCharmsSuite provides infrastructure for tests which need to 737 // work with charms. 738 type MachineWithCharmsSuite struct { 739 charmtesting.CharmSuite 740 741 machine *state.Machine 742 } 743 744 var _ = gc.Suite(&MachineWithCharmsSuite{}) 745 746 func (s *MachineWithCharmsSuite) SetUpTest(c *gc.C) { 747 s.CharmSuite.SetUpTest(c) 748 749 // Create a state server machine. 750 var err error 751 s.machine, err = s.State.AddOneMachine(state.MachineTemplate{ 752 Series: "quantal", 753 InstanceId: "ardbeg-0", 754 Nonce: state.BootstrapNonce, 755 Jobs: []state.MachineJob{state.JobManageEnviron}, 756 }) 757 c.Assert(err, gc.IsNil) 758 err = s.machine.SetPassword(initialMachinePassword) 759 c.Assert(err, gc.IsNil) 760 tag := names.MachineTag(s.machine.Id()) 761 err = s.machine.SetMongoPassword(initialMachinePassword) 762 c.Assert(err, gc.IsNil) 763 764 // Set up the agent configuration. 765 stateInfo := s.StateInfo(c) 766 writeStateAgentConfig(c, stateInfo, s.DataDir(), tag, initialMachinePassword) 767 } 768 769 func (s *MachineWithCharmsSuite) TestManageEnvironRunsCharmRevisionUpdater(c *gc.C) { 770 s.SetupScenario(c) 771 772 // Start the machine agent. 773 a := &MachineAgent{} 774 args := []string{"--data-dir", s.DataDir(), "--machine-id", s.machine.Id()} 775 err := coretesting.InitCommand(a, args) 776 c.Assert(err, gc.IsNil) 777 778 go func() { 779 c.Check(a.Run(nil), gc.IsNil) 780 }() 781 defer func() { c.Check(a.Stop(), gc.IsNil) }() 782 783 checkRevision := func() bool { 784 curl := charm.MustParseURL("cs:quantal/mysql") 785 placeholder, err := s.State.LatestPlaceholderCharm(curl) 786 return err == nil && placeholder.String() == curl.WithRevision(23).String() 787 } 788 success := false 789 for attempt := coretesting.LongAttempt.Start(); attempt.Next(); { 790 if success = checkRevision(); success { 791 break 792 } 793 } 794 c.Assert(success, gc.Equals, true) 795 }