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  }