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 }