Skip to content

Commit 375a48d

Browse files
committed
objstore: add experimental encryption wrapper
Signed-off-by: Michael Hoffmann <[email protected]>
1 parent 11ffbc4 commit 375a48d

File tree

7 files changed

+218
-21
lines changed

7 files changed

+218
-21
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ NOTE: As semantic versioning states all 0.y.z releases can contain breaking chan
99
We use *breaking :warning:* to mark changes that are not backward compatible (relates only to v0.y.z releases.)
1010

1111
## Unreleased
12+
- [#46](https://github.com/thanos-io/objstore/pull/46) Objstore: Add experimental encryption wrapper
1213

1314
### Fixed
1415
- [#33](https://github.com/thanos-io/objstore/pull/33) Tracing: Add `ContextWithTracer()` to inject the tracer into the context.

README.md

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ See [MAINTAINERS.md](https://github.com/thanos-io/thanos/blob/main/MAINTAINERS.m
4949
The core this module is the [`Bucket` interface](objstore.go):
5050

5151
```go mdox-exec="sed -n '37,50p' objstore.go"
52+
OpAttributes = "attributes"
53+
)
54+
5255
// Bucket provides read and write access to an object storage bucket.
5356
// NOTE: We assume strong consistency for write-read flow.
5457
type Bucket interface {
@@ -60,14 +63,14 @@ type Bucket interface {
6063
Upload(ctx context.Context, name string, r io.Reader) error
6164

6265
// Delete removes the object with the given name.
63-
// If object does not exists in the moment of deletion, Delete should throw error.
64-
Delete(ctx context.Context, name string) error
65-
6666
```
6767
6868
All [provider implementations](providers) have to implement `Bucket` interface that allows common read and write operations that all supported by all object providers. If you want to limit the code that will do bucket operation to only read access (smart idea, allowing to limit access permissions), you can use the [`BucketReader` interface](objstore.go):
6969
7070
```go mdox-exec="sed -n '68,88p' objstore.go"
71+
// TODO(bwplotka): Remove this when moved to Go 1.14 and replace with InstrumentedBucketReader.
72+
ReaderWithExpectedErrs(IsOpFailureExpectedFunc) BucketReader
73+
}
7174

7275
// BucketReader provides read access to an object storage bucket.
7376
type BucketReader interface {
@@ -86,9 +89,6 @@ type BucketReader interface {
8689
Exists(ctx context.Context, name string) (bool, error)
8790

8891
// IsObjNotFoundErr returns true if error means that object is not found. Relevant to Get operations.
89-
IsObjNotFoundErr(err error) bool
90-
91-
// Attributes returns information about the specified object.
9292
```
9393
9494
Those interfaces represent the object storage operations your code can use from `objstore` clients.
@@ -152,6 +152,7 @@ config:
152152
insecure: false
153153
signature_version2: false
154154
secret_key: ""
155+
session_token: ""
155156
put_user_metadata: {}
156157
http_config:
157158
idle_conn_timeout: 1m30s
@@ -181,6 +182,9 @@ config:
181182
encryption_key: ""
182183
sts_endpoint: ""
183184
prefix: ""
185+
encryption:
186+
enabled: false
187+
key_base64: ""
184188
```
185189
186190
At a minimum, you will need to provide a value for the `bucket`, `endpoint`, `access_key`, and `secret_key` keys. The rest of the keys are optional.
@@ -345,6 +349,9 @@ config:
345349
bucket: ""
346350
service_account: ""
347351
prefix: ""
352+
encryption:
353+
enabled: false
354+
key_base64: ""
348355
```
349356
350357
###### Using GOOGLE_APPLICATION_CREDENTIALS
@@ -445,6 +452,9 @@ config:
445452
disable_compression: false
446453
msi_resource: ""
447454
prefix: ""
455+
encryption:
456+
enabled: false
457+
key_base64: ""
448458
```
449459
450460
If `msi_resource` is used, authentication is done via system-assigned managed identity. The value for Azure should be `https://<storage-account-name>.blob.core.windows.net`.
@@ -489,6 +499,9 @@ config:
489499
timeout: 5m
490500
use_dynamic_large_objects: false
491501
prefix: ""
502+
encryption:
503+
enabled: false
504+
key_base64: ""
492505
```
493506

494507
##### Tencent COS
@@ -523,6 +536,9 @@ config:
523536
insecure_skip_verify: false
524537
disable_compression: false
525538
prefix: ""
539+
encryption:
540+
enabled: false
541+
key_base64: ""
526542
```
527543

528544
The `secret_key` and `secret_id` field is required. The `http_config` field is optional for optimize HTTP transport settings. There are two ways to configure the required bucket information:
@@ -543,6 +559,9 @@ config:
543559
access_key_id: ""
544560
access_key_secret: ""
545561
prefix: ""
562+
encryption:
563+
enabled: false
564+
key_base64: ""
546565
```
547566

548567
##### Baidu BOS
@@ -557,6 +576,9 @@ config:
557576
access_key: ""
558577
secret_key: ""
559578
prefix: ""
579+
encryption:
580+
enabled: false
581+
key_base64: ""
560582
```
561583

562584
##### Filesystem
@@ -572,6 +594,9 @@ type: FILESYSTEM
572594
config:
573595
directory: ""
574596
prefix: ""
597+
encryption:
598+
enabled: false
599+
key_base64: ""
575600
```
576601

577602
### Oracle Cloud Infrastructure Object Storage

client/factory.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package client
55

66
import (
77
"context"
8+
"encoding/base64"
89
"fmt"
910
"strings"
1011

@@ -41,9 +42,15 @@ const (
4142
)
4243

4344
type BucketConfig struct {
44-
Type ObjProvider `yaml:"type"`
45-
Config interface{} `yaml:"config"`
46-
Prefix string `yaml:"prefix" default:""`
45+
Type ObjProvider `yaml:"type"`
46+
Config interface{} `yaml:"config"`
47+
Prefix string `yaml:"prefix" default:""`
48+
Encryption EncryptionConfig `yaml:"encryption"`
49+
}
50+
51+
type EncryptionConfig struct {
52+
Enabled bool `yaml:"enabled"`
53+
KeyBase64 string `yaml:"key_base64"`
4754
}
4855

4956
// NewBucket initializes and returns new object storage clients.
@@ -87,5 +94,16 @@ func NewBucket(logger log.Logger, confContentYaml []byte, reg prometheus.Registe
8794
return nil, errors.Wrap(err, fmt.Sprintf("create %s client", bucketConf.Type))
8895
}
8996

97+
if bucketConf.Encryption.Enabled {
98+
key, err := base64.RawStdEncoding.DecodeString(bucketConf.Encryption.KeyBase64)
99+
if err != nil {
100+
return nil, errors.Wrap(err, "unable to read base64 key")
101+
}
102+
if len(key) != 32 {
103+
return nil, errors.New("decoded key must have size 32")
104+
}
105+
bucket = objstore.BucketWithEncryption(bucket, key)
106+
}
107+
90108
return objstore.NewTracingBucket(objstore.BucketWithMetrics(bucket.Name(), objstore.NewPrefixedBucket(bucket, bucketConf.Prefix), reg)), nil
91109
}

go.mod

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ require (
1313
github.com/fatih/structtag v1.2.0
1414
github.com/go-kit/log v0.2.1
1515
github.com/minio/minio-go/v7 v7.0.45
16+
github.com/minio/sio v0.3.0
1617
github.com/ncw/swift v1.0.53
1718
github.com/opentracing/opentracing-go v1.2.0
1819
github.com/oracle/oci-go-sdk/v65 v65.13.0
@@ -80,9 +81,9 @@ require (
8081
github.com/sony/gobreaker v0.5.0 // indirect
8182
github.com/stretchr/objx v0.2.0 // indirect
8283
go.opencensus.io v0.23.0 // indirect
83-
golang.org/x/net v0.2.0 // indirect
84-
golang.org/x/sys v0.3.0 // indirect
85-
golang.org/x/text v0.5.0 // indirect
84+
golang.org/x/net v0.6.0 // indirect
85+
golang.org/x/sys v0.5.0 // indirect
86+
golang.org/x/text v0.7.0 // indirect
8687
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
8788
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
8889
google.golang.org/appengine v1.6.7 // indirect
@@ -100,5 +101,5 @@ require (
100101
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.5.1
101102
github.com/kr/text v0.2.0 // indirect
102103
github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b // indirect
103-
golang.org/x/crypto v0.3.0 // indirect
104+
golang.org/x/crypto v0.6.0 // indirect
104105
)

go.sum

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,8 @@ github.com/minio/minio-go/v7 v7.0.45 h1:g4IeM9M9pW/Lo8AGGNOjBZYlvmtlE1N5TQEYWXRW
307307
github.com/minio/minio-go/v7 v7.0.45/go.mod h1:nCrRzjoSUQh8hgKKtu3Y708OLvRLtuASMg2/nvmbarw=
308308
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
309309
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
310+
github.com/minio/sio v0.3.0 h1:syEFBewzOMOYVzSTFpp1MqpSZk8rUNbz8VIIc+PNzus=
311+
github.com/minio/sio v0.3.0/go.mod h1:8b0yPp2avGThviy/+OCJBI6OMpvxoUuiLvE6F1lebhw=
310312
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
311313
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
312314
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -407,11 +409,12 @@ go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
407409
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
408410
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
409411
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
412+
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
410413
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
411414
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
412415
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
413-
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
414-
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
416+
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
417+
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
415418
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
416419
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
417420
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -490,8 +493,8 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su
490493
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
491494
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
492495
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
493-
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
494-
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
496+
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
497+
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
495498
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
496499
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
497500
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -592,8 +595,8 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
592595
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
593596
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
594597
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
595-
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
596-
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
598+
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
599+
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
597600
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
598601
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
599602
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -605,8 +608,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
605608
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
606609
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
607610
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
608-
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
609-
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
611+
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
612+
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
610613
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
611614
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
612615
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

objstore.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ package objstore
66
import (
77
"bytes"
88
"context"
9+
"crypto/rand"
10+
"crypto/sha256"
911
"io"
1012
"io/fs"
13+
"math"
1114
"os"
1215
"path"
1316
"path/filepath"
@@ -18,6 +21,7 @@ import (
1821
"github.com/efficientgo/core/logerrcapture"
1922
"github.com/go-kit/log"
2023
"github.com/go-kit/log/level"
24+
"github.com/minio/sio"
2125
"github.com/pkg/errors"
2226
"github.com/prometheus/client_golang/prometheus"
2327
"github.com/prometheus/client_golang/prometheus/promauto"
@@ -395,6 +399,101 @@ func DownloadDir(ctx context.Context, logger log.Logger, bkt BucketReader, origi
395399
// IsOpFailureExpectedFunc allows to mark certain errors as expected, so they will not increment thanos_objstore_bucket_operation_failures_total metric.
396400
type IsOpFailureExpectedFunc func(error) bool
397401

402+
// BucketWithEncryption takes a bucket and transparently encrypts and decrypts its payloads.
403+
func BucketWithEncryption(b Bucket, key []byte) *encryptedBucket {
404+
return &encryptedBucket{Bucket: b, masterKey: key}
405+
}
406+
407+
type encryptedBucket struct {
408+
Bucket
409+
410+
masterKey []byte
411+
}
412+
413+
const saltSizeBytes = 32
414+
415+
// As per https://github.com/minio/sio/blob/master/DARE.md#appendices we need a unique key data stream.
416+
// We derive a unique key from the configuration provided master key by fetching 32 random bits salt and
417+
// using Sha256(master key ++ salt) as our derived encryption key. The salt is prepended to the encrypted
418+
// object. This is okay since the salt does not need to be kept a secret.
419+
func (eb *encryptedBucket) encryptionConfig(salt []byte) sio.Config {
420+
key := sha256.Sum256(append(eb.masterKey, salt...))
421+
return sio.Config{Key: key[:], CipherSuites: []byte{sio.AES_256_GCM}}
422+
}
423+
424+
func (eb *encryptedBucket) Attributes(ctx context.Context, name string) (ObjectAttributes, error) {
425+
attrs, err := eb.Bucket.Attributes(ctx, name)
426+
if err != nil {
427+
return attrs, err
428+
}
429+
430+
decSize, err := sio.DecryptedSize(uint64(attrs.Size) - saltSizeBytes)
431+
if err != nil {
432+
return ObjectAttributes{}, errors.Wrap(err, "unable to determine unecrypted size")
433+
}
434+
435+
if decSize > math.MaxInt64 {
436+
return ObjectAttributes{}, errors.New("size of decrypted blob too large")
437+
}
438+
439+
return ObjectAttributes{Size: int64(decSize), LastModified: attrs.LastModified}, nil
440+
}
441+
442+
func (eb *encryptedBucket) Upload(ctx context.Context, name string, r io.Reader) error {
443+
salt := make([]byte, saltSizeBytes)
444+
if _, err := rand.Read(salt); err != nil {
445+
return errors.Wrap(err, "unable to derive encryption key for stream")
446+
}
447+
448+
er, err := sio.EncryptReader(r, eb.encryptionConfig(salt))
449+
if err != nil {
450+
return errors.Wrap(err, "unable to create encryption stream")
451+
}
452+
453+
tr := io.MultiReader(bytes.NewReader(salt), er)
454+
return eb.Bucket.Upload(ctx, name, tr)
455+
}
456+
457+
func (eb *encryptedBucket) Get(ctx context.Context, name string) (io.ReadCloser, error) {
458+
return eb.GetRange(ctx, name, 0, -1)
459+
}
460+
461+
func (eb *encryptedBucket) GetRange(ctx context.Context, name string, off, length int64) (io.ReadCloser, error) {
462+
saltReader, err := eb.Bucket.GetRange(ctx, name, 0, saltSizeBytes)
463+
if err != nil {
464+
return nil, errors.Wrap(err, "unable to fetch salt")
465+
}
466+
defer saltReader.Close()
467+
468+
salt, err := io.ReadAll(saltReader)
469+
if err != nil {
470+
return nil, errors.Wrap(err, "unable to read salt")
471+
}
472+
473+
br := &bucketReaderAt{ctx: ctx, name: name, b: eb.Bucket}
474+
dr, err := sio.DecryptReaderAt(br, eb.encryptionConfig(salt))
475+
if err != nil {
476+
return nil, errors.Wrap(err, "unable to create decryption stream")
477+
}
478+
return io.NopCloser(io.NewSectionReader(dr, off, length)), nil
479+
}
480+
481+
type bucketReaderAt struct {
482+
ctx context.Context
483+
name string
484+
b BucketReader
485+
}
486+
487+
func (br *bucketReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
488+
rc, err := br.b.GetRange(br.ctx, br.name, off+saltSizeBytes, int64(len(p)))
489+
if err != nil {
490+
return 0, err
491+
}
492+
defer rc.Close()
493+
494+
return rc.Read(p)
495+
}
496+
398497
var _ InstrumentedBucket = &metricBucket{}
399498

400499
// BucketWithMetrics takes a bucket and registers metrics with the given registry for

0 commit comments

Comments
 (0)