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

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package apiserver_test
     5  
     6  import (
     7  	"net/http"
     8  	"time"
     9  
    10  	"github.com/juju/clock/testclock"
    11  	"github.com/juju/errors"
    12  	"github.com/juju/pubsub"
    13  	"github.com/juju/testing"
    14  	jc "github.com/juju/testing/checkers"
    15  	"github.com/prometheus/client_golang/prometheus"
    16  	gc "gopkg.in/check.v1"
    17  	"gopkg.in/juju/names.v2"
    18  	"gopkg.in/juju/worker.v1"
    19  	"gopkg.in/juju/worker.v1/dependency"
    20  	dt "gopkg.in/juju/worker.v1/dependency/testing"
    21  	"gopkg.in/juju/worker.v1/workertest"
    22  
    23  	"github.com/juju/juju/agent"
    24  	coreapiserver "github.com/juju/juju/apiserver"
    25  	"github.com/juju/juju/apiserver/apiserverhttp"
    26  	"github.com/juju/juju/apiserver/httpcontext"
    27  	"github.com/juju/juju/apiserver/params"
    28  	"github.com/juju/juju/core/auditlog"
    29  	"github.com/juju/juju/core/cache"
    30  	"github.com/juju/juju/core/presence"
    31  	"github.com/juju/juju/state"
    32  	coretesting "github.com/juju/juju/testing"
    33  	"github.com/juju/juju/worker/apiserver"
    34  	"github.com/juju/juju/worker/gate"
    35  	"github.com/juju/juju/worker/lease"
    36  )
    37  
    38  type ManifoldSuite struct {
    39  	testing.IsolationSuite
    40  
    41  	manifold             dependency.Manifold
    42  	context              dependency.Context
    43  	agent                *mockAgent
    44  	authenticator        *mockAuthenticator
    45  	clock                *testclock.Clock
    46  	controller           *cache.Controller
    47  	mux                  *apiserverhttp.Mux
    48  	state                stubStateTracker
    49  	prometheusRegisterer stubPrometheusRegisterer
    50  	hub                  pubsub.StructuredHub
    51  	upgradeGate          stubGateWaiter
    52  	auditConfig          stubAuditConfig
    53  	leaseManager         *lease.Manager
    54  	metricsCollector     *coreapiserver.Collector
    55  
    56  	stub testing.Stub
    57  }
    58  
    59  var _ = gc.Suite(&ManifoldSuite{})
    60  
    61  func (s *ManifoldSuite) SetUpTest(c *gc.C) {
    62  	s.IsolationSuite.SetUpTest(c)
    63  
    64  	s.agent = &mockAgent{}
    65  	s.authenticator = &mockAuthenticator{}
    66  	s.clock = testclock.NewClock(time.Time{})
    67  	controller, err := cache.NewController(cache.ControllerConfig{
    68  		Changes: make(chan interface{}),
    69  	})
    70  	c.Assert(err, jc.ErrorIsNil)
    71  	s.controller = controller
    72  	s.mux = apiserverhttp.NewMux()
    73  	s.state = stubStateTracker{}
    74  	s.metricsCollector = coreapiserver.NewMetricsCollector()
    75  	s.upgradeGate = stubGateWaiter{}
    76  	s.auditConfig = stubAuditConfig{}
    77  	s.leaseManager = &lease.Manager{}
    78  	s.stub.ResetCalls()
    79  
    80  	s.context = s.newContext(nil)
    81  	s.manifold = apiserver.Manifold(apiserver.ManifoldConfig{
    82  		AgentName:                         "agent",
    83  		AuthenticatorName:                 "authenticator",
    84  		ClockName:                         "clock",
    85  		MuxName:                           "mux",
    86  		ModelCacheName:                    "modelcache",
    87  		RestoreStatusName:                 "restore-status",
    88  		StateName:                         "state",
    89  		UpgradeGateName:                   "upgrade",
    90  		AuditConfigUpdaterName:            "auditconfig-updater",
    91  		LeaseManagerName:                  "lease-manager",
    92  		RaftTransportName:                 "raft-transport",
    93  		PrometheusRegisterer:              &s.prometheusRegisterer,
    94  		RegisterIntrospectionHTTPHandlers: func(func(string, http.Handler)) {},
    95  		Hub:                               &s.hub,
    96  		Presence:                          presence.New(s.clock),
    97  		NewWorker:                         s.newWorker,
    98  		NewMetricsCollector:               s.newMetricsCollector,
    99  	})
   100  }
   101  
   102  func (s *ManifoldSuite) newContext(overlay map[string]interface{}) dependency.Context {
   103  	resources := map[string]interface{}{
   104  		"agent":               s.agent,
   105  		"authenticator":       s.authenticator,
   106  		"clock":               s.clock,
   107  		"mux":                 s.mux,
   108  		"modelcache":          s.controller,
   109  		"restore-status":      s.RestoreStatus,
   110  		"state":               &s.state,
   111  		"upgrade":             &s.upgradeGate,
   112  		"auditconfig-updater": s.auditConfig.get,
   113  		"lease-manager":       s.leaseManager,
   114  		"raft-transport":      nil,
   115  	}
   116  	for k, v := range overlay {
   117  		resources[k] = v
   118  	}
   119  	return dt.StubContext(nil, resources)
   120  }
   121  
   122  func (s *ManifoldSuite) RestoreStatus() state.RestoreStatus {
   123  	s.stub.MethodCall(s, "RestoreStatus")
   124  	return ""
   125  }
   126  
   127  func (s *ManifoldSuite) newWorker(config apiserver.Config) (worker.Worker, error) {
   128  	s.stub.MethodCall(s, "NewWorker", config)
   129  	if err := s.stub.NextErr(); err != nil {
   130  		return nil, err
   131  	}
   132  	return worker.NewRunner(worker.RunnerParams{}), nil
   133  }
   134  
   135  func (s *ManifoldSuite) newMetricsCollector() *coreapiserver.Collector {
   136  	return s.metricsCollector
   137  }
   138  
   139  var expectedInputs = []string{
   140  	"agent", "authenticator", "clock", "modelcache", "mux", "restore-status", "state", "upgrade", "auditconfig-updater", "lease-manager", "raft-transport",
   141  }
   142  
   143  func (s *ManifoldSuite) TestInputs(c *gc.C) {
   144  	c.Assert(s.manifold.Inputs, jc.SameContents, expectedInputs)
   145  }
   146  
   147  func (s *ManifoldSuite) TestMissingInputs(c *gc.C) {
   148  	for _, input := range expectedInputs {
   149  		context := s.newContext(map[string]interface{}{
   150  			input: dependency.ErrMissing,
   151  		})
   152  		_, err := s.manifold.Start(context)
   153  		c.Assert(errors.Cause(err), gc.Equals, dependency.ErrMissing)
   154  
   155  		// The state tracker must have either no calls, or a Use and a Done.
   156  		if len(s.state.Calls()) > 0 {
   157  			s.state.CheckCallNames(c, "Use", "Done")
   158  		}
   159  		s.state.ResetCalls()
   160  	}
   161  }
   162  
   163  func (s *ManifoldSuite) TestStart(c *gc.C) {
   164  	w := s.startWorkerClean(c)
   165  	workertest.CleanKill(c, w)
   166  
   167  	s.stub.CheckCallNames(c, "NewWorker")
   168  	args := s.stub.Calls()[0].Args
   169  	c.Assert(args, gc.HasLen, 1)
   170  	c.Assert(args[0], gc.FitsTypeOf, apiserver.Config{})
   171  	config := args[0].(apiserver.Config)
   172  
   173  	c.Assert(config.GetAuditConfig, gc.NotNil)
   174  	c.Assert(config.GetAuditConfig(), gc.DeepEquals, s.auditConfig.config)
   175  	config.GetAuditConfig = nil
   176  
   177  	c.Assert(config.UpgradeComplete, gc.NotNil)
   178  	config.UpgradeComplete()
   179  	config.UpgradeComplete = nil
   180  	s.upgradeGate.CheckCallNames(c, "IsUnlocked")
   181  
   182  	c.Assert(config.RestoreStatus, gc.NotNil)
   183  	config.RestoreStatus()
   184  	config.RestoreStatus = nil
   185  	s.stub.CheckCallNames(c, "NewWorker", "RestoreStatus")
   186  
   187  	c.Assert(config.RegisterIntrospectionHTTPHandlers, gc.NotNil)
   188  	config.RegisterIntrospectionHTTPHandlers = nil
   189  
   190  	c.Assert(config.Presence, gc.NotNil)
   191  	config.Presence = nil
   192  
   193  	// NewServer is hard-coded by the manifold to an internal shim.
   194  	c.Assert(config.NewServer, gc.NotNil)
   195  	config.NewServer = nil
   196  
   197  	c.Assert(config, jc.DeepEquals, apiserver.Config{
   198  		AgentConfig:      &s.agent.conf,
   199  		Authenticator:    s.authenticator,
   200  		Clock:            s.clock,
   201  		Controller:       s.controller,
   202  		Mux:              s.mux,
   203  		StatePool:        &s.state.pool,
   204  		LeaseManager:     s.leaseManager,
   205  		MetricsCollector: s.metricsCollector,
   206  		Hub:              &s.hub,
   207  	})
   208  }
   209  
   210  func (s *ManifoldSuite) TestStopWorkerClosesState(c *gc.C) {
   211  	w := s.startWorkerClean(c)
   212  	defer workertest.CleanKill(c, w)
   213  
   214  	s.state.CheckCallNames(c, "Use")
   215  
   216  	workertest.CleanKill(c, w)
   217  	s.state.CheckCallNames(c, "Use", "Done")
   218  }
   219  
   220  func (s *ManifoldSuite) startWorkerClean(c *gc.C) worker.Worker {
   221  	w, err := s.manifold.Start(s.context)
   222  	c.Assert(err, jc.ErrorIsNil)
   223  	workertest.CheckAlive(c, w)
   224  	return w
   225  }
   226  
   227  func (s *ManifoldSuite) TestAddsAndRemovesMuxClients(c *gc.C) {
   228  	waitFinished := make(chan struct{})
   229  	w := s.startWorkerClean(c)
   230  	go func() {
   231  		defer close(waitFinished)
   232  		s.mux.Wait()
   233  	}()
   234  
   235  	select {
   236  	case <-waitFinished:
   237  		c.Fatalf("didn't add clients to the mux")
   238  	case <-time.After(coretesting.ShortWait):
   239  	}
   240  
   241  	workertest.CleanKill(c, w)
   242  
   243  	select {
   244  	case <-waitFinished:
   245  	case <-time.After(coretesting.LongWait):
   246  		c.Fatalf("didn't tell the mux we were finished")
   247  	}
   248  }
   249  
   250  type mockAgent struct {
   251  	agent.Agent
   252  	conf mockAgentConfig
   253  }
   254  
   255  func (ma *mockAgent) CurrentConfig() agent.Config {
   256  	return &ma.conf
   257  }
   258  
   259  type mockAgentConfig struct {
   260  	agent.Config
   261  	dataDir string
   262  	logDir  string
   263  	info    *params.StateServingInfo
   264  	values  map[string]string
   265  }
   266  
   267  func (c *mockAgentConfig) Tag() names.Tag {
   268  	return names.NewMachineTag("123")
   269  }
   270  
   271  func (c *mockAgentConfig) LogDir() string {
   272  	return c.logDir
   273  }
   274  
   275  func (c *mockAgentConfig) DataDir() string {
   276  	return c.dataDir
   277  }
   278  
   279  func (c *mockAgentConfig) StateServingInfo() (params.StateServingInfo, bool) {
   280  	if c.info != nil {
   281  		return *c.info, true
   282  	}
   283  	return params.StateServingInfo{}, false
   284  }
   285  
   286  func (c *mockAgentConfig) Value(key string) string {
   287  	return c.values[key]
   288  }
   289  
   290  type stubStateTracker struct {
   291  	testing.Stub
   292  	pool state.StatePool
   293  }
   294  
   295  func (s *stubStateTracker) Use() (*state.StatePool, error) {
   296  	s.MethodCall(s, "Use")
   297  	return &s.pool, s.NextErr()
   298  }
   299  
   300  func (s *stubStateTracker) Done() error {
   301  	s.MethodCall(s, "Done")
   302  	return s.NextErr()
   303  }
   304  
   305  func (s *stubStateTracker) Report() map[string]interface{} {
   306  	s.MethodCall(s, "Report")
   307  	return nil
   308  }
   309  
   310  type stubPrometheusRegisterer struct {
   311  	testing.Stub
   312  }
   313  
   314  func (s *stubPrometheusRegisterer) MustRegister(...prometheus.Collector) {
   315  	panic("should not be called")
   316  }
   317  
   318  func (s *stubPrometheusRegisterer) Register(c prometheus.Collector) error {
   319  	s.MethodCall(s, "Register", c)
   320  	return s.NextErr()
   321  }
   322  
   323  func (s *stubPrometheusRegisterer) Unregister(c prometheus.Collector) bool {
   324  	s.MethodCall(s, "Unregister", c)
   325  	return false
   326  }
   327  
   328  type stubGateWaiter struct {
   329  	testing.Stub
   330  	gate.Waiter
   331  }
   332  
   333  func (w *stubGateWaiter) IsUnlocked() bool {
   334  	w.MethodCall(w, "IsUnlocked")
   335  	return true
   336  }
   337  
   338  type stubAuditConfig struct {
   339  	testing.Stub
   340  	config auditlog.Config
   341  }
   342  
   343  func (c *stubAuditConfig) get() auditlog.Config {
   344  	c.MethodCall(c, "get")
   345  	return c.config
   346  }
   347  
   348  type mockAuthenticator struct {
   349  	httpcontext.LocalMacaroonAuthenticator
   350  }