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 }