github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/introspection/socket_test.go (about) 1 /// Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package introspection_test 5 6 import ( 7 "bufio" 8 "bytes" 9 "fmt" 10 "io/ioutil" 11 "net" 12 "os" 13 "regexp" 14 "runtime" 15 "time" 16 17 "github.com/juju/clock/testclock" 18 "github.com/juju/testing" 19 jc "github.com/juju/testing/checkers" 20 "github.com/prometheus/client_golang/prometheus" 21 gc "gopkg.in/check.v1" 22 "gopkg.in/juju/worker.v1" 23 "gopkg.in/juju/worker.v1/workertest" 24 25 // Bring in the state package for the tracker profile. 26 "github.com/juju/juju/core/presence" 27 _ "github.com/juju/juju/state" 28 "github.com/juju/juju/worker/introspection" 29 ) 30 31 type suite struct { 32 testing.IsolationSuite 33 } 34 35 var _ = gc.Suite(&suite{}) 36 37 func (s *suite) TestConfigValidation(c *gc.C) { 38 w, err := introspection.NewWorker(introspection.Config{}) 39 c.Check(w, gc.IsNil) 40 c.Assert(err, gc.ErrorMatches, "empty SocketName not valid") 41 } 42 43 func (s *suite) TestStartStop(c *gc.C) { 44 if runtime.GOOS != "linux" { 45 c.Skip("introspection worker not supported on non-linux") 46 } 47 48 w, err := introspection.NewWorker(introspection.Config{ 49 SocketName: "introspection-test", 50 PrometheusGatherer: prometheus.NewRegistry(), 51 }) 52 c.Assert(err, jc.ErrorIsNil) 53 workertest.CheckKill(c, w) 54 } 55 56 type introspectionSuite struct { 57 testing.IsolationSuite 58 59 name string 60 worker worker.Worker 61 reporter introspection.DepEngineReporter 62 gatherer prometheus.Gatherer 63 recorder presence.Recorder 64 } 65 66 var _ = gc.Suite(&introspectionSuite{}) 67 68 func (s *introspectionSuite) SetUpTest(c *gc.C) { 69 if runtime.GOOS != "linux" { 70 c.Skip("introspection worker not supported on non-linux") 71 } 72 s.IsolationSuite.SetUpTest(c) 73 s.reporter = nil 74 s.worker = nil 75 s.recorder = nil 76 s.gatherer = newPrometheusGatherer() 77 s.startWorker(c) 78 } 79 80 func (s *introspectionSuite) startWorker(c *gc.C) { 81 s.name = fmt.Sprintf("introspection-test-%d", os.Getpid()) 82 w, err := introspection.NewWorker(introspection.Config{ 83 SocketName: s.name, 84 DepEngine: s.reporter, 85 PrometheusGatherer: s.gatherer, 86 Presence: s.recorder, 87 }) 88 c.Assert(err, jc.ErrorIsNil) 89 s.worker = w 90 s.AddCleanup(func(c *gc.C) { 91 workertest.CheckKill(c, w) 92 }) 93 } 94 95 func (s *introspectionSuite) call(c *gc.C, url string) []byte { 96 path := "@" + s.name 97 conn, err := net.Dial("unix", path) 98 c.Assert(err, jc.ErrorIsNil) 99 defer conn.Close() 100 101 _, err = fmt.Fprintf(conn, "GET %s HTTP/1.0\r\n\r\n", url) 102 c.Assert(err, jc.ErrorIsNil) 103 104 buf, err := ioutil.ReadAll(conn) 105 c.Assert(err, jc.ErrorIsNil) 106 return buf 107 } 108 109 func (s *introspectionSuite) TestCmdLine(c *gc.C) { 110 buf := s.call(c, "/debug/pprof/cmdline") 111 c.Assert(buf, gc.NotNil) 112 matches(c, buf, ".*/introspection.test") 113 } 114 115 func (s *introspectionSuite) TestGoroutineProfile(c *gc.C) { 116 buf := s.call(c, "/debug/pprof/goroutine?debug=1") 117 c.Assert(buf, gc.NotNil) 118 matches(c, buf, `^goroutine profile: total \d+`) 119 } 120 121 func (s *introspectionSuite) TestTrace(c *gc.C) { 122 buf := s.call(c, "/debug/pprof/trace?seconds=1") 123 c.Assert(buf, gc.NotNil) 124 matches(c, buf, `^Content-Type: application/octet-stream*`) 125 } 126 127 func (s *introspectionSuite) TestMissingDepEngineReporter(c *gc.C) { 128 buf := s.call(c, "/depengine") 129 matches(c, buf, "404 Not Found") 130 matches(c, buf, "missing dependency engine reporter") 131 } 132 133 func (s *introspectionSuite) TestMissingStatePoolReporter(c *gc.C) { 134 buf := s.call(c, "/statepool") 135 matches(c, buf, "404 Not Found") 136 matches(c, buf, "State Pool Report: missing reporter") 137 } 138 139 func (s *introspectionSuite) TestMissingPubSubReporter(c *gc.C) { 140 buf := s.call(c, "/pubsub") 141 matches(c, buf, "404 Not Found") 142 matches(c, buf, "PubSub Report: missing reporter") 143 } 144 145 func (s *introspectionSuite) TestMissingMachineLock(c *gc.C) { 146 buf := s.call(c, "/machinelock/") 147 matches(c, buf, "404 Not Found") 148 matches(c, buf, "missing machine lock reporter") 149 } 150 151 func (s *introspectionSuite) TestStateTrackerReporter(c *gc.C) { 152 buf := s.call(c, "/debug/pprof/juju/state/tracker?debug=1") 153 matches(c, buf, "200 OK") 154 matches(c, buf, "juju/state/tracker profile: total") 155 } 156 157 func (s *introspectionSuite) TestEngineReporter(c *gc.C) { 158 // We need to make sure the existing worker is shut down 159 // so we can connect to the socket. 160 workertest.CheckKill(c, s.worker) 161 s.reporter = &reporter{ 162 values: map[string]interface{}{ 163 "working": true, 164 }, 165 } 166 s.startWorker(c) 167 buf := s.call(c, "/depengine") 168 169 matches(c, buf, "200 OK") 170 matches(c, buf, "working: true") 171 } 172 173 func (s *introspectionSuite) TestMissingPresenceReporter(c *gc.C) { 174 buf := s.call(c, "/presence/") 175 matches(c, buf, "404 Not Found") 176 matches(c, buf, "page not found") 177 } 178 179 func (s *introspectionSuite) TestDisabledPresenceReporter(c *gc.C) { 180 // We need to make sure the existing worker is shut down 181 // so we can connect to the socket. 182 workertest.CheckKill(c, s.worker) 183 s.recorder = presence.New(testclock.NewClock(time.Now())) 184 s.startWorker(c) 185 186 buf := s.call(c, "/presence/") 187 matches(c, buf, "404 Not Found") 188 matches(c, buf, "agent is not an apiserver") 189 } 190 191 func (s *introspectionSuite) TestEnabledPresenceReporter(c *gc.C) { 192 // We need to make sure the existing worker is shut down 193 // so we can connect to the socket. 194 workertest.CheckKill(c, s.worker) 195 s.recorder = presence.New(testclock.NewClock(time.Now())) 196 s.recorder.Enable() 197 s.recorder.Connect("server", "model-uuid", "agent-1", 42, false, "") 198 s.startWorker(c) 199 200 buf := s.call(c, "/presence/") 201 matches(c, buf, "200 OK") 202 matches(c, buf, "AGENT SERVER CONN ID STATUS") 203 matches(c, buf, "agent-1 server 42 alive") 204 } 205 206 func (s *introspectionSuite) TestPrometheusMetrics(c *gc.C) { 207 buf := s.call(c, "/metrics/") 208 c.Assert(buf, gc.NotNil) 209 matches(c, buf, "# HELP tau Tau") 210 matches(c, buf, "# TYPE tau counter") 211 matches(c, buf, "tau 6.283185") 212 } 213 214 // matches fails if regex is not found in the contents of b. 215 // b is expected to be the response from the pprof http server, and will 216 // contain some HTTP preamble that should be ignored. 217 func matches(c *gc.C, b []byte, regex string) { 218 re, err := regexp.Compile(regex) 219 c.Assert(err, jc.ErrorIsNil) 220 r := bytes.NewReader(b) 221 sc := bufio.NewScanner(r) 222 for sc.Scan() { 223 if re.MatchString(sc.Text()) { 224 return 225 } 226 } 227 c.Fatalf("%q did not match regex %q", string(b), regex) 228 } 229 230 type reporter struct { 231 values map[string]interface{} 232 } 233 234 func (r *reporter) Report() map[string]interface{} { 235 return r.values 236 } 237 238 func newPrometheusGatherer() prometheus.Gatherer { 239 counter := prometheus.NewCounter(prometheus.CounterOpts{Name: "tau", Help: "Tau."}) 240 counter.Add(6.283185) 241 r := prometheus.NewPedanticRegistry() 242 r.MustRegister(counter) 243 return r 244 }