Skip to content

Commit a50e1d7

Browse files
feat: update S3 client initialization (thomaspoignant#2480)
Co-authored-by: Thomas Poignant <[email protected]>
1 parent 012659f commit a50e1d7

File tree

4 files changed

+112
-23
lines changed

4 files changed

+112
-23
lines changed

exporter/s3exporterv2/exporter.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ type Exporter struct {
5151
// Default: SNAPPY
5252
ParquetCompressionCodec string
5353

54+
// S3ClientOptions is a list of functional options to configure the S3 client.
55+
// Provide additional functional options to further configure the behavior of the client,
56+
// such as changing the client's endpoint or adding custom middleware behavior.
57+
// For more information about the options, please check:
58+
// https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/s3#Options
59+
S3ClientOptions []func(*s3.Options)
60+
5461
s3Uploader UploaderAPI
5562
init sync.Once
5663
ffLogger *fflog.FFLogger
@@ -68,7 +75,7 @@ func (f *Exporter) initializeUploader(ctx context.Context) error {
6875
f.AwsConfig = &cfg
6976
}
7077

71-
client := s3.NewFromConfig(*f.AwsConfig)
78+
client := s3.NewFromConfig(*f.AwsConfig, f.S3ClientOptions...)
7279
f.s3Uploader = manager.NewUploader(client)
7380
})
7481
return initErr

exporter/s3exporterv2/exporter_test.go

+41-13
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"testing"
88

99
"github.com/aws/aws-sdk-go-v2/aws"
10+
"github.com/aws/aws-sdk-go-v2/service/s3"
1011
"github.com/stretchr/testify/assert"
1112
"github.com/thomaspoignant/go-feature-flag/exporter"
1213
"github.com/thomaspoignant/go-feature-flag/testutils"
@@ -16,12 +17,13 @@ import (
1617
func TestS3_Export(t *testing.T) {
1718
hostname, _ := os.Hostname()
1819
type fields struct {
19-
Bucket string
20-
AwsConfig *aws.Config
21-
Format string
22-
S3Path string
23-
Filename string
24-
CsvTemplate string
20+
Bucket string
21+
AwsConfig *aws.Config
22+
Format string
23+
S3Path string
24+
Filename string
25+
CsvTemplate string
26+
S3ClientOptions []func(*s3.Options)
2527
}
2628

2729
tests := []struct {
@@ -164,19 +166,45 @@ func TestS3_Export(t *testing.T) {
164166
},
165167
wantErr: true,
166168
},
169+
{
170+
name: "With S3 Client Options",
171+
fields: fields{
172+
Bucket: "test",
173+
S3ClientOptions: []func(*s3.Options){
174+
func(o *s3.Options) {
175+
o.UseAccelerate = true
176+
},
177+
},
178+
},
179+
events: []exporter.FeatureEvent{
180+
{
181+
Kind: "feature", ContextKind: "anonymousUser", UserKey: "ABCD", CreationDate: 1617970547, Key: "random-key",
182+
Variation: "Default", Value: "YO", Default: false, Source: "SERVER",
183+
},
184+
},
185+
expectedFile: "./testdata/all_default.json",
186+
expectedName: "^/flag-variation-" + hostname + "-[0-9]*\\.json$",
187+
},
167188
}
168189
for _, tt := range tests {
169190
t.Run(tt.name, func(t *testing.T) {
170191
s3ManagerMock := testutils.S3ManagerV2Mock{}
171192
f := &Exporter{
172-
Bucket: tt.fields.Bucket,
173-
AwsConfig: tt.fields.AwsConfig,
174-
Format: tt.fields.Format,
175-
S3Path: tt.fields.S3Path,
176-
Filename: tt.fields.Filename,
177-
CsvTemplate: tt.fields.CsvTemplate,
178-
s3Uploader: &s3ManagerMock,
193+
Bucket: tt.fields.Bucket,
194+
AwsConfig: tt.fields.AwsConfig,
195+
Format: tt.fields.Format,
196+
S3Path: tt.fields.S3Path,
197+
Filename: tt.fields.Filename,
198+
CsvTemplate: tt.fields.CsvTemplate,
199+
S3ClientOptions: tt.fields.S3ClientOptions,
200+
s3Uploader: &s3ManagerMock,
201+
}
202+
203+
// Verify that S3ClientOptions are correctly set on the Exporter
204+
if tt.fields.S3ClientOptions != nil {
205+
assert.Equal(t, tt.fields.S3ClientOptions, f.S3ClientOptions, "S3ClientOptions should be set correctly on the Exporter")
179206
}
207+
180208
err := f.Export(context.Background(), &fflog.FFLogger{LeveledLogger: slog.Default()}, tt.events)
181209
if tt.wantErr {
182210
assert.Error(t, err, "Export should error")

retriever/s3retrieverv2/retriever.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ type Retriever struct {
2424
// download your feature flag configuration file.
2525
AwsConfig *aws.Config
2626

27+
// S3ClientOptions is a list of functional options to configure the S3 client.
28+
// Provide additional functional options to further configure the behavior of the client,
29+
// such as changing the client's endpoint or adding custom middleware behavior.
30+
// For more information about the options, please check:
31+
// https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/s3#Options
32+
S3ClientOptions []func(*s3.Options)
33+
2734
// downloader is an internal field, it is the downloader use by the AWS-SDK
2835
downloader DownloaderAPI
2936
status retriever.Status
@@ -40,7 +47,7 @@ func (s *Retriever) Init(ctx context.Context, _ *fflog.FFLogger) error {
4047
}
4148
s.AwsConfig = &cfg
4249
}
43-
client := s3.NewFromConfig(*s.AwsConfig)
50+
client := s3.NewFromConfig(*s.AwsConfig, s.S3ClientOptions...)
4451
s.downloader = manager.NewDownloader(client)
4552
}
4653
s.status = retriever.RetrieverReady

retriever/s3retrieverv2/retriever_test.go

+55-8
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@ import (
66
"testing"
77

88
"github.com/aws/aws-sdk-go-v2/config"
9+
"github.com/aws/aws-sdk-go-v2/service/s3"
910
"github.com/stretchr/testify/assert"
1011
"github.com/thomaspoignant/go-feature-flag/retriever"
1112
"github.com/thomaspoignant/go-feature-flag/testutils"
1213
)
1314

1415
func Test_s3Retriever_Retrieve(t *testing.T) {
1516
type fields struct {
16-
downloader DownloaderAPI
17-
bucket string
18-
item string
19-
context context.Context
17+
downloader DownloaderAPI
18+
bucket string
19+
item string
20+
context context.Context
21+
S3ClientOptions []func(*s3.Options)
2022
}
2123
tests := []struct {
2224
name string
@@ -60,22 +62,46 @@ func Test_s3Retriever_Retrieve(t *testing.T) {
6062
want: "./testdata/flag-config.yaml",
6163
wantErr: false,
6264
},
65+
{
66+
name: "With S3 Client Options",
67+
fields: fields{
68+
downloader: &testutils.S3ManagerV2Mock{
69+
TestDataLocation: "./testdata",
70+
},
71+
bucket: "Bucket",
72+
item: "valid",
73+
S3ClientOptions: []func(*s3.Options){
74+
func(o *s3.Options) {
75+
o.UseAccelerate = true
76+
},
77+
},
78+
},
79+
want: "./testdata/flag-config.yaml",
80+
wantErr: false,
81+
},
6382
}
6483
for _, tt := range tests {
6584
t.Run(tt.name, func(t *testing.T) {
6685
awsConf, _ := config.LoadDefaultConfig(context.TODO())
6786
s := Retriever{
68-
Bucket: tt.fields.bucket,
69-
Item: tt.fields.item,
70-
AwsConfig: &awsConf,
71-
downloader: tt.fields.downloader,
87+
Bucket: tt.fields.bucket,
88+
Item: tt.fields.item,
89+
AwsConfig: &awsConf,
90+
downloader: tt.fields.downloader,
91+
S3ClientOptions: tt.fields.S3ClientOptions,
7292
}
7393
err := s.Init(context.Background(), nil)
7494
assert.NoError(t, err)
7595
defer func() {
7696
err := s.Shutdown(context.Background())
7797
assert.NoError(t, err)
7898
}()
99+
100+
// Verify that S3ClientOptions are correctly set on the Retriever
101+
if tt.fields.S3ClientOptions != nil {
102+
assert.Equal(t, tt.fields.S3ClientOptions, s.S3ClientOptions, "S3ClientOptions should be set correctly on the Retriever")
103+
}
104+
79105
got, err := s.Retrieve(tt.fields.context)
80106
assert.Equal(t, tt.wantErr, err != nil, "Retrieve() error = %v, wantErr %v", err, tt.wantErr)
81107
if err == nil {
@@ -118,4 +144,25 @@ func TestRetriever_Init(t *testing.T) {
118144
assert.Equal(t, "us-east-1", s.AwsConfig.Region, "Setting the region from the AwsConfig should be used over the environment variable")
119145
assert.Equal(t, retriever.RetrieverReady, s.Status())
120146
})
147+
148+
t.Run("With S3 Client Options", func(t *testing.T) {
149+
t.Setenv("AWS_REGION", "us-west-2")
150+
s := Retriever{
151+
Bucket: "TestBucket",
152+
Item: "TestItem",
153+
S3ClientOptions: []func(*s3.Options){
154+
func(o *s3.Options) {
155+
o.UseAccelerate = true
156+
},
157+
},
158+
}
159+
err := s.Init(context.Background(), nil)
160+
assert.NoError(t, err)
161+
assert.NotNil(t, s.AwsConfig)
162+
assert.NotNil(t, s.downloader)
163+
assert.Equal(t, "us-west-2", s.AwsConfig.Region, "Setting the region from the environment variable should be copied to the aws config")
164+
assert.Equal(t, retriever.RetrieverReady, s.Status())
165+
assert.NotNil(t, s.S3ClientOptions, "S3ClientOptions should be set")
166+
assert.Len(t, s.S3ClientOptions, 1, "S3ClientOptions should have one option")
167+
})
121168
}

0 commit comments

Comments
 (0)