1// Copyright 2013 The Prometheus Authors 2// Licensed under the Apache License, Version 2.0 (the "License"); 3// you may not use this file except in compliance with the License. 4// You may obtain a copy of the License at 5// 6// http://www.apache.org/licenses/LICENSE-2.0 7// 8// Unless required by applicable law or agreed to in writing, software 9// distributed under the License is distributed on an "AS IS" BASIS, 10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11// See the License for the specific language governing permissions and 12// limitations under the License. 13 14package scrape 15 16import ( 17 "crypto/tls" 18 "crypto/x509" 19 "fmt" 20 "io/ioutil" 21 "net/http" 22 "net/http/httptest" 23 "net/url" 24 "strings" 25 "testing" 26 "time" 27 28 config_util "github.com/prometheus/common/config" 29 "github.com/prometheus/common/model" 30 "github.com/stretchr/testify/require" 31 32 "github.com/prometheus/prometheus/config" 33 "github.com/prometheus/prometheus/discovery/targetgroup" 34 "github.com/prometheus/prometheus/pkg/labels" 35) 36 37const ( 38 caCertPath = "testdata/ca.cer" 39) 40 41func TestTargetLabels(t *testing.T) { 42 target := newTestTarget("example.com:80", 0, labels.FromStrings("job", "some_job", "foo", "bar")) 43 want := labels.FromStrings(model.JobLabel, "some_job", "foo", "bar") 44 got := target.Labels() 45 require.Equal(t, want, got) 46} 47 48func TestTargetOffset(t *testing.T) { 49 interval := 10 * time.Second 50 jitter := uint64(0) 51 52 offsets := make([]time.Duration, 10000) 53 54 // Calculate offsets for 10000 different targets. 55 for i := range offsets { 56 target := newTestTarget("example.com:80", 0, labels.FromStrings( 57 "label", fmt.Sprintf("%d", i), 58 )) 59 offsets[i] = target.offset(interval, jitter) 60 } 61 62 // Put the offsets into buckets and validate that they are all 63 // within bounds. 64 bucketSize := 1 * time.Second 65 buckets := make([]int, interval/bucketSize) 66 67 for _, offset := range offsets { 68 if offset < 0 || offset >= interval { 69 t.Fatalf("Offset %v out of bounds", offset) 70 } 71 72 bucket := offset / bucketSize 73 buckets[bucket]++ 74 } 75 76 t.Log(buckets) 77 78 // Calculate whether the number of targets per bucket 79 // does not differ more than a given tolerance. 80 avg := len(offsets) / len(buckets) 81 tolerance := 0.15 82 83 for _, bucket := range buckets { 84 diff := bucket - avg 85 if diff < 0 { 86 diff = -diff 87 } 88 89 if float64(diff)/float64(avg) > tolerance { 90 t.Fatalf("Bucket out of tolerance bounds") 91 } 92 } 93} 94 95func TestTargetURL(t *testing.T) { 96 params := url.Values{ 97 "abc": []string{"foo", "bar", "baz"}, 98 "xyz": []string{"hoo"}, 99 } 100 labels := labels.FromMap(map[string]string{ 101 model.AddressLabel: "example.com:1234", 102 model.SchemeLabel: "https", 103 model.MetricsPathLabel: "/metricz", 104 "__param_abc": "overwrite", 105 "__param_cde": "huu", 106 }) 107 target := NewTarget(labels, labels, params) 108 109 // The reserved labels are concatenated into a full URL. The first value for each 110 // URL query parameter can be set/modified via labels as well. 111 expectedParams := url.Values{ 112 "abc": []string{"overwrite", "bar", "baz"}, 113 "cde": []string{"huu"}, 114 "xyz": []string{"hoo"}, 115 } 116 expectedURL := &url.URL{ 117 Scheme: "https", 118 Host: "example.com:1234", 119 Path: "/metricz", 120 RawQuery: expectedParams.Encode(), 121 } 122 123 require.Equal(t, expectedURL, target.URL()) 124} 125 126func newTestTarget(targetURL string, deadline time.Duration, lbls labels.Labels) *Target { 127 lb := labels.NewBuilder(lbls) 128 lb.Set(model.SchemeLabel, "http") 129 lb.Set(model.AddressLabel, strings.TrimPrefix(targetURL, "http://")) 130 lb.Set(model.MetricsPathLabel, "/metrics") 131 132 return &Target{labels: lb.Labels()} 133} 134 135func TestNewHTTPBearerToken(t *testing.T) { 136 server := httptest.NewServer( 137 http.HandlerFunc( 138 func(w http.ResponseWriter, r *http.Request) { 139 expected := "Bearer 1234" 140 received := r.Header.Get("Authorization") 141 if expected != received { 142 t.Fatalf("Authorization header was not set correctly: expected '%v', got '%v'", expected, received) 143 } 144 }, 145 ), 146 ) 147 defer server.Close() 148 149 cfg := config_util.HTTPClientConfig{ 150 BearerToken: "1234", 151 } 152 c, err := config_util.NewClientFromConfig(cfg, "test", config_util.WithHTTP2Disabled()) 153 if err != nil { 154 t.Fatal(err) 155 } 156 _, err = c.Get(server.URL) 157 if err != nil { 158 t.Fatal(err) 159 } 160} 161 162func TestNewHTTPBearerTokenFile(t *testing.T) { 163 server := httptest.NewServer( 164 http.HandlerFunc( 165 func(w http.ResponseWriter, r *http.Request) { 166 expected := "Bearer 12345" 167 received := r.Header.Get("Authorization") 168 if expected != received { 169 t.Fatalf("Authorization header was not set correctly: expected '%v', got '%v'", expected, received) 170 } 171 }, 172 ), 173 ) 174 defer server.Close() 175 176 cfg := config_util.HTTPClientConfig{ 177 BearerTokenFile: "testdata/bearertoken.txt", 178 } 179 c, err := config_util.NewClientFromConfig(cfg, "test", config_util.WithHTTP2Disabled()) 180 if err != nil { 181 t.Fatal(err) 182 } 183 _, err = c.Get(server.URL) 184 if err != nil { 185 t.Fatal(err) 186 } 187} 188 189func TestNewHTTPBasicAuth(t *testing.T) { 190 server := httptest.NewServer( 191 http.HandlerFunc( 192 func(w http.ResponseWriter, r *http.Request) { 193 username, password, ok := r.BasicAuth() 194 if !(ok && username == "user" && password == "password123") { 195 t.Fatalf("Basic authorization header was not set correctly: expected '%v:%v', got '%v:%v'", "user", "password123", username, password) 196 } 197 }, 198 ), 199 ) 200 defer server.Close() 201 202 cfg := config_util.HTTPClientConfig{ 203 BasicAuth: &config_util.BasicAuth{ 204 Username: "user", 205 Password: "password123", 206 }, 207 } 208 c, err := config_util.NewClientFromConfig(cfg, "test", config_util.WithHTTP2Disabled()) 209 if err != nil { 210 t.Fatal(err) 211 } 212 _, err = c.Get(server.URL) 213 if err != nil { 214 t.Fatal(err) 215 } 216} 217 218func TestNewHTTPCACert(t *testing.T) { 219 server := httptest.NewUnstartedServer( 220 http.HandlerFunc( 221 func(w http.ResponseWriter, r *http.Request) { 222 w.Header().Set("Content-Type", `text/plain; version=0.0.4`) 223 w.Write([]byte{}) 224 }, 225 ), 226 ) 227 server.TLS = newTLSConfig("server", t) 228 server.StartTLS() 229 defer server.Close() 230 231 cfg := config_util.HTTPClientConfig{ 232 TLSConfig: config_util.TLSConfig{ 233 CAFile: caCertPath, 234 }, 235 } 236 c, err := config_util.NewClientFromConfig(cfg, "test", config_util.WithHTTP2Disabled()) 237 if err != nil { 238 t.Fatal(err) 239 } 240 _, err = c.Get(server.URL) 241 if err != nil { 242 t.Fatal(err) 243 } 244} 245 246func TestNewHTTPClientCert(t *testing.T) { 247 server := httptest.NewUnstartedServer( 248 http.HandlerFunc( 249 func(w http.ResponseWriter, r *http.Request) { 250 w.Header().Set("Content-Type", `text/plain; version=0.0.4`) 251 w.Write([]byte{}) 252 }, 253 ), 254 ) 255 tlsConfig := newTLSConfig("server", t) 256 tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert 257 tlsConfig.ClientCAs = tlsConfig.RootCAs 258 server.TLS = tlsConfig 259 server.StartTLS() 260 defer server.Close() 261 262 cfg := config_util.HTTPClientConfig{ 263 TLSConfig: config_util.TLSConfig{ 264 CAFile: caCertPath, 265 CertFile: "testdata/client.cer", 266 KeyFile: "testdata/client.key", 267 }, 268 } 269 c, err := config_util.NewClientFromConfig(cfg, "test", config_util.WithHTTP2Disabled()) 270 if err != nil { 271 t.Fatal(err) 272 } 273 _, err = c.Get(server.URL) 274 if err != nil { 275 t.Fatal(err) 276 } 277} 278 279func TestNewHTTPWithServerName(t *testing.T) { 280 server := httptest.NewUnstartedServer( 281 http.HandlerFunc( 282 func(w http.ResponseWriter, r *http.Request) { 283 w.Header().Set("Content-Type", `text/plain; version=0.0.4`) 284 w.Write([]byte{}) 285 }, 286 ), 287 ) 288 server.TLS = newTLSConfig("servername", t) 289 server.StartTLS() 290 defer server.Close() 291 292 cfg := config_util.HTTPClientConfig{ 293 TLSConfig: config_util.TLSConfig{ 294 CAFile: caCertPath, 295 ServerName: "prometheus.rocks", 296 }, 297 } 298 c, err := config_util.NewClientFromConfig(cfg, "test", config_util.WithHTTP2Disabled()) 299 if err != nil { 300 t.Fatal(err) 301 } 302 _, err = c.Get(server.URL) 303 if err != nil { 304 t.Fatal(err) 305 } 306} 307 308func TestNewHTTPWithBadServerName(t *testing.T) { 309 server := httptest.NewUnstartedServer( 310 http.HandlerFunc( 311 func(w http.ResponseWriter, r *http.Request) { 312 w.Header().Set("Content-Type", `text/plain; version=0.0.4`) 313 w.Write([]byte{}) 314 }, 315 ), 316 ) 317 server.TLS = newTLSConfig("servername", t) 318 server.StartTLS() 319 defer server.Close() 320 321 cfg := config_util.HTTPClientConfig{ 322 TLSConfig: config_util.TLSConfig{ 323 CAFile: caCertPath, 324 ServerName: "badname", 325 }, 326 } 327 c, err := config_util.NewClientFromConfig(cfg, "test", config_util.WithHTTP2Disabled()) 328 if err != nil { 329 t.Fatal(err) 330 } 331 _, err = c.Get(server.URL) 332 if err == nil { 333 t.Fatal("Expected error, got nil.") 334 } 335} 336 337func newTLSConfig(certName string, t *testing.T) *tls.Config { 338 tlsConfig := &tls.Config{} 339 caCertPool := x509.NewCertPool() 340 caCert, err := ioutil.ReadFile(caCertPath) 341 if err != nil { 342 t.Fatalf("Couldn't set up TLS server: %v", err) 343 } 344 caCertPool.AppendCertsFromPEM(caCert) 345 tlsConfig.RootCAs = caCertPool 346 tlsConfig.ServerName = "127.0.0.1" 347 certPath := fmt.Sprintf("testdata/%s.cer", certName) 348 keyPath := fmt.Sprintf("testdata/%s.key", certName) 349 cert, err := tls.LoadX509KeyPair(certPath, keyPath) 350 if err != nil { 351 t.Errorf("Unable to use specified server cert (%s) & key (%v): %s", certPath, keyPath, err) 352 } 353 tlsConfig.Certificates = []tls.Certificate{cert} 354 return tlsConfig 355} 356 357func TestNewClientWithBadTLSConfig(t *testing.T) { 358 cfg := config_util.HTTPClientConfig{ 359 TLSConfig: config_util.TLSConfig{ 360 CAFile: "testdata/nonexistent_ca.cer", 361 CertFile: "testdata/nonexistent_client.cer", 362 KeyFile: "testdata/nonexistent_client.key", 363 }, 364 } 365 _, err := config_util.NewClientFromConfig(cfg, "test", config_util.WithHTTP2Disabled()) 366 if err == nil { 367 t.Fatalf("Expected error, got nil.") 368 } 369} 370 371func TestTargetsFromGroup(t *testing.T) { 372 expectedError := "instance 0 in group : no address" 373 374 targets, failures := targetsFromGroup(&targetgroup.Group{Targets: []model.LabelSet{{}, {model.AddressLabel: "localhost:9090"}}}, &config.ScrapeConfig{}) 375 if len(targets) != 1 { 376 t.Fatalf("Expected 1 target, got %v", len(targets)) 377 } 378 if len(failures) != 1 { 379 t.Fatalf("Expected 1 failure, got %v", len(failures)) 380 } 381 if failures[0].Error() != expectedError { 382 t.Fatalf("Expected error %s, got %s", expectedError, failures[0]) 383 } 384} 385 386func TestTargetHash(t *testing.T) { 387 target1 := &Target{ 388 labels: labels.Labels{ 389 {Name: model.AddressLabel, Value: "localhost"}, 390 {Name: model.SchemeLabel, Value: "http"}, 391 {Name: model.MetricsPathLabel, Value: "/metrics"}, 392 {Name: model.ScrapeIntervalLabel, Value: "15s"}, 393 {Name: model.ScrapeTimeoutLabel, Value: "500ms"}, 394 }, 395 } 396 hash1 := target1.hash() 397 398 target2 := &Target{ 399 labels: labels.Labels{ 400 {Name: model.AddressLabel, Value: "localhost"}, 401 {Name: model.SchemeLabel, Value: "http"}, 402 {Name: model.MetricsPathLabel, Value: "/metrics"}, 403 {Name: model.ScrapeIntervalLabel, Value: "14s"}, 404 {Name: model.ScrapeTimeoutLabel, Value: "600ms"}, 405 }, 406 } 407 hash2 := target2.hash() 408 409 require.Equal(t, hash1, hash2, "Scrape interval and duration labels should not effect hash.") 410} 411