github.com/galamsiva2020/kubernetes-heapster-monitoring@v0.0.0-20210823134957-3c1baa7c1e70/metrics/sinks/influxdb/influxdb_test.go (about) 1 // Copyright 2015 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package influxdb 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "testing" 21 "time" 22 23 "net/http/httptest" 24 "net/url" 25 26 influx_models "github.com/influxdata/influxdb/models" 27 "github.com/stretchr/testify/assert" 28 util "k8s.io/client-go/util/testing" 29 influxdb_common "k8s.io/heapster/common/influxdb" 30 "k8s.io/heapster/metrics/core" 31 ) 32 33 type fakeInfluxDBDataSink struct { 34 core.DataSink 35 fakeDbClient *influxdb_common.FakeInfluxDBClient 36 } 37 38 func newRawInfluxSink() *influxdbSink { 39 return &influxdbSink{ 40 client: influxdb_common.Client, 41 c: influxdb_common.Config, 42 conChan: make(chan struct{}, influxdb_common.Config.Concurrency), 43 } 44 } 45 46 func NewFakeSink() fakeInfluxDBDataSink { 47 return fakeInfluxDBDataSink{ 48 newRawInfluxSink(), 49 influxdb_common.Client, 50 } 51 } 52 func TestStoreDataEmptyInput(t *testing.T) { 53 fakeSink := NewFakeSink() 54 dataBatch := core.DataBatch{} 55 fakeSink.ExportData(&dataBatch) 56 assert.Equal(t, 0, len(fakeSink.fakeDbClient.Pnts)) 57 } 58 59 func TestStoreMultipleDataInput(t *testing.T) { 60 fakeSink := NewFakeSink() 61 timestamp := time.Now() 62 63 l := make(map[string]string) 64 l["namespace_id"] = "123" 65 l["container_name"] = "/system.slice/-.mount" 66 l[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" 67 68 l2 := make(map[string]string) 69 l2["namespace_id"] = "123" 70 l2["container_name"] = "/system.slice/dbus.service" 71 l2[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" 72 73 l3 := make(map[string]string) 74 l3["namespace_id"] = "123" 75 l3[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" 76 77 l4 := make(map[string]string) 78 l4["namespace_id"] = "" 79 l4[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" 80 81 l5 := make(map[string]string) 82 l5["namespace_id"] = "123" 83 l5[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" 84 85 metricSet1 := core.MetricSet{ 86 Labels: l, 87 MetricValues: map[string]core.MetricValue{ 88 "/system.slice/-.mount//cpu/limit": { 89 ValueType: core.ValueInt64, 90 MetricType: core.MetricCumulative, 91 IntValue: 123456, 92 }, 93 }, 94 } 95 96 metricSet2 := core.MetricSet{ 97 Labels: l2, 98 MetricValues: map[string]core.MetricValue{ 99 "/system.slice/dbus.service//cpu/usage": { 100 ValueType: core.ValueInt64, 101 MetricType: core.MetricCumulative, 102 IntValue: 123456, 103 }, 104 }, 105 } 106 107 metricSet3 := core.MetricSet{ 108 Labels: l3, 109 MetricValues: map[string]core.MetricValue{ 110 "test/metric/1": { 111 ValueType: core.ValueInt64, 112 MetricType: core.MetricCumulative, 113 IntValue: 123456, 114 }, 115 }, 116 } 117 118 metricSet4 := core.MetricSet{ 119 Labels: l4, 120 MetricValues: map[string]core.MetricValue{ 121 "test/metric/1": { 122 ValueType: core.ValueInt64, 123 MetricType: core.MetricCumulative, 124 IntValue: 123456, 125 }, 126 }, 127 } 128 129 metricSet5 := core.MetricSet{ 130 Labels: l5, 131 MetricValues: map[string]core.MetricValue{ 132 "removeme": { 133 ValueType: core.ValueInt64, 134 MetricType: core.MetricCumulative, 135 IntValue: 123456, 136 }, 137 }, 138 } 139 140 data := core.DataBatch{ 141 Timestamp: timestamp, 142 MetricSets: map[string]*core.MetricSet{ 143 "pod1": &metricSet1, 144 "pod2": &metricSet2, 145 "pod3": &metricSet3, 146 "pod4": &metricSet4, 147 "pod5": &metricSet5, 148 }, 149 } 150 151 fakeSink.ExportData(&data) 152 assert.Equal(t, 5, len(fakeSink.fakeDbClient.Pnts)) 153 } 154 155 func TestCreateInfluxdbSink(t *testing.T) { 156 handler := util.FakeHandler{ 157 StatusCode: 200, 158 RequestBody: "", 159 ResponseBody: "", 160 T: t, 161 } 162 server := httptest.NewServer(&handler) 163 defer server.Close() 164 165 stubInfluxDBUrl, err := url.Parse(server.URL) 166 assert.NoError(t, err) 167 168 //create influxdb sink 169 sink, err := CreateInfluxdbSink(stubInfluxDBUrl) 170 assert.NoError(t, err) 171 172 //check sink name 173 assert.Equal(t, sink.Name(), "InfluxDB Sink") 174 } 175 176 func makeRow(results [][]string) influx_models.Row { 177 resRow := influx_models.Row{ 178 Values: make([][]interface{}, len(results)), 179 } 180 181 for setInd, valSet := range results { 182 outVals := make([]interface{}, len(valSet)) 183 for valInd, val := range valSet { 184 if valInd == 0 { 185 // timestamp should just be a string 186 outVals[valInd] = val 187 } else { 188 outVals[valInd] = json.Number(val) 189 } 190 } 191 resRow.Values[setInd] = outVals 192 } 193 194 return resRow 195 } 196 197 func checkMetricVal(expected, actual core.MetricValue) bool { 198 if expected.ValueType != actual.ValueType { 199 return false 200 } 201 202 // only check the relevant value type 203 switch expected.ValueType { 204 case core.ValueFloat: 205 return expected.FloatValue == actual.FloatValue 206 case core.ValueInt64: 207 return expected.IntValue == actual.IntValue 208 default: 209 return expected == actual 210 } 211 } 212 213 func TestHistoricalMissingResponses(t *testing.T) { 214 sink := newRawInfluxSink() 215 216 podKeys := []core.HistoricalKey{ 217 {ObjectType: core.MetricSetTypePod, NamespaceName: "cheese", PodName: "cheddar"}, 218 {ObjectType: core.MetricSetTypePod, NamespaceName: "cheese", PodName: "swiss"}, 219 } 220 labels := map[string]string{"crackers": "ritz"} 221 222 errStr := fmt.Sprintf("No results for metric %q describing %q", "cpu/usage_rate", podKeys[0].String()) 223 224 _, err := sink.GetMetric("cpu/usage_rate", podKeys, time.Now().Add(-5*time.Minute), time.Now()) 225 assert.EqualError(t, err, errStr) 226 227 _, err = sink.GetLabeledMetric("cpu/usage_rate", labels, podKeys, time.Now().Add(-5*time.Minute), time.Now()) 228 assert.EqualError(t, err, errStr) 229 230 _, err = sink.GetAggregation("cpu/usage_rate", []core.AggregationType{core.AggregationTypeAverage}, podKeys, time.Now().Add(-5*time.Minute), time.Now(), 5*time.Minute) 231 assert.EqualError(t, err, errStr) 232 233 _, err = sink.GetLabeledAggregation("cpu/usage_rate", labels, []core.AggregationType{core.AggregationTypeAverage}, podKeys, time.Now().Add(-5*time.Minute), time.Now(), 5*time.Minute) 234 assert.EqualError(t, err, errStr) 235 } 236 237 func TestHistoricalInfluxRawMetricsParsing(t *testing.T) { 238 // in order to just test the parsing, we just go directly to the sink type 239 sink := newRawInfluxSink() 240 241 baseTime := time.Time{} 242 243 rawTests := []struct { 244 name string 245 rawData influx_models.Row 246 expectedResults []core.TimestampedMetricValue 247 expectedError bool 248 }{ 249 { 250 name: "all-integer data", 251 rawData: makeRow([][]string{ 252 { 253 baseTime.Add(24 * time.Hour).Format(time.RFC3339), 254 "1234", 255 }, 256 { 257 baseTime.Add(48 * time.Hour).Format(time.RFC3339), 258 "5678", 259 }, 260 }), 261 expectedResults: []core.TimestampedMetricValue{ 262 { 263 Timestamp: baseTime.Add(24 * time.Hour), 264 MetricValue: core.MetricValue{IntValue: 1234, ValueType: core.ValueInt64}, 265 }, 266 { 267 Timestamp: baseTime.Add(48 * time.Hour), 268 MetricValue: core.MetricValue{IntValue: 5678, ValueType: core.ValueInt64}, 269 }, 270 }, 271 }, 272 { 273 name: "all-float data", 274 rawData: makeRow([][]string{ 275 { 276 baseTime.Add(24 * time.Hour).Format(time.RFC3339), 277 "1.23e10", 278 }, 279 { 280 baseTime.Add(48 * time.Hour).Format(time.RFC3339), 281 "4.56e11", 282 }, 283 }), 284 expectedResults: []core.TimestampedMetricValue{ 285 { 286 Timestamp: baseTime.Add(24 * time.Hour), 287 MetricValue: core.MetricValue{FloatValue: 12300000000.0, ValueType: core.ValueFloat}, 288 }, 289 { 290 Timestamp: baseTime.Add(48 * time.Hour), 291 MetricValue: core.MetricValue{FloatValue: 456000000000.0, ValueType: core.ValueFloat}, 292 }, 293 }, 294 }, 295 { 296 name: "mixed data", 297 rawData: makeRow([][]string{ 298 { 299 baseTime.Add(24 * time.Hour).Format(time.RFC3339), 300 "123", 301 }, 302 { 303 baseTime.Add(48 * time.Hour).Format(time.RFC3339), 304 "4.56e11", 305 }, 306 }), 307 expectedResults: []core.TimestampedMetricValue{ 308 { 309 Timestamp: baseTime.Add(24 * time.Hour), 310 MetricValue: core.MetricValue{FloatValue: 123.0, ValueType: core.ValueFloat}, 311 }, 312 { 313 Timestamp: baseTime.Add(48 * time.Hour), 314 MetricValue: core.MetricValue{FloatValue: 456000000000.0, ValueType: core.ValueFloat}, 315 }, 316 }, 317 }, 318 { 319 name: "data with invalid value", 320 rawData: makeRow([][]string{ 321 { 322 baseTime.Add(24 * time.Hour).Format(time.RFC3339), 323 "true", 324 }, 325 }), 326 expectedError: true, 327 }, 328 } 329 330 RAWTESTLOOP: 331 for _, test := range rawTests { 332 parsedRawResults, err := sink.parseRawQueryRow(test.rawData) 333 if (err != nil) != test.expectedError { 334 t.Errorf("When parsing raw %s: expected error %v != actual error %v", test.name, test.expectedError, err) 335 continue RAWTESTLOOP 336 } 337 338 if len(parsedRawResults) != len(test.expectedResults) { 339 t.Errorf("When parsing raw %s: expected results %#v != actual results %#v", test.name, test.expectedResults, parsedRawResults) 340 continue RAWTESTLOOP 341 } 342 343 for i, metricVal := range parsedRawResults { 344 if !test.expectedResults[i].Timestamp.Equal(metricVal.Timestamp) { 345 t.Errorf("When parsing raw %s: expected results %#v != actual results %#v", test.name, test.expectedResults, parsedRawResults) 346 continue RAWTESTLOOP 347 } 348 349 if !checkMetricVal(test.expectedResults[i].MetricValue, metricVal.MetricValue) { 350 t.Errorf("When parsing raw %s: expected results %#v != actual results %#v", test.name, test.expectedResults, parsedRawResults) 351 continue RAWTESTLOOP 352 } 353 } 354 } 355 356 var countVal2 uint64 = 2 357 aggregatedTests := []struct { 358 name string 359 rawData influx_models.Row 360 expectedResults []core.TimestampedAggregationValue 361 expectedError bool 362 }{ 363 { 364 name: "all-integer data", 365 rawData: makeRow([][]string{ 366 { 367 baseTime.Add(24 * time.Hour).Format(time.RFC3339), 368 "2", 369 "1234", 370 }, 371 { 372 baseTime.Add(48 * time.Hour).Format(time.RFC3339), 373 "2", 374 "5678", 375 }, 376 }), 377 expectedResults: []core.TimestampedAggregationValue{ 378 { 379 Timestamp: baseTime.Add(24 * time.Hour), 380 AggregationValue: core.AggregationValue{ 381 Count: &countVal2, 382 Aggregations: map[core.AggregationType]core.MetricValue{ 383 core.AggregationTypeAverage: {IntValue: 1234, ValueType: core.ValueInt64}, 384 }, 385 }, 386 }, 387 { 388 Timestamp: baseTime.Add(48 * time.Hour), 389 AggregationValue: core.AggregationValue{ 390 Count: &countVal2, 391 Aggregations: map[core.AggregationType]core.MetricValue{ 392 core.AggregationTypeAverage: {IntValue: 5678, ValueType: core.ValueInt64}, 393 }, 394 }, 395 }, 396 }, 397 }, 398 { 399 name: "all-float data", 400 rawData: makeRow([][]string{ 401 { 402 baseTime.Add(24 * time.Hour).Format(time.RFC3339), 403 "2", 404 "1.23e10", 405 }, 406 { 407 baseTime.Add(48 * time.Hour).Format(time.RFC3339), 408 "2", 409 "4.56e11", 410 }, 411 }), 412 expectedResults: []core.TimestampedAggregationValue{ 413 { 414 Timestamp: baseTime.Add(24 * time.Hour), 415 AggregationValue: core.AggregationValue{ 416 Count: &countVal2, 417 Aggregations: map[core.AggregationType]core.MetricValue{ 418 core.AggregationTypeAverage: {FloatValue: 12300000000.0, ValueType: core.ValueFloat}, 419 }, 420 }, 421 }, 422 { 423 Timestamp: baseTime.Add(48 * time.Hour), 424 AggregationValue: core.AggregationValue{ 425 Count: &countVal2, 426 Aggregations: map[core.AggregationType]core.MetricValue{ 427 core.AggregationTypeAverage: {FloatValue: 456000000000.0, ValueType: core.ValueFloat}, 428 }, 429 }, 430 }, 431 }, 432 }, 433 { 434 name: "mixed data", 435 rawData: makeRow([][]string{ 436 { 437 baseTime.Add(24 * time.Hour).Format(time.RFC3339), 438 "2", 439 "123", 440 }, 441 { 442 baseTime.Add(48 * time.Hour).Format(time.RFC3339), 443 "2", 444 "4.56e11", 445 }, 446 }), 447 expectedResults: []core.TimestampedAggregationValue{ 448 { 449 Timestamp: baseTime.Add(24 * time.Hour), 450 AggregationValue: core.AggregationValue{ 451 Count: &countVal2, 452 Aggregations: map[core.AggregationType]core.MetricValue{ 453 core.AggregationTypeAverage: {FloatValue: 123.0, ValueType: core.ValueFloat}, 454 }, 455 }, 456 }, 457 { 458 Timestamp: baseTime.Add(48 * time.Hour), 459 AggregationValue: core.AggregationValue{ 460 Count: &countVal2, 461 Aggregations: map[core.AggregationType]core.MetricValue{ 462 core.AggregationTypeAverage: {FloatValue: 456000000000.0, ValueType: core.ValueFloat}, 463 }, 464 }, 465 }, 466 }, 467 }, 468 { 469 name: "data with invalid value", 470 rawData: makeRow([][]string{ 471 { 472 baseTime.Add(24 * time.Hour).Format(time.RFC3339), 473 "2", 474 "true", 475 }, 476 }), 477 expectedError: true, 478 }, 479 } 480 481 aggregationLookup := map[core.AggregationType]int{ 482 core.AggregationTypeCount: 1, 483 core.AggregationTypeAverage: 2, 484 } 485 AGGTESTLOOP: 486 for _, test := range aggregatedTests { 487 parsedAggResults, err := sink.parseAggregateQueryRow(test.rawData, aggregationLookup, 24*time.Hour) 488 if (err != nil) != test.expectedError { 489 t.Errorf("When parsing aggregated %s: expected error %v != actual error %v", test.name, test.expectedError, err) 490 continue AGGTESTLOOP 491 } 492 493 if len(parsedAggResults) != len(test.expectedResults) { 494 t.Errorf("When parsing aggregated %s: expected results %#v had a different length from actual results %#v", test.name, test.expectedResults, parsedAggResults) 495 continue AGGTESTLOOP 496 } 497 498 for i, metricVal := range parsedAggResults { 499 expVal := test.expectedResults[i] 500 if !expVal.Timestamp.Equal(metricVal.Timestamp) { 501 t.Errorf("When parsing aggregated %s: expected results %#v had a different timestamp from actual results %#v", test.name, expVal, metricVal) 502 continue AGGTESTLOOP 503 } 504 505 if len(expVal.Aggregations) != len(metricVal.Aggregations) { 506 t.Errorf("When parsing aggregated %s: expected results %#v had a number of aggregations from actual results %#v", test.name, expVal, metricVal) 507 continue AGGTESTLOOP 508 } 509 510 for aggName, aggVal := range metricVal.Aggregations { 511 if expAggVal, ok := expVal.Aggregations[aggName]; !ok || !checkMetricVal(expAggVal, aggVal) { 512 t.Errorf("When parsing aggregated %s: expected results %#v != actual results %#v", test.name, expAggVal, aggVal) 513 continue AGGTESTLOOP 514 } 515 } 516 } 517 } 518 } 519 520 func TestSanitizers(t *testing.T) { 521 badMetricName := "foo; baz" 522 goodMetricName := "cheese/types-crackers" 523 524 goodKeyValue := "cheddar.CHEESE-ritz.Crackers_1" 525 badKeyValue := "foobar'; baz" 526 527 sink := newRawInfluxSink() 528 529 if err := sink.checkSanitizedMetricName(goodMetricName); err != nil { 530 t.Errorf("Expected %q to be a valid metric name, but it was not: %v", goodMetricName, err) 531 } 532 533 if err := sink.checkSanitizedMetricName(badMetricName); err == nil { 534 t.Errorf("Expected %q to be an invalid metric name, but it was valid", badMetricName) 535 } 536 537 badKeys := []core.HistoricalKey{ 538 { 539 NodeName: badKeyValue, 540 }, 541 { 542 NamespaceName: badKeyValue, 543 }, 544 { 545 PodName: badKeyValue, 546 }, 547 { 548 ContainerName: badKeyValue, 549 }, 550 { 551 PodId: badKeyValue, 552 }, 553 } 554 555 for _, key := range badKeys { 556 if err := sink.checkSanitizedKey(&key); err == nil { 557 t.Errorf("Expected key %#v to be invalid, but it was not", key) 558 } 559 } 560 561 goodKeys := []core.HistoricalKey{ 562 { 563 NodeName: goodKeyValue, 564 }, 565 { 566 NamespaceName: goodKeyValue, 567 }, 568 { 569 PodName: goodKeyValue, 570 }, 571 { 572 ContainerName: goodKeyValue, 573 }, 574 { 575 PodId: goodKeyValue, 576 }, 577 } 578 579 for _, key := range goodKeys { 580 if err := sink.checkSanitizedKey(&key); err != nil { 581 t.Errorf("Expected key %#v to be valid, but it was not: %v", key, err) 582 } 583 } 584 }