Listen bucket notification for multiple prefixes/suffixes (#2911)

* Listen bucket notification for multiple prefixes/suffixes

* After review fixes
This commit is contained in:
Mateusz Gajewski 2016-10-12 20:02:15 +02:00 committed by Harshavardhana
parent 6199aa0707
commit 73982c8cb6
5 changed files with 74 additions and 19 deletions

View File

@ -80,9 +80,19 @@ func getObjectResources(values url.Values) (uploadID string, partNumberMarker, m
} }
// Parse listen bucket notification resources. // Parse listen bucket notification resources.
func getListenBucketNotificationResources(values url.Values) (prefix string, suffix string, events []string) { func getListenBucketNotificationResources(values url.Values) (prefix []string, suffix []string, events []string) {
prefix = values.Get("prefix") prefix = values["prefix"]
suffix = values.Get("suffix") suffix = values["suffix"]
events = values["events"] events = values["events"]
return prefix, suffix, events return prefix, suffix, events
} }
// Validates filter values
func validateFilterValues(values []string) (err APIErrorCode) {
for _, value := range values {
if !IsValidObjectPrefix(value) {
return ErrFilterValueInvalid
}
}
return ErrNone
}

View File

@ -18,6 +18,7 @@ package cmd
import ( import (
"net/url" "net/url"
"strings"
"testing" "testing"
) )
@ -188,3 +189,38 @@ func TestGetObjectsResources(t *testing.T) {
} }
} }
} }
// Validates if filter values are correct
func TestValidateFilterValues(t *testing.T) {
testCases := []struct {
values []string
expectedError APIErrorCode
}{
{
values: []string{""},
expectedError: ErrNone,
},
{
values: []string{"", "prefix"},
expectedError: ErrNone,
},
{
values: []string{strings.Repeat("a", 1025)},
expectedError: ErrFilterValueInvalid,
},
{
values: []string{"a\\b"},
expectedError: ErrFilterValueInvalid,
},
{
values: []string{string([]byte{0xff, 0xfe, 0xfd})},
expectedError: ErrFilterValueInvalid,
},
}
for i, testCase := range testCases {
if actualError := validateFilterValues(testCase.values); actualError != testCase.expectedError {
t.Errorf("Test %d: Expected %d, got %d", i+1, testCase.expectedError, actualError)
}
}
}

View File

@ -250,9 +250,15 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit
bucket := vars["bucket"] bucket := vars["bucket"]
// Parse listen bucket notification resources. // Parse listen bucket notification resources.
prefix, suffix, events := getListenBucketNotificationResources(r.URL.Query()) prefixes, suffixes, events := getListenBucketNotificationResources(r.URL.Query())
if !IsValidObjectPrefix(prefix) || !IsValidObjectPrefix(suffix) {
writeErrorResponse(w, r, ErrFilterValueInvalid, r.URL.Path) if err := validateFilterValues(prefixes); err != ErrNone {
writeErrorResponse(w, r, err, r.URL.Path)
return
}
if err := validateFilterValues(suffixes); err != ErrNone {
writeErrorResponse(w, r, err, r.URL.Path)
return return
} }
@ -279,13 +285,15 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit
globalMinioAddr, globalMinioAddr,
) )
var filterRules []filterRule var filterRules []filterRule
if prefix != "" {
for _, prefix := range prefixes {
filterRules = append(filterRules, filterRule{ filterRules = append(filterRules, filterRule{
Name: "prefix", Name: "prefix",
Value: prefix, Value: prefix,
}) })
} }
if suffix != "" {
for _, suffix := range suffixes {
filterRules = append(filterRules, filterRule{ filterRules = append(filterRules, filterRule{
Name: "suffix", Name: "suffix",
Value: suffix, Value: suffix,

View File

@ -235,24 +235,24 @@ func testListenBucketNotificationHandler(obj ObjectLayer, instanceType string, t
invalidEvents := []string{"invalidEvent"} invalidEvents := []string{"invalidEvent"}
testCases := []struct { testCases := []struct {
bucketName string bucketName string
prefix string prefixes []string
suffix string suffixes []string
events []string events []string
kind testKind kind testKind
expectedHTTPCode int expectedHTTPCode int
expectedAPIError string expectedAPIError string
}{ }{
// FIXME: Need to find a way to run valid listen bucket notification test case without blocking the unit test. // FIXME: Need to find a way to run valid listen bucket notification test case without blocking the unit test.
{randBucket, "", "", invalidEvents, CheckStatus, signatureMismatchError.HTTPStatusCode, ""}, {randBucket, []string{}, []string{}, invalidEvents, CheckStatus, signatureMismatchError.HTTPStatusCode, ""},
{randBucket, tooBigPrefix, "", validEvents, CheckStatus, http.StatusBadRequest, ""}, {randBucket, []string{tooBigPrefix}, []string{}, validEvents, CheckStatus, http.StatusBadRequest, ""},
{invalidBucket, "", "", validEvents, CheckStatus, http.StatusBadRequest, ""}, {invalidBucket, []string{}, []string{}, validEvents, CheckStatus, http.StatusBadRequest, ""},
{randBucket, "", "", validEvents, InvalidAuth, signatureMismatchError.HTTPStatusCode, signatureMismatchError.Code}, {randBucket, []string{}, []string{}, validEvents, InvalidAuth, signatureMismatchError.HTTPStatusCode, signatureMismatchError.Code},
} }
for i, test := range testCases { for i, test := range testCases {
testRec = httptest.NewRecorder() testRec = httptest.NewRecorder()
testReq, tErr = newTestSignedRequestV4("GET", testReq, tErr = newTestSignedRequestV4("GET",
getListenBucketNotificationURL("", test.bucketName, test.prefix, test.suffix, test.events), getListenBucketNotificationURL("", test.bucketName, test.prefixes, test.suffixes, test.events),
0, nil, credentials.AccessKeyID, credentials.SecretAccessKey) 0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
if tErr != nil { if tErr != nil {
t.Fatalf("%s: Failed to create HTTP testRequest for ListenBucketNotification: <ERROR> %v", instanceType, tErr) t.Fatalf("%s: Failed to create HTTP testRequest for ListenBucketNotification: <ERROR> %v", instanceType, tErr)
@ -301,7 +301,7 @@ func testListenBucketNotificationHandler(obj ObjectLayer, instanceType string, t
}) })
testRec = httptest.NewRecorder() testRec = httptest.NewRecorder()
testReq, tErr = newTestSignedRequestV4("GET", testReq, tErr = newTestSignedRequestV4("GET",
getListenBucketNotificationURL("", randBucket, "", "*.jpg", []string{"s3:ObjectCreated:*", "s3:ObjectRemoved:*"}), getListenBucketNotificationURL("", randBucket, []string{}, []string{"*.jpg"}, []string{"s3:ObjectCreated:*", "s3:ObjectRemoved:*"}),
0, nil, credentials.AccessKeyID, credentials.SecretAccessKey) 0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
if tErr != nil { if tErr != nil {
t.Fatalf("%s: Failed to create HTTP testRequest for ListenBucketNotification: <ERROR> %v", instanceType, tErr) t.Fatalf("%s: Failed to create HTTP testRequest for ListenBucketNotification: <ERROR> %v", instanceType, tErr)

View File

@ -1402,10 +1402,11 @@ func getGetBucketNotificationURL(endPoint, bucketName string) string {
} }
// return URL for listen bucket notification. // return URL for listen bucket notification.
func getListenBucketNotificationURL(endPoint, bucketName, prefix, suffix string, events []string) string { func getListenBucketNotificationURL(endPoint, bucketName string, prefixes, suffixes, events []string) string {
queryValue := url.Values{} queryValue := url.Values{}
queryValue.Set("prefix", prefix)
queryValue.Set("suffix", suffix) queryValue["prefix"] = prefixes
queryValue["suffix"] = suffixes
queryValue["events"] = events queryValue["events"] = events
return makeTestTargetURL(endPoint, bucketName, "", queryValue) return makeTestTargetURL(endPoint, bucketName, "", queryValue)
} }