Skip to content

Commit 8897e65

Browse files
authored
Merge pull request #140 from milinddethe15/support-custom-roundtripper
Add support for custom roundtripper
2 parents 075bf61 + 7f505a2 commit 8897e65

23 files changed

+303
-94
lines changed

client/factory.go

+9-8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package client
66
import (
77
"context"
88
"fmt"
9+
"net/http"
910
"strings"
1011

1112
"github.com/thanos-io/objstore"
@@ -49,7 +50,7 @@ type BucketConfig struct {
4950

5051
// NewBucket initializes and returns new object storage clients.
5152
// NOTE: confContentYaml can contain secrets.
52-
func NewBucket(logger log.Logger, confContentYaml []byte, component string) (objstore.Bucket, error) {
53+
func NewBucket(logger log.Logger, confContentYaml []byte, component string, rt http.RoundTripper) (objstore.Bucket, error) {
5354
level.Info(logger).Log("msg", "loading bucket configuration")
5455
bucketConf := &BucketConfig{}
5556
if err := yaml.UnmarshalStrict(confContentYaml, bucketConf); err != nil {
@@ -64,23 +65,23 @@ func NewBucket(logger log.Logger, confContentYaml []byte, component string) (obj
6465
var bucket objstore.Bucket
6566
switch strings.ToUpper(string(bucketConf.Type)) {
6667
case string(GCS):
67-
bucket, err = gcs.NewBucket(context.Background(), logger, config, component)
68+
bucket, err = gcs.NewBucket(context.Background(), logger, config, component, rt)
6869
case string(S3):
69-
bucket, err = s3.NewBucket(logger, config, component)
70+
bucket, err = s3.NewBucket(logger, config, component, rt)
7071
case string(AZURE):
71-
bucket, err = azure.NewBucket(logger, config, component)
72+
bucket, err = azure.NewBucket(logger, config, component, rt)
7273
case string(SWIFT):
73-
bucket, err = swift.NewContainer(logger, config)
74+
bucket, err = swift.NewContainer(logger, config, rt)
7475
case string(COS):
75-
bucket, err = cos.NewBucket(logger, config, component)
76+
bucket, err = cos.NewBucket(logger, config, component, rt)
7677
case string(ALIYUNOSS):
77-
bucket, err = oss.NewBucket(logger, config, component)
78+
bucket, err = oss.NewBucket(logger, config, component, rt)
7879
case string(FILESYSTEM):
7980
bucket, err = filesystem.NewBucketFromConfig(config)
8081
case string(BOS):
8182
bucket, err = bos.NewBucket(logger, config, component)
8283
case string(OCI):
83-
bucket, err = oci.NewBucket(logger, config)
84+
bucket, err = oci.NewBucket(logger, config, rt)
8485
case string(OBS):
8586
bucket, err = obs.NewBucket(logger, config)
8687
default:

client/factory_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func ExampleBucket() {
2323
}
2424

2525
// Create a new bucket.
26-
bucket, err := NewBucket(log.NewNopLogger(), confContentYaml, "example")
26+
bucket, err := NewBucket(log.NewNopLogger(), confContentYaml, "example", nil)
2727
if err != nil {
2828
panic(err)
2929
}
@@ -46,7 +46,7 @@ func ExampleTracingBucketUsingOpenTracing() { //nolint:govet
4646
}
4747

4848
// Create a new bucket.
49-
bucket, err := NewBucket(log.NewNopLogger(), confContentYaml, "example")
49+
bucket, err := NewBucket(log.NewNopLogger(), confContentYaml, "example", nil)
5050
if err != nil {
5151
panic(err)
5252
}
@@ -72,7 +72,7 @@ func ExampleTracingBucketUsingOpenTelemetry() { //nolint:govet
7272
}
7373

7474
// Create a new bucket.
75-
bucket, err := NewBucket(log.NewNopLogger(), confContentYaml, "example")
75+
bucket, err := NewBucket(log.NewNopLogger(), confContentYaml, "example", nil)
7676
if err != nil {
7777
panic(err)
7878
}

errutil/rt_error.go

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package errutil
2+
3+
import "net/http"
4+
5+
// ErrorRoundTripper is a custom RoundTripper that always returns an error.
6+
type ErrorRoundTripper struct {
7+
Err error
8+
}
9+
10+
func (ert *ErrorRoundTripper) RoundTrip(*http.Request) (*http.Response, error) {
11+
return nil, ert.Err
12+
}

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ require (
8888
github.com/prometheus/procfs v0.11.1 // indirect
8989
github.com/rs/xid v1.5.0 // indirect
9090
github.com/sony/gobreaker v0.5.0 // indirect
91+
github.com/stretchr/objx v0.5.2 // indirect
9192
go.opencensus.io v0.24.0 // indirect
9293
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
9394
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect

go.sum

+2-1
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,9 @@ github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=
210210
github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
211211
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
212212
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
213-
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
214213
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
214+
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
215+
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
215216
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
216217
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
217218
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=

providers/azure/azure.go

+8-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package azure
66
import (
77
"context"
88
"io"
9+
"net/http"
910
"os"
1011
"strings"
1112
"testing"
@@ -145,7 +146,7 @@ type Bucket struct {
145146
}
146147

147148
// NewBucket returns a new Bucket using the provided Azure config.
148-
func NewBucket(logger log.Logger, azureConfig []byte, component string) (*Bucket, error) {
149+
func NewBucket(logger log.Logger, azureConfig []byte, component string, rt http.RoundTripper) (*Bucket, error) {
149150
level.Debug(logger).Log("msg", "creating new Azure bucket connection", "component", component)
150151
conf, err := parseConfig(azureConfig)
151152
if err != nil {
@@ -154,11 +155,14 @@ func NewBucket(logger log.Logger, azureConfig []byte, component string) (*Bucket
154155
if conf.MSIResource != "" {
155156
level.Warn(logger).Log("msg", "The field msi_resource has been deprecated and should no longer be set")
156157
}
157-
return NewBucketWithConfig(logger, conf, component)
158+
return NewBucketWithConfig(logger, conf, component, rt)
158159
}
159160

160161
// NewBucketWithConfig returns a new Bucket using the provided Azure config struct.
161-
func NewBucketWithConfig(logger log.Logger, conf Config, component string) (*Bucket, error) {
162+
func NewBucketWithConfig(logger log.Logger, conf Config, component string, rt http.RoundTripper) (*Bucket, error) {
163+
if rt != nil {
164+
conf.HTTPConfig.Transport = rt
165+
}
162166
if err := conf.validate(); err != nil {
163167
return nil, err
164168
}
@@ -355,7 +359,7 @@ func NewTestBucket(t testing.TB, component string) (objstore.Bucket, func(), err
355359
if err != nil {
356360
return nil, nil, err
357361
}
358-
bkt, err := NewBucket(log.NewNopLogger(), bc, component)
362+
bkt, err := NewBucket(log.NewNopLogger(), bc, component, nil)
359363
if err != nil {
360364
t.Errorf("Cannot create Azure storage container:")
361365
return nil, nil, err

providers/azure/azure_test.go

+17-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import (
88
"time"
99

1010
"github.com/efficientgo/core/testutil"
11+
"github.com/go-kit/log"
12+
"github.com/pkg/errors"
1113

14+
"github.com/thanos-io/objstore/errutil"
1215
"github.com/thanos-io/objstore/exthttp"
1316
)
1417

@@ -20,7 +23,7 @@ type TestCase struct {
2023
}
2124

2225
var validConfig = []byte(`storage_account: "myStorageAccount"
23-
storage_account_key: "abc123"
26+
storage_account_key: "bXlTdXBlclNlY3JldEtleTEyMyFAIw=="
2427
container: "MyContainer"
2528
endpoint: "blob.core.windows.net"
2629
reader_config:
@@ -222,3 +225,16 @@ http_config:
222225
testutil.Ok(t, err)
223226
testutil.Equals(t, true, transport.TLSClientConfig.InsecureSkipVerify)
224227
}
228+
229+
func TestNewBucketWithErrorRoundTripper(t *testing.T) {
230+
cfg, err := parseConfig(validConfig)
231+
testutil.Ok(t, err)
232+
233+
rt := &errutil.ErrorRoundTripper{Err: errors.New("RoundTripper error")}
234+
235+
_, err = NewBucketWithConfig(log.NewNopLogger(), cfg, "test", rt)
236+
237+
// We expect an error from the RoundTripper
238+
testutil.NotOk(t, err)
239+
testutil.Assert(t, errors.Is(err, rt.Err), "Expected RoundTripper error, got: %v", err)
240+
}

providers/azure/helpers.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@ import (
2020
const DirDelim = "/"
2121

2222
func getContainerClient(conf Config) (*container.Client, error) {
23-
dt, err := exthttp.DefaultTransport(conf.HTTPConfig)
23+
var rt http.RoundTripper
24+
rt, err := exthttp.DefaultTransport(conf.HTTPConfig)
2425
if err != nil {
2526
return nil, err
2627
}
28+
if conf.HTTPConfig.Transport != nil {
29+
rt = conf.HTTPConfig.Transport
30+
}
2731
opt := &container.ClientOptions{
2832
ClientOptions: azcore.ClientOptions{
2933
Retry: policy.RetryOptions{
@@ -35,7 +39,7 @@ func getContainerClient(conf Config) (*container.Client, error) {
3539
Telemetry: policy.TelemetryOptions{
3640
ApplicationID: "Thanos",
3741
},
38-
Transport: &http.Client{Transport: dt},
42+
Transport: &http.Client{Transport: rt},
3943
},
4044
}
4145

providers/bos/bos.go

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ func parseConfig(conf []byte) (Config, error) {
6666

6767
// NewBucket new bos bucket.
6868
func NewBucket(logger log.Logger, conf []byte, component string) (*Bucket, error) {
69+
// TODO(https://github.com/thanos-io/objstore/pull/140): Add support for custom roundtripper.
6970
if logger == nil {
7071
logger = log.NewNopLogger()
7172
}

providers/cos/cos.go

+13-7
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func parseConfig(conf []byte) (Config, error) {
9595
}
9696

9797
// NewBucket returns a new Bucket using the provided cos configuration.
98-
func NewBucket(logger log.Logger, conf []byte, component string) (*Bucket, error) {
98+
func NewBucket(logger log.Logger, conf []byte, component string, rt http.RoundTripper) (*Bucket, error) {
9999
if logger == nil {
100100
logger = log.NewNopLogger()
101101
}
@@ -104,12 +104,11 @@ func NewBucket(logger log.Logger, conf []byte, component string) (*Bucket, error
104104
if err != nil {
105105
return nil, errors.Wrap(err, "parsing cos configuration")
106106
}
107-
108-
return NewBucketWithConfig(logger, config, component)
107+
return NewBucketWithConfig(logger, config, component, rt)
109108
}
110109

111110
// NewBucketWithConfig returns a new Bucket using the provided cos config values.
112-
func NewBucketWithConfig(logger log.Logger, config Config, component string) (*Bucket, error) {
111+
func NewBucketWithConfig(logger log.Logger, config Config, component string, rt http.RoundTripper) (*Bucket, error) {
113112
if err := config.validate(); err != nil {
114113
return nil, errors.Wrap(err, "validate cos configuration")
115114
}
@@ -128,7 +127,14 @@ func NewBucketWithConfig(logger log.Logger, config Config, component string) (*B
128127
}
129128
}
130129
b := &cos.BaseURL{BucketURL: bucketURL}
131-
tpt, _ := exthttp.DefaultTransport(config.HTTPConfig)
130+
var tpt http.RoundTripper
131+
tpt, err = exthttp.DefaultTransport(config.HTTPConfig)
132+
if err != nil {
133+
return nil, err
134+
}
135+
if rt != nil {
136+
tpt = rt
137+
}
132138
client := cos.NewClient(b, &http.Client{
133139
Transport: &cos.AuthorizationTransport{
134140
SecretID: config.SecretId,
@@ -485,7 +491,7 @@ func NewTestBucket(t testing.TB) (objstore.Bucket, func(), error) {
485491
return nil, nil, err
486492
}
487493

488-
b, err := NewBucket(log.NewNopLogger(), bc, "thanos-e2e-test")
494+
b, err := NewBucket(log.NewNopLogger(), bc, "thanos-e2e-test", nil)
489495
if err != nil {
490496
return nil, nil, err
491497
}
@@ -506,7 +512,7 @@ func NewTestBucket(t testing.TB) (objstore.Bucket, func(), error) {
506512
return nil, nil, err
507513
}
508514

509-
b, err := NewBucket(log.NewNopLogger(), bc, "thanos-e2e-test")
515+
b, err := NewBucket(log.NewNopLogger(), bc, "thanos-e2e-test", nil)
510516
if err != nil {
511517
return nil, nil, err
512518
}

providers/cos/cos_test.go

+22
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@
44
package cos
55

66
import (
7+
"context"
78
"testing"
89
"time"
910

1011
"github.com/efficientgo/core/testutil"
12+
"github.com/go-kit/log"
13+
"github.com/pkg/errors"
1114
"github.com/prometheus/common/model"
1215

16+
"github.com/thanos-io/objstore/errutil"
1317
"github.com/thanos-io/objstore/exthttp"
1418
)
1519

@@ -137,3 +141,21 @@ func TestConfig_validate(t *testing.T) {
137141
})
138142
}
139143
}
144+
145+
func TestNewBucketWithErrorRoundTripper(t *testing.T) {
146+
config := Config{
147+
Bucket: "bucket",
148+
AppId: "123",
149+
Region: "test",
150+
SecretId: "sid",
151+
SecretKey: "skey",
152+
}
153+
rt := &errutil.ErrorRoundTripper{Err: errors.New("RoundTripper error")}
154+
155+
bkt, err := NewBucketWithConfig(log.NewNopLogger(), config, "test", rt)
156+
testutil.Ok(t, err)
157+
_, err = bkt.Get(context.Background(), "Test")
158+
// We expect an error from the RoundTripper
159+
testutil.NotOk(t, err)
160+
testutil.Assert(t, errors.Is(err, rt.Err), "Expected RoundTripper error, got: %v", err)
161+
}

providers/gcs/gcs.go

+16-13
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ type Config struct {
5252
// ChunkSizeBytes controls the maximum number of bytes of the object that the
5353
// Writer will attempt to send to the server in a single request
5454
// Used as storage.Writer.ChunkSize of https://pkg.go.dev/google.golang.org/cloud/storage#Writer
55-
ChunkSizeBytes int `yaml:"chunk_size_bytes"`
55+
ChunkSizeBytes int `yaml:"chunk_size_bytes"`
56+
noAuth bool `yaml:"no_auth"`
5657
}
5758

5859
// Bucket implements the store.Bucket and shipper.Bucket interfaces against GCS.
@@ -76,20 +77,22 @@ func parseConfig(conf []byte) (Config, error) {
7677
}
7778

7879
// NewBucket returns a new Bucket against the given bucket handle.
79-
func NewBucket(ctx context.Context, logger log.Logger, conf []byte, component string) (*Bucket, error) {
80+
func NewBucket(ctx context.Context, logger log.Logger, conf []byte, component string, rt http.RoundTripper) (*Bucket, error) {
8081
config, err := parseConfig(conf)
8182
if err != nil {
8283
return nil, err
8384
}
84-
return NewBucketWithConfig(ctx, logger, config, component)
85+
return NewBucketWithConfig(ctx, logger, config, component, rt)
8586
}
8687

8788
// NewBucketWithConfig returns a new Bucket with gcs Config struct.
88-
func NewBucketWithConfig(ctx context.Context, logger log.Logger, gc Config, component string) (*Bucket, error) {
89+
func NewBucketWithConfig(ctx context.Context, logger log.Logger, gc Config, component string, rt http.RoundTripper) (*Bucket, error) {
8990
if gc.Bucket == "" {
9091
return nil, errors.New("missing Google Cloud Storage bucket name for stored blocks")
9192
}
92-
93+
if rt != nil {
94+
gc.HTTPConfig.Transport = rt
95+
}
9396
var opts []option.ClientOption
9497

9598
// If ServiceAccount is provided, use them in GCS client, otherwise fallback to Google default logic.
@@ -100,7 +103,9 @@ func NewBucketWithConfig(ctx context.Context, logger log.Logger, gc Config, comp
100103
}
101104
opts = append(opts, option.WithCredentials(credentials))
102105
}
103-
106+
if gc.noAuth {
107+
opts = append(opts, option.WithoutAuthentication())
108+
}
104109
opts = append(opts,
105110
option.WithUserAgent(fmt.Sprintf("thanos-%s/%s (%s)", component, version.Version, runtime.Version())),
106111
)
@@ -120,14 +125,12 @@ func appendHttpOptions(gc Config, opts []option.ClientOption) ([]option.ClientOp
120125
// Check if a roundtripper has been set in the config
121126
// otherwise build the default transport.
122127
var rt http.RoundTripper
128+
rt, err := exthttp.DefaultTransport(gc.HTTPConfig)
129+
if err != nil {
130+
return nil, err
131+
}
123132
if gc.HTTPConfig.Transport != nil {
124133
rt = gc.HTTPConfig.Transport
125-
} else {
126-
var err error
127-
rt, err = exthttp.DefaultTransport(gc.HTTPConfig)
128-
if err != nil {
129-
return nil, err
130-
}
131134
}
132135

133136
// GCS uses some defaults when "options.WithHTTPClient" is not used that are important when we call
@@ -312,7 +315,7 @@ func NewTestBucket(t testing.TB, project string) (objstore.Bucket, func(), error
312315
return nil, nil, err
313316
}
314317

315-
b, err := NewBucket(ctx, log.NewNopLogger(), bc, "thanos-e2e-test")
318+
b, err := NewBucket(ctx, log.NewNopLogger(), bc, "thanos-e2e-test", nil)
316319
if err != nil {
317320
return nil, nil, err
318321
}

0 commit comments

Comments
 (0)