github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/apiserver/manifold.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package apiserver
     5  
     6  import (
     7  	"net/http"
     8  	"strings"
     9  
    10  	"github.com/juju/clock"
    11  	"github.com/juju/cmd/v3"
    12  	"github.com/juju/errors"
    13  	"github.com/juju/pubsub/v2"
    14  	"github.com/juju/worker/v3"
    15  	"github.com/juju/worker/v3/dependency"
    16  	"github.com/prometheus/client_golang/prometheus"
    17  
    18  	"github.com/juju/juju/agent"
    19  	"github.com/juju/juju/apiserver"
    20  	"github.com/juju/juju/apiserver/apiserverhttp"
    21  	"github.com/juju/juju/apiserver/authentication/macaroon"
    22  	"github.com/juju/juju/cmd/juju/commands"
    23  	"github.com/juju/juju/core/auditlog"
    24  	"github.com/juju/juju/core/cache"
    25  	coredatabase "github.com/juju/juju/core/database"
    26  	"github.com/juju/juju/core/lease"
    27  	"github.com/juju/juju/core/multiwatcher"
    28  	"github.com/juju/juju/core/presence"
    29  	"github.com/juju/juju/jujuclient"
    30  	"github.com/juju/juju/worker/common"
    31  	"github.com/juju/juju/worker/gate"
    32  	workerstate "github.com/juju/juju/worker/state"
    33  	"github.com/juju/juju/worker/syslogger"
    34  )
    35  
    36  // ManifoldConfig holds the information necessary to run an apiserver
    37  // worker in a dependency.Engine.
    38  type ManifoldConfig struct {
    39  	AgentName              string
    40  	AuthenticatorName      string
    41  	ClockName              string
    42  	ModelCacheName         string
    43  	MultiwatcherName       string
    44  	MuxName                string
    45  	StateName              string
    46  	UpgradeGateName        string
    47  	AuditConfigUpdaterName string
    48  	LeaseManagerName       string
    49  	SyslogName             string
    50  	CharmhubHTTPClientName string
    51  	DBAccessorName         string
    52  
    53  	PrometheusRegisterer              prometheus.Registerer
    54  	RegisterIntrospectionHTTPHandlers func(func(path string, _ http.Handler))
    55  	Hub                               *pubsub.StructuredHub
    56  	Presence                          presence.Recorder
    57  
    58  	NewWorker           func(Config) (worker.Worker, error)
    59  	NewMetricsCollector func() *apiserver.Collector
    60  }
    61  
    62  // Validate validates the manifold configuration.
    63  func (config ManifoldConfig) Validate() error {
    64  	if config.AgentName == "" {
    65  		return errors.NotValidf("empty AgentName")
    66  	}
    67  	if config.AuthenticatorName == "" {
    68  		return errors.NotValidf("empty AuthenticatorName")
    69  	}
    70  	if config.ClockName == "" {
    71  		return errors.NotValidf("empty ClockName")
    72  	}
    73  	if config.ModelCacheName == "" {
    74  		return errors.NotValidf("empty ModelCacheName")
    75  	}
    76  	if config.MultiwatcherName == "" {
    77  		return errors.NotValidf("empty MultiwatcherName")
    78  	}
    79  	if config.MuxName == "" {
    80  		return errors.NotValidf("empty MuxName")
    81  	}
    82  	if config.StateName == "" {
    83  		return errors.NotValidf("empty StateName")
    84  	}
    85  	if config.UpgradeGateName == "" {
    86  		return errors.NotValidf("empty UpgradeGateName")
    87  	}
    88  	if config.AuditConfigUpdaterName == "" {
    89  		return errors.NotValidf("empty AuditConfigUpdaterName")
    90  	}
    91  	if config.LeaseManagerName == "" {
    92  		return errors.NotValidf("empty LeaseManagerName")
    93  	}
    94  	if config.PrometheusRegisterer == nil {
    95  		return errors.NotValidf("nil PrometheusRegisterer")
    96  	}
    97  	if config.RegisterIntrospectionHTTPHandlers == nil {
    98  		return errors.NotValidf("nil RegisterIntrospectionHTTPHandlers")
    99  	}
   100  	if config.SyslogName == "" {
   101  		return errors.NotValidf("empty SyslogName")
   102  	}
   103  	if config.CharmhubHTTPClientName == "" {
   104  		return errors.NotValidf("empty CharmhubHTTPClientName")
   105  	}
   106  	if config.DBAccessorName == "" {
   107  		return errors.NotValidf("empty DBAccessorName")
   108  	}
   109  	if config.Hub == nil {
   110  		return errors.NotValidf("nil Hub")
   111  	}
   112  	if config.Presence == nil {
   113  		return errors.NotValidf("nil Presence")
   114  	}
   115  	if config.NewWorker == nil {
   116  		return errors.NotValidf("nil NewWorker")
   117  	}
   118  	if config.NewMetricsCollector == nil {
   119  		return errors.NotValidf("nil NewMetricsCollector")
   120  	}
   121  	return nil
   122  }
   123  
   124  // Manifold returns a dependency.Manifold that will run an apiserver
   125  // worker. The manifold outputs an *apiserverhttp.Mux, for other workers
   126  // to register handlers against.
   127  func Manifold(config ManifoldConfig) dependency.Manifold {
   128  	return dependency.Manifold{
   129  		Inputs: []string{
   130  			config.AgentName,
   131  			config.AuthenticatorName,
   132  			config.ClockName,
   133  			config.ModelCacheName,
   134  			config.MultiwatcherName,
   135  			config.MuxName,
   136  			config.StateName,
   137  			config.UpgradeGateName,
   138  			config.AuditConfigUpdaterName,
   139  			config.LeaseManagerName,
   140  			config.SyslogName,
   141  			config.CharmhubHTTPClientName,
   142  			config.DBAccessorName,
   143  		},
   144  		Start: config.start,
   145  	}
   146  }
   147  
   148  // start is a method on ManifoldConfig because it's more readable than a closure.
   149  func (config ManifoldConfig) start(context dependency.Context) (worker.Worker, error) {
   150  	if err := config.Validate(); err != nil {
   151  		return nil, errors.Trace(err)
   152  	}
   153  
   154  	var agent agent.Agent
   155  	if err := context.Get(config.AgentName, &agent); err != nil {
   156  		return nil, errors.Trace(err)
   157  	}
   158  
   159  	var clock clock.Clock
   160  	if err := context.Get(config.ClockName, &clock); err != nil {
   161  		return nil, errors.Trace(err)
   162  	}
   163  
   164  	var mux *apiserverhttp.Mux
   165  	if err := context.Get(config.MuxName, &mux); err != nil {
   166  		return nil, errors.Trace(err)
   167  	}
   168  
   169  	var macaroonAuthenticator macaroon.LocalMacaroonAuthenticator
   170  	if err := context.Get(config.AuthenticatorName, &macaroonAuthenticator); err != nil {
   171  		return nil, errors.Trace(err)
   172  	}
   173  
   174  	var stTracker workerstate.StateTracker
   175  	if err := context.Get(config.StateName, &stTracker); err != nil {
   176  		return nil, errors.Trace(err)
   177  	}
   178  
   179  	var factory multiwatcher.Factory
   180  	if err := context.Get(config.MultiwatcherName, &factory); err != nil {
   181  		return nil, errors.Trace(err)
   182  	}
   183  
   184  	var controller *cache.Controller
   185  	if err := context.Get(config.ModelCacheName, &controller); err != nil {
   186  		return nil, errors.Trace(err)
   187  	}
   188  
   189  	var upgradeLock gate.Waiter
   190  	if err := context.Get(config.UpgradeGateName, &upgradeLock); err != nil {
   191  		return nil, errors.Trace(err)
   192  	}
   193  
   194  	var getAuditConfig func() auditlog.Config
   195  	if err := context.Get(config.AuditConfigUpdaterName, &getAuditConfig); err != nil {
   196  		return nil, errors.Trace(err)
   197  	}
   198  
   199  	var leaseManager lease.Manager
   200  	if err := context.Get(config.LeaseManagerName, &leaseManager); err != nil {
   201  		return nil, errors.Trace(err)
   202  	}
   203  
   204  	var sysLogger syslogger.SysLogger
   205  	if err := context.Get(config.SyslogName, &sysLogger); err != nil {
   206  		return nil, errors.Trace(err)
   207  	}
   208  
   209  	var charmhubHTTPClient HTTPClient
   210  	if err := context.Get(config.CharmhubHTTPClientName, &charmhubHTTPClient); err != nil {
   211  		return nil, errors.Trace(err)
   212  	}
   213  
   214  	var dbGetter coredatabase.DBGetter
   215  	if err := context.Get(config.DBAccessorName, &dbGetter); err != nil {
   216  		return nil, errors.Trace(err)
   217  	}
   218  
   219  	// Register the metrics collector against the prometheus register.
   220  	metricsCollector := config.NewMetricsCollector()
   221  	if err := config.PrometheusRegisterer.Register(metricsCollector); err != nil {
   222  		return nil, errors.Trace(err)
   223  	}
   224  
   225  	execEmbeddedCommand := func(ctx *cmd.Context, store jujuclient.ClientStore, whitelist []string, cmdPlusARgs string) int {
   226  		jujuCmd := commands.NewJujuCommandWithStore(ctx, store, nil, "", `Type "help" to see a list of commands`, whitelist, true)
   227  		return cmd.Main(jujuCmd, ctx, strings.Split(cmdPlusARgs, " "))
   228  	}
   229  
   230  	// Get the state pool after grabbing dependencies so we don't need
   231  	// to remember to call Done on it if they're not running yet.
   232  	statePool, err := stTracker.Use()
   233  	if err != nil {
   234  		return nil, errors.Trace(err)
   235  	}
   236  
   237  	w, err := config.NewWorker(Config{
   238  		AgentConfig:                       agent.CurrentConfig(),
   239  		Clock:                             clock,
   240  		Mux:                               mux,
   241  		StatePool:                         statePool,
   242  		Controller:                        controller,
   243  		MultiwatcherFactory:               factory,
   244  		LeaseManager:                      leaseManager,
   245  		RegisterIntrospectionHTTPHandlers: config.RegisterIntrospectionHTTPHandlers,
   246  		UpgradeComplete:                   upgradeLock.IsUnlocked,
   247  		Hub:                               config.Hub,
   248  		Presence:                          config.Presence,
   249  		LocalMacaroonAuthenticator:        macaroonAuthenticator,
   250  		GetAuditConfig:                    getAuditConfig,
   251  		NewServer:                         newServerShim,
   252  		MetricsCollector:                  metricsCollector,
   253  		EmbeddedCommand:                   execEmbeddedCommand,
   254  		SysLogger:                         sysLogger,
   255  		CharmhubHTTPClient:                charmhubHTTPClient,
   256  		DBGetter:                          dbGetter,
   257  	})
   258  	if err != nil {
   259  		// Ensure we clean up the resources we've registered with. This includes
   260  		// the state pool and the metrics collector.
   261  		_ = stTracker.Done()
   262  		_ = config.PrometheusRegisterer.Unregister(metricsCollector)
   263  
   264  		return nil, errors.Trace(err)
   265  	}
   266  	mux.AddClient()
   267  	return common.NewCleanupWorker(w, func() {
   268  		mux.ClientDone()
   269  
   270  		// Ensure we clean up the resources we've registered with. This includes
   271  		// the state pool and the metrics collector.
   272  		_ = stTracker.Done()
   273  		_ = config.PrometheusRegisterer.Unregister(metricsCollector)
   274  	}), nil
   275  }