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