github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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 "gopkg.in/tomb.v1" 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) TestRestartsFinishedWorker(c *gc.C) { 91 // It must be possible to restart the workers for a model due to 92 // model migrations: a model can be migrated away from a 93 // controller and then migrated back later. 94 s.runTest(c, func(w worker.Worker, backend *mockBackend) { 95 backend.sendModelChange("uuid") 96 workers := s.waitWorkers(c, 1) 97 workertest.CleanKill(c, workers[0]) 98 99 s.assertNoWorkers(c) 100 101 backend.sendModelChange("uuid") 102 workertest.CheckAlive(c, w) 103 s.waitWorkers(c, 1) 104 }) 105 } 106 107 func (s *suite) TestKillsManagers(c *gc.C) { 108 s.runTest(c, func(w worker.Worker, backend *mockBackend) { 109 backend.sendModelChange("uuid1", "uuid2") 110 workers := s.waitWorkers(c, 2) 111 112 workertest.CleanKill(c, w) 113 for _, worker := range workers { 114 workertest.CheckKilled(c, worker) 115 } 116 s.assertNoWorkers(c) 117 }) 118 } 119 120 func (s *suite) TestClosedChangesChannel(c *gc.C) { 121 s.runDirtyTest(c, func(w worker.Worker, backend *mockBackend) { 122 backend.sendModelChange("uuid1", "uuid2") 123 workers := s.waitWorkers(c, 2) 124 125 close(backend.envWatcher.changes) 126 err := workertest.CheckKilled(c, w) 127 c.Check(err, gc.ErrorMatches, "changes stopped") 128 for _, worker := range workers { 129 workertest.CheckKilled(c, worker) 130 } 131 s.assertNoWorkers(c) 132 }) 133 } 134 135 type testFunc func(worker.Worker, *mockBackend) 136 type killFunc func(*gc.C, worker.Worker) 137 138 func (s *suite) runTest(c *gc.C, test testFunc) { 139 s.runKillTest(c, workertest.CleanKill, test) 140 } 141 142 func (s *suite) runDirtyTest(c *gc.C, test testFunc) { 143 s.runKillTest(c, workertest.DirtyKill, test) 144 } 145 146 func (s *suite) runKillTest(c *gc.C, kill killFunc, test testFunc) { 147 backend := newMockBackend() 148 config := modelworkermanager.Config{ 149 ControllerUUID: coretesting.ControllerTag.Id(), 150 Backend: backend, 151 NewWorker: s.startModelWorker, 152 ErrorDelay: time.Millisecond, 153 } 154 w, err := modelworkermanager.New(config) 155 c.Assert(err, jc.ErrorIsNil) 156 defer kill(c, w) 157 test(w, backend) 158 } 159 160 func (s *suite) startModelWorker(controllerUUID, modelUUID string) (worker.Worker, error) { 161 worker := newMockWorker(controllerUUID, modelUUID) 162 s.workerC <- worker 163 return worker, nil 164 } 165 166 func (s *suite) assertStarts(c *gc.C, expect ...string) { 167 count := len(expect) 168 actual := make([]string, count) 169 workers := s.waitWorkers(c, count) 170 for i, worker := range workers { 171 actual[i] = worker.uuid 172 } 173 c.Assert(actual, jc.SameContents, expect) 174 } 175 176 func (s *suite) waitWorkers(c *gc.C, expectedCount int) []*mockWorker { 177 if expectedCount < 1 { 178 c.Fatal("expectedCount must be >= 1") 179 } 180 workers := make([]*mockWorker, 0, expectedCount) 181 for { 182 select { 183 case worker := <-s.workerC: 184 workers = append(workers, worker) 185 if len(workers) == expectedCount { 186 s.assertNoWorkers(c) 187 return workers 188 } 189 case <-time.After(coretesting.LongWait): 190 c.Fatal("timed out waiting for workers to be started") 191 } 192 } 193 } 194 195 func (s *suite) assertNoWorkers(c *gc.C) { 196 select { 197 case worker := <-s.workerC: 198 c.Fatalf("saw unexpected worker: %s", worker.uuid) 199 case <-time.After(coretesting.ShortWait): 200 } 201 } 202 203 func newMockWorker(_, modelUUID string) *mockWorker { 204 w := &mockWorker{uuid: modelUUID} 205 go func() { 206 defer w.tomb.Done() 207 <-w.tomb.Dying() 208 }() 209 return w 210 } 211 212 type mockWorker struct { 213 tomb tomb.Tomb 214 uuid string 215 } 216 217 func (mock *mockWorker) Kill() { 218 mock.tomb.Kill(nil) 219 } 220 221 func (mock *mockWorker) Wait() error { 222 return mock.tomb.Wait() 223 } 224 225 func newMockBackend() *mockBackend { 226 return &mockBackend{ 227 envWatcher: &mockEnvWatcher{ 228 Worker: workertest.NewErrorWorker(nil), 229 changes: make(chan []string), 230 }, 231 } 232 } 233 234 type mockBackend struct { 235 envWatcher *mockEnvWatcher 236 } 237 238 func (mock *mockBackend) WatchModels() state.StringsWatcher { 239 return mock.envWatcher 240 } 241 242 func (mock *mockBackend) sendModelChange(uuids ...string) { 243 mock.envWatcher.changes <- uuids 244 } 245 246 type mockEnvWatcher struct { 247 worker.Worker 248 changes chan []string 249 } 250 251 func (w *mockEnvWatcher) Err() error { 252 panic("not used") 253 } 254 255 func (w *mockEnvWatcher) Stop() error { 256 return worker.Stop(w) 257 } 258 259 func (w *mockEnvWatcher) Changes() <-chan []string { 260 return w.changes 261 }