github.com/thanos-io/thanos@v0.32.5/test/e2e/query_test.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package e2e_test 5 6 import ( 7 "context" 8 "fmt" 9 "io" 10 "math/rand" 11 "net/http" 12 "net/http/httptest" 13 "net/url" 14 "os" 15 "path" 16 "path/filepath" 17 "reflect" 18 "runtime" 19 "sort" 20 "strings" 21 "testing" 22 "time" 23 24 "github.com/chromedp/cdproto/network" 25 "github.com/chromedp/chromedp" 26 "github.com/efficientgo/e2e" 27 e2edb "github.com/efficientgo/e2e/db" 28 e2emon "github.com/efficientgo/e2e/monitoring" 29 "github.com/go-kit/log" 30 "github.com/gogo/protobuf/proto" 31 "github.com/golang/snappy" 32 "github.com/pkg/errors" 33 config_util "github.com/prometheus/common/config" 34 "github.com/prometheus/common/model" 35 "github.com/prometheus/prometheus/model/labels" 36 "github.com/prometheus/prometheus/model/timestamp" 37 "github.com/prometheus/prometheus/prompb" 38 "github.com/prometheus/prometheus/rules" 39 "github.com/prometheus/prometheus/storage/remote" 40 "google.golang.org/grpc" 41 "google.golang.org/grpc/credentials/insecure" 42 43 "github.com/thanos-io/objstore" 44 "github.com/thanos-io/objstore/client" 45 "github.com/thanos-io/objstore/providers/s3" 46 47 "github.com/efficientgo/core/testutil" 48 49 "github.com/thanos-io/thanos/pkg/api/query/querypb" 50 "github.com/thanos-io/thanos/pkg/block/metadata" 51 "github.com/thanos-io/thanos/pkg/exemplars/exemplarspb" 52 "github.com/thanos-io/thanos/pkg/metadata/metadatapb" 53 "github.com/thanos-io/thanos/pkg/promclient" 54 "github.com/thanos-io/thanos/pkg/rules/rulespb" 55 "github.com/thanos-io/thanos/pkg/runutil" 56 "github.com/thanos-io/thanos/pkg/store/labelpb" 57 prompb_copy "github.com/thanos-io/thanos/pkg/store/storepb/prompb" 58 "github.com/thanos-io/thanos/pkg/targets/targetspb" 59 "github.com/thanos-io/thanos/pkg/testutil/e2eutil" 60 "github.com/thanos-io/thanos/test/e2e/e2ethanos" 61 ) 62 63 func defaultWebConfig() string { 64 // username: test, secret: test(bcrypt hash) 65 return ` 66 basic_auth_users: 67 test: $2y$10$IsC9GG9U61sPCuDwwwcnPuMRyzx62cIcdNRs4SIdKwgWihfX4IC.C 68 ` 69 } 70 71 func sortResults(res model.Vector) { 72 sort.Slice(res, func(i, j int) bool { 73 return res[i].String() < res[j].String() 74 }) 75 } 76 77 func TestQueryServiceAttribute(t *testing.T) { 78 t.Parallel() 79 80 e, err := e2e.NewDockerEnvironment("queryserviceattr") 81 testutil.Ok(t, err) 82 t.Cleanup(e2ethanos.CleanScenario(t, e)) 83 84 newJaegerRunnable := e.Runnable("jaeger"). 85 WithPorts( 86 map[string]int{ 87 "http": 16686, 88 "http.admin": 14269, 89 "jaeger.thrift-model.proto": 14250, 90 "jaeger.thrift": 14268, 91 }). 92 Init(e2e.StartOptions{ 93 Image: "jaegertracing/all-in-one:1.33", 94 Readiness: e2e.NewHTTPReadinessProbe("http.admin", "/", 200, 200), 95 }) 96 newJaeger := e2emon.AsInstrumented(newJaegerRunnable, "http.admin") 97 testutil.Ok(t, e2e.StartAndWaitReady(newJaeger)) 98 99 otelcolConfig := fmt.Sprintf(`--- 100 receivers: 101 otlp: 102 protocols: 103 grpc: 104 http: 105 106 processors: 107 batch: 108 109 exporters: 110 logging: 111 loglevel: debug 112 jaeger: 113 endpoint: %s 114 tls: 115 insecure: true 116 117 extensions: 118 health_check: 119 pprof: 120 zpages: 121 122 service: 123 extensions: [health_check,pprof,zpages] 124 pipelines: 125 traces: 126 receivers: [otlp] 127 processors: [batch] 128 exporters: [logging, jaeger]`, newJaeger.InternalEndpoint("jaeger.thrift-model.proto")) 129 130 t.Log(otelcolConfig) 131 132 otelcol := e.Runnable("otelcol").WithPorts(map[string]int{ 133 "grpc": 4317, 134 "http": 4318, 135 }) 136 137 otelcolFuture := otelcol.Future() 138 139 testutil.Ok(t, os.WriteFile(filepath.Join(e.SharedDir(), "otelcol.yml"), []byte(otelcolConfig), os.ModePerm)) 140 testutil.Ok(t, os.WriteFile(filepath.Join(e.SharedDir(), "spans.json"), []byte(``), os.ModePerm)) 141 142 otelcolRunnable := otelcolFuture.Init(e2e.StartOptions{ 143 Image: "otel/opentelemetry-collector-contrib:0.80.0", 144 Volumes: []string{ 145 fmt.Sprintf("%s:/otelcol.yml:ro", filepath.Join(e.SharedDir(), "otelcol.yml")), 146 }, 147 Command: e2e.NewCommandWithoutEntrypoint("/otelcol-contrib", "--config", "/otelcol.yml"), 148 }) 149 150 testutil.Ok(t, e2e.StartAndWaitReady(otelcolRunnable)) 151 152 q := e2ethanos.NewQuerierBuilder(e, "queriertester").WithTracingConfig(fmt.Sprintf(`type: OTLP 153 config: 154 client_type: grpc 155 insecure: true 156 endpoint: %s`, otelcolRunnable.InternalEndpoint("grpc"))).WithEnvVars(map[string]string{ 157 "OTEL_RESOURCE_ATTRIBUTES": "service.name=thanos-query", 158 }).Init() 159 testutil.Ok(t, e2e.StartAndWaitReady(q)) 160 161 instantQuery(t, 162 context.Background(), 163 q.Endpoint("http"), 164 func() string { return "time()" }, 165 time.Now, 166 promclient.QueryOptions{}, 167 1) 168 169 url := "http://" + strings.TrimSpace(newJaeger.Endpoint("http")+"/api/traces?service=thanos-query") 170 request, err := http.NewRequest("GET", url, nil) 171 testutil.Ok(t, err) 172 173 testutil.Ok(t, runutil.Retry(1*time.Second, make(<-chan struct{}), func() error { 174 response, err := http.DefaultClient.Do(request) 175 if err != nil { 176 return err 177 } 178 179 if response.StatusCode != http.StatusOK { 180 return errors.New("status code not OK") 181 } 182 183 defer response.Body.Close() 184 185 body, err := io.ReadAll(response.Body) 186 testutil.Ok(t, err) 187 188 resp := string(body) 189 if strings.Contains(resp, `"data":[]`) { 190 return errors.New("no data returned") 191 } 192 193 testutil.Assert(t, strings.Contains(resp, `"serviceName":"thanos-query"`)) 194 return nil 195 })) 196 } 197 198 func TestSidecarNotReady(t *testing.T) { 199 t.Parallel() 200 201 e, err := e2e.NewDockerEnvironment("sidecar-notReady") 202 testutil.Ok(t, err) 203 t.Cleanup(e2ethanos.CleanScenario(t, e)) 204 205 prom, sidecar := e2ethanos.NewPrometheusWithSidecar(e, "alone", e2ethanos.DefaultPromConfig("prom-alone", 0, "", "", e2ethanos.LocalPrometheusTarget), "", e2ethanos.DefaultPrometheusImage(), "") 206 testutil.Ok(t, e2e.StartAndWaitReady(prom, sidecar)) 207 testutil.Ok(t, prom.Stop()) 208 209 ctx, cancel := context.WithCancel(context.Background()) 210 defer cancel() 211 212 // Sidecar should not be ready - it cannot accept traffic if Prometheus is down. 213 testutil.Ok(t, runutil.Retry(1*time.Second, ctx.Done(), func() (rerr error) { 214 req, err := http.NewRequestWithContext(ctx, "GET", "http://"+sidecar.Endpoint("http")+"/-/ready", nil) 215 if err != nil { 216 return err 217 } 218 resp, err := http.DefaultClient.Do(req) 219 if err != nil { 220 return err 221 } 222 defer runutil.CloseWithErrCapture(&rerr, resp.Body, "closing resp body") 223 224 if resp.StatusCode == 200 { 225 return fmt.Errorf("got status code %d", resp.StatusCode) 226 } 227 return nil 228 })) 229 } 230 231 func TestQuery(t *testing.T) { 232 t.Parallel() 233 234 e, err := e2e.NewDockerEnvironment("e2e-test-query") 235 testutil.Ok(t, err) 236 t.Cleanup(e2ethanos.CleanScenario(t, e)) 237 238 receiver := e2ethanos.NewReceiveBuilder(e, "1").WithIngestionEnabled().Init() 239 testutil.Ok(t, e2e.StartAndWaitReady(receiver)) 240 241 prom1, sidecar1 := e2ethanos.NewPrometheusWithSidecar(e, "alone", e2ethanos.DefaultPromConfig("prom-alone", 0, "", "", e2ethanos.LocalPrometheusTarget), "", e2ethanos.DefaultPrometheusImage(), "") 242 prom2, sidecar2 := e2ethanos.NewPrometheusWithSidecar(e, "remote-and-sidecar", e2ethanos.DefaultPromConfig("prom-both-remote-write-and-sidecar", 1234, e2ethanos.RemoteWriteEndpoint(receiver.InternalEndpoint("remote-write")), "", e2ethanos.LocalPrometheusTarget), "", e2ethanos.DefaultPrometheusImage(), "") 243 prom3, sidecar3 := e2ethanos.NewPrometheusWithSidecar(e, "ha1", e2ethanos.DefaultPromConfig("prom-ha", 0, "", "", e2ethanos.LocalPrometheusTarget), "", e2ethanos.DefaultPrometheusImage(), "") 244 prom4, sidecar4 := e2ethanos.NewPrometheusWithSidecar(e, "ha2", e2ethanos.DefaultPromConfig("prom-ha", 1, "", "", e2ethanos.LocalPrometheusTarget), "", e2ethanos.DefaultPrometheusImage(), "") 245 testutil.Ok(t, e2e.StartAndWaitReady(prom1, sidecar1, prom2, sidecar2, prom3, sidecar3, prom4, sidecar4)) 246 247 // Querier. Both fileSD and directly by flags. 248 q := e2ethanos.NewQuerierBuilder(e, "1", sidecar1.InternalEndpoint("grpc"), sidecar2.InternalEndpoint("grpc"), receiver.InternalEndpoint("grpc")). 249 WithFileSDStoreAddresses(sidecar3.InternalEndpoint("grpc"), sidecar4.InternalEndpoint("grpc")).Init() 250 testutil.Ok(t, e2e.StartAndWaitReady(q)) 251 252 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) 253 t.Cleanup(cancel) 254 255 testutil.Ok(t, q.WaitSumMetricsWithOptions(e2emon.Equals(5), []string{"thanos_store_nodes_grpc_connections"}, e2emon.WaitMissingMetrics())) 256 257 queryAndAssertSeries(t, ctx, q.Endpoint("http"), e2ethanos.QueryUpWithoutInstance, time.Now, promclient.QueryOptions{ 258 Deduplicate: false, 259 }, []model.Metric{ 260 { 261 "job": "myself", 262 "prometheus": "prom-alone", 263 "replica": "0", 264 }, 265 { 266 "job": "myself", 267 "prometheus": "prom-both-remote-write-and-sidecar", 268 "receive": "receive-1", 269 "replica": "1234", 270 "tenant_id": "default-tenant", 271 }, 272 { 273 "job": "myself", 274 "prometheus": "prom-both-remote-write-and-sidecar", 275 "replica": "1234", 276 }, 277 { 278 "job": "myself", 279 "prometheus": "prom-ha", 280 "replica": "0", 281 }, 282 { 283 "job": "myself", 284 "prometheus": "prom-ha", 285 "replica": "1", 286 }, 287 }) 288 289 // With deduplication. 290 queryAndAssertSeries(t, ctx, q.Endpoint("http"), e2ethanos.QueryUpWithoutInstance, time.Now, promclient.QueryOptions{ 291 Deduplicate: true, 292 }, []model.Metric{ 293 { 294 "job": "myself", 295 "prometheus": "prom-alone", 296 }, 297 { 298 "job": "myself", 299 "prometheus": "prom-both-remote-write-and-sidecar", 300 "receive": "receive-1", 301 "tenant_id": "default-tenant", 302 }, 303 { 304 "job": "myself", 305 "prometheus": "prom-both-remote-write-and-sidecar", 306 }, 307 { 308 "job": "myself", 309 "prometheus": "prom-ha", 310 }, 311 }) 312 } 313 314 func TestQueryExternalPrefixWithoutReverseProxy(t *testing.T) { 315 t.Parallel() 316 317 e, err := e2e.NewDockerEnvironment("route-prefix") 318 testutil.Ok(t, err) 319 t.Cleanup(e2ethanos.CleanScenario(t, e)) 320 321 externalPrefix := "test" 322 323 q := e2ethanos.NewQuerierBuilder(e, "1"). 324 WithExternalPrefix(externalPrefix).Init() 325 testutil.Ok(t, e2e.StartAndWaitReady(q)) 326 327 checkNetworkRequests(t, "http://"+q.Endpoint("http")+"/"+externalPrefix+"/graph") 328 } 329 330 func TestQueryExternalPrefix(t *testing.T) { 331 t.Parallel() 332 333 e, err := e2e.NewDockerEnvironment("external-prefix") 334 testutil.Ok(t, err) 335 t.Cleanup(e2ethanos.CleanScenario(t, e)) 336 337 externalPrefix := "thanos" 338 339 q := e2ethanos.NewQuerierBuilder(e, "1"). 340 WithExternalPrefix(externalPrefix).Init() 341 testutil.Ok(t, e2e.StartAndWaitReady(q)) 342 343 querierURL := urlParse(t, "http://"+q.Endpoint("http")+"/"+externalPrefix) 344 345 querierProxy := httptest.NewServer(e2ethanos.NewSingleHostReverseProxy(querierURL, externalPrefix)) 346 t.Cleanup(querierProxy.Close) 347 348 checkNetworkRequests(t, querierProxy.URL+"/"+externalPrefix+"/graph") 349 } 350 351 func TestQueryExternalPrefixAndRoutePrefix(t *testing.T) { 352 t.Parallel() 353 354 e, err := e2e.NewDockerEnvironment("prefix") 355 testutil.Ok(t, err) 356 t.Cleanup(e2ethanos.CleanScenario(t, e)) 357 358 externalPrefix := "thanos" 359 routePrefix := "test" 360 361 q := e2ethanos.NewQuerierBuilder(e, "1"). 362 WithRoutePrefix(routePrefix). 363 WithExternalPrefix(externalPrefix). 364 Init() 365 testutil.Ok(t, err) 366 testutil.Ok(t, e2e.StartAndWaitReady(q)) 367 368 querierURL := urlParse(t, "http://"+q.Endpoint("http")+"/"+routePrefix) 369 370 querierProxy := httptest.NewServer(e2ethanos.NewSingleHostReverseProxy(querierURL, externalPrefix)) 371 t.Cleanup(querierProxy.Close) 372 373 checkNetworkRequests(t, querierProxy.URL+"/"+externalPrefix+"/graph") 374 } 375 376 func TestQueryLabelNames(t *testing.T) { 377 t.Parallel() 378 379 e, err := e2e.NewDockerEnvironment("label-names") 380 testutil.Ok(t, err) 381 t.Cleanup(e2ethanos.CleanScenario(t, e)) 382 383 receiver := e2ethanos.NewReceiveBuilder(e, "1").WithIngestionEnabled().Init() 384 testutil.Ok(t, e2e.StartAndWaitReady(receiver)) 385 386 prom1, sidecar1 := e2ethanos.NewPrometheusWithSidecar(e, "alone", e2ethanos.DefaultPromConfig("prom-alone", 0, "", "", e2ethanos.LocalPrometheusTarget), "", e2ethanos.DefaultPrometheusImage(), "") 387 prom2, sidecar2 := e2ethanos.NewPrometheusWithSidecar(e, "remote-and-sidecar", e2ethanos.DefaultPromConfig("prom-both-remote-write-and-sidecar", 1234, e2ethanos.RemoteWriteEndpoint(receiver.InternalEndpoint("remote-write")), "", e2ethanos.LocalPrometheusTarget), "", e2ethanos.DefaultPrometheusImage(), "") 388 testutil.Ok(t, e2e.StartAndWaitReady(prom1, sidecar1, prom2, sidecar2)) 389 390 q := e2ethanos.NewQuerierBuilder(e, "1", sidecar1.InternalEndpoint("grpc"), sidecar2.InternalEndpoint("grpc"), receiver.InternalEndpoint("grpc")).Init() 391 testutil.Ok(t, e2e.StartAndWaitReady(q)) 392 393 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) 394 t.Cleanup(cancel) 395 396 now := time.Now() 397 labelNames(t, ctx, q.Endpoint("http"), nil, timestamp.FromTime(now.Add(-time.Hour)), timestamp.FromTime(now.Add(time.Hour)), func(res []string) bool { 398 return len(res) > 0 399 }) 400 401 // Outside time range. 402 labelNames(t, ctx, q.Endpoint("http"), nil, timestamp.FromTime(now.Add(-24*time.Hour)), timestamp.FromTime(now.Add(-23*time.Hour)), func(res []string) bool { 403 return len(res) == 0 404 }) 405 406 labelNames(t, ctx, q.Endpoint("http"), []*labels.Matcher{{Type: labels.MatchEqual, Name: "__name__", Value: "up"}}, 407 timestamp.FromTime(now.Add(-time.Hour)), timestamp.FromTime(now.Add(time.Hour)), func(res []string) bool { 408 // Expected result: [__name__, instance, job, prometheus, replica, receive, tenant_id] 409 // Pre-labelnames pushdown we've done Select() over all series and picked out the label names hence they all had external labels. 410 // With labelnames pushdown we had to extend the LabelNames() call to enrich the response with the external labelset when there is more than one label. 411 return len(res) == 7 412 }, 413 ) 414 415 // There is no matched series. 416 labelNames(t, ctx, q.Endpoint("http"), []*labels.Matcher{{Type: labels.MatchEqual, Name: "__name__", Value: "foobar"}}, 417 timestamp.FromTime(now.Add(-time.Hour)), timestamp.FromTime(now.Add(time.Hour)), func(res []string) bool { 418 return len(res) == 0 419 }, 420 ) 421 } 422 423 func TestQueryLabelValues(t *testing.T) { 424 t.Parallel() 425 426 e, err := e2e.NewDockerEnvironment("label-values") 427 testutil.Ok(t, err) 428 t.Cleanup(e2ethanos.CleanScenario(t, e)) 429 430 receiver := e2ethanos.NewReceiveBuilder(e, "1").WithIngestionEnabled().Init() 431 testutil.Ok(t, e2e.StartAndWaitReady(receiver)) 432 433 prom1, sidecar1 := e2ethanos.NewPrometheusWithSidecar(e, "alone", e2ethanos.DefaultPromConfig("prom-alone", 0, "", "", e2ethanos.LocalPrometheusTarget), "", e2ethanos.DefaultPrometheusImage(), "") 434 prom2, sidecar2 := e2ethanos.NewPrometheusWithSidecar(e, "remote-and-sidecar", e2ethanos.DefaultPromConfig("prom-both-remote-write-and-sidecar", 1234, e2ethanos.RemoteWriteEndpoint(receiver.InternalEndpoint("remote-write")), ""), "", e2ethanos.DefaultPrometheusImage(), "") 435 testutil.Ok(t, e2e.StartAndWaitReady(prom1, sidecar1, prom2, sidecar2)) 436 437 q := e2ethanos.NewQuerierBuilder(e, "1", sidecar1.InternalEndpoint("grpc"), sidecar2.InternalEndpoint("grpc"), receiver.InternalEndpoint("grpc")).Init() 438 testutil.Ok(t, e2e.StartAndWaitReady(q)) 439 440 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) 441 t.Cleanup(cancel) 442 443 now := time.Now() 444 labelValues(t, ctx, q.Endpoint("http"), "instance", nil, timestamp.FromTime(now.Add(-time.Hour)), timestamp.FromTime(now.Add(time.Hour)), func(res []string) bool { 445 return len(res) == 1 && res[0] == "localhost:9090" 446 }) 447 448 // Outside time range. 449 labelValues(t, ctx, q.Endpoint("http"), "instance", nil, timestamp.FromTime(now.Add(-24*time.Hour)), timestamp.FromTime(now.Add(-23*time.Hour)), func(res []string) bool { 450 return len(res) == 0 451 }) 452 453 labelValues(t, ctx, q.Endpoint("http"), "__name__", []*labels.Matcher{{Type: labels.MatchEqual, Name: "__name__", Value: "up"}}, 454 timestamp.FromTime(now.Add(-time.Hour)), timestamp.FromTime(now.Add(time.Hour)), func(res []string) bool { 455 return len(res) == 1 && res[0] == "up" 456 }, 457 ) 458 459 labelValues(t, ctx, q.Endpoint("http"), "__name__", []*labels.Matcher{{Type: labels.MatchEqual, Name: "__name__", Value: "foobar"}}, 460 timestamp.FromTime(now.Add(-time.Hour)), timestamp.FromTime(now.Add(time.Hour)), func(res []string) bool { 461 return len(res) == 0 462 }, 463 ) 464 } 465 466 func TestQueryWithAuthorizedSidecar(t *testing.T) { 467 t.Parallel() 468 469 e, err := e2e.NewDockerEnvironment("sidecar-auth") 470 testutil.Ok(t, err) 471 t.Cleanup(e2ethanos.CleanScenario(t, e)) 472 473 prom, sidecar := e2ethanos.NewPrometheusWithSidecar(e, "alone", e2ethanos.DefaultPromConfig("prom-alone", 0, "", "", e2ethanos.LocalPrometheusTarget), defaultWebConfig(), e2ethanos.DefaultPrometheusImage(), "") 474 testutil.Ok(t, e2e.StartAndWaitReady(prom, sidecar)) 475 476 q := e2ethanos.NewQuerierBuilder(e, "1", []string{sidecar.InternalEndpoint("grpc")}...).Init() 477 testutil.Ok(t, e2e.StartAndWaitReady(q)) 478 479 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) 480 t.Cleanup(cancel) 481 482 testutil.Ok(t, q.WaitSumMetricsWithOptions(e2emon.Equals(1), []string{"thanos_store_nodes_grpc_connections"}, e2emon.WaitMissingMetrics())) 483 484 queryAndAssertSeries(t, ctx, q.Endpoint("http"), e2ethanos.QueryUpWithoutInstance, time.Now, promclient.QueryOptions{ 485 Deduplicate: false, 486 }, []model.Metric{ 487 { 488 "job": "myself", 489 "prometheus": "prom-alone", 490 "replica": "0", 491 }, 492 }) 493 } 494 495 func TestQueryCompatibilityWithPreInfoAPI(t *testing.T) { 496 t.Parallel() 497 if runtime.GOARCH != "amd64" { 498 t.Skip("Skip pre-info API test because of lack of multi-arch image for Thanos v0.22.0.") 499 } 500 501 for i, tcase := range []struct { 502 queryImage string 503 sidecarImage string 504 }{ 505 { 506 queryImage: e2ethanos.DefaultImage(), 507 sidecarImage: "quay.io/thanos/thanos:v0.22.0", // Thanos components from version before 0.23 does not have new InfoAPI. 508 }, 509 { 510 queryImage: "quay.io/thanos/thanos:v0.22.0", // Thanos querier from version before 0.23 did not know about InfoAPI. 511 sidecarImage: e2ethanos.DefaultImage(), 512 }, 513 } { 514 i := i 515 t.Run(fmt.Sprintf("%+v", tcase), func(t *testing.T) { 516 e, err := e2e.NewDockerEnvironment(fmt.Sprintf("query-comp-%d", i)) 517 testutil.Ok(t, err) 518 t.Cleanup(e2ethanos.CleanScenario(t, e)) 519 520 qBuilder := e2ethanos.NewQuerierBuilder(e, "1") 521 522 // Use qBuilder work dir to share rules. 523 promRulesSubDir := "rules" 524 testutil.Ok(t, os.MkdirAll(filepath.Join(qBuilder.Dir(), promRulesSubDir), os.ModePerm)) 525 // Create the abort_on_partial_response alert for Prometheus. 526 // We don't create the warn_on_partial_response alert as Prometheus has strict yaml unmarshalling. 527 createRuleFile(t, filepath.Join(qBuilder.Dir(), promRulesSubDir, "rules.yaml"), testAlertRuleAbortOnPartialResponse) 528 529 p1, s1 := e2ethanos.NewPrometheusWithSidecarCustomImage( 530 e, 531 "p1", 532 e2ethanos.DefaultPromConfig("p1", 0, "", filepath.Join(qBuilder.InternalDir(), promRulesSubDir, "*.yaml"), e2ethanos.LocalPrometheusTarget, qBuilder.InternalEndpoint("http")), 533 "", 534 e2ethanos.DefaultPrometheusImage(), 535 "", 536 tcase.sidecarImage, 537 e2ethanos.FeatureExemplarStorage, 538 ) 539 testutil.Ok(t, e2e.StartAndWaitReady(p1, s1)) 540 541 // Newest querier with old --rules --meta etc flags. 542 q := qBuilder. 543 WithStoreAddresses(s1.InternalEndpoint("grpc")). 544 WithMetadataAddresses(s1.InternalEndpoint("grpc")). 545 WithExemplarAddresses(s1.InternalEndpoint("grpc")). 546 WithTargetAddresses(s1.InternalEndpoint("grpc")). 547 WithRuleAddresses(s1.InternalEndpoint("grpc")). 548 WithTracingConfig(fmt.Sprintf(`type: JAEGER 549 config: 550 sampler_type: const 551 sampler_param: 1 552 service_name: %s`, qBuilder.Name())). // Use fake tracing config to trigger exemplar. 553 WithImage(tcase.queryImage). 554 Init() 555 testutil.Ok(t, e2e.StartAndWaitReady(q)) 556 557 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) 558 t.Cleanup(cancel) 559 560 // We should have single TCP connection, since all APIs are against the same server. 561 testutil.Ok(t, q.WaitSumMetricsWithOptions(e2emon.Equals(1), []string{"thanos_store_nodes_grpc_connections"}, e2emon.WaitMissingMetrics())) 562 563 queryAndAssertSeries(t, ctx, q.Endpoint("http"), e2ethanos.QueryUpWithoutInstance, time.Now, promclient.QueryOptions{ 564 Deduplicate: false, 565 }, []model.Metric{ 566 { 567 "job": "myself", 568 "prometheus": "p1", 569 "replica": "0", 570 }, 571 }) 572 573 // We expect rule and other APIs to work. 574 575 // Metadata. 576 { 577 var promMeta map[string][]metadatapb.Meta 578 // Wait metadata response to be ready as Prometheus gets metadata after scrape. 579 testutil.Ok(t, runutil.Retry(3*time.Second, ctx.Done(), func() error { 580 promMeta, err = promclient.NewDefaultClient().MetricMetadataInGRPC(ctx, urlParse(t, "http://"+p1.Endpoint("http")), "", -1) 581 testutil.Ok(t, err) 582 if len(promMeta) > 0 { 583 return nil 584 } 585 return fmt.Errorf("empty metadata response from Prometheus") 586 })) 587 588 thanosMeta, err := promclient.NewDefaultClient().MetricMetadataInGRPC(ctx, urlParse(t, "http://"+q.Endpoint("http")), "", -1) 589 testutil.Ok(t, err) 590 testutil.Assert(t, len(thanosMeta) > 0, "got empty metadata response from Thanos") 591 592 // Metadata response from Prometheus and Thanos Querier should be the same after deduplication. 593 metadataEqual(t, thanosMeta, promMeta) 594 } 595 596 // Exemplars. 597 { 598 now := time.Now() 599 start := timestamp.FromTime(now.Add(-time.Hour)) 600 end := timestamp.FromTime(now.Add(time.Hour)) 601 602 // Send HTTP requests to thanos query to trigger exemplars. 603 labelNames(t, ctx, q.Endpoint("http"), nil, start, end, func(res []string) bool { 604 return true 605 }) 606 607 queryExemplars(t, ctx, q.Endpoint("http"), `http_request_duration_seconds_bucket{handler="label_names"}`, start, end, exemplarsOnExpectedSeries(map[string]string{ 608 "__name__": "http_request_duration_seconds_bucket", 609 "handler": "label_names", 610 "job": "myself", 611 "method": "get", 612 "prometheus": "p1", 613 })) 614 } 615 616 // Targets. 617 { 618 targetAndAssert(t, ctx, q.Endpoint("http"), "", &targetspb.TargetDiscovery{ 619 ActiveTargets: []*targetspb.ActiveTarget{ 620 { 621 DiscoveredLabels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 622 {Name: "__address__", Value: "localhost:9090"}, 623 {Name: "__metrics_path__", Value: "/metrics"}, 624 {Name: "__scheme__", Value: "http"}, 625 {Name: "__scrape_interval__", Value: "1s"}, 626 {Name: "__scrape_timeout__", Value: "1s"}, 627 {Name: "job", Value: "myself"}, 628 {Name: "prometheus", Value: "p1"}, 629 }}, 630 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 631 {Name: "instance", Value: "localhost:9090"}, 632 {Name: "job", Value: "myself"}, 633 {Name: "prometheus", Value: "p1"}, 634 }}, 635 ScrapePool: "myself", 636 ScrapeUrl: "http://localhost:9090/metrics", 637 Health: targetspb.TargetHealth_UP, 638 }, 639 { 640 DiscoveredLabels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 641 {Name: "__address__", Value: fmt.Sprintf("query-comp-%d-querier-1:8080", i)}, 642 {Name: "__metrics_path__", Value: "/metrics"}, 643 {Name: "__scheme__", Value: "http"}, 644 {Name: "__scrape_interval__", Value: "1s"}, 645 {Name: "__scrape_timeout__", Value: "1s"}, 646 {Name: "job", Value: "myself"}, 647 {Name: "prometheus", Value: "p1"}, 648 }}, 649 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 650 {Name: "instance", Value: fmt.Sprintf("query-comp-%d-querier-1:8080", i)}, 651 {Name: "job", Value: "myself"}, 652 {Name: "prometheus", Value: "p1"}, 653 }}, 654 ScrapePool: "myself", 655 ScrapeUrl: fmt.Sprintf("http://query-comp-%d-querier-1:8080/metrics", i), 656 Health: targetspb.TargetHealth_UP, 657 }, 658 }, 659 DroppedTargets: []*targetspb.DroppedTarget{}, 660 }) 661 } 662 663 // Rules. 664 { 665 ruleAndAssert(t, ctx, q.Endpoint("http"), "", []*rulespb.RuleGroup{ 666 { 667 Name: "example_abort", 668 File: q.Dir() + "/rules/rules.yaml", 669 Rules: []*rulespb.Rule{ 670 rulespb.NewAlertingRule(&rulespb.Alert{ 671 Name: "TestAlert_AbortOnPartialResponse", 672 State: rulespb.AlertState_FIRING, 673 Query: "absent(some_metric)", 674 Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{ 675 {Name: "prometheus", Value: "p1"}, 676 {Name: "severity", Value: "page"}, 677 }}, 678 Health: string(rules.HealthGood), 679 }), 680 }, 681 }, 682 }) 683 } 684 }) 685 } 686 } 687 688 type fakeMetricSample struct { 689 label string 690 value int64 691 timestampUnixNano int64 692 } 693 694 func newSample(s fakeMetricSample) model.Sample { 695 return model.Sample{ 696 Metric: map[model.LabelName]model.LabelValue{ 697 "__name__": "my_fake_metric", 698 "instance": model.LabelValue(s.label), 699 }, 700 Value: model.SampleValue(s.value), 701 Timestamp: model.TimeFromUnixNano(s.timestampUnixNano), 702 } 703 } 704 705 func TestQueryStoreMetrics(t *testing.T) { 706 t.Parallel() 707 708 // Build up. 709 e, err := e2e.New(e2e.WithName("storemetrics01")) 710 testutil.Ok(t, err) 711 t.Cleanup(e2ethanos.CleanScenario(t, e)) 712 713 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) 714 t.Cleanup(cancel) 715 716 bucket := "store-gw-test" 717 minio := e2edb.NewMinio(e, "thanos-minio", bucket, e2edb.WithMinioTLS()) 718 testutil.Ok(t, e2e.StartAndWaitReady(minio)) 719 720 l := log.NewLogfmtLogger(os.Stdout) 721 bkt, err := s3.NewBucketWithConfig(l, e2ethanos.NewS3Config(bucket, minio.Endpoint("http"), minio.Dir()), "test") 722 testutil.Ok(t, err) 723 724 // Preparing 3 different blocks for the tests. 725 { 726 blockSizes := []struct { 727 samples int 728 series int 729 name string 730 }{ 731 {samples: 10, series: 1, name: "one_series"}, 732 {samples: 10, series: 1001, name: "thousand_one_series"}, 733 {samples: 10, series: 10001, name: "inf_series"}, 734 } 735 now := time.Now() 736 externalLabels := labels.FromStrings("prometheus", "p1", "replica", "0") 737 dir := filepath.Join(e.SharedDir(), "tmp") 738 testutil.Ok(t, os.MkdirAll(filepath.Join(e.SharedDir(), dir), os.ModePerm)) 739 for _, blockSize := range blockSizes { 740 series := make([]labels.Labels, blockSize.series) 741 for i := 0; i < blockSize.series; i++ { 742 bigSeriesLabels := labels.FromStrings("__name__", blockSize.name, "instance", fmt.Sprintf("foo_%d", i)) 743 series[i] = bigSeriesLabels 744 } 745 blockID, err := e2eutil.CreateBlockWithBlockDelay(ctx, 746 dir, 747 series, 748 blockSize.samples, 749 timestamp.FromTime(now), 750 timestamp.FromTime(now.Add(2*time.Hour)), 751 30*time.Minute, 752 externalLabels, 753 0, 754 metadata.NoneFunc, 755 ) 756 testutil.Ok(t, err) 757 testutil.Ok(t, objstore.UploadDir(ctx, l, bkt, path.Join(dir, blockID.String()), blockID.String())) 758 } 759 } 760 761 storeGW := e2ethanos.NewStoreGW( 762 e, 763 "s1", 764 client.BucketConfig{ 765 Type: client.S3, 766 Config: e2ethanos.NewS3Config(bucket, minio.InternalEndpoint("http"), minio.InternalDir()), 767 }, 768 "", 769 "", 770 nil, 771 ) 772 773 sampleBuckets := []float64{100, 1000, 10000, 100000} 774 seriesBuckets := []float64{10, 100, 1000, 10000} 775 querier := e2ethanos.NewQuerierBuilder(e, "1", storeGW.InternalEndpoint("grpc")).WithTelemetryQuantiles(nil, sampleBuckets, seriesBuckets).Init() 776 testutil.Ok(t, e2e.StartAndWaitReady(storeGW, querier)) 777 testutil.Ok(t, storeGW.WaitSumMetrics(e2emon.Equals(3), "thanos_blocks_meta_synced")) 778 779 // Querying the series in the previously created blocks to ensure we produce Store API query metrics. 780 { 781 instantQuery(t, ctx, querier.Endpoint("http"), func() string { 782 return "max_over_time(one_series{instance='foo_0'}[2h])" 783 }, time.Now, promclient.QueryOptions{ 784 Deduplicate: true, 785 }, 1) 786 testutil.Ok(t, err) 787 788 instantQuery(t, ctx, querier.Endpoint("http"), func() string { 789 return "max_over_time(thousand_one_series[2h])" 790 }, time.Now, promclient.QueryOptions{ 791 Deduplicate: true, 792 }, 1001) 793 testutil.Ok(t, err) 794 795 instantQuery(t, ctx, querier.Endpoint("http"), func() string { 796 return "max_over_time(inf_series[2h])" 797 }, time.Now, promclient.QueryOptions{ 798 Deduplicate: true, 799 }, 10001) 800 testutil.Ok(t, err) 801 } 802 803 mon, err := e2emon.Start(e) 804 testutil.Ok(t, err) 805 806 queryWaitAndAssert(t, ctx, mon.GetMonitoringRunnable().Endpoint(e2edb.AccessPortName), func() string { 807 return "thanos_store_api_query_duration_seconds_count{samples_le='100000',series_le='10000'}" 808 }, time.Now, promclient.QueryOptions{ 809 Deduplicate: true, 810 }, model.Vector{ 811 &model.Sample{ 812 Metric: model.Metric{ 813 "__name__": "thanos_store_api_query_duration_seconds_count", 814 "instance": "storemetrics01-querier-1:8080", 815 "job": "querier-1", 816 "samples_le": "100000", 817 "series_le": "10000", 818 }, 819 Value: model.SampleValue(1), 820 }, 821 }) 822 823 queryWaitAndAssert(t, ctx, mon.GetMonitoringRunnable().Endpoint(e2edb.AccessPortName), func() string { 824 return "thanos_store_api_query_duration_seconds_count{samples_le='100',series_le='10'}" 825 }, time.Now, promclient.QueryOptions{ 826 Deduplicate: true, 827 }, model.Vector{ 828 &model.Sample{ 829 Metric: model.Metric{ 830 "__name__": "thanos_store_api_query_duration_seconds_count", 831 "instance": "storemetrics01-querier-1:8080", 832 "job": "querier-1", 833 "samples_le": "100", 834 "series_le": "10", 835 }, 836 Value: model.SampleValue(1), 837 }, 838 }) 839 840 queryWaitAndAssert(t, ctx, mon.GetMonitoringRunnable().Endpoint(e2edb.AccessPortName), func() string { 841 return "thanos_store_api_query_duration_seconds_count{samples_le='+Inf',series_le='+Inf'}" 842 }, time.Now, promclient.QueryOptions{ 843 Deduplicate: true, 844 }, model.Vector{ 845 &model.Sample{ 846 Metric: model.Metric{ 847 "__name__": "thanos_store_api_query_duration_seconds_count", 848 "instance": "storemetrics01-querier-1:8080", 849 "job": "querier-1", 850 "samples_le": "+Inf", 851 "series_le": "+Inf", 852 }, 853 Value: model.SampleValue(1), 854 }, 855 }) 856 857 } 858 859 // Regression test for https://github.com/thanos-io/thanos/issues/5033. 860 // Tests whether queries work with mixed sources, and with functions 861 // that we are pushing down: min, max, min_over_time, max_over_time, 862 // group. 863 func TestSidecarStorePushdown(t *testing.T) { 864 t.Parallel() 865 866 // Build up. 867 e, err := e2e.NewDockerEnvironment("sidecar-pushdown") 868 testutil.Ok(t, err) 869 t.Cleanup(e2ethanos.CleanScenario(t, e)) 870 871 prom1, sidecar1 := e2ethanos.NewPrometheusWithSidecar(e, "p1", e2ethanos.DefaultPromConfig("p1", 0, "", ""), "", e2ethanos.DefaultPrometheusImage(), "", "remote-write-receiver") 872 testutil.Ok(t, e2e.StartAndWaitReady(prom1, sidecar1)) 873 874 const bucket = "store-gateway-test" 875 m := e2edb.NewMinio(e, "thanos-minio", bucket, e2edb.WithMinioTLS()) 876 testutil.Ok(t, e2e.StartAndWaitReady(m)) 877 878 dir := filepath.Join(e.SharedDir(), "tmp") 879 testutil.Ok(t, os.MkdirAll(filepath.Join(e.SharedDir(), dir), os.ModePerm)) 880 881 series := []labels.Labels{labels.FromStrings("__name__", "my_fake_metric", "instance", "foo")} 882 extLset := labels.FromStrings("prometheus", "p1", "replica", "0") 883 884 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) 885 t.Cleanup(cancel) 886 887 now := time.Now() 888 id1, err := e2eutil.CreateBlockWithBlockDelay(ctx, dir, series, 10, timestamp.FromTime(now), timestamp.FromTime(now.Add(2*time.Hour)), 30*time.Minute, extLset, 0, metadata.NoneFunc) 889 testutil.Ok(t, err) 890 891 l := log.NewLogfmtLogger(os.Stdout) 892 bkt, err := s3.NewBucketWithConfig(l, e2ethanos.NewS3Config(bucket, m.Endpoint("http"), m.Dir()), "test") 893 testutil.Ok(t, err) 894 testutil.Ok(t, objstore.UploadDir(ctx, l, bkt, path.Join(dir, id1.String()), id1.String())) 895 896 s1 := e2ethanos.NewStoreGW( 897 e, 898 "1", 899 client.BucketConfig{ 900 Type: client.S3, 901 Config: e2ethanos.NewS3Config(bucket, m.InternalEndpoint("http"), m.InternalDir()), 902 }, 903 "", 904 "", 905 nil, 906 ) 907 testutil.Ok(t, e2e.StartAndWaitReady(s1)) 908 909 q := e2ethanos.NewQuerierBuilder(e, "1", s1.InternalEndpoint("grpc"), sidecar1.InternalEndpoint("grpc")).WithEnabledFeatures([]string{"query-pushdown"}).Init() 910 testutil.Ok(t, e2e.StartAndWaitReady(q)) 911 testutil.Ok(t, s1.WaitSumMetrics(e2emon.Equals(1), "thanos_blocks_meta_synced")) 912 913 testutil.Ok(t, synthesizeFakeMetricSamples(ctx, prom1, []fakeMetricSample{ 914 { 915 label: "foo", 916 value: 123, 917 timestampUnixNano: now.UnixNano(), 918 }, 919 })) 920 921 queryAndAssertSeries(t, ctx, q.Endpoint("http"), func() string { 922 return "max_over_time(my_fake_metric[2h])" 923 }, time.Now, promclient.QueryOptions{ 924 Deduplicate: true, 925 }, []model.Metric{ 926 { 927 "instance": "foo", 928 "prometheus": "p1", 929 }, 930 }) 931 932 queryAndAssertSeries(t, ctx, q.Endpoint("http"), func() string { 933 return "max(my_fake_metric) by (__name__, instance)" 934 }, time.Now, promclient.QueryOptions{ 935 Deduplicate: true, 936 }, []model.Metric{ 937 { 938 "instance": "foo", 939 "__name__": "my_fake_metric", 940 }, 941 }) 942 943 queryAndAssertSeries(t, ctx, q.Endpoint("http"), func() string { 944 return "min_over_time(my_fake_metric[2h])" 945 }, time.Now, promclient.QueryOptions{ 946 Deduplicate: true, 947 }, []model.Metric{ 948 { 949 "instance": "foo", 950 "prometheus": "p1", 951 }, 952 }) 953 954 queryAndAssertSeries(t, ctx, q.Endpoint("http"), func() string { 955 return "min(my_fake_metric) by (instance, __name__)" 956 }, time.Now, promclient.QueryOptions{ 957 Deduplicate: true, 958 }, []model.Metric{ 959 { 960 "instance": "foo", 961 "__name__": "my_fake_metric", 962 }, 963 }) 964 965 queryAndAssertSeries(t, ctx, q.Endpoint("http"), func() string { 966 return "group(my_fake_metric) by (__name__, instance)" 967 }, time.Now, promclient.QueryOptions{ 968 Deduplicate: true, 969 }, []model.Metric{ 970 { 971 "instance": "foo", 972 "__name__": "my_fake_metric", 973 }, 974 }) 975 } 976 977 type seriesWithLabels struct { 978 intLabels labels.Labels 979 extLabels labels.Labels 980 } 981 982 func TestQueryStoreDedup(t *testing.T) { 983 t.Parallel() 984 985 e, err := e2e.New(e2e.WithName("storededup")) 986 testutil.Ok(t, err) 987 t.Cleanup(e2ethanos.CleanScenario(t, e)) 988 989 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) 990 t.Cleanup(cancel) 991 992 bucket := "store-gw-dedup-test" 993 minio := e2edb.NewMinio(e, "thanos-minio", bucket, e2edb.WithMinioTLS()) 994 testutil.Ok(t, e2e.StartAndWaitReady(minio)) 995 996 l := log.NewLogfmtLogger(os.Stdout) 997 bkt, err := s3.NewBucketWithConfig(l, e2ethanos.NewS3Config(bucket, minio.Endpoint("http"), minio.Dir()), "test") 998 testutil.Ok(t, err) 999 1000 storeGW := e2ethanos.NewStoreGW( 1001 e, 1002 "s1", 1003 client.BucketConfig{ 1004 Type: client.S3, 1005 Config: e2ethanos.NewS3Config(bucket, minio.InternalEndpoint("http"), minio.InternalDir()), 1006 }, 1007 "", 1008 "", 1009 nil, 1010 ) 1011 testutil.Ok(t, e2e.StartAndWaitReady(storeGW)) 1012 1013 tests := []struct { 1014 extReplicaLabel string 1015 intReplicaLabel string 1016 desc string 1017 blockFinderLabel string 1018 series []seriesWithLabels 1019 expectedSeries int 1020 }{ 1021 { 1022 desc: "Deduplication works with external label", 1023 extReplicaLabel: "replica", 1024 series: []seriesWithLabels{ 1025 { 1026 intLabels: labels.FromStrings("__name__", "simple_series"), 1027 extLabels: labels.FromStrings("replica", "a"), 1028 }, 1029 { 1030 intLabels: labels.FromStrings("__name__", "simple_series"), 1031 extLabels: labels.FromStrings("replica", "b"), 1032 }, 1033 }, 1034 blockFinderLabel: "dedupext", 1035 expectedSeries: 1, 1036 }, 1037 { 1038 desc: "Deduplication works on external label with resorting required", 1039 extReplicaLabel: "a", 1040 series: []seriesWithLabels{ 1041 { 1042 intLabels: labels.FromStrings("__name__", "simple_series"), 1043 extLabels: labels.FromStrings("a", "1", "b", "1"), 1044 }, 1045 { 1046 intLabels: labels.FromStrings("__name__", "simple_series"), 1047 extLabels: labels.FromStrings("a", "1", "b", "2"), 1048 }, 1049 { 1050 intLabels: labels.FromStrings("__name__", "simple_series"), 1051 extLabels: labels.FromStrings("a", "2", "b", "1"), 1052 }, 1053 { 1054 intLabels: labels.FromStrings("__name__", "simple_series"), 1055 extLabels: labels.FromStrings("a", "2", "b", "2"), 1056 }, 1057 }, 1058 blockFinderLabel: "dedupextresort", 1059 expectedSeries: 2, 1060 }, 1061 { 1062 desc: "Deduplication works with internal label", 1063 intReplicaLabel: "replica", 1064 series: []seriesWithLabels{ 1065 { 1066 intLabels: labels.FromStrings("__name__", "simple_series", "replica", "a"), 1067 }, 1068 { 1069 intLabels: labels.FromStrings("__name__", "simple_series", "replica", "b"), 1070 }, 1071 }, 1072 blockFinderLabel: "dedupint", 1073 expectedSeries: 1, 1074 }, 1075 // This is a regression test for the bug outlined in https://github.com/thanos-io/thanos/issues/6257. 1076 { 1077 desc: "Deduplication works on internal label with resorting required", 1078 intReplicaLabel: "a", 1079 series: []seriesWithLabels{ 1080 { 1081 intLabels: labels.FromStrings("__name__", "simple_series", "a", "1", "b", "1"), 1082 }, 1083 { 1084 intLabels: labels.FromStrings("__name__", "simple_series", "a", "1", "b", "2"), 1085 }, 1086 { 1087 intLabels: labels.FromStrings("__name__", "simple_series", "a", "2", "b", "1"), 1088 }, 1089 { 1090 intLabels: labels.FromStrings("__name__", "simple_series", "a", "2", "b", "2"), 1091 }, 1092 }, 1093 blockFinderLabel: "dedupintresort", 1094 expectedSeries: 2, 1095 }, 1096 // This is a regression test for the bug outlined in https://github.com/thanos-io/thanos/issues/6257. 1097 { 1098 desc: "Deduplication works with extra internal label", 1099 intReplicaLabel: "replica", 1100 series: []seriesWithLabels{ 1101 { 1102 intLabels: labels.FromStrings("__name__", "simple_series", "replica", "a", "my_label", "1"), 1103 }, 1104 { 1105 intLabels: labels.FromStrings("__name__", "simple_series", "replica", "a", "my_label", "2"), 1106 }, 1107 { 1108 intLabels: labels.FromStrings("__name__", "simple_series", "replica", "b", "my_label", "1"), 1109 }, 1110 { 1111 intLabels: labels.FromStrings("__name__", "simple_series", "replica", "b", "my_label", "2"), 1112 }, 1113 }, 1114 blockFinderLabel: "dedupintextra", 1115 expectedSeries: 2, 1116 }, 1117 // This is a regression test for the bug outlined in https://github.com/thanos-io/thanos/issues/6257. 1118 { 1119 desc: "Deduplication works with both internal and external label", 1120 intReplicaLabel: "replica", 1121 extReplicaLabel: "receive_replica", 1122 series: []seriesWithLabels{ 1123 { 1124 intLabels: labels.FromStrings("__name__", "simple_series", "replica", "a"), 1125 extLabels: labels.FromStrings("replica", "a"), 1126 }, 1127 { 1128 intLabels: labels.FromStrings("__name__", "simple_series", "replica", "b"), 1129 extLabels: labels.FromStrings("replica", "b"), 1130 }, 1131 }, 1132 blockFinderLabel: "dedupintext", 1133 expectedSeries: 1, 1134 }, 1135 } 1136 1137 // Prepare and upload all the blocks that will be used to S3. 1138 var totalBlocks int 1139 for _, tt := range tests { 1140 createSimpleReplicatedBlocksInS3(ctx, t, e, l, bkt, tt.series, tt.blockFinderLabel) 1141 totalBlocks += len(tt.series) 1142 } 1143 testutil.Ok(t, storeGW.WaitSumMetrics(e2emon.Equals(float64(totalBlocks)), "thanos_blocks_meta_synced")) 1144 1145 for _, tt := range tests { 1146 t.Run(tt.desc, func(t *testing.T) { 1147 querierBuilder := e2ethanos.NewQuerierBuilder(e, tt.blockFinderLabel, storeGW.InternalEndpoint("grpc")).WithProxyStrategy("lazy") 1148 var replicaLabels []string 1149 if tt.intReplicaLabel != "" { 1150 replicaLabels = append(replicaLabels, tt.intReplicaLabel) 1151 } 1152 if tt.extReplicaLabel != "" { 1153 replicaLabels = append(replicaLabels, tt.extReplicaLabel) 1154 } 1155 if len(replicaLabels) > 0 { 1156 sort.Strings(replicaLabels) 1157 querierBuilder = querierBuilder.WithReplicaLabels(replicaLabels...) 1158 } 1159 querier := querierBuilder.Init() 1160 testutil.Ok(t, e2e.StartAndWaitReady(querier)) 1161 1162 expectedSeries := tt.expectedSeries 1163 instantQuery(t, ctx, querier.Endpoint("http"), func() string { 1164 return fmt.Sprintf("max_over_time(simple_series{block_finder='%s'}[2h])", tt.blockFinderLabel) 1165 }, time.Now, promclient.QueryOptions{ 1166 Deduplicate: true, 1167 }, expectedSeries) 1168 testutil.Ok(t, err) 1169 testutil.Ok(t, querier.Stop()) 1170 }) 1171 } 1172 } 1173 1174 // createSimpleReplicatedBlocksInS3 creates blocks in S3 with the series. If blockFinderLabel is not empty, 1175 // it will be added to the block's labels to easily find the blocks on with queries later. 1176 func createSimpleReplicatedBlocksInS3( 1177 ctx context.Context, 1178 t *testing.T, 1179 dockerEnv *e2e.DockerEnvironment, 1180 logger log.Logger, 1181 bucket *s3.Bucket, 1182 series []seriesWithLabels, 1183 blockFinderLabel string, 1184 ) { 1185 now := time.Now() 1186 dir := filepath.Join(dockerEnv.SharedDir(), "tmp") 1187 testutil.Ok(t, os.MkdirAll(dir, os.ModePerm)) 1188 for _, s := range series { 1189 intLabels := s.intLabels 1190 if blockFinderLabel != "" { 1191 intLabels = labels.NewBuilder(s.intLabels.Copy()).Set("block_finder", blockFinderLabel).Labels() 1192 } 1193 blockID, err := e2eutil.CreateBlockWithBlockDelay(ctx, 1194 dir, 1195 []labels.Labels{intLabels}, 1196 1, 1197 timestamp.FromTime(now), 1198 timestamp.FromTime(now.Add(2*time.Hour)), 1199 30*time.Minute, 1200 s.extLabels, 1201 0, 1202 metadata.NoneFunc, 1203 ) 1204 testutil.Ok(t, err) 1205 blockPath := path.Join(dir, blockID.String()) 1206 testutil.Ok(t, objstore.UploadDir(ctx, logger, bucket, blockPath, blockID.String())) 1207 } 1208 } 1209 1210 func TestSidecarQueryDedup(t *testing.T) { 1211 t.Parallel() 1212 1213 e, err := e2e.NewDockerEnvironment("sidecar-dedup") 1214 testutil.Ok(t, err) 1215 t.Cleanup(e2ethanos.CleanScenario(t, e)) 1216 ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) 1217 t.Cleanup(cancel) 1218 1219 prom1, sidecar1 := e2ethanos.NewPrometheusWithSidecar(e, "p1", e2ethanos.DefaultPromConfig("p1", 0, "", ""), "", e2ethanos.DefaultPrometheusImage(), "", "remote-write-receiver") 1220 prom2, sidecar2 := e2ethanos.NewPrometheusWithSidecar(e, "p2", e2ethanos.DefaultPromConfig("p1", 1, "", ""), "", e2ethanos.DefaultPrometheusImage(), "", "remote-write-receiver") 1221 prom3, sidecar3 := e2ethanos.NewPrometheusWithSidecar(e, "p3", e2ethanos.DefaultPromConfig("p2", 0, "", ""), "", e2ethanos.DefaultPrometheusImage(), "", "remote-write-receiver") 1222 testutil.Ok(t, e2e.StartAndWaitReady(prom1, sidecar1, prom2, sidecar2, prom3, sidecar3)) 1223 1224 { 1225 samples := []seriesWithLabels{ 1226 {intLabels: labels.FromStrings("__name__", "my_fake_metric", "a", "1", "b", "1", "instance", "1")}, 1227 {intLabels: labels.FromStrings("__name__", "my_fake_metric", "a", "1", "b", "2", "instance", "1")}, 1228 {intLabels: labels.FromStrings("__name__", "my_fake_metric", "a", "2", "b", "1", "instance", "1")}, 1229 {intLabels: labels.FromStrings("__name__", "my_fake_metric", "a", "2", "b", "2", "instance", "1")}, 1230 // Now replicating based on the "instance" label. 1231 {intLabels: labels.FromStrings("__name__", "my_fake_metric", "a", "1", "b", "1", "instance", "2")}, 1232 {intLabels: labels.FromStrings("__name__", "my_fake_metric", "a", "1", "b", "2", "instance", "2")}, 1233 {intLabels: labels.FromStrings("__name__", "my_fake_metric", "a", "2", "b", "1", "instance", "2")}, 1234 {intLabels: labels.FromStrings("__name__", "my_fake_metric", "a", "2", "b", "2", "instance", "2")}, 1235 } 1236 testutil.Ok(t, remoteWriteSeriesWithLabels(ctx, prom1, samples)) 1237 testutil.Ok(t, remoteWriteSeriesWithLabels(ctx, prom2, samples)) 1238 } 1239 1240 { 1241 samples := []seriesWithLabels{ 1242 {intLabels: labels.FromStrings("__name__", "my_fake_metric", "a", "1", "b", "1")}, 1243 {intLabels: labels.FromStrings("__name__", "my_fake_metric", "a", "1", "b", "2")}, 1244 {intLabels: labels.FromStrings("__name__", "my_fake_metric", "a", "2", "b", "1")}, 1245 {intLabels: labels.FromStrings("__name__", "my_fake_metric", "a", "2", "b", "2")}, 1246 } 1247 testutil.Ok(t, remoteWriteSeriesWithLabels(ctx, prom3, samples)) 1248 } 1249 1250 query1 := e2ethanos.NewQuerierBuilder(e, "1", sidecar1.InternalEndpoint("grpc"), sidecar2.InternalEndpoint("grpc")). 1251 WithReplicaLabels("replica", "instance"). 1252 Init() 1253 query2 := e2ethanos.NewQuerierBuilder(e, "2", sidecar1.InternalEndpoint("grpc"), sidecar2.InternalEndpoint("grpc")). 1254 WithReplicaLabels("replica"). 1255 Init() 1256 query3 := e2ethanos.NewQuerierBuilder(e, "3", sidecar1.InternalEndpoint("grpc"), sidecar2.InternalEndpoint("grpc")). 1257 WithReplicaLabels("instance"). 1258 Init() 1259 query4 := e2ethanos.NewQuerierBuilder(e, "4", sidecar3.InternalEndpoint("grpc")). 1260 WithReplicaLabels("a"). 1261 Init() 1262 testutil.Ok(t, e2e.StartAndWaitReady(query1, query2, query3, query4)) 1263 1264 t.Run("deduplication on internal and external labels", func(t *testing.T) { 1265 // Uses both an internal and external labels as replica labels 1266 instantQuery(t, ctx, query1.Endpoint("http"), func() string { 1267 return "my_fake_metric" 1268 }, time.Now, promclient.QueryOptions{ 1269 Deduplicate: true, 1270 }, 4) 1271 }) 1272 1273 t.Run("deduplication on external label", func(t *testing.T) { 1274 // Uses "replica" as replica label, which is an external label when being captured by Thanos Sidecar. 1275 instantQuery(t, ctx, query2.Endpoint("http"), func() string { 1276 return "my_fake_metric" 1277 }, time.Now, promclient.QueryOptions{ 1278 Deduplicate: true, 1279 }, 8) 1280 }) 1281 1282 t.Run("deduplication on internal label", func(t *testing.T) { 1283 // Uses "instance" as replica label, which is an internal label from the samples used. 1284 instantQuery(t, ctx, query3.Endpoint("http"), func() string { 1285 return "my_fake_metric" 1286 }, time.Now, promclient.QueryOptions{ 1287 Deduplicate: true, 1288 }, 8) 1289 }) 1290 1291 t.Run("deduplication on internal label with reorder", func(t *testing.T) { 1292 // Uses "a" as replica label, which is an internal label from the samples used. 1293 // This is a regression test for the bug outlined in https://github.com/thanos-io/thanos/issues/6257. 1294 // Until the bug was fixed, this testcase would return 4 samples instead of 2. 1295 instantQuery(t, ctx, query4.Endpoint("http"), func() string { 1296 return "my_fake_metric" 1297 }, time.Now, promclient.QueryOptions{ 1298 Deduplicate: true, 1299 }, 2) 1300 }) 1301 } 1302 1303 func TestSidecarQueryEvaluation(t *testing.T) { 1304 t.Parallel() 1305 1306 timeNow := time.Now().UnixNano() 1307 1308 ts := []struct { 1309 prom1Samples []fakeMetricSample 1310 prom2Samples []fakeMetricSample 1311 query string 1312 result model.Vector 1313 }{ 1314 { 1315 query: "max (my_fake_metric)", 1316 prom1Samples: []fakeMetricSample{{"i1", 1, timeNow}, {"i2", 5, timeNow}, {"i3", 9, timeNow}}, 1317 prom2Samples: []fakeMetricSample{{"i1", 3, timeNow}, {"i2", 4, timeNow}, {"i3", 10, timeNow}}, 1318 result: []*model.Sample{ 1319 { 1320 Metric: map[model.LabelName]model.LabelValue{}, 1321 Value: 10, 1322 }, 1323 }, 1324 }, 1325 { 1326 query: "max by (instance) (my_fake_metric)", 1327 prom1Samples: []fakeMetricSample{{"i1", 1, timeNow}, {"i2", 5, timeNow}, {"i3", 9, timeNow}}, 1328 prom2Samples: []fakeMetricSample{{"i1", 3, timeNow}, {"i2", 4, timeNow}, {"i3", 10, timeNow}}, 1329 result: []*model.Sample{ 1330 { 1331 Metric: map[model.LabelName]model.LabelValue{"instance": "i1"}, 1332 Value: 3, 1333 }, 1334 { 1335 Metric: map[model.LabelName]model.LabelValue{"instance": "i2"}, 1336 Value: 5, 1337 }, 1338 { 1339 Metric: map[model.LabelName]model.LabelValue{"instance": "i3"}, 1340 Value: 10, 1341 }, 1342 }, 1343 }, 1344 { 1345 query: "group by (instance) (my_fake_metric)", 1346 prom1Samples: []fakeMetricSample{{"i1", 1, timeNow}, {"i2", 5, timeNow}, {"i3", 9, timeNow}}, 1347 prom2Samples: []fakeMetricSample{{"i1", 3, timeNow}, {"i2", 4, timeNow}}, 1348 result: []*model.Sample{ 1349 { 1350 Metric: map[model.LabelName]model.LabelValue{"instance": "i1"}, 1351 Value: 1, 1352 }, 1353 { 1354 Metric: map[model.LabelName]model.LabelValue{"instance": "i2"}, 1355 Value: 1, 1356 }, 1357 { 1358 Metric: map[model.LabelName]model.LabelValue{"instance": "i3"}, 1359 Value: 1, 1360 }, 1361 }, 1362 }, 1363 { 1364 query: "max_over_time(my_fake_metric[10m])", 1365 prom1Samples: []fakeMetricSample{{"i1", 1, timeNow}, {"i2", 5, timeNow}}, 1366 prom2Samples: []fakeMetricSample{{"i1", 3, timeNow}}, 1367 result: []*model.Sample{ 1368 { 1369 Metric: map[model.LabelName]model.LabelValue{"instance": "i1", "prometheus": "p1"}, 1370 Value: 1, 1371 }, 1372 { 1373 Metric: map[model.LabelName]model.LabelValue{"instance": "i1", "prometheus": "p2"}, 1374 Value: 3, 1375 }, 1376 { 1377 Metric: map[model.LabelName]model.LabelValue{"instance": "i2", "prometheus": "p1"}, 1378 Value: 5, 1379 }, 1380 }, 1381 }, 1382 { 1383 query: "min_over_time(my_fake_metric[10m])", 1384 prom1Samples: []fakeMetricSample{{"i1", 1, timeNow}, {"i2", 5, timeNow}}, 1385 prom2Samples: []fakeMetricSample{{"i1", 3, timeNow}}, 1386 result: []*model.Sample{ 1387 { 1388 Metric: map[model.LabelName]model.LabelValue{"instance": "i1", "prometheus": "p1"}, 1389 Value: 1, 1390 }, 1391 { 1392 Metric: map[model.LabelName]model.LabelValue{"instance": "i1", "prometheus": "p2"}, 1393 Value: 3, 1394 }, 1395 { 1396 Metric: map[model.LabelName]model.LabelValue{"instance": "i2", "prometheus": "p1"}, 1397 Value: 5, 1398 }, 1399 }, 1400 }, 1401 } 1402 1403 for _, tc := range ts { 1404 t.Run(tc.query, func(t *testing.T) { 1405 e, err := e2e.NewDockerEnvironment("query-pushdown") 1406 testutil.Ok(t, err) 1407 t.Cleanup(e2ethanos.CleanScenario(t, e)) 1408 1409 prom1, sidecar1 := e2ethanos.NewPrometheusWithSidecar(e, "p1", e2ethanos.DefaultPromConfig("p1", 0, "", ""), "", e2ethanos.DefaultPrometheusImage(), "", "remote-write-receiver") 1410 testutil.Ok(t, e2e.StartAndWaitReady(prom1, sidecar1)) 1411 1412 prom2, sidecar2 := e2ethanos.NewPrometheusWithSidecar(e, "p2", e2ethanos.DefaultPromConfig("p2", 0, "", ""), "", e2ethanos.DefaultPrometheusImage(), "", "remote-write-receiver") 1413 testutil.Ok(t, e2e.StartAndWaitReady(prom2, sidecar2)) 1414 1415 endpoints := []string{ 1416 sidecar1.InternalEndpoint("grpc"), 1417 sidecar2.InternalEndpoint("grpc"), 1418 } 1419 q := e2ethanos. 1420 NewQuerierBuilder(e, "1", endpoints...). 1421 WithEnabledFeatures([]string{"query-pushdown"}). 1422 Init() 1423 testutil.Ok(t, e2e.StartAndWaitReady(q)) 1424 1425 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) 1426 t.Cleanup(cancel) 1427 1428 testutil.Ok(t, synthesizeFakeMetricSamples(ctx, prom1, tc.prom1Samples)) 1429 testutil.Ok(t, synthesizeFakeMetricSamples(ctx, prom2, tc.prom2Samples)) 1430 1431 testQuery := func() string { return tc.query } 1432 queryAndAssert(t, ctx, q.Endpoint("http"), testQuery, time.Now, promclient.QueryOptions{ 1433 Deduplicate: true, 1434 }, tc.result) 1435 }) 1436 } 1437 } 1438 1439 // An emptyCtx is never canceled, has no values, and has no deadline. It is not 1440 // struct{}, since vars of this type must have distinct addresses. 1441 type emptyCtx int 1442 1443 func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { 1444 return 1445 } 1446 1447 func (*emptyCtx) Done() <-chan struct{} { 1448 return nil 1449 } 1450 1451 func (*emptyCtx) Err() error { 1452 return nil 1453 } 1454 1455 func (*emptyCtx) Value(key any) any { 1456 return nil 1457 } 1458 1459 func (e *emptyCtx) String() string { 1460 return "Context" 1461 } 1462 1463 func checkNetworkRequests(t *testing.T, addr string) { 1464 ctx, cancel := chromedp.NewContext(new(emptyCtx)) 1465 t.Cleanup(cancel) 1466 1467 testutil.Ok(t, runutil.Retry(1*time.Minute, ctx.Done(), func() error { 1468 var networkErrors []string 1469 1470 // Listen for failed network requests and push them to an array. 1471 chromedp.ListenTarget(ctx, func(ev interface{}) { 1472 switch ev := ev.(type) { 1473 case *network.EventLoadingFailed: 1474 networkErrors = append(networkErrors, ev.ErrorText) 1475 } 1476 }) 1477 1478 err := chromedp.Run(ctx, 1479 network.Enable(), 1480 chromedp.Navigate(addr), 1481 chromedp.WaitVisible(`body`), 1482 ) 1483 1484 if err != nil { 1485 return err 1486 } 1487 1488 if len(networkErrors) > 0 { 1489 err = fmt.Errorf("some network requests failed: %s", strings.Join(networkErrors, "; ")) 1490 } 1491 return err 1492 })) 1493 } 1494 1495 func urlParse(t testing.TB, addr string) *url.URL { 1496 u, err := url.Parse(addr) 1497 testutil.Ok(t, err) 1498 1499 return u 1500 } 1501 1502 func instantQuery(t testing.TB, ctx context.Context, addr string, q func() string, ts func() time.Time, opts promclient.QueryOptions, expectedSeriesLen int) (model.Vector, *promclient.Explanation) { 1503 t.Helper() 1504 1505 var result model.Vector 1506 1507 logger := log.NewLogfmtLogger(os.Stdout) 1508 logger = log.With(logger, "ts", log.DefaultTimestampUTC) 1509 _ = logger.Log( 1510 "caller", "instantQuery", 1511 "msg", fmt.Sprintf("Waiting for %d results for query %s", expectedSeriesLen, q()), 1512 ) 1513 1514 var explanation *promclient.Explanation 1515 testutil.Ok(t, runutil.RetryWithLog(logger, 5*time.Second, ctx.Done(), func() error { 1516 res, rexplanation, err := simpleInstantQuery(t, ctx, addr, q, ts, opts, expectedSeriesLen) 1517 if err != nil { 1518 return err 1519 } 1520 result = res 1521 explanation = rexplanation 1522 return nil 1523 })) 1524 sortResults(result) 1525 return result, explanation 1526 } 1527 1528 func simpleInstantQuery(t testing.TB, ctx context.Context, addr string, q func() string, ts func() time.Time, opts promclient.QueryOptions, expectedSeriesLen int) (model.Vector, *promclient.Explanation, error) { 1529 res, warnings, explanation, err := promclient.NewDefaultClient().QueryInstant(ctx, urlParse(t, "http://"+addr), q(), ts(), opts) 1530 if err != nil { 1531 return nil, nil, err 1532 } 1533 1534 if len(warnings) > 0 { 1535 return nil, nil, errors.Errorf("unexpected warnings %s", warnings) 1536 } 1537 1538 if len(res) != expectedSeriesLen { 1539 return nil, nil, errors.Errorf("unexpected result size, expected %d; result %d: %v", expectedSeriesLen, len(res), res) 1540 } 1541 1542 sortResults(res) 1543 return res, explanation, nil 1544 } 1545 1546 func queryWaitAndAssert(t *testing.T, ctx context.Context, addr string, q func() string, ts func() time.Time, opts promclient.QueryOptions, expected model.Vector) { 1547 t.Helper() 1548 1549 var result model.Vector 1550 1551 logger := log.NewLogfmtLogger(os.Stdout) 1552 logger = log.With(logger, "ts", log.DefaultTimestampUTC) 1553 _ = logger.Log( 1554 "caller", "queryWaitAndAssert", 1555 "msg", fmt.Sprintf("Waiting for %d results for query %s", len(expected), q()), 1556 ) 1557 testutil.Ok(t, runutil.RetryWithLog(logger, 10*time.Second, ctx.Done(), func() error { 1558 res, warnings, _, err := promclient.NewDefaultClient().QueryInstant(ctx, urlParse(t, "http://"+addr), q(), ts(), opts) 1559 if err != nil { 1560 return err 1561 } 1562 1563 if len(warnings) > 0 { 1564 return errors.Errorf("unexpected warnings %s", warnings) 1565 } 1566 1567 if len(res) != len(expected) { 1568 return errors.Errorf("unexpected result size, expected %d; result %d: %v", len(expected), len(res), res) 1569 } 1570 result = res 1571 sortResults(result) 1572 for _, r := range result { 1573 r.Timestamp = 0 // Does not matter for us. 1574 } 1575 1576 // Retry if not expected result 1577 if reflect.DeepEqual(expected, result) { 1578 return nil 1579 } 1580 return errors.New("series are different") 1581 })) 1582 1583 testutil.Equals(t, expected, result) 1584 } 1585 1586 func queryAndAssertSeries(t *testing.T, ctx context.Context, addr string, q func() string, ts func() time.Time, opts promclient.QueryOptions, expected []model.Metric) { 1587 t.Helper() 1588 1589 result, _ := instantQuery(t, ctx, addr, q, ts, opts, len(expected)) 1590 for i, exp := range expected { 1591 testutil.Equals(t, exp, result[i].Metric) 1592 } 1593 } 1594 1595 func queryAndAssert(t *testing.T, ctx context.Context, addr string, q func() string, ts func() time.Time, opts promclient.QueryOptions, expected model.Vector) { 1596 t.Helper() 1597 1598 sortResults(expected) 1599 result, _ := instantQuery(t, ctx, addr, q, ts, opts, len(expected)) 1600 for _, r := range result { 1601 r.Timestamp = 0 // Does not matter for us. 1602 } 1603 testutil.Equals(t, expected, result) 1604 } 1605 1606 func labelNames(t *testing.T, ctx context.Context, addr string, matchers []*labels.Matcher, start, end int64, check func(res []string) bool) { 1607 t.Helper() 1608 1609 logger := log.NewLogfmtLogger(os.Stdout) 1610 logger = log.With(logger, "ts", log.DefaultTimestampUTC) 1611 testutil.Ok(t, runutil.RetryWithLog(logger, 2*time.Second, ctx.Done(), func() error { 1612 res, err := promclient.NewDefaultClient().LabelNamesInGRPC(ctx, urlParse(t, "http://"+addr), matchers, start, end) 1613 if err != nil { 1614 return err 1615 } 1616 if check(res) { 1617 return nil 1618 } 1619 1620 return errors.Errorf("unexpected results %v", res) 1621 })) 1622 } 1623 1624 //nolint:unparam 1625 func labelValues(t *testing.T, ctx context.Context, addr, label string, matchers []*labels.Matcher, start, end int64, check func(res []string) bool) { 1626 t.Helper() 1627 1628 logger := log.NewLogfmtLogger(os.Stdout) 1629 logger = log.With(logger, "ts", log.DefaultTimestampUTC) 1630 testutil.Ok(t, runutil.RetryWithLog(logger, 2*time.Second, ctx.Done(), func() error { 1631 res, err := promclient.NewDefaultClient().LabelValuesInGRPC(ctx, urlParse(t, "http://"+addr), label, matchers, start, end) 1632 if err != nil { 1633 return err 1634 } 1635 if check(res) { 1636 return nil 1637 } 1638 1639 return errors.Errorf("unexpected results %v", res) 1640 })) 1641 } 1642 1643 func series(t *testing.T, ctx context.Context, addr string, matchers []*labels.Matcher, start, end int64, check func(res []map[string]string) bool) { 1644 t.Helper() 1645 1646 logger := log.NewLogfmtLogger(os.Stdout) 1647 logger = log.With(logger, "ts", log.DefaultTimestampUTC) 1648 testutil.Ok(t, runutil.RetryWithLog(logger, 2*time.Second, ctx.Done(), func() error { 1649 res, err := promclient.NewDefaultClient().SeriesInGRPC(ctx, urlParse(t, "http://"+addr), matchers, start, end) 1650 if err != nil { 1651 return err 1652 } 1653 if check(res) { 1654 return nil 1655 } 1656 1657 return errors.Errorf("unexpected results %v", res) 1658 })) 1659 } 1660 1661 //nolint:unparam 1662 func rangeQuery(t *testing.T, ctx context.Context, addr string, q func() string, start, end, step int64, opts promclient.QueryOptions, check func(res model.Matrix) error) *promclient.Explanation { 1663 t.Helper() 1664 1665 logger := log.NewLogfmtLogger(os.Stdout) 1666 logger = log.With(logger, "ts", log.DefaultTimestampUTC) 1667 var retExplanation *promclient.Explanation 1668 testutil.Ok(t, runutil.RetryWithLog(logger, time.Second, ctx.Done(), func() error { 1669 res, warnings, explanation, err := promclient.NewDefaultClient().QueryRange(ctx, urlParse(t, "http://"+addr), q(), start, end, step, opts) 1670 if err != nil { 1671 return err 1672 } 1673 1674 if len(warnings) > 0 { 1675 return errors.Errorf("unexpected warnings %s", warnings) 1676 } 1677 1678 if err := check(res); err != nil { 1679 return errors.Wrap(err, "result check failed") 1680 } 1681 1682 retExplanation = explanation 1683 1684 return nil 1685 })) 1686 1687 return retExplanation 1688 } 1689 1690 func queryExemplars(t *testing.T, ctx context.Context, addr, q string, start, end int64, check func(data []*exemplarspb.ExemplarData) error) { 1691 t.Helper() 1692 1693 logger := log.NewLogfmtLogger(os.Stdout) 1694 logger = log.With(logger, "ts", log.DefaultTimestampUTC) 1695 u := urlParse(t, "http://"+addr) 1696 testutil.Ok(t, runutil.RetryWithLog(logger, time.Second, ctx.Done(), func() error { 1697 res, err := promclient.NewDefaultClient().ExemplarsInGRPC(ctx, u, q, start, end) 1698 if err != nil { 1699 return err 1700 } 1701 1702 if err := check(res); err != nil { 1703 return errors.Wrap(err, "exemplar check failed") 1704 } 1705 1706 return nil 1707 })) 1708 } 1709 1710 func synthesizeFakeMetricSamples(ctx context.Context, prometheus *e2emon.InstrumentedRunnable, testSamples []fakeMetricSample) error { 1711 samples := make([]model.Sample, len(testSamples)) 1712 for i, s := range testSamples { 1713 samples[i] = newSample(s) 1714 } 1715 1716 return synthesizeSamples(ctx, prometheus, samples) 1717 } 1718 1719 func synthesizeSamples(ctx context.Context, prometheus *e2emon.InstrumentedRunnable, samples []model.Sample) error { 1720 rawRemoteWriteURL := "http://" + prometheus.Endpoint("http") + "/api/v1/write" 1721 1722 samplespb := make([]prompb.TimeSeries, 0, len(samples)) 1723 for _, sample := range samples { 1724 labelspb := make([]prompb.Label, 0, len(sample.Metric)) 1725 for labelKey, labelValue := range sample.Metric { 1726 labelspb = append(labelspb, prompb.Label{ 1727 Name: string(labelKey), 1728 Value: string(labelValue), 1729 }) 1730 } 1731 samplespb = append(samplespb, prompb.TimeSeries{ 1732 Labels: labelspb, 1733 Samples: []prompb.Sample{ 1734 { 1735 Value: float64(sample.Value), 1736 Timestamp: sample.Timestamp.Time().Unix() * 1000, 1737 }, 1738 }, 1739 }) 1740 } 1741 1742 writeRequest := &prompb.WriteRequest{ 1743 Timeseries: samplespb, 1744 } 1745 1746 return storeWriteRequest(ctx, rawRemoteWriteURL, writeRequest) 1747 } 1748 1749 func remoteWriteSeriesWithLabels(ctx context.Context, prometheus *e2emon.InstrumentedRunnable, series []seriesWithLabels) error { 1750 rawRemoteWriteURL := "http://" + prometheus.Endpoint("http") + "/api/v1/write" 1751 1752 samplespb := make([]prompb.TimeSeries, 0, len(series)) 1753 r := rand.New(rand.NewSource(int64(len(series)))) 1754 for _, serie := range series { 1755 labelspb := make([]prompb.Label, 0, len(serie.intLabels)) 1756 for labelKey, labelValue := range serie.intLabels.Map() { 1757 labelspb = append(labelspb, prompb.Label{ 1758 Name: labelKey, 1759 Value: labelValue, 1760 }) 1761 } 1762 samplespb = append(samplespb, prompb.TimeSeries{ 1763 Labels: labelspb, 1764 Samples: []prompb.Sample{ 1765 { 1766 Value: r.Float64(), 1767 Timestamp: time.Now().Unix() * 1000, 1768 }, 1769 }, 1770 }) 1771 } 1772 1773 writeRequest := &prompb.WriteRequest{ 1774 Timeseries: samplespb, 1775 } 1776 1777 return storeWriteRequest(ctx, rawRemoteWriteURL, writeRequest) 1778 } 1779 1780 func storeWriteRequest(ctx context.Context, rawRemoteWriteURL string, req *prompb.WriteRequest) error { 1781 remoteWriteURL, err := url.Parse(rawRemoteWriteURL) 1782 if err != nil { 1783 return err 1784 } 1785 1786 client, err := remote.NewWriteClient("remote-write-client", &remote.ClientConfig{ 1787 URL: &config_util.URL{URL: remoteWriteURL}, 1788 Timeout: model.Duration(30 * time.Second), 1789 }) 1790 if err != nil { 1791 return err 1792 } 1793 1794 var buf []byte 1795 pBuf := proto.NewBuffer(nil) 1796 if err := pBuf.Marshal(req); err != nil { 1797 return err 1798 } 1799 1800 compressed := snappy.Encode(buf, pBuf.Bytes()) 1801 return client.Store(ctx, compressed) 1802 } 1803 1804 func TestSidecarQueryEvaluationWithDedup(t *testing.T) { 1805 t.Parallel() 1806 1807 timeNow := time.Now().UnixNano() 1808 1809 ts := []struct { 1810 prom1Samples []fakeMetricSample 1811 prom2Samples []fakeMetricSample 1812 query string 1813 result model.Vector 1814 }{ 1815 { 1816 query: "max (my_fake_metric)", 1817 prom1Samples: []fakeMetricSample{{"i1", 1, timeNow}, {"i2", 5, timeNow}, {"i3", 9, timeNow}}, 1818 prom2Samples: []fakeMetricSample{{"i1", 3, timeNow}, {"i2", 4, timeNow}, {"i3", 10, timeNow}}, 1819 result: []*model.Sample{ 1820 { 1821 Metric: map[model.LabelName]model.LabelValue{}, 1822 Value: 10, 1823 }, 1824 }, 1825 }, 1826 { 1827 query: "max by (instance) (my_fake_metric)", 1828 prom1Samples: []fakeMetricSample{{"i1", 1, timeNow}, {"i2", 5, timeNow}, {"i3", 9, timeNow}}, 1829 prom2Samples: []fakeMetricSample{{"i1", 3, timeNow}, {"i2", 4, timeNow}, {"i3", 10, timeNow}}, 1830 result: []*model.Sample{ 1831 { 1832 Metric: map[model.LabelName]model.LabelValue{"instance": "i1"}, 1833 Value: 3, 1834 }, 1835 { 1836 Metric: map[model.LabelName]model.LabelValue{"instance": "i2"}, 1837 Value: 5, 1838 }, 1839 { 1840 Metric: map[model.LabelName]model.LabelValue{"instance": "i3"}, 1841 Value: 10, 1842 }, 1843 }, 1844 }, 1845 { 1846 query: "group by (instance) (my_fake_metric)", 1847 prom1Samples: []fakeMetricSample{{"i1", 1, timeNow}, {"i2", 5, timeNow}, {"i3", 9, timeNow}}, 1848 prom2Samples: []fakeMetricSample{{"i1", 3, timeNow}, {"i2", 4, timeNow}}, 1849 result: []*model.Sample{ 1850 { 1851 Metric: map[model.LabelName]model.LabelValue{"instance": "i1"}, 1852 Value: 1, 1853 }, 1854 { 1855 Metric: map[model.LabelName]model.LabelValue{"instance": "i2"}, 1856 Value: 1, 1857 }, 1858 { 1859 Metric: map[model.LabelName]model.LabelValue{"instance": "i3"}, 1860 Value: 1, 1861 }, 1862 }, 1863 }, 1864 { 1865 query: "max_over_time(my_fake_metric[10m])", 1866 prom1Samples: []fakeMetricSample{{"i1", 1, timeNow}, {"i2", 5, timeNow}}, 1867 prom2Samples: []fakeMetricSample{{"i1", 3, timeNow}}, 1868 result: []*model.Sample{ 1869 { 1870 Metric: map[model.LabelName]model.LabelValue{"instance": "i1", "prometheus": "p1"}, 1871 Value: 3, 1872 }, 1873 { 1874 Metric: map[model.LabelName]model.LabelValue{"instance": "i2", "prometheus": "p1"}, 1875 Value: 5, 1876 }, 1877 }, 1878 }, 1879 { 1880 query: "min_over_time(my_fake_metric[10m])", 1881 prom1Samples: []fakeMetricSample{{"i1", 1, timeNow}, {"i2", 5, timeNow}}, 1882 prom2Samples: []fakeMetricSample{{"i1", 3, timeNow}}, 1883 result: []*model.Sample{ 1884 { 1885 Metric: map[model.LabelName]model.LabelValue{"instance": "i1", "prometheus": "p1"}, 1886 Value: 1, 1887 }, 1888 { 1889 Metric: map[model.LabelName]model.LabelValue{"instance": "i2", "prometheus": "p1"}, 1890 Value: 5, 1891 }, 1892 }, 1893 }, 1894 } 1895 1896 for _, tc := range ts { 1897 t.Run(tc.query, func(t *testing.T) { 1898 e, err := e2e.NewDockerEnvironment("pushdown-dedup") 1899 testutil.Ok(t, err) 1900 t.Cleanup(e2ethanos.CleanScenario(t, e)) 1901 1902 prom1, sidecar1 := e2ethanos.NewPrometheusWithSidecar(e, "p1", e2ethanos.DefaultPromConfig("p1", 0, "", ""), "", e2ethanos.DefaultPrometheusImage(), "", "remote-write-receiver") 1903 testutil.Ok(t, e2e.StartAndWaitReady(prom1, sidecar1)) 1904 1905 prom2, sidecar2 := e2ethanos.NewPrometheusWithSidecar(e, "p2", e2ethanos.DefaultPromConfig("p1", 1, "", ""), "", e2ethanos.DefaultPrometheusImage(), "", "remote-write-receiver") 1906 testutil.Ok(t, e2e.StartAndWaitReady(prom2, sidecar2)) 1907 1908 endpoints := []string{ 1909 sidecar1.InternalEndpoint("grpc"), 1910 sidecar2.InternalEndpoint("grpc"), 1911 } 1912 q := e2ethanos. 1913 NewQuerierBuilder(e, "1", endpoints...). 1914 WithEnabledFeatures([]string{"query-pushdown"}). 1915 Init() 1916 testutil.Ok(t, err) 1917 testutil.Ok(t, e2e.StartAndWaitReady(q)) 1918 1919 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) 1920 t.Cleanup(cancel) 1921 1922 testutil.Ok(t, synthesizeFakeMetricSamples(ctx, prom1, tc.prom1Samples)) 1923 testutil.Ok(t, synthesizeFakeMetricSamples(ctx, prom2, tc.prom2Samples)) 1924 1925 testQuery := func() string { return tc.query } 1926 queryAndAssert(t, ctx, q.Endpoint("http"), testQuery, time.Now, promclient.QueryOptions{ 1927 Deduplicate: true, 1928 }, tc.result) 1929 }) 1930 } 1931 } 1932 1933 // TestSidecarStoreAlignmentPushdown tests how pushdown works with 1934 // --min-time and --max-time. 1935 func TestSidecarAlignmentPushdown(t *testing.T) { 1936 t.Parallel() 1937 1938 e, err := e2e.NewDockerEnvironment("pushdown-min-max") 1939 testutil.Ok(t, err) 1940 t.Cleanup(e2ethanos.CleanScenario(t, e)) 1941 1942 now := time.Now() 1943 1944 prom1, sidecar1 := e2ethanos.NewPrometheusWithSidecar(e, "p1", e2ethanos.DefaultPromConfig("p1", 0, "", ""), "", e2ethanos.DefaultPrometheusImage(), now.Add(time.Duration(-1)*time.Hour).Format(time.RFC3339), now.Format(time.RFC3339), "remote-write-receiver") 1945 testutil.Ok(t, e2e.StartAndWaitReady(prom1, sidecar1)) 1946 1947 endpoints := []string{ 1948 sidecar1.InternalEndpoint("grpc"), 1949 } 1950 q1 := e2ethanos. 1951 NewQuerierBuilder(e, "1", endpoints...). 1952 Init() 1953 testutil.Ok(t, err) 1954 testutil.Ok(t, e2e.StartAndWaitReady(q1)) 1955 q2 := e2ethanos. 1956 NewQuerierBuilder(e, "2", endpoints...). 1957 WithEnabledFeatures([]string{"query-pushdown"}). 1958 Init() 1959 testutil.Ok(t, err) 1960 testutil.Ok(t, e2e.StartAndWaitReady(q2)) 1961 1962 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) 1963 t.Cleanup(cancel) 1964 1965 samples := make([]fakeMetricSample, 0) 1966 for i := now.Add(time.Duration(-3) * time.Hour); i.Before(now); i = i.Add(30 * time.Second) { 1967 samples = append(samples, fakeMetricSample{ 1968 label: "test", 1969 value: 1, 1970 timestampUnixNano: i.UnixNano(), 1971 }) 1972 } 1973 1974 testutil.Ok(t, synthesizeFakeMetricSamples(ctx, prom1, samples)) 1975 1976 // This query should have identical requests. 1977 testQuery := func() string { return `max_over_time({instance="test"}[5m])` } 1978 1979 logger := log.NewLogfmtLogger(os.Stdout) 1980 logger = log.With(logger, "ts", log.DefaultTimestampUTC) 1981 1982 var expectedRes model.Matrix 1983 testutil.Ok(t, runutil.RetryWithLog(logger, time.Second, ctx.Done(), func() error { 1984 res, warnings, _, err := promclient.NewDefaultClient().QueryRange(ctx, urlParse(t, "http://"+q1.Endpoint("http")), testQuery(), 1985 timestamp.FromTime(now.Add(time.Duration(-7*24)*time.Hour)), 1986 timestamp.FromTime(now), 1987 2419, // Taken from UI. 1988 promclient.QueryOptions{ 1989 Deduplicate: true, 1990 }) 1991 if err != nil { 1992 return err 1993 } 1994 1995 if len(warnings) > 0 { 1996 return errors.Errorf("unexpected warnings %s", warnings) 1997 } 1998 1999 if len(res) == 0 { 2000 return errors.Errorf("got empty result") 2001 } 2002 2003 expectedRes = res 2004 return nil 2005 })) 2006 2007 rangeQuery(t, ctx, q2.Endpoint("http"), testQuery, timestamp.FromTime(now.Add(time.Duration(-7*24)*time.Hour)), 2008 timestamp.FromTime(now), 2009 2419, // Taken from UI. 2010 promclient.QueryOptions{ 2011 Deduplicate: true, 2012 }, func(res model.Matrix) error { 2013 if !reflect.DeepEqual(res, expectedRes) { 2014 return fmt.Errorf("unexpected results (got %v but expected %v)", res, expectedRes) 2015 } 2016 return nil 2017 }) 2018 } 2019 2020 func TestGrpcInstantQuery(t *testing.T) { 2021 t.Parallel() 2022 2023 e, err := e2e.NewDockerEnvironment("grpc-api-instant") 2024 testutil.Ok(t, err) 2025 t.Cleanup(e2ethanos.CleanScenario(t, e)) 2026 2027 promConfig := e2ethanos.DefaultPromConfig("p1", 0, "", "") 2028 prom, sidecar := e2ethanos.NewPrometheusWithSidecar(e, "p1", promConfig, "", e2ethanos.DefaultPrometheusImage(), "", "remote-write-receiver") 2029 testutil.Ok(t, e2e.StartAndWaitReady(prom, sidecar)) 2030 2031 endpoints := []string{ 2032 sidecar.InternalEndpoint("grpc"), 2033 } 2034 querier := e2ethanos. 2035 NewQuerierBuilder(e, "1", endpoints...). 2036 Init() 2037 testutil.Ok(t, e2e.StartAndWaitReady(querier)) 2038 2039 now := time.Now() 2040 samples := []fakeMetricSample{ 2041 { 2042 label: "test", 2043 value: 1, 2044 timestampUnixNano: now.UnixNano(), 2045 }, 2046 { 2047 label: "test", 2048 value: 2, 2049 timestampUnixNano: now.Add(time.Hour).UnixNano(), 2050 }, 2051 } 2052 ctx := context.Background() 2053 testutil.Ok(t, synthesizeFakeMetricSamples(ctx, prom, samples)) 2054 2055 grpcConn, err := grpc.Dial(querier.Endpoint("grpc"), grpc.WithTransportCredentials(insecure.NewCredentials())) 2056 testutil.Ok(t, err) 2057 queryClient := querypb.NewQueryClient(grpcConn) 2058 2059 queries := []struct { 2060 time time.Time 2061 expectedResult float64 2062 }{ 2063 { 2064 time: now, 2065 expectedResult: 1, 2066 }, 2067 { 2068 time: now.Add(time.Hour), 2069 expectedResult: 2, 2070 }, 2071 } 2072 2073 for _, query := range queries { 2074 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) 2075 err = runutil.Retry(5*time.Second, ctx.Done(), func() error { 2076 result, err := queryClient.Query(ctx, &querypb.QueryRequest{ 2077 Query: "my_fake_metric", 2078 TimeSeconds: query.time.Unix(), 2079 }) 2080 2081 if err != nil { 2082 return err 2083 } 2084 2085 var warnings string 2086 var series []*prompb_copy.TimeSeries 2087 for { 2088 msg, err := result.Recv() 2089 if err == io.EOF { 2090 break 2091 } 2092 2093 s := msg.GetTimeseries() 2094 if s != nil { 2095 series = append(series, s) 2096 } 2097 w := msg.GetWarnings() 2098 if w != "" { 2099 warnings = w 2100 } 2101 } 2102 2103 if warnings != "" { 2104 return fmt.Errorf("got warnings, expected none") 2105 } 2106 2107 if len(series) != 1 { 2108 return fmt.Errorf("got empty result from querier") 2109 } 2110 2111 if len(series[0].Samples) != 1 { 2112 return fmt.Errorf("got empty timeseries from querier") 2113 } 2114 2115 if series[0].Samples[0].Value != query.expectedResult { 2116 return fmt.Errorf("got invalid result from querier") 2117 } 2118 2119 return nil 2120 }) 2121 testutil.Ok(t, err) 2122 cancel() 2123 } 2124 } 2125 2126 func TestGrpcQueryRange(t *testing.T) { 2127 t.Parallel() 2128 2129 e, err := e2e.NewDockerEnvironment("grpc-api-range") 2130 testutil.Ok(t, err) 2131 t.Cleanup(e2ethanos.CleanScenario(t, e)) 2132 2133 promConfig := e2ethanos.DefaultPromConfig("p1", 0, "", "") 2134 prom, sidecar := e2ethanos.NewPrometheusWithSidecar(e, "p1", promConfig, "", e2ethanos.DefaultPrometheusImage(), "", "remote-write-receiver") 2135 testutil.Ok(t, e2e.StartAndWaitReady(prom, sidecar)) 2136 2137 endpoints := []string{ 2138 sidecar.InternalEndpoint("grpc"), 2139 } 2140 querier := e2ethanos. 2141 NewQuerierBuilder(e, "1", endpoints...). 2142 Init() 2143 testutil.Ok(t, err) 2144 testutil.Ok(t, e2e.StartAndWaitReady(querier)) 2145 2146 now := time.Now() 2147 samples := []fakeMetricSample{ 2148 { 2149 label: "test", 2150 value: 1, 2151 timestampUnixNano: now.UnixNano(), 2152 }, 2153 { 2154 label: "test", 2155 value: 2, 2156 timestampUnixNano: now.Add(time.Second * 15).UnixNano(), 2157 }, 2158 { 2159 label: "test", 2160 value: 3, 2161 timestampUnixNano: now.Add(time.Second * 30).UnixNano(), 2162 }, 2163 { 2164 label: "test", 2165 value: 4, 2166 timestampUnixNano: now.Add(time.Second * 45).UnixNano(), 2167 }, 2168 { 2169 label: "test", 2170 value: 5, 2171 timestampUnixNano: now.Add(time.Minute).UnixNano(), 2172 }, 2173 } 2174 ctx := context.Background() 2175 testutil.Ok(t, synthesizeFakeMetricSamples(ctx, prom, samples)) 2176 2177 grpcConn, err := grpc.Dial(querier.Endpoint("grpc"), grpc.WithTransportCredentials(insecure.NewCredentials())) 2178 testutil.Ok(t, err) 2179 queryClient := querypb.NewQueryClient(grpcConn) 2180 2181 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) 2182 defer cancel() 2183 err = runutil.Retry(5*time.Second, ctx.Done(), func() error { 2184 result, err := queryClient.QueryRange(ctx, &querypb.QueryRangeRequest{ 2185 Query: "my_fake_metric", 2186 StartTimeSeconds: now.Unix(), 2187 EndTimeSeconds: now.Add(time.Minute).Unix(), 2188 IntervalSeconds: 15, 2189 }) 2190 2191 if err != nil { 2192 return err 2193 } 2194 2195 var warnings string 2196 var series []*prompb_copy.TimeSeries 2197 for { 2198 msg, err := result.Recv() 2199 if err == io.EOF { 2200 break 2201 } 2202 2203 s := msg.GetTimeseries() 2204 if s != nil { 2205 series = append(series, s) 2206 } 2207 w := msg.GetWarnings() 2208 if w != "" { 2209 warnings = w 2210 } 2211 } 2212 if warnings != "" { 2213 return fmt.Errorf("got warnings, expected none") 2214 } 2215 2216 if len(series) != 1 { 2217 return fmt.Errorf("got empty result from querier") 2218 } 2219 2220 if len(series[0].Samples) != 5 { 2221 return fmt.Errorf("got empty timeseries from querier") 2222 } 2223 2224 return nil 2225 }) 2226 testutil.Ok(t, err) 2227 } 2228 2229 // Repro for https://github.com/thanos-io/thanos/pull/5296#issuecomment-1217875271. 2230 func TestConnectedQueriesWithLazyProxy(t *testing.T) { 2231 t.Parallel() 2232 2233 e, err := e2e.NewDockerEnvironment("lazy-proxy") 2234 testutil.Ok(t, err) 2235 t.Cleanup(e2ethanos.CleanScenario(t, e)) 2236 2237 promConfig := e2ethanos.DefaultPromConfig("p1", 0, "", "", e2ethanos.LocalPrometheusTarget) 2238 prom, sidecar := e2ethanos.NewPrometheusWithSidecar(e, "p1", promConfig, "", e2ethanos.DefaultPrometheusImage(), "") 2239 2240 querier1 := e2ethanos.NewQuerierBuilder(e, "1", sidecar.InternalEndpoint("grpc")).WithProxyStrategy("lazy").WithDisablePartialResponses(true).Init() 2241 querier2 := e2ethanos.NewQuerierBuilder(e, "2", querier1.InternalEndpoint("grpc")).WithProxyStrategy("lazy").WithDisablePartialResponses(true).Init() 2242 2243 testutil.Ok(t, e2e.StartAndWaitReady(prom, sidecar, querier1, querier2)) 2244 testutil.Ok(t, querier2.WaitSumMetricsWithOptions(e2emon.Equals(1), []string{"thanos_store_nodes_grpc_connections"}, e2emon.WaitMissingMetrics())) 2245 2246 result, _ := instantQuery(t, context.Background(), querier2.Endpoint("http"), func() string { 2247 return "sum(up)" 2248 }, time.Now, promclient.QueryOptions{}, 1) 2249 testutil.Equals(t, model.SampleValue(1.0), result[0].Value) 2250 2251 instantQuery(t, context.Background(), querier2.Endpoint("http"), func() string { 2252 return "sum(metric_that_does_not_exist)" 2253 }, time.Now, promclient.QueryOptions{}, 0) 2254 2255 } 2256 2257 func TestSidecarPrefersExtLabels(t *testing.T) { 2258 t.Parallel() 2259 2260 e, err := e2e.NewDockerEnvironment("sidecar-extlbls") 2261 testutil.Ok(t, err) 2262 t.Cleanup(e2ethanos.CleanScenario(t, e)) 2263 2264 promCfg := `global: 2265 external_labels: 2266 region: test` 2267 2268 prom, sidecar := e2ethanos.NewPrometheusWithSidecar(e, "p1", promCfg, "", e2ethanos.DefaultPrometheusImage(), "", "remote-write-receiver") 2269 testutil.Ok(t, e2e.StartAndWaitReady(prom, sidecar)) 2270 2271 endpoints := []string{ 2272 sidecar.InternalEndpoint("grpc"), 2273 } 2274 querier := e2ethanos. 2275 NewQuerierBuilder(e, "1", endpoints...). 2276 Init() 2277 testutil.Ok(t, e2e.StartAndWaitReady(querier)) 2278 2279 now := time.Now() 2280 ctx := context.Background() 2281 m := model.Sample{ 2282 Metric: map[model.LabelName]model.LabelValue{ 2283 "__name__": "sidecar_test_metric", 2284 "instance": model.LabelValue("test"), 2285 "region": model.LabelValue("foo"), 2286 }, 2287 Value: model.SampleValue(2), 2288 Timestamp: model.TimeFromUnixNano(now.Add(time.Hour).UnixNano()), 2289 } 2290 testutil.Ok(t, synthesizeSamples(ctx, prom, []model.Sample{m})) 2291 2292 retv, _ := instantQuery(t, context.Background(), querier.Endpoint("http"), func() string { 2293 return "sidecar_test_metric" 2294 }, func() time.Time { return now.Add(time.Hour) }, promclient.QueryOptions{}, 1) 2295 2296 testutil.Equals(t, model.Vector{&model.Sample{ 2297 Metric: model.Metric{ 2298 "__name__": "sidecar_test_metric", 2299 "instance": "test", 2300 "region": "test", 2301 }, 2302 Value: model.SampleValue(2), 2303 Timestamp: model.TimeFromUnixNano(now.Add(time.Hour).UnixNano()), 2304 }}, retv) 2305 }