github.com/thanos-io/thanos@v0.32.5/test/e2e/tools_bucket_web_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  	"encoding/json"
     9  	"io"
    10  	"net/http"
    11  	"net/http/httptest"
    12  	"os"
    13  	"path"
    14  	"path/filepath"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/efficientgo/e2e"
    19  	e2edb "github.com/efficientgo/e2e/db"
    20  	"github.com/go-kit/log"
    21  	"github.com/prometheus/prometheus/model/labels"
    22  	"github.com/prometheus/prometheus/model/timestamp"
    23  	"github.com/thanos-io/objstore/providers/s3"
    24  
    25  	"github.com/thanos-io/objstore"
    26  	"github.com/thanos-io/objstore/client"
    27  
    28  	"github.com/efficientgo/core/testutil"
    29  	v1 "github.com/thanos-io/thanos/pkg/api/blocks"
    30  	"github.com/thanos-io/thanos/pkg/errors"
    31  	"github.com/thanos-io/thanos/pkg/runutil"
    32  	"github.com/thanos-io/thanos/test/e2e/e2ethanos"
    33  )
    34  
    35  func TestToolsBucketWebExternalPrefixWithoutReverseProxy(t *testing.T) {
    36  	t.Parallel()
    37  
    38  	e, err := e2e.NewDockerEnvironment("route-prefix")
    39  	testutil.Ok(t, err)
    40  	t.Cleanup(e2ethanos.CleanScenario(t, e))
    41  
    42  	externalPrefix := "testThanos"
    43  
    44  	const bucket = "compact-test"
    45  	m := e2edb.NewMinio(e, "thanos", bucket, e2edb.WithMinioTLS())
    46  	testutil.Ok(t, e2e.StartAndWaitReady(m))
    47  
    48  	svcConfig := client.BucketConfig{
    49  		Type:   client.S3,
    50  		Config: e2ethanos.NewS3Config(bucket, m.Endpoint("http"), m.InternalDir()),
    51  	}
    52  
    53  	b := e2ethanos.NewToolsBucketWeb(
    54  		e,
    55  		"1",
    56  		svcConfig,
    57  		"",
    58  		externalPrefix,
    59  		"",
    60  		"",
    61  		"",
    62  	)
    63  	testutil.Ok(t, e2e.StartAndWaitReady(b))
    64  
    65  	checkNetworkRequests(t, "http://"+b.Endpoint("http")+"/"+externalPrefix+"/blocks")
    66  }
    67  
    68  func TestToolsBucketWebExternalPrefix(t *testing.T) {
    69  	t.Parallel()
    70  
    71  	e, err := e2e.NewDockerEnvironment("external-prefix")
    72  	testutil.Ok(t, err)
    73  	t.Cleanup(e2ethanos.CleanScenario(t, e))
    74  
    75  	externalPrefix := "testThanos"
    76  	const bucket = "toolsBucketWeb-test"
    77  	m := e2edb.NewMinio(e, "thanos", bucket, e2edb.WithMinioTLS())
    78  	testutil.Ok(t, e2e.StartAndWaitReady(m))
    79  
    80  	svcConfig := client.BucketConfig{
    81  		Type:   client.S3,
    82  		Config: e2ethanos.NewS3Config(bucket, m.Endpoint("http"), m.InternalDir()),
    83  	}
    84  
    85  	b := e2ethanos.NewToolsBucketWeb(
    86  		e,
    87  		"1",
    88  		svcConfig,
    89  		"",
    90  		externalPrefix,
    91  		"",
    92  		"",
    93  		"",
    94  	)
    95  	testutil.Ok(t, e2e.StartAndWaitReady(b))
    96  
    97  	toolsBucketWebURL := urlParse(t, "http://"+b.Endpoint("http")+"/"+externalPrefix)
    98  
    99  	toolsBucketWebProxy := httptest.NewServer(e2ethanos.NewSingleHostReverseProxy(toolsBucketWebURL, externalPrefix))
   100  	t.Cleanup(toolsBucketWebProxy.Close)
   101  
   102  	checkNetworkRequests(t, toolsBucketWebProxy.URL+"/"+externalPrefix+"/blocks")
   103  }
   104  
   105  func TestToolsBucketWebExternalPrefixAndRoutePrefix(t *testing.T) {
   106  	t.Parallel()
   107  
   108  	e, err := e2e.NewDockerEnvironment("route-prefix")
   109  	testutil.Ok(t, err)
   110  	t.Cleanup(e2ethanos.CleanScenario(t, e))
   111  
   112  	externalPrefix := "testThanos"
   113  	routePrefix := "test"
   114  	const bucket = "toolsBucketWeb-test"
   115  	m := e2edb.NewMinio(e, "thanos", bucket, e2edb.WithMinioTLS())
   116  	testutil.Ok(t, err)
   117  	testutil.Ok(t, e2e.StartAndWaitReady(m))
   118  
   119  	svcConfig := client.BucketConfig{
   120  		Type:   client.S3,
   121  		Config: e2ethanos.NewS3Config(bucket, m.Endpoint("http"), m.InternalDir()),
   122  	}
   123  
   124  	b := e2ethanos.NewToolsBucketWeb(
   125  		e,
   126  		"1",
   127  		svcConfig,
   128  		routePrefix,
   129  		externalPrefix,
   130  		"",
   131  		"",
   132  		"",
   133  	)
   134  	testutil.Ok(t, e2e.StartAndWaitReady(b))
   135  
   136  	toolsBucketWebURL := urlParse(t, "http://"+b.Endpoint("http")+"/"+routePrefix)
   137  
   138  	toolsBucketWebProxy := httptest.NewServer(e2ethanos.NewSingleHostReverseProxy(toolsBucketWebURL, externalPrefix))
   139  	t.Cleanup(toolsBucketWebProxy.Close)
   140  
   141  	checkNetworkRequests(t, toolsBucketWebProxy.URL+"/"+externalPrefix+"/blocks")
   142  }
   143  
   144  func TestToolsBucketWebWithTimeAndRelabelFilter(t *testing.T) {
   145  	t.Parallel()
   146  
   147  	e, err := e2e.NewDockerEnvironment("time-relabel")
   148  	testutil.Ok(t, err)
   149  	t.Cleanup(e2ethanos.CleanScenario(t, e))
   150  
   151  	// Create Minio.
   152  	const bucket = "toolsBucketWeb-test"
   153  	m := e2edb.NewMinio(e, "thanos", bucket, e2edb.WithMinioTLS())
   154  	testutil.Ok(t, e2e.StartAndWaitReady(m))
   155  
   156  	// Create bucket.
   157  	logger := log.NewLogfmtLogger(os.Stdout)
   158  	bkt, err := s3.NewBucketWithConfig(logger,
   159  		e2ethanos.NewS3Config(bucket, m.Endpoint("http"), m.Dir()), "tools")
   160  	testutil.Ok(t, err)
   161  
   162  	// Create share dir for upload.
   163  	dir := filepath.Join(e.SharedDir(), "tmp")
   164  	testutil.Ok(t, os.MkdirAll(dir, os.ModePerm))
   165  
   166  	// Upload blocks.
   167  	now, err := time.Parse(time.RFC3339, "2021-07-24T08:00:00Z")
   168  	testutil.Ok(t, err)
   169  	blocks := []blockDesc{
   170  		{
   171  			series:  []labels.Labels{labels.FromStrings("a", "1", "b", "2")},
   172  			extLset: labels.FromStrings("tenant_id", "b", "replica", "1"),
   173  			mint:    timestamp.FromTime(now),
   174  			maxt:    timestamp.FromTime(now.Add(2 * time.Hour)),
   175  		},
   176  		{
   177  			series:  []labels.Labels{labels.FromStrings("a", "1", "b", "2")},
   178  			extLset: labels.FromStrings("tenant_id", "a", "replica", "1"),
   179  			mint:    timestamp.FromTime(now),
   180  			maxt:    timestamp.FromTime(now.Add(2 * time.Hour)),
   181  		},
   182  		{
   183  			series:  []labels.Labels{labels.FromStrings("a", "1", "b", "2")},
   184  			extLset: labels.FromStrings("tenant_id", "b", "replica", "1"),
   185  			mint:    timestamp.FromTime(now.Add(2 * time.Hour)),
   186  			maxt:    timestamp.FromTime(now.Add(4 * time.Hour)),
   187  		},
   188  	}
   189  	for _, b := range blocks {
   190  		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
   191  		t.Cleanup(cancel)
   192  
   193  		id, err := b.Create(ctx, dir, 0, b.hashFunc, 120)
   194  		testutil.Ok(t, err)
   195  		testutil.Ok(t, runutil.Retry(time.Second, ctx.Done(), func() error {
   196  			return objstore.UploadDir(ctx, logger, bkt, path.Join(dir, id.String()), id.String())
   197  		}))
   198  	}
   199  	// Start thanos tool bucket web.
   200  	svcConfig := client.BucketConfig{
   201  		Type:   client.S3,
   202  		Config: e2ethanos.NewS3Config(bucket, m.InternalEndpoint("http"), m.InternalDir()),
   203  	}
   204  	b := e2ethanos.NewToolsBucketWeb(
   205  		e,
   206  		"1",
   207  		svcConfig,
   208  		"",
   209  		"",
   210  		now.Format(time.RFC3339),
   211  		now.Add(1*time.Hour).Format(time.RFC3339),
   212  		`
   213  - action: keep
   214    regex: "b"
   215    source_labels: ["tenant_id"]`,
   216  	)
   217  	testutil.Ok(t, e2e.StartAndWaitReady(b))
   218  
   219  	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
   220  	t.Cleanup(cancel)
   221  
   222  	var respData struct {
   223  		Status string
   224  		Data   *v1.BlocksInfo
   225  	}
   226  
   227  	testutil.Ok(t, runutil.Retry(5*time.Second, ctx.Done(), func() error {
   228  		// Request blocks api.
   229  		resp, err := http.DefaultClient.Get("http://" + b.Endpoint("http") + "/api/v1/blocks")
   230  		if err != nil {
   231  			return err
   232  		}
   233  
   234  		if resp.StatusCode != http.StatusOK {
   235  			return errors.Newf("statuscode is not 200, got %d", resp.StatusCode)
   236  		}
   237  
   238  		body, err := io.ReadAll(resp.Body)
   239  		if err != nil {
   240  			return errors.Wrapf(err, "error reading body")
   241  		}
   242  
   243  		if err := resp.Body.Close(); err != nil {
   244  			return errors.Wrapf(err, "error closing body")
   245  		}
   246  
   247  		if err := json.Unmarshal(body, &respData); err != nil {
   248  			return errors.Wrapf(err, "error unmarshaling body")
   249  		}
   250  
   251  		if respData.Status != "success" {
   252  			return errors.Newf("status is not success, got %s", respData.Status)
   253  		}
   254  
   255  		// Filtered by time and relabel, result only one blocks.
   256  		if len(respData.Data.Blocks) == 1 {
   257  			return nil
   258  		}
   259  
   260  		return errors.Newf("expected 1 block, got %d", len(respData.Data.Blocks))
   261  	}))
   262  
   263  	testutil.Equals(t, 1, len(respData.Data.Blocks))
   264  	testutil.Equals(t, respData.Data.Blocks[0].MaxTime, blocks[0].maxt)
   265  	testutil.Equals(t, respData.Data.Blocks[0].MinTime, blocks[0].mint)
   266  	testutil.Equals(t, respData.Data.Blocks[0].Thanos.Labels, blocks[0].extLset.Map())
   267  }