github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/cmd/jujud/agent/util_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 "fmt" 8 "io/ioutil" 9 "path/filepath" 10 "reflect" 11 "sync" 12 "time" 13 14 "github.com/juju/cmd" 15 jc "github.com/juju/testing/checkers" 16 "github.com/juju/utils/arch" 17 "github.com/juju/utils/series" 18 "github.com/juju/utils/set" 19 "github.com/juju/version" 20 gc "gopkg.in/check.v1" 21 "gopkg.in/juju/charmrepo.v2-unstable" 22 "gopkg.in/juju/names.v2" 23 24 "github.com/juju/juju/agent" 25 "github.com/juju/juju/api" 26 apideployer "github.com/juju/juju/api/deployer" 27 "github.com/juju/juju/cmd/jujud/agent/agenttest" 28 cmdutil "github.com/juju/juju/cmd/jujud/util" 29 "github.com/juju/juju/environs" 30 "github.com/juju/juju/environs/config" 31 "github.com/juju/juju/instance" 32 jujutesting "github.com/juju/juju/juju/testing" 33 "github.com/juju/juju/mongo/mongotest" 34 "github.com/juju/juju/network" 35 "github.com/juju/juju/provider/dummy" 36 "github.com/juju/juju/service/upstart" 37 "github.com/juju/juju/state" 38 coretesting "github.com/juju/juju/testing" 39 "github.com/juju/juju/tools" 40 jujuversion "github.com/juju/juju/version" 41 "github.com/juju/juju/worker" 42 "github.com/juju/juju/worker/authenticationworker" 43 "github.com/juju/juju/worker/deployer" 44 "github.com/juju/juju/worker/logsender" 45 "github.com/juju/juju/worker/singular" 46 ) 47 48 const ( 49 initialMachinePassword = "machine-password-1234567890" 50 initialUnitPassword = "unit-password-1234567890" 51 startWorkerWait = 250 * time.Millisecond 52 ) 53 54 var fastDialOpts = api.DialOpts{ 55 Timeout: coretesting.LongWait, 56 RetryDelay: coretesting.ShortWait, 57 } 58 59 type commonMachineSuite struct { 60 singularRecord *singularRunnerRecord 61 fakeEnsureMongo *agenttest.FakeEnsureMongo 62 AgentSuite 63 } 64 65 func (s *commonMachineSuite) SetUpSuite(c *gc.C) { 66 s.AgentSuite.SetUpSuite(c) 67 s.AgentSuite.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber) 68 s.AgentSuite.PatchValue(&stateWorkerDialOpts, mongotest.DialOpts()) 69 } 70 71 func (s *commonMachineSuite) TearDownSuite(c *gc.C) { 72 s.AgentSuite.TearDownSuite(c) 73 } 74 75 func (s *commonMachineSuite) SetUpTest(c *gc.C) { 76 s.AgentSuite.SetUpTest(c) 77 s.AgentSuite.PatchValue(&charmrepo.CacheDir, c.MkDir()) 78 79 // Patch ssh user to avoid touching ~ubuntu/.ssh/authorized_keys. 80 s.AgentSuite.PatchValue(&authenticationworker.SSHUser, "") 81 82 testpath := c.MkDir() 83 s.AgentSuite.PatchEnvPathPrepend(testpath) 84 // mock out the start method so we can fake install services without sudo 85 fakeCmd(filepath.Join(testpath, "start")) 86 fakeCmd(filepath.Join(testpath, "stop")) 87 88 s.AgentSuite.PatchValue(&upstart.InitDir, c.MkDir()) 89 90 s.singularRecord = newSingularRunnerRecord() 91 s.AgentSuite.PatchValue(&newSingularRunner, s.singularRecord.newSingularRunner) 92 s.AgentSuite.PatchValue(&peergrouperNew, func(*state.State, bool) (worker.Worker, error) { 93 return newDummyWorker(), nil 94 }) 95 96 s.fakeEnsureMongo = agenttest.InstallFakeEnsureMongo(s) 97 } 98 99 func (s *commonMachineSuite) assertChannelActive(c *gc.C, aChannel chan struct{}, intent string) { 100 // Wait for channel to be active. 101 select { 102 case <-aChannel: 103 case <-time.After(coretesting.LongWait): 104 c.Fatalf("timeout while waiting for %v", intent) 105 } 106 } 107 108 func (s *commonMachineSuite) assertChannelInactive(c *gc.C, aChannel chan struct{}, intent string) { 109 // Now make sure the channel is not active. 110 select { 111 case <-aChannel: 112 c.Fatalf("%v unexpectedly", intent) 113 case <-time.After(startWorkerWait): 114 } 115 } 116 117 func fakeCmd(path string) { 118 err := ioutil.WriteFile(path, []byte("#!/bin/bash --norc\nexit 0"), 0755) 119 if err != nil { 120 panic(err) 121 } 122 } 123 124 func (s *commonMachineSuite) TearDownTest(c *gc.C) { 125 s.AgentSuite.TearDownTest(c) 126 } 127 128 // primeAgent adds a new Machine to run the given jobs, and sets up the 129 // machine agent's directory. It returns the new machine, the 130 // agent's configuration and the tools currently running. 131 func (s *commonMachineSuite) primeAgent(c *gc.C, jobs ...state.MachineJob) (m *state.Machine, agentConfig agent.ConfigSetterWriter, tools *tools.Tools) { 132 vers := version.Binary{ 133 Number: jujuversion.Current, 134 Arch: arch.HostArch(), 135 Series: series.HostSeries(), 136 } 137 return s.primeAgentVersion(c, vers, jobs...) 138 } 139 140 // primeAgentVersion is similar to primeAgent, but permits the 141 // caller to specify the version.Binary to prime with. 142 func (s *commonMachineSuite) primeAgentVersion(c *gc.C, vers version.Binary, jobs ...state.MachineJob) (m *state.Machine, agentConfig agent.ConfigSetterWriter, tools *tools.Tools) { 143 m, err := s.State.AddMachine("quantal", jobs...) 144 c.Assert(err, jc.ErrorIsNil) 145 return s.primeAgentWithMachine(c, m, vers) 146 } 147 148 func (s *commonMachineSuite) primeAgentWithMachine(c *gc.C, m *state.Machine, vers version.Binary) (*state.Machine, agent.ConfigSetterWriter, *tools.Tools) { 149 pinger, err := m.SetAgentPresence() 150 c.Assert(err, jc.ErrorIsNil) 151 s.AddCleanup(func(c *gc.C) { 152 c.Assert(worker.Stop(pinger), jc.ErrorIsNil) 153 }) 154 return s.configureMachine(c, m.Id(), vers) 155 } 156 157 func (s *commonMachineSuite) configureMachine(c *gc.C, machineId string, vers version.Binary) ( 158 machine *state.Machine, agentConfig agent.ConfigSetterWriter, tools *tools.Tools, 159 ) { 160 m, err := s.State.Machine(machineId) 161 c.Assert(err, jc.ErrorIsNil) 162 163 // Add a machine and ensure it is provisioned. 164 inst, md := jujutesting.AssertStartInstance(c, s.Environ, s.ControllerConfig.ControllerUUID(), machineId) 165 c.Assert(m.SetProvisioned(inst.Id(), agent.BootstrapNonce, md), jc.ErrorIsNil) 166 167 // Add an address for the tests in case the initiateMongoServer 168 // codepath is exercised. 169 s.setFakeMachineAddresses(c, m) 170 171 // Set up the new machine. 172 err = m.SetAgentVersion(vers) 173 c.Assert(err, jc.ErrorIsNil) 174 err = m.SetPassword(initialMachinePassword) 175 c.Assert(err, jc.ErrorIsNil) 176 tag := m.Tag() 177 if m.IsManager() { 178 err = m.SetMongoPassword(initialMachinePassword) 179 c.Assert(err, jc.ErrorIsNil) 180 agentConfig, tools = s.PrimeStateAgentVersion(c, tag, initialMachinePassword, vers) 181 info, ok := agentConfig.StateServingInfo() 182 c.Assert(ok, jc.IsTrue) 183 ssi := cmdutil.ParamsStateServingInfoToStateStateServingInfo(info) 184 err = s.State.SetStateServingInfo(ssi) 185 c.Assert(err, jc.ErrorIsNil) 186 } else { 187 agentConfig, tools = s.PrimeAgentVersion(c, tag, initialMachinePassword, vers) 188 } 189 err = agentConfig.Write() 190 c.Assert(err, jc.ErrorIsNil) 191 return m, agentConfig, tools 192 } 193 194 func NewTestMachineAgentFactory( 195 agentConfWriter AgentConfigWriter, 196 bufferedLogs logsender.LogRecordCh, 197 rootDir string, 198 ) func(string) *MachineAgent { 199 return func(machineId string) *MachineAgent { 200 return NewMachineAgent( 201 machineId, 202 agentConfWriter, 203 bufferedLogs, 204 worker.NewRunner(cmdutil.IsFatal, cmdutil.MoreImportant, worker.RestartDelay), 205 &mockLoopDeviceManager{}, 206 rootDir, 207 ) 208 } 209 } 210 211 // newAgent returns a new MachineAgent instance 212 func (s *commonMachineSuite) newAgent(c *gc.C, m *state.Machine) *MachineAgent { 213 agentConf := agentConf{dataDir: s.DataDir()} 214 agentConf.ReadConfig(names.NewMachineTag(m.Id()).String()) 215 machineAgentFactory := NewTestMachineAgentFactory(&agentConf, nil, c.MkDir()) 216 return machineAgentFactory(m.Id()) 217 } 218 219 func patchDeployContext(c *gc.C, st *state.State) (*fakeContext, func()) { 220 ctx := &fakeContext{ 221 inited: newSignal(), 222 deployed: make(set.Strings), 223 } 224 orig := newDeployContext 225 newDeployContext = func(dst *apideployer.State, agentConfig agent.Config) deployer.Context { 226 ctx.st = st 227 ctx.agentConfig = agentConfig 228 ctx.inited.trigger() 229 return ctx 230 } 231 return ctx, func() { newDeployContext = orig } 232 } 233 234 func (s *commonMachineSuite) setFakeMachineAddresses(c *gc.C, machine *state.Machine) { 235 addrs := network.NewAddresses("0.1.2.3") 236 err := machine.SetProviderAddresses(addrs...) 237 c.Assert(err, jc.ErrorIsNil) 238 // Set the addresses in the environ instance as well so that if the instance poller 239 // runs it won't overwrite them. 240 instId, err := machine.InstanceId() 241 c.Assert(err, jc.ErrorIsNil) 242 insts, err := s.Environ.Instances([]instance.Id{instId}) 243 c.Assert(err, jc.ErrorIsNil) 244 dummy.SetInstanceAddresses(insts[0], addrs) 245 } 246 247 // opRecvTimeout waits for any of the given kinds of operation to 248 // be received from ops, and times out if not. 249 func opRecvTimeout(c *gc.C, st *state.State, opc <-chan dummy.Operation, kinds ...dummy.Operation) dummy.Operation { 250 st.StartSync() 251 timeout := time.After(coretesting.LongWait) 252 for { 253 select { 254 case op := <-opc: 255 for _, k := range kinds { 256 if reflect.TypeOf(op) == reflect.TypeOf(k) { 257 return op 258 } 259 } 260 c.Logf("discarding unknown event %#v", op) 261 case <-time.After(coretesting.ShortWait): 262 st.StartSync() 263 case <-timeout: 264 c.Fatalf("time out wating for operation") 265 } 266 } 267 } 268 269 type mockAgentConfig struct { 270 agent.Config 271 providerType string 272 tag names.Tag 273 } 274 275 func (m *mockAgentConfig) Tag() names.Tag { 276 return m.tag 277 } 278 279 func (m *mockAgentConfig) Value(key string) string { 280 if key == agent.ProviderType { 281 return m.providerType 282 } 283 return "" 284 } 285 286 type singularRunnerRecord struct { 287 runnerC chan *fakeSingularRunner 288 } 289 290 func newSingularRunnerRecord() *singularRunnerRecord { 291 return &singularRunnerRecord{ 292 runnerC: make(chan *fakeSingularRunner, 64), 293 } 294 } 295 296 func (r *singularRunnerRecord) newSingularRunner(runner worker.Runner, conn singular.Conn) (worker.Runner, error) { 297 sr, err := singular.New(runner, conn) 298 if err != nil { 299 return nil, err 300 } 301 fakeRunner := &fakeSingularRunner{ 302 Runner: sr, 303 startC: make(chan string, 64), 304 } 305 r.runnerC <- fakeRunner 306 return fakeRunner, nil 307 } 308 309 // nextRunner blocks until a new singular runner is created. 310 func (r *singularRunnerRecord) nextRunner(c *gc.C) *fakeSingularRunner { 311 timeout := time.After(coretesting.LongWait) 312 for { 313 select { 314 case r := <-r.runnerC: 315 return r 316 case <-timeout: 317 c.Fatal("timed out waiting for singular runner to be created") 318 } 319 } 320 } 321 322 type fakeSingularRunner struct { 323 worker.Runner 324 startC chan string 325 } 326 327 func (r *fakeSingularRunner) StartWorker(name string, start func() (worker.Worker, error)) error { 328 logger.Infof("starting fake worker %q", name) 329 r.startC <- name 330 return r.Runner.StartWorker(name, start) 331 } 332 333 // waitForWorker waits for a given worker to be started, returning all 334 // workers started while waiting. 335 func (r *fakeSingularRunner) waitForWorker(c *gc.C, target string) []string { 336 var seen []string 337 timeout := time.After(coretesting.LongWait) 338 for { 339 select { 340 case <-time.After(coretesting.ShortWait): 341 c.Logf("still waiting for %q; workers seen so far: %+v", target, seen) 342 case workerName := <-r.startC: 343 seen = append(seen, workerName) 344 if workerName == target { 345 c.Logf("target worker %q started; workers seen so far: %+v", workerName, seen) 346 return seen 347 } 348 c.Logf("worker %q started; still waiting for %q; workers seen so far: %+v", workerName, target, seen) 349 case <-timeout: 350 c.Fatal("timed out waiting for " + target) 351 } 352 } 353 } 354 355 // waitForWorkers waits for a given worker to be started, returning all 356 // workers started while waiting. 357 func (r *fakeSingularRunner) waitForWorkers(c *gc.C, targets []string) []string { 358 var seen []string 359 seenTargets := make(map[string]bool) 360 numSeenTargets := 0 361 timeout := time.After(coretesting.LongWait) 362 for { 363 select { 364 case workerName := <-r.startC: 365 c.Logf("worker %q started; workers seen so far: %+v (len: %d, len(targets): %d)", workerName, seen, len(seen), len(targets)) 366 if seenTargets[workerName] == true { 367 c.Fatal("worker started twice: " + workerName) 368 } 369 seenTargets[workerName] = true 370 numSeenTargets++ 371 seen = append(seen, workerName) 372 if numSeenTargets == len(targets) { 373 c.Logf("all expected target workers started: %+v", seen) 374 return seen 375 } 376 c.Logf("still waiting for workers %+v to start; numSeenTargets=%d", targets, numSeenTargets) 377 case <-timeout: 378 c.Fatalf("timed out waiting for %v", targets) 379 } 380 } 381 } 382 383 type mockMetricAPI struct { 384 stop chan struct{} 385 cleanUpCalled chan struct{} 386 sendCalled chan struct{} 387 } 388 389 func newMockMetricAPI() *mockMetricAPI { 390 return &mockMetricAPI{ 391 stop: make(chan struct{}), 392 cleanUpCalled: make(chan struct{}), 393 sendCalled: make(chan struct{}), 394 } 395 } 396 397 func (m *mockMetricAPI) CleanupOldMetrics() error { 398 go func() { 399 select { 400 case m.cleanUpCalled <- struct{}{}: 401 case <-m.stop: 402 break 403 } 404 }() 405 return nil 406 } 407 408 func (m *mockMetricAPI) SendMetrics() error { 409 go func() { 410 select { 411 case m.sendCalled <- struct{}{}: 412 case <-m.stop: 413 break 414 } 415 }() 416 return nil 417 } 418 419 func (m *mockMetricAPI) SendCalled() <-chan struct{} { 420 return m.sendCalled 421 } 422 423 func (m *mockMetricAPI) CleanupCalled() <-chan struct{} { 424 return m.cleanUpCalled 425 } 426 427 func (m *mockMetricAPI) Stop() { 428 close(m.stop) 429 } 430 431 type mockLoopDeviceManager struct { 432 detachLoopDevicesArgRootfs string 433 detachLoopDevicesArgPrefix string 434 } 435 436 func (m *mockLoopDeviceManager) DetachLoopDevices(rootfs, prefix string) error { 437 m.detachLoopDevicesArgRootfs = rootfs 438 m.detachLoopDevicesArgPrefix = prefix 439 return nil 440 } 441 442 func newSignal() *signal { 443 return &signal{ch: make(chan struct{})} 444 } 445 446 type signal struct { 447 mu sync.Mutex 448 ch chan struct{} 449 } 450 451 func (s *signal) triggered() <-chan struct{} { 452 return s.ch 453 } 454 455 func (s *signal) assertTriggered(c *gc.C, thing string) { 456 select { 457 case <-s.triggered(): 458 case <-time.After(coretesting.LongWait): 459 c.Fatalf("timed out waiting for " + thing) 460 } 461 } 462 463 func (s *signal) assertNotTriggered(c *gc.C, wait time.Duration, thing string) { 464 select { 465 case <-s.triggered(): 466 c.Fatalf("%v unexpectedly", thing) 467 case <-time.After(wait): 468 } 469 } 470 471 func (s *signal) trigger() { 472 s.mu.Lock() 473 defer s.mu.Unlock() 474 475 select { 476 case <-s.ch: 477 // Already closed. 478 default: 479 close(s.ch) 480 } 481 } 482 483 type runner interface { 484 Run(*cmd.Context) error 485 Stop() error 486 } 487 488 // runWithTimeout runs an agent and waits 489 // for it to complete within a reasonable time. 490 func runWithTimeout(r runner) error { 491 done := make(chan error) 492 go func() { 493 done <- r.Run(nil) 494 }() 495 select { 496 case err := <-done: 497 return err 498 case <-time.After(coretesting.LongWait): 499 } 500 err := r.Stop() 501 return fmt.Errorf("timed out waiting for agent to finish; stop error: %v", err) 502 } 503 504 func newDummyWorker() worker.Worker { 505 return worker.NewSimpleWorker(func(stop <-chan struct{}) error { 506 <-stop 507 return nil 508 }) 509 } 510 511 type FakeConfig struct { 512 agent.ConfigSetter 513 } 514 515 func (FakeConfig) LogDir() string { 516 return filepath.FromSlash("/var/log/juju/") 517 } 518 519 func (FakeConfig) Tag() names.Tag { 520 return names.NewMachineTag("42") 521 } 522 523 type FakeAgentConfig struct { 524 AgentConf 525 } 526 527 func (FakeAgentConfig) ReadConfig(string) error { return nil } 528 529 func (FakeAgentConfig) CurrentConfig() agent.Config { 530 return FakeConfig{} 531 } 532 533 func (FakeAgentConfig) ChangeConfig(mutate agent.ConfigMutator) error { 534 return mutate(FakeConfig{}) 535 } 536 537 func (FakeAgentConfig) CheckArgs([]string) error { return nil } 538 539 // minModelWorkersEnviron implements just enough of environs.Environ 540 // to allow model workers to run. 541 type minModelWorkersEnviron struct { 542 environs.Environ 543 } 544 545 func (e *minModelWorkersEnviron) Config() *config.Config { 546 attrs := coretesting.FakeConfig() 547 cfg, err := config.New(config.NoDefaults, attrs) 548 if err != nil { 549 panic(err) 550 } 551 return cfg 552 } 553 554 func (e *minModelWorkersEnviron) SetConfig(*config.Config) error { 555 return nil 556 } 557 558 func (e *minModelWorkersEnviron) AllInstances() ([]instance.Instance, error) { 559 return nil, nil 560 }