github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/metrics_test.go (about)

     1  // Copyright 2013, 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/names"
    11  	jc "github.com/juju/testing/checkers"
    12  	"github.com/juju/utils"
    13  	gc "gopkg.in/check.v1"
    14  
    15  	"github.com/juju/juju/state"
    16  	"github.com/juju/juju/testing/factory"
    17  )
    18  
    19  type MetricSuite struct {
    20  	ConnSuite
    21  	unit         *state.Unit
    22  	service      *state.Service
    23  	meteredCharm *state.Charm
    24  }
    25  
    26  var _ = gc.Suite(&MetricSuite{})
    27  
    28  func (s *MetricSuite) SetUpTest(c *gc.C) {
    29  	s.ConnSuite.SetUpTest(c)
    30  	s.assertAddUnit(c)
    31  }
    32  
    33  func (s *MetricSuite) TestAddNoMetrics(c *gc.C) {
    34  	now := state.NowToTheSecond()
    35  	_, err := s.State.AddMetrics(state.BatchParam{
    36  		UUID:     utils.MustNewUUID().String(),
    37  		CharmURL: s.meteredCharm.URL().String(),
    38  		Created:  now,
    39  		Metrics:  []state.Metric{},
    40  		Unit:     s.unit.UnitTag(),
    41  	})
    42  	c.Assert(err, gc.ErrorMatches, "cannot add a batch of 0 metrics")
    43  }
    44  
    45  func removeUnit(c *gc.C, unit *state.Unit) {
    46  	ensureUnitDead(c, unit)
    47  	err := unit.Remove()
    48  	c.Assert(err, jc.ErrorIsNil)
    49  }
    50  
    51  func ensureUnitDead(c *gc.C, unit *state.Unit) {
    52  	err := unit.EnsureDead()
    53  	c.Assert(err, jc.ErrorIsNil)
    54  }
    55  
    56  func (s *MetricSuite) assertAddUnit(c *gc.C) {
    57  	s.meteredCharm = s.Factory.MakeCharm(c, &factory.CharmParams{Name: "metered", URL: "cs:quantal/metered"})
    58  	s.service = s.Factory.MakeService(c, &factory.ServiceParams{Charm: s.meteredCharm})
    59  	s.unit = s.Factory.MakeUnit(c, &factory.UnitParams{Service: s.service, SetCharmURL: true})
    60  }
    61  
    62  func (s *MetricSuite) TestAddMetric(c *gc.C) {
    63  	now := state.NowToTheSecond()
    64  	modelUUID := s.State.ModelUUID()
    65  	m := state.Metric{"pings", "5", now}
    66  	metricBatch, err := s.State.AddMetrics(
    67  		state.BatchParam{
    68  			UUID:     utils.MustNewUUID().String(),
    69  			Created:  now,
    70  			CharmURL: s.meteredCharm.URL().String(),
    71  			Metrics:  []state.Metric{m},
    72  			Unit:     s.unit.UnitTag(),
    73  		},
    74  	)
    75  	c.Assert(err, jc.ErrorIsNil)
    76  	c.Assert(metricBatch.Unit(), gc.Equals, "metered/0")
    77  	c.Assert(metricBatch.ModelUUID(), gc.Equals, modelUUID)
    78  	c.Assert(metricBatch.CharmURL(), gc.Equals, "cs:quantal/metered")
    79  	c.Assert(metricBatch.Sent(), jc.IsFalse)
    80  	c.Assert(metricBatch.Created(), gc.Equals, now)
    81  	c.Assert(metricBatch.Metrics(), gc.HasLen, 1)
    82  
    83  	metric := metricBatch.Metrics()[0]
    84  	c.Assert(metric.Key, gc.Equals, "pings")
    85  	c.Assert(metric.Value, gc.Equals, "5")
    86  	c.Assert(metric.Time.Equal(now), jc.IsTrue)
    87  
    88  	saved, err := s.State.MetricBatch(metricBatch.UUID())
    89  	c.Assert(err, jc.ErrorIsNil)
    90  	c.Assert(saved.Unit(), gc.Equals, "metered/0")
    91  	c.Assert(metricBatch.CharmURL(), gc.Equals, "cs:quantal/metered")
    92  	c.Assert(saved.Sent(), jc.IsFalse)
    93  	c.Assert(saved.Metrics(), gc.HasLen, 1)
    94  	metric = saved.Metrics()[0]
    95  	c.Assert(metric.Key, gc.Equals, "pings")
    96  	c.Assert(metric.Value, gc.Equals, "5")
    97  	c.Assert(metric.Time.Equal(now), jc.IsTrue)
    98  }
    99  
   100  func (s *MetricSuite) TestAddMetricNonExistentUnit(c *gc.C) {
   101  	removeUnit(c, s.unit)
   102  	now := state.NowToTheSecond()
   103  	m := state.Metric{"pings", "5", now}
   104  	unitTag := names.NewUnitTag("test/0")
   105  	_, err := s.State.AddMetrics(
   106  		state.BatchParam{
   107  			UUID:     utils.MustNewUUID().String(),
   108  			Created:  now,
   109  			CharmURL: s.meteredCharm.URL().String(),
   110  			Metrics:  []state.Metric{m},
   111  			Unit:     unitTag,
   112  		},
   113  	)
   114  	c.Assert(err, gc.ErrorMatches, ".*not found")
   115  }
   116  
   117  func (s *MetricSuite) TestAddMetricDeadUnit(c *gc.C) {
   118  	ensureUnitDead(c, s.unit)
   119  	now := state.NowToTheSecond()
   120  	m := state.Metric{"pings", "5", now}
   121  	_, err := s.State.AddMetrics(
   122  		state.BatchParam{
   123  			UUID:     utils.MustNewUUID().String(),
   124  			Created:  now,
   125  			CharmURL: s.meteredCharm.URL().String(),
   126  			Metrics:  []state.Metric{m},
   127  			Unit:     s.unit.UnitTag(),
   128  		},
   129  	)
   130  	c.Assert(err, gc.ErrorMatches, `metered/0 not found`)
   131  }
   132  
   133  func (s *MetricSuite) TestSetMetricSent(c *gc.C) {
   134  	now := state.NowToTheSecond()
   135  	m := state.Metric{"pings", "5", now}
   136  	added, err := s.State.AddMetrics(
   137  		state.BatchParam{
   138  			UUID:     utils.MustNewUUID().String(),
   139  			Created:  now,
   140  			CharmURL: s.meteredCharm.URL().String(),
   141  			Metrics:  []state.Metric{m},
   142  			Unit:     s.unit.UnitTag(),
   143  		},
   144  	)
   145  	c.Assert(err, jc.ErrorIsNil)
   146  	saved, err := s.State.MetricBatch(added.UUID())
   147  	c.Assert(err, jc.ErrorIsNil)
   148  	err = saved.SetSent(time.Now())
   149  	c.Assert(err, jc.ErrorIsNil)
   150  	c.Assert(saved.Sent(), jc.IsTrue)
   151  	saved, err = s.State.MetricBatch(added.UUID())
   152  	c.Assert(err, jc.ErrorIsNil)
   153  	c.Assert(saved.Sent(), jc.IsTrue)
   154  }
   155  
   156  func (s *MetricSuite) TestCleanupMetrics(c *gc.C) {
   157  	oldTime := time.Now().Add(-(time.Hour * 25))
   158  	now := time.Now()
   159  	m := state.Metric{"pings", "5", oldTime}
   160  	oldMetric1, err := s.State.AddMetrics(
   161  		state.BatchParam{
   162  			UUID:     utils.MustNewUUID().String(),
   163  			Created:  now,
   164  			CharmURL: s.meteredCharm.URL().String(),
   165  			Metrics:  []state.Metric{m},
   166  			Unit:     s.unit.UnitTag(),
   167  		},
   168  	)
   169  	c.Assert(err, jc.ErrorIsNil)
   170  	oldMetric1.SetSent(time.Now().Add(-25 * time.Hour))
   171  
   172  	oldMetric2, err := s.State.AddMetrics(
   173  		state.BatchParam{
   174  			UUID:     utils.MustNewUUID().String(),
   175  			Created:  now,
   176  			CharmURL: s.meteredCharm.URL().String(),
   177  			Metrics:  []state.Metric{m},
   178  			Unit:     s.unit.UnitTag(),
   179  		},
   180  	)
   181  	c.Assert(err, jc.ErrorIsNil)
   182  	oldMetric2.SetSent(time.Now().Add(-25 * time.Hour))
   183  
   184  	m = state.Metric{"pings", "5", now}
   185  	newMetric, err := s.State.AddMetrics(
   186  		state.BatchParam{
   187  			UUID:     utils.MustNewUUID().String(),
   188  			Created:  now,
   189  			CharmURL: s.meteredCharm.URL().String(),
   190  			Metrics:  []state.Metric{m},
   191  			Unit:     s.unit.UnitTag(),
   192  		},
   193  	)
   194  	c.Assert(err, jc.ErrorIsNil)
   195  	newMetric.SetSent(time.Now())
   196  	err = s.State.CleanupOldMetrics()
   197  	c.Assert(err, jc.ErrorIsNil)
   198  
   199  	_, err = s.State.MetricBatch(newMetric.UUID())
   200  	c.Assert(err, jc.ErrorIsNil)
   201  
   202  	_, err = s.State.MetricBatch(oldMetric1.UUID())
   203  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   204  
   205  	_, err = s.State.MetricBatch(oldMetric2.UUID())
   206  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   207  }
   208  
   209  func (s *MetricSuite) TestCleanupNoMetrics(c *gc.C) {
   210  	err := s.State.CleanupOldMetrics()
   211  	c.Assert(err, jc.ErrorIsNil)
   212  }
   213  
   214  func (s *MetricSuite) TestCleanupMetricsIgnoreNotSent(c *gc.C) {
   215  	oldTime := time.Now().Add(-(time.Hour * 25))
   216  	m := state.Metric{"pings", "5", oldTime}
   217  	oldMetric, err := s.State.AddMetrics(
   218  		state.BatchParam{
   219  			UUID:     utils.MustNewUUID().String(),
   220  			Created:  oldTime,
   221  			CharmURL: s.meteredCharm.URL().String(),
   222  			Metrics:  []state.Metric{m},
   223  			Unit:     s.unit.UnitTag(),
   224  		},
   225  	)
   226  	c.Assert(err, jc.ErrorIsNil)
   227  
   228  	now := time.Now()
   229  	m = state.Metric{"pings", "5", now}
   230  	newMetric, err := s.State.AddMetrics(
   231  		state.BatchParam{
   232  			UUID:     utils.MustNewUUID().String(),
   233  			Created:  now,
   234  			CharmURL: s.meteredCharm.URL().String(),
   235  			Metrics:  []state.Metric{m},
   236  			Unit:     s.unit.UnitTag(),
   237  		},
   238  	)
   239  	c.Assert(err, jc.ErrorIsNil)
   240  	newMetric.SetSent(time.Now())
   241  	err = s.State.CleanupOldMetrics()
   242  	c.Assert(err, jc.ErrorIsNil)
   243  
   244  	_, err = s.State.MetricBatch(newMetric.UUID())
   245  	c.Assert(err, jc.ErrorIsNil)
   246  
   247  	_, err = s.State.MetricBatch(oldMetric.UUID())
   248  	c.Assert(err, jc.ErrorIsNil)
   249  }
   250  
   251  func (s *MetricSuite) TestAllMetricBatches(c *gc.C) {
   252  	now := state.NowToTheSecond()
   253  	m := state.Metric{"pings", "5", now}
   254  	_, err := s.State.AddMetrics(
   255  		state.BatchParam{
   256  			UUID:     utils.MustNewUUID().String(),
   257  			Created:  now,
   258  			CharmURL: s.meteredCharm.URL().String(),
   259  			Metrics:  []state.Metric{m},
   260  			Unit:     s.unit.UnitTag(),
   261  		},
   262  	)
   263  	c.Assert(err, jc.ErrorIsNil)
   264  	metricBatches, err := s.State.AllMetricBatches()
   265  	c.Assert(err, jc.ErrorIsNil)
   266  	c.Assert(metricBatches, gc.HasLen, 1)
   267  	c.Assert(metricBatches[0].Unit(), gc.Equals, "metered/0")
   268  	c.Assert(metricBatches[0].CharmURL(), gc.Equals, "cs:quantal/metered")
   269  	c.Assert(metricBatches[0].Sent(), jc.IsFalse)
   270  	c.Assert(metricBatches[0].Metrics(), gc.HasLen, 1)
   271  }
   272  
   273  func (s *MetricSuite) TestAllMetricBatchesCustomCharmURLAndUUID(c *gc.C) {
   274  	now := state.NowToTheSecond()
   275  	m := state.Metric{"pings", "5", now}
   276  	uuid := utils.MustNewUUID().String()
   277  	charmUrl := "cs:quantal/metered"
   278  	_, err := s.State.AddMetrics(
   279  		state.BatchParam{
   280  			UUID:     uuid,
   281  			Created:  now,
   282  			CharmURL: charmUrl,
   283  			Metrics:  []state.Metric{m},
   284  			Unit:     s.unit.UnitTag(),
   285  		},
   286  	)
   287  	c.Assert(err, jc.ErrorIsNil)
   288  	metricBatches, err := s.State.AllMetricBatches()
   289  	c.Assert(err, jc.ErrorIsNil)
   290  	c.Assert(metricBatches, gc.HasLen, 1)
   291  	c.Assert(metricBatches[0].Unit(), gc.Equals, "metered/0")
   292  	c.Assert(metricBatches[0].UUID(), gc.Equals, uuid)
   293  	c.Assert(metricBatches[0].CharmURL(), gc.Equals, charmUrl)
   294  	c.Assert(metricBatches[0].Sent(), jc.IsFalse)
   295  	c.Assert(metricBatches[0].Metrics(), gc.HasLen, 1)
   296  }
   297  
   298  func (s *MetricSuite) TestMetricCredentials(c *gc.C) {
   299  	now := state.NowToTheSecond()
   300  	m := state.Metric{"pings", "5", now}
   301  	err := s.service.SetMetricCredentials([]byte("hello there"))
   302  	c.Assert(err, gc.IsNil)
   303  	_, err = s.State.AddMetrics(
   304  		state.BatchParam{
   305  			UUID:     utils.MustNewUUID().String(),
   306  			Created:  now,
   307  			CharmURL: s.meteredCharm.URL().String(),
   308  			Metrics:  []state.Metric{m},
   309  			Unit:     s.unit.UnitTag(),
   310  		},
   311  	)
   312  	c.Assert(err, jc.ErrorIsNil)
   313  	metricBatches, err := s.State.AllMetricBatches()
   314  	c.Assert(err, jc.ErrorIsNil)
   315  	c.Assert(metricBatches, gc.HasLen, 1)
   316  	c.Assert(metricBatches[0].Credentials(), gc.DeepEquals, []byte("hello there"))
   317  }
   318  
   319  // TestCountMetrics asserts the correct values are returned
   320  // by CountOfUnsentMetrics and CountOfSentMetrics.
   321  func (s *MetricSuite) TestCountMetrics(c *gc.C) {
   322  	now := time.Now()
   323  	m := []state.Metric{{Key: "pings", Value: "123", Time: now}}
   324  	s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: false, Time: &now, Metrics: m})
   325  	s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: false, Time: &now, Metrics: m})
   326  	s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: true, Time: &now, Metrics: m})
   327  	sent, err := s.State.CountOfSentMetrics()
   328  	c.Assert(err, jc.ErrorIsNil)
   329  	c.Assert(sent, gc.Equals, 1)
   330  	unsent, err := s.State.CountOfUnsentMetrics()
   331  	c.Assert(err, jc.ErrorIsNil)
   332  	c.Assert(unsent, gc.Equals, 2)
   333  	c.Assert(unsent+sent, gc.Equals, 3)
   334  }
   335  
   336  func (s *MetricSuite) TestSetMetricBatchesSent(c *gc.C) {
   337  	now := time.Now()
   338  	metrics := make([]*state.MetricBatch, 3)
   339  	for i := range metrics {
   340  		m := []state.Metric{{Key: "pings", Value: "123", Time: now}}
   341  		metrics[i] = s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: false, Time: &now, Metrics: m})
   342  	}
   343  	uuids := make([]string, len(metrics))
   344  	for i, m := range metrics {
   345  		uuids[i] = m.UUID()
   346  	}
   347  	err := s.State.SetMetricBatchesSent(uuids)
   348  	c.Assert(err, jc.ErrorIsNil)
   349  	sent, err := s.State.CountOfSentMetrics()
   350  	c.Assert(err, jc.ErrorIsNil)
   351  	c.Assert(sent, gc.Equals, 3)
   352  
   353  }
   354  
   355  func (s *MetricSuite) TestMetricsToSend(c *gc.C) {
   356  	now := state.NowToTheSecond()
   357  	m := []state.Metric{{Key: "pings", Value: "123", Time: now}}
   358  	s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: false, Time: &now, Metrics: m})
   359  	s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: false, Time: &now, Metrics: m})
   360  	s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: true, Time: &now, Metrics: m})
   361  	result, err := s.State.MetricsToSend(5)
   362  	c.Assert(err, jc.ErrorIsNil)
   363  	c.Assert(result, gc.HasLen, 2)
   364  }
   365  
   366  // TestMetricsToSendBatches checks that metrics are properly batched.
   367  func (s *MetricSuite) TestMetricsToSendBatches(c *gc.C) {
   368  	now := state.NowToTheSecond()
   369  	for i := 0; i < 6; i++ {
   370  		m := []state.Metric{{Key: "pings", Value: "123", Time: now}}
   371  		s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: false, Time: &now, Metrics: m})
   372  	}
   373  	for i := 0; i < 4; i++ {
   374  		m := []state.Metric{{Key: "pings", Value: "123", Time: now}}
   375  		s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: true, Time: &now, Metrics: m})
   376  	}
   377  	for i := 0; i < 3; i++ {
   378  		result, err := s.State.MetricsToSend(2)
   379  		c.Assert(err, jc.ErrorIsNil)
   380  		c.Assert(result, gc.HasLen, 2)
   381  		uuids := make([]string, len(result))
   382  		for i, m := range result {
   383  			uuids[i] = m.UUID()
   384  		}
   385  		s.State.SetMetricBatchesSent(uuids)
   386  	}
   387  	result, err := s.State.MetricsToSend(2)
   388  	c.Assert(err, jc.ErrorIsNil)
   389  	c.Assert(result, gc.HasLen, 0)
   390  }
   391  
   392  func (s *MetricSuite) TestMetricValidation(c *gc.C) {
   393  	nonMeteredUnit := s.Factory.MakeUnit(c, &factory.UnitParams{SetCharmURL: true})
   394  	meteredService := s.Factory.MakeService(c, &factory.ServiceParams{Name: "metered-service", Charm: s.meteredCharm})
   395  	meteredUnit := s.Factory.MakeUnit(c, &factory.UnitParams{Service: meteredService, SetCharmURL: true})
   396  	dyingUnit, err := meteredService.AddUnit()
   397  	c.Assert(err, jc.ErrorIsNil)
   398  	err = dyingUnit.SetCharmURL(s.meteredCharm.URL())
   399  	c.Assert(err, jc.ErrorIsNil)
   400  	err = dyingUnit.Destroy()
   401  	c.Assert(err, jc.ErrorIsNil)
   402  	now := time.Now()
   403  	tests := []struct {
   404  		about   string
   405  		metrics []state.Metric
   406  		unit    *state.Unit
   407  		err     string
   408  	}{{
   409  		"assert non metered unit returns an error",
   410  		[]state.Metric{{"metric-key", "1", now}},
   411  		nonMeteredUnit,
   412  		"charm doesn't implement metrics",
   413  	}, {
   414  		"assert metric with no errors and passes validation",
   415  		[]state.Metric{{"pings", "1", now}},
   416  		meteredUnit,
   417  		"",
   418  	}, {
   419  		"assert valid metric fails on dying unit",
   420  		[]state.Metric{{"pings", "1", now}},
   421  		dyingUnit,
   422  		"unit \"metered-service/1\" not found",
   423  	}, {
   424  		"assert charm doesn't implement key returns error",
   425  		[]state.Metric{{"not-implemented", "1", now}},
   426  		meteredUnit,
   427  		`metric "not-implemented" not defined`,
   428  	}, {
   429  		"assert invalid value returns error",
   430  		[]state.Metric{{"pings", "foobar", now}},
   431  		meteredUnit,
   432  		`invalid value type: expected float, got "foobar"`,
   433  	}, {
   434  		"long value returns error",
   435  		[]state.Metric{{"pings", "3.141592653589793238462643383279", now}},
   436  		meteredUnit,
   437  		`metric value is too large`,
   438  	}, {
   439  		"negative value returns error",
   440  		[]state.Metric{{"pings", "-42.0", now}},
   441  		meteredUnit,
   442  		`invalid value: value must be greater or equal to zero, got -42.0`,
   443  	}, {
   444  		"non-float value returns an error",
   445  		[]state.Metric{{"pings", "abcd", now}},
   446  		meteredUnit,
   447  		`invalid value type: expected float, got "abcd"`,
   448  	}}
   449  	for i, t := range tests {
   450  		c.Logf("test %d: %s", i, t.about)
   451  		chURL, ok := t.unit.CharmURL()
   452  		c.Assert(ok, jc.IsTrue)
   453  		_, err := s.State.AddMetrics(
   454  			state.BatchParam{
   455  				UUID:     utils.MustNewUUID().String(),
   456  				Created:  now,
   457  				CharmURL: chURL.String(),
   458  				Metrics:  t.metrics,
   459  				Unit:     t.unit.UnitTag(),
   460  			},
   461  		)
   462  		if t.err == "" {
   463  			c.Assert(err, jc.ErrorIsNil)
   464  		} else {
   465  			c.Assert(err, gc.ErrorMatches, t.err)
   466  		}
   467  	}
   468  }
   469  
   470  func (s *MetricSuite) TestMetricsAcrossEnvironments(c *gc.C) {
   471  	now := state.NowToTheSecond().Add(-48 * time.Hour)
   472  	m := state.Metric{"pings", "5", now}
   473  	m1, err := s.State.AddMetrics(
   474  		state.BatchParam{
   475  			UUID:     utils.MustNewUUID().String(),
   476  			Created:  now,
   477  			CharmURL: s.meteredCharm.URL().String(),
   478  			Metrics:  []state.Metric{m},
   479  			Unit:     s.unit.UnitTag(),
   480  		},
   481  	)
   482  	c.Assert(err, jc.ErrorIsNil)
   483  
   484  	st := s.Factory.MakeModel(c, nil)
   485  	defer st.Close()
   486  	f := factory.NewFactory(st)
   487  	meteredCharm := f.MakeCharm(c, &factory.CharmParams{Name: "metered", URL: "cs:quantal/metered"})
   488  	service := f.MakeService(c, &factory.ServiceParams{Charm: meteredCharm})
   489  	unit := f.MakeUnit(c, &factory.UnitParams{Service: service, SetCharmURL: true})
   490  	m2, err := s.State.AddMetrics(
   491  		state.BatchParam{
   492  			UUID:     utils.MustNewUUID().String(),
   493  			Created:  now,
   494  			CharmURL: s.meteredCharm.URL().String(),
   495  			Metrics:  []state.Metric{m},
   496  			Unit:     unit.UnitTag(),
   497  		},
   498  	)
   499  	c.Assert(err, jc.ErrorIsNil)
   500  
   501  	batches, err := s.State.AllMetricBatches()
   502  	c.Assert(err, jc.ErrorIsNil)
   503  	c.Assert(batches, gc.HasLen, 2)
   504  
   505  	unsent, err := s.State.CountOfUnsentMetrics()
   506  	c.Assert(err, jc.ErrorIsNil)
   507  	c.Assert(unsent, gc.Equals, 2)
   508  
   509  	toSend, err := s.State.MetricsToSend(10)
   510  	c.Assert(err, jc.ErrorIsNil)
   511  	c.Assert(toSend, gc.HasLen, 2)
   512  
   513  	err = m1.SetSent(time.Now().Add(-25 * time.Hour))
   514  	c.Assert(err, jc.ErrorIsNil)
   515  	err = m2.SetSent(time.Now().Add(-25 * time.Hour))
   516  	c.Assert(err, jc.ErrorIsNil)
   517  
   518  	sent, err := s.State.CountOfSentMetrics()
   519  	c.Assert(err, jc.ErrorIsNil)
   520  	c.Assert(sent, gc.Equals, 2)
   521  
   522  	err = s.State.CleanupOldMetrics()
   523  	c.Assert(err, jc.ErrorIsNil)
   524  
   525  	batches, err = s.State.AllMetricBatches()
   526  	c.Assert(err, jc.ErrorIsNil)
   527  	c.Assert(batches, gc.HasLen, 0)
   528  }
   529  
   530  func (s *MetricSuite) TestAddMetricDuplicateUUID(c *gc.C) {
   531  	now := state.NowToTheSecond()
   532  	mUUID := utils.MustNewUUID().String()
   533  	_, err := s.State.AddMetrics(
   534  		state.BatchParam{
   535  			UUID:     mUUID,
   536  			Created:  now,
   537  			CharmURL: s.meteredCharm.URL().String(),
   538  			Metrics:  []state.Metric{{"pings", "5", now}},
   539  			Unit:     s.unit.UnitTag(),
   540  		},
   541  	)
   542  	c.Assert(err, jc.ErrorIsNil)
   543  
   544  	_, err = s.State.AddMetrics(
   545  		state.BatchParam{
   546  			UUID:     mUUID,
   547  			Created:  now,
   548  			CharmURL: s.meteredCharm.URL().String(),
   549  			Metrics:  []state.Metric{{"pings", "10", now}},
   550  			Unit:     s.unit.UnitTag(),
   551  		},
   552  	)
   553  	c.Assert(err, gc.ErrorMatches, "metrics batch .* already exists")
   554  }
   555  
   556  func (s *MetricSuite) TestAddBuiltInMetric(c *gc.C) {
   557  	tests := []struct {
   558  		about         string
   559  		value         string
   560  		expectedError string
   561  	}{{
   562  		about: "adding a positive value must succeed",
   563  		value: "5",
   564  	}, {
   565  		about:         "negative values return an error",
   566  		value:         "-42.0",
   567  		expectedError: "invalid value: value must be greater or equal to zero, got -42.0",
   568  	}, {
   569  		about:         "non-float values return an error",
   570  		value:         "abcd",
   571  		expectedError: `invalid value type: expected float, got "abcd"`,
   572  	}, {
   573  		about:         "long values return an error",
   574  		value:         "1234567890123456789012345678901234567890",
   575  		expectedError: "metric value is too large",
   576  	},
   577  	}
   578  	for _, test := range tests {
   579  		c.Logf("running test: %v", test.about)
   580  		now := state.NowToTheSecond()
   581  		modelUUID := s.State.ModelUUID()
   582  		m := state.Metric{"juju-units", test.value, now}
   583  		metricBatch, err := s.State.AddMetrics(
   584  			state.BatchParam{
   585  				UUID:     utils.MustNewUUID().String(),
   586  				Created:  now,
   587  				CharmURL: s.meteredCharm.URL().String(),
   588  				Metrics:  []state.Metric{m},
   589  				Unit:     s.unit.UnitTag(),
   590  			},
   591  		)
   592  		if test.expectedError == "" {
   593  			c.Assert(err, jc.ErrorIsNil)
   594  			c.Assert(metricBatch.Unit(), gc.Equals, "metered/0")
   595  			c.Assert(metricBatch.ModelUUID(), gc.Equals, modelUUID)
   596  			c.Assert(metricBatch.CharmURL(), gc.Equals, "cs:quantal/metered")
   597  			c.Assert(metricBatch.Sent(), jc.IsFalse)
   598  			c.Assert(metricBatch.Created(), gc.Equals, now)
   599  			c.Assert(metricBatch.Metrics(), gc.HasLen, 1)
   600  
   601  			metric := metricBatch.Metrics()[0]
   602  			c.Assert(metric.Key, gc.Equals, "juju-units")
   603  			c.Assert(metric.Value, gc.Equals, test.value)
   604  			c.Assert(metric.Time.Equal(now), jc.IsTrue)
   605  
   606  			saved, err := s.State.MetricBatch(metricBatch.UUID())
   607  			c.Assert(err, jc.ErrorIsNil)
   608  			c.Assert(saved.Unit(), gc.Equals, "metered/0")
   609  			c.Assert(metricBatch.CharmURL(), gc.Equals, "cs:quantal/metered")
   610  			c.Assert(saved.Sent(), jc.IsFalse)
   611  			c.Assert(saved.Metrics(), gc.HasLen, 1)
   612  			metric = saved.Metrics()[0]
   613  			c.Assert(metric.Key, gc.Equals, "juju-units")
   614  			c.Assert(metric.Value, gc.Equals, test.value)
   615  			c.Assert(metric.Time.Equal(now), jc.IsTrue)
   616  		} else {
   617  			c.Assert(err, gc.ErrorMatches, test.expectedError)
   618  		}
   619  	}
   620  }
   621  
   622  func (s *MetricSuite) TestUnitMetricBatchesReturnsJustLocal(c *gc.C) {
   623  	now := state.NowToTheSecond()
   624  	m := state.Metric{"pings", "5", now}
   625  	_, err := s.State.AddMetrics(
   626  		state.BatchParam{
   627  			UUID:     utils.MustNewUUID().String(),
   628  			Created:  now,
   629  			CharmURL: s.meteredCharm.URL().String(),
   630  			Metrics:  []state.Metric{m},
   631  			Unit:     s.unit.UnitTag(),
   632  		},
   633  	)
   634  	c.Assert(err, jc.ErrorIsNil)
   635  	localMeteredCharm := s.Factory.MakeCharm(c, &factory.CharmParams{Name: "metered", URL: "local:quantal/metered"})
   636  	service := s.Factory.MakeService(c, &factory.ServiceParams{Name: "localmetered", Charm: localMeteredCharm})
   637  	unit := s.Factory.MakeUnit(c, &factory.UnitParams{Service: service, SetCharmURL: true})
   638  	_, err = s.State.AddMetrics(
   639  		state.BatchParam{
   640  			UUID:     utils.MustNewUUID().String(),
   641  			Created:  now,
   642  			CharmURL: localMeteredCharm.URL().String(),
   643  			Metrics:  []state.Metric{m},
   644  			Unit:     unit.UnitTag(),
   645  		},
   646  	)
   647  
   648  	c.Assert(err, jc.ErrorIsNil)
   649  	metricBatches, err := s.State.MetricBatchesForUnit("metered/0")
   650  	c.Assert(metricBatches, gc.HasLen, 0)
   651  	metricBatches, err = s.State.MetricBatchesForUnit("localmetered/0")
   652  	c.Assert(metricBatches, gc.HasLen, 1)
   653  }
   654  
   655  type MetricLocalCharmSuite struct {
   656  	ConnSuite
   657  	unit         *state.Unit
   658  	service      *state.Service
   659  	meteredCharm *state.Charm
   660  }
   661  
   662  var _ = gc.Suite(&MetricLocalCharmSuite{})
   663  
   664  func (s *MetricLocalCharmSuite) SetUpTest(c *gc.C) {
   665  	s.ConnSuite.SetUpTest(c)
   666  	s.assertAddLocalUnit(c)
   667  }
   668  
   669  func (s *MetricLocalCharmSuite) assertAddLocalUnit(c *gc.C) {
   670  	s.meteredCharm = s.Factory.MakeCharm(c, &factory.CharmParams{Name: "metered", URL: "local:quantal/metered"})
   671  	s.service = s.Factory.MakeService(c, &factory.ServiceParams{Charm: s.meteredCharm})
   672  	s.unit = s.Factory.MakeUnit(c, &factory.UnitParams{Service: s.service, SetCharmURL: true})
   673  }
   674  
   675  func (s *MetricLocalCharmSuite) TestUnitMetricBatches(c *gc.C) {
   676  	now := state.NowToTheSecond()
   677  	m := state.Metric{"pings", "5", now}
   678  	m2 := state.Metric{"pings", "10", now}
   679  	_, err := s.State.AddMetrics(
   680  		state.BatchParam{
   681  			UUID:     utils.MustNewUUID().String(),
   682  			Created:  now,
   683  			CharmURL: s.meteredCharm.URL().String(),
   684  			Metrics:  []state.Metric{m},
   685  			Unit:     s.unit.UnitTag(),
   686  		},
   687  	)
   688  	c.Assert(err, jc.ErrorIsNil)
   689  	newUnit, err := s.service.AddUnit()
   690  	c.Assert(err, jc.ErrorIsNil)
   691  	_, err = s.State.AddMetrics(
   692  		state.BatchParam{
   693  			UUID:     utils.MustNewUUID().String(),
   694  			Created:  now,
   695  			CharmURL: s.meteredCharm.URL().String(),
   696  			Metrics:  []state.Metric{m2},
   697  			Unit:     newUnit.UnitTag(),
   698  		},
   699  	)
   700  	c.Assert(err, jc.ErrorIsNil)
   701  
   702  	metricBatches, err := s.State.MetricBatchesForUnit("metered/0")
   703  	c.Assert(err, jc.ErrorIsNil)
   704  	c.Assert(metricBatches, gc.HasLen, 1)
   705  	c.Assert(metricBatches[0].Unit(), gc.Equals, "metered/0")
   706  	c.Assert(metricBatches[0].CharmURL(), gc.Equals, "local:quantal/metered")
   707  	c.Assert(metricBatches[0].Sent(), jc.IsFalse)
   708  	c.Assert(metricBatches[0].Metrics(), gc.HasLen, 1)
   709  	c.Assert(metricBatches[0].Metrics()[0].Value, gc.Equals, "5")
   710  
   711  	metricBatches, err = s.State.MetricBatchesForUnit("metered/1")
   712  	c.Assert(err, jc.ErrorIsNil)
   713  	c.Assert(metricBatches, gc.HasLen, 1)
   714  	c.Assert(metricBatches[0].Unit(), gc.Equals, "metered/1")
   715  	c.Assert(metricBatches[0].CharmURL(), gc.Equals, "local:quantal/metered")
   716  	c.Assert(metricBatches[0].Sent(), jc.IsFalse)
   717  	c.Assert(metricBatches[0].Metrics(), gc.HasLen, 1)
   718  	c.Assert(metricBatches[0].Metrics()[0].Value, gc.Equals, "10")
   719  }
   720  
   721  func (s *MetricLocalCharmSuite) TestServiceMetricBatches(c *gc.C) {
   722  	now := state.NowToTheSecond()
   723  	m := state.Metric{"pings", "5", now}
   724  	m2 := state.Metric{"pings", "10", now}
   725  	_, err := s.State.AddMetrics(
   726  		state.BatchParam{
   727  			UUID:     utils.MustNewUUID().String(),
   728  			Created:  now,
   729  			CharmURL: s.meteredCharm.URL().String(),
   730  			Metrics:  []state.Metric{m},
   731  			Unit:     s.unit.UnitTag(),
   732  		},
   733  	)
   734  	c.Assert(err, jc.ErrorIsNil)
   735  	newUnit, err := s.service.AddUnit()
   736  	c.Assert(err, jc.ErrorIsNil)
   737  	_, err = s.State.AddMetrics(
   738  		state.BatchParam{
   739  			UUID:     utils.MustNewUUID().String(),
   740  			Created:  now,
   741  			CharmURL: s.meteredCharm.URL().String(),
   742  			Metrics:  []state.Metric{m2},
   743  			Unit:     newUnit.UnitTag(),
   744  		},
   745  	)
   746  	c.Assert(err, jc.ErrorIsNil)
   747  
   748  	metricBatches, err := s.State.MetricBatchesForService("metered")
   749  	c.Assert(err, jc.ErrorIsNil)
   750  	c.Assert(metricBatches, gc.HasLen, 2)
   751  
   752  	c.Assert(metricBatches[0].Unit(), gc.Equals, "metered/0")
   753  	c.Assert(metricBatches[0].CharmURL(), gc.Equals, "local:quantal/metered")
   754  	c.Assert(metricBatches[0].Sent(), jc.IsFalse)
   755  	c.Assert(metricBatches[0].Metrics(), gc.HasLen, 1)
   756  	c.Assert(metricBatches[0].Metrics()[0].Value, gc.Equals, "5")
   757  
   758  	c.Assert(metricBatches[1].Unit(), gc.Equals, "metered/1")
   759  	c.Assert(metricBatches[1].CharmURL(), gc.Equals, "local:quantal/metered")
   760  	c.Assert(metricBatches[1].Sent(), jc.IsFalse)
   761  	c.Assert(metricBatches[1].Metrics(), gc.HasLen, 1)
   762  	c.Assert(metricBatches[1].Metrics()[0].Value, gc.Equals, "10")
   763  }
   764  
   765  func (s *MetricLocalCharmSuite) TestUnitMetricBatchesReturnsJustLocal(c *gc.C) {
   766  	now := state.NowToTheSecond()
   767  	m := state.Metric{"pings", "5", now}
   768  	_, err := s.State.AddMetrics(
   769  		state.BatchParam{
   770  			UUID:     utils.MustNewUUID().String(),
   771  			Created:  now,
   772  			CharmURL: s.meteredCharm.URL().String(),
   773  			Metrics:  []state.Metric{m},
   774  			Unit:     s.unit.UnitTag(),
   775  		},
   776  	)
   777  	c.Assert(err, jc.ErrorIsNil)
   778  	csMeteredCharm := s.Factory.MakeCharm(c, &factory.CharmParams{Name: "metered", URL: "cs:quantal/metered"})
   779  	service := s.Factory.MakeService(c, &factory.ServiceParams{Name: "csmetered", Charm: csMeteredCharm})
   780  	unit := s.Factory.MakeUnit(c, &factory.UnitParams{Service: service, SetCharmURL: true})
   781  	_, err = s.State.AddMetrics(
   782  		state.BatchParam{
   783  			UUID:     utils.MustNewUUID().String(),
   784  			Created:  now,
   785  			CharmURL: csMeteredCharm.URL().String(),
   786  			Metrics:  []state.Metric{m},
   787  			Unit:     unit.UnitTag(),
   788  		},
   789  	)
   790  
   791  	c.Assert(err, jc.ErrorIsNil)
   792  	metricBatches, err := s.State.MetricBatchesForUnit("metered/0")
   793  	c.Assert(metricBatches, gc.HasLen, 1)
   794  	metricBatches, err = s.State.MetricBatchesForUnit("csmetered/0")
   795  	c.Assert(metricBatches, gc.HasLen, 0)
   796  }