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 := &params.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  }