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 }