github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/metrics/sender/sender_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package sender_test 5 6 import ( 7 "errors" 8 "fmt" 9 "io" 10 "net" 11 "path" 12 "runtime" 13 "time" 14 15 "github.com/juju/testing" 16 jc "github.com/juju/testing/checkers" 17 gc "gopkg.in/check.v1" 18 corecharm "gopkg.in/juju/charm.v6-unstable" 19 20 "github.com/juju/juju/apiserver/params" 21 "github.com/juju/juju/worker/metrics/sender" 22 "github.com/juju/juju/worker/metrics/spool" 23 ) 24 25 var _ = gc.Suite(&senderSuite{}) 26 27 type senderSuite struct { 28 spoolDir string 29 socketDir string 30 metricfactory spool.MetricFactory 31 } 32 33 func (s *senderSuite) SetUpTest(c *gc.C) { 34 s.spoolDir = c.MkDir() 35 s.socketDir = c.MkDir() 36 37 s.metricfactory = &stubMetricFactory{ 38 &testing.Stub{}, 39 s.spoolDir, 40 } 41 42 declaredMetrics := map[string]corecharm.Metric{ 43 "pings": corecharm.Metric{Description: "test pings", Type: corecharm.MetricTypeAbsolute}, 44 } 45 recorder, err := s.metricfactory.Recorder(declaredMetrics, "local:trusty/testcharm", "testcharm/0") 46 c.Assert(err, jc.ErrorIsNil) 47 48 err = recorder.AddMetric("pings", "50", time.Now()) 49 c.Assert(err, jc.ErrorIsNil) 50 51 err = recorder.Close() 52 c.Assert(err, jc.ErrorIsNil) 53 54 reader, err := s.metricfactory.Reader() 55 c.Assert(err, jc.ErrorIsNil) 56 batches, err := reader.Read() 57 c.Assert(err, jc.ErrorIsNil) 58 c.Assert(batches, gc.HasLen, 1) 59 60 testing.PatchValue(sender.SocketName, func(_, _ string) string { 61 return sockPath(c) 62 }) 63 } 64 65 func (s *senderSuite) TestHandler(c *gc.C) { 66 apiSender := newTestAPIMetricSender() 67 tmpDir := c.MkDir() 68 metricFactory := &stubMetricFactory{ 69 &testing.Stub{}, 70 tmpDir, 71 } 72 73 declaredMetrics := map[string]corecharm.Metric{ 74 "pings": corecharm.Metric{Description: "test pings", Type: corecharm.MetricTypeAbsolute}, 75 } 76 recorder, err := metricFactory.Recorder(declaredMetrics, "local:trusty/testcharm", "testcharm/0") 77 c.Assert(err, jc.ErrorIsNil) 78 79 err = recorder.AddMetric("pings", "50", time.Now()) 80 c.Assert(err, jc.ErrorIsNil) 81 82 err = recorder.Close() 83 c.Assert(err, jc.ErrorIsNil) 84 85 metricSender, err := sender.NewSender(apiSender, s.metricfactory, s.socketDir, "") 86 c.Assert(err, jc.ErrorIsNil) 87 88 conn := &mockConnection{data: []byte(fmt.Sprintf("%v\n", tmpDir))} 89 ch := make(chan struct{}) 90 err = metricSender.Handle(conn, ch) 91 c.Assert(err, jc.ErrorIsNil) 92 93 c.Assert(apiSender.batches, gc.HasLen, 1) 94 c.Assert(apiSender.batches[0].Tag, gc.Equals, "testcharm/0") 95 c.Assert(apiSender.batches[0].Batch.CharmURL, gc.Equals, "local:trusty/testcharm") 96 c.Assert(apiSender.batches[0].Batch.Metrics, gc.HasLen, 1) 97 c.Assert(apiSender.batches[0].Batch.Metrics[0].Key, gc.Equals, "pings") 98 c.Assert(apiSender.batches[0].Batch.Metrics[0].Value, gc.Equals, "50") 99 } 100 101 func (s *senderSuite) TestMetricSendingSuccess(c *gc.C) { 102 apiSender := newTestAPIMetricSender() 103 104 metricSender, err := sender.NewSender(apiSender, s.metricfactory, s.socketDir, "test-unit-0") 105 c.Assert(err, jc.ErrorIsNil) 106 stopCh := make(chan struct{}) 107 err = metricSender.Do(stopCh) 108 c.Assert(err, jc.ErrorIsNil) 109 110 c.Assert(apiSender.batches, gc.HasLen, 1) 111 112 reader, err := spool.NewJSONMetricReader(s.spoolDir) 113 c.Assert(err, jc.ErrorIsNil) 114 batches, err := reader.Read() 115 c.Assert(err, jc.ErrorIsNil) 116 c.Assert(batches, gc.HasLen, 0) 117 } 118 119 func (s *senderSuite) TestSendingGetDuplicate(c *gc.C) { 120 apiSender := newTestAPIMetricSender() 121 122 apiErr := ¶ms.Error{Message: "already exists", Code: params.CodeAlreadyExists} 123 select { 124 case apiSender.errors <- apiErr: 125 default: 126 c.Fatalf("blocked error channel") 127 } 128 129 metricSender, err := sender.NewSender(apiSender, s.metricfactory, s.socketDir, "test-unit-0") 130 c.Assert(err, jc.ErrorIsNil) 131 stopCh := make(chan struct{}) 132 err = metricSender.Do(stopCh) 133 c.Assert(err, jc.ErrorIsNil) 134 135 c.Assert(apiSender.batches, gc.HasLen, 1) 136 137 reader, err := spool.NewJSONMetricReader(s.spoolDir) 138 c.Assert(err, jc.ErrorIsNil) 139 batches, err := reader.Read() 140 c.Assert(err, jc.ErrorIsNil) 141 c.Assert(batches, gc.HasLen, 0) 142 } 143 144 func (s *senderSuite) TestSendingFails(c *gc.C) { 145 apiSender := newTestAPIMetricSender() 146 147 select { 148 case apiSender.sendError <- errors.New("something went wrong"): 149 default: 150 c.Fatalf("blocked error channel") 151 } 152 153 metricSender, err := sender.NewSender(apiSender, s.metricfactory, s.socketDir, "test-unit-0") 154 c.Assert(err, jc.ErrorIsNil) 155 stopCh := make(chan struct{}) 156 err = metricSender.Do(stopCh) 157 c.Assert(err, gc.ErrorMatches, "could not send metrics: something went wrong") 158 159 c.Assert(apiSender.batches, gc.HasLen, 1) 160 161 reader, err := spool.NewJSONMetricReader(s.spoolDir) 162 c.Assert(err, jc.ErrorIsNil) 163 batches, err := reader.Read() 164 c.Assert(err, jc.ErrorIsNil) 165 c.Assert(batches, gc.HasLen, 1) 166 } 167 168 func (s *senderSuite) TestNoSpoolDirectory(c *gc.C) { 169 apiSender := newTestAPIMetricSender() 170 171 metricfactory := &stubMetricFactory{ 172 &testing.Stub{}, 173 "/some/random/spool/dir", 174 } 175 176 metricSender, err := sender.NewSender(apiSender, metricfactory, s.socketDir, "") 177 c.Assert(err, jc.ErrorIsNil) 178 stopCh := make(chan struct{}) 179 err = metricSender.Do(stopCh) 180 c.Assert(err, gc.ErrorMatches, `failed to open spool directory "/some/random/spool/dir": .*`) 181 182 c.Assert(apiSender.batches, gc.HasLen, 0) 183 } 184 185 func (s *senderSuite) TestNoMetricsToSend(c *gc.C) { 186 apiSender := newTestAPIMetricSender() 187 188 newTmpSpoolDir := c.MkDir() 189 metricfactory := &stubMetricFactory{ 190 &testing.Stub{}, 191 newTmpSpoolDir, 192 } 193 194 metricSender, err := sender.NewSender(apiSender, metricfactory, s.socketDir, "test-unit-0") 195 c.Assert(err, jc.ErrorIsNil) 196 stopCh := make(chan struct{}) 197 err = metricSender.Do(stopCh) 198 c.Assert(err, jc.ErrorIsNil) 199 200 c.Assert(apiSender.batches, gc.HasLen, 0) 201 } 202 203 func newTestAPIMetricSender() *testAPIMetricSender { 204 return &testAPIMetricSender{errors: make(chan error, 1), sendError: make(chan error, 1)} 205 } 206 207 type testAPIMetricSender struct { 208 batches []params.MetricBatchParam 209 errors chan error 210 sendError chan error 211 } 212 213 func (t *testAPIMetricSender) AddMetricBatches(batches []params.MetricBatchParam) (map[string]error, error) { 214 t.batches = batches 215 216 var err error 217 select { 218 case e := <-t.errors: 219 err = e 220 default: 221 err = (*params.Error)(nil) 222 } 223 224 var sendErr error 225 select { 226 case e := <-t.sendError: 227 sendErr = e 228 default: 229 sendErr = nil 230 } 231 232 errors := make(map[string]error) 233 for _, b := range batches { 234 errors[b.Batch.UUID] = err 235 } 236 return errors, sendErr 237 } 238 239 type stubMetricFactory struct { 240 *testing.Stub 241 spoolDir string 242 } 243 244 func (s *stubMetricFactory) Recorder(declaredMetrics map[string]corecharm.Metric, charmURL, unitTag string) (spool.MetricRecorder, error) { 245 s.MethodCall(s, "Recorder", declaredMetrics, charmURL, unitTag) 246 config := spool.MetricRecorderConfig{ 247 SpoolDir: s.spoolDir, 248 Metrics: declaredMetrics, 249 CharmURL: charmURL, 250 UnitTag: unitTag, 251 } 252 253 return spool.NewJSONMetricRecorder(config) 254 } 255 256 func (s *stubMetricFactory) Reader() (spool.MetricReader, error) { 257 s.MethodCall(s, "Reader") 258 return spool.NewJSONMetricReader(s.spoolDir) 259 260 } 261 262 type mockConnection struct { 263 net.Conn 264 testing.Stub 265 data []byte 266 } 267 268 // SetDeadline implements the net.Conn interface. 269 func (c *mockConnection) SetDeadline(t time.Time) error { 270 c.AddCall("SetDeadline", t) 271 return nil 272 } 273 274 // Write implements the net.Conn interface. 275 func (c *mockConnection) Write(data []byte) (int, error) { 276 c.AddCall("Write", data) 277 c.data = data 278 return len(data), nil 279 } 280 281 // Close implements the net.Conn interface. 282 func (c *mockConnection) Close() error { 283 c.AddCall("Close") 284 return nil 285 } 286 287 func (c *mockConnection) eof() bool { 288 return len(c.data) == 0 289 } 290 291 func (c *mockConnection) readByte() byte { 292 b := c.data[0] 293 c.data = c.data[1:] 294 return b 295 } 296 297 func (c *mockConnection) Read(p []byte) (n int, err error) { 298 if c.eof() { 299 err = io.EOF 300 return 301 } 302 if cp := cap(p); cp > 0 { 303 for n < cp { 304 p[n] = c.readByte() 305 n++ 306 if c.eof() { 307 break 308 } 309 } 310 } 311 return 312 } 313 314 func sockPath(c *gc.C) string { 315 sockPath := path.Join(c.MkDir(), "test.listener") 316 if runtime.GOOS == "windows" { 317 return `\\.\pipe` + sockPath[2:] 318 } 319 return sockPath 320 }