Merge pull request #3624 from milosgajdos/aws-s3-listv2
Update s3 ListObjects to V2 API
This commit is contained in:
commit
27b5563245
2 changed files with 94 additions and 11 deletions
|
@ -703,7 +703,7 @@ func (d *driver) Writer(ctx context.Context, path string, appendParam bool) (sto
|
||||||
// Stat retrieves the FileInfo for the given path, including the current size
|
// Stat retrieves the FileInfo for the given path, including the current size
|
||||||
// in bytes and the creation time.
|
// in bytes and the creation time.
|
||||||
func (d *driver) Stat(ctx context.Context, path string) (storagedriver.FileInfo, error) {
|
func (d *driver) Stat(ctx context.Context, path string) (storagedriver.FileInfo, error) {
|
||||||
resp, err := d.S3.ListObjects(&s3.ListObjectsInput{
|
resp, err := d.S3.ListObjectsV2(&s3.ListObjectsV2Input{
|
||||||
Bucket: aws.String(d.Bucket),
|
Bucket: aws.String(d.Bucket),
|
||||||
Prefix: aws.String(d.s3Path(path)),
|
Prefix: aws.String(d.s3Path(path)),
|
||||||
MaxKeys: aws.Int64(1),
|
MaxKeys: aws.Int64(1),
|
||||||
|
@ -748,7 +748,7 @@ func (d *driver) List(ctx context.Context, opath string) ([]string, error) {
|
||||||
prefix = "/"
|
prefix = "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := d.S3.ListObjects(&s3.ListObjectsInput{
|
resp, err := d.S3.ListObjectsV2(&s3.ListObjectsV2Input{
|
||||||
Bucket: aws.String(d.Bucket),
|
Bucket: aws.String(d.Bucket),
|
||||||
Prefix: aws.String(d.s3Path(path)),
|
Prefix: aws.String(d.s3Path(path)),
|
||||||
Delimiter: aws.String("/"),
|
Delimiter: aws.String("/"),
|
||||||
|
@ -772,12 +772,12 @@ func (d *driver) List(ctx context.Context, opath string) ([]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if *resp.IsTruncated {
|
if *resp.IsTruncated {
|
||||||
resp, err = d.S3.ListObjects(&s3.ListObjectsInput{
|
resp, err = d.S3.ListObjectsV2(&s3.ListObjectsV2Input{
|
||||||
Bucket: aws.String(d.Bucket),
|
Bucket: aws.String(d.Bucket),
|
||||||
Prefix: aws.String(d.s3Path(path)),
|
Prefix: aws.String(d.s3Path(path)),
|
||||||
Delimiter: aws.String("/"),
|
Delimiter: aws.String("/"),
|
||||||
MaxKeys: aws.Int64(listMax),
|
MaxKeys: aws.Int64(listMax),
|
||||||
Marker: resp.NextMarker,
|
ContinuationToken: resp.NextContinuationToken,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -926,14 +926,14 @@ func (d *driver) Delete(ctx context.Context, path string) error {
|
||||||
|
|
||||||
// list objects under the given path as a subpath (suffix with slash "/")
|
// list objects under the given path as a subpath (suffix with slash "/")
|
||||||
s3Path := d.s3Path(path) + "/"
|
s3Path := d.s3Path(path) + "/"
|
||||||
listObjectsInput := &s3.ListObjectsInput{
|
listObjectsInput := &s3.ListObjectsV2Input{
|
||||||
Bucket: aws.String(d.Bucket),
|
Bucket: aws.String(d.Bucket),
|
||||||
Prefix: aws.String(s3Path),
|
Prefix: aws.String(s3Path),
|
||||||
}
|
}
|
||||||
ListLoop:
|
ListLoop:
|
||||||
for {
|
for {
|
||||||
// list all the objects
|
// list all the objects
|
||||||
resp, err := d.S3.ListObjects(listObjectsInput)
|
resp, err := d.S3.ListObjectsV2(listObjectsInput)
|
||||||
|
|
||||||
// resp.Contents can only be empty on the first call
|
// resp.Contents can only be empty on the first call
|
||||||
// if there were no more results to return after the first call, resp.IsTruncated would have been false
|
// if there were no more results to return after the first call, resp.IsTruncated would have been false
|
||||||
|
@ -949,7 +949,7 @@ ListLoop:
|
||||||
}
|
}
|
||||||
|
|
||||||
// resp.Contents must have at least one element or we would have returned not found
|
// resp.Contents must have at least one element or we would have returned not found
|
||||||
listObjectsInput.Marker = resp.Contents[len(resp.Contents)-1].Key
|
listObjectsInput.StartAfter = resp.Contents[len(resp.Contents)-1].Key
|
||||||
|
|
||||||
// from the s3 api docs, IsTruncated "specifies whether (true) or not (false) all of the results were returned"
|
// from the s3 api docs, IsTruncated "specifies whether (true) or not (false) all of the results were returned"
|
||||||
// if everything has been returned, break
|
// if everything has been returned, break
|
||||||
|
|
|
@ -7,7 +7,9 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -779,6 +781,87 @@ func TestMoveWithMultipartCopy(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestListObjectsV2(t *testing.T) {
|
||||||
|
rootDir, err := ioutil.TempDir("", "driver-")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error creating temporary directory: %v", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(rootDir)
|
||||||
|
|
||||||
|
d, err := s3DriverConstructor(rootDir, s3.StorageClassStandard)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error creating driver: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
n := 6
|
||||||
|
prefix := "/test-list-objects-v2"
|
||||||
|
var filePaths []string
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
filePaths = append(filePaths, fmt.Sprintf("%s/%d", prefix, i))
|
||||||
|
}
|
||||||
|
for _, path := range filePaths {
|
||||||
|
if err := d.PutContent(ctx, path, []byte(path)); err != nil {
|
||||||
|
t.Fatalf("unexpected error putting content: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := d.Stat(ctx, filePaths[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error stating: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.IsDir() || info.Size() != int64(len(filePaths[0])) || info.Path() != filePaths[0] {
|
||||||
|
t.Fatal("unexcepted state info")
|
||||||
|
}
|
||||||
|
|
||||||
|
subDirPath := prefix + "/sub/0"
|
||||||
|
if err := d.PutContent(ctx, subDirPath, []byte(subDirPath)); err != nil {
|
||||||
|
t.Fatalf("unexpected error putting content: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
subPaths := append(filePaths, path.Dir(subDirPath))
|
||||||
|
|
||||||
|
result, err := d.List(ctx, prefix)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error listing: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(subPaths)
|
||||||
|
sort.Strings(result)
|
||||||
|
if !reflect.DeepEqual(subPaths, result) {
|
||||||
|
t.Fatalf("unexpected list result")
|
||||||
|
}
|
||||||
|
|
||||||
|
var walkPaths []string
|
||||||
|
if err := d.Walk(ctx, prefix, func(fileInfo storagedriver.FileInfo) error {
|
||||||
|
walkPaths = append(walkPaths, fileInfo.Path())
|
||||||
|
if fileInfo.Path() == path.Dir(subDirPath) {
|
||||||
|
if !fileInfo.IsDir() {
|
||||||
|
t.Fatalf("unexpected walking file info")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if fileInfo.IsDir() || fileInfo.Size() != int64(len(fileInfo.Path())) {
|
||||||
|
t.Fatalf("unexpected walking file info")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("unexpected error walking: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
subPaths = append(subPaths, subDirPath)
|
||||||
|
sort.Strings(walkPaths)
|
||||||
|
sort.Strings(subPaths)
|
||||||
|
if !reflect.DeepEqual(subPaths, walkPaths) {
|
||||||
|
t.Fatalf("unexpected walking paths")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.Delete(ctx, prefix); err != nil {
|
||||||
|
t.Fatalf("unexpected error deleting: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func compareWalked(t *testing.T, expected, walked []string) {
|
func compareWalked(t *testing.T, expected, walked []string) {
|
||||||
if len(walked) != len(expected) {
|
if len(walked) != len(expected) {
|
||||||
t.Fatalf("Mismatch number of fileInfo walked %d expected %d; walked %s; expected %s;", len(walked), len(expected), walked, expected)
|
t.Fatalf("Mismatch number of fileInfo walked %d expected %d; walked %s; expected %s;", len(walked), len(expected), walked, expected)
|
||||||
|
|
Loading…
Reference in a new issue