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  }