github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/dbaccessor/manifold.go (about) 1 // Copyright 2021 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package dbaccessor 5 6 import ( 7 "context" 8 9 "github.com/juju/clock" 10 "github.com/juju/errors" 11 "github.com/juju/loggo" 12 "github.com/juju/worker/v3" 13 "github.com/juju/worker/v3/dependency" 14 "github.com/prometheus/client_golang/prometheus" 15 16 "github.com/juju/juju/agent" 17 coredatabase "github.com/juju/juju/core/database" 18 "github.com/juju/juju/database" 19 "github.com/juju/juju/database/app" 20 "github.com/juju/juju/worker/common" 21 ) 22 23 // Logger represents the logging methods called. 24 type Logger interface { 25 Errorf(message string, args ...interface{}) 26 Warningf(message string, args ...interface{}) 27 Infof(message string, args ...interface{}) 28 Debugf(message string, args ...interface{}) 29 Tracef(message string, args ...interface{}) 30 31 // Logf is used to proxy Dqlite logs via this logger. 32 Logf(level loggo.Level, msg string, args ...interface{}) 33 34 IsTraceEnabled() bool 35 } 36 37 // Hub defines the methods of the API server central hub 38 // that the DB accessor requires. 39 type Hub interface { 40 Subscribe(topic string, handler interface{}) (func(), error) 41 Publish(topic string, data interface{}) (func(), error) 42 } 43 44 // ManifoldConfig contains: 45 // - The names of other manifolds on which the DB accessor depends. 46 // - Other dependencies from ManifoldsConfig required by the worker. 47 type ManifoldConfig struct { 48 AgentName string 49 QueryLoggerName string 50 Clock clock.Clock 51 Hub Hub 52 Logger Logger 53 LogDir string 54 PrometheusRegisterer prometheus.Registerer 55 NewApp func(string, ...app.Option) (DBApp, error) 56 NewDBWorker func(context.Context, DBApp, string, ...TrackedDBWorkerOption) (TrackedDB, error) 57 NewNodeManager func(agent.Config, Logger, coredatabase.SlowQueryLogger) NodeManager 58 NewMetricsCollector func() *Collector 59 } 60 61 func (cfg ManifoldConfig) Validate() error { 62 if cfg.AgentName == "" { 63 return errors.NotValidf("empty AgentName") 64 } 65 if cfg.QueryLoggerName == "" { 66 return errors.NotValidf("empty QueryLoggerName") 67 } 68 if cfg.Clock == nil { 69 return errors.NotValidf("nil Clock") 70 } 71 if cfg.Hub == nil { 72 return errors.NotValidf("nil Hub") 73 } 74 if cfg.Logger == nil { 75 return errors.NotValidf("nil Logger") 76 } 77 if cfg.LogDir == "" { 78 return errors.NotValidf("empty LogDir") 79 } 80 if cfg.PrometheusRegisterer == nil { 81 return errors.NotValidf("nil PrometheusRegisterer") 82 } 83 if cfg.NewApp == nil { 84 return errors.NotValidf("nil NewApp") 85 } 86 if cfg.NewDBWorker == nil { 87 return errors.NotValidf("nil NewDBWorker") 88 } 89 if cfg.NewNodeManager == nil { 90 return errors.NotValidf("nil NewNodeManager") 91 } 92 if cfg.NewMetricsCollector == nil { 93 return errors.NotValidf("nil NewMetricsCollector") 94 } 95 return nil 96 } 97 98 // Manifold returns a dependency manifold that runs the dbaccessor 99 // worker, using the resource names defined in the supplied config. 100 func Manifold(config ManifoldConfig) dependency.Manifold { 101 return dependency.Manifold{ 102 Inputs: []string{ 103 config.AgentName, 104 config.QueryLoggerName, 105 }, 106 Output: dbAccessorOutput, 107 Start: func(context dependency.Context) (worker.Worker, error) { 108 if err := config.Validate(); err != nil { 109 return nil, errors.Trace(err) 110 } 111 112 var agent agent.Agent 113 if err := context.Get(config.AgentName, &agent); err != nil { 114 return nil, err 115 } 116 agentConfig := agent.CurrentConfig() 117 118 // Register the metrics collector against the prometheus register. 119 metricsCollector := config.NewMetricsCollector() 120 if err := config.PrometheusRegisterer.Register(metricsCollector); err != nil { 121 return nil, errors.Trace(err) 122 } 123 124 var slowQueryLogger coredatabase.SlowQueryLogger 125 if err := context.Get(config.QueryLoggerName, &slowQueryLogger); err != nil { 126 config.PrometheusRegisterer.Unregister(metricsCollector) 127 return nil, err 128 } 129 130 cfg := WorkerConfig{ 131 NodeManager: config.NewNodeManager(agentConfig, config.Logger, slowQueryLogger), 132 Clock: config.Clock, 133 Hub: config.Hub, 134 ControllerID: agentConfig.Tag().Id(), 135 MetricsCollector: metricsCollector, 136 Logger: config.Logger, 137 NewApp: config.NewApp, 138 NewDBWorker: config.NewDBWorker, 139 } 140 141 w, err := newWorker(cfg) 142 if err != nil { 143 config.PrometheusRegisterer.Unregister(metricsCollector) 144 return nil, errors.Trace(err) 145 } 146 return common.NewCleanupWorker(w, func() { 147 // Clean up the metrics for the worker, so the next time a 148 // worker is created we can safely register the metrics again. 149 config.PrometheusRegisterer.Unregister(metricsCollector) 150 }), nil 151 }, 152 } 153 } 154 155 func dbAccessorOutput(in worker.Worker, out interface{}) error { 156 if w, ok := in.(*common.CleanupWorker); ok { 157 in = w.Worker 158 } 159 w, ok := in.(*dbWorker) 160 if !ok { 161 return errors.Errorf("expected input of type dbWorker, got %T", in) 162 } 163 164 switch out := out.(type) { 165 case *coredatabase.DBGetter: 166 var target coredatabase.DBGetter = w 167 *out = target 168 default: 169 return errors.Errorf("expected output of *database.DBGetter, got %T", out) 170 } 171 return nil 172 } 173 174 // IAASNodeManager returns a NodeManager that is configured to use 175 // the cloud-local TLS terminated address for Dqlite. 176 func IAASNodeManager(cfg agent.Config, logger Logger, slowQueryLogger coredatabase.SlowQueryLogger) NodeManager { 177 return database.NewNodeManager(cfg, false, logger, slowQueryLogger) 178 } 179 180 // CAASNodeManager returns a NodeManager that is configured to use 181 // the loopback address for Dqlite. 182 func CAASNodeManager(cfg agent.Config, logger Logger, slowQueryLogger coredatabase.SlowQueryLogger) NodeManager { 183 return database.NewNodeManager(cfg, true, logger, slowQueryLogger) 184 }