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  }