github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/modelworkermanager/modelworkermanager_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package modelworkermanager_test 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 "launchpad.net/tomb" 14 15 "github.com/juju/juju/state" 16 coretesting "github.com/juju/juju/testing" 17 "github.com/juju/juju/worker" 18 "github.com/juju/juju/worker/modelworkermanager" 19 "github.com/juju/juju/worker/workertest" 20 ) 21 22 var _ = gc.Suite(&suite{}) 23 24 type suite struct { 25 testing.IsolationSuite 26 workerC chan *mockWorker 27 } 28 29 func (s *suite) SetUpTest(c *gc.C) { 30 s.IsolationSuite.SetUpTest(c) 31 s.workerC = make(chan *mockWorker, 100) 32 } 33 34 func (s *suite) TestStartEmpty(c *gc.C) { 35 s.runTest(c, func(_ worker.Worker, backend *mockBackend) { 36 backend.sendModelChange() 37 38 s.assertNoWorkers(c) 39 }) 40 } 41 42 func (s *suite) TestStartsInitialWorker(c *gc.C) { 43 s.runTest(c, func(_ worker.Worker, backend *mockBackend) { 44 backend.sendModelChange("uuid") 45 46 s.assertStarts(c, "uuid") 47 }) 48 } 49 50 func (s *suite) TestStartsLaterWorker(c *gc.C) { 51 s.runTest(c, func(_ worker.Worker, backend *mockBackend) { 52 backend.sendModelChange() 53 backend.sendModelChange("uuid") 54 55 s.assertStarts(c, "uuid") 56 }) 57 } 58 59 func (s *suite) TestStartsMultiple(c *gc.C) { 60 s.runTest(c, func(_ worker.Worker, backend *mockBackend) { 61 backend.sendModelChange("uuid1") 62 backend.sendModelChange("uuid2", "uuid3") 63 backend.sendModelChange("uuid4") 64 65 s.assertStarts(c, "uuid1", "uuid2", "uuid3", "uuid4") 66 }) 67 } 68 69 func (s *suite) TestIgnoresRepetition(c *gc.C) { 70 s.runTest(c, func(_ worker.Worker, backend *mockBackend) { 71 backend.sendModelChange("uuid") 72 backend.sendModelChange("uuid", "uuid") 73 backend.sendModelChange("uuid") 74 75 s.assertStarts(c, "uuid") 76 }) 77 } 78 79 func (s *suite) TestRestartsErrorWorker(c *gc.C) { 80 s.runTest(c, func(w worker.Worker, backend *mockBackend) { 81 backend.sendModelChange("uuid") 82 workers := s.waitWorkers(c, 1) 83 workers[0].tomb.Kill(errors.New("blaf")) 84 85 s.assertStarts(c, "uuid") 86 workertest.CheckAlive(c, w) 87 }) 88 } 89 90 func (s *suite) TestNeverRestartsFinishedWorker(c *gc.C) { 91 s.runTest(c, func(w worker.Worker, backend *mockBackend) { 92 backend.sendModelChange("uuid") 93 workers := s.waitWorkers(c, 1) 94 workers[0].tomb.Kill(nil) 95 96 // even when we get a change for it 97 backend.sendModelChange("uuid") 98 workertest.CheckAlive(c, w) 99 s.assertNoWorkers(c) 100 }) 101 } 102 103 func (s *suite) TestKillsManagers(c *gc.C) { 104 s.runTest(c, func(w worker.Worker, backend *mockBackend) { 105 backend.sendModelChange("uuid1", "uuid2") 106 workers := s.waitWorkers(c, 2) 107 108 workertest.CleanKill(c, w) 109 for _, worker := range workers { 110 workertest.CheckKilled(c, worker) 111 } 112 s.assertNoWorkers(c) 113 }) 114 } 115 116 func (s *suite) TestClosedChangesChannel(c *gc.C) { 117 s.runDirtyTest(c, func(w worker.Worker, backend *mockBackend) { 118 backend.sendModelChange("uuid1", "uuid2") 119 workers := s.waitWorkers(c, 2) 120 121 close(backend.envWatcher.changes) 122 err := workertest.CheckKilled(c, w) 123 c.Check(err, gc.ErrorMatches, "changes stopped") 124 for _, worker := range workers { 125 workertest.CheckKilled(c, worker) 126 } 127 s.assertNoWorkers(c) 128 }) 129 } 130 131 type testFunc func(worker.Worker, *mockBackend) 132 type killFunc func(*gc.C, worker.Worker) 133 134 func (s *suite) runTest(c *gc.C, test testFunc) { 135 s.runKillTest(c, workertest.CleanKill, test) 136 } 137 138 func (s *suite) runDirtyTest(c *gc.C, test testFunc) { 139 s.runKillTest(c, workertest.DirtyKill, test) 140 } 141 142 func (s *suite) runKillTest(c *gc.C, kill killFunc, test testFunc) { 143 backend := newMockBackend() 144 config := modelworkermanager.Config{ 145 Backend: backend, 146 NewWorker: s.startModelWorker, 147 ErrorDelay: time.Millisecond, 148 } 149 w, err := modelworkermanager.New(config) 150 c.Assert(err, jc.ErrorIsNil) 151 defer kill(c, w) 152 test(w, backend) 153 } 154 155 func (s *suite) startModelWorker(uuid string) (worker.Worker, error) { 156 worker := newMockWorker(uuid) 157 s.workerC <- worker 158 return worker, nil 159 } 160 161 func (s *suite) assertStarts(c *gc.C, expect ...string) { 162 count := len(expect) 163 actual := make([]string, count) 164 workers := s.waitWorkers(c, count) 165 for i, worker := range workers { 166 actual[i] = worker.uuid 167 } 168 c.Assert(actual, jc.SameContents, expect) 169 } 170 171 func (s *suite) waitWorkers(c *gc.C, expectedCount int) []*mockWorker { 172 if expectedCount < 1 { 173 c.Fatal("expectedCount must be >= 1") 174 } 175 workers := make([]*mockWorker, 0, expectedCount) 176 for { 177 select { 178 case worker := <-s.workerC: 179 workers = append(workers, worker) 180 if len(workers) == expectedCount { 181 s.assertNoWorkers(c) 182 return workers 183 } 184 case <-time.After(coretesting.LongWait): 185 c.Fatal("timed out waiting for workers to be started") 186 } 187 } 188 } 189 190 func (s *suite) assertNoWorkers(c *gc.C) { 191 select { 192 case worker := <-s.workerC: 193 c.Fatalf("saw unexpected worker: %s", worker.uuid) 194 case <-time.After(coretesting.ShortWait): 195 } 196 } 197 198 func newMockWorker(uuid string) *mockWorker { 199 w := &mockWorker{uuid: uuid} 200 go func() { 201 defer w.tomb.Done() 202 <-w.tomb.Dying() 203 }() 204 return w 205 } 206 207 type mockWorker struct { 208 tomb tomb.Tomb 209 uuid string 210 } 211 212 func (mock *mockWorker) Kill() { 213 mock.tomb.Kill(nil) 214 } 215 216 func (mock *mockWorker) Wait() error { 217 return mock.tomb.Wait() 218 } 219 220 func newMockBackend() *mockBackend { 221 return &mockBackend{ 222 envWatcher: &mockEnvWatcher{ 223 Worker: workertest.NewErrorWorker(nil), 224 changes: make(chan []string), 225 }, 226 } 227 } 228 229 type mockBackend struct { 230 envWatcher *mockEnvWatcher 231 } 232 233 func (mock *mockBackend) WatchModels() state.StringsWatcher { 234 return mock.envWatcher 235 } 236 237 func (mock *mockBackend) sendModelChange(uuids ...string) { 238 mock.envWatcher.changes <- uuids 239 } 240 241 type mockEnvWatcher struct { 242 worker.Worker 243 changes chan []string 244 } 245 246 func (w *mockEnvWatcher) Err() error { 247 panic("not used") 248 } 249 250 func (w *mockEnvWatcher) Stop() error { 251 return worker.Stop(w) 252 } 253 254 func (w *mockEnvWatcher) Changes() <-chan []string { 255 return w.changes 256 }