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  }