github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/meterstatus/manifold_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package meterstatus_test
     5  
     6  import (
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/juju/names"
    11  	"github.com/juju/testing"
    12  	jc "github.com/juju/testing/checkers"
    13  	"github.com/juju/utils/fslock"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/agent"
    17  	"github.com/juju/juju/api/base"
    18  	msapi "github.com/juju/juju/api/meterstatus"
    19  	coretesting "github.com/juju/juju/testing"
    20  	"github.com/juju/juju/watcher"
    21  	"github.com/juju/juju/worker"
    22  	"github.com/juju/juju/worker/dependency"
    23  	dt "github.com/juju/juju/worker/dependency/testing"
    24  	"github.com/juju/juju/worker/meterstatus"
    25  	"github.com/juju/juju/worker/uniter/runner"
    26  )
    27  
    28  type ManifoldSuite struct {
    29  	coretesting.BaseSuite
    30  
    31  	stub *testing.Stub
    32  
    33  	dataDir string
    34  
    35  	manifoldConfig meterstatus.ManifoldConfig
    36  	manifold       dependency.Manifold
    37  	resources      dt.StubResources
    38  }
    39  
    40  var _ = gc.Suite(&ManifoldSuite{})
    41  
    42  func (s *ManifoldSuite) SetUpTest(c *gc.C) {
    43  	s.BaseSuite.SetUpTest(c)
    44  	s.stub = &testing.Stub{}
    45  
    46  	s.manifoldConfig = meterstatus.ManifoldConfig{
    47  		AgentName:               "agent-name",
    48  		APICallerName:           "apicaller-name",
    49  		MachineLockName:         "machine-lock-name",
    50  		NewHookRunner:           meterstatus.NewHookRunner,
    51  		NewMeterStatusAPIClient: msapi.NewClient,
    52  
    53  		NewConnectedStatusWorker: meterstatus.NewConnectedStatusWorker,
    54  		NewIsolatedStatusWorker:  meterstatus.NewIsolatedStatusWorker,
    55  	}
    56  	s.manifold = meterstatus.Manifold(s.manifoldConfig)
    57  	s.dataDir = c.MkDir()
    58  
    59  	locksDir := c.MkDir()
    60  	lock, err := fslock.NewLock(locksDir, "machine-lock", fslock.Defaults())
    61  	c.Assert(err, jc.ErrorIsNil)
    62  
    63  	s.resources = dt.StubResources{
    64  		"agent-name":        dt.StubResource{Output: &dummyAgent{dataDir: s.dataDir}},
    65  		"apicaller-name":    dt.StubResource{Output: &dummyAPICaller{}},
    66  		"machine-lock-name": dt.StubResource{Output: lock},
    67  	}
    68  }
    69  
    70  // TestInputs ensures the collect manifold has the expected defined inputs.
    71  func (s *ManifoldSuite) TestInputs(c *gc.C) {
    72  	c.Check(s.manifold.Inputs, jc.DeepEquals, []string{
    73  		"agent-name", "apicaller-name", "machine-lock-name",
    74  	})
    75  }
    76  
    77  // TestStartMissingDeps ensures that the manifold correctly handles a missing
    78  // resource dependency.
    79  func (s *ManifoldSuite) TestStartMissingDeps(c *gc.C) {
    80  	for _, missingDep := range []string{
    81  		"agent-name", "machine-lock-name",
    82  	} {
    83  		testResources := dt.StubResources{}
    84  		for k, v := range s.resources {
    85  			if k == missingDep {
    86  				testResources[k] = dt.StubResource{Error: dependency.ErrMissing}
    87  			} else {
    88  				testResources[k] = v
    89  			}
    90  		}
    91  		worker, err := s.manifold.Start(testResources.Context())
    92  		c.Check(worker, gc.IsNil)
    93  		c.Check(err, gc.Equals, dependency.ErrMissing)
    94  	}
    95  }
    96  
    97  type PatchedManifoldSuite struct {
    98  	coretesting.BaseSuite
    99  	msClient       *stubMeterStatusClient
   100  	manifoldConfig meterstatus.ManifoldConfig
   101  	stub           *testing.Stub
   102  	resources      dt.StubResources
   103  }
   104  
   105  func (s *PatchedManifoldSuite) SetUpTest(c *gc.C) {
   106  	s.BaseSuite.SetUpTest(c)
   107  
   108  	s.stub = &testing.Stub{}
   109  	s.msClient = &stubMeterStatusClient{stub: s.stub, changes: make(chan struct{})}
   110  	newMSClient := func(_ base.APICaller, _ names.UnitTag) msapi.MeterStatusClient {
   111  		return s.msClient
   112  	}
   113  	newHookRunner := func(_ names.UnitTag, _ *fslock.Lock, _ agent.Config) meterstatus.HookRunner {
   114  		return &stubRunner{stub: s.stub}
   115  	}
   116  
   117  	s.manifoldConfig = meterstatus.ManifoldConfig{
   118  		AgentName:               "agent-name",
   119  		APICallerName:           "apicaller-name",
   120  		MachineLockName:         "machine-lock-name",
   121  		NewHookRunner:           newHookRunner,
   122  		NewMeterStatusAPIClient: newMSClient,
   123  	}
   124  }
   125  
   126  // TestStatusWorkerStarts ensures that the manifold correctly sets up the connected worker.
   127  func (s *PatchedManifoldSuite) TestStatusWorkerStarts(c *gc.C) {
   128  	var called bool
   129  	s.manifoldConfig.NewConnectedStatusWorker = func(cfg meterstatus.ConnectedConfig) (worker.Worker, error) {
   130  		called = true
   131  		return meterstatus.NewConnectedStatusWorker(cfg)
   132  	}
   133  	manifold := meterstatus.Manifold(s.manifoldConfig)
   134  	worker, err := manifold.Start(s.resources.Context())
   135  	c.Assert(called, jc.IsTrue)
   136  	c.Assert(err, jc.ErrorIsNil)
   137  	c.Assert(worker, gc.NotNil)
   138  	worker.Kill()
   139  	err = worker.Wait()
   140  	c.Assert(err, jc.ErrorIsNil)
   141  	s.stub.CheckCallNames(c, "MeterStatus", "RunHook", "WatchMeterStatus")
   142  }
   143  
   144  // TestInactiveWorker ensures that the manifold correctly sets up the isolated worker.
   145  func (s *PatchedManifoldSuite) TestIsolatedWorker(c *gc.C) {
   146  	delete(s.resources, "apicaller-name")
   147  	var called bool
   148  	s.manifoldConfig.NewIsolatedStatusWorker = func(cfg meterstatus.IsolatedConfig) (worker.Worker, error) {
   149  		called = true
   150  		return meterstatus.NewIsolatedStatusWorker(cfg)
   151  	}
   152  	manifold := meterstatus.Manifold(s.manifoldConfig)
   153  	worker, err := manifold.Start(s.resources.Context())
   154  	c.Assert(called, jc.IsTrue)
   155  	c.Assert(err, jc.ErrorIsNil)
   156  	c.Assert(worker, gc.NotNil)
   157  	worker.Kill()
   158  	err = worker.Wait()
   159  	c.Assert(err, jc.ErrorIsNil)
   160  	s.stub.CheckCallNames(c, "MeterStatus", "RunHook", "WatchMeterStatus")
   161  }
   162  
   163  type dummyAgent struct {
   164  	agent.Agent
   165  	dataDir string
   166  }
   167  
   168  func (a dummyAgent) CurrentConfig() agent.Config {
   169  	return &dummyAgentConfig{dataDir: a.dataDir}
   170  }
   171  
   172  type dummyAgentConfig struct {
   173  	agent.Config
   174  	dataDir string
   175  }
   176  
   177  // Tag implements agent.AgentConfig.
   178  func (ac dummyAgentConfig) Tag() names.Tag {
   179  	return names.NewUnitTag("u/0")
   180  }
   181  
   182  // DataDir implements agent.AgentConfig.
   183  func (ac dummyAgentConfig) DataDir() string {
   184  	return ac.dataDir
   185  }
   186  
   187  type dummyAPICaller struct {
   188  	base.APICaller
   189  }
   190  
   191  func (dummyAPICaller) BestFacadeVersion(facade string) int {
   192  	return 42
   193  }
   194  
   195  type stubMeterStatusClient struct {
   196  	sync.RWMutex
   197  	stub    *testing.Stub
   198  	changes chan struct{}
   199  	code    string
   200  }
   201  
   202  func newStubMeterStatusClient(stub *testing.Stub) *stubMeterStatusClient {
   203  	changes := make(chan struct{})
   204  	return &stubMeterStatusClient{stub: stub, changes: changes}
   205  }
   206  
   207  func (s *stubMeterStatusClient) SignalStatus(codes ...string) {
   208  	if len(codes) == 0 {
   209  		codes = []string{s.code}
   210  	}
   211  	for _, code := range codes {
   212  		s.SetStatus(code)
   213  		select {
   214  		case s.changes <- struct{}{}:
   215  		case <-time.After(coretesting.LongWait):
   216  			panic("timed out signaling meter status change")
   217  		}
   218  	}
   219  }
   220  
   221  func (s *stubMeterStatusClient) SetStatus(code string) {
   222  	s.Lock()
   223  	defer s.Unlock()
   224  	s.code = code
   225  }
   226  
   227  func (s *stubMeterStatusClient) MeterStatus() (string, string, error) {
   228  	s.RLock()
   229  	defer s.RUnlock()
   230  	s.stub.MethodCall(s, "MeterStatus")
   231  	if s.code == "" {
   232  		return "GREEN", "", nil
   233  	} else {
   234  		return s.code, "", nil
   235  	}
   236  
   237  }
   238  
   239  func (s *stubMeterStatusClient) WatchMeterStatus() (watcher.NotifyWatcher, error) {
   240  	s.stub.MethodCall(s, "WatchMeterStatus")
   241  	return s, nil
   242  }
   243  
   244  func (s *stubMeterStatusClient) Changes() watcher.NotifyChannel {
   245  	return s.changes
   246  }
   247  
   248  func (s *stubMeterStatusClient) Kill() {
   249  }
   250  
   251  func (s *stubMeterStatusClient) Wait() error {
   252  	return nil
   253  }
   254  
   255  type stubRunner struct {
   256  	runner.Runner
   257  	stub *testing.Stub
   258  	ran  chan struct{}
   259  }
   260  
   261  func (r *stubRunner) RunHook(code, info string, abort <-chan struct{}) error {
   262  	r.stub.MethodCall(r, "RunHook", code, info)
   263  	if r.ran != nil {
   264  		select {
   265  		case r.ran <- struct{}{}:
   266  		case <-time.After(coretesting.LongWait):
   267  			panic("timed out signaling hook run")
   268  		}
   269  	}
   270  	return r.stub.NextErr()
   271  }