package agent import ( "context" "fmt" "io" "net" "net/http" "time" autoconf "github.com/hashicorp/consul/agent/auto-config" "github.com/hashicorp/consul/agent/cache" certmon "github.com/hashicorp/consul/agent/cert-monitor" "github.com/hashicorp/consul/agent/config" "github.com/hashicorp/consul/agent/pool" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/token" "github.com/hashicorp/consul/ipaddr" "github.com/hashicorp/consul/lib" "github.com/hashicorp/consul/logging" "github.com/hashicorp/consul/tlsutil" "github.com/hashicorp/go-hclog" ) // TODO: BaseDeps should be renamed in the future once more of Agent.Start // has been moved out in front of Agent.New, and we can better see the setup // dependencies. type BaseDeps struct { Logger hclog.InterceptLogger TLSConfigurator *tlsutil.Configurator // TODO: use an interface MetricsHandler MetricsHandler RuntimeConfig *config.RuntimeConfig Tokens *token.Store Cache *cache.Cache AutoConfig *autoconf.AutoConfig // TODO: use an interface ConnPool *pool.ConnPool // TODO: use an interface } // MetricsHandler provides an http.Handler for displaying metrics. type MetricsHandler interface { DisplayMetrics(resp http.ResponseWriter, req *http.Request) (interface{}, error) } type ConfigLoader func(source config.Source) (cfg *config.RuntimeConfig, warnings []string, err error) func NewBaseDeps(configLoader ConfigLoader, logOut io.Writer) (BaseDeps, error) { d := BaseDeps{} cfg, warnings, err := configLoader(nil) if err != nil { return d, err } // TODO: use logging.Config in RuntimeConfig instead of separate fields logConf := &logging.Config{ LogLevel: cfg.LogLevel, LogJSON: cfg.LogJSON, Name: logging.Agent, EnableSyslog: cfg.EnableSyslog, SyslogFacility: cfg.SyslogFacility, LogFilePath: cfg.LogFile, LogRotateDuration: cfg.LogRotateDuration, LogRotateBytes: cfg.LogRotateBytes, LogRotateMaxFiles: cfg.LogRotateMaxFiles, } d.Logger, err = logging.Setup(logConf, []io.Writer{logOut}) if err != nil { return d, err } for _, w := range warnings { d.Logger.Warn(w) } cfg.NodeID, err = newNodeIDFromConfig(cfg, d.Logger) if err != nil { return d, fmt.Errorf("failed to setup node ID: %w", err) } d.MetricsHandler, err = lib.InitTelemetry(cfg.Telemetry) if err != nil { return d, fmt.Errorf("failed to initialize telemetry: %w", err) } d.TLSConfigurator, err = tlsutil.NewConfigurator(cfg.ToTLSUtilConfig(), d.Logger) if err != nil { return d, err } d.RuntimeConfig = cfg d.Tokens = new(token.Store) // cache-types are not registered yet, but they won't be used until the components are started. d.Cache = cache.New(cfg.Cache) d.ConnPool = newConnPool(cfg, d.Logger, d.TLSConfigurator) deferredAC := &deferredAutoConfig{} cmConf := new(certmon.Config). WithCache(d.Cache). WithTLSConfigurator(d.TLSConfigurator). WithDNSSANs(cfg.AutoConfig.DNSSANs). WithIPSANs(cfg.AutoConfig.IPSANs). WithDatacenter(cfg.Datacenter). WithNodeName(cfg.NodeName). WithFallback(deferredAC.autoConfigFallbackTLS). WithLogger(d.Logger.Named(logging.AutoConfig)). WithTokens(d.Tokens). WithPersistence(deferredAC.autoConfigPersist) acCertMon, err := certmon.New(cmConf) if err != nil { return d, err } acConf := autoconf.Config{ DirectRPC: d.ConnPool, Logger: d.Logger, CertMonitor: acCertMon, Loader: configLoader, } d.AutoConfig, err = autoconf.New(acConf) if err != nil { return d, err } // TODO: can this cyclic dependency be un-cycled? deferredAC.autoConf = d.AutoConfig return d, nil } func newConnPool(config *config.RuntimeConfig, logger hclog.Logger, tls *tlsutil.Configurator) *pool.ConnPool { var rpcSrcAddr *net.TCPAddr if !ipaddr.IsAny(config.RPCBindAddr) { rpcSrcAddr = &net.TCPAddr{IP: config.RPCBindAddr.IP} } pool := &pool.ConnPool{ Server: config.ServerMode, SrcAddr: rpcSrcAddr, Logger: logger.StandardLogger(&hclog.StandardLoggerOptions{InferLevels: true}), TLSConfigurator: tls, Datacenter: config.Datacenter, } if config.ServerMode { pool.MaxTime = 2 * time.Minute pool.MaxStreams = 64 } else { pool.MaxTime = 127 * time.Second pool.MaxStreams = 32 } return pool } type deferredAutoConfig struct { autoConf *autoconf.AutoConfig // TODO: use an interface } func (a *deferredAutoConfig) autoConfigFallbackTLS(ctx context.Context) (*structs.SignedResponse, error) { if a.autoConf == nil { return nil, fmt.Errorf("AutoConfig manager has not been created yet") } return a.autoConf.FallbackTLS(ctx) } func (a *deferredAutoConfig) autoConfigPersist(resp *structs.SignedResponse) error { if a.autoConf == nil { return fmt.Errorf("AutoConfig manager has not been created yet") } return a.autoConf.RecordUpdatedCerts(resp) }