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  }