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

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package modelcache
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/pubsub/v2"
     9  	"github.com/juju/worker/v3"
    10  	"github.com/juju/worker/v3/dependency"
    11  	"github.com/prometheus/client_golang/prometheus"
    12  
    13  	"github.com/juju/juju/core/cache"
    14  	"github.com/juju/juju/core/multiwatcher"
    15  	"github.com/juju/juju/worker/gate"
    16  	workerstate "github.com/juju/juju/worker/state"
    17  )
    18  
    19  // Logger describes the logging methods used in this package by the worker.
    20  type Logger interface {
    21  	IsTraceEnabled() bool
    22  	Tracef(string, ...interface{})
    23  	Errorf(string, ...interface{})
    24  	Criticalf(string, ...interface{})
    25  }
    26  
    27  // ManifoldConfig holds the information necessary to run a model cache worker in
    28  // a dependency.Engine.
    29  type ManifoldConfig struct {
    30  	StateName           string
    31  	CentralHubName      string
    32  	MultiwatcherName    string
    33  	InitializedGateName string
    34  	Logger              Logger
    35  
    36  	PrometheusRegisterer prometheus.Registerer
    37  
    38  	NewWorker func(Config) (worker.Worker, error)
    39  }
    40  
    41  // Validate validates the manifold configuration.
    42  func (config ManifoldConfig) Validate() error {
    43  	if config.StateName == "" {
    44  		return errors.NotValidf("missing StateName")
    45  	}
    46  	if config.CentralHubName == "" {
    47  		return errors.NotValidf("missing CentralHubName")
    48  	}
    49  	if config.MultiwatcherName == "" {
    50  		return errors.NotValidf("missing MultiwatcherName")
    51  	}
    52  	if config.InitializedGateName == "" {
    53  		return errors.NotValidf("missing InitializedGateName")
    54  	}
    55  	if config.Logger == nil {
    56  		return errors.NotValidf("missing Logger")
    57  	}
    58  	if config.PrometheusRegisterer == nil {
    59  		return errors.NotValidf("missing PrometheusRegisterer")
    60  	}
    61  	if config.NewWorker == nil {
    62  		return errors.NotValidf("missing NewWorker func")
    63  	}
    64  	return nil
    65  }
    66  
    67  // Manifold returns a dependency.Manifold that will run a model cache
    68  // worker. The manifold outputs a *cache.Controller, primarily for
    69  // the apiserver to depend on and use.
    70  func Manifold(config ManifoldConfig) dependency.Manifold {
    71  	return dependency.Manifold{
    72  		Inputs: []string{
    73  			config.StateName,
    74  			config.CentralHubName,
    75  			config.MultiwatcherName,
    76  			config.InitializedGateName,
    77  		},
    78  		Start:  config.start,
    79  		Output: ExtractCacheController,
    80  	}
    81  }
    82  
    83  // start is a method on ManifoldConfig because it's more readable than a closure.
    84  func (config ManifoldConfig) start(context dependency.Context) (worker.Worker, error) {
    85  	if err := config.Validate(); err != nil {
    86  		return nil, errors.Trace(err)
    87  	}
    88  	// Get the hub.
    89  	var hub *pubsub.StructuredHub
    90  	if err := context.Get(config.CentralHubName, &hub); err != nil {
    91  		config.Logger.Tracef("hub dependency not available")
    92  		return nil, err
    93  	}
    94  	var unlocker gate.Unlocker
    95  	if err := context.Get(config.InitializedGateName, &unlocker); err != nil {
    96  		return nil, errors.Trace(err)
    97  	}
    98  	var factory multiwatcher.Factory
    99  	if err := context.Get(config.MultiwatcherName, &factory); err != nil {
   100  		return nil, errors.Trace(err)
   101  	}
   102  
   103  	var stTracker workerstate.StateTracker
   104  	if err := context.Get(config.StateName, &stTracker); err != nil {
   105  		return nil, errors.Trace(err)
   106  	}
   107  
   108  	pool, err := stTracker.Use()
   109  	if err != nil {
   110  		return nil, errors.Trace(err)
   111  	}
   112  
   113  	w, err := config.NewWorker(Config{
   114  		StatePool:            pool,
   115  		Hub:                  hub,
   116  		InitializedGate:      unlocker,
   117  		Logger:               config.Logger,
   118  		WatcherFactory:       factory.WatchController,
   119  		PrometheusRegisterer: config.PrometheusRegisterer,
   120  		Cleanup:              func() { _ = stTracker.Done() },
   121  	}.WithDefaultRestartStrategy())
   122  	if err != nil {
   123  		_ = stTracker.Done()
   124  		return nil, errors.Trace(err)
   125  	}
   126  	return w, nil
   127  }
   128  
   129  // ExtractCacheController extracts a *cache.Controller from a *cacheWorker.
   130  func ExtractCacheController(in worker.Worker, out interface{}) error {
   131  	inWorker, _ := in.(*cacheWorker)
   132  	if inWorker == nil {
   133  		return errors.Errorf("in should be a %T; got %T", inWorker, in)
   134  	}
   135  
   136  	switch outPointer := out.(type) {
   137  	case **cache.Controller:
   138  		*outPointer = inWorker.controller
   139  	default:
   140  		return errors.Errorf("out should be *cache.Controller; got %T", out)
   141  	}
   142  	return nil
   143  }