github.com/thanos-io/thanos@v0.32.5/test/e2e/exemplars_api_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  	"testing"
    10  	"time"
    11  
    12  	"github.com/efficientgo/e2e"
    13  	e2emon "github.com/efficientgo/e2e/monitoring"
    14  	"github.com/pkg/errors"
    15  	"github.com/prometheus/prometheus/model/timestamp"
    16  
    17  	"github.com/efficientgo/core/testutil"
    18  	"github.com/thanos-io/thanos/pkg/exemplars/exemplarspb"
    19  	"github.com/thanos-io/thanos/pkg/store/labelpb"
    20  	"github.com/thanos-io/thanos/test/e2e/e2ethanos"
    21  )
    22  
    23  const (
    24  	traceIDLabel = "traceID"
    25  )
    26  
    27  func TestExemplarsAPI_Fanout(t *testing.T) {
    28  	t.Parallel()
    29  	var (
    30  		prom1, prom2       *e2emon.InstrumentedRunnable
    31  		sidecar1, sidecar2 *e2emon.InstrumentedRunnable
    32  		err                error
    33  		e                  *e2e.DockerEnvironment
    34  	)
    35  
    36  	e, err = e2e.NewDockerEnvironment("exemplars-fanout")
    37  	testutil.Ok(t, err)
    38  	t.Cleanup(e2ethanos.CleanScenario(t, e))
    39  
    40  	qBuilder := e2ethanos.NewQuerierBuilder(e, "query")
    41  
    42  	prom1, sidecar1 = e2ethanos.NewPrometheusWithSidecar(
    43  		e,
    44  		"prom1",
    45  		e2ethanos.DefaultPromConfig("ha", 0, "", "", "localhost:9090", qBuilder.InternalEndpoint("http"), e2ethanos.LocalPrometheusTarget),
    46  		"",
    47  		e2ethanos.DefaultPrometheusImage(),
    48  		"",
    49  		e2ethanos.FeatureExemplarStorage,
    50  	)
    51  	prom2, sidecar2 = e2ethanos.NewPrometheusWithSidecar(
    52  		e,
    53  		"prom2",
    54  		e2ethanos.DefaultPromConfig("ha", 1, "", "", "localhost:9090", qBuilder.InternalEndpoint("http"), e2ethanos.LocalPrometheusTarget),
    55  		"",
    56  		e2ethanos.DefaultPrometheusImage(),
    57  		"",
    58  		e2ethanos.FeatureExemplarStorage,
    59  	)
    60  
    61  	tracingCfg := fmt.Sprintf(`type: JAEGER
    62  config:
    63    sampler_type: const
    64    sampler_param: 1
    65    service_name: %s`, qBuilder.Name())
    66  
    67  	stores := []string{sidecar1.InternalEndpoint("grpc"), sidecar2.InternalEndpoint("grpc")}
    68  
    69  	qBuilder = qBuilder.
    70  		WithStoreAddresses(stores...).
    71  		WithExemplarAddresses(stores...).
    72  		WithTracingConfig(tracingCfg)
    73  
    74  	q := qBuilder.Init()
    75  	testutil.Ok(t, e2e.StartAndWaitReady(q))
    76  
    77  	testutil.Ok(t, err)
    78  	testutil.Ok(t, e2e.StartAndWaitReady(prom1, sidecar1, prom2, sidecar2))
    79  
    80  	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
    81  	t.Cleanup(cancel)
    82  
    83  	testutil.Ok(t, q.WaitSumMetricsWithOptions(e2emon.Equals(2), []string{"thanos_store_nodes_grpc_connections"}, e2emon.WaitMissingMetrics()))
    84  	testutil.Ok(t, q.WaitSumMetricsWithOptions(e2emon.Equals(2), []string{"thanos_query_exemplar_apis_dns_provider_results"}, e2emon.WaitMissingMetrics()))
    85  
    86  	now := time.Now()
    87  	start := timestamp.FromTime(now.Add(-time.Hour))
    88  	end := timestamp.FromTime(now.Add(time.Hour))
    89  
    90  	// Send HTTP requests to thanos query to trigger exemplars.
    91  	labelNames(t, ctx, q.Endpoint("http"), nil, start, end, func(res []string) bool { return true })
    92  
    93  	t.Run("Basic exemplars query", func(t *testing.T) {
    94  		queryExemplars(t, ctx, q.Endpoint("http"), `http_request_duration_seconds_bucket{handler="label_names"}`, start, end, exemplarsOnExpectedSeries(map[string]string{
    95  			"__name__":   "http_request_duration_seconds_bucket",
    96  			"handler":    "label_names",
    97  			"job":        "myself",
    98  			"method":     "get",
    99  			"prometheus": "ha",
   100  		}))
   101  	})
   102  
   103  	t.Run("Exemplars query with matched external label", func(t *testing.T) {
   104  		// Here replica is an external label.
   105  		queryExemplars(t, ctx, q.Endpoint("http"), `http_request_duration_seconds_bucket{handler="label_names", replica="0"}`, start, end, exemplarsOnExpectedSeries(map[string]string{
   106  			"__name__":   "http_request_duration_seconds_bucket",
   107  			"handler":    "label_names",
   108  			"job":        "myself",
   109  			"method":     "get",
   110  			"prometheus": "ha",
   111  		}))
   112  	})
   113  
   114  	t.Run("Exemplars query doesn't match external label", func(t *testing.T) {
   115  		// Here replica is an external label, but it doesn't match.
   116  		queryExemplars(t, ctx, q.Endpoint("http"), `http_request_duration_seconds_bucket{handler="label_names", replica="foo"}`,
   117  			start, end, func(data []*exemplarspb.ExemplarData) error {
   118  				if len(data) > 0 {
   119  					return errors.Errorf("expected no examplers, got %v", data)
   120  				}
   121  				return nil
   122  			})
   123  	})
   124  }
   125  
   126  func exemplarsOnExpectedSeries(requiredSeriesLabels map[string]string) func(data []*exemplarspb.ExemplarData) error {
   127  	return func(data []*exemplarspb.ExemplarData) error {
   128  		if len(data) != 1 {
   129  			return errors.Errorf("unexpected result size, expected 1, got: %v", len(data))
   130  		}
   131  
   132  		// Compare series labels.
   133  		seriesLabels := labelpb.ZLabelSetsToPromLabelSets(data[0].SeriesLabels)
   134  		for _, lbls := range seriesLabels {
   135  			for k, v := range requiredSeriesLabels {
   136  				if lbls.Get(k) != v {
   137  					return errors.Errorf("unexpected labels in result, expected %v, got: %v", requiredSeriesLabels, seriesLabels)
   138  				}
   139  			}
   140  		}
   141  
   142  		// Make sure the exemplar contains the correct traceID label.
   143  		for _, exemplar := range data[0].Exemplars {
   144  			for _, lbls := range labelpb.ZLabelSetsToPromLabelSets(exemplar.Labels) {
   145  				if !lbls.Has(traceIDLabel) {
   146  					return errors.Errorf("unexpected labels in exemplar, expected %v, got: %v", traceIDLabel, exemplar.Labels)
   147  				}
   148  			}
   149  		}
   150  		return nil
   151  	}
   152  }