diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 89ba4e7..4874723 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -2,16 +2,16 @@ name: Go on: push: - branches: [ main ] + branches: [ main, v1, v2 ] pull_request: - branches: [ main ] + branches: [ main, v1, v2 ] jobs: localstack: runs-on: ubuntu-latest services: minio: - image: localstack/localstack + image: localstack/localstack:0.14.0 ports: - "4566:4566" - "4571:4571" @@ -24,13 +24,13 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.19 + go-version: 1.21 - name: Wait for localstack run: 'for i in {1..20}; do sleep 3 && curl --silent --fail http://localhost:4566/health | grep "\"s3\": \"available\"" > /dev/null && break; done' - name: Test - run: go test -v -endpoint='localhost:4566' -cover + run: go test -v -endpoint='http://localhost:4566' -cover minio: runs-on: ubuntu-latest @@ -41,11 +41,11 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.19 + go-version: 1.21 - name: Test env: - SERVER_ENDPOINT: localhost:9000 + SERVER_ENDPOINT: http://localhost:9000 ACCESS_KEY: minioadmin SECRET_KEY: minioadmin MINIO_ACCESS_KEY: minioadmin diff --git a/dir.go b/dir.go index 5229d57..4182774 100644 --- a/dir.go +++ b/dir.go @@ -1,6 +1,7 @@ package s3fs import ( + "context" "errors" "io" "io/fs" @@ -9,16 +10,14 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/s3" - "github.com/aws/aws-sdk-go/service/s3/s3iface" + "github.com/aws/aws-sdk-go-v2/service/s3" ) var _ fs.ReadDirFile = (*dir)(nil) type dir struct { fileInfo - s3cl s3iface.S3API + s3cl Client bucket string marker *string done bool @@ -105,12 +104,14 @@ func (d *dir) readNext() error { name += "/" } - out, err := d.s3cl.ListObjects(&s3.ListObjectsInput{ - Bucket: &d.bucket, - Delimiter: aws.String("/"), - Prefix: &name, - Marker: d.marker, - }) + out, err := d.s3cl.ListObjects( + context.Background(), + &s3.ListObjectsInput{ + Bucket: &d.bucket, + Delimiter: ptr("/"), + Prefix: &name, + Marker: d.marker, + }) if err != nil { return err } @@ -131,7 +132,7 @@ func (d *dir) readNext() error { } for _, p := range out.CommonPrefixes { - if p == nil || p.Prefix == nil { + if p.Prefix == nil { continue } @@ -148,7 +149,7 @@ func (d *dir) readNext() error { } for _, o := range out.Contents { - if o == nil || o.Key == nil { + if o.Key == nil { continue } diff --git a/file.go b/file.go index 7d7fed2..42b3594 100644 --- a/file.go +++ b/file.go @@ -1,6 +1,7 @@ package s3fs import ( + "context" "errors" "fmt" "io" @@ -9,11 +10,8 @@ import ( "path" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - - "github.com/aws/aws-sdk-go/service/s3" - "github.com/aws/aws-sdk-go/service/s3/s3iface" + awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http" + "github.com/aws/aws-sdk-go-v2/service/s3" ) var ( @@ -23,7 +21,7 @@ var ( ) type file struct { - cl s3iface.S3API + cl Client bucket string name string @@ -33,8 +31,8 @@ type file struct { eTag string } -func openFile(cl s3iface.S3API, bucket string, name string) (fs.File, error) { - out, err := cl.GetObject(&s3.GetObjectInput{ +func openFile(cl Client, bucket string, name string) (fs.File, error) { + out, err := cl.GetObject(context.Background(), &s3.GetObjectInput{ Key: &name, Bucket: &bucket, }) @@ -56,7 +54,7 @@ func openFile(cl s3iface.S3API, bucket string, name string) (fs.File, error) { }, nil } -func getStatFunc(cl s3iface.S3API, bucket string, name string, s3ObjOutput s3.GetObjectOutput) func() (fs.FileInfo, error) { +func getStatFunc(cl Client, bucket string, name string, s3ObjOutput s3.GetObjectOutput) func() (fs.FileInfo, error) { statFunc := func() (fs.FileInfo, error) { return stat(cl, bucket, name) } @@ -127,17 +125,19 @@ func (f *file) Seek(offset int64, whence int) (int64, error) { } rawObject, err := f.cl.GetObject( + context.Background(), &s3.GetObjectInput{ - Bucket: aws.String(f.bucket), - Key: aws.String(f.name), - Range: aws.String(fmt.Sprintf("bytes=%d-", newOffset)), - IfMatch: aws.String(f.eTag), + Bucket: &f.bucket, + Key: &f.name, + Range: ptr(fmt.Sprintf("bytes=%d-", newOffset)), + IfMatch: &f.eTag, }) if err != nil { - var requestFailureError awserr.RequestFailure - if errors.As(err, &requestFailureError) && requestFailureError.StatusCode() == http.StatusPreconditionFailed { - return 0, fmt.Errorf("s3fs.file.Seek: file has changed while seeking: %w", fs.ErrNotExist) + if e := new(awshttp.ResponseError); errors.As(err, &e) { + if e.HTTPStatusCode() == http.StatusPreconditionFailed { + return 0, fmt.Errorf("s3fs.file.Seek: file has changed while seeking: %w", fs.ErrNotExist) + } } return 0, err } @@ -167,3 +167,7 @@ func (fi fileInfo) Sys() interface{} { return nil } type eofReader struct{} func (eofReader) Read([]byte) (int, error) { return 0, io.EOF } + +func ptr[T any](v T) *T { + return &v +} diff --git a/fs.go b/fs.go index 46894f1..46feb6e 100644 --- a/fs.go +++ b/fs.go @@ -2,13 +2,13 @@ package s3fs import ( + "context" "errors" "io/fs" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/s3" - "github.com/aws/aws-sdk-go/service/s3/s3iface" + "github.com/aws/aws-sdk-go-v2/aws/transport/http" + "github.com/aws/aws-sdk-go-v2/service/s3" + "github.com/aws/aws-sdk-go-v2/service/s3/types" ) var ( @@ -30,19 +30,28 @@ type Option func(*S3FS) // has to be handled by the caller. func WithReadSeeker(fsys *S3FS) { fsys.readSeeker = true } +// Client wraps the s3 client methods that this package is using. +// This interface may change in the future and should not be relied on by +// packages using it. +type Client interface { + HeadObject(ctx context.Context, params *s3.HeadObjectInput, optFns ...func(*s3.Options)) (*s3.HeadObjectOutput, error) + ListObjects(ctx context.Context, params *s3.ListObjectsInput, optFns ...func(*s3.Options)) (*s3.ListObjectsOutput, error) + GetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) +} + // S3FS is a S3 filesystem implementation. // // S3 has a flat structure instead of a hierarchy. S3FS simulates directories // by using prefixes and delims ("/"). Because directories are simulated, ModTime // is always a default Time value (IsZero returns true). type S3FS struct { - cl s3iface.S3API + cl Client bucket string readSeeker bool } // New returns a new filesystem that works on the specified bucket. -func New(cl s3iface.S3API, bucket string, opts ...Option) *S3FS { +func New(cl Client, bucket string, opts ...Option) *S3FS { fsys := &S3FS{ cl: cl, bucket: bucket, @@ -127,7 +136,7 @@ func (f *S3FS) ReadDir(name string) ([]fs.DirEntry, error) { return d.ReadDir(-1) } -func stat(s3cl s3iface.S3API, bucket, name string) (fs.FileInfo, error) { +func stat(s3cl Client, bucket, name string) (fs.FileInfo, error) { if !fs.ValidPath(name) { return nil, fs.ErrInvalid } @@ -143,10 +152,12 @@ func stat(s3cl s3iface.S3API, bucket, name string) (fs.FileInfo, error) { }, nil } - head, err := s3cl.HeadObject(&s3.HeadObjectInput{ - Bucket: &bucket, - Key: aws.String(name), - }) + head, err := s3cl.HeadObject( + context.Background(), + &s3.HeadObjectInput{ + Bucket: &bucket, + Key: &name, + }) if err != nil { if !isNotFoundErr(err) { return nil, err @@ -160,12 +171,14 @@ func stat(s3cl s3iface.S3API, bucket, name string) (fs.FileInfo, error) { }, nil } - out, err := s3cl.ListObjectsV2(&s3.ListObjectsV2Input{ - Bucket: &bucket, - Delimiter: aws.String("/"), - Prefix: aws.String(name + "/"), - MaxKeys: aws.Int64(1), - }) + out, err := s3cl.ListObjects( + context.Background(), + &s3.ListObjectsInput{ + Bucket: &bucket, + Delimiter: ptr("/"), + Prefix: ptr(name + "/"), + MaxKeys: ptr[int32](1), + }) if err != nil { return nil, err } @@ -182,7 +195,7 @@ func stat(s3cl s3iface.S3API, bucket, name string) (fs.FileInfo, error) { return nil, fs.ErrNotExist } -func openDir(s3cl s3iface.S3API, bucket, name string) (fs.ReadDirFile, error) { +func openDir(s3cl Client, bucket, name string) (fs.ReadDirFile, error) { fi, err := stat(s3cl, bucket, name) if err != nil { return nil, err @@ -194,16 +207,18 @@ func openDir(s3cl s3iface.S3API, bucket, name string) (fs.ReadDirFile, error) { return nil, errNotDir } -var notFoundCodes = map[string]struct{}{ - s3.ErrCodeNoSuchKey: {}, - "NotFound": {}, // localstack -} - func isNotFoundErr(err error) bool { - if aerr, ok := err.(awserr.Error); ok { - _, ok := notFoundCodes[aerr.Code()] - return ok + if e := new(types.NoSuchKey); errors.As(err, &e) { + return true } + + if e := new(http.ResponseError); errors.As(err, &e) { + // localstack workaround + if e.HTTPStatusCode() == 404 { + return true + } + } + return false } diff --git a/fs_test.go b/fs_test.go index 53ea627..1b60ca8 100644 --- a/fs_test.go +++ b/fs_test.go @@ -2,6 +2,7 @@ package s3fs_test import ( "bytes" + "context" "crypto/tls" "errors" "flag" @@ -16,19 +17,15 @@ import ( "testing/fstest" "time" - "github.com/jszwec/s3fs" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/s3" - "github.com/aws/aws-sdk-go/service/s3/s3iface" - "github.com/aws/aws-sdk-go/service/s3/s3manager" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/feature/s3/manager" + "github.com/aws/aws-sdk-go-v2/service/s3" + "github.com/aws/aws-sdk-go-v2/service/s3/types" + "github.com/jszwec/s3fs/v2" ) var ( - endpoint = flag.String("endpoint", "localhost:4566", "s3 endpoint") + endpoint = flag.String("endpoint", "http://localhost:4566", "s3 endpoint") bucket = flag.String("bucket", "test-github.com-jszwec-s3fs", "bucket name") skipVerify = flag.Bool("skip-verify", true, "http insecure skip verify") ) @@ -45,7 +42,7 @@ func TestMain(m *testing.M) { } func TestSeeker(t *testing.T) { - s3cl := newClient(t) + s3cl, cl := newClient(t) const testFile = "file.txt" content := []byte("content") @@ -64,7 +61,7 @@ func TestSeeker(t *testing.T) { }) t.Run("'s3fs.New' does not implement Seeker", func(t *testing.T) { - testFs := s3fs.New(s3cl, *bucket) + testFs := s3fs.New(cl, *bucket) data, err := testFs.Open(testFile) if err != nil { @@ -538,7 +535,7 @@ func TestSeeker(t *testing.T) { } func TestFS(t *testing.T) { - s3cl := newClient(t) + s3cl, wrappedCl := newClient(t) const testFile = "file.txt" @@ -564,7 +561,7 @@ func TestFS(t *testing.T) { cleanBucket(t, s3cl, *bucket) t.Run("list empty bucket", func(t *testing.T) { - fi, err := s3fs.New(s3cl, *bucket).Open(".") + fi, err := s3fs.New(wrappedCl, *bucket).Open(".") if err != nil { t.Errorf("want err to be nil; got %v", err) } @@ -1053,10 +1050,10 @@ func TestFS(t *testing.T) { desc string s3fs *s3fs.S3FS }{ - {desc: "standard", s3fs: s3fs.New(s3cl, *bucket)}, - {desc: "max keys = 1", s3fs: s3fs.New(&client{MaxKeys: aws.Int64(1), S3API: s3cl}, *bucket)}, - {desc: "max keys = 2", s3fs: s3fs.New(&client{MaxKeys: aws.Int64(2), S3API: s3cl}, *bucket)}, - {desc: "max keys = 3", s3fs: s3fs.New(&client{MaxKeys: aws.Int64(3), S3API: s3cl}, *bucket)}, + {desc: "standard", s3fs: s3fs.New(wrappedCl, *bucket)}, + {desc: "max keys = 1", s3fs: s3fs.New(&client{MaxKeys: ptr[int32](1), Client: wrappedCl}, *bucket)}, + {desc: "max keys = 2", s3fs: s3fs.New(&client{MaxKeys: ptr[int32](2), Client: wrappedCl}, *bucket)}, + {desc: "max keys = 3", s3fs: s3fs.New(&client{MaxKeys: ptr[int32](3), Client: wrappedCl}, *bucket)}, } for _, f := range fixtures { @@ -1262,40 +1259,44 @@ func TestDirRead(t *testing.T) { } type mockClient struct { - s3iface.S3API + s3fs.Client outs []s3.ListObjectsOutput i int } -func (c *mockClient) ListObjects(in *s3.ListObjectsInput) (*s3.ListObjectsOutput, error) { +func (c *mockClient) ListObjects(ctx context.Context, in *s3.ListObjectsInput, _ ...func(*s3.Options)) (*s3.ListObjectsOutput, error) { defer func() { c.i++ }() if c.i < len(c.outs) { return &c.outs[c.i], nil } return &s3.ListObjectsOutput{ - IsTruncated: aws.Bool(false), + IsTruncated: ptr(false), }, nil } func newListOutput(dirs, files []string) (out s3.ListObjectsOutput) { for _, d := range dirs { - out.CommonPrefixes = append(out.CommonPrefixes, &s3.CommonPrefix{ - Prefix: aws.String(d), + out.CommonPrefixes = append(out.CommonPrefixes, types.CommonPrefix{ + Prefix: ptr(d), }) } for _, f := range files { - out.Contents = append(out.Contents, &s3.Object{ - Key: aws.String(f), - Size: aws.Int64(0), - LastModified: aws.Time(time.Time{}), + out.Contents = append(out.Contents, types.Object{ + Key: ptr(f), + Size: ptr[int64](0), + LastModified: ptr(time.Time{}), }) } return out } -func newClient(t *testing.T) s3iface.S3API { +type Client interface { + s3fs.Client +} + +func newClient(t *testing.T) (*s3.Client, Client) { t.Helper() cl := &http.Client{ @@ -1306,26 +1307,27 @@ func newClient(t *testing.T) s3iface.S3API { }, } - s, err := session.NewSession( - aws.NewConfig(). - WithEndpoint(*endpoint). - WithRegion(region). - WithS3ForcePathStyle(true). - WithHTTPClient(cl). - WithCredentials(credentials.NewStaticCredentials(accessKeyID, secretKey, "")), - ) - if err != nil { - t.Fatal(err) - } + client := s3.New(s3.Options{ + BaseEndpoint: endpoint, + Credentials: aws.CredentialsProviderFunc(func(ctx context.Context) (aws.Credentials, error) { + return aws.Credentials{ + AccessKeyID: accessKeyID, + SecretAccessKey: secretKey, + }, nil + }), + Region: region, + UsePathStyle: true, + HTTPClient: cl, + }) - return &modTimeTruncateClient{&metricClient{s3.New(s)}} + return client, &modTimeTruncateClient{&metricClient{client}} } -func writeFile(t *testing.T, cl s3iface.S3API, bucket, name string, data []byte) { +func writeFile(t *testing.T, cl *s3.Client, bucket, name string, data []byte) { t.Helper() - uploader := s3manager.NewUploaderWithClient(cl) - _, err := uploader.Upload(&s3manager.UploadInput{ + uploader := manager.NewUploader(cl) + _, err := uploader.Upload(context.Background(), &s3.PutObjectInput{ Body: strings.NewReader(string(data)), Bucket: &bucket, Key: &name, @@ -1335,47 +1337,54 @@ func writeFile(t *testing.T, cl s3iface.S3API, bucket, name string, data []byte) } } -func deleteFile(t *testing.T, cl s3iface.S3API, bucket, name string) { +func deleteFile(t *testing.T, cl *s3.Client, bucket, name string) { t.Helper() - _, err := cl.DeleteObject(&s3.DeleteObjectInput{ - Bucket: aws.String(bucket), - Key: &name, - }) + _, err := cl.DeleteObject( + context.Background(), + &s3.DeleteObjectInput{ + Bucket: ptr(bucket), + Key: &name, + }) if err != nil { t.Fatal(err) } } -func createBucket(t *testing.T, cl s3iface.S3API, bucket string) { +func createBucket(t *testing.T, cl *s3.Client, bucket string) { t.Helper() - _, err := cl.CreateBucket(&s3.CreateBucketInput{ + _, err := cl.CreateBucket(context.Background(), &s3.CreateBucketInput{ Bucket: &bucket, }) if err != nil { - if awserr, ok := err.(awserr.Error); ok && awserr.Code() == s3.ErrCodeBucketAlreadyOwnedByYou { + var e *types.BucketAlreadyOwnedByYou + if errors.As(err, &e) { return } t.Fatal(err) } } -func cleanBucket(t *testing.T, cl s3iface.S3API, bucket string) { +func cleanBucket(t *testing.T, cl *s3.Client, bucket string) { t.Helper() - out, err := cl.ListObjects(&s3.ListObjectsInput{ - Bucket: aws.String(bucket), - }) + out, err := cl.ListObjects( + context.Background(), + &s3.ListObjectsInput{ + Bucket: ptr(bucket), + }) if err != nil { t.Fatal("failed to delete bucket:", err) } for _, o := range out.Contents { - _, err := cl.DeleteObject(&s3.DeleteObjectInput{ - Bucket: aws.String(bucket), - Key: o.Key, - }) + _, err := cl.DeleteObject( + context.Background(), + &s3.DeleteObjectInput{ + Bucket: ptr(bucket), + Key: o.Key, + }) if err != nil { t.Error("failed to delete file:", err) } @@ -1390,33 +1399,33 @@ func envDefault(env, def string) string { } type client struct { - MaxKeys *int64 - s3iface.S3API + MaxKeys *int32 + s3fs.Client } -func (c *client) ListObjects(in *s3.ListObjectsInput) (*s3.ListObjectsOutput, error) { +func (c *client) ListObjects(ctx context.Context, in *s3.ListObjectsInput, _ ...func(*s3.Options)) (*s3.ListObjectsOutput, error) { if c.MaxKeys != nil { in.MaxKeys = c.MaxKeys } - return c.S3API.ListObjects(in) + return c.Client.ListObjects(ctx, in) } type modTimeTruncateClient struct { - s3iface.S3API + Client } // Minio returns modTime that includes microseconds if data comes from ListObjects // while data coming from GetObject's modTimes are accurate down to seconds. // To make this test pass while using Minio we build this client that truncates // modTimes to Second. -func (c *modTimeTruncateClient) ListObjects(in *s3.ListObjectsInput) (*s3.ListObjectsOutput, error) { - out, err := c.S3API.ListObjects(in) +func (c *modTimeTruncateClient) ListObjects(ctx context.Context, in *s3.ListObjectsInput, _ ...func(*s3.Options)) (*s3.ListObjectsOutput, error) { + out, err := c.Client.ListObjects(context.Background(), in) if err != nil { return out, err } - for _, o := range out.Contents { - o.LastModified = aws.Time(o.LastModified.Truncate(time.Second)) + for i, o := range out.Contents { + out.Contents[i].LastModified = ptr(o.LastModified.Truncate(time.Second)) } return out, err } @@ -1428,15 +1437,19 @@ var ( ) type metricClient struct { - s3iface.S3API + Client } -func (c *metricClient) ListObjects(in *s3.ListObjectsInput) (*s3.ListObjectsOutput, error) { +func (c *metricClient) ListObjects(ctx context.Context, in *s3.ListObjectsInput, _ ...func(*s3.Options)) (*s3.ListObjectsOutput, error) { atomic.AddInt64(&listC, 1) - return c.S3API.ListObjects(in) + return c.Client.ListObjects(ctx, in) } -func (c *metricClient) GetObject(in *s3.GetObjectInput) (*s3.GetObjectOutput, error) { +func (c *metricClient) GetObject(ctx context.Context, in *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) { atomic.AddInt64(&getC, 1) - return c.S3API.GetObject(in) + return c.Client.GetObject(context.Background(), in) +} + +func ptr[T any](v T) *T { + return &v } diff --git a/go.mod b/go.mod index ce65ade..9cee9dc 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,22 @@ -module github.com/jszwec/s3fs +module github.com/jszwec/s3fs/v2 -go 1.16 +go 1.21 -require github.com/aws/aws-sdk-go v1.36.24 +require ( + github.com/aws/aws-sdk-go-v2 v1.24.0 + github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7 + github.com/aws/aws-sdk-go-v2/service/s3 v1.47.5 +) + +require ( + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9 // indirect + github.com/aws/smithy-go v1.19.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect +) diff --git a/go.sum b/go.sum index 6d6cd42..1c7f657 100644 --- a/go.sum +++ b/go.sum @@ -1,27 +1,52 @@ -github.com/aws/aws-sdk-go v1.36.24 h1:uVuio0zA5ideP3DGZDpIoExQJd0WcoNUVlNZaKwBnf8= -github.com/aws/aws-sdk-go v1.36.24/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go-v2 v1.24.0 h1:890+mqQ+hTpNuw0gGP6/4akolQkSToDJgHfQE7AwGuk= +github.com/aws/aws-sdk-go-v2 v1.24.0/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 h1:OCs21ST2LrepDfD3lwlQiOqIGp6JiEUqG84GzTDoyJs= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4/go.mod h1:usURWEKSNNAcAZuzRn/9ZYPT8aZQkR7xcCtunK/LkJo= +github.com/aws/aws-sdk-go-v2/config v1.26.1 h1:z6DqMxclFGL3Zfo+4Q0rLnAZ6yVkzCRxhRMsiRQnD1o= +github.com/aws/aws-sdk-go-v2/config v1.26.1/go.mod h1:ZB+CuKHRbb5v5F0oJtGdhFTelmrxd4iWO1lf0rQwSAg= +github.com/aws/aws-sdk-go-v2/credentials v1.16.12 h1:v/WgB8NxprNvr5inKIiVVrXPuuTegM+K8nncFkr1usU= +github.com/aws/aws-sdk-go-v2/credentials v1.16.12/go.mod h1:X21k0FjEJe+/pauud82HYiQbEr9jRKY3kXEIQ4hXeTQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 h1:w98BT5w+ao1/r5sUuiH6JkVzjowOKeOJRHERyy1vh58= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10/go.mod h1:K2WGI7vUvkIv1HoNbfBA1bvIZ+9kL3YVmWxeKuLQsiw= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7 h1:FnLf60PtjXp8ZOzQfhJVsqF0OtYKQZWQfqOLshh8YXg= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7/go.mod h1:tDVvl8hyU6E9B8TrnNrZQEVkQlB8hjJwcgpPhgtlnNg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9 h1:v+HbZaCGmOwnTTVS86Fleq0vPzOd7tnJGbFhP0stNLs= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9/go.mod h1:Xjqy+Nyj7VDLBtCMkQYOw1QYfAEZCVLrfI0ezve8wd4= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9 h1:N94sVhRACtXyVcjXxrwK1SKFIJrA9pOJ5yu2eSHnmls= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9/go.mod h1:hqamLz7g1/4EJP+GH5NBhcUMLjW+gKLQabgyz6/7WAU= +github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 h1:ugD6qzjYtB7zM5PN/ZIeaAIyefPaD82G8+SJopgvUpw= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9/go.mod h1:YD0aYBWCrPENpHolhKw2XDlTIWae2GKXT1T4o6N6hiM= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9 h1:/90OR2XbSYfXucBMJ4U14wrjlfleq/0SB6dZDPncgmo= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9/go.mod h1:dN/Of9/fNZet7UrQQ6kTDo/VSwKPIq94vjlU16bRARc= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9 h1:Nf2sHxjMJR8CSImIVCONRi4g0Su3J+TSTbS7G0pUeMU= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9/go.mod h1:idky4TER38YIjr2cADF1/ugFMKvZV7p//pVeV5LZbF0= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9 h1:iEAeF6YC3l4FzlJPP9H3Ko1TXpdjdqWffxXjp8SY6uk= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9/go.mod h1:kjsXoK23q9Z/tLBrckZLLyvjhZoS+AGrzqzUfEClvMM= +github.com/aws/aws-sdk-go-v2/service/s3 v1.47.5 h1:Keso8lIOS+IzI2MkPZyK6G0LYcK3My2LQ+T5bxghEAY= +github.com/aws/aws-sdk-go-v2/service/s3 v1.47.5/go.mod h1:vADO6Jn+Rq4nDtfwNjhgR84qkZwiC6FqCaXdw/kYwjA= +github.com/aws/aws-sdk-go-v2/service/sso v1.18.5 h1:ldSFWz9tEHAwHNmjx2Cvy1MjP5/L9kNoR0skc6wyOOM= +github.com/aws/aws-sdk-go-v2/service/sso v1.18.5/go.mod h1:CaFfXLYL376jgbP7VKC96uFcU8Rlavak0UlAwk1Dlhc= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 h1:2k9KmFawS63euAkY4/ixVNsYYwrwnd5fIvgEKkfZFNM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5/go.mod h1:W+nd4wWDVkSUIox9bacmkBP5NMFQeTJ/xqNabpzSR38= +github.com/aws/aws-sdk-go-v2/service/sts v1.26.5 h1:5UYvv8JUvllZsRnfrcMQ+hJ9jNICmcgKPAO1CER25Wg= +github.com/aws/aws-sdk-go-v2/service/sts v1.26.5/go.mod h1:XX5gh4CB7wAs4KhcF46G6C8a2i7eupU19dcAAE+EydU= +github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= +github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/test/localstack/docker-compose.yml b/test/localstack/docker-compose.yml index 6316dce..06d134c 100644 --- a/test/localstack/docker-compose.yml +++ b/test/localstack/docker-compose.yml @@ -3,7 +3,7 @@ version: '3.8' services: localstack: container_name: "localstack" - image: localstack/localstack + image: localstack/localstack:0.14.0 network_mode: bridge ports: - "4566:4566"