github.com/thanos-io/thanos@v0.32.5/test/e2e/metadata_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  	"sort"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/efficientgo/e2e"
    14  	e2emon "github.com/efficientgo/e2e/monitoring"
    15  
    16  	"github.com/efficientgo/core/testutil"
    17  	"github.com/thanos-io/thanos/pkg/metadata/metadatapb"
    18  	"github.com/thanos-io/thanos/pkg/promclient"
    19  	"github.com/thanos-io/thanos/pkg/runutil"
    20  	"github.com/thanos-io/thanos/test/e2e/e2ethanos"
    21  )
    22  
    23  func TestMetadataAPI_Fanout(t *testing.T) {
    24  	t.Parallel()
    25  
    26  	e, err := e2e.NewDockerEnvironment("metadata-fanout")
    27  	testutil.Ok(t, err)
    28  	t.Cleanup(e2ethanos.CleanScenario(t, e))
    29  
    30  	// 2x Prometheus.
    31  	// Each Prometheus scrapes its own metrics and Sidecar's metrics.
    32  	prom1, sidecar1 := e2ethanos.NewPrometheusWithSidecar(
    33  		e,
    34  		"prom1",
    35  		e2ethanos.DefaultPromConfig("ha", 0, "", "", e2ethanos.LocalPrometheusTarget, "sidecar-prom1:8080"),
    36  		"",
    37  		e2ethanos.DefaultPrometheusImage(), "",
    38  	)
    39  	prom2, sidecar2 := e2ethanos.NewPrometheusWithSidecar(
    40  		e,
    41  		"prom2",
    42  		e2ethanos.DefaultPromConfig("ha", 1, "", "", e2ethanos.LocalPrometheusTarget, "sidecar-prom2:8080"),
    43  		"",
    44  		e2ethanos.DefaultPrometheusImage(), "",
    45  	)
    46  	testutil.Ok(t, e2e.StartAndWaitReady(prom1, sidecar1, prom2, sidecar2))
    47  
    48  	stores := []string{sidecar1.InternalEndpoint("grpc"), sidecar2.InternalEndpoint("grpc")}
    49  	q := e2ethanos.NewQuerierBuilder(
    50  		e, "query", stores...).
    51  		WithMetadataAddresses(stores...).
    52  		Init()
    53  	testutil.Ok(t, e2e.StartAndWaitReady(q))
    54  
    55  	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
    56  	t.Cleanup(cancel)
    57  
    58  	testutil.Ok(t, q.WaitSumMetricsWithOptions(e2emon.Equals(2), []string{"thanos_store_nodes_grpc_connections"}, e2emon.WaitMissingMetrics()))
    59  	testutil.Ok(t, q.WaitSumMetricsWithOptions(e2emon.Equals(2), []string{"thanos_query_metadata_apis_dns_provider_results"}, e2emon.WaitMissingMetrics()))
    60  
    61  	var promMeta map[string][]metadatapb.Meta
    62  	// Wait metadata response to be ready as Prometheus gets metadata after scrape.
    63  	testutil.Ok(t, runutil.Retry(5*time.Second, ctx.Done(), func() error {
    64  		promMeta, err = promclient.NewDefaultClient().MetricMetadataInGRPC(ctx, urlParse(t, "http://"+prom1.Endpoint("http")), "", -1)
    65  		if err != nil {
    66  			return err
    67  		}
    68  		if len(promMeta) > 0 {
    69  			return nil
    70  		}
    71  		return fmt.Errorf("empty metadata response from Prometheus")
    72  	}))
    73  
    74  	var thanosMeta map[string][]metadatapb.Meta
    75  	// Retry until length of metadata response is the same as Prometheus.
    76  	testutil.Ok(t, runutil.Retry(5*time.Second, ctx.Done(), func() error {
    77  		thanosMeta, err = promclient.NewDefaultClient().MetricMetadataInGRPC(ctx, urlParse(t, "http://"+q.Endpoint("http")), "", -1)
    78  		if err != nil {
    79  			return err
    80  		}
    81  		if len(thanosMeta) == len(promMeta) {
    82  			return nil
    83  		}
    84  
    85  		return fmt.Errorf("different metadata response from Prometheus")
    86  	}))
    87  
    88  	testutil.Assert(t, len(thanosMeta) > 0, "got empty metadata response from Thanos")
    89  
    90  	// Metadata response from Prometheus and Thanos Querier should be the same after deduplication.
    91  	metadataEqual(t, thanosMeta, promMeta)
    92  
    93  	// We only expect to see one metadata returned.
    94  	thanosMeta, err = promclient.NewDefaultClient().MetricMetadataInGRPC(ctx, urlParse(t, "http://"+q.Endpoint("http")), "", 1)
    95  	testutil.Ok(t, err)
    96  	testutil.Equals(t, len(thanosMeta), 1)
    97  
    98  	// We only expect to see ten metadata returned.
    99  	thanosMeta, err = promclient.NewDefaultClient().MetricMetadataInGRPC(ctx, urlParse(t, "http://"+q.Endpoint("http")), "", 10)
   100  	testutil.Ok(t, err)
   101  	testutil.Equals(t, len(thanosMeta), 10)
   102  
   103  	// No metadata returned.
   104  	thanosMeta, err = promclient.NewDefaultClient().MetricMetadataInGRPC(ctx, urlParse(t, "http://"+q.Endpoint("http")), "", 0)
   105  	testutil.Ok(t, err)
   106  	testutil.Equals(t, len(thanosMeta), 0)
   107  
   108  	// Only prometheus_build_info metric will be returned.
   109  	thanosMeta, err = promclient.NewDefaultClient().MetricMetadataInGRPC(ctx, urlParse(t, "http://"+q.Endpoint("http")), "prometheus_build_info", -1)
   110  	testutil.Ok(t, err)
   111  	testutil.Assert(t, len(thanosMeta) == 1 && len(thanosMeta["prometheus_build_info"]) > 0, "expected one prometheus_build_info metadata from Thanos, got %v", thanosMeta)
   112  }
   113  
   114  func metadataEqual(t *testing.T, meta1, meta2 map[string][]metadatapb.Meta) {
   115  	// The two responses should have equal # of entries.
   116  	testutil.Equals(t, len(meta1), len(meta2))
   117  
   118  	for metric := range meta1 {
   119  		// Get metadata for the metric.
   120  		meta1MetricMeta := meta1[metric]
   121  		meta2MetricMeta, ok := meta2[metric]
   122  		testutil.Assert(t, ok)
   123  
   124  		sort.Slice(meta1MetricMeta, func(i, j int) bool {
   125  			return meta1MetricMeta[i].Help < meta1MetricMeta[j].Help
   126  		})
   127  		sort.Slice(meta2MetricMeta, func(i, j int) bool {
   128  			return meta2MetricMeta[i].Help < meta2MetricMeta[j].Help
   129  		})
   130  		testutil.Equals(t, meta1MetricMeta, meta2MetricMeta)
   131  	}
   132  }