github.com/thanos-io/thanos@v0.32.5/cmd/thanos/main_test.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package main
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"path"
    12  	"strings"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/go-kit/log"
    17  	"github.com/oklog/ulid"
    18  	"github.com/prometheus/client_golang/prometheus"
    19  	promtest "github.com/prometheus/client_golang/prometheus/testutil"
    20  	"github.com/prometheus/prometheus/model/labels"
    21  	"github.com/thanos-io/objstore"
    22  
    23  	"github.com/efficientgo/core/testutil"
    24  	"github.com/thanos-io/thanos/pkg/block"
    25  	"github.com/thanos-io/thanos/pkg/block/metadata"
    26  	"github.com/thanos-io/thanos/pkg/compact/downsample"
    27  	"github.com/thanos-io/thanos/pkg/testutil/e2eutil"
    28  )
    29  
    30  type erroringBucket struct {
    31  	bkt objstore.InstrumentedBucket
    32  }
    33  
    34  func (b *erroringBucket) Close() error {
    35  	return b.bkt.Close()
    36  }
    37  
    38  // WithExpectedErrs allows to specify a filter that marks certain errors as expected, so it will not increment
    39  // thanos_objstore_bucket_operation_failures_total metric.
    40  func (b *erroringBucket) WithExpectedErrs(f objstore.IsOpFailureExpectedFunc) objstore.Bucket {
    41  	return b.bkt.WithExpectedErrs(f)
    42  }
    43  
    44  // ReaderWithExpectedErrs allows to specify a filter that marks certain errors as expected, so it will not increment
    45  // thanos_objstore_bucket_operation_failures_total metric.
    46  func (b *erroringBucket) ReaderWithExpectedErrs(f objstore.IsOpFailureExpectedFunc) objstore.BucketReader {
    47  	return b.bkt.ReaderWithExpectedErrs(f)
    48  }
    49  
    50  func (b *erroringBucket) Iter(ctx context.Context, dir string, f func(string) error, options ...objstore.IterOption) error {
    51  	return b.bkt.Iter(ctx, dir, f, options...)
    52  }
    53  
    54  // Get returns a reader for the given object name.
    55  func (b *erroringBucket) Get(ctx context.Context, name string) (io.ReadCloser, error) {
    56  	if strings.Contains(name, "chunk") {
    57  		return nil, fmt.Errorf("some random error has occurred")
    58  	}
    59  	return b.bkt.Get(ctx, name)
    60  }
    61  
    62  // GetRange returns a new range reader for the given object name and range.
    63  func (b *erroringBucket) GetRange(ctx context.Context, name string, off, length int64) (io.ReadCloser, error) {
    64  	if strings.Contains(name, "chunk") {
    65  		return nil, fmt.Errorf("some random error has occurred")
    66  	}
    67  	return b.bkt.GetRange(ctx, name, off, length)
    68  }
    69  
    70  // Exists checks if the given object exists in the bucket.
    71  func (b *erroringBucket) Exists(ctx context.Context, name string) (bool, error) {
    72  	return b.bkt.Exists(ctx, name)
    73  }
    74  
    75  // IsObjNotFoundErr returns true if error means that object is not found. Relevant to Get operations.
    76  func (b *erroringBucket) IsObjNotFoundErr(err error) bool {
    77  	return b.bkt.IsObjNotFoundErr(err)
    78  }
    79  
    80  // IsAccessDeniedErr returns true if error means that access to the object was denied.
    81  func (b *erroringBucket) IsAccessDeniedErr(err error) bool {
    82  	return b.bkt.IsAccessDeniedErr(err)
    83  }
    84  
    85  // Attributes returns information about the specified object.
    86  func (b *erroringBucket) Attributes(ctx context.Context, name string) (objstore.ObjectAttributes, error) {
    87  	return b.bkt.Attributes(ctx, name)
    88  }
    89  
    90  // Upload the contents of the reader as an object into the bucket.
    91  // Upload should be idempotent.
    92  func (b *erroringBucket) Upload(ctx context.Context, name string, r io.Reader) error {
    93  	return b.bkt.Upload(ctx, name, r)
    94  }
    95  
    96  // Delete removes the object with the given name.
    97  // If object does not exists in the moment of deletion, Delete should throw error.
    98  func (b *erroringBucket) Delete(ctx context.Context, name string) error {
    99  	return b.bkt.Delete(ctx, name)
   100  }
   101  
   102  // Name returns the bucket name for the provider.
   103  func (b *erroringBucket) Name() string {
   104  	return b.bkt.Name()
   105  }
   106  
   107  // Ensures that downsampleBucket() stops its work properly
   108  // after an error occurs with some blocks in the backlog.
   109  // Testing for https://github.com/thanos-io/thanos/issues/4960.
   110  func TestRegression4960_Deadlock(t *testing.T) {
   111  	logger := log.NewLogfmtLogger(os.Stderr)
   112  	dir := t.TempDir()
   113  
   114  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   115  	defer cancel()
   116  
   117  	bkt := objstore.WithNoopInstr(objstore.NewInMemBucket())
   118  	bkt = &erroringBucket{bkt: bkt}
   119  	var id, id2, id3 ulid.ULID
   120  	var err error
   121  	{
   122  		id, err = e2eutil.CreateBlock(
   123  			ctx,
   124  			dir,
   125  			[]labels.Labels{{{Name: "a", Value: "1"}}},
   126  			1, 0, downsample.ResLevel1DownsampleRange+1, // Pass the minimum ResLevel1DownsampleRange check.
   127  			labels.Labels{{Name: "e1", Value: "1"}},
   128  			downsample.ResLevel0, metadata.NoneFunc)
   129  		testutil.Ok(t, err)
   130  		testutil.Ok(t, block.Upload(ctx, logger, bkt, path.Join(dir, id.String()), metadata.NoneFunc))
   131  	}
   132  	{
   133  		id2, err = e2eutil.CreateBlock(
   134  			ctx,
   135  			dir,
   136  			[]labels.Labels{{{Name: "a", Value: "2"}}},
   137  			1, 0, downsample.ResLevel1DownsampleRange+1, // Pass the minimum ResLevel1DownsampleRange check.
   138  			labels.Labels{{Name: "e1", Value: "2"}},
   139  			downsample.ResLevel0, metadata.NoneFunc)
   140  		testutil.Ok(t, err)
   141  		testutil.Ok(t, block.Upload(ctx, logger, bkt, path.Join(dir, id2.String()), metadata.NoneFunc))
   142  	}
   143  	{
   144  		id3, err = e2eutil.CreateBlock(
   145  			ctx,
   146  			dir,
   147  			[]labels.Labels{{{Name: "a", Value: "2"}}},
   148  			1, 0, downsample.ResLevel1DownsampleRange+1, // Pass the minimum ResLevel1DownsampleRange check.
   149  			labels.Labels{{Name: "e1", Value: "2"}},
   150  			downsample.ResLevel0, metadata.NoneFunc)
   151  		testutil.Ok(t, err)
   152  		testutil.Ok(t, block.Upload(ctx, logger, bkt, path.Join(dir, id3.String()), metadata.NoneFunc))
   153  	}
   154  
   155  	meta, err := block.DownloadMeta(ctx, logger, bkt, id)
   156  	testutil.Ok(t, err)
   157  
   158  	metrics := newDownsampleMetrics(prometheus.NewRegistry())
   159  	testutil.Equals(t, 0.0, promtest.ToFloat64(metrics.downsamples.WithLabelValues(meta.Thanos.GroupKey())))
   160  	metaFetcher, err := block.NewMetaFetcher(nil, block.FetcherConcurrency, bkt, "", nil, nil)
   161  	testutil.Ok(t, err)
   162  
   163  	metas, _, err := metaFetcher.Fetch(ctx)
   164  	testutil.Ok(t, err)
   165  	err = downsampleBucket(ctx, logger, metrics, bkt, metas, dir, 1, 1, metadata.NoneFunc, false)
   166  	testutil.NotOk(t, err)
   167  
   168  	testutil.Assert(t, strings.Contains(err.Error(), "some random error has occurred"))
   169  
   170  }
   171  
   172  func TestCleanupDownsampleCacheFolder(t *testing.T) {
   173  	logger := log.NewLogfmtLogger(os.Stderr)
   174  	dir := t.TempDir()
   175  
   176  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   177  	defer cancel()
   178  
   179  	bkt := objstore.WithNoopInstr(objstore.NewInMemBucket())
   180  	var id ulid.ULID
   181  	var err error
   182  	{
   183  		id, err = e2eutil.CreateBlock(
   184  			ctx,
   185  			dir,
   186  			[]labels.Labels{{{Name: "a", Value: "1"}}},
   187  			1, 0, downsample.ResLevel1DownsampleRange+1, // Pass the minimum ResLevel1DownsampleRange check.
   188  			labels.Labels{{Name: "e1", Value: "1"}},
   189  			downsample.ResLevel0, metadata.NoneFunc)
   190  		testutil.Ok(t, err)
   191  		testutil.Ok(t, block.Upload(ctx, logger, bkt, path.Join(dir, id.String()), metadata.NoneFunc))
   192  	}
   193  
   194  	meta, err := block.DownloadMeta(ctx, logger, bkt, id)
   195  	testutil.Ok(t, err)
   196  
   197  	metrics := newDownsampleMetrics(prometheus.NewRegistry())
   198  	testutil.Equals(t, 0.0, promtest.ToFloat64(metrics.downsamples.WithLabelValues(meta.Thanos.GroupKey())))
   199  	metaFetcher, err := block.NewMetaFetcher(nil, block.FetcherConcurrency, bkt, "", nil, nil)
   200  	testutil.Ok(t, err)
   201  
   202  	metas, _, err := metaFetcher.Fetch(ctx)
   203  	testutil.Ok(t, err)
   204  	testutil.Ok(t, downsampleBucket(ctx, logger, metrics, bkt, metas, dir, 1, 1, metadata.NoneFunc, false))
   205  	testutil.Equals(t, 1.0, promtest.ToFloat64(metrics.downsamples.WithLabelValues(meta.Thanos.GroupKey())))
   206  
   207  	_, err = os.Stat(dir)
   208  	testutil.Assert(t, os.IsNotExist(err), "index cache dir should not exist at the end of execution")
   209  }