github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/jujud/agent/introspection.go (about)

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