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 }