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

     1  package client
     2  
     3  import (
     4  	"errors"
     5  	"flag"
     6  	"fmt"
     7  	"strings"
     8  
     9  	"github.com/go-kit/log"
    10  	"github.com/go-kit/log/level"
    11  	"github.com/samber/lo"
    12  	"github.com/thanos-io/objstore"
    13  
    14  	"github.com/grafana/pyroscope/pkg/objstore/providers/azure"
    15  	"github.com/grafana/pyroscope/pkg/objstore/providers/cos"
    16  	"github.com/grafana/pyroscope/pkg/objstore/providers/filesystem"
    17  	"github.com/grafana/pyroscope/pkg/objstore/providers/gcs"
    18  	"github.com/grafana/pyroscope/pkg/objstore/providers/s3"
    19  	"github.com/grafana/pyroscope/pkg/objstore/providers/swift"
    20  )
    21  
    22  const (
    23  	// None is the null value for the storage backends.
    24  	None = ""
    25  
    26  	// S3 is the value for the S3 storage backend.
    27  	S3 = "s3"
    28  
    29  	// GCS is the value for the GCS storage backend.
    30  	GCS = "gcs"
    31  
    32  	// Azure is the value for the Azure storage backend.
    33  	Azure = "azure"
    34  
    35  	// Swift is the value for the Openstack Swift storage backend.
    36  	Swift = "swift"
    37  
    38  	// COS is the value for the Tencent Cloud COS storage backend.
    39  	COS = "cos"
    40  
    41  	// Filesystem is the value for the filesystem storage backend.
    42  	Filesystem = "filesystem"
    43  )
    44  
    45  var (
    46  	SupportedBackends = []string{S3, GCS, Azure, Swift, Filesystem, COS}
    47  
    48  	ErrUnsupportedStorageBackend      = errors.New("unsupported storage backend")
    49  	ErrStoragePrefixStartsWithSlash   = errors.New("storage prefix starts with a slash")
    50  	ErrStoragePrefixEmptyPathSegment  = errors.New("storage prefix contains an empty path segment")
    51  	ErrStoragePrefixInvalidCharacters = errors.New("storage prefix contains invalid characters: only alphanumeric, hyphen, underscore, dot, and no segement should be '.' or '..'")
    52  	ErrStoragePrefixBothFlagsSet      = errors.New("both storage.prefix and storage.storage-prefix are set, please use only storage.prefix, as storage.storage-prefix is deprecated")
    53  )
    54  
    55  type ErrInvalidCharactersInStoragePrefix struct {
    56  	Prefix string
    57  }
    58  
    59  func (e *ErrInvalidCharactersInStoragePrefix) Error() string {
    60  	return fmt.Sprintf("storage prefix '%s' contains invalid characters", e.Prefix)
    61  }
    62  
    63  type StorageBackendConfig struct {
    64  	Backend string `yaml:"backend"`
    65  
    66  	// Backends
    67  	S3         s3.Config         `yaml:"s3"`
    68  	GCS        gcs.Config        `yaml:"gcs"`
    69  	Azure      azure.Config      `yaml:"azure"`
    70  	Swift      swift.Config      `yaml:"swift"`
    71  	COS        cos.Config        `yaml:"cos"`
    72  	Filesystem filesystem.Config `yaml:"filesystem"`
    73  }
    74  
    75  // Returns the supportedBackends for the package and any custom backends injected into the config.
    76  func (cfg *StorageBackendConfig) supportedBackends() []string {
    77  	return SupportedBackends
    78  }
    79  
    80  // RegisterFlags registers the backend storage config.
    81  func (cfg *StorageBackendConfig) RegisterFlags(f *flag.FlagSet) {
    82  	cfg.RegisterFlagsWithPrefix("", f)
    83  }
    84  
    85  func (cfg *StorageBackendConfig) RegisterFlagsWithPrefixAndDefaultDirectory(prefix, dir string, f *flag.FlagSet) {
    86  	cfg.S3.RegisterFlagsWithPrefix(prefix, f)
    87  	cfg.GCS.RegisterFlagsWithPrefix(prefix, f)
    88  	cfg.Azure.RegisterFlagsWithPrefix(prefix, f)
    89  	cfg.Swift.RegisterFlagsWithPrefix(prefix, f)
    90  	cfg.Filesystem.RegisterFlagsWithPrefixAndDefaultDirectory(prefix, dir, f)
    91  	cfg.COS.RegisterFlagsWithPrefix(prefix, f)
    92  	f.StringVar(&cfg.Backend, prefix+"backend", None, fmt.Sprintf("Backend storage to use. Supported backends are: %s.", strings.Join(cfg.supportedBackends(), ", ")))
    93  }
    94  
    95  func (cfg *StorageBackendConfig) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) {
    96  	cfg.RegisterFlagsWithPrefixAndDefaultDirectory(prefix, "", f)
    97  }
    98  
    99  func (cfg *StorageBackendConfig) Validate() error {
   100  	if cfg.Backend == None {
   101  		return nil
   102  	}
   103  
   104  	if !lo.Contains(cfg.supportedBackends(), cfg.Backend) {
   105  		return ErrUnsupportedStorageBackend
   106  	}
   107  
   108  	switch cfg.Backend {
   109  	case S3:
   110  		return cfg.S3.Validate()
   111  	case COS:
   112  		return cfg.COS.Validate()
   113  	default:
   114  		return nil
   115  	}
   116  }
   117  
   118  // Config holds configuration for accessing long-term storage.
   119  type Config struct {
   120  	StorageBackendConfig `yaml:",inline"`
   121  
   122  	Prefix                  string `yaml:"prefix"`
   123  	DeprecatedStoragePrefix string `yaml:"storage_prefix" category:"experimental"` // Deprecated: use Prefix instead
   124  
   125  	// Not used internally, meant to allow callers to wrap Buckets
   126  	// created using this config
   127  	Middlewares []func(objstore.Bucket) (objstore.Bucket, error) `yaml:"-"`
   128  }
   129  
   130  // RegisterFlags registers the backend storage config.
   131  func (cfg *Config) RegisterFlags(f *flag.FlagSet) {
   132  	cfg.RegisterFlagsWithPrefix("", f)
   133  }
   134  
   135  func (cfg *Config) RegisterFlagsWithPrefixAndDefaultDirectory(prefix, dir string, f *flag.FlagSet) {
   136  	cfg.StorageBackendConfig.RegisterFlagsWithPrefixAndDefaultDirectory(prefix, dir, f)
   137  	f.StringVar(&cfg.Prefix, prefix+"prefix", "", "Prefix for all objects stored in the backend storage. For simplicity, it may only contain digits and English alphabet characters, hyphens, underscores, dots and forward slashes.")
   138  	f.StringVar(&cfg.DeprecatedStoragePrefix, prefix+"storage-prefix", "", "Deprecated: Use '"+prefix+".prefix' instead. Prefix for all objects stored in the backend storage. For simplicity, it may only contain digits and English alphabet characters, hyphens, underscores, dots and forward slashes.")
   139  }
   140  
   141  func (cfg *Config) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) {
   142  	cfg.RegisterFlagsWithPrefixAndDefaultDirectory(prefix, "./data-shared", f)
   143  }
   144  
   145  // alphanumeric, hyphen, underscore, dot, and must not be . or ..
   146  func validStoragePrefixPart(part string) bool {
   147  	if part == "." || part == ".." {
   148  		return false
   149  	}
   150  	for i, b := range part {
   151  		if (b < 'a' || b > 'z') && (b < 'A' || b > 'Z') && b != '_' && b != '-' && b != '.' && (b < '0' || b > '9' || i == 0) {
   152  			return false
   153  		}
   154  	}
   155  	return true
   156  }
   157  
   158  func validStoragePrefix(prefix string) error {
   159  	// without a prefix exit quickly
   160  	if prefix == "" {
   161  		return nil
   162  	}
   163  
   164  	parts := strings.Split(prefix, "/")
   165  
   166  	for idx, part := range parts {
   167  		if part == "" {
   168  			if idx == 0 {
   169  				return ErrStoragePrefixStartsWithSlash
   170  			}
   171  			if idx != len(parts)-1 {
   172  				return ErrStoragePrefixEmptyPathSegment
   173  			}
   174  			// slash at the end is fine
   175  		}
   176  		if !validStoragePrefixPart(part) {
   177  			return ErrStoragePrefixInvalidCharacters
   178  		}
   179  	}
   180  
   181  	return nil
   182  }
   183  
   184  func (cfg *Config) getPrefix() string {
   185  	if cfg.Prefix != "" {
   186  		return cfg.Prefix
   187  	}
   188  
   189  	return cfg.DeprecatedStoragePrefix
   190  }
   191  
   192  func (cfg *Config) Validate(logger log.Logger) error {
   193  	if cfg.DeprecatedStoragePrefix != "" {
   194  		if cfg.Prefix != "" {
   195  			return ErrStoragePrefixBothFlagsSet
   196  		}
   197  		level.Warn(logger).Log("msg", "bucket config has a deprecated storage.storage-prefix flag set. Please, use storage.prefix instead.")
   198  	}
   199  	if err := validStoragePrefix(cfg.getPrefix()); err != nil {
   200  		return err
   201  	}
   202  
   203  	return cfg.StorageBackendConfig.Validate()
   204  }