github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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 jc "github.com/juju/testing/checkers" 11 "github.com/juju/utils" 12 gc "gopkg.in/check.v1" 13 "gopkg.in/juju/names.v2" 14 15 "github.com/juju/juju/state" 16 "github.com/juju/juju/testing" 17 "github.com/juju/juju/testing/factory" 18 ) 19 20 type MetricSuite struct { 21 ConnSuite 22 unit *state.Unit 23 application *state.Application 24 meteredCharm *state.Charm 25 } 26 27 var _ = gc.Suite(&MetricSuite{}) 28 29 func (s *MetricSuite) SetUpTest(c *gc.C) { 30 s.ConnSuite.SetUpTest(c) 31 s.meteredCharm = s.Factory.MakeCharm(c, &factory.CharmParams{Name: "metered", URL: "cs:quantal/metered"}) 32 s.application = s.Factory.MakeApplication(c, &factory.ApplicationParams{Charm: s.meteredCharm}) 33 s.unit = s.Factory.MakeUnit(c, &factory.UnitParams{Application: s.application, SetCharmURL: true}) 34 } 35 36 func (s *MetricSuite) TestAddNoMetrics(c *gc.C) { 37 now := s.State.NowToTheSecond() 38 _, err := s.State.AddMetrics(state.BatchParam{ 39 UUID: utils.MustNewUUID().String(), 40 CharmURL: s.meteredCharm.URL().String(), 41 Created: now, 42 Metrics: []state.Metric{}, 43 Unit: s.unit.UnitTag(), 44 }) 45 c.Assert(err, gc.ErrorMatches, "cannot add a batch of 0 metrics") 46 } 47 48 func removeUnit(c *gc.C, unit *state.Unit) { 49 ensureUnitDead(c, unit) 50 err := unit.Remove() 51 c.Assert(err, jc.ErrorIsNil) 52 } 53 54 func ensureUnitDead(c *gc.C, unit *state.Unit) { 55 err := unit.EnsureDead() 56 c.Assert(err, jc.ErrorIsNil) 57 } 58 59 func (s *MetricSuite) TestAddMetric(c *gc.C) { 60 now := s.State.NowToTheSecond() 61 modelUUID := s.State.ModelUUID() 62 m := state.Metric{"pings", "5", now} 63 metricBatch, err := s.State.AddMetrics( 64 state.BatchParam{ 65 UUID: utils.MustNewUUID().String(), 66 Created: now, 67 CharmURL: s.meteredCharm.URL().String(), 68 Metrics: []state.Metric{m}, 69 Unit: s.unit.UnitTag(), 70 }, 71 ) 72 c.Assert(err, jc.ErrorIsNil) 73 c.Assert(metricBatch.Unit(), gc.Equals, "metered/0") 74 c.Assert(metricBatch.ModelUUID(), gc.Equals, modelUUID) 75 c.Assert(metricBatch.CharmURL(), gc.Equals, "cs:quantal/metered") 76 c.Assert(metricBatch.Sent(), jc.IsFalse) 77 c.Assert(metricBatch.Created(), gc.Equals, now) 78 c.Assert(metricBatch.Metrics(), gc.HasLen, 1) 79 80 metric := metricBatch.Metrics()[0] 81 c.Assert(metric.Key, gc.Equals, "pings") 82 c.Assert(metric.Value, gc.Equals, "5") 83 c.Assert(metric.Time.Equal(now), jc.IsTrue) 84 85 saved, err := s.State.MetricBatch(metricBatch.UUID()) 86 c.Assert(err, jc.ErrorIsNil) 87 c.Assert(saved.Unit(), gc.Equals, "metered/0") 88 c.Assert(metricBatch.CharmURL(), gc.Equals, "cs:quantal/metered") 89 c.Assert(saved.Sent(), jc.IsFalse) 90 c.Assert(saved.Metrics(), gc.HasLen, 1) 91 metric = saved.Metrics()[0] 92 c.Assert(metric.Key, gc.Equals, "pings") 93 c.Assert(metric.Value, gc.Equals, "5") 94 c.Assert(metric.Time.Equal(now), jc.IsTrue) 95 } 96 97 func (s *MetricSuite) TestAddMetricNonExistentUnit(c *gc.C) { 98 removeUnit(c, s.unit) 99 now := s.State.NowToTheSecond() 100 m := state.Metric{"pings", "5", now} 101 unitTag := names.NewUnitTag("test/0") 102 _, err := s.State.AddMetrics( 103 state.BatchParam{ 104 UUID: utils.MustNewUUID().String(), 105 Created: now, 106 CharmURL: s.meteredCharm.URL().String(), 107 Metrics: []state.Metric{m}, 108 Unit: unitTag, 109 }, 110 ) 111 c.Assert(err, gc.ErrorMatches, ".*not found") 112 } 113 114 func (s *MetricSuite) TestAddMetricDeadUnit(c *gc.C) { 115 ensureUnitDead(c, s.unit) 116 now := s.State.NowToTheSecond() 117 m := state.Metric{"pings", "5", now} 118 _, err := s.State.AddMetrics( 119 state.BatchParam{ 120 UUID: utils.MustNewUUID().String(), 121 Created: now, 122 CharmURL: s.meteredCharm.URL().String(), 123 Metrics: []state.Metric{m}, 124 Unit: s.unit.UnitTag(), 125 }, 126 ) 127 c.Assert(err, gc.ErrorMatches, `metered/0 not found`) 128 } 129 130 func (s *MetricSuite) TestSetMetricSent(c *gc.C) { 131 now := s.State.NowToTheSecond() 132 m := state.Metric{"pings", "5", now} 133 added, err := s.State.AddMetrics( 134 state.BatchParam{ 135 UUID: utils.MustNewUUID().String(), 136 Created: now, 137 CharmURL: s.meteredCharm.URL().String(), 138 Metrics: []state.Metric{m}, 139 Unit: s.unit.UnitTag(), 140 }, 141 ) 142 c.Assert(err, jc.ErrorIsNil) 143 saved, err := s.State.MetricBatch(added.UUID()) 144 c.Assert(err, jc.ErrorIsNil) 145 err = saved.SetSent(testing.NonZeroTime()) 146 c.Assert(err, jc.ErrorIsNil) 147 c.Assert(saved.Sent(), jc.IsTrue) 148 saved, err = s.State.MetricBatch(added.UUID()) 149 c.Assert(err, jc.ErrorIsNil) 150 c.Assert(saved.Sent(), jc.IsTrue) 151 } 152 153 func (s *MetricSuite) TestCleanupMetrics(c *gc.C) { 154 oldTime := testing.NonZeroTime().Add(-(time.Hour * 25)) 155 now := testing.NonZeroTime() 156 m := state.Metric{"pings", "5", oldTime} 157 oldMetric1, err := s.State.AddMetrics( 158 state.BatchParam{ 159 UUID: utils.MustNewUUID().String(), 160 Created: now, 161 CharmURL: s.meteredCharm.URL().String(), 162 Metrics: []state.Metric{m}, 163 Unit: s.unit.UnitTag(), 164 }, 165 ) 166 c.Assert(err, jc.ErrorIsNil) 167 oldMetric1.SetSent(testing.NonZeroTime().Add(-25 * time.Hour)) 168 169 oldMetric2, err := s.State.AddMetrics( 170 state.BatchParam{ 171 UUID: utils.MustNewUUID().String(), 172 Created: now, 173 CharmURL: s.meteredCharm.URL().String(), 174 Metrics: []state.Metric{m}, 175 Unit: s.unit.UnitTag(), 176 }, 177 ) 178 c.Assert(err, jc.ErrorIsNil) 179 oldMetric2.SetSent(testing.NonZeroTime().Add(-25 * time.Hour)) 180 181 m = state.Metric{"pings", "5", now} 182 newMetric, err := s.State.AddMetrics( 183 state.BatchParam{ 184 UUID: utils.MustNewUUID().String(), 185 Created: now, 186 CharmURL: s.meteredCharm.URL().String(), 187 Metrics: []state.Metric{m}, 188 Unit: s.unit.UnitTag(), 189 }, 190 ) 191 c.Assert(err, jc.ErrorIsNil) 192 newMetric.SetSent(testing.NonZeroTime()) 193 err = s.State.CleanupOldMetrics() 194 c.Assert(err, jc.ErrorIsNil) 195 196 _, err = s.State.MetricBatch(newMetric.UUID()) 197 c.Assert(err, jc.ErrorIsNil) 198 199 _, err = s.State.MetricBatch(oldMetric1.UUID()) 200 c.Assert(err, jc.Satisfies, errors.IsNotFound) 201 202 _, err = s.State.MetricBatch(oldMetric2.UUID()) 203 c.Assert(err, jc.Satisfies, errors.IsNotFound) 204 } 205 206 func (s *MetricSuite) TestCleanupNoMetrics(c *gc.C) { 207 err := s.State.CleanupOldMetrics() 208 c.Assert(err, jc.ErrorIsNil) 209 } 210 211 func (s *MetricSuite) TestCleanupMetricsIgnoreNotSent(c *gc.C) { 212 oldTime := testing.NonZeroTime().Add(-(time.Hour * 25)) 213 m := state.Metric{"pings", "5", oldTime} 214 oldMetric, err := s.State.AddMetrics( 215 state.BatchParam{ 216 UUID: utils.MustNewUUID().String(), 217 Created: oldTime, 218 CharmURL: s.meteredCharm.URL().String(), 219 Metrics: []state.Metric{m}, 220 Unit: s.unit.UnitTag(), 221 }, 222 ) 223 c.Assert(err, jc.ErrorIsNil) 224 225 now := testing.NonZeroTime() 226 m = state.Metric{"pings", "5", now} 227 newMetric, err := s.State.AddMetrics( 228 state.BatchParam{ 229 UUID: utils.MustNewUUID().String(), 230 Created: now, 231 CharmURL: s.meteredCharm.URL().String(), 232 Metrics: []state.Metric{m}, 233 Unit: s.unit.UnitTag(), 234 }, 235 ) 236 c.Assert(err, jc.ErrorIsNil) 237 newMetric.SetSent(testing.NonZeroTime()) 238 err = s.State.CleanupOldMetrics() 239 c.Assert(err, jc.ErrorIsNil) 240 241 _, err = s.State.MetricBatch(newMetric.UUID()) 242 c.Assert(err, jc.ErrorIsNil) 243 244 _, err = s.State.MetricBatch(oldMetric.UUID()) 245 c.Assert(err, jc.ErrorIsNil) 246 } 247 248 func (s *MetricSuite) TestAllMetricBatches(c *gc.C) { 249 now := s.State.NowToTheSecond() 250 m := state.Metric{"pings", "5", now} 251 _, err := s.State.AddMetrics( 252 state.BatchParam{ 253 UUID: utils.MustNewUUID().String(), 254 Created: now, 255 CharmURL: s.meteredCharm.URL().String(), 256 Metrics: []state.Metric{m}, 257 Unit: s.unit.UnitTag(), 258 }, 259 ) 260 c.Assert(err, jc.ErrorIsNil) 261 metricBatches, err := s.State.AllMetricBatches() 262 c.Assert(err, jc.ErrorIsNil) 263 c.Assert(metricBatches, gc.HasLen, 1) 264 c.Assert(metricBatches[0].Unit(), gc.Equals, "metered/0") 265 c.Assert(metricBatches[0].CharmURL(), gc.Equals, "cs:quantal/metered") 266 c.Assert(metricBatches[0].Sent(), jc.IsFalse) 267 c.Assert(metricBatches[0].Metrics(), gc.HasLen, 1) 268 } 269 270 func (s *MetricSuite) TestAllMetricBatchesCustomCharmURLAndUUID(c *gc.C) { 271 now := s.State.NowToTheSecond() 272 m := state.Metric{"pings", "5", now} 273 uuid := utils.MustNewUUID().String() 274 charmURL := "cs:quantal/metered" 275 _, err := s.State.AddMetrics( 276 state.BatchParam{ 277 UUID: uuid, 278 Created: now, 279 CharmURL: charmURL, 280 Metrics: []state.Metric{m}, 281 Unit: s.unit.UnitTag(), 282 }, 283 ) 284 c.Assert(err, jc.ErrorIsNil) 285 metricBatches, err := s.State.AllMetricBatches() 286 c.Assert(err, jc.ErrorIsNil) 287 c.Assert(metricBatches, gc.HasLen, 1) 288 c.Assert(metricBatches[0].Unit(), gc.Equals, "metered/0") 289 c.Assert(metricBatches[0].UUID(), gc.Equals, uuid) 290 c.Assert(metricBatches[0].CharmURL(), gc.Equals, charmURL) 291 c.Assert(metricBatches[0].Sent(), jc.IsFalse) 292 c.Assert(metricBatches[0].Metrics(), gc.HasLen, 1) 293 } 294 295 func (s *MetricSuite) TestMetricCredentials(c *gc.C) { 296 now := s.State.NowToTheSecond() 297 m := state.Metric{"pings", "5", now} 298 err := s.application.SetMetricCredentials([]byte("hello there")) 299 c.Assert(err, gc.IsNil) 300 _, err = s.State.AddMetrics( 301 state.BatchParam{ 302 UUID: utils.MustNewUUID().String(), 303 Created: now, 304 CharmURL: s.meteredCharm.URL().String(), 305 Metrics: []state.Metric{m}, 306 Unit: s.unit.UnitTag(), 307 }, 308 ) 309 c.Assert(err, jc.ErrorIsNil) 310 metricBatches, err := s.State.AllMetricBatches() 311 c.Assert(err, jc.ErrorIsNil) 312 c.Assert(metricBatches, gc.HasLen, 1) 313 c.Assert(metricBatches[0].Credentials(), gc.DeepEquals, []byte("hello there")) 314 } 315 316 // TestCountMetrics asserts the correct values are returned 317 // by CountOfUnsentMetrics and CountOfSentMetrics. 318 func (s *MetricSuite) TestCountMetrics(c *gc.C) { 319 now := testing.NonZeroTime() 320 m := []state.Metric{{Key: "pings", Value: "123", Time: now}} 321 s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: false, Time: &now, Metrics: m}) 322 s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: false, Time: &now, Metrics: m}) 323 s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: true, Time: &now, Metrics: m}) 324 sent, err := s.State.CountOfSentMetrics() 325 c.Assert(err, jc.ErrorIsNil) 326 c.Assert(sent, gc.Equals, 1) 327 unsent, err := s.State.CountOfUnsentMetrics() 328 c.Assert(err, jc.ErrorIsNil) 329 c.Assert(unsent, gc.Equals, 2) 330 c.Assert(unsent+sent, gc.Equals, 3) 331 } 332 333 func (s *MetricSuite) TestSetMetricBatchesSent(c *gc.C) { 334 now := testing.NonZeroTime() 335 metrics := make([]*state.MetricBatch, 3) 336 for i := range metrics { 337 m := []state.Metric{{Key: "pings", Value: "123", Time: now}} 338 metrics[i] = s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: false, Time: &now, Metrics: m}) 339 } 340 uuids := make([]string, len(metrics)) 341 for i, m := range metrics { 342 uuids[i] = m.UUID() 343 } 344 err := s.State.SetMetricBatchesSent(uuids) 345 c.Assert(err, jc.ErrorIsNil) 346 sent, err := s.State.CountOfSentMetrics() 347 c.Assert(err, jc.ErrorIsNil) 348 c.Assert(sent, gc.Equals, 3) 349 350 } 351 352 func (s *MetricSuite) TestMetricsToSend(c *gc.C) { 353 now := s.State.NowToTheSecond() 354 m := []state.Metric{{Key: "pings", Value: "123", Time: now}} 355 s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: false, Time: &now, Metrics: m}) 356 s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: false, Time: &now, Metrics: m}) 357 s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: true, Time: &now, Metrics: m}) 358 result, err := s.State.MetricsToSend(5) 359 c.Assert(err, jc.ErrorIsNil) 360 c.Assert(result, gc.HasLen, 2) 361 } 362 363 // TestMetricsToSendBatches checks that metrics are properly batched. 364 func (s *MetricSuite) TestMetricsToSendBatches(c *gc.C) { 365 now := s.State.NowToTheSecond() 366 for i := 0; i < 6; i++ { 367 m := []state.Metric{{Key: "pings", Value: "123", Time: now}} 368 s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: false, Time: &now, Metrics: m}) 369 } 370 for i := 0; i < 4; i++ { 371 m := []state.Metric{{Key: "pings", Value: "123", Time: now}} 372 s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: true, Time: &now, Metrics: m}) 373 } 374 for i := 0; i < 3; i++ { 375 result, err := s.State.MetricsToSend(2) 376 c.Assert(err, jc.ErrorIsNil) 377 c.Assert(result, gc.HasLen, 2) 378 uuids := make([]string, len(result)) 379 for i, m := range result { 380 uuids[i] = m.UUID() 381 } 382 s.State.SetMetricBatchesSent(uuids) 383 } 384 result, err := s.State.MetricsToSend(2) 385 c.Assert(err, jc.ErrorIsNil) 386 c.Assert(result, gc.HasLen, 0) 387 } 388 389 func (s *MetricSuite) TestMetricValidation(c *gc.C) { 390 nonMeteredUnit := s.Factory.MakeUnit(c, &factory.UnitParams{SetCharmURL: true}) 391 meteredApplication := s.Factory.MakeApplication(c, &factory.ApplicationParams{Name: "metered-service", Charm: s.meteredCharm}) 392 meteredUnit := s.Factory.MakeUnit(c, &factory.UnitParams{Application: meteredApplication, SetCharmURL: true}) 393 dyingUnit, err := meteredApplication.AddUnit() 394 c.Assert(err, jc.ErrorIsNil) 395 err = dyingUnit.SetCharmURL(s.meteredCharm.URL()) 396 c.Assert(err, jc.ErrorIsNil) 397 err = dyingUnit.Destroy() 398 c.Assert(err, jc.ErrorIsNil) 399 now := testing.NonZeroTime() 400 tests := []struct { 401 about string 402 metrics []state.Metric 403 unit *state.Unit 404 err string 405 }{{ 406 "assert non metered unit returns an error", 407 []state.Metric{{"metric-key", "1", now}}, 408 nonMeteredUnit, 409 "charm doesn't implement metrics", 410 }, { 411 "assert metric with no errors and passes validation", 412 []state.Metric{{"pings", "1", now}}, 413 meteredUnit, 414 "", 415 }, { 416 "assert valid metric fails on dying unit", 417 []state.Metric{{"pings", "1", now}}, 418 dyingUnit, 419 "unit \"metered-service/1\" not found", 420 }, { 421 "assert charm doesn't implement key returns error", 422 []state.Metric{{"not-implemented", "1", now}}, 423 meteredUnit, 424 `metric "not-implemented" not defined`, 425 }, { 426 "assert invalid value returns error", 427 []state.Metric{{"pings", "foobar", now}}, 428 meteredUnit, 429 `invalid value type: expected float, got "foobar"`, 430 }, { 431 "long value returns error", 432 []state.Metric{{"pings", "3.141592653589793238462643383279", now}}, 433 meteredUnit, 434 `metric value is too large`, 435 }, { 436 "negative value returns error", 437 []state.Metric{{"pings", "-42.0", now}}, 438 meteredUnit, 439 `invalid value: value must be greater or equal to zero, got -42.0`, 440 }, { 441 "non-float value returns an error", 442 []state.Metric{{"pings", "abcd", now}}, 443 meteredUnit, 444 `invalid value type: expected float, got "abcd"`, 445 }} 446 for i, t := range tests { 447 c.Logf("test %d: %s", i, t.about) 448 chURL, ok := t.unit.CharmURL() 449 c.Assert(ok, jc.IsTrue) 450 _, err := s.State.AddMetrics( 451 state.BatchParam{ 452 UUID: utils.MustNewUUID().String(), 453 Created: now, 454 CharmURL: chURL.String(), 455 Metrics: t.metrics, 456 Unit: t.unit.UnitTag(), 457 }, 458 ) 459 if t.err == "" { 460 c.Assert(err, jc.ErrorIsNil) 461 } else { 462 c.Assert(err, gc.ErrorMatches, t.err) 463 } 464 } 465 } 466 467 func (s *MetricSuite) TestAddMetricDuplicateUUID(c *gc.C) { 468 now := s.State.NowToTheSecond() 469 mUUID := utils.MustNewUUID().String() 470 _, err := s.State.AddMetrics( 471 state.BatchParam{ 472 UUID: mUUID, 473 Created: now, 474 CharmURL: s.meteredCharm.URL().String(), 475 Metrics: []state.Metric{{"pings", "5", now}}, 476 Unit: s.unit.UnitTag(), 477 }, 478 ) 479 c.Assert(err, jc.ErrorIsNil) 480 481 _, err = s.State.AddMetrics( 482 state.BatchParam{ 483 UUID: mUUID, 484 Created: now, 485 CharmURL: s.meteredCharm.URL().String(), 486 Metrics: []state.Metric{{"pings", "10", now}}, 487 Unit: s.unit.UnitTag(), 488 }, 489 ) 490 c.Assert(err, gc.ErrorMatches, "metrics batch .* already exists") 491 } 492 493 func (s *MetricSuite) TestAddBuiltInMetric(c *gc.C) { 494 tests := []struct { 495 about string 496 value string 497 expectedError string 498 }{{ 499 about: "adding a positive value must succeed", 500 value: "5", 501 }, { 502 about: "negative values return an error", 503 value: "-42.0", 504 expectedError: "invalid value: value must be greater or equal to zero, got -42.0", 505 }, { 506 about: "non-float values return an error", 507 value: "abcd", 508 expectedError: `invalid value type: expected float, got "abcd"`, 509 }, { 510 about: "long values return an error", 511 value: "1234567890123456789012345678901234567890", 512 expectedError: "metric value is too large", 513 }, 514 } 515 for _, test := range tests { 516 c.Logf("running test: %v", test.about) 517 now := s.State.NowToTheSecond() 518 modelUUID := s.State.ModelUUID() 519 m := state.Metric{"juju-units", test.value, now} 520 metricBatch, err := s.State.AddMetrics( 521 state.BatchParam{ 522 UUID: utils.MustNewUUID().String(), 523 Created: now, 524 CharmURL: s.meteredCharm.URL().String(), 525 Metrics: []state.Metric{m}, 526 Unit: s.unit.UnitTag(), 527 }, 528 ) 529 if test.expectedError == "" { 530 c.Assert(err, jc.ErrorIsNil) 531 c.Assert(metricBatch.Unit(), gc.Equals, "metered/0") 532 c.Assert(metricBatch.ModelUUID(), gc.Equals, modelUUID) 533 c.Assert(metricBatch.CharmURL(), gc.Equals, "cs:quantal/metered") 534 c.Assert(metricBatch.Sent(), jc.IsFalse) 535 c.Assert(metricBatch.Created(), gc.Equals, now) 536 c.Assert(metricBatch.Metrics(), gc.HasLen, 1) 537 538 metric := metricBatch.Metrics()[0] 539 c.Assert(metric.Key, gc.Equals, "juju-units") 540 c.Assert(metric.Value, gc.Equals, test.value) 541 c.Assert(metric.Time.Equal(now), jc.IsTrue) 542 543 saved, err := s.State.MetricBatch(metricBatch.UUID()) 544 c.Assert(err, jc.ErrorIsNil) 545 c.Assert(saved.Unit(), gc.Equals, "metered/0") 546 c.Assert(metricBatch.CharmURL(), gc.Equals, "cs:quantal/metered") 547 c.Assert(saved.Sent(), jc.IsFalse) 548 c.Assert(saved.Metrics(), gc.HasLen, 1) 549 metric = saved.Metrics()[0] 550 c.Assert(metric.Key, gc.Equals, "juju-units") 551 c.Assert(metric.Value, gc.Equals, test.value) 552 c.Assert(metric.Time.Equal(now), jc.IsTrue) 553 } else { 554 c.Assert(err, gc.ErrorMatches, test.expectedError) 555 } 556 } 557 } 558 559 func (s *MetricSuite) TestUnitMetricBatchesMatchesAllCharms(c *gc.C) { 560 now := s.State.NowToTheSecond() 561 m := state.Metric{"pings", "5", now} 562 _, err := s.State.AddMetrics( 563 state.BatchParam{ 564 UUID: utils.MustNewUUID().String(), 565 Created: now, 566 CharmURL: s.meteredCharm.URL().String(), 567 Metrics: []state.Metric{m}, 568 Unit: s.unit.UnitTag(), 569 }, 570 ) 571 c.Assert(err, jc.ErrorIsNil) 572 localMeteredCharm := s.Factory.MakeCharm(c, &factory.CharmParams{Name: "metered", URL: "local:quantal/metered"}) 573 application := s.Factory.MakeApplication(c, &factory.ApplicationParams{Name: "localmetered", Charm: localMeteredCharm}) 574 unit := s.Factory.MakeUnit(c, &factory.UnitParams{Application: application, SetCharmURL: true}) 575 _, err = s.State.AddMetrics( 576 state.BatchParam{ 577 UUID: utils.MustNewUUID().String(), 578 Created: now, 579 CharmURL: localMeteredCharm.URL().String(), 580 Metrics: []state.Metric{m}, 581 Unit: unit.UnitTag(), 582 }, 583 ) 584 585 c.Assert(err, jc.ErrorIsNil) 586 metricBatches, err := s.State.MetricBatchesForUnit("metered/0") 587 c.Assert(metricBatches, gc.HasLen, 1) 588 metricBatches, err = s.State.MetricBatchesForUnit("localmetered/0") 589 c.Assert(metricBatches, gc.HasLen, 1) 590 } 591 592 func (s *MetricSuite) TestNoSuchUnitMetricBatches(c *gc.C) { 593 _, err := s.State.MetricBatchesForUnit("chimerical-unit/0") 594 c.Assert(err, gc.ErrorMatches, `unit "chimerical-unit/0" not found`) 595 } 596 597 func (s *MetricSuite) TestNoSuchApplicationMetricBatches(c *gc.C) { 598 _, err := s.State.MetricBatchesForApplication("unicorn-app") 599 c.Assert(err, gc.ErrorMatches, `application "unicorn-app" not found`) 600 } 601 602 type MetricLocalCharmSuite struct { 603 ConnSuite 604 unit *state.Unit 605 application *state.Application 606 meteredCharm *state.Charm 607 } 608 609 var _ = gc.Suite(&MetricLocalCharmSuite{}) 610 611 func (s *MetricLocalCharmSuite) SetUpTest(c *gc.C) { 612 s.ConnSuite.SetUpTest(c) 613 s.meteredCharm = s.Factory.MakeCharm(c, &factory.CharmParams{Name: "metered", URL: "local:quantal/metered"}) 614 s.application = s.Factory.MakeApplication(c, &factory.ApplicationParams{Charm: s.meteredCharm}) 615 s.unit = s.Factory.MakeUnit(c, &factory.UnitParams{Application: s.application, SetCharmURL: true}) 616 } 617 618 func (s *MetricLocalCharmSuite) TestUnitMetricBatches(c *gc.C) { 619 now := s.State.NowToTheSecond() 620 m := state.Metric{"pings", "5", now} 621 m2 := state.Metric{"pings", "10", now} 622 _, err := s.State.AddMetrics( 623 state.BatchParam{ 624 UUID: utils.MustNewUUID().String(), 625 Created: now, 626 CharmURL: s.meteredCharm.URL().String(), 627 Metrics: []state.Metric{m}, 628 Unit: s.unit.UnitTag(), 629 }, 630 ) 631 c.Assert(err, jc.ErrorIsNil) 632 newUnit, err := s.application.AddUnit() 633 c.Assert(err, jc.ErrorIsNil) 634 _, err = s.State.AddMetrics( 635 state.BatchParam{ 636 UUID: utils.MustNewUUID().String(), 637 Created: now, 638 CharmURL: s.meteredCharm.URL().String(), 639 Metrics: []state.Metric{m2}, 640 Unit: newUnit.UnitTag(), 641 }, 642 ) 643 c.Assert(err, jc.ErrorIsNil) 644 645 metricBatches, err := s.State.MetricBatchesForUnit("metered/0") 646 c.Assert(err, jc.ErrorIsNil) 647 c.Assert(metricBatches, gc.HasLen, 1) 648 c.Check(metricBatches[0].Unit(), gc.Equals, "metered/0") 649 c.Check(metricBatches[0].CharmURL(), gc.Equals, "local:quantal/metered") 650 c.Check(metricBatches[0].Sent(), jc.IsFalse) 651 c.Assert(metricBatches[0].Metrics(), gc.HasLen, 1) 652 c.Check(metricBatches[0].Metrics()[0].Value, gc.Equals, "5") 653 654 metricBatches, err = s.State.MetricBatchesForUnit("metered/1") 655 c.Assert(err, jc.ErrorIsNil) 656 c.Assert(metricBatches, gc.HasLen, 1) 657 c.Check(metricBatches[0].Unit(), gc.Equals, "metered/1") 658 c.Check(metricBatches[0].CharmURL(), gc.Equals, "local:quantal/metered") 659 c.Check(metricBatches[0].Sent(), jc.IsFalse) 660 c.Assert(metricBatches[0].Metrics(), gc.HasLen, 1) 661 c.Check(metricBatches[0].Metrics()[0].Value, gc.Equals, "10") 662 } 663 664 func (s *MetricLocalCharmSuite) TestApplicationMetricBatches(c *gc.C) { 665 now := s.State.NowToTheSecond() 666 m := state.Metric{"pings", "5", now} 667 m2 := state.Metric{"pings", "10", now} 668 _, err := s.State.AddMetrics( 669 state.BatchParam{ 670 UUID: utils.MustNewUUID().String(), 671 Created: now, 672 CharmURL: s.meteredCharm.URL().String(), 673 Metrics: []state.Metric{m}, 674 Unit: s.unit.UnitTag(), 675 }, 676 ) 677 c.Assert(err, jc.ErrorIsNil) 678 newUnit, err := s.application.AddUnit() 679 c.Assert(err, jc.ErrorIsNil) 680 _, err = s.State.AddMetrics( 681 state.BatchParam{ 682 UUID: utils.MustNewUUID().String(), 683 Created: now, 684 CharmURL: s.meteredCharm.URL().String(), 685 Metrics: []state.Metric{m2}, 686 Unit: newUnit.UnitTag(), 687 }, 688 ) 689 c.Assert(err, jc.ErrorIsNil) 690 691 metricBatches, err := s.State.MetricBatchesForApplication("metered") 692 c.Assert(err, jc.ErrorIsNil) 693 c.Assert(metricBatches, gc.HasLen, 2) 694 695 c.Check(metricBatches[0].Unit(), gc.Equals, "metered/0") 696 c.Check(metricBatches[0].CharmURL(), gc.Equals, "local:quantal/metered") 697 c.Check(metricBatches[0].Sent(), jc.IsFalse) 698 c.Assert(metricBatches[0].Metrics(), gc.HasLen, 1) 699 c.Check(metricBatches[0].Metrics()[0].Value, gc.Equals, "5") 700 701 c.Check(metricBatches[1].Unit(), gc.Equals, "metered/1") 702 c.Check(metricBatches[1].CharmURL(), gc.Equals, "local:quantal/metered") 703 c.Check(metricBatches[1].Sent(), jc.IsFalse) 704 c.Assert(metricBatches[1].Metrics(), gc.HasLen, 1) 705 c.Check(metricBatches[1].Metrics()[0].Value, gc.Equals, "10") 706 } 707 708 func (s *MetricLocalCharmSuite) TestModelMetricBatches(c *gc.C) { 709 now := s.State.NowToTheSecond() 710 // Add 2 metric batches to a single unit. 711 m := state.Metric{"pings", "5", now} 712 m2 := state.Metric{"pings", "10", now} 713 _, err := s.State.AddMetrics( 714 state.BatchParam{ 715 UUID: utils.MustNewUUID().String(), 716 Created: now, 717 CharmURL: s.meteredCharm.URL().String(), 718 Metrics: []state.Metric{m}, 719 Unit: s.unit.UnitTag(), 720 }, 721 ) 722 c.Assert(err, jc.ErrorIsNil) 723 newUnit, err := s.application.AddUnit() 724 c.Assert(err, jc.ErrorIsNil) 725 _, err = s.State.AddMetrics( 726 state.BatchParam{ 727 UUID: utils.MustNewUUID().String(), 728 Created: now.Add(time.Second), 729 CharmURL: s.meteredCharm.URL().String(), 730 Metrics: []state.Metric{m2}, 731 Unit: newUnit.UnitTag(), 732 }, 733 ) 734 c.Assert(err, jc.ErrorIsNil) 735 736 // Create a new model and add a metric batch. 737 st := s.Factory.MakeModel(c, nil) 738 defer st.Close() 739 f := factory.NewFactory(st) 740 meteredCharm := f.MakeCharm(c, &factory.CharmParams{Name: "metered", URL: "cs:quantal/metered"}) 741 service := f.MakeApplication(c, &factory.ApplicationParams{Charm: meteredCharm}) 742 unit := f.MakeUnit(c, &factory.UnitParams{Application: service, SetCharmURL: true}) 743 _, err = st.AddMetrics( 744 state.BatchParam{ 745 UUID: utils.MustNewUUID().String(), 746 Created: now, 747 CharmURL: meteredCharm.URL().String(), 748 Metrics: []state.Metric{m}, 749 Unit: unit.UnitTag(), 750 }, 751 ) 752 c.Assert(err, jc.ErrorIsNil) 753 754 // We expect 2 metric batches in our first model. 755 metricBatches, err := s.State.MetricBatchesForModel() 756 c.Assert(err, jc.ErrorIsNil) 757 c.Assert(metricBatches, gc.HasLen, 2) 758 759 var first, second state.MetricBatch 760 for _, m := range metricBatches { 761 if m.Unit() == "metered/0" { 762 first = m 763 } 764 if m.Unit() == "metered/1" { 765 second = m 766 } 767 } 768 c.Assert(first, gc.NotNil) 769 c.Assert(second, gc.NotNil) 770 771 c.Check(first.Unit(), gc.Equals, "metered/0") 772 c.Check(first.CharmURL(), gc.Equals, "local:quantal/metered") 773 c.Check(first.ModelUUID(), gc.Equals, s.State.ModelUUID()) 774 c.Check(first.Sent(), jc.IsFalse) 775 c.Assert(first.Metrics(), gc.HasLen, 1) 776 c.Check(first.Metrics()[0].Value, gc.Equals, "5") 777 778 c.Check(second.Unit(), gc.Equals, "metered/1") 779 c.Check(second.CharmURL(), gc.Equals, "local:quantal/metered") 780 c.Check(second.ModelUUID(), gc.Equals, s.State.ModelUUID()) 781 c.Check(second.Sent(), jc.IsFalse) 782 c.Assert(second.Metrics(), gc.HasLen, 1) 783 c.Check(second.Metrics()[0].Value, gc.Equals, "10") 784 785 // And a single metric batch in the second model. 786 metricBatches, err = st.MetricBatchesForModel() 787 c.Assert(err, jc.ErrorIsNil) 788 c.Assert(metricBatches, gc.HasLen, 1) 789 } 790 791 func (s *MetricLocalCharmSuite) TestMetricsSorted(c *gc.C) { 792 newUnit, err := s.application.AddUnit() 793 c.Assert(err, jc.ErrorIsNil) 794 795 t0 := time.Date(2016, time.August, 16, 16, 00, 35, 0, time.Local) 796 var times []time.Time 797 for i := 0; i < 3; i++ { 798 times = append(times, t0.Add(time.Minute*time.Duration(i))) 799 } 800 801 for _, t := range times { 802 _, err := s.State.AddMetrics( 803 state.BatchParam{ 804 UUID: utils.MustNewUUID().String(), 805 Created: t, 806 CharmURL: s.meteredCharm.URL().String(), 807 Metrics: []state.Metric{{"pings", "5", t}}, 808 Unit: s.unit.UnitTag(), 809 }, 810 ) 811 c.Assert(err, jc.ErrorIsNil) 812 813 _, err = s.State.AddMetrics( 814 state.BatchParam{ 815 UUID: utils.MustNewUUID().String(), 816 Created: t, 817 CharmURL: s.meteredCharm.URL().String(), 818 Metrics: []state.Metric{{"pings", "10", t}}, 819 Unit: newUnit.UnitTag(), 820 }, 821 ) 822 c.Assert(err, jc.ErrorIsNil) 823 } 824 825 metricBatches, err := s.State.MetricBatchesForUnit("metered/0") 826 c.Assert(err, jc.ErrorIsNil) 827 assertMetricBatchesTimeAscending(c, metricBatches) 828 829 metricBatches, err = s.State.MetricBatchesForUnit("metered/1") 830 c.Assert(err, jc.ErrorIsNil) 831 assertMetricBatchesTimeAscending(c, metricBatches) 832 833 metricBatches, err = s.State.MetricBatchesForApplication("metered") 834 c.Assert(err, jc.ErrorIsNil) 835 assertMetricBatchesTimeAscending(c, metricBatches) 836 837 metricBatches, err = s.State.MetricBatchesForModel() 838 c.Assert(err, jc.ErrorIsNil) 839 assertMetricBatchesTimeAscending(c, metricBatches) 840 841 } 842 843 func assertMetricBatchesTimeAscending(c *gc.C, batches []state.MetricBatch) { 844 var tPrev time.Time 845 846 for i := range batches { 847 if i > 0 { 848 afterOrEqualPrev := func(t time.Time) bool { 849 return t.After(tPrev) || t.Equal(tPrev) 850 } 851 desc := gc.Commentf("%+v <= %+v", batches[i-1], batches[i]) 852 c.Assert(batches[i].Created(), jc.Satisfies, afterOrEqualPrev, desc) 853 c.Assert(batches[i].Metrics(), gc.HasLen, 1) 854 c.Assert(batches[i].Metrics()[0].Time, jc.Satisfies, afterOrEqualPrev, desc) 855 } 856 tPrev = batches[i].Created() 857 } 858 } 859 860 func (s *MetricLocalCharmSuite) TestUnitMetricBatchesReturnsAllCharms(c *gc.C) { 861 now := s.State.NowToTheSecond() 862 m := state.Metric{"pings", "5", now} 863 _, err := s.State.AddMetrics( 864 state.BatchParam{ 865 UUID: utils.MustNewUUID().String(), 866 Created: now, 867 CharmURL: s.meteredCharm.URL().String(), 868 Metrics: []state.Metric{m}, 869 Unit: s.unit.UnitTag(), 870 }, 871 ) 872 c.Assert(err, jc.ErrorIsNil) 873 csMeteredCharm := s.Factory.MakeCharm(c, &factory.CharmParams{Name: "metered", URL: "cs:quantal/metered"}) 874 application := s.Factory.MakeApplication(c, &factory.ApplicationParams{Name: "csmetered", Charm: csMeteredCharm}) 875 unit := s.Factory.MakeUnit(c, &factory.UnitParams{Application: application, SetCharmURL: true}) 876 _, err = s.State.AddMetrics( 877 state.BatchParam{ 878 UUID: utils.MustNewUUID().String(), 879 Created: now, 880 CharmURL: csMeteredCharm.URL().String(), 881 Metrics: []state.Metric{m}, 882 Unit: unit.UnitTag(), 883 }, 884 ) 885 886 c.Assert(err, jc.ErrorIsNil) 887 metricBatches, err := s.State.MetricBatchesForUnit("metered/0") 888 c.Assert(metricBatches, gc.HasLen, 1) 889 metricBatches, err = s.State.MetricBatchesForUnit("csmetered/0") 890 c.Assert(metricBatches, gc.HasLen, 1) 891 } 892 893 func (s *MetricLocalCharmSuite) TestUnique(c *gc.C) { 894 t0 := s.State.NowToTheSecond() 895 t1 := t0.Add(time.Second) 896 batch, err := s.State.AddMetrics( 897 state.BatchParam{ 898 UUID: utils.MustNewUUID().String(), 899 Created: t0, 900 CharmURL: s.meteredCharm.URL().String(), 901 Metrics: []state.Metric{{ 902 Key: "pings", 903 Value: "1", 904 Time: t0, 905 }, { 906 Key: "pings", 907 Value: "2", 908 Time: t1, 909 }, { 910 Key: "juju-units", 911 Value: "1", 912 Time: t1, 913 }, { 914 Key: "juju-units", 915 Value: "2", 916 Time: t0, 917 }}, 918 Unit: s.unit.UnitTag(), 919 }, 920 ) 921 c.Assert(err, jc.ErrorIsNil) 922 metrics := batch.UniqueMetrics() 923 c.Assert(metrics, gc.HasLen, 2) 924 c.Assert(metrics, jc.SameContents, []state.Metric{{ 925 Key: "pings", 926 Value: "2", 927 Time: t1, 928 }, { 929 Key: "juju-units", 930 Value: "1", 931 Time: t1, 932 }}) 933 } 934 935 type modelData struct { 936 state *state.State 937 application *state.Application 938 unit *state.Unit 939 meteredCharm *state.Charm 940 } 941 942 type CrossModelMetricSuite struct { 943 ConnSuite 944 models []modelData 945 } 946 947 var _ = gc.Suite(&CrossModelMetricSuite{}) 948 949 func (s *CrossModelMetricSuite) SetUpTest(c *gc.C) { 950 s.ConnSuite.SetUpTest(c) 951 // Set up two models. 952 s.models = make([]modelData, 2) 953 var cleanup func(*gc.C) 954 for i := 0; i < 2; i++ { 955 s.models[i], cleanup = mustCreateMeteredModel(c, s.Factory) 956 s.AddCleanup(cleanup) 957 } 958 } 959 960 func mustCreateMeteredModel(c *gc.C, stateFactory *factory.Factory) (modelData, func(*gc.C)) { 961 st := stateFactory.MakeModel(c, nil) 962 localFactory := factory.NewFactory(st) 963 964 meteredCharm := localFactory.MakeCharm(c, &factory.CharmParams{Name: "metered", URL: "cs:quantal/metered"}) 965 application := localFactory.MakeApplication(c, &factory.ApplicationParams{Charm: meteredCharm}) 966 unit := localFactory.MakeUnit(c, &factory.UnitParams{Application: application, SetCharmURL: true}) 967 cleanup := func(*gc.C) { st.Close() } 968 return modelData{ 969 state: st, 970 application: application, 971 unit: unit, 972 meteredCharm: meteredCharm, 973 }, cleanup 974 } 975 976 func (s *CrossModelMetricSuite) TestMetricsAcrossEnvironments(c *gc.C) { 977 now := s.State.NowToTheSecond().Add(-48 * time.Hour) 978 m := state.Metric{"pings", "5", now} 979 m1, err := s.models[0].state.AddMetrics( 980 state.BatchParam{ 981 UUID: utils.MustNewUUID().String(), 982 Created: now, 983 CharmURL: s.models[0].meteredCharm.URL().String(), 984 Metrics: []state.Metric{m}, 985 Unit: s.models[0].unit.UnitTag(), 986 }, 987 ) 988 c.Assert(err, jc.ErrorIsNil) 989 990 m2, err := s.models[1].state.AddMetrics( 991 state.BatchParam{ 992 UUID: utils.MustNewUUID().String(), 993 Created: now, 994 CharmURL: s.models[1].meteredCharm.URL().String(), 995 Metrics: []state.Metric{m}, 996 Unit: s.models[1].unit.UnitTag(), 997 }, 998 ) 999 c.Assert(err, jc.ErrorIsNil) 1000 1001 batches, err := s.State.AllMetricBatches() 1002 c.Assert(err, jc.ErrorIsNil) 1003 c.Assert(batches, gc.HasLen, 2) 1004 1005 unsent, err := s.models[0].state.CountOfUnsentMetrics() 1006 c.Assert(err, jc.ErrorIsNil) 1007 c.Assert(unsent, gc.Equals, 1) 1008 1009 toSend, err := s.models[0].state.MetricsToSend(10) 1010 c.Assert(err, jc.ErrorIsNil) 1011 c.Assert(toSend, gc.HasLen, 1) 1012 1013 err = m1.SetSent(testing.NonZeroTime().Add(-25 * time.Hour)) 1014 c.Assert(err, jc.ErrorIsNil) 1015 err = m2.SetSent(testing.NonZeroTime().Add(-25 * time.Hour)) 1016 c.Assert(err, jc.ErrorIsNil) 1017 1018 sent, err := s.models[0].state.CountOfSentMetrics() 1019 c.Assert(err, jc.ErrorIsNil) 1020 c.Assert(sent, gc.Equals, 1) 1021 1022 err = s.models[0].state.CleanupOldMetrics() 1023 c.Assert(err, jc.ErrorIsNil) 1024 1025 // The metric from model s.models[1] should still be in place. 1026 batches, err = s.State.AllMetricBatches() 1027 c.Assert(err, jc.ErrorIsNil) 1028 c.Assert(batches, gc.HasLen, 1) 1029 }