github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/stateconfigwatcher/manifold_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package stateconfigwatcher_test
     5  
     6  import (
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/juju/testing"
    11  	jc "github.com/juju/testing/checkers"
    12  	"github.com/juju/utils/voyeur"
    13  	gc "gopkg.in/check.v1"
    14  	"gopkg.in/juju/names.v2"
    15  	"gopkg.in/juju/worker.v1"
    16  	"gopkg.in/juju/worker.v1/dependency"
    17  	dt "gopkg.in/juju/worker.v1/dependency/testing"
    18  
    19  	coreagent "github.com/juju/juju/agent"
    20  	"github.com/juju/juju/apiserver/params"
    21  	coretesting "github.com/juju/juju/testing"
    22  	"github.com/juju/juju/worker/stateconfigwatcher"
    23  )
    24  
    25  type ManifoldSuite struct {
    26  	testing.IsolationSuite
    27  	agent              *mockAgent
    28  	goodContext        dependency.Context
    29  	agentConfigChanged *voyeur.Value
    30  	manifold           dependency.Manifold
    31  	worker             worker.Worker
    32  }
    33  
    34  var _ = gc.Suite(&ManifoldSuite{})
    35  
    36  func (s *ManifoldSuite) SetUpTest(c *gc.C) {
    37  	s.IsolationSuite.SetUpTest(c)
    38  
    39  	s.agent = new(mockAgent)
    40  	s.agent.conf.setStateServingInfo(true)
    41  	s.agent.conf.tag = names.NewMachineTag("99")
    42  
    43  	s.goodContext = dt.StubContext(nil, map[string]interface{}{
    44  		"agent": s.agent,
    45  	})
    46  
    47  	s.agentConfigChanged = voyeur.NewValue(0)
    48  	s.manifold = stateconfigwatcher.Manifold(stateconfigwatcher.ManifoldConfig{
    49  		AgentName:          "agent",
    50  		AgentConfigChanged: s.agentConfigChanged,
    51  	})
    52  }
    53  
    54  func (s *ManifoldSuite) TestInputs(c *gc.C) {
    55  	c.Assert(s.manifold.Inputs, jc.SameContents, []string{"agent"})
    56  }
    57  
    58  func (s *ManifoldSuite) TestNoAgent(c *gc.C) {
    59  	context := dt.StubContext(nil, map[string]interface{}{
    60  		"agent": dependency.ErrMissing,
    61  	})
    62  	_, err := s.manifold.Start(context)
    63  	c.Assert(err, gc.Equals, dependency.ErrMissing)
    64  }
    65  
    66  func (s *ManifoldSuite) TestNilAgentConfigChanged(c *gc.C) {
    67  	manifold := stateconfigwatcher.Manifold(stateconfigwatcher.ManifoldConfig{
    68  		AgentName: "agent",
    69  	})
    70  	_, err := manifold.Start(s.goodContext)
    71  	c.Assert(err, gc.ErrorMatches, "nil AgentConfigChanged .+")
    72  }
    73  
    74  func (s *ManifoldSuite) TestNotMachineAgent(c *gc.C) {
    75  	s.agent.conf.tag = names.NewUnitTag("foo/0")
    76  	_, err := s.manifold.Start(s.goodContext)
    77  	c.Assert(err, gc.ErrorMatches, "manifold can only be used with a machine agent")
    78  }
    79  
    80  func (s *ManifoldSuite) TestStart(c *gc.C) {
    81  	w, err := s.manifold.Start(s.goodContext)
    82  	c.Assert(err, jc.ErrorIsNil)
    83  	checkStop(c, w)
    84  }
    85  
    86  func (s *ManifoldSuite) TestOutputBadWorker(c *gc.C) {
    87  	var out bool
    88  	err := s.manifold.Output(dummyWorker{}, &out)
    89  	c.Check(err, gc.ErrorMatches, `in should be a \*stateconfigwatcher.stateConfigWatcher; .+`)
    90  }
    91  
    92  func (s *ManifoldSuite) TestOutputWrongType(c *gc.C) {
    93  	w, err := s.manifold.Start(s.goodContext)
    94  	c.Assert(err, jc.ErrorIsNil)
    95  	defer checkStop(c, w)
    96  
    97  	var out int
    98  	err = s.manifold.Output(w, &out)
    99  	c.Check(err, gc.ErrorMatches, `out should be \*bool; got .+`)
   100  }
   101  
   102  func (s *ManifoldSuite) TestOutputSuccessNotStateServer(c *gc.C) {
   103  	s.agent.conf.setStateServingInfo(false)
   104  	w, err := s.manifold.Start(s.goodContext)
   105  	c.Assert(err, jc.ErrorIsNil)
   106  	defer checkStop(c, w)
   107  
   108  	var out bool
   109  	err = s.manifold.Output(w, &out)
   110  	c.Check(err, jc.ErrorIsNil)
   111  	c.Check(out, jc.IsFalse)
   112  }
   113  
   114  func (s *ManifoldSuite) TestOutputSuccessStateServer(c *gc.C) {
   115  	s.agent.conf.setStateServingInfo(true)
   116  	w, err := s.manifold.Start(s.goodContext)
   117  	c.Assert(err, jc.ErrorIsNil)
   118  	defer checkStop(c, w)
   119  
   120  	var out bool
   121  	err = s.manifold.Output(w, &out)
   122  	c.Check(err, jc.ErrorIsNil)
   123  	c.Check(out, jc.IsTrue)
   124  }
   125  
   126  func (s *ManifoldSuite) TestBounceOnChange(c *gc.C) {
   127  	s.agent.conf.setStateServingInfo(false)
   128  	w, err := s.manifold.Start(s.goodContext)
   129  	c.Assert(err, jc.ErrorIsNil)
   130  	checkNotExiting(c, w)
   131  
   132  	checkOutput := func(expected bool) {
   133  		var out bool
   134  		err = s.manifold.Output(w, &out)
   135  		c.Assert(err, jc.ErrorIsNil)
   136  		c.Check(out, gc.Equals, expected)
   137  	}
   138  
   139  	// Not a state server yet, initial output should be False.
   140  	checkOutput(false)
   141  
   142  	// Changing the config without changing the state server status -
   143  	// worker should keep running and output should remain false.
   144  	s.agentConfigChanged.Set(0)
   145  	checkNotExiting(c, w)
   146  	checkOutput(false)
   147  
   148  	// Now change the config to include state serving info, worker
   149  	// should bounce.
   150  	s.agent.conf.setStateServingInfo(true)
   151  	s.agentConfigChanged.Set(0)
   152  	checkExitsWithError(c, w, dependency.ErrBounce)
   153  
   154  	// Restart the worker, the output should now be true.
   155  	w, err = s.manifold.Start(s.goodContext)
   156  	c.Assert(err, jc.ErrorIsNil)
   157  	checkNotExiting(c, w)
   158  	checkOutput(true)
   159  
   160  	// Changing the config again without changing the state serving
   161  	// info shouldn't cause the agent to exit.
   162  	s.agentConfigChanged.Set(0)
   163  	checkNotExiting(c, w)
   164  	checkOutput(true)
   165  
   166  	// Now remove the state serving info, the agent should bounce.
   167  	s.agent.conf.setStateServingInfo(false)
   168  	s.agentConfigChanged.Set(0)
   169  	checkExitsWithError(c, w, dependency.ErrBounce)
   170  }
   171  
   172  func (s *ManifoldSuite) TestClosedVoyeur(c *gc.C) {
   173  	w, err := s.manifold.Start(s.goodContext)
   174  	c.Assert(err, jc.ErrorIsNil)
   175  	checkNotExiting(c, w)
   176  
   177  	s.agentConfigChanged.Close()
   178  
   179  	c.Check(waitForExit(c, w), gc.ErrorMatches, "config changed value closed")
   180  }
   181  
   182  func checkStop(c *gc.C, w worker.Worker) {
   183  	err := worker.Stop(w)
   184  	c.Check(err, jc.ErrorIsNil)
   185  }
   186  
   187  func checkNotExiting(c *gc.C, w worker.Worker) {
   188  	exited := make(chan bool)
   189  	go func() {
   190  		w.Wait()
   191  		close(exited)
   192  	}()
   193  
   194  	select {
   195  	case <-exited:
   196  		c.Fatal("worker exited unexpectedly")
   197  	case <-time.After(coretesting.ShortWait):
   198  		// Worker didn't exit (good)
   199  	}
   200  }
   201  
   202  func checkExitsWithError(c *gc.C, w worker.Worker, expectedErr error) {
   203  	c.Check(waitForExit(c, w), gc.Equals, expectedErr)
   204  }
   205  
   206  func waitForExit(c *gc.C, w worker.Worker) error {
   207  	errCh := make(chan error)
   208  	go func() {
   209  		errCh <- w.Wait()
   210  	}()
   211  	select {
   212  	case err := <-errCh:
   213  		return err
   214  	case <-time.After(coretesting.LongWait):
   215  		c.Fatal("timed out waiting for worker to exit")
   216  	}
   217  	panic("can't get here")
   218  }
   219  
   220  type mockAgent struct {
   221  	coreagent.Agent
   222  	conf mockConfig
   223  }
   224  
   225  func (ma *mockAgent) CurrentConfig() coreagent.Config {
   226  	return &ma.conf
   227  }
   228  
   229  type mockConfig struct {
   230  	coreagent.ConfigSetter
   231  	tag         names.Tag
   232  	mu          sync.Mutex
   233  	ssInfoIsSet bool
   234  }
   235  
   236  func (mc *mockConfig) Tag() names.Tag {
   237  	return mc.tag
   238  }
   239  
   240  func (mc *mockConfig) setStateServingInfo(isSet bool) {
   241  	mc.mu.Lock()
   242  	defer mc.mu.Unlock()
   243  	mc.ssInfoIsSet = isSet
   244  }
   245  
   246  func (mc *mockConfig) StateServingInfo() (params.StateServingInfo, bool) {
   247  	mc.mu.Lock()
   248  	defer mc.mu.Unlock()
   249  	return params.StateServingInfo{}, mc.ssInfoIsSet
   250  }
   251  
   252  type dummyWorker struct {
   253  	worker.Worker
   254  }