github.com/grafana/pyroscope@v1.18.0/pkg/objstore/objstore.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package objstore
     5  
     6  import (
     7  	"context"
     8  	"io"
     9  	"strings"
    10  
    11  	"github.com/thanos-io/objstore"
    12  )
    13  
    14  type Bucket interface {
    15  	objstore.Bucket
    16  	ReaderAt(ctx context.Context, filename string) (ReaderAtCloser, error)
    17  }
    18  
    19  type BucketReader interface {
    20  	objstore.BucketReader
    21  	ReaderAt(ctx context.Context, filename string) (ReaderAtCloser, error)
    22  }
    23  
    24  // IsOpFailureExpectedFunc allows to mark certain errors as expected, so they will not increment objstore_bucket_operation_failures_total metric.
    25  type IsOpFailureExpectedFunc func(error) bool
    26  
    27  type InstrumentedBucket interface {
    28  	Bucket
    29  	// WithExpectedErrs allows to specify a filter that marks certain errors as expected, so it will not increment
    30  	// objstore_bucket_operation_failures_total metric.
    31  	WithExpectedErrs(IsOpFailureExpectedFunc) Bucket
    32  
    33  	// ReaderWithExpectedErrs allows to specify a filter that marks certain errors as expected, so it will not increment
    34  	// objstore_bucket_operation_failures_total metric.
    35  	// TODO(bwplotka): Remove this when moved to Go 1.14 and replace with InstrumentedBucketReader.
    36  	ReaderWithExpectedErrs(IsOpFailureExpectedFunc) BucketReader
    37  }
    38  
    39  type PrefixedBucket struct {
    40  	Bucket
    41  	prefix string
    42  }
    43  
    44  func NewPrefixedBucket(bkt Bucket, prefix string) Bucket {
    45  	if validPrefix(prefix) {
    46  		return &PrefixedBucket{Bucket: bkt, prefix: strings.Trim(prefix, objstore.DirDelim)}
    47  	}
    48  
    49  	return bkt
    50  }
    51  
    52  func validPrefix(prefix string) bool {
    53  	prefix = strings.ReplaceAll(prefix, "/", "")
    54  	return len(prefix) > 0
    55  }
    56  
    57  func conditionalPrefix(prefix, name string) string {
    58  	if len(name) > 0 {
    59  		return withPrefix(prefix, name)
    60  	}
    61  
    62  	return name
    63  }
    64  
    65  func withPrefix(prefix, name string) string {
    66  	return prefix + objstore.DirDelim + name
    67  }
    68  
    69  func (p *PrefixedBucket) Close() error {
    70  	return p.Bucket.Close()
    71  }
    72  
    73  func (p *PrefixedBucket) Prefix() string {
    74  	if prefixed, ok := p.Bucket.(interface{ Prefix() string }); ok && prefixed.Prefix() != "" {
    75  		return prefixed.Prefix() + objstore.DirDelim + p.prefix
    76  	}
    77  	return p.prefix
    78  }
    79  
    80  func (p *PrefixedBucket) ReaderAt(ctx context.Context, name string) (ReaderAtCloser, error) {
    81  	return p.Bucket.ReaderAt(ctx, conditionalPrefix(p.prefix, name))
    82  }
    83  
    84  // Iter calls f for each entry in the given directory (not recursive.). The argument to f is the full
    85  // object name including the prefix of the inspected directory.
    86  // Entries are passed to function in sorted order.
    87  func (p *PrefixedBucket) Iter(ctx context.Context, dir string, f func(string) error, options ...objstore.IterOption) error {
    88  	pdir := withPrefix(p.prefix, dir)
    89  
    90  	return p.Bucket.Iter(ctx, pdir, func(s string) error {
    91  		return f(strings.TrimPrefix(s, p.prefix+objstore.DirDelim))
    92  	}, options...)
    93  }
    94  
    95  // Get returns a reader for the given object name.
    96  func (p *PrefixedBucket) Get(ctx context.Context, name string) (io.ReadCloser, error) {
    97  	return p.Bucket.Get(ctx, conditionalPrefix(p.prefix, name))
    98  }
    99  
   100  // GetRange returns a new range reader for the given object name and range.
   101  func (p *PrefixedBucket) GetRange(ctx context.Context, name string, off int64, length int64) (io.ReadCloser, error) {
   102  	return p.Bucket.GetRange(ctx, conditionalPrefix(p.prefix, name), off, length)
   103  }
   104  
   105  // Exists checks if the given object exists in the bucket.
   106  func (p *PrefixedBucket) Exists(ctx context.Context, name string) (bool, error) {
   107  	return p.Bucket.Exists(ctx, conditionalPrefix(p.prefix, name))
   108  }
   109  
   110  // IsObjNotFoundErr returns true if error means that object is not found. Relevant to Get operations.
   111  func (p *PrefixedBucket) IsObjNotFoundErr(err error) bool {
   112  	return p.Bucket.IsObjNotFoundErr(err)
   113  }
   114  
   115  // Attributes returns information about the specified object.
   116  func (p PrefixedBucket) Attributes(ctx context.Context, name string) (objstore.ObjectAttributes, error) {
   117  	return p.Bucket.Attributes(ctx, conditionalPrefix(p.prefix, name))
   118  }
   119  
   120  // Upload the contents of the reader as an object into the bucket.
   121  // Upload should be idempotent.
   122  func (p *PrefixedBucket) Upload(ctx context.Context, name string, r io.Reader, opts ...objstore.ObjectUploadOption) error {
   123  	return p.Bucket.Upload(ctx, conditionalPrefix(p.prefix, name), r, opts...)
   124  }
   125  
   126  // Delete removes the object with the given name.
   127  // If object does not exists in the moment of deletion, Delete should throw error.
   128  func (p *PrefixedBucket) Delete(ctx context.Context, name string) error {
   129  	return p.Bucket.Delete(ctx, conditionalPrefix(p.prefix, name))
   130  }
   131  
   132  // Name returns the bucket name for the provider.
   133  func (p *PrefixedBucket) Name() string {
   134  	return p.Bucket.Name()
   135  }
   136  
   137  // ReaderWithExpectedErrs implements objstore.Bucket.
   138  func (p *PrefixedBucket) ReaderWithExpectedErrs(fn IsOpFailureExpectedFunc) BucketReader {
   139  	return p.WithExpectedErrs(fn)
   140  }
   141  
   142  // WithExpectedErrs implements objstore.Bucket.
   143  func (p *PrefixedBucket) WithExpectedErrs(fn IsOpFailureExpectedFunc) Bucket {
   144  	if ib, ok := p.Bucket.(InstrumentedBucket); ok {
   145  		return &PrefixedBucket{
   146  			Bucket: ib.WithExpectedErrs(fn),
   147  			prefix: p.prefix,
   148  		}
   149  	}
   150  
   151  	return p
   152  }