github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/lease/manifold/manifold_test.go (about)

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package manifold_test
     5  
     6  import (
     7  	"io"
     8  	"time"
     9  
    10  	"github.com/juju/clock/testclock"
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	"github.com/juju/names/v5"
    14  	"github.com/juju/testing"
    15  	jc "github.com/juju/testing/checkers"
    16  	"github.com/juju/worker/v3"
    17  	"github.com/juju/worker/v3/dependency"
    18  	dt "github.com/juju/worker/v3/dependency/testing"
    19  	"github.com/prometheus/client_golang/prometheus"
    20  	"go.uber.org/mock/gomock"
    21  	gc "gopkg.in/check.v1"
    22  
    23  	"github.com/juju/juju/agent"
    24  	"github.com/juju/juju/api"
    25  	coredatabase "github.com/juju/juju/core/database"
    26  	corelease "github.com/juju/juju/core/lease"
    27  	statetesting "github.com/juju/juju/state/testing"
    28  	"github.com/juju/juju/worker/lease"
    29  	leasemanager "github.com/juju/juju/worker/lease/manifold"
    30  )
    31  
    32  type manifoldSuite struct {
    33  	statetesting.StateSuite
    34  
    35  	context  dependency.Context
    36  	manifold dependency.Manifold
    37  
    38  	agent      *mockAgent
    39  	clock      *testclock.Clock
    40  	dbAccessor stubDBGetter
    41  
    42  	logger  loggo.Logger
    43  	metrics prometheus.Registerer
    44  
    45  	worker    worker.Worker
    46  	trackedDB coredatabase.TrackedDB
    47  	store     *lease.Store
    48  
    49  	stub testing.Stub
    50  }
    51  
    52  var _ = gc.Suite(&manifoldSuite{})
    53  
    54  func (s *manifoldSuite) SetUpTest(c *gc.C) {
    55  	ctrl := gomock.NewController(c)
    56  	defer ctrl.Finish()
    57  
    58  	s.StateSuite.SetUpTest(c)
    59  
    60  	s.stub.ResetCalls()
    61  
    62  	s.trackedDB = NewMockTrackedDB(ctrl)
    63  
    64  	s.agent = &mockAgent{conf: mockAgentConfig{
    65  		uuid:    "controller-uuid",
    66  		apiInfo: &api.Info{},
    67  	}}
    68  	s.clock = testclock.NewClock(time.Now())
    69  	s.dbAccessor = stubDBGetter{s.trackedDB}
    70  
    71  	s.logger = loggo.GetLogger("lease.manifold_test")
    72  	registerer := struct{ prometheus.Registerer }{}
    73  	s.metrics = &registerer
    74  
    75  	s.worker = &mockWorker{}
    76  	s.store = &lease.Store{}
    77  
    78  	s.context = s.newContext(nil)
    79  	s.manifold = leasemanager.Manifold(leasemanager.ManifoldConfig{
    80  		AgentName:            "agent",
    81  		ClockName:            "clock",
    82  		DBAccessorName:       "db-accessor",
    83  		Logger:               &s.logger,
    84  		PrometheusRegisterer: s.metrics,
    85  		NewWorker:            s.newWorker,
    86  		NewStore:             s.newStore,
    87  	})
    88  }
    89  
    90  func (s *manifoldSuite) newContext(overlay map[string]interface{}) dependency.Context {
    91  	resources := map[string]interface{}{
    92  		"agent":       s.agent,
    93  		"clock":       s.clock,
    94  		"db-accessor": s.dbAccessor,
    95  	}
    96  	for k, v := range overlay {
    97  		resources[k] = v
    98  	}
    99  	return dt.StubContext(nil, resources)
   100  }
   101  
   102  func (s *manifoldSuite) newWorker(config lease.ManagerConfig) (worker.Worker, error) {
   103  	s.stub.MethodCall(s, "NewWorker", config)
   104  	if err := s.stub.NextErr(); err != nil {
   105  		return nil, err
   106  	}
   107  	return s.worker, nil
   108  }
   109  
   110  func (s *manifoldSuite) newStore(config lease.StoreConfig) *lease.Store {
   111  	s.stub.MethodCall(s, "NewStore", config)
   112  	return s.store
   113  }
   114  
   115  var expectedInputs = []string{
   116  	"agent", "clock", "db-accessor",
   117  }
   118  
   119  func (s *manifoldSuite) TestInputs(c *gc.C) {
   120  	c.Assert(s.manifold.Inputs, jc.SameContents, expectedInputs)
   121  }
   122  
   123  func (s *manifoldSuite) TestMissingInputs(c *gc.C) {
   124  	for _, input := range expectedInputs {
   125  		ctx := s.newContext(map[string]interface{}{
   126  			input: dependency.ErrMissing,
   127  		})
   128  		_, err := s.manifold.Start(ctx)
   129  		c.Assert(errors.Cause(err), gc.Equals, dependency.ErrMissing)
   130  	}
   131  }
   132  
   133  func (s *manifoldSuite) TestStart(c *gc.C) {
   134  	_, err := s.manifold.Start(s.context)
   135  	c.Assert(err, jc.ErrorIsNil)
   136  
   137  	s.stub.CheckCallNames(c, "NewStore", "NewWorker")
   138  
   139  	args := s.stub.Calls()[0].Args
   140  	c.Assert(args, gc.HasLen, 1)
   141  	c.Assert(args[0], gc.FitsTypeOf, lease.StoreConfig{})
   142  
   143  	storeConfig := args[0].(lease.StoreConfig)
   144  
   145  	c.Assert(storeConfig, gc.DeepEquals, lease.StoreConfig{
   146  		TrackedDB: s.trackedDB,
   147  		Logger:    &s.logger,
   148  	})
   149  
   150  	args = s.stub.Calls()[1].Args
   151  	c.Assert(args, gc.HasLen, 1)
   152  	c.Assert(args[0], gc.FitsTypeOf, lease.ManagerConfig{})
   153  	config := args[0].(lease.ManagerConfig)
   154  
   155  	secretary, err := config.Secretary(corelease.SingularControllerNamespace)
   156  	c.Assert(err, jc.ErrorIsNil)
   157  	// Check that this secretary knows the controller uuid.
   158  	err = secretary.CheckLease(corelease.Key{Lease: "controller-uuid"})
   159  	c.Assert(err, jc.ErrorIsNil)
   160  	config.Secretary = nil
   161  
   162  	c.Assert(config, jc.DeepEquals, lease.ManagerConfig{
   163  		Store:                s.store,
   164  		Clock:                s.clock,
   165  		Logger:               &s.logger,
   166  		MaxSleep:             time.Minute,
   167  		EntityUUID:           "controller-uuid",
   168  		PrometheusRegisterer: s.metrics,
   169  	})
   170  }
   171  
   172  func (s *manifoldSuite) TestOutput(c *gc.C) {
   173  	s.worker = &lease.Manager{}
   174  	w, err := s.manifold.Start(s.context)
   175  	c.Assert(err, jc.ErrorIsNil)
   176  
   177  	var manager corelease.Manager
   178  	err = s.manifold.Output(w, &manager)
   179  	c.Assert(err, jc.ErrorIsNil)
   180  	c.Assert(manager, gc.Equals, s.worker)
   181  
   182  	var other io.Writer
   183  	err = s.manifold.Output(w, &other)
   184  	c.Assert(err, gc.ErrorMatches, `expected output of type \*core/lease.Manager, got \*io.Writer`)
   185  }
   186  
   187  type mockAgent struct {
   188  	agent.Agent
   189  	conf mockAgentConfig
   190  }
   191  
   192  func (ma *mockAgent) CurrentConfig() agent.Config {
   193  	return &ma.conf
   194  }
   195  
   196  type mockAgentConfig struct {
   197  	agent.Config
   198  	uuid    string
   199  	apiInfo *api.Info
   200  }
   201  
   202  func (c *mockAgentConfig) Controller() names.ControllerTag {
   203  	return names.NewControllerTag(c.uuid)
   204  }
   205  
   206  func (c *mockAgentConfig) APIInfo() (*api.Info, bool) {
   207  	return c.apiInfo, true
   208  }
   209  
   210  type mockWorker struct{}
   211  
   212  func (*mockWorker) Kill() {}
   213  func (*mockWorker) Wait() error {
   214  	return nil
   215  }
   216  
   217  type stubDBGetter struct {
   218  	trackedDB coredatabase.TrackedDB
   219  }
   220  
   221  func (s stubDBGetter) GetDB(name string) (coredatabase.TrackedDB, error) {
   222  	if name != "controller" {
   223  		return nil, errors.Errorf(`expected a request for "controller" DB; got %q`, name)
   224  	}
   225  	return s.trackedDB, nil
   226  }