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 }