Skip to content

Commit 081dfbd

Browse files
Add S3 prefix support (#498)
* Start working on S3 prefix * Make amends with linter. * Simplify buildKey and add more info on prefix setting in example template * Make buildKey an S3 method.
1 parent e16e3db commit 081dfbd

File tree

3 files changed

+38
-10
lines changed

3 files changed

+38
-10
lines changed

config.toml.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ path = "test"
2626
endpoint_url = "https://s3.us-west-2.amazonaws.com"
2727
region = "us-west-2"
2828
bucket = "example-bucket-name"
29+
# If you use prefix, you may need to add a path to `server.hostname` setting
30+
# e.g. https://example-bucket-name.s3.us-west-2.amazonaws.com/example/prefix/
31+
prefix = "example/prefix"
2932

3033
# API keys to be used to access Youtube and Vimeo.
3134
# These can be either specified as string parameter or array of string (so those will be rotated).

pkg/fs/s3.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"io"
66
"net/http"
77
"os"
8+
"path"
89

910
"github.com/aws/aws-sdk-go/aws"
1011
"github.com/aws/aws-sdk-go/aws/awserr"
@@ -24,13 +25,16 @@ type S3Config struct {
2425
Region string `toml:"region"`
2526
// EndpointURL is an HTTP endpoint of the S3 API
2627
EndpointURL string `toml:"endpoint_url"`
28+
// Prefix is a prefix (subfolder) to use to build key names
29+
Prefix string `toml:"prefix"`
2730
}
2831

2932
// S3 implements file storage for S3-compatible providers.
3033
type S3 struct {
3134
api s3iface.S3API
3235
uploader *s3manager.Uploader
3336
bucket string
37+
prefix string
3438
}
3539

3640
func NewS3(c S3Config) (*S3, error) {
@@ -47,6 +51,7 @@ func NewS3(c S3Config) (*S3, error) {
4751
api: s3.New(sess),
4852
uploader: s3manager.NewUploader(sess),
4953
bucket: c.Bucket,
54+
prefix: c.Prefix,
5055
}, nil
5156
}
5257

@@ -55,21 +60,23 @@ func (s *S3) Open(_name string) (http.File, error) {
5560
}
5661

5762
func (s *S3) Delete(ctx context.Context, name string) error {
63+
key := s.buildKey(name)
5864
_, err := s.api.DeleteObjectWithContext(ctx, &s3.DeleteObjectInput{
5965
Bucket: &s.bucket,
60-
Key: &name,
66+
Key: &key,
6167
})
6268
return err
6369
}
6470

6571
func (s *S3) Create(ctx context.Context, name string, reader io.Reader) (int64, error) {
66-
logger := log.WithField("name", name)
72+
key := s.buildKey(name)
73+
logger := log.WithField("key", key)
6774

6875
logger.Infof("uploading file to %s", s.bucket)
6976
r := &readerWithN{Reader: reader}
7077
_, err := s.uploader.UploadWithContext(ctx, &s3manager.UploadInput{
7178
Bucket: &s.bucket,
72-
Key: &name,
79+
Key: &key,
7380
Body: r,
7481
})
7582
if err != nil {
@@ -81,12 +88,13 @@ func (s *S3) Create(ctx context.Context, name string, reader io.Reader) (int64,
8188
}
8289

8390
func (s *S3) Size(ctx context.Context, name string) (int64, error) {
84-
logger := log.WithField("name", name)
91+
key := s.buildKey(name)
92+
logger := log.WithField("key", key)
8593

8694
logger.Debugf("getting file size from %s", s.bucket)
8795
resp, err := s.api.HeadObjectWithContext(ctx, &s3.HeadObjectInput{
8896
Bucket: &s.bucket,
89-
Key: &name,
97+
Key: &key,
9098
})
9199
if err != nil {
92100
if awsErr, ok := err.(awserr.Error); ok {
@@ -100,6 +108,10 @@ func (s *S3) Size(ctx context.Context, name string) (int64, error) {
100108
return *resp.ContentLength, nil
101109
}
102110

111+
func (s *S3) buildKey(name string) string {
112+
return path.Join(s.prefix, name)
113+
}
114+
103115
type readerWithN struct {
104116
io.Reader
105117
n int

pkg/fs/s3_test.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818

1919
func TestS3_Create(t *testing.T) {
2020
files := make(map[string][]byte)
21-
stor, err := newMockS3(files)
21+
stor, err := newMockS3(files, "")
2222
assert.NoError(t, err)
2323

2424
written, err := stor.Create(testCtx, "1/test", bytes.NewBuffer([]byte{1, 5, 7, 8, 3}))
@@ -32,7 +32,7 @@ func TestS3_Create(t *testing.T) {
3232

3333
func TestS3_Size(t *testing.T) {
3434
files := make(map[string][]byte)
35-
stor, err := newMockS3(files)
35+
stor, err := newMockS3(files, "")
3636
assert.NoError(t, err)
3737

3838
_, err = stor.Create(testCtx, "1/test", bytes.NewBuffer([]byte{1, 5, 7, 8, 3}))
@@ -45,7 +45,7 @@ func TestS3_Size(t *testing.T) {
4545

4646
func TestS3_NoSize(t *testing.T) {
4747
files := make(map[string][]byte)
48-
stor, err := newMockS3(files)
48+
stor, err := newMockS3(files, "")
4949
assert.NoError(t, err)
5050

5151
_, err = stor.Size(testCtx, "1/test")
@@ -54,7 +54,7 @@ func TestS3_NoSize(t *testing.T) {
5454

5555
func TestS3_Delete(t *testing.T) {
5656
files := make(map[string][]byte)
57-
stor, err := newMockS3(files)
57+
stor, err := newMockS3(files, "")
5858
assert.NoError(t, err)
5959

6060
_, err = stor.Create(testCtx, "1/test", bytes.NewBuffer([]byte{1, 5, 7, 8, 3}))
@@ -70,17 +70,30 @@ func TestS3_Delete(t *testing.T) {
7070
assert.False(t, ok)
7171
}
7272

73+
func TestS3_BuildKey(t *testing.T) {
74+
files := make(map[string][]byte)
75+
76+
stor, _ := newMockS3(files, "")
77+
key := stor.buildKey("test-fn")
78+
assert.EqualValues(t, "test-fn", key)
79+
80+
stor, _ = newMockS3(files, "mock-prefix")
81+
key = stor.buildKey("test-fn")
82+
assert.EqualValues(t, "mock-prefix/test-fn", key)
83+
}
84+
7385
type mockS3API struct {
7486
s3iface.S3API
7587
files map[string][]byte
7688
}
7789

78-
func newMockS3(files map[string][]byte) (*S3, error) {
90+
func newMockS3(files map[string][]byte, prefix string) (*S3, error) {
7991
api := &mockS3API{files: files}
8092
return &S3{
8193
api: api,
8294
uploader: s3manager.NewUploaderWithClient(api),
8395
bucket: "mock-bucket",
96+
prefix: prefix,
8497
}, nil
8598
}
8699

0 commit comments

Comments
 (0)