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 }