github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/metrics/collect/handler_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package collect_test
     5  
     6  import (
     7  	"net"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/juju/testing"
    14  	jc "github.com/juju/testing/checkers"
    15  	gc "gopkg.in/check.v1"
    16  	corecharm "gopkg.in/juju/charm.v6-unstable"
    17  	"gopkg.in/juju/names.v2"
    18  
    19  	coretesting "github.com/juju/juju/testing"
    20  	"github.com/juju/juju/worker/dependency"
    21  	dt "github.com/juju/juju/worker/dependency/testing"
    22  	"github.com/juju/juju/worker/metrics/collect"
    23  	"github.com/juju/juju/worker/metrics/spool"
    24  	"github.com/juju/juju/worker/uniter/runner/context"
    25  	"github.com/juju/juju/worker/workertest"
    26  )
    27  
    28  type handlerSuite struct {
    29  	coretesting.BaseSuite
    30  
    31  	manifoldConfig collect.ManifoldConfig
    32  	manifold       dependency.Manifold
    33  	dataDir        string
    34  	resources      dt.StubResources
    35  	recorder       *dummyRecorder
    36  	listener       *mockListener
    37  	mockReadCharm  *mockReadCharm
    38  }
    39  
    40  var _ = gc.Suite(&handlerSuite{})
    41  
    42  func (s *handlerSuite) SetUpTest(c *gc.C) {
    43  	s.BaseSuite.SetUpTest(c)
    44  	s.manifoldConfig = collect.ManifoldConfig{
    45  		AgentName:       "agent-name",
    46  		MetricSpoolName: "metric-spool-name",
    47  		CharmDirName:    "charmdir-name",
    48  	}
    49  	s.manifold = collect.Manifold(s.manifoldConfig)
    50  	s.dataDir = c.MkDir()
    51  
    52  	// create unit agent base dir so that hooks can run.
    53  	err := os.MkdirAll(filepath.Join(s.dataDir, "agents", "unit-u-0"), 0777)
    54  	c.Assert(err, jc.ErrorIsNil)
    55  
    56  	s.recorder = &dummyRecorder{
    57  		charmURL: "local:trusty/metered-1",
    58  		unitTag:  "metered/0",
    59  		metrics: map[string]corecharm.Metric{
    60  			"pings": corecharm.Metric{
    61  				Description: "test metric",
    62  				Type:        corecharm.MetricTypeAbsolute,
    63  			},
    64  			"juju-units": corecharm.Metric{},
    65  		},
    66  	}
    67  
    68  	s.resources = dt.StubResources{
    69  		"agent-name":        dt.StubResource{Output: &dummyAgent{dataDir: s.dataDir}},
    70  		"metric-spool-name": dt.StubResource{Output: &mockMetricFactory{recorder: s.recorder}},
    71  		"charmdir-name":     dt.StubResource{Output: &dummyCharmdir{aborted: false}},
    72  	}
    73  
    74  	s.PatchValue(collect.NewRecorder,
    75  		func(_ names.UnitTag, _ context.Paths, _ spool.MetricFactory) (spool.MetricRecorder, error) {
    76  			// Return a dummyRecorder here, because otherwise a real one
    77  			// *might* get instantiated and error out, if the periodic worker
    78  			// happens to fire before the worker shuts down (as seen in
    79  			// LP:#1497355).
    80  			return &dummyRecorder{
    81  				charmURL: "local:trusty/metered-1",
    82  				unitTag:  "metered/0",
    83  				metrics: map[string]corecharm.Metric{
    84  					"pings": corecharm.Metric{
    85  						Description: "test metric",
    86  						Type:        corecharm.MetricTypeAbsolute,
    87  					},
    88  					"juju-units": corecharm.Metric{},
    89  				},
    90  			}, nil
    91  		},
    92  	)
    93  	s.mockReadCharm = &mockReadCharm{}
    94  	s.PatchValue(collect.ReadCharm, s.mockReadCharm.ReadCharm)
    95  	s.listener = &mockListener{}
    96  	s.PatchValue(collect.NewSocketListener, collect.NewSocketListenerFnc(s.listener))
    97  }
    98  
    99  func (s *handlerSuite) TestListenerStart(c *gc.C) {
   100  	worker, err := s.manifold.Start(s.resources.Context())
   101  	c.Assert(err, jc.ErrorIsNil)
   102  	c.Assert(worker, gc.NotNil)
   103  	c.Assert(s.listener.Calls(), gc.HasLen, 0)
   104  	workertest.CleanKill(c, worker)
   105  	s.listener.CheckCall(c, 0, "Stop")
   106  }
   107  
   108  func (s *handlerSuite) TestJujuUnitsBuiltinMetric(c *gc.C) {
   109  	worker, err := s.manifold.Start(s.resources.Context())
   110  	c.Assert(err, jc.ErrorIsNil)
   111  	c.Assert(worker, gc.NotNil)
   112  	c.Assert(s.listener.Calls(), gc.HasLen, 0)
   113  
   114  	conn, err := s.listener.trigger()
   115  	c.Assert(err, jc.ErrorIsNil)
   116  	conn.CheckCallNames(c, "SetDeadline", "Write", "Close")
   117  
   118  	responseString := strings.Trim(string(conn.data), " \n\t")
   119  	c.Assert(responseString, gc.Equals, "ok")
   120  	c.Assert(s.recorder.batches, gc.HasLen, 1)
   121  
   122  	workertest.CleanKill(c, worker)
   123  	s.listener.CheckCall(c, 0, "Stop")
   124  }
   125  
   126  func (s *handlerSuite) TestReadCharmCalledOnEachTrigger(c *gc.C) {
   127  	worker, err := s.manifold.Start(s.resources.Context())
   128  	c.Assert(err, jc.ErrorIsNil)
   129  	c.Assert(worker, gc.NotNil)
   130  	c.Assert(s.listener.Calls(), gc.HasLen, 0)
   131  
   132  	_, err = s.listener.trigger()
   133  	c.Assert(err, jc.ErrorIsNil)
   134  	_, err = s.listener.trigger()
   135  	c.Assert(err, jc.ErrorIsNil)
   136  
   137  	s.PatchValue(collect.ReadCharm, s.mockReadCharm.ReadCharm)
   138  	workertest.CleanKill(c, worker)
   139  
   140  	// Expect 3 calls to ReadCharm, one on start and one per handler call
   141  	s.mockReadCharm.CheckCallNames(c, "ReadCharm", "ReadCharm", "ReadCharm")
   142  	s.listener.CheckCall(c, 0, "Stop")
   143  }
   144  
   145  func (s *handlerSuite) TestHandlerError(c *gc.C) {
   146  	worker, err := s.manifold.Start(s.resources.Context())
   147  	c.Assert(err, jc.ErrorIsNil)
   148  	c.Assert(worker, gc.NotNil)
   149  	c.Assert(s.listener.Calls(), gc.HasLen, 0)
   150  
   151  	s.recorder.err = "well, this is embarassing"
   152  
   153  	conn, err := s.listener.trigger()
   154  	c.Assert(err, gc.ErrorMatches, "failed to collect metrics: error adding 'juju-units' metric: well, this is embarassing")
   155  	conn.CheckCallNames(c, "SetDeadline", "Write", "Close")
   156  
   157  	responseString := strings.Trim(string(conn.data), " \n\t")
   158  	//c.Assert(responseString, gc.Matches, ".*well, this is embarassing")
   159  	c.Assert(responseString, gc.Equals, `error: failed to collect metrics: error adding 'juju-units' metric: well, this is embarassing`)
   160  	c.Assert(s.recorder.batches, gc.HasLen, 0)
   161  
   162  	workertest.CleanKill(c, worker)
   163  	s.listener.CheckCall(c, 0, "Stop")
   164  }
   165  
   166  type mockListener struct {
   167  	testing.Stub
   168  	handler spool.ConnectionHandler
   169  }
   170  
   171  func (l *mockListener) trigger() (*mockConnection, error) {
   172  	conn := &mockConnection{}
   173  	dying := make(chan struct{})
   174  	err := l.handler.Handle(conn, dying)
   175  	if err != nil {
   176  		return conn, err
   177  	}
   178  	return conn, nil
   179  }
   180  
   181  // Stop implements the stopper interface.
   182  func (l *mockListener) Stop() error {
   183  	l.AddCall("Stop")
   184  	return nil
   185  }
   186  
   187  func (l *mockListener) SetHandler(handler spool.ConnectionHandler) {
   188  	l.handler = handler
   189  }
   190  
   191  type mockConnection struct {
   192  	net.Conn
   193  	testing.Stub
   194  	data []byte
   195  }
   196  
   197  // SetDeadline implements the net.Conn interface.
   198  func (c *mockConnection) SetDeadline(t time.Time) error {
   199  	c.AddCall("SetDeadline", t)
   200  	return nil
   201  }
   202  
   203  // Write implements the net.Conn interface.
   204  func (c *mockConnection) Write(data []byte) (int, error) {
   205  	c.AddCall("Write", data)
   206  	c.data = make([]byte, len(data))
   207  	copy(c.data, data)
   208  	return len(data), nil
   209  }
   210  
   211  // Close implements the net.Conn interface.
   212  func (c *mockConnection) Close() error {
   213  	c.AddCall("Close")
   214  	return nil
   215  }
   216  
   217  type mockMetricFactory struct {
   218  	spool.MetricFactory
   219  	recorder *dummyRecorder
   220  }
   221  
   222  // Recorder implements the spool.MetricFactory interface.
   223  func (f *mockMetricFactory) Recorder(metrics map[string]corecharm.Metric, charmURL, unitTag string) (spool.MetricRecorder, error) {
   224  	return f.recorder, nil
   225  }
   226  
   227  type mockReadCharm struct {
   228  	testing.Stub
   229  }
   230  
   231  func (m *mockReadCharm) ReadCharm(unitTag names.UnitTag, paths context.Paths) (*corecharm.URL, map[string]corecharm.Metric, error) {
   232  	m.MethodCall(m, "ReadCharm", unitTag, paths)
   233  	return corecharm.MustParseURL("local:trusty/metered-1"),
   234  		map[string]corecharm.Metric{
   235  			"pings":      corecharm.Metric{Description: "test metric", Type: corecharm.MetricTypeAbsolute},
   236  			"juju-units": corecharm.Metric{},
   237  		}, nil
   238  }