github.com/timstclair/heapster@v0.20.0-alpha1/metrics/sinks/hawkular/driver_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 hawkular
    16  
    17  import (
    18  	"encoding/json"
    19  	"fmt"
    20  	"io/ioutil"
    21  	"net/http"
    22  	"net/http/httptest"
    23  	"net/url"
    24  	"strings"
    25  	"sync"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/hawkular/hawkular-client-go/metrics"
    30  	"k8s.io/heapster/metrics/core"
    31  
    32  	assert "github.com/stretchr/testify/require"
    33  )
    34  
    35  func dummySink() *hawkularSink {
    36  	return &hawkularSink{
    37  		reg:    make(map[string]*metrics.MetricDefinition),
    38  		models: make(map[string]*metrics.MetricDefinition),
    39  	}
    40  }
    41  
    42  func TestDescriptorTransform(t *testing.T) {
    43  
    44  	hSink := dummySink()
    45  
    46  	ld := core.LabelDescriptor{
    47  		Key:         "k1",
    48  		Description: "d1",
    49  	}
    50  	smd := core.MetricDescriptor{
    51  		Name:      "test/metric/1",
    52  		Units:     core.UnitsBytes,
    53  		ValueType: core.ValueInt64,
    54  		Type:      core.MetricGauge,
    55  		Labels:    []core.LabelDescriptor{ld},
    56  	}
    57  
    58  	md := hSink.descriptorToDefinition(&smd)
    59  
    60  	assert.Equal(t, smd.Name, md.Id)
    61  	assert.Equal(t, 3, len(md.Tags)) // descriptorTag, unitsTag, typesTag, k1
    62  
    63  	assert.Equal(t, smd.Units.String(), md.Tags[unitsTag])
    64  	assert.Equal(t, "d1", md.Tags["k1_description"])
    65  
    66  	smd.Type = core.MetricCumulative
    67  
    68  	md = hSink.descriptorToDefinition(&smd)
    69  	assert.Equal(t, md.Type, metrics.Counter)
    70  }
    71  
    72  func TestMetricTransform(t *testing.T) {
    73  	hSink := dummySink()
    74  
    75  	l := make(map[string]string)
    76  	l["spooky"] = "notvisible"
    77  	l[core.LabelHostname.Key] = "localhost"
    78  	l[core.LabelHostID.Key] = "localhost"
    79  	l[core.LabelContainerName.Key] = "docker"
    80  	l[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd"
    81  
    82  	metricName := "test/metric/1"
    83  
    84  	metricSet := core.MetricSet{
    85  		Labels: l,
    86  		MetricValues: map[string]core.MetricValue{
    87  			metricName: {
    88  				ValueType:  core.ValueInt64,
    89  				MetricType: core.MetricGauge,
    90  				IntValue:   123456,
    91  			},
    92  		},
    93  	}
    94  
    95  	now := time.Now()
    96  	m, err := hSink.pointToMetricHeader(&metricSet, metricName, now)
    97  	assert.NoError(t, err)
    98  
    99  	assert.Equal(t, fmt.Sprintf("%s/%s/%s", metricSet.Labels[core.LabelContainerName.Key],
   100  		metricSet.Labels[core.LabelPodId.Key], metricName), m.Id)
   101  
   102  	assert.Equal(t, 1, len(m.Data))
   103  	_, ok := m.Data[0].Value.(float64)
   104  	assert.True(t, ok, "Value should have been converted to float64")
   105  
   106  	delete(l, core.LabelPodId.Key)
   107  
   108  	m, err = hSink.pointToMetricHeader(&metricSet, metricName, now)
   109  	assert.NoError(t, err)
   110  
   111  	assert.Equal(t, fmt.Sprintf("%s/%s/%s", metricSet.Labels[core.LabelContainerName.Key], metricSet.Labels[core.LabelHostID.Key], metricName), m.Id)
   112  
   113  }
   114  
   115  func TestRecentTest(t *testing.T) {
   116  	hSink := dummySink()
   117  
   118  	modelT := make(map[string]string)
   119  
   120  	id := "test.name"
   121  	modelT[descriptorTag] = "d"
   122  	modelT[groupTag] = id
   123  	modelT["hep"+descriptionTag] = "n"
   124  
   125  	model := metrics.MetricDefinition{
   126  		Id:   id,
   127  		Tags: modelT,
   128  	}
   129  
   130  	liveT := make(map[string]string)
   131  	for k, v := range modelT {
   132  		liveT[k] = v
   133  	}
   134  
   135  	live := metrics.MetricDefinition{
   136  		Id:   "test/" + id,
   137  		Tags: liveT,
   138  	}
   139  
   140  	assert.True(t, hSink.recent(&live, &model), "Tags are equal, live is newest")
   141  
   142  	delete(liveT, "hep"+descriptionTag)
   143  	live.Tags = liveT
   144  
   145  	assert.False(t, hSink.recent(&live, &model), "Tags are not equal, live isn't recent")
   146  
   147  }
   148  
   149  func TestParseFiltersErrors(t *testing.T) {
   150  	_, err := parseFilters([]string{"(missingcommand)"})
   151  	assert.Error(t, err)
   152  
   153  	_, err = parseFilters([]string{"missingeverything"})
   154  	assert.Error(t, err)
   155  
   156  	_, err = parseFilters([]string{"labelstart:^missing$)"})
   157  	assert.Error(t, err)
   158  
   159  	_, err = parseFilters([]string{"label(endmissing"})
   160  	assert.Error(t, err)
   161  
   162  	_, err = parseFilters([]string{"label(wrongsyntax)"})
   163  	assert.Error(t, err)
   164  }
   165  
   166  // Integration tests
   167  func integSink(uri string) (*hawkularSink, error) {
   168  
   169  	u, err := url.Parse(uri)
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  
   174  	sink := &hawkularSink{
   175  		uri: u,
   176  	}
   177  	if err = sink.init(); err != nil {
   178  		return nil, err
   179  	}
   180  
   181  	return sink, nil
   182  }
   183  
   184  // Test that Definitions is called for Gauges & Counters
   185  // Test that we have single registered model
   186  // Test that the tags for metric is updated..
   187  func TestRegister(t *testing.T) {
   188  	m := &sync.Mutex{}
   189  	definitionsCalled := make(map[string]bool)
   190  	updateTagsCalled := false
   191  
   192  	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   193  		m.Lock()
   194  		defer m.Unlock()
   195  		w.Header().Set("Content-Type", "application/json")
   196  
   197  		if strings.Contains(r.RequestURI, "metrics?type=") {
   198  			typ := r.RequestURI[strings.Index(r.RequestURI, "type=")+5:]
   199  			definitionsCalled[typ] = true
   200  			if typ == "gauge" {
   201  				fmt.Fprintln(w, `[{ "id": "test.create.gauge.1", "tenantId": "test-heapster", "type": "gauge", "tags": { "descriptor_name": "test/metric/1" } }]`)
   202  			} else {
   203  				w.WriteHeader(http.StatusNoContent)
   204  			}
   205  		} else if strings.Contains(r.RequestURI, "/tags") && r.Method == "PUT" {
   206  			updateTagsCalled = true
   207  			// assert.True(t, strings.Contains(r.RequestURI, "k1:d1"), "Tag k1 was not updated with value d1")
   208  			defer r.Body.Close()
   209  			b, err := ioutil.ReadAll(r.Body)
   210  			assert.NoError(t, err)
   211  
   212  			tags := make(map[string]string)
   213  			err = json.Unmarshal(b, &tags)
   214  			assert.NoError(t, err)
   215  
   216  			_, kt1 := tags["k1_description"]
   217  			_, dt := tags["descriptor_name"]
   218  
   219  			assert.True(t, kt1, "k1_description tag is missing")
   220  			assert.True(t, dt, "descriptor_name is missing")
   221  
   222  			w.WriteHeader(http.StatusOK)
   223  		}
   224  	}))
   225  	defer s.Close()
   226  
   227  	hSink, err := integSink(s.URL + "?tenant=test-heapster")
   228  	assert.NoError(t, err)
   229  
   230  	md := make([]core.MetricDescriptor, 0, 1)
   231  	ld := core.LabelDescriptor{
   232  		Key:         "k1",
   233  		Description: "d1",
   234  	}
   235  	smd := core.MetricDescriptor{
   236  		Name:      "test/metric/1",
   237  		Units:     core.UnitsBytes,
   238  		ValueType: core.ValueInt64,
   239  		Type:      core.MetricGauge,
   240  		Labels:    []core.LabelDescriptor{ld},
   241  	}
   242  	smdg := core.MetricDescriptor{
   243  		Name:      "test/metric/2",
   244  		Units:     core.UnitsBytes,
   245  		ValueType: core.ValueFloat,
   246  		Type:      core.MetricCumulative,
   247  		Labels:    []core.LabelDescriptor{},
   248  	}
   249  
   250  	md = append(md, smd, smdg)
   251  
   252  	err = hSink.Register(md)
   253  	assert.NoError(t, err)
   254  
   255  	assert.Equal(t, 2, len(hSink.models))
   256  	assert.Equal(t, 1, len(hSink.reg))
   257  
   258  	assert.True(t, definitionsCalled["gauge"], "Gauge definitions were not fetched")
   259  	assert.True(t, definitionsCalled["counter"], "Counter definitions were not fetched")
   260  	assert.True(t, updateTagsCalled, "Updating outdated tags was not called")
   261  }
   262  
   263  // Store timeseries with both gauges and cumulatives
   264  func TestStoreTimeseries(t *testing.T) {
   265  	m := &sync.Mutex{}
   266  	ids := make([]string, 0, 2)
   267  	calls := make([]string, 0, 2)
   268  	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   269  		m.Lock()
   270  		defer m.Unlock()
   271  		calls = append(calls, r.RequestURI)
   272  		w.Header().Set("Content-Type", "application/json")
   273  
   274  		typ := r.RequestURI[strings.Index(r.RequestURI, "hawkular/metrics/")+17:]
   275  		typ = typ[:len(typ)-5]
   276  
   277  		switch typ {
   278  		case "counters":
   279  			assert.Equal(t, "test-label", r.Header.Get("Hawkular-Tenant"))
   280  			break
   281  		case "gauges":
   282  			assert.Equal(t, "test-heapster", r.Header.Get("Hawkular-Tenant"))
   283  			break
   284  		default:
   285  			assert.FailNow(t, "Unrecognized type "+typ)
   286  		}
   287  
   288  		defer r.Body.Close()
   289  		b, err := ioutil.ReadAll(r.Body)
   290  		assert.NoError(t, err)
   291  
   292  		mH := []metrics.MetricHeader{}
   293  		err = json.Unmarshal(b, &mH)
   294  		assert.NoError(t, err)
   295  
   296  		assert.Equal(t, 1, len(mH))
   297  
   298  		ids = append(ids, mH[0].Id)
   299  	}))
   300  	defer s.Close()
   301  
   302  	hSink, err := integSink(s.URL + "?tenant=test-heapster&labelToTenant=projectId")
   303  	assert.NoError(t, err)
   304  
   305  	l := make(map[string]string)
   306  	l["projectId"] = "test-label"
   307  	l[core.LabelContainerName.Key] = "test-container"
   308  	l[core.LabelPodId.Key] = "test-podid"
   309  
   310  	lg := make(map[string]string)
   311  	lg[core.LabelContainerName.Key] = "test-container"
   312  	lg[core.LabelPodId.Key] = "test-podid"
   313  
   314  	metricSet1 := core.MetricSet{
   315  		Labels: l,
   316  		MetricValues: map[string]core.MetricValue{
   317  			"test/metric/1": {
   318  				ValueType:  core.ValueInt64,
   319  				MetricType: core.MetricCumulative,
   320  				IntValue:   123456,
   321  			},
   322  		},
   323  	}
   324  
   325  	metricSet2 := core.MetricSet{
   326  		Labels: lg,
   327  		MetricValues: map[string]core.MetricValue{
   328  			"test/metric/2": {
   329  				ValueType:  core.ValueFloat,
   330  				MetricType: core.MetricGauge,
   331  				FloatValue: 123.456,
   332  			},
   333  		},
   334  	}
   335  
   336  	data := core.DataBatch{
   337  		Timestamp: time.Now(),
   338  		MetricSets: map[string]*core.MetricSet{
   339  			"pod1": &metricSet1,
   340  			"pod2": &metricSet2,
   341  		},
   342  	}
   343  
   344  	hSink.ExportData(&data)
   345  	assert.Equal(t, 2, len(calls))
   346  	assert.Equal(t, 2, len(ids))
   347  
   348  	assert.NotEqual(t, ids[0], ids[1])
   349  }
   350  
   351  func TestUserPass(t *testing.T) {
   352  	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   353  		w.Header().Set("X-Authorization", r.Header.Get("Authorization"))
   354  		auth := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
   355  		if len(auth) != 2 || auth[0] != "Basic" {
   356  			assert.FailNow(t, "Could not find Basic authentication")
   357  		}
   358  		assert.True(t, len(auth[1]) > 0)
   359  		w.WriteHeader(http.StatusNoContent)
   360  	}))
   361  	defer s.Close()
   362  
   363  	hSink, err := integSink(s.URL + "?user=tester&pass=hidden")
   364  	assert.NoError(t, err)
   365  
   366  	// md := make([]core.MetricDescriptor, 0, 1)
   367  	ld := core.LabelDescriptor{
   368  		Key:         "k1",
   369  		Description: "d1",
   370  	}
   371  	smd := core.MetricDescriptor{
   372  		Name:      "test/metric/1",
   373  		Units:     core.UnitsBytes,
   374  		ValueType: core.ValueInt64,
   375  		Type:      core.MetricGauge,
   376  		Labels:    []core.LabelDescriptor{ld},
   377  	}
   378  	err = hSink.Register([]core.MetricDescriptor{smd})
   379  	assert.NoError(t, err)
   380  }
   381  
   382  func TestFiltering(t *testing.T) {
   383  	m := &sync.Mutex{}
   384  	mH := []metrics.MetricHeader{}
   385  	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   386  		m.Lock()
   387  		defer m.Unlock()
   388  		if strings.Contains(r.RequestURI, "data") {
   389  			defer r.Body.Close()
   390  			b, err := ioutil.ReadAll(r.Body)
   391  			assert.NoError(t, err)
   392  
   393  			err = json.Unmarshal(b, &mH)
   394  			assert.NoError(t, err)
   395  		}
   396  	}))
   397  	defer s.Close()
   398  
   399  	hSink, err := integSink(s.URL + "?filter=label(namespace_id:^$)&filter=label(container_name:^[/system.slice/|/user.slice].*)&filter=name(remove*)")
   400  	assert.NoError(t, err)
   401  
   402  	l := make(map[string]string)
   403  	l["namespace_id"] = "123"
   404  	l["container_name"] = "/system.slice/-.mount"
   405  	l[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd"
   406  
   407  	l2 := make(map[string]string)
   408  	l2["namespace_id"] = "123"
   409  	l2["container_name"] = "/system.slice/dbus.service"
   410  	l2[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd"
   411  
   412  	l3 := make(map[string]string)
   413  	l3["namespace_id"] = "123"
   414  	l3[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd"
   415  
   416  	l4 := make(map[string]string)
   417  	l4["namespace_id"] = ""
   418  	l4[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd"
   419  
   420  	l5 := make(map[string]string)
   421  	l5["namespace_id"] = "123"
   422  	l5[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd"
   423  
   424  	metricSet1 := core.MetricSet{
   425  		Labels: l,
   426  		MetricValues: map[string]core.MetricValue{
   427  			"/system.slice/-.mount//cpu/limit": {
   428  				ValueType:  core.ValueInt64,
   429  				MetricType: core.MetricCumulative,
   430  				IntValue:   123456,
   431  			},
   432  		},
   433  	}
   434  
   435  	metricSet2 := core.MetricSet{
   436  		Labels: l2,
   437  		MetricValues: map[string]core.MetricValue{
   438  			"/system.slice/dbus.service//cpu/usage": {
   439  				ValueType:  core.ValueInt64,
   440  				MetricType: core.MetricCumulative,
   441  				IntValue:   123456,
   442  			},
   443  		},
   444  	}
   445  
   446  	metricSet3 := core.MetricSet{
   447  		Labels: l3,
   448  		MetricValues: map[string]core.MetricValue{
   449  			"test/metric/1": {
   450  				ValueType:  core.ValueInt64,
   451  				MetricType: core.MetricCumulative,
   452  				IntValue:   123456,
   453  			},
   454  		},
   455  	}
   456  
   457  	metricSet4 := core.MetricSet{
   458  		Labels: l4,
   459  		MetricValues: map[string]core.MetricValue{
   460  			"test/metric/1": {
   461  				ValueType:  core.ValueInt64,
   462  				MetricType: core.MetricCumulative,
   463  				IntValue:   123456,
   464  			},
   465  		},
   466  	}
   467  
   468  	metricSet5 := core.MetricSet{
   469  		Labels: l5,
   470  		MetricValues: map[string]core.MetricValue{
   471  			"removeme": {
   472  				ValueType:  core.ValueInt64,
   473  				MetricType: core.MetricCumulative,
   474  				IntValue:   123456,
   475  			},
   476  		},
   477  	}
   478  
   479  	data := core.DataBatch{
   480  		Timestamp: time.Now(),
   481  		MetricSets: map[string]*core.MetricSet{
   482  			"pod1": &metricSet1,
   483  			"pod2": &metricSet2,
   484  			"pod3": &metricSet3,
   485  			"pod4": &metricSet4,
   486  			"pod5": &metricSet5,
   487  		},
   488  	}
   489  	hSink.ExportData(&data)
   490  
   491  	assert.Equal(t, 1, len(mH))
   492  }