github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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 err = metricSender.Handle(conn) 90 c.Assert(err, jc.ErrorIsNil) 91 92 c.Assert(apiSender.batches, gc.HasLen, 1) 93 c.Assert(apiSender.batches[0].Tag, gc.Equals, "testcharm/0") 94 c.Assert(apiSender.batches[0].Batch.CharmURL, gc.Equals, "local:trusty/testcharm") 95 c.Assert(apiSender.batches[0].Batch.Metrics, gc.HasLen, 1) 96 c.Assert(apiSender.batches[0].Batch.Metrics[0].Key, gc.Equals, "pings") 97 c.Assert(apiSender.batches[0].Batch.Metrics[0].Value, gc.Equals, "50") 98 } 99 100 func (s *senderSuite) TestMetricSendingSuccess(c *gc.C) { 101 apiSender := newTestAPIMetricSender() 102 103 metricSender, err := sender.NewSender(apiSender, s.metricfactory, s.socketDir, "test-unit-0") 104 c.Assert(err, jc.ErrorIsNil) 105 stopCh := make(chan struct{}) 106 err = metricSender.Do(stopCh) 107 c.Assert(err, jc.ErrorIsNil) 108 109 c.Assert(apiSender.batches, gc.HasLen, 1) 110 111 reader, err := spool.NewJSONMetricReader(s.spoolDir) 112 c.Assert(err, jc.ErrorIsNil) 113 batches, err := reader.Read() 114 c.Assert(err, jc.ErrorIsNil) 115 c.Assert(batches, gc.HasLen, 0) 116 } 117 118 func (s *senderSuite) TestSendingGetDuplicate(c *gc.C) { 119 apiSender := newTestAPIMetricSender() 120 121 apiErr := ¶ms.Error{Message: "already exists", Code: params.CodeAlreadyExists} 122 select { 123 case apiSender.errors <- apiErr: 124 default: 125 c.Fatalf("blocked error channel") 126 } 127 128 metricSender, err := sender.NewSender(apiSender, s.metricfactory, s.socketDir, "test-unit-0") 129 c.Assert(err, jc.ErrorIsNil) 130 stopCh := make(chan struct{}) 131 err = metricSender.Do(stopCh) 132 c.Assert(err, jc.ErrorIsNil) 133 134 c.Assert(apiSender.batches, gc.HasLen, 1) 135 136 reader, err := spool.NewJSONMetricReader(s.spoolDir) 137 c.Assert(err, jc.ErrorIsNil) 138 batches, err := reader.Read() 139 c.Assert(err, jc.ErrorIsNil) 140 c.Assert(batches, gc.HasLen, 0) 141 } 142 143 func (s *senderSuite) TestSendingFails(c *gc.C) { 144 apiSender := newTestAPIMetricSender() 145 146 select { 147 case apiSender.sendError <- errors.New("something went wrong"): 148 default: 149 c.Fatalf("blocked error channel") 150 } 151 152 metricSender, err := sender.NewSender(apiSender, s.metricfactory, s.socketDir, "test-unit-0") 153 c.Assert(err, jc.ErrorIsNil) 154 stopCh := make(chan struct{}) 155 err = metricSender.Do(stopCh) 156 c.Assert(err, gc.ErrorMatches, "something went wrong") 157 158 c.Assert(apiSender.batches, gc.HasLen, 1) 159 160 reader, err := spool.NewJSONMetricReader(s.spoolDir) 161 c.Assert(err, jc.ErrorIsNil) 162 batches, err := reader.Read() 163 c.Assert(err, jc.ErrorIsNil) 164 c.Assert(batches, gc.HasLen, 1) 165 } 166 167 func (s *senderSuite) TestNoSpoolDirectory(c *gc.C) { 168 apiSender := newTestAPIMetricSender() 169 170 metricfactory := &stubMetricFactory{ 171 &testing.Stub{}, 172 "/some/random/spool/dir", 173 } 174 175 metricSender, err := sender.NewSender(apiSender, metricfactory, s.socketDir, "") 176 c.Assert(err, jc.ErrorIsNil) 177 stopCh := make(chan struct{}) 178 err = metricSender.Do(stopCh) 179 c.Assert(err, gc.ErrorMatches, `failed to open spool directory "/some/random/spool/dir": .*`) 180 181 c.Assert(apiSender.batches, gc.HasLen, 0) 182 } 183 184 func (s *senderSuite) TestNoMetricsToSend(c *gc.C) { 185 apiSender := newTestAPIMetricSender() 186 187 newTmpSpoolDir := c.MkDir() 188 metricfactory := &stubMetricFactory{ 189 &testing.Stub{}, 190 newTmpSpoolDir, 191 } 192 193 metricSender, err := sender.NewSender(apiSender, metricfactory, s.socketDir, "test-unit-0") 194 c.Assert(err, jc.ErrorIsNil) 195 stopCh := make(chan struct{}) 196 err = metricSender.Do(stopCh) 197 c.Assert(err, jc.ErrorIsNil) 198 199 c.Assert(apiSender.batches, gc.HasLen, 0) 200 } 201 202 func newTestAPIMetricSender() *testAPIMetricSender { 203 return &testAPIMetricSender{errors: make(chan error, 1), sendError: make(chan error, 1)} 204 } 205 206 type testAPIMetricSender struct { 207 batches []params.MetricBatchParam 208 errors chan error 209 sendError chan error 210 } 211 212 func (t *testAPIMetricSender) AddMetricBatches(batches []params.MetricBatchParam) (map[string]error, error) { 213 t.batches = batches 214 215 var err error 216 select { 217 case e := <-t.errors: 218 err = e 219 default: 220 err = (*params.Error)(nil) 221 } 222 223 var sendErr error 224 select { 225 case e := <-t.sendError: 226 sendErr = e 227 default: 228 sendErr = nil 229 } 230 231 errors := make(map[string]error) 232 for _, b := range batches { 233 errors[b.Batch.UUID] = err 234 } 235 return errors, sendErr 236 } 237 238 type stubMetricFactory struct { 239 *testing.Stub 240 spoolDir string 241 } 242 243 func (s *stubMetricFactory) Recorder(declaredMetrics map[string]corecharm.Metric, charmURL, unitTag string) (spool.MetricRecorder, error) { 244 s.MethodCall(s, "Recorder", declaredMetrics, charmURL, unitTag) 245 config := spool.MetricRecorderConfig{ 246 SpoolDir: s.spoolDir, 247 Metrics: declaredMetrics, 248 CharmURL: charmURL, 249 UnitTag: unitTag, 250 } 251 252 return spool.NewJSONMetricRecorder(config) 253 } 254 255 func (s *stubMetricFactory) Reader() (spool.MetricReader, error) { 256 s.MethodCall(s, "Reader") 257 return spool.NewJSONMetricReader(s.spoolDir) 258 259 } 260 261 type mockConnection struct { 262 net.Conn 263 testing.Stub 264 data []byte 265 } 266 267 // SetDeadline implements the net.Conn interface. 268 func (c *mockConnection) SetDeadline(t time.Time) error { 269 c.AddCall("SetDeadline", t) 270 return nil 271 } 272 273 // Write implements the net.Conn interface. 274 func (c *mockConnection) Write(data []byte) (int, error) { 275 c.AddCall("Write", data) 276 c.data = data 277 return len(data), nil 278 } 279 280 // Close implements the net.Conn interface. 281 func (c *mockConnection) Close() error { 282 c.AddCall("Close") 283 return nil 284 } 285 286 func (c *mockConnection) eof() bool { 287 return len(c.data) == 0 288 } 289 290 func (c *mockConnection) readByte() byte { 291 b := c.data[0] 292 c.data = c.data[1:] 293 return b 294 } 295 296 func (c *mockConnection) Read(p []byte) (n int, err error) { 297 if c.eof() { 298 err = io.EOF 299 return 300 } 301 if cp := cap(p); cp > 0 { 302 for n < cp { 303 p[n] = c.readByte() 304 n++ 305 if c.eof() { 306 break 307 } 308 } 309 } 310 return 311 } 312 313 func sockPath(c *gc.C) string { 314 sockPath := path.Join(c.MkDir(), "test.listener") 315 if runtime.GOOS == "windows" { 316 return `\\.\pipe` + sockPath[2:] 317 } 318 return sockPath 319 }