github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/jujud/agent/machine_test.go (about) 1 // Copyright 2012-2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package agent 5 6 import ( 7 "bufio" 8 "bytes" 9 "encoding/json" 10 "io/ioutil" 11 "os" 12 "path/filepath" 13 "reflect" 14 "runtime" 15 "runtime/pprof" 16 "strings" 17 "time" 18 19 "github.com/juju/cmd" 20 "github.com/juju/cmd/cmdtesting" 21 "github.com/juju/collections/set" 22 "github.com/juju/errors" 23 "github.com/juju/juju/cmd/jujud/agent/agenttest" 24 "github.com/juju/os/series" 25 jc "github.com/juju/testing/checkers" 26 "github.com/juju/utils" 27 "github.com/juju/utils/arch" 28 "github.com/juju/utils/cert" 29 "github.com/juju/utils/ssh" 30 sshtesting "github.com/juju/utils/ssh/testing" 31 "github.com/juju/utils/symlink" 32 "github.com/juju/version" 33 gc "gopkg.in/check.v1" 34 "gopkg.in/juju/charmrepo.v3" 35 "gopkg.in/juju/names.v2" 36 "gopkg.in/juju/worker.v1" 37 "gopkg.in/juju/worker.v1/dependency" 38 "gopkg.in/juju/worker.v1/workertest" 39 "gopkg.in/natefinch/lumberjack.v2" 40 41 "github.com/juju/juju/agent" 42 "github.com/juju/juju/api" 43 apimachiner "github.com/juju/juju/api/machiner" 44 "github.com/juju/juju/apiserver/params" 45 "github.com/juju/juju/cloud" 46 "github.com/juju/juju/cmd/jujud/agent/model" 47 "github.com/juju/juju/controller" 48 "github.com/juju/juju/core/auditlog" 49 "github.com/juju/juju/core/instance" 50 "github.com/juju/juju/core/migration" 51 "github.com/juju/juju/environs" 52 "github.com/juju/juju/environs/context" 53 envtesting "github.com/juju/juju/environs/testing" 54 "github.com/juju/juju/network" 55 "github.com/juju/juju/provider/dummy" 56 "github.com/juju/juju/state" 57 "github.com/juju/juju/state/multiwatcher" 58 "github.com/juju/juju/storage" 59 coretesting "github.com/juju/juju/testing" 60 "github.com/juju/juju/testing/factory" 61 "github.com/juju/juju/tools" 62 jujuversion "github.com/juju/juju/version" 63 jworker "github.com/juju/juju/worker" 64 "github.com/juju/juju/worker/authenticationworker" 65 "github.com/juju/juju/worker/diskmanager" 66 "github.com/juju/juju/worker/instancepoller" 67 "github.com/juju/juju/worker/machiner" 68 "github.com/juju/juju/worker/migrationmaster" 69 "github.com/juju/juju/worker/storageprovisioner" 70 "github.com/juju/juju/worker/upgrader" 71 ) 72 73 type MachineLegacyLeasesSuite struct { 74 commonMachineSuite 75 } 76 77 var _ = gc.Suite(&MachineLegacyLeasesSuite{}) 78 79 func (s *MachineLegacyLeasesSuite) SetUpTest(c *gc.C) { 80 s.ControllerConfigAttrs = map[string]interface{}{ 81 controller.AuditingEnabled: true, 82 controller.CharmStoreURL: "staging.charmstore", 83 controller.Features: []interface{}{"legacy-leases"}, 84 } 85 s.commonMachineSuite.SetUpTest(c) 86 coretesting.DumpTestLogsAfter(time.Minute, c, s) 87 } 88 89 // TODO (manadart 2018-10-26): Tests that work with Raft leases should be 90 // migrated to this suite. 91 // When the Raft startup issue is resolved all tests can use this suite 92 // and the MachineLegacyLeasesSuite can be removed. 93 type MachineSuite struct { 94 commonMachineSuite 95 } 96 97 var _ = gc.Suite(&MachineSuite{}) 98 99 func (s *MachineSuite) SetUpTest(c *gc.C) { 100 s.ControllerConfigAttrs = map[string]interface{}{ 101 controller.AuditingEnabled: true, 102 controller.CharmStoreURL: "staging.charmstore", 103 } 104 s.commonMachineSuite.SetUpTest(c) 105 // Most of these tests normally finish sub-second on a fast machine. 106 // If any given test hits a minute, we have almost certainly become 107 // wedged, so dump the logs. 108 coretesting.DumpTestLogsAfter(time.Minute, c, s) 109 } 110 111 func (s *MachineLegacyLeasesSuite) TestParseNonsense(c *gc.C) { 112 for _, args := range [][]string{ 113 {}, 114 {"--machine-id", "-4004"}, 115 } { 116 var agentConf agentConf 117 err := ParseAgentCommand(&machineAgentCmd{agentInitializer: &agentConf}, args) 118 c.Assert(err, gc.ErrorMatches, "--machine-id option must be set, and expects a non-negative integer") 119 } 120 } 121 122 func (s *MachineLegacyLeasesSuite) TestParseUnknown(c *gc.C) { 123 var agentConf agentConf 124 a := &machineAgentCmd{agentInitializer: &agentConf} 125 err := ParseAgentCommand(a, []string{"--machine-id", "42", "blistering barnacles"}) 126 c.Assert(err, gc.ErrorMatches, `unrecognized args: \["blistering barnacles"\]`) 127 } 128 129 func (s *MachineLegacyLeasesSuite) TestParseSuccess(c *gc.C) { 130 create := func() (cmd.Command, AgentConf) { 131 agentConf := agentConf{dataDir: s.DataDir()} 132 logger := s.newBufferedLogWriter() 133 a := NewMachineAgentCmd( 134 nil, 135 NewTestMachineAgentFactory(&agentConf, logger, c.MkDir()), 136 &agentConf, 137 &agentConf, 138 ) 139 a.(*machineAgentCmd).logToStdErr = true 140 141 return a, &agentConf 142 } 143 a := CheckAgentCommand(c, create, []string{"--machine-id", "42"}) 144 c.Assert(a.(*machineAgentCmd).machineId, gc.Equals, "42") 145 } 146 147 func (s *MachineLegacyLeasesSuite) TestRunInvalidMachineId(c *gc.C) { 148 c.Skip("agents don't yet distinguish between temporary and permanent errors") 149 m, _, _ := s.primeAgent(c, state.JobHostUnits) 150 err := s.newAgent(c, m).Run(nil) 151 c.Assert(err, gc.ErrorMatches, "some error") 152 } 153 154 func (s *MachineLegacyLeasesSuite) TestUseLumberjack(c *gc.C) { 155 ctx := cmdtesting.Context(c) 156 agentConf := FakeAgentConfig{} 157 logger := s.newBufferedLogWriter() 158 159 a := NewMachineAgentCmd( 160 ctx, 161 NewTestMachineAgentFactory(&agentConf, logger, c.MkDir()), 162 agentConf, 163 agentConf, 164 ) 165 // little hack to set the data that Init expects to already be set 166 a.(*machineAgentCmd).machineId = "42" 167 168 err := a.Init(nil) 169 c.Assert(err, gc.IsNil) 170 171 l, ok := ctx.Stderr.(*lumberjack.Logger) 172 c.Assert(ok, jc.IsTrue) 173 c.Check(l.MaxAge, gc.Equals, 0) 174 c.Check(l.MaxBackups, gc.Equals, 2) 175 c.Check(l.Filename, gc.Equals, filepath.FromSlash("/var/log/juju/machine-42.log")) 176 c.Check(l.MaxSize, gc.Equals, 300) 177 } 178 179 func (s *MachineLegacyLeasesSuite) TestDontUseLumberjack(c *gc.C) { 180 ctx := cmdtesting.Context(c) 181 agentConf := FakeAgentConfig{} 182 logger := s.newBufferedLogWriter() 183 184 a := NewMachineAgentCmd( 185 ctx, 186 NewTestMachineAgentFactory(&agentConf, logger, c.MkDir()), 187 agentConf, 188 agentConf, 189 ) 190 // little hack to set the data that Init expects to already be set 191 a.(*machineAgentCmd).machineId = "42" 192 193 // set the value that normally gets set by the flag parsing 194 a.(*machineAgentCmd).logToStdErr = true 195 196 err := a.Init(nil) 197 c.Assert(err, gc.IsNil) 198 199 _, ok := ctx.Stderr.(*lumberjack.Logger) 200 c.Assert(ok, jc.IsFalse) 201 } 202 203 func (s *MachineLegacyLeasesSuite) TestRunStop(c *gc.C) { 204 m, ac, _ := s.primeAgent(c, state.JobHostUnits) 205 a := s.newAgent(c, m) 206 done := make(chan error) 207 go func() { 208 done <- a.Run(nil) 209 }() 210 err := a.Stop() 211 c.Assert(err, jc.ErrorIsNil) 212 c.Assert(<-done, jc.ErrorIsNil) 213 c.Assert(charmrepo.CacheDir, gc.Equals, filepath.Join(ac.DataDir(), "charmcache")) 214 } 215 216 func (s *MachineLegacyLeasesSuite) TestWithDeadMachine(c *gc.C) { 217 m, ac, _ := s.primeAgent(c, state.JobHostUnits) 218 err := m.EnsureDead() 219 c.Assert(err, jc.ErrorIsNil) 220 a := s.newAgent(c, m) 221 err = runWithTimeout(a) 222 c.Assert(err, jc.ErrorIsNil) 223 224 _, err = os.Stat(ac.DataDir()) 225 c.Assert(err, jc.Satisfies, os.IsNotExist) 226 } 227 228 func (s *MachineLegacyLeasesSuite) TestWithRemovedMachine(c *gc.C) { 229 m, ac, _ := s.primeAgent(c, state.JobHostUnits) 230 err := m.EnsureDead() 231 c.Assert(err, jc.ErrorIsNil) 232 err = m.Remove() 233 c.Assert(err, jc.ErrorIsNil) 234 a := s.newAgent(c, m) 235 err = runWithTimeout(a) 236 c.Assert(err, jc.ErrorIsNil) 237 238 _, err = os.Stat(ac.DataDir()) 239 c.Assert(err, jc.Satisfies, os.IsNotExist) 240 } 241 242 func (s *MachineLegacyLeasesSuite) TestDyingMachine(c *gc.C) { 243 m, _, _ := s.primeAgent(c, state.JobHostUnits) 244 a := s.newAgent(c, m) 245 done := make(chan error) 246 go func() { 247 done <- a.Run(nil) 248 }() 249 defer func() { 250 c.Check(a.Stop(), jc.ErrorIsNil) 251 }() 252 // Wait for configuration to be finished 253 <-a.WorkersStarted() 254 err := m.Destroy() 255 c.Assert(err, jc.ErrorIsNil) 256 // Tearing down the dependency engine can take a non-trivial amount of 257 // time. 258 select { 259 case err := <-done: 260 c.Assert(err, jc.ErrorIsNil) 261 case <-time.After(coretesting.LongWait): 262 // This test intermittently fails and we haven't been able to determine 263 // why it gets wedged. So we will dump the goroutines before the fatal call. 264 buff := bytes.Buffer{} 265 err = pprof.Lookup("goroutine").WriteTo(&buff, 1) 266 c.Check(err, jc.ErrorIsNil) 267 c.Logf("\nagent didn't stop, here's what it was doing\n\n%s", buff) 268 c.Fatalf("timed out waiting for agent to terminate") 269 } 270 err = m.Refresh() 271 c.Assert(err, jc.ErrorIsNil) 272 c.Assert(m.Life(), gc.Equals, state.Dead) 273 } 274 275 func (s *MachineLegacyLeasesSuite) TestManageModelRunsInstancePoller(c *gc.C) { 276 s.AgentSuite.PatchValue(&instancepoller.ShortPoll, 500*time.Millisecond) 277 usefulVersion := version.Binary{ 278 Number: jujuversion.Current, 279 Arch: arch.HostArch(), 280 Series: "quantal", // to match the charm created below 281 } 282 envtesting.AssertUploadFakeToolsVersions( 283 c, s.DefaultToolsStorage, 284 s.Environ.Config().AgentStream(), 285 s.Environ.Config().AgentStream(), 286 usefulVersion, 287 ) 288 m, _, _ := s.primeAgent(c, state.JobManageModel) 289 a := s.newAgent(c, m) 290 defer a.Stop() 291 go func() { 292 c.Check(a.Run(nil), jc.ErrorIsNil) 293 }() 294 295 // Add one unit to an application; 296 charm := s.AddTestingCharm(c, "dummy") 297 app := s.AddTestingApplication(c, "test-application", charm) 298 unit, err := app.AddUnit(state.AddUnitParams{}) 299 c.Assert(err, jc.ErrorIsNil) 300 err = s.State.AssignUnit(unit, state.AssignCleanEmpty) 301 c.Assert(err, jc.ErrorIsNil) 302 303 m, instId := s.waitProvisioned(c, unit) 304 insts, err := s.Environ.Instances(context.NewCloudCallContext(), []instance.Id{instId}) 305 c.Assert(err, jc.ErrorIsNil) 306 addrs := network.NewAddresses("1.2.3.4") 307 dummy.SetInstanceAddresses(insts[0], addrs) 308 dummy.SetInstanceStatus(insts[0], "running") 309 310 for attempt := coretesting.LongAttempt.Start(); attempt.Next(); { 311 if !attempt.HasNext() { 312 c.Logf("final machine addresses: %#v", m.Addresses()) 313 c.Fatalf("timed out waiting for machine to get address") 314 } 315 err := m.Refresh() 316 c.Assert(err, jc.ErrorIsNil) 317 instStatus, err := m.InstanceStatus() 318 c.Assert(err, jc.ErrorIsNil) 319 c.Logf("found status is %q %q", instStatus.Status, instStatus.Message) 320 if reflect.DeepEqual(m.Addresses(), addrs) && instStatus.Message == "running" { 321 c.Logf("machine %q address updated: %+v", m.Id(), addrs) 322 break 323 } 324 c.Logf("waiting for machine %q address to be updated", m.Id()) 325 } 326 } 327 328 func (s *MachineLegacyLeasesSuite) TestCallsUseMultipleCPUs(c *gc.C) { 329 // All machine agents call UseMultipleCPUs. 330 m, _, _ := s.primeAgent(c, state.JobHostUnits) 331 calledChan := make(chan struct{}, 1) 332 s.AgentSuite.PatchValue(&useMultipleCPUs, func() { calledChan <- struct{}{} }) 333 a := s.newAgent(c, m) 334 defer a.Stop() 335 go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }() 336 337 // Wait for configuration to be finished 338 <-a.WorkersStarted() 339 s.assertChannelActive(c, calledChan, "UseMultipleCPUs() to be called") 340 c.Check(a.Stop(), jc.ErrorIsNil) 341 } 342 343 func (s *MachineLegacyLeasesSuite) waitProvisioned(c *gc.C, unit *state.Unit) (*state.Machine, instance.Id) { 344 c.Logf("waiting for unit %q to be provisioned", unit) 345 machineId, err := unit.AssignedMachineId() 346 c.Assert(err, jc.ErrorIsNil) 347 m, err := s.State.Machine(machineId) 348 c.Assert(err, jc.ErrorIsNil) 349 w := m.Watch() 350 defer worker.Stop(w) 351 timeout := time.After(coretesting.LongWait) 352 for { 353 select { 354 case <-timeout: 355 c.Fatalf("timed out waiting for provisioning") 356 case <-time.After(coretesting.ShortWait): 357 s.State.StartSync() 358 case _, ok := <-w.Changes(): 359 c.Assert(ok, jc.IsTrue) 360 err := m.Refresh() 361 c.Assert(err, jc.ErrorIsNil) 362 if instId, err := m.InstanceId(); err == nil { 363 c.Logf("unit provisioned with instance %s", instId) 364 return m, instId 365 } else { 366 c.Check(err, jc.Satisfies, errors.IsNotProvisioned) 367 } 368 } 369 } 370 } 371 372 func (s *MachineLegacyLeasesSuite) testUpgradeRequest(c *gc.C, agent runner, tag string, currentTools *tools.Tools) { 373 newVers := version.Binary{ 374 Number: jujuversion.Current, 375 Arch: arch.HostArch(), 376 Series: series.MustHostSeries(), 377 } 378 newVers.Patch++ 379 newTools := envtesting.AssertUploadFakeToolsVersions( 380 c, s.DefaultToolsStorage, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), newVers)[0] 381 err := s.State.SetModelAgentVersion(newVers.Number, true) 382 c.Assert(err, jc.ErrorIsNil) 383 err = runWithTimeout(agent) 384 envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{ 385 AgentName: tag, 386 OldTools: currentTools.Version, 387 NewTools: newTools.Version, 388 DataDir: s.DataDir(), 389 }) 390 } 391 392 func (s *MachineLegacyLeasesSuite) TestUpgradeRequest(c *gc.C) { 393 m, _, currentTools := s.primeAgent(c, state.JobManageModel, state.JobHostUnits) 394 a := s.newAgent(c, m) 395 s.testUpgradeRequest(c, a, m.Tag().String(), currentTools) 396 c.Assert(a.initialUpgradeCheckComplete.IsUnlocked(), jc.IsFalse) 397 } 398 399 func (s *MachineLegacyLeasesSuite) TestNoUpgradeRequired(c *gc.C) { 400 m, _, _ := s.primeAgent(c, state.JobManageModel, state.JobHostUnits) 401 a := s.newAgent(c, m) 402 done := make(chan error) 403 go func() { done <- a.Run(nil) }() 404 select { 405 case <-a.initialUpgradeCheckComplete.Unlocked(): 406 case <-time.After(coretesting.LongWait): 407 c.Fatalf("timeout waiting for upgrade check") 408 } 409 defer a.Stop() // in case of failure 410 s.waitStopped(c, state.JobManageModel, a, done) 411 c.Assert(a.initialUpgradeCheckComplete.IsUnlocked(), jc.IsTrue) 412 } 413 414 func (s *MachineLegacyLeasesSuite) waitStopped(c *gc.C, job state.MachineJob, a *MachineAgent, done chan error) { 415 err := a.Stop() 416 if job == state.JobManageModel { 417 // When shutting down, the API server can be shut down before 418 // the other workers that connect to it, so they get an error so 419 // they then die, causing Stop to return an error. It's not 420 // easy to control the actual error that's received in this 421 // circumstance so we just log it rather than asserting that it 422 // is not nil. 423 if err != nil { 424 c.Logf("error shutting down state manager: %v", err) 425 } 426 } else { 427 c.Assert(err, jc.ErrorIsNil) 428 } 429 430 select { 431 case err := <-done: 432 c.Assert(err, jc.ErrorIsNil) 433 case <-time.After(coretesting.LongWait): 434 c.Fatalf("timed out waiting for agent to terminate") 435 } 436 } 437 438 func (s *MachineLegacyLeasesSuite) assertJobWithState( 439 c *gc.C, 440 job state.MachineJob, 441 test func(agent.Config, *state.State), 442 ) { 443 paramsJob := job.ToParams() 444 if !paramsJob.NeedsState() { 445 c.Fatalf("%v does not use state", paramsJob) 446 } 447 s.assertAgentOpensState(c, job, test) 448 } 449 450 // assertAgentOpensState asserts that a machine agent started with the 451 // given job. The agent's configuration and the agent's state.State are 452 // then passed to the test function for further checking. 453 func (s *MachineLegacyLeasesSuite) assertAgentOpensState(c *gc.C, job state.MachineJob, test func(agent.Config, *state.State)) { 454 stm, conf, _ := s.primeAgent(c, job) 455 a := s.newAgent(c, stm) 456 defer a.Stop() 457 logger.Debugf("new agent %#v", a) 458 459 // All state jobs currently also run an APIWorker, so no 460 // need to check for that here, like in assertJobWithState. 461 st, done := s.waitForOpenState(c, a) 462 test(conf, st) 463 s.waitStopped(c, job, a, done) 464 } 465 466 func (s *MachineLegacyLeasesSuite) waitForOpenState(c *gc.C, a *MachineAgent) (*state.State, chan error) { 467 agentAPIs := make(chan *state.State, 1) 468 s.AgentSuite.PatchValue(&reportOpenedState, func(st *state.State) { 469 select { 470 case agentAPIs <- st: 471 default: 472 } 473 }) 474 475 done := make(chan error) 476 go func() { 477 done <- a.Run(nil) 478 }() 479 480 select { 481 case agentAPI := <-agentAPIs: 482 c.Assert(agentAPI, gc.NotNil) 483 return agentAPI, done 484 case <-time.After(coretesting.LongWait): 485 c.Fatalf("API not opened") 486 } 487 panic("can't happen") 488 } 489 490 func (s *MachineLegacyLeasesSuite) TestManageModelServesAPI(c *gc.C) { 491 s.assertJobWithState(c, state.JobManageModel, func(conf agent.Config, agentState *state.State) { 492 apiInfo, ok := conf.APIInfo() 493 c.Assert(ok, jc.IsTrue) 494 st, err := api.Open(apiInfo, fastDialOpts) 495 c.Assert(err, jc.ErrorIsNil) 496 defer st.Close() 497 m, err := apimachiner.NewState(st).Machine(conf.Tag().(names.MachineTag)) 498 c.Assert(err, jc.ErrorIsNil) 499 c.Assert(m.Life(), gc.Equals, params.Alive) 500 }) 501 } 502 503 func (s *MachineLegacyLeasesSuite) TestManageModelAuditsAPI(c *gc.C) { 504 password := "shhh..." 505 user := s.Factory.MakeUser(c, &factory.UserParams{ 506 Password: password, 507 }) 508 509 err := s.State.UpdateControllerConfig(map[string]interface{}{ 510 "audit-log-exclude-methods": []interface{}{"Client.FullStatus"}, 511 }, nil) 512 c.Assert(err, jc.ErrorIsNil) 513 514 s.assertJobWithState(c, state.JobManageModel, func(conf agent.Config, agentState *state.State) { 515 logPath := filepath.Join(conf.LogDir(), "audit.log") 516 517 makeAPIRequest := func(doRequest func(*api.Client)) { 518 apiInfo, ok := conf.APIInfo() 519 c.Assert(ok, jc.IsTrue) 520 apiInfo.Tag = user.Tag() 521 apiInfo.Password = password 522 st, err := api.Open(apiInfo, fastDialOpts) 523 c.Assert(err, jc.ErrorIsNil) 524 defer st.Close() 525 doRequest(st.Client()) 526 } 527 528 // Make requests in separate API connections so they're separate conversations. 529 makeAPIRequest(func(client *api.Client) { 530 _, err = client.Status(nil) 531 c.Assert(err, jc.ErrorIsNil) 532 }) 533 makeAPIRequest(func(client *api.Client) { 534 _, err = client.AddMachines([]params.AddMachineParams{{ 535 Jobs: []multiwatcher.MachineJob{"JobHostUnits"}, 536 }}) 537 c.Assert(err, jc.ErrorIsNil) 538 }) 539 540 // Check that there's a call to Client.AddMachinesV2 in the 541 // log, but no call to Client.FullStatus. 542 records := readAuditLog(c, logPath) 543 c.Assert(records, gc.HasLen, 3) 544 c.Assert(records[1].Request, gc.NotNil) 545 c.Assert(records[1].Request.Facade, gc.Equals, "Client") 546 c.Assert(records[1].Request.Method, gc.Equals, "AddMachinesV2") 547 548 // Now update the controller config to remove the exclusion. 549 err := s.State.UpdateControllerConfig(map[string]interface{}{ 550 "audit-log-exclude-methods": []interface{}{}, 551 }, nil) 552 c.Assert(err, jc.ErrorIsNil) 553 554 prevRecords := len(records) 555 556 // We might need to wait until the controller config change is 557 // propagated to the apiserver. 558 for a := coretesting.LongAttempt.Start(); a.Next(); { 559 makeAPIRequest(func(client *api.Client) { 560 _, err = client.Status(nil) 561 c.Assert(err, jc.ErrorIsNil) 562 }) 563 // Check to see whether there are more logged requests. 564 records = readAuditLog(c, logPath) 565 if prevRecords < len(records) { 566 break 567 } 568 } 569 // Now there should also be a call to Client.FullStatus (and a response). 570 lastRequest := records[len(records)-2] 571 c.Assert(lastRequest.Request, gc.NotNil) 572 c.Assert(lastRequest.Request.Facade, gc.Equals, "Client") 573 c.Assert(lastRequest.Request.Method, gc.Equals, "FullStatus") 574 }) 575 } 576 577 func readAuditLog(c *gc.C, logPath string) []auditlog.Record { 578 file, err := os.Open(logPath) 579 c.Assert(err, jc.ErrorIsNil) 580 defer file.Close() 581 582 scanner := bufio.NewScanner(file) 583 var results []auditlog.Record 584 for scanner.Scan() { 585 var record auditlog.Record 586 err := json.Unmarshal(scanner.Bytes(), &record) 587 c.Assert(err, jc.ErrorIsNil) 588 results = append(results, record) 589 } 590 return results 591 } 592 593 func (s *MachineLegacyLeasesSuite) assertAgentSetsToolsVersion(c *gc.C, job state.MachineJob) { 594 vers := version.Binary{ 595 Number: jujuversion.Current, 596 Arch: arch.HostArch(), 597 Series: series.MustHostSeries(), 598 } 599 vers.Minor++ 600 m, _, _ := s.primeAgentVersion(c, vers, job) 601 a := s.newAgent(c, m) 602 go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }() 603 defer func() { c.Check(a.Stop(), jc.ErrorIsNil) }() 604 605 timeout := time.After(coretesting.LongWait) 606 for done := false; !done; { 607 select { 608 case <-timeout: 609 c.Fatalf("timeout while waiting for agent version to be set") 610 case <-time.After(coretesting.ShortWait): 611 c.Log("Refreshing") 612 err := m.Refresh() 613 c.Assert(err, jc.ErrorIsNil) 614 c.Log("Fetching agent tools") 615 agentTools, err := m.AgentTools() 616 c.Assert(err, jc.ErrorIsNil) 617 c.Logf("(%v vs. %v)", agentTools.Version, jujuversion.Current) 618 if agentTools.Version.Minor != jujuversion.Current.Minor { 619 continue 620 } 621 c.Assert(agentTools.Version.Number, gc.DeepEquals, jujuversion.Current) 622 done = true 623 } 624 } 625 } 626 627 func (s *MachineLegacyLeasesSuite) TestAgentSetsToolsVersionManageModel(c *gc.C) { 628 s.assertAgentSetsToolsVersion(c, state.JobManageModel) 629 } 630 631 func (s *MachineLegacyLeasesSuite) TestAgentSetsToolsVersionHostUnits(c *gc.C) { 632 s.assertAgentSetsToolsVersion(c, state.JobHostUnits) 633 } 634 635 func (s *MachineLegacyLeasesSuite) TestManageModelRunsCleaner(c *gc.C) { 636 s.assertJobWithState(c, state.JobManageModel, func(conf agent.Config, agentState *state.State) { 637 // Create an application and unit, and destroy the app. 638 app := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 639 unit, err := app.AddUnit(state.AddUnitParams{}) 640 c.Assert(err, jc.ErrorIsNil) 641 err = app.Destroy() 642 c.Assert(err, jc.ErrorIsNil) 643 644 // Check the unit was not yet removed. 645 err = unit.Refresh() 646 c.Assert(err, jc.ErrorIsNil) 647 w := unit.Watch() 648 defer worker.Stop(w) 649 650 // Trigger a sync on the state used by the agent, and wait 651 // for the unit to be removed. 652 agentState.StartSync() 653 timeout := time.After(coretesting.LongWait) 654 for done := false; !done; { 655 select { 656 case <-timeout: 657 c.Fatalf("unit not cleaned up") 658 case <-time.After(coretesting.ShortWait): 659 s.State.StartSync() 660 case <-w.Changes(): 661 err := unit.Refresh() 662 if errors.IsNotFound(err) { 663 done = true 664 } else { 665 c.Assert(err, jc.ErrorIsNil) 666 } 667 } 668 } 669 }) 670 } 671 672 func (s *MachineLegacyLeasesSuite) TestJobManageModelRunsMinUnitsWorker(c *gc.C) { 673 s.assertJobWithState(c, state.JobManageModel, func(_ agent.Config, agentState *state.State) { 674 // Ensure that the MinUnits worker is alive by doing a simple check 675 // that it responds to state changes: add an application, set its minimum 676 // number of units to one, wait for the worker to add the missing unit. 677 app := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 678 err := app.SetMinUnits(1) 679 c.Assert(err, jc.ErrorIsNil) 680 w := app.Watch() 681 defer worker.Stop(w) 682 683 // Trigger a sync on the state used by the agent, and wait for the unit 684 // to be created. 685 agentState.StartSync() 686 timeout := time.After(coretesting.LongWait) 687 for { 688 select { 689 case <-timeout: 690 c.Fatalf("unit not created") 691 case <-time.After(coretesting.ShortWait): 692 s.State.StartSync() 693 case <-w.Changes(): 694 units, err := app.AllUnits() 695 c.Assert(err, jc.ErrorIsNil) 696 if len(units) == 1 { 697 return 698 } 699 } 700 } 701 }) 702 } 703 704 func (s *MachineLegacyLeasesSuite) TestMachineAgentRunsAuthorisedKeysWorker(c *gc.C) { 705 //TODO(bogdanteleaga): Fix once we get authentication worker up on windows 706 if runtime.GOOS == "windows" { 707 c.Skip("bug 1403084: authentication worker not yet implemented on windows") 708 } 709 // Start the machine agent. 710 m, _, _ := s.primeAgent(c, state.JobHostUnits) 711 a := s.newAgent(c, m) 712 go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }() 713 defer func() { c.Check(a.Stop(), jc.ErrorIsNil) }() 714 715 // Update the keys in the environment. 716 sshKey := sshtesting.ValidKeyOne.Key + " user@host" 717 err := s.Model.UpdateModelConfig(map[string]interface{}{"authorized-keys": sshKey}, nil) 718 c.Assert(err, jc.ErrorIsNil) 719 720 // Wait for ssh keys file to be updated. 721 s.State.StartSync() 722 timeout := time.After(coretesting.LongWait) 723 sshKeyWithCommentPrefix := sshtesting.ValidKeyOne.Key + " Juju:user@host" 724 for { 725 select { 726 case <-timeout: 727 c.Fatalf("timeout while waiting for authorised ssh keys to change") 728 case <-time.After(coretesting.ShortWait): 729 s.State.StartSync() 730 keys, err := ssh.ListKeys(authenticationworker.SSHUser, ssh.FullKeys) 731 c.Assert(err, jc.ErrorIsNil) 732 keysStr := strings.Join(keys, "\n") 733 if sshKeyWithCommentPrefix != keysStr { 734 continue 735 } 736 return 737 } 738 } 739 } 740 741 func (s *MachineLegacyLeasesSuite) TestMachineAgentSymlinks(c *gc.C) { 742 stm, _, _ := s.primeAgent(c, state.JobManageModel) 743 a := s.newAgent(c, stm) 744 defer a.Stop() 745 _, done := s.waitForOpenState(c, a) 746 747 // Symlinks should have been created 748 for _, link := range jujudSymlinks { 749 _, err := os.Stat(utils.EnsureBaseDir(a.rootDir, link)) 750 c.Assert(err, jc.ErrorIsNil, gc.Commentf(link)) 751 } 752 753 s.waitStopped(c, state.JobManageModel, a, done) 754 } 755 756 func (s *MachineLegacyLeasesSuite) TestMachineAgentSymlinkJujuRunExists(c *gc.C) { 757 if runtime.GOOS == "windows" { 758 // Cannot make symlink to nonexistent file on windows or 759 // create a file point a symlink to it then remove it 760 c.Skip("Cannot test this on windows") 761 } 762 763 stm, _, _ := s.primeAgent(c, state.JobManageModel) 764 a := s.newAgent(c, stm) 765 defer a.Stop() 766 767 // Pre-create the symlinks, but pointing to the incorrect location. 768 a.rootDir = c.MkDir() 769 for _, link := range jujudSymlinks { 770 fullLink := utils.EnsureBaseDir(a.rootDir, link) 771 c.Assert(os.MkdirAll(filepath.Dir(fullLink), os.FileMode(0755)), jc.ErrorIsNil) 772 c.Assert(symlink.New("/nowhere/special", fullLink), jc.ErrorIsNil, gc.Commentf(link)) 773 } 774 775 // Start the agent and wait for it be running. 776 _, done := s.waitForOpenState(c, a) 777 778 // juju-run symlink should have been recreated. 779 for _, link := range jujudSymlinks { 780 fullLink := utils.EnsureBaseDir(a.rootDir, link) 781 linkTarget, err := symlink.Read(fullLink) 782 c.Assert(err, jc.ErrorIsNil) 783 c.Assert(linkTarget, gc.Not(gc.Equals), "/nowhere/special", gc.Commentf(link)) 784 } 785 786 s.waitStopped(c, state.JobManageModel, a, done) 787 } 788 789 func (s *MachineLegacyLeasesSuite) TestMachineAgentUninstall(c *gc.C) { 790 m, ac, _ := s.primeAgent(c, state.JobHostUnits) 791 err := m.EnsureDead() 792 c.Assert(err, jc.ErrorIsNil) 793 a := s.newAgent(c, m) 794 err = runWithTimeout(a) 795 c.Assert(err, jc.ErrorIsNil) 796 797 // juju-* symlinks should have been removed on termination. 798 for _, link := range []string{jujuRun, jujuDumpLogs, jujuIntrospect} { 799 _, err = os.Stat(utils.EnsureBaseDir(a.rootDir, link)) 800 c.Assert(err, jc.Satisfies, os.IsNotExist) 801 } 802 803 // data-dir should have been removed on termination 804 _, err = os.Stat(ac.DataDir()) 805 c.Assert(err, jc.Satisfies, os.IsNotExist) 806 } 807 808 func (s *MachineLegacyLeasesSuite) TestMachineAgentRunsAPIAddressUpdaterWorker(c *gc.C) { 809 // Start the machine agent. 810 m, _, _ := s.primeAgent(c, state.JobHostUnits) 811 a := s.newAgent(c, m) 812 go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }() 813 defer func() { c.Check(a.Stop(), jc.ErrorIsNil) }() 814 815 // Update the API addresses. 816 updatedServers := [][]network.HostPort{ 817 network.NewHostPorts(1234, "localhost"), 818 } 819 err := s.BackingState.SetAPIHostPorts(updatedServers) 820 c.Assert(err, jc.ErrorIsNil) 821 822 // Wait for config to be updated. 823 for attempt := coretesting.LongAttempt.Start(); attempt.Next(); { 824 s.BackingState.StartSync() 825 if !attempt.HasNext() { 826 break 827 } 828 addrs, err := a.CurrentConfig().APIAddresses() 829 c.Assert(err, jc.ErrorIsNil) 830 if reflect.DeepEqual(addrs, []string{"localhost:1234"}) { 831 return 832 } 833 } 834 c.Fatalf("timeout while waiting for agent config to change") 835 } 836 837 func (s *MachineLegacyLeasesSuite) TestMachineAgentRunsDiskManagerWorker(c *gc.C) { 838 // Patch out the worker func before starting the agent. 839 started := newSignal() 840 newWorker := func(diskmanager.ListBlockDevicesFunc, diskmanager.BlockDeviceSetter) worker.Worker { 841 started.trigger() 842 return jworker.NewNoOpWorker() 843 } 844 s.PatchValue(&diskmanager.NewWorker, newWorker) 845 846 // Start the machine agent. 847 m, _, _ := s.primeAgent(c, state.JobHostUnits) 848 a := s.newAgent(c, m) 849 go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }() 850 defer func() { c.Check(a.Stop(), jc.ErrorIsNil) }() 851 started.assertTriggered(c, "diskmanager worker to start") 852 } 853 854 func (s *MachineLegacyLeasesSuite) TestDiskManagerWorkerUpdatesState(c *gc.C) { 855 expected := []storage.BlockDevice{{DeviceName: "whatever"}} 856 s.PatchValue(&diskmanager.DefaultListBlockDevices, func() ([]storage.BlockDevice, error) { 857 return expected, nil 858 }) 859 860 // Start the machine agent. 861 m, _, _ := s.primeAgent(c, state.JobHostUnits) 862 a := s.newAgent(c, m) 863 go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }() 864 defer func() { c.Check(a.Stop(), jc.ErrorIsNil) }() 865 866 sb, err := state.NewStorageBackend(s.BackingState) 867 c.Assert(err, jc.ErrorIsNil) 868 869 // Wait for state to be updated. 870 s.BackingState.StartSync() 871 for attempt := coretesting.LongAttempt.Start(); attempt.Next(); { 872 devices, err := sb.BlockDevices(m.MachineTag()) 873 c.Assert(err, jc.ErrorIsNil) 874 if len(devices) > 0 { 875 c.Assert(devices, gc.HasLen, 1) 876 c.Assert(devices[0].DeviceName, gc.Equals, expected[0].DeviceName) 877 return 878 } 879 } 880 c.Fatalf("timeout while waiting for block devices to be recorded") 881 } 882 883 func (s *MachineLegacyLeasesSuite) TestMachineAgentRunsMachineStorageWorker(c *gc.C) { 884 m, _, _ := s.primeAgent(c, state.JobHostUnits) 885 886 started := newSignal() 887 newWorker := func(config storageprovisioner.Config) (worker.Worker, error) { 888 c.Check(config.Scope, gc.Equals, m.Tag()) 889 c.Check(config.Validate(), jc.ErrorIsNil) 890 started.trigger() 891 return jworker.NewNoOpWorker(), nil 892 } 893 s.PatchValue(&storageprovisioner.NewStorageProvisioner, newWorker) 894 895 // Start the machine agent. 896 a := s.newAgent(c, m) 897 go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }() 898 defer func() { c.Check(a.Stop(), jc.ErrorIsNil) }() 899 started.assertTriggered(c, "storage worker to start") 900 } 901 902 func (s *MachineLegacyLeasesSuite) TestCertificateDNSUpdated(c *gc.C) { 903 m, _, _ := s.primeAgent(c, state.JobManageModel) 904 a := s.newAgent(c, m) 905 s.testCertificateDNSUpdated(c, a) 906 } 907 908 func (s *MachineLegacyLeasesSuite) TestCertificateDNSUpdatedInvalidPrivateKey(c *gc.C) { 909 m, agentConfig, _ := s.primeAgent(c, state.JobManageModel) 910 911 // Write out config with an invalid private key. This should 912 // cause the agent to rewrite the cert and key. 913 si, ok := agentConfig.StateServingInfo() 914 c.Assert(ok, jc.IsTrue) 915 si.PrivateKey = "foo" 916 agentConfig.SetStateServingInfo(si) 917 err := agentConfig.Write() 918 c.Assert(err, jc.ErrorIsNil) 919 920 a := s.newAgent(c, m) 921 s.testCertificateDNSUpdated(c, a) 922 } 923 924 func (s *MachineLegacyLeasesSuite) testCertificateDNSUpdated(c *gc.C, a *MachineAgent) { 925 // Set up a channel which fires when State is opened. 926 started := make(chan struct{}, 16) 927 s.PatchValue(&reportOpenedState, func(*state.State) { 928 started <- struct{}{} 929 }) 930 931 // Start the agent. 932 go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }() 933 defer func() { c.Check(a.Stop(), jc.ErrorIsNil) }() 934 935 // Wait for State to be opened. Once this occurs we know that the 936 // agent's initial startup has happened. 937 s.assertChannelActive(c, started, "agent to start up") 938 939 // Check that certificate was updated when the agent started. 940 stateInfo, _ := a.CurrentConfig().StateServingInfo() 941 srvCert, _, err := cert.ParseCertAndKey(stateInfo.Cert, stateInfo.PrivateKey) 942 c.Assert(err, jc.ErrorIsNil) 943 expectedDnsNames := set.NewStrings("localhost", "juju-apiserver", "juju-mongodb") 944 certDnsNames := set.NewStrings(srvCert.DNSNames...) 945 c.Check(expectedDnsNames.Difference(certDnsNames).IsEmpty(), jc.IsTrue) 946 947 // Check the mongo certificate file too. 948 pemContent, err := ioutil.ReadFile(filepath.Join(s.DataDir(), "server.pem")) 949 c.Assert(err, jc.ErrorIsNil) 950 c.Check(string(pemContent), gc.Equals, stateInfo.Cert+"\n"+stateInfo.PrivateKey) 951 } 952 953 func (s *MachineLegacyLeasesSuite) setupIgnoreAddresses(c *gc.C, expectedIgnoreValue bool) chan bool { 954 ignoreAddressCh := make(chan bool, 1) 955 s.AgentSuite.PatchValue(&machiner.NewMachiner, func(cfg machiner.Config) (worker.Worker, error) { 956 select { 957 case ignoreAddressCh <- cfg.ClearMachineAddressesOnStart: 958 default: 959 } 960 961 // The test just cares that NewMachiner is called with the correct 962 // value, nothing else is done with the worker. 963 return newDummyWorker(), nil 964 }) 965 966 attrs := coretesting.Attrs{"ignore-machine-addresses": expectedIgnoreValue} 967 err := s.Model.UpdateModelConfig(attrs, nil) 968 c.Assert(err, jc.ErrorIsNil) 969 return ignoreAddressCh 970 } 971 972 func (s *MachineLegacyLeasesSuite) TestMachineAgentIgnoreAddresses(c *gc.C) { 973 for _, expectedIgnoreValue := range []bool{true, false} { 974 ignoreAddressCh := s.setupIgnoreAddresses(c, expectedIgnoreValue) 975 976 m, _, _ := s.primeAgent(c, state.JobHostUnits) 977 a := s.newAgent(c, m) 978 defer a.Stop() 979 doneCh := make(chan error) 980 go func() { 981 doneCh <- a.Run(nil) 982 }() 983 984 select { 985 case ignoreMachineAddresses := <-ignoreAddressCh: 986 if ignoreMachineAddresses != expectedIgnoreValue { 987 c.Fatalf("expected ignore-machine-addresses = %v, got = %v", expectedIgnoreValue, ignoreMachineAddresses) 988 } 989 case <-time.After(coretesting.LongWait): 990 c.Fatalf("timed out waiting for the machiner to start") 991 } 992 s.waitStopped(c, state.JobHostUnits, a, doneCh) 993 } 994 } 995 996 func (s *MachineLegacyLeasesSuite) TestMachineAgentIgnoreAddressesContainer(c *gc.C) { 997 ignoreAddressCh := s.setupIgnoreAddresses(c, true) 998 999 parent, err := s.State.AddMachine("quantal", state.JobHostUnits) 1000 c.Assert(err, jc.ErrorIsNil) 1001 m, err := s.State.AddMachineInsideMachine( 1002 state.MachineTemplate{ 1003 Series: "trusty", 1004 Jobs: []state.MachineJob{state.JobHostUnits}, 1005 }, 1006 parent.Id(), 1007 instance.LXD, 1008 ) 1009 c.Assert(err, jc.ErrorIsNil) 1010 1011 vers := version.Binary{ 1012 Number: jujuversion.Current, 1013 Arch: arch.HostArch(), 1014 Series: series.MustHostSeries(), 1015 } 1016 s.primeAgentWithMachine(c, m, vers) 1017 a := s.newAgent(c, m) 1018 defer a.Stop() 1019 doneCh := make(chan error) 1020 go func() { 1021 doneCh <- a.Run(nil) 1022 }() 1023 1024 select { 1025 case ignoreMachineAddresses := <-ignoreAddressCh: 1026 if ignoreMachineAddresses { 1027 c.Fatalf("expected ignore-machine-addresses = false, got = true") 1028 } 1029 case <-time.After(coretesting.LongWait): 1030 c.Fatalf("timed out waiting for the machiner to start") 1031 } 1032 s.waitStopped(c, state.JobHostUnits, a, doneCh) 1033 } 1034 1035 func (s *MachineSuite) TestMachineWorkers(c *gc.C) { 1036 s.ControllerConfigAttrs = map[string]interface{}{ 1037 controller.AuditingEnabled: true, 1038 controller.CharmStoreURL: "staging.charmstore", 1039 } 1040 1041 tracker := agenttest.NewEngineTracker() 1042 instrumented := TrackMachines(c, tracker, machineManifolds) 1043 s.PatchValue(&machineManifolds, instrumented) 1044 1045 m, _, _ := s.primeAgent(c, state.JobHostUnits) 1046 a := s.newAgent(c, m) 1047 go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }() 1048 defer func() { c.Check(a.Stop(), jc.ErrorIsNil) }() 1049 1050 // Wait for it to stabilise, running as normal. 1051 matcher := agenttest.NewWorkerMatcher(c, tracker, a.Tag().String(), 1052 append(alwaysMachineWorkers, notMigratingMachineWorkers...)) 1053 agenttest.WaitMatch(c, matcher.Check, coretesting.LongWait, s.BackingState.StartSync) 1054 } 1055 1056 func (s *MachineLegacyLeasesSuite) TestControllerModelWorkers(c *gc.C) { 1057 uuid := s.BackingState.ModelUUID() 1058 1059 tracker := agenttest.NewEngineTracker() 1060 instrumented := TrackModels(c, tracker, iaasModelManifolds) 1061 s.PatchValue(&iaasModelManifolds, instrumented) 1062 1063 expectedWorkers := append(alwaysModelWorkers, aliveModelWorkers...) 1064 1065 matcher := agenttest.NewWorkerMatcher(c, tracker, uuid, expectedWorkers) 1066 s.assertJobWithState(c, state.JobManageModel, func(agent.Config, *state.State) { 1067 agenttest.WaitMatch(c, matcher.Check, coretesting.LongWait, s.BackingState.StartSync) 1068 }) 1069 } 1070 1071 func (s *MachineLegacyLeasesSuite) TestHostedModelWorkers(c *gc.C) { 1072 // The dummy provider blows up in the face of multi-model 1073 // scenarios so patch in a minimal environs.Environ that's good 1074 // enough to allow the model workers to run. 1075 s.PatchValue(&newEnvirons, func(environs.OpenParams) (environs.Environ, error) { 1076 return &minModelWorkersEnviron{}, nil 1077 }) 1078 1079 st, closer := s.setUpNewModel(c) 1080 defer closer() 1081 1082 uuid := st.ModelUUID() 1083 1084 tracker := agenttest.NewEngineTracker() 1085 instrumented := TrackModels(c, tracker, iaasModelManifolds) 1086 s.PatchValue(&iaasModelManifolds, instrumented) 1087 1088 matcher := agenttest.NewWorkerMatcher(c, tracker, uuid, 1089 append(alwaysModelWorkers, aliveModelWorkers...)) 1090 s.assertJobWithState(c, state.JobManageModel, func(agent.Config, *state.State) { 1091 agenttest.WaitMatch(c, matcher.Check, ReallyLongWait, st.StartSync) 1092 }) 1093 } 1094 1095 func (s *MachineLegacyLeasesSuite) TestWorkersForHostedModelWithInvalidCredential(c *gc.C) { 1096 // The dummy provider blows up in the face of multi-model 1097 // scenarios so patch in a minimal environs.Environ that's good 1098 // enough to allow the model workers to run. 1099 s.PatchValue(&newEnvirons, func(environs.OpenParams) (environs.Environ, error) { 1100 return &minModelWorkersEnviron{}, nil 1101 }) 1102 1103 st := s.Factory.MakeModel(c, &factory.ModelParams{ 1104 ConfigAttrs: coretesting.Attrs{ 1105 "max-status-history-age": "2h", 1106 "max-status-history-size": "4M", 1107 "max-action-results-age": "2h", 1108 "max-action-results-size": "4M", 1109 }, 1110 CloudCredential: names.NewCloudCredentialTag("dummy/admin/cred"), 1111 }) 1112 defer func() { 1113 err := st.Close() 1114 c.Check(err, jc.ErrorIsNil) 1115 }() 1116 1117 uuid := st.ModelUUID() 1118 1119 // invalidate cloud credential for this model 1120 err := st.InvalidateModelCredential("coz i can") 1121 c.Assert(err, jc.ErrorIsNil) 1122 1123 tracker := agenttest.NewEngineTracker() 1124 instrumented := TrackModels(c, tracker, iaasModelManifolds) 1125 s.PatchValue(&iaasModelManifolds, instrumented) 1126 1127 expectedWorkers := append(alwaysModelWorkers, aliveModelWorkers...) 1128 // Since this model's cloud credential is no longer valid, 1129 // only the workers that don't require a valid credential should remain. 1130 remainingWorkers := set.NewStrings(expectedWorkers...).Difference( 1131 set.NewStrings(requireValidCredentialModelWorkers...)) 1132 1133 matcher := agenttest.NewWorkerMatcher(c, tracker, uuid, remainingWorkers.SortedValues()) 1134 s.assertJobWithState(c, state.JobManageModel, func(agent.Config, *state.State) { 1135 agenttest.WaitMatch(c, matcher.Check, ReallyLongWait, st.StartSync) 1136 }) 1137 } 1138 1139 func (s *MachineLegacyLeasesSuite) TestWorkersForHostedModelWithDeletedCredential(c *gc.C) { 1140 // The dummy provider blows up in the face of multi-model 1141 // scenarios so patch in a minimal environs.Environ that's good 1142 // enough to allow the model workers to run. 1143 s.PatchValue(&newEnvirons, func(environs.OpenParams) (environs.Environ, error) { 1144 return &minModelWorkersEnviron{}, nil 1145 }) 1146 1147 credentialTag := names.NewCloudCredentialTag("dummy/admin/another") 1148 err := s.State.UpdateCloudCredential(credentialTag, cloud.NewCredential(cloud.UserPassAuthType, nil)) 1149 c.Assert(err, jc.ErrorIsNil) 1150 1151 st := s.Factory.MakeModel(c, &factory.ModelParams{ 1152 ConfigAttrs: coretesting.Attrs{ 1153 "max-status-history-age": "2h", 1154 "max-status-history-size": "4M", 1155 "max-action-results-age": "2h", 1156 "max-action-results-size": "4M", 1157 }, 1158 CloudCredential: credentialTag, 1159 }) 1160 defer func() { 1161 err := st.Close() 1162 c.Check(err, jc.ErrorIsNil) 1163 }() 1164 1165 uuid := st.ModelUUID() 1166 1167 // remove cloud credential used by this model but keep model reference to it 1168 err = s.State.RemoveCloudCredential(credentialTag) 1169 c.Assert(err, jc.ErrorIsNil) 1170 1171 tracker := agenttest.NewEngineTracker() 1172 instrumented := TrackModels(c, tracker, iaasModelManifolds) 1173 s.PatchValue(&iaasModelManifolds, instrumented) 1174 1175 expectedWorkers := append(alwaysModelWorkers, aliveModelWorkers...) 1176 // Since this model's cloud credential is no longer valid, 1177 // only the workers that don't require a valid credential should remain. 1178 remainingWorkers := set.NewStrings(expectedWorkers...).Difference( 1179 set.NewStrings(requireValidCredentialModelWorkers...)) 1180 matcher := agenttest.NewWorkerMatcher(c, tracker, uuid, remainingWorkers.SortedValues()) 1181 1182 s.assertJobWithState(c, state.JobManageModel, func(agent.Config, *state.State) { 1183 agenttest.WaitMatch(c, matcher.Check, ReallyLongWait, st.StartSync) 1184 }) 1185 } 1186 1187 func (s *MachineLegacyLeasesSuite) TestMigratingModelWorkers(c *gc.C) { 1188 st, closer := s.setUpNewModel(c) 1189 defer closer() 1190 uuid := st.ModelUUID() 1191 1192 tracker := agenttest.NewEngineTracker() 1193 1194 // Replace the real migrationmaster worker with a fake one which 1195 // does nothing. This is required to make this test be reliable as 1196 // the environment required for the migrationmaster to operate 1197 // correctly is too involved to set up from here. 1198 // 1199 // TODO(mjs) - an alternative might be to provide a fake Facade 1200 // and api.Open to the real migrationmaster but this test is 1201 // awfully far away from the low level details of the worker. 1202 origModelManifolds := iaasModelManifolds 1203 modelManifoldsDisablingMigrationMaster := func(config model.ManifoldsConfig) dependency.Manifolds { 1204 config.NewMigrationMaster = func(config migrationmaster.Config) (worker.Worker, error) { 1205 return &nullWorker{dead: make(chan struct{})}, nil 1206 } 1207 return origModelManifolds(config) 1208 } 1209 instrumented := TrackModels(c, tracker, modelManifoldsDisablingMigrationMaster) 1210 s.PatchValue(&iaasModelManifolds, instrumented) 1211 1212 targetControllerTag := names.NewControllerTag(utils.MustNewUUID().String()) 1213 _, err := st.CreateMigration(state.MigrationSpec{ 1214 InitiatedBy: names.NewUserTag("admin"), 1215 TargetInfo: migration.TargetInfo{ 1216 ControllerTag: targetControllerTag, 1217 Addrs: []string{"1.2.3.4:5555"}, 1218 CACert: "cert", 1219 AuthTag: names.NewUserTag("user"), 1220 Password: "password", 1221 }, 1222 }) 1223 c.Assert(err, jc.ErrorIsNil) 1224 1225 matcher := agenttest.NewWorkerMatcher(c, tracker, uuid, 1226 append(alwaysModelWorkers, migratingModelWorkers...)) 1227 s.assertJobWithState(c, state.JobManageModel, func(agent.Config, *state.State) { 1228 agenttest.WaitMatch(c, matcher.Check, ReallyLongWait, st.StartSync) 1229 }) 1230 } 1231 1232 func (s *MachineLegacyLeasesSuite) TestDyingModelCleanedUp(c *gc.C) { 1233 st, closer := s.setUpNewModel(c) 1234 defer closer() 1235 1236 timeout := time.After(ReallyLongWait) 1237 s.assertJobWithState(c, state.JobManageModel, func(agent.Config, *state.State) { 1238 m, err := st.Model() 1239 c.Assert(err, jc.ErrorIsNil) 1240 watch := m.Watch() 1241 defer workertest.CleanKill(c, watch) 1242 1243 err = m.Destroy(state.DestroyModelParams{}) 1244 c.Assert(err, jc.ErrorIsNil) 1245 for { 1246 select { 1247 case <-watch.Changes(): 1248 err := m.Refresh() 1249 cause := errors.Cause(err) 1250 if err == nil { 1251 continue // still there 1252 } else if errors.IsNotFound(cause) { 1253 return // successfully removed 1254 } 1255 c.Assert(err, jc.ErrorIsNil) // guaranteed fail 1256 case <-time.After(coretesting.ShortWait): 1257 st.StartSync() 1258 case <-timeout: 1259 c.Fatalf("timed out waiting for workers") 1260 } 1261 } 1262 }) 1263 } 1264 1265 func (s *MachineLegacyLeasesSuite) TestModelWorkersRespectSingularResponsibilityFlag(c *gc.C) { 1266 1267 // Grab responsibility for the model on behalf of another machine. 1268 claimer := s.BackingState.SingularClaimer() 1269 uuid := s.BackingState.ModelUUID() 1270 err := claimer.Claim(uuid, "machine-999-lxd-99", time.Hour) 1271 c.Assert(err, jc.ErrorIsNil) 1272 1273 // Then run a normal model-tracking test, just checking for 1274 // a different set of workers. 1275 tracker := agenttest.NewEngineTracker() 1276 instrumented := TrackModels(c, tracker, iaasModelManifolds) 1277 s.PatchValue(&iaasModelManifolds, instrumented) 1278 1279 matcher := agenttest.NewWorkerMatcher(c, tracker, uuid, alwaysModelWorkers) 1280 s.assertJobWithState(c, state.JobManageModel, func(agent.Config, *state.State) { 1281 agenttest.WaitMatch(c, matcher.Check, coretesting.LongWait, s.BackingState.StartSync) 1282 }) 1283 } 1284 1285 func (s *MachineLegacyLeasesSuite) setUpNewModel(c *gc.C) (newSt *state.State, closer func()) { 1286 // Create a new environment, tests can now watch if workers start for it. 1287 newSt = s.Factory.MakeModel(c, &factory.ModelParams{ 1288 ConfigAttrs: coretesting.Attrs{ 1289 "max-status-history-age": "2h", 1290 "max-status-history-size": "4M", 1291 "max-action-results-age": "2h", 1292 "max-action-results-size": "4M", 1293 }, 1294 }) 1295 return newSt, func() { 1296 err := newSt.Close() 1297 c.Check(err, jc.ErrorIsNil) 1298 } 1299 } 1300 1301 func (s *MachineLegacyLeasesSuite) TestReplicasetInitForNewController(c *gc.C) { 1302 if runtime.GOOS == "windows" { 1303 c.Skip("controllers on windows aren't supported") 1304 } 1305 1306 s.fakeEnsureMongo.ServiceInstalled = false 1307 1308 m, _, _ := s.primeAgent(c, state.JobManageModel) 1309 a := s.newAgent(c, m) 1310 agentConfig := a.CurrentConfig() 1311 1312 err := a.ensureMongoServer(agentConfig) 1313 c.Assert(err, jc.ErrorIsNil) 1314 1315 c.Assert(s.fakeEnsureMongo.EnsureCount, gc.Equals, 1) 1316 c.Assert(s.fakeEnsureMongo.InitiateCount, gc.Equals, 0) 1317 } 1318 1319 type nullWorker struct { 1320 dead chan struct{} 1321 } 1322 1323 func (w *nullWorker) Kill() { 1324 close(w.dead) 1325 } 1326 1327 func (w *nullWorker) Wait() error { 1328 <-w.dead 1329 return nil 1330 }