github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/metricsender/metricsender_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package metricsender_test
     5  
     6  import (
     7  	"errors"
     8  	"time"
     9  
    10  	"github.com/juju/clock"
    11  	"github.com/juju/clock/testclock"
    12  	wireformat "github.com/juju/romulus/wireformat/metrics"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/apiserver/facades/agent/metricsender"
    17  	"github.com/juju/juju/apiserver/facades/agent/metricsender/testing"
    18  	jujujutesting "github.com/juju/juju/juju/testing"
    19  	"github.com/juju/juju/state"
    20  	"github.com/juju/juju/testing/factory"
    21  )
    22  
    23  type MetricSenderSuite struct {
    24  	jujujutesting.JujuConnSuite
    25  	meteredUnit *state.Unit
    26  	credUnit    *state.Unit
    27  	clock       clock.Clock
    28  }
    29  
    30  // TODO(externalreality): This may go away once the separation of
    31  // responsibilities between state.State and the different model types become
    32  // visible in the code.
    33  type TestSenderBackend struct {
    34  	*state.State
    35  	*state.Model
    36  }
    37  
    38  var _ = gc.Suite(&MetricSenderSuite{})
    39  
    40  var _ metricsender.MetricSender = (*testing.MockSender)(nil)
    41  
    42  var _ metricsender.MetricSender = (*metricsender.NopSender)(nil)
    43  
    44  func (s *MetricSenderSuite) SetUpTest(c *gc.C) {
    45  	s.JujuConnSuite.SetUpTest(c)
    46  
    47  	meteredCharm := s.Factory.MakeCharm(c, &factory.CharmParams{Name: "metered", URL: "cs:quantal/metered"})
    48  	// Application with metrics credentials set.
    49  	credApp := s.Factory.MakeApplication(c, &factory.ApplicationParams{Charm: meteredCharm, Name: "cred"})
    50  	err := credApp.SetMetricCredentials([]byte("something here"))
    51  	c.Assert(err, jc.ErrorIsNil)
    52  	meteredApp := s.Factory.MakeApplication(c, &factory.ApplicationParams{Charm: meteredCharm})
    53  	s.meteredUnit = s.Factory.MakeUnit(c, &factory.UnitParams{Application: meteredApp, SetCharmURL: true})
    54  	s.credUnit = s.Factory.MakeUnit(c, &factory.UnitParams{Application: credApp, SetCharmURL: true})
    55  	s.clock = testclock.NewClock(time.Now())
    56  }
    57  
    58  func (s *MetricSenderSuite) TestToWire(c *gc.C) {
    59  	now := time.Now().Round(time.Second)
    60  	metric := s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.credUnit, Sent: false, Time: &now})
    61  	result := metricsender.ToWire(metric, s.Model.Name())
    62  	m := metric.Metrics()[0]
    63  	metrics := []wireformat.Metric{
    64  		{
    65  			Key:   m.Key,
    66  			Value: m.Value,
    67  			Time:  m.Time.UTC(),
    68  			Labels: map[string]string{
    69  				"foo": "bar",
    70  			},
    71  		},
    72  	}
    73  	expected := &wireformat.MetricBatch{
    74  		UUID:           metric.UUID(),
    75  		ModelUUID:      metric.ModelUUID(),
    76  		ModelName:      "controller",
    77  		UnitName:       metric.Unit(),
    78  		CharmUrl:       metric.CharmURL(),
    79  		Created:        metric.Created().UTC(),
    80  		Metrics:        metrics,
    81  		Credentials:    metric.Credentials(),
    82  		SLACredentials: metric.SLACredentials(),
    83  	}
    84  	c.Assert(result, gc.DeepEquals, expected)
    85  }
    86  
    87  func (s *MetricSenderSuite) TestSendMetricsFromNewModel(c *gc.C) {
    88  	var sender testing.MockSender
    89  	now := time.Now()
    90  	clock := testclock.NewClock(time.Now())
    91  
    92  	state := s.Factory.MakeModel(c, &factory.ModelParams{Name: "test-model"})
    93  	defer state.Close()
    94  	f := factory.NewFactory(state, s.StatePool)
    95  	model, err := state.Model()
    96  	c.Assert(err, jc.ErrorIsNil)
    97  	modelName := model.Name()
    98  	c.Assert(modelName, gc.Equals, "test-model")
    99  
   100  	meteredCharm := f.MakeCharm(c, &factory.CharmParams{Name: "metered", URL: "cs:quantal/metered"})
   101  	// Application with metrics credentials set.
   102  	credApp := f.MakeApplication(c, &factory.ApplicationParams{Charm: meteredCharm, Name: "cred"})
   103  	err = credApp.SetMetricCredentials([]byte("something here"))
   104  	c.Assert(err, jc.ErrorIsNil)
   105  	meteredApp := f.MakeApplication(c, &factory.ApplicationParams{Charm: meteredCharm})
   106  	meteredUnit := f.MakeUnit(c, &factory.UnitParams{Application: meteredApp, SetCharmURL: true})
   107  	credUnit := f.MakeUnit(c, &factory.UnitParams{Application: credApp, SetCharmURL: true})
   108  
   109  	f.MakeMetric(c, &factory.MetricParams{Unit: credUnit, Time: &now})
   110  	f.MakeMetric(c, &factory.MetricParams{Unit: meteredUnit, Time: &now})
   111  	f.MakeMetric(c, &factory.MetricParams{Unit: credUnit, Sent: true, Time: &now})
   112  	err = metricsender.SendMetrics(TestSenderBackend{state, model}, &sender, clock, 10, true)
   113  	c.Assert(err, jc.ErrorIsNil)
   114  	c.Assert(sender.Data, gc.HasLen, 1)
   115  	c.Assert(sender.Data[0], gc.HasLen, 2)
   116  	c.Assert(sender.Data[0][0].ModelName, gc.Equals, "test-model")
   117  	c.Assert(sender.Data[0][1].ModelName, gc.Equals, "test-model")
   118  }
   119  
   120  // TestSendMetrics creates 2 unsent metrics and a sent metric
   121  // and checks that the 2 unsent metrics get marked as sent (have their
   122  // sent field set to true).
   123  func (s *MetricSenderSuite) TestSendMetrics(c *gc.C) {
   124  	var sender testing.MockSender
   125  	now := time.Now()
   126  	unsent1 := s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.credUnit, Time: &now})
   127  	unsent2 := s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.meteredUnit, Time: &now})
   128  	s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.credUnit, Sent: true, Time: &now})
   129  	err := metricsender.SendMetrics(TestSenderBackend{s.State, s.Model}, &sender, s.clock, 10, true)
   130  	c.Assert(err, jc.ErrorIsNil)
   131  	c.Assert(sender.Data, gc.HasLen, 1)
   132  	c.Assert(sender.Data[0], gc.HasLen, 2)
   133  
   134  	sent1, err := s.State.MetricBatch(unsent1.UUID())
   135  	c.Assert(err, jc.ErrorIsNil)
   136  	c.Assert(sent1.Sent(), jc.IsTrue)
   137  
   138  	sent2, err := s.State.MetricBatch(unsent2.UUID())
   139  	c.Assert(err, jc.ErrorIsNil)
   140  	c.Assert(sent2.Sent(), jc.IsTrue)
   141  }
   142  
   143  func (s *MetricSenderSuite) TestSendingHandlesModelMeterStatus(c *gc.C) {
   144  	var sender testing.MockSender
   145  	sender.MeterStatusResponse = "RED"
   146  	now := time.Now()
   147  	s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.credUnit, Time: &now})
   148  	s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.meteredUnit, Time: &now})
   149  	s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.credUnit, Sent: true, Time: &now})
   150  	err := metricsender.SendMetrics(TestSenderBackend{s.State, s.Model}, &sender, s.clock, 10, true)
   151  	c.Assert(err, jc.ErrorIsNil)
   152  	c.Assert(sender.Data, gc.HasLen, 1)
   153  	c.Assert(sender.Data[0], gc.HasLen, 2)
   154  
   155  	meterStatus, err := s.State.ModelMeterStatus()
   156  	c.Assert(err, jc.ErrorIsNil)
   157  	c.Assert(meterStatus.Code.String(), gc.Equals, "RED")
   158  	c.Assert(meterStatus.Info, gc.Equals, "mocked response")
   159  }
   160  
   161  func (s *MetricSenderSuite) TestSendingHandlesEmptyModelMeterStatus(c *gc.C) {
   162  	var sender testing.MockSender
   163  	sender.MeterStatusResponse = ""
   164  	now := time.Now()
   165  	s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.credUnit, Time: &now})
   166  	s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.meteredUnit, Time: &now})
   167  	s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.credUnit, Sent: true, Time: &now})
   168  	err := metricsender.SendMetrics(TestSenderBackend{s.State, s.Model}, &sender, s.clock, 10, true)
   169  	c.Assert(err, jc.ErrorIsNil)
   170  	c.Assert(sender.Data, gc.HasLen, 1)
   171  	c.Assert(sender.Data[0], gc.HasLen, 2)
   172  
   173  	meterStatus, err := s.State.ModelMeterStatus()
   174  	c.Assert(err, jc.ErrorIsNil)
   175  	c.Assert(meterStatus.Code.String(), gc.Equals, "NOT AVAILABLE")
   176  	c.Assert(meterStatus.Info, gc.Equals, "")
   177  }
   178  
   179  // TestSendMetricsAbort creates 7 unsent metrics and
   180  // checks that the sending stops when no more batches are ack'ed.
   181  func (s *MetricSenderSuite) TestSendMetricsAbort(c *gc.C) {
   182  	sender := &testing.MockSender{}
   183  	now := time.Now()
   184  	metrics := make([]*state.MetricBatch, 7)
   185  	for i := 0; i < 7; i++ {
   186  		metrics[i] = s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.credUnit, Time: &now})
   187  	}
   188  
   189  	sender.IgnoreBatches(metrics[0:2]...)
   190  
   191  	// Send 4 batches per POST.
   192  	err := metricsender.SendMetrics(TestSenderBackend{s.State, s.Model}, sender, s.clock, 4, true)
   193  	c.Assert(err, jc.ErrorIsNil)
   194  	c.Assert(sender.Data, gc.HasLen, 4)
   195  
   196  	unsent := 0
   197  	sent := 0
   198  	for _, batch := range metrics {
   199  		b, err := s.State.MetricBatch(batch.UUID())
   200  		c.Assert(err, jc.ErrorIsNil)
   201  		if b.Sent() {
   202  			sent++
   203  		} else {
   204  			unsent++
   205  		}
   206  	}
   207  	c.Assert(sent, gc.Equals, 5)
   208  	c.Assert(unsent, gc.Equals, 2)
   209  }
   210  
   211  // TestHoldMetrics creates 2 unsent metrics and a sent metric
   212  // and checks that only the metric from the application with credentials is sent.
   213  // But both metrics are marked as sent.
   214  func (s *MetricSenderSuite) TestHoldMetrics(c *gc.C) {
   215  	var sender testing.MockSender
   216  	now := time.Now()
   217  	unsent1 := s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.credUnit, Time: &now})
   218  	unsent2 := s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.meteredUnit, Time: &now})
   219  	s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.credUnit, Sent: true, Time: &now})
   220  	err := metricsender.SendMetrics(TestSenderBackend{s.State, s.Model}, &sender, s.clock, 10, false)
   221  	c.Assert(err, jc.ErrorIsNil)
   222  	c.Assert(sender.Data, gc.HasLen, 1)
   223  	c.Assert(sender.Data[0], gc.HasLen, 1)
   224  	c.Assert(sender.Data[0][0].UUID, gc.Equals, unsent1.UUID())
   225  	sent1, err := s.State.MetricBatch(unsent1.UUID())
   226  	c.Assert(err, jc.ErrorIsNil)
   227  	c.Assert(sent1.Sent(), jc.IsTrue)
   228  
   229  	sent2, err := s.State.MetricBatch(unsent2.UUID())
   230  	c.Assert(err, jc.ErrorIsNil)
   231  	c.Assert(sent2.Sent(), jc.IsTrue)
   232  }
   233  
   234  func (s *MetricSenderSuite) TestHoldMetricsSetsMeterStatus(c *gc.C) {
   235  	var sender testing.MockSender
   236  	now := time.Now()
   237  	err := s.credUnit.SetMeterStatus("GREEN", "known starting point")
   238  	c.Assert(err, jc.ErrorIsNil)
   239  	err = s.meteredUnit.SetMeterStatus("GREEN", "known starting point")
   240  	c.Assert(err, jc.ErrorIsNil)
   241  	unsent1 := s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.credUnit, Time: &now})
   242  	s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.meteredUnit, Time: &now})
   243  	s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.credUnit, Sent: true, Time: &now})
   244  	err = metricsender.SendMetrics(TestSenderBackend{s.State, s.Model}, &sender, s.clock, 10, false)
   245  	c.Assert(err, jc.ErrorIsNil)
   246  	c.Assert(sender.Data, gc.HasLen, 1)
   247  	c.Assert(sender.Data[0], gc.HasLen, 1)
   248  	c.Assert(sender.Data[0][0].UUID, gc.Equals, unsent1.UUID())
   249  	msCred, err := s.credUnit.GetMeterStatus()
   250  	c.Assert(msCred.Code, gc.Equals, state.MeterGreen)
   251  	c.Assert(msCred.Info, gc.Equals, "known starting point")
   252  	msMetered, err := s.meteredUnit.GetMeterStatus()
   253  	c.Assert(msMetered.Code, gc.Equals, state.MeterRed)
   254  	c.Assert(msMetered.Info, gc.Equals, "transmit-vendor-metrics turned off")
   255  }
   256  
   257  // TestSendBulkMetrics tests the logic of splitting sends
   258  // into batches is done correctly. The batch size is changed
   259  // to send batches of 10 metrics. If we create 100 metrics 10 calls
   260  // will be made to the sender
   261  func (s *MetricSenderSuite) TestSendBulkMetrics(c *gc.C) {
   262  	var sender testing.MockSender
   263  	now := time.Now()
   264  	for i := 0; i < 100; i++ {
   265  		s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.credUnit, Time: &now})
   266  	}
   267  	err := metricsender.SendMetrics(TestSenderBackend{s.State, s.Model}, &sender, s.clock, 10, true)
   268  	c.Assert(err, jc.ErrorIsNil)
   269  
   270  	c.Assert(sender.Data, gc.HasLen, 10)
   271  	for _, d := range sender.Data {
   272  		c.Assert(d, gc.HasLen, 10)
   273  	}
   274  }
   275  
   276  // TestDontSendWithNopSender check that if the default sender
   277  // is nil we don't send anything, but still mark the items as sent
   278  func (s *MetricSenderSuite) TestDontSendWithNopSender(c *gc.C) {
   279  	now := time.Now()
   280  	for i := 0; i < 3; i++ {
   281  		s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.credUnit, Sent: false, Time: &now})
   282  	}
   283  	err := metricsender.SendMetrics(TestSenderBackend{s.State, s.Model}, metricsender.NopSender{}, s.clock, 10, true)
   284  	c.Assert(err, jc.ErrorIsNil)
   285  	sent, err := s.State.CountOfSentMetrics()
   286  	c.Assert(err, jc.ErrorIsNil)
   287  	c.Assert(sent, gc.Equals, 3)
   288  }
   289  
   290  func (s *MetricSenderSuite) TestFailureIncrementsConsecutiveFailures(c *gc.C) {
   291  	sender := &testing.ErrorSender{Err: errors.New("something went wrong")}
   292  	now := time.Now()
   293  	for i := 0; i < 3; i++ {
   294  		s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.credUnit, Sent: false, Time: &now})
   295  	}
   296  	err := metricsender.SendMetrics(TestSenderBackend{s.State, s.Model}, sender, s.clock, 1, true)
   297  	c.Assert(err, gc.ErrorMatches, "something went wrong")
   298  	mm, err := s.State.MetricsManager()
   299  	c.Assert(err, jc.ErrorIsNil)
   300  	c.Assert(mm.ConsecutiveErrors(), gc.Equals, 1)
   301  }
   302  
   303  func (s *MetricSenderSuite) TestFailuresResetOnSuccessfulSend(c *gc.C) {
   304  	mm, err := s.State.MetricsManager()
   305  	c.Assert(err, jc.ErrorIsNil)
   306  	err = mm.IncrementConsecutiveErrors()
   307  	c.Assert(err, jc.ErrorIsNil)
   308  	now := time.Now()
   309  	for i := 0; i < 3; i++ {
   310  		s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.credUnit, Sent: false, Time: &now})
   311  	}
   312  	err = metricsender.SendMetrics(TestSenderBackend{s.State, s.Model}, metricsender.NopSender{}, s.clock, 10, true)
   313  	c.Assert(err, jc.ErrorIsNil)
   314  	mm, err = s.State.MetricsManager()
   315  	c.Assert(err, jc.ErrorIsNil)
   316  	c.Assert(mm.ConsecutiveErrors(), gc.Equals, 0)
   317  }