github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/agent/addons/addons.go (about)

     1  // Copyright 2020 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package addons
     5  
     6  import (
     7  	"runtime"
     8  
     9  	"github.com/juju/clock"
    10  	"github.com/juju/errors"
    11  	"github.com/juju/loggo"
    12  	"github.com/juju/names/v5"
    13  	"github.com/juju/worker/v3"
    14  	"github.com/juju/worker/v3/dependency"
    15  	"github.com/prometheus/client_golang/prometheus"
    16  
    17  	"github.com/juju/juju/cmd/jujud/agent/engine"
    18  	"github.com/juju/juju/core/machinelock"
    19  	"github.com/juju/juju/core/presence"
    20  	"github.com/juju/juju/worker/introspection"
    21  )
    22  
    23  var logger = loggo.GetLogger("juju.cmd.jujud.agent.addons")
    24  
    25  // DefaultIntrospectionSocketName returns the socket name to use for the
    26  // abstract domain socket that the introspection worker serves requests
    27  // over.
    28  func DefaultIntrospectionSocketName(entityTag names.Tag) string {
    29  	return "jujud-" + entityTag.String()
    30  }
    31  
    32  // IntrospectionConfig defines the various components that the introspection
    33  // worker reports on or needs to start up.
    34  type IntrospectionConfig struct {
    35  	AgentTag           names.Tag
    36  	Engine             *dependency.Engine
    37  	StatePoolReporter  introspection.Reporter
    38  	PubSubReporter     introspection.Reporter
    39  	MachineLock        machinelock.Lock
    40  	PrometheusGatherer prometheus.Gatherer
    41  	PresenceRecorder   presence.Recorder
    42  	Clock              clock.Clock
    43  	LocalHub           introspection.SimpleHub
    44  	CentralHub         introspection.StructuredHub
    45  
    46  	NewSocketName func(names.Tag) string
    47  	WorkerFunc    func(config introspection.Config) (worker.Worker, error)
    48  }
    49  
    50  // StartIntrospection creates the introspection worker. It cannot and should
    51  // not be in the engine itself as it reports on the engine, and other aspects
    52  // of the runtime. If we put it in the engine, then it is mostly likely shut
    53  // down in the times we need it most, which is when the agent is having
    54  // problems shutting down. Here we effectively start the worker and tie its
    55  // life to that of the engine that is returned.
    56  func StartIntrospection(cfg IntrospectionConfig) error {
    57  	if runtime.GOOS != "linux" {
    58  		logger.Debugf("introspection worker not supported on %q", runtime.GOOS)
    59  		return nil
    60  	}
    61  
    62  	socketName := cfg.NewSocketName(cfg.AgentTag)
    63  	w, err := cfg.WorkerFunc(introspection.Config{
    64  		SocketName:         socketName,
    65  		DepEngine:          cfg.Engine,
    66  		StatePool:          cfg.StatePoolReporter,
    67  		PubSub:             cfg.PubSubReporter,
    68  		MachineLock:        cfg.MachineLock,
    69  		PrometheusGatherer: cfg.PrometheusGatherer,
    70  		Presence:           cfg.PresenceRecorder,
    71  		Clock:              cfg.Clock,
    72  		LocalHub:           cfg.LocalHub,
    73  		CentralHub:         cfg.CentralHub,
    74  		// TODO(leases) - add lease introspection
    75  	})
    76  	if err != nil {
    77  		return errors.Trace(err)
    78  	}
    79  	go func() {
    80  		_ = cfg.Engine.Wait()
    81  		logger.Debugf("engine stopped, stopping introspection")
    82  		w.Kill()
    83  		_ = w.Wait()
    84  		logger.Debugf("introspection stopped")
    85  	}()
    86  
    87  	return nil
    88  }
    89  
    90  // NewPrometheusRegistry returns a new prometheus.Registry with
    91  // the Go and process metric collectors registered. This registry
    92  // is exposed by the introspection abstract domain socket on all
    93  // Linux agents.
    94  func NewPrometheusRegistry() (*prometheus.Registry, error) {
    95  	r := prometheus.NewRegistry()
    96  	if err := r.Register(prometheus.NewGoCollector()); err != nil {
    97  		return nil, errors.Trace(err)
    98  	}
    99  	if err := r.Register(prometheus.NewProcessCollector(
   100  		prometheus.ProcessCollectorOpts{})); err != nil {
   101  		return nil, errors.Trace(err)
   102  	}
   103  	return r, nil
   104  }
   105  
   106  // RegisterEngineMetrics registers the metrics sink on a prometheus registerer,
   107  // ensuring that we cleanup when the worker has stopped.
   108  func RegisterEngineMetrics(registry prometheus.Registerer, metrics prometheus.Collector, worker worker.Worker, sink engine.MetricSink) error {
   109  	if err := registry.Register(metrics); err != nil {
   110  		return errors.Annotatef(err, "failed to register engine metrics")
   111  	}
   112  
   113  	go func() {
   114  		_ = worker.Wait()
   115  		_ = sink.Unregister()
   116  		_ = registry.Unregister(metrics)
   117  	}()
   118  	return nil
   119  }