Fix S3 broken glob matching

main
Elie 2021-06-24 11:01:21 +02:00
parent 2f07640e0a
commit b5fe9e5e73
No known key found for this signature in database
GPG Key ID: 399AF69092C727B6
3 changed files with 121 additions and 20 deletions

View File

@ -11,9 +11,6 @@ import (
)
func GlobS3(path string) (prefix string, pattern string) {
if !HasMeta(path) {
return path, ""
}
prefix, pattern = splitDirPattern(path)
return
}
@ -23,23 +20,25 @@ func HasMeta(path string) bool {
return strings.ContainsAny(path, magicChars)
}
func splitDirPattern(p string) (base string, pattern string) {
base = p
// Should split a path :
// - prefix : path part that should not contains glob patterns, that is used in S3 query to filter result
// - pattern : should contains the glob pattern to be used by doublestar matching library
func splitDirPattern(p string) (prefix string, pattern string) {
sep := "/"
for {
if !HasMeta(base) {
break
splitPath := strings.Split(p, sep)
prefixEnded := false
for _, s := range splitPath {
if HasMeta(s) || prefixEnded {
prefixEnded = true
pattern = strings.Join([]string{pattern, s}, sep)
continue
}
if !strings.Contains(base, sep) {
return "", base
}
base = base[:strings.LastIndex(base, sep)]
prefix = strings.Join([]string{prefix, s}, sep)
}
if len(base) == len(p) {
return p, ""
}
return base, p[len(base)+1:]
return strings.Trim(prefix, sep), strings.Trim(pattern, sep)
}
func Glob(pattern string) ([]string, error) {

View File

@ -41,12 +41,12 @@ func (s *S3Enumerator) Enumerate() ([]string, error) {
}
bucket := bucketPath[0]
// prefix should contains everything that does not have a glob pattern
// Pattern should be the glob matcher string
prefix, pattern := GlobS3(strings.Join(bucketPath[1:], "/"))
fullPattern := prefix
if pattern != "" {
fullPattern = strings.Join([]string{prefix, pattern}, "/")
}
fullPattern := strings.Join([]string{prefix, pattern}, "/")
fullPattern = strings.Trim(fullPattern, "/")
files := make([]string, 0)
input := &s3.ListObjectsV2Input{

View File

@ -125,6 +125,108 @@ func TestS3Enumerator_Enumerate(t *testing.T) {
},
want: []string{"bucket-name/a/nested/prefix/state2"},
},
{
name: "test results with simple doublestar glob",
config: config.SupplierConfig{
Path: "bucket-name/**/*.tfstate",
},
mocks: func(client *awstest.MockFakeS3) {
input := &s3.ListObjectsV2Input{
Bucket: awssdk.String("bucket-name"),
Prefix: awssdk.String(""),
}
client.On(
"ListObjectsV2Pages",
input,
mock.MatchedBy(func(callback func(res *s3.ListObjectsV2Output, lastPage bool) bool) bool {
callback(&s3.ListObjectsV2Output{
Contents: []*s3.Object{
{
Key: awssdk.String("a/nested/prefix/1/state1.tfstate"),
Size: awssdk.Int64(5),
},
{
Key: awssdk.String("a/nested/folder1/2/state2.tfstate"),
Size: awssdk.Int64(5),
},
{
Key: awssdk.String("a/nested/prefix/state3.tfstate"),
Size: awssdk.Int64(5),
},
},
}, false)
callback(&s3.ListObjectsV2Output{
Contents: []*s3.Object{
{
Key: awssdk.String("a/nested/prefix/4/4/state4.tfstate"),
Size: awssdk.Int64(5),
},
{
Key: awssdk.String("a/nested/state5.tfstate"),
Size: awssdk.Int64(5),
},
{
Key: awssdk.String("a/nested/prefix/state6.tfstate.backup"),
Size: awssdk.Int64(5),
},
},
}, true)
return true
}),
).Return(nil)
},
want: []string{
"bucket-name/a/nested/prefix/1/state1.tfstate",
"bucket-name/a/nested/folder1/2/state2.tfstate",
"bucket-name/a/nested/prefix/state3.tfstate",
"bucket-name/a/nested/prefix/4/4/state4.tfstate",
"bucket-name/a/nested/state5.tfstate",
},
err: "",
},
{
name: "test results with glob and prefix after glob",
config: config.SupplierConfig{
Path: "bucket-name/a/**/b/*.tfstate",
},
mocks: func(client *awstest.MockFakeS3) {
input := &s3.ListObjectsV2Input{
Bucket: awssdk.String("bucket-name"),
Prefix: awssdk.String("a"),
}
client.On(
"ListObjectsV2Pages",
input,
mock.MatchedBy(func(callback func(res *s3.ListObjectsV2Output, lastPage bool) bool) bool {
callback(&s3.ListObjectsV2Output{
Contents: []*s3.Object{
{
Key: awssdk.String("a/prefix/b/state1.tfstate"),
Size: awssdk.Int64(5),
},
{
Key: awssdk.String("a/b/state2.tfstate"),
Size: awssdk.Int64(5),
},
{
Key: awssdk.String("a/prefix/state3.tfstate"),
Size: awssdk.Int64(5),
}, {
Key: awssdk.String("a/prefix/state4.tfstate.backup"),
Size: awssdk.Int64(5),
},
},
}, true)
return true
}),
).Return(nil)
},
want: []string{
"bucket-name/a/prefix/b/state1.tfstate",
"bucket-name/a/b/state2.tfstate",
},
err: "",
},
{
name: "test results with glob",
config: config.SupplierConfig{