github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/worker/envworkermanager/envworkermanager_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package envworkermanager_test 5 6 import ( 7 stdtesting "testing" 8 "time" 9 10 "github.com/juju/errors" 11 cmdutil "github.com/juju/juju/cmd/jujud/util" 12 "github.com/juju/loggo" 13 "github.com/juju/names" 14 jc "github.com/juju/testing/checkers" 15 gc "gopkg.in/check.v1" 16 "launchpad.net/tomb" 17 18 "github.com/juju/juju/state" 19 statetesting "github.com/juju/juju/state/testing" 20 "github.com/juju/juju/testing" 21 "github.com/juju/juju/testing/factory" 22 "github.com/juju/juju/worker" 23 "github.com/juju/juju/worker/envworkermanager" 24 ) 25 26 func TestPackage(t *stdtesting.T) { 27 testing.MgoTestPackage(t) 28 } 29 30 var _ = gc.Suite(&suite{}) 31 32 type suite struct { 33 statetesting.StateSuite 34 factory *factory.Factory 35 runnerC chan *fakeRunner 36 startErr error 37 } 38 39 func (s *suite) SetUpTest(c *gc.C) { 40 s.StateSuite.SetUpTest(c) 41 s.factory = factory.NewFactory(s.State) 42 s.runnerC = make(chan *fakeRunner, 1) 43 s.startErr = nil 44 } 45 46 func (s *suite) makeEnvironment(c *gc.C) *state.State { 47 st := s.factory.MakeEnvironment(c, nil) 48 s.AddCleanup(func(*gc.C) { st.Close() }) 49 return st 50 } 51 52 func (s *suite) TestStartsWorkersForPreExistingEnvs(c *gc.C) { 53 moreState := s.makeEnvironment(c) 54 55 var seenEnvs []string 56 m := envworkermanager.NewEnvWorkerManager(s.State, s.startEnvWorker) 57 defer m.Kill() 58 for _, r := range s.seeRunnersStart(c, 2) { 59 seenEnvs = append(seenEnvs, r.envUUID) 60 } 61 c.Assert(seenEnvs, jc.SameContents, 62 []string{s.State.EnvironUUID(), moreState.EnvironUUID()}, 63 ) 64 } 65 66 func (s *suite) TestStartsWorkersForNewEnv(c *gc.C) { 67 m := envworkermanager.NewEnvWorkerManager(s.State, s.startEnvWorker) 68 defer m.Kill() 69 s.seeRunnersStart(c, 1) // Runner for state server env 70 71 // Create another environment and watch a runner be created for it. 72 st2 := s.makeEnvironment(c) 73 runner := s.seeRunnersStart(c, 1)[0] 74 c.Assert(runner.envUUID, gc.Equals, st2.EnvironUUID()) 75 } 76 77 func (s *suite) TestStopsWorkersWhenEnvGoesAway(c *gc.C) { 78 m := envworkermanager.NewEnvWorkerManager(s.State, s.startEnvWorker) 79 defer m.Kill() 80 runner0 := s.seeRunnersStart(c, 1)[0] 81 82 // Create an environment and grab the runner for it. 83 otherState := s.makeEnvironment(c) 84 runner1 := s.seeRunnersStart(c, 1)[0] 85 86 // Destroy the new environment. 87 env, err := otherState.Environment() 88 c.Assert(err, jc.ErrorIsNil) 89 err = env.Destroy() 90 c.Assert(err, jc.ErrorIsNil) 91 92 // See that the first runner is still running but the runner for 93 // the new environment is stopped. 94 s.State.StartSync() 95 select { 96 case <-runner0.tomb.Dying(): 97 c.Fatal("first runner should not die here") 98 case <-runner1.tomb.Dying(): 99 break 100 case <-time.After(testing.LongWait): 101 c.Fatal("timed out waiting for runner to die") 102 } 103 104 // Make sure the first runner doesn't get stopped. 105 s.State.StartSync() 106 select { 107 case <-runner0.tomb.Dying(): 108 c.Fatal("first runner should not die here") 109 case <-time.After(testing.ShortWait): 110 break 111 } 112 } 113 114 func (s *suite) TestKillPropagates(c *gc.C) { 115 s.makeEnvironment(c) 116 117 m := envworkermanager.NewEnvWorkerManager(s.State, s.startEnvWorker) 118 runners := s.seeRunnersStart(c, 2) 119 c.Assert(runners[0].killed, jc.IsFalse) 120 c.Assert(runners[1].killed, jc.IsFalse) 121 122 m.Kill() 123 err := waitOrFatal(c, m.Wait) 124 c.Assert(err, jc.ErrorIsNil) 125 126 c.Assert(runners[0].killed, jc.IsTrue) 127 c.Assert(runners[1].killed, jc.IsTrue) 128 } 129 130 // stateWithFailingGetEnvironment wraps a *state.State, overriding the 131 // GetEnvironment to generate an error. 132 type stateWithFailingGetEnvironment struct { 133 *stateWithFakeWatcher 134 shouldFail bool 135 } 136 137 func newStateWithFailingGetEnvironment(realSt *state.State) *stateWithFailingGetEnvironment { 138 return &stateWithFailingGetEnvironment{ 139 stateWithFakeWatcher: newStateWithFakeWatcher(realSt), 140 shouldFail: false, 141 } 142 } 143 func (s *stateWithFailingGetEnvironment) GetEnvironment(tag names.EnvironTag) (*state.Environment, error) { 144 if s.shouldFail { 145 return nil, errors.New("unable to GetEnvironment") 146 } 147 return s.State.GetEnvironment(tag) 148 } 149 150 func (s *suite) TestLoopExitKillsRunner(c *gc.C) { 151 // If something causes EnvWorkerManager.loop to exit that isn't Kill() then it should stop the runner. 152 // Currently the best way to cause this is to make 153 // m.st.GetEnvironment(tag) fail with any error other than NotFound 154 st := newStateWithFailingGetEnvironment(s.State) 155 uuid := st.EnvironUUID() 156 m := envworkermanager.NewEnvWorkerManager(st, s.startEnvWorker) 157 defer m.Kill() 158 159 // First time: runners started 160 st.sendEnvChange(uuid) 161 runners := s.seeRunnersStart(c, 1) 162 c.Assert(runners[0].killed, jc.IsFalse) 163 164 // Now we start failing 165 st.shouldFail = true 166 st.sendEnvChange(uuid) 167 168 // This should kill the manager 169 err := waitOrFatal(c, m.Wait) 170 c.Assert(err, gc.ErrorMatches, "error loading environment .*: unable to GetEnvironment") 171 172 // And that should kill all the runners 173 c.Assert(runners[0].killed, jc.IsTrue) 174 } 175 176 func (s *suite) TestWorkerErrorIsPropagatedWhenKilled(c *gc.C) { 177 st := newStateWithFakeWatcher(s.State) 178 started := make(chan struct{}, 1) 179 m := envworkermanager.NewEnvWorkerManager(st, func(envworkermanager.InitialState, *state.State) (worker.Worker, error) { 180 c.Logf("starting worker") 181 started <- struct{}{} 182 return &errorWhenKilledWorker{ 183 err: &cmdutil.FatalError{"an error"}, 184 }, nil 185 }) 186 st.sendEnvChange(st.EnvironUUID()) 187 s.State.StartSync() 188 <-started 189 m.Kill() 190 err := m.Wait() 191 c.Assert(err, gc.ErrorMatches, "an error") 192 } 193 194 type errorWhenKilledWorker struct { 195 tomb tomb.Tomb 196 err error 197 } 198 199 var logger = loggo.GetLogger("juju.worker.envworkermanager") 200 201 func (w *errorWhenKilledWorker) Kill() { 202 w.tomb.Kill(w.err) 203 logger.Errorf("errorWhenKilledWorker dying with error %v", w.err) 204 w.tomb.Done() 205 } 206 207 func (w *errorWhenKilledWorker) Wait() error { 208 err := w.tomb.Wait() 209 logger.Errorf("errorWhenKilledWorker wait -> error %v", err) 210 return err 211 } 212 213 func (s *suite) TestNothingHappensWhenEnvIsSeenAgain(c *gc.C) { 214 // This could happen if there's a change to an environment doc but 215 // it's otherwise still alive (unlikely but possible). 216 st := newStateWithFakeWatcher(s.State) 217 uuid := st.EnvironUUID() 218 219 m := envworkermanager.NewEnvWorkerManager(st, s.startEnvWorker) 220 defer m.Kill() 221 222 // First time: runners started 223 st.sendEnvChange(uuid) 224 s.seeRunnersStart(c, 1) 225 226 // Second time: no runners started 227 st.sendEnvChange(uuid) 228 s.checkNoRunnersStart(c) 229 } 230 231 func (s *suite) TestNothingHappensWhenUnknownEnvReported(c *gc.C) { 232 // This could perhaps happen when an environment is dying just as 233 // the EnvWorkerManager is coming up (unlikely but possible). 234 st := newStateWithFakeWatcher(s.State) 235 236 m := envworkermanager.NewEnvWorkerManager(st, s.startEnvWorker) 237 defer m.Kill() 238 239 st.sendEnvChange("unknown-env-uuid") 240 s.checkNoRunnersStart(c) 241 242 // Existing environment still works. 243 st.sendEnvChange(st.EnvironUUID()) 244 s.seeRunnersStart(c, 1) 245 } 246 247 func (s *suite) TestFatalErrorKillsEnvWorkerManager(c *gc.C) { 248 m := envworkermanager.NewEnvWorkerManager(s.State, s.startEnvWorker) 249 runner := s.seeRunnersStart(c, 1)[0] 250 251 runner.tomb.Kill(worker.ErrTerminateAgent) 252 runner.tomb.Done() 253 254 err := waitOrFatal(c, m.Wait) 255 c.Assert(errors.Cause(err), gc.Equals, worker.ErrTerminateAgent) 256 } 257 258 func (s *suite) TestNonFatalErrorCausesRunnerRestart(c *gc.C) { 259 s.PatchValue(&worker.RestartDelay, time.Millisecond) 260 261 m := envworkermanager.NewEnvWorkerManager(s.State, s.startEnvWorker) 262 defer m.Kill() 263 runner0 := s.seeRunnersStart(c, 1)[0] 264 265 runner0.tomb.Kill(errors.New("trivial")) 266 runner0.tomb.Done() 267 268 s.seeRunnersStart(c, 1) 269 } 270 271 func (s *suite) TestStateIsClosedIfStartEnvWorkersFails(c *gc.C) { 272 // If State is not closed when startEnvWorker errors, MgoSuite's 273 // dirty socket detection will pick up the leaked socket and 274 // panic. 275 s.startErr = worker.ErrTerminateAgent // This will make envWorkerManager exit. 276 m := envworkermanager.NewEnvWorkerManager(s.State, s.startEnvWorker) 277 waitOrFatal(c, m.Wait) 278 } 279 280 func (s *suite) seeRunnersStart(c *gc.C, expectedCount int) []*fakeRunner { 281 if expectedCount < 1 { 282 panic("expectedCount must be >= 1") 283 } 284 s.State.StartSync() 285 runners := make([]*fakeRunner, 0, expectedCount) 286 for { 287 select { 288 case r := <-s.runnerC: 289 c.Assert(r.ssEnvUUID, gc.Equals, s.State.EnvironUUID()) 290 291 runners = append(runners, r) 292 if len(runners) == expectedCount { 293 s.checkNoRunnersStart(c) // Check no more runners start 294 return runners 295 } 296 case <-time.After(testing.LongWait): 297 c.Fatal("timed out waiting for runners to be started") 298 } 299 } 300 } 301 302 func (s *suite) checkNoRunnersStart(c *gc.C) { 303 s.State.StartSync() 304 for { 305 select { 306 case <-s.runnerC: 307 c.Fatal("saw runner creation when expecting none") 308 case <-time.After(testing.ShortWait): 309 return 310 } 311 } 312 } 313 314 // startEnvWorker is passed to NewEnvWorkerManager in these tests. It 315 // creates fake Runner instances when envWorkerManager starts workers 316 // for an environment. 317 func (s *suite) startEnvWorker(ssSt envworkermanager.InitialState, st *state.State) (worker.Worker, error) { 318 if s.startErr != nil { 319 return nil, s.startErr 320 } 321 runner := &fakeRunner{ 322 ssEnvUUID: ssSt.EnvironUUID(), 323 envUUID: st.EnvironUUID(), 324 } 325 s.runnerC <- runner 326 return runner, nil 327 } 328 329 func waitOrFatal(c *gc.C, wait func() error) error { 330 errC := make(chan error) 331 go func() { 332 errC <- wait() 333 }() 334 335 select { 336 case err := <-errC: 337 return err 338 case <-time.After(testing.LongWait): 339 c.Fatal("waited too long") 340 } 341 return nil 342 } 343 344 // fakeRunner minimally implements the worker.Worker interface. It 345 // doesn't actually run anything, recording some execution details for 346 // testing. 347 type fakeRunner struct { 348 tomb tomb.Tomb 349 ssEnvUUID string 350 envUUID string 351 killed bool 352 } 353 354 func (r *fakeRunner) Kill() { 355 r.killed = true 356 r.tomb.Done() 357 } 358 359 func (r *fakeRunner) Wait() error { 360 return r.tomb.Wait() 361 } 362 363 func newStateWithFakeWatcher(realSt *state.State) *stateWithFakeWatcher { 364 return &stateWithFakeWatcher{ 365 State: realSt, 366 envWatcher: &fakeEnvWatcher{ 367 changes: make(chan []string), 368 }, 369 } 370 } 371 372 // stateWithFakeWatcher wraps a *state.State, overriding the 373 // WatchEnvironments method to allow control over the reported 374 // environment lifecycle events for testing. 375 // 376 // Use sendEnvChange to cause an environment event to be emitted by 377 // the watcher returned by WatchEnvironments. 378 type stateWithFakeWatcher struct { 379 *state.State 380 envWatcher *fakeEnvWatcher 381 } 382 383 func (s *stateWithFakeWatcher) WatchEnvironments() state.StringsWatcher { 384 return s.envWatcher 385 } 386 387 func (s *stateWithFakeWatcher) sendEnvChange(uuids ...string) { 388 s.envWatcher.changes <- uuids 389 } 390 391 type fakeEnvWatcher struct { 392 state.StringsWatcher 393 changes chan []string 394 } 395 396 func (w *fakeEnvWatcher) Stop() error { 397 return nil 398 } 399 400 func (w *fakeEnvWatcher) Changes() <-chan []string { 401 return w.changes 402 }