github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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/juju/worker.v1" 14 "gopkg.in/juju/worker.v1/workertest" 15 "gopkg.in/tomb.v2" 16 17 "github.com/juju/juju/state" 18 coretesting "github.com/juju/juju/testing" 19 "github.com/juju/juju/worker/modelworkermanager" 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, w *mockModelWatcher, _ *mockModelGetter) { 36 w.sendModelChange() 37 38 s.assertNoWorkers(c) 39 }) 40 } 41 42 func (s *suite) TestStartsInitialWorker(c *gc.C) { 43 s.runTest(c, func(_ worker.Worker, w *mockModelWatcher, _ *mockModelGetter) { 44 w.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, w *mockModelWatcher, _ *mockModelGetter) { 52 w.sendModelChange() 53 w.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, w *mockModelWatcher, _ *mockModelGetter) { 61 w.sendModelChange("uuid1") 62 w.sendModelChange("uuid2", "uuid3") 63 w.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, w *mockModelWatcher, _ *mockModelGetter) { 71 w.sendModelChange("uuid") 72 w.sendModelChange("uuid", "uuid") 73 w.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, mw *mockModelWatcher, _ *mockModelGetter) { 81 mw.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, mw *mockModelWatcher, _ *mockModelGetter) { 95 mw.sendModelChange("uuid") 96 workers := s.waitWorkers(c, 1) 97 workertest.CleanKill(c, workers[0]) 98 99 s.assertNoWorkers(c) 100 101 mw.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, mw *mockModelWatcher, _ *mockModelGetter) { 109 mw.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, mw *mockModelWatcher, _ *mockModelGetter) { 122 mw.sendModelChange("uuid1", "uuid2") 123 workers := s.waitWorkers(c, 2) 124 125 close(mw.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 func (s *suite) TestNoStartingWorkersForImportingModel(c *gc.C) { 136 // We shouldn't start workers while the model is importing, 137 // otherwise the migrationmaster gets very confused. 138 // https://bugs.launchpad.net/juju/+bug/1646310 139 s.runTest(c, func(_ worker.Worker, w *mockModelWatcher, g *mockModelGetter) { 140 g.model.migrationMode = state.MigrationModeImporting 141 w.sendModelChange("uuid1") 142 143 s.assertNoWorkers(c) 144 }) 145 } 146 147 func (s *suite) TestReport(c *gc.C) { 148 s.runTest(c, func(w worker.Worker, mw *mockModelWatcher, _ *mockModelGetter) { 149 mw.sendModelChange("uuid") 150 s.assertStarts(c, "uuid") 151 152 reporter, ok := w.(worker.Reporter) 153 c.Assert(ok, jc.IsTrue) 154 report := reporter.Report() 155 c.Assert(report, gc.NotNil) 156 // TODO: pass a clock through in the worker config so it can be passed 157 // to the worker.Runner used in the model to control time. 158 // For now, we just look at the started state. 159 workers := report["workers"].(map[string]interface{}) 160 modelWorker := workers["uuid"].(map[string]interface{}) 161 c.Assert(modelWorker["state"], gc.Equals, "started") 162 }) 163 } 164 165 type testFunc func(worker.Worker, *mockModelWatcher, *mockModelGetter) 166 type killFunc func(*gc.C, worker.Worker) 167 168 func (s *suite) runTest(c *gc.C, test testFunc) { 169 s.runKillTest(c, workertest.CleanKill, test) 170 } 171 172 func (s *suite) runDirtyTest(c *gc.C, test testFunc) { 173 s.runKillTest(c, workertest.DirtyKill, test) 174 } 175 176 func (s *suite) runKillTest(c *gc.C, kill killFunc, test testFunc) { 177 watcher := newMockModelWatcher() 178 getter := newMockModelGetter() 179 config := modelworkermanager.Config{ 180 ModelWatcher: watcher, 181 ModelGetter: getter, 182 NewModelWorker: s.startModelWorker, 183 ErrorDelay: time.Millisecond, 184 } 185 w, err := modelworkermanager.New(config) 186 c.Assert(err, jc.ErrorIsNil) 187 defer kill(c, w) 188 test(w, watcher, getter) 189 } 190 191 func (s *suite) startModelWorker(modelUUID string, modelType state.ModelType) (worker.Worker, error) { 192 worker := newMockWorker(modelUUID, modelType) 193 s.workerC <- worker 194 return worker, nil 195 } 196 197 func (s *suite) assertStarts(c *gc.C, expect ...string) { 198 count := len(expect) 199 actual := make([]string, count) 200 workers := s.waitWorkers(c, count) 201 for i, worker := range workers { 202 actual[i] = worker.uuid 203 c.Assert(worker.modelType, gc.Equals, state.ModelTypeIAAS) 204 } 205 c.Assert(actual, jc.SameContents, expect) 206 } 207 208 func (s *suite) waitWorkers(c *gc.C, expectedCount int) []*mockWorker { 209 if expectedCount < 1 { 210 c.Fatal("expectedCount must be >= 1") 211 } 212 workers := make([]*mockWorker, 0, expectedCount) 213 for { 214 select { 215 case worker := <-s.workerC: 216 workers = append(workers, worker) 217 if len(workers) == expectedCount { 218 s.assertNoWorkers(c) 219 return workers 220 } 221 case <-time.After(coretesting.LongWait): 222 c.Fatal("timed out waiting for workers to be started") 223 } 224 } 225 } 226 227 func (s *suite) assertNoWorkers(c *gc.C) { 228 select { 229 case worker := <-s.workerC: 230 c.Fatalf("saw unexpected worker: %s", worker.uuid) 231 case <-time.After(coretesting.ShortWait): 232 } 233 } 234 235 func newMockWorker(modelUUID string, modelType state.ModelType) *mockWorker { 236 w := &mockWorker{uuid: modelUUID, modelType: modelType} 237 w.tomb.Go(func() error { 238 <-w.tomb.Dying() 239 return nil 240 }) 241 return w 242 } 243 244 type mockWorker struct { 245 tomb tomb.Tomb 246 uuid string 247 modelType state.ModelType 248 } 249 250 func (mock *mockWorker) Kill() { 251 mock.tomb.Kill(nil) 252 } 253 254 func (mock *mockWorker) Wait() error { 255 return mock.tomb.Wait() 256 } 257 258 func newMockModelWatcher() *mockModelWatcher { 259 return &mockModelWatcher{ 260 envWatcher: &mockEnvWatcher{ 261 Worker: workertest.NewErrorWorker(nil), 262 changes: make(chan []string), 263 }, 264 } 265 } 266 267 type mockModelWatcher struct { 268 envWatcher *mockEnvWatcher 269 modelErr error 270 } 271 272 func (mock *mockModelWatcher) WatchModels() state.StringsWatcher { 273 return mock.envWatcher 274 } 275 276 func (mock *mockModelWatcher) sendModelChange(uuids ...string) { 277 mock.envWatcher.changes <- uuids 278 } 279 280 type mockModelGetter struct { 281 testing.Stub 282 model mockModel 283 } 284 285 func newMockModelGetter() *mockModelGetter { 286 return &mockModelGetter{ 287 model: mockModel{ 288 migrationMode: state.MigrationModeNone, 289 modelType: state.ModelTypeIAAS, 290 }, 291 } 292 } 293 294 func (mock *mockModelGetter) Model(uuid string) (modelworkermanager.Model, func(), error) { 295 mock.MethodCall(mock, "Model", uuid) 296 if err := mock.NextErr(); err != nil { 297 return nil, nil, err 298 } 299 release := func() { 300 mock.MethodCall(mock, "release") 301 } 302 return &mock.model, release, nil 303 } 304 305 type mockModel struct { 306 migrationMode state.MigrationMode 307 modelType state.ModelType 308 } 309 310 func (m *mockModel) MigrationMode() state.MigrationMode { 311 return m.migrationMode 312 } 313 314 func (m *mockModel) Type() state.ModelType { 315 return m.modelType 316 } 317 318 type mockEnvWatcher struct { 319 worker.Worker 320 changes chan []string 321 } 322 323 func (w *mockEnvWatcher) Err() error { 324 panic("not used") 325 } 326 327 func (w *mockEnvWatcher) Stop() error { 328 return worker.Stop(w) 329 } 330 331 func (w *mockEnvWatcher) Changes() <-chan []string { 332 return w.changes 333 }