mirror of
https://github.com/minio/minio.git
synced 2024-12-27 15:45:55 -05:00
fbb5e75e01
use memory for async events when necessary and dequeue them as needed, for all synchronous events customers must enable ``` MINIO_API_SYNC_EVENTS=on ``` Async events can be lost but is upto to the admin to decide what they want, we will not create run-away number of goroutines per event instead we will queue them properly. Currently the max async workers is set to runtime.GOMAXPROCS(0) which is more than sufficient in general, but it can be made configurable in future but may not be needed.
963 lines
29 KiB
Go
963 lines
29 KiB
Go
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
//
|
|
// This file is part of MinIO Object Storage stack
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package event
|
|
|
|
import (
|
|
"context"
|
|
"encoding/xml"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestValidateFilterRuleValue(t *testing.T) {
|
|
testCases := []struct {
|
|
value string
|
|
expectErr bool
|
|
}{
|
|
{"foo/.", true},
|
|
{"../foo", true},
|
|
{`foo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/bazfoo/bar/baz`, true},
|
|
{string([]byte{0xff, 0xfe, 0xfd}), true},
|
|
{`foo\bar`, true},
|
|
{"Hello/世界", false},
|
|
}
|
|
|
|
for i, testCase := range testCases {
|
|
err := ValidateFilterRuleValue(testCase.value)
|
|
expectErr := (err != nil)
|
|
|
|
if expectErr != testCase.expectErr {
|
|
t.Fatalf("test %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFilterRuleUnmarshalXML(t *testing.T) {
|
|
testCases := []struct {
|
|
data []byte
|
|
expectedResult *FilterRule
|
|
expectErr bool
|
|
}{
|
|
{[]byte(`<FilterRule></FilterRule>`), nil, true},
|
|
{[]byte(`<FilterRule><Name></Name></FilterRule>`), nil, true},
|
|
{[]byte(`<FilterRule><Value></Value></FilterRule>`), nil, true},
|
|
{[]byte(`<FilterRule><Name></Name><Value></Value></FilterRule>`), nil, true},
|
|
{[]byte(`<FilterRule><Name>Prefix</Name><Value>Hello/世界</Value></FilterRule>`), nil, true},
|
|
{[]byte(`<FilterRule><Name>ends</Name><Value>foo/bar</Value></FilterRule>`), nil, true},
|
|
{[]byte(`<FilterRule><Name>prefix</Name><Value>Hello/世界</Value></FilterRule>`), &FilterRule{"prefix", "Hello/世界"}, false},
|
|
{[]byte(`<FilterRule><Name>suffix</Name><Value>foo/bar</Value></FilterRule>`), &FilterRule{"suffix", "foo/bar"}, false},
|
|
}
|
|
|
|
for i, testCase := range testCases {
|
|
result := &FilterRule{}
|
|
err := xml.Unmarshal(testCase.data, result)
|
|
expectErr := (err != nil)
|
|
|
|
if expectErr != testCase.expectErr {
|
|
t.Fatalf("test %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
}
|
|
|
|
if !testCase.expectErr {
|
|
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
t.Fatalf("test %v: data: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFilterRuleListUnmarshalXML(t *testing.T) {
|
|
testCases := []struct {
|
|
data []byte
|
|
expectedResult *FilterRuleList
|
|
expectErr bool
|
|
}{
|
|
{[]byte(`<S3Key><FilterRule><Name>suffix</Name><Value>Hello/世界</Value></FilterRule><FilterRule><Name>suffix</Name><Value>foo/bar</Value></FilterRule></S3Key>`), nil, true},
|
|
{[]byte(`<S3Key><FilterRule><Name>prefix</Name><Value>Hello/世界</Value></FilterRule><FilterRule><Name>prefix</Name><Value>foo/bar</Value></FilterRule></S3Key>`), nil, true},
|
|
{[]byte(`<S3Key><FilterRule><Name>prefix</Name><Value>Hello/世界</Value></FilterRule></S3Key>`), &FilterRuleList{[]FilterRule{{"prefix", "Hello/世界"}}}, false},
|
|
{[]byte(`<S3Key><FilterRule><Name>suffix</Name><Value>foo/bar</Value></FilterRule></S3Key>`), &FilterRuleList{[]FilterRule{{"suffix", "foo/bar"}}}, false},
|
|
{[]byte(`<S3Key><FilterRule><Name>prefix</Name><Value>Hello/世界</Value></FilterRule><FilterRule><Name>suffix</Name><Value>foo/bar</Value></FilterRule></S3Key>`), &FilterRuleList{[]FilterRule{{"prefix", "Hello/世界"}, {"suffix", "foo/bar"}}}, false},
|
|
}
|
|
|
|
for i, testCase := range testCases {
|
|
result := &FilterRuleList{}
|
|
err := xml.Unmarshal(testCase.data, result)
|
|
expectErr := (err != nil)
|
|
|
|
if expectErr != testCase.expectErr {
|
|
t.Fatalf("test %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
}
|
|
|
|
if !testCase.expectErr {
|
|
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
t.Fatalf("test %v: data: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFilterRuleListPattern(t *testing.T) {
|
|
testCases := []struct {
|
|
filterRuleList FilterRuleList
|
|
expectedResult string
|
|
}{
|
|
{FilterRuleList{}, ""},
|
|
{FilterRuleList{[]FilterRule{{"prefix", "Hello/世界"}}}, "Hello/世界*"},
|
|
{FilterRuleList{[]FilterRule{{"suffix", "foo/bar"}}}, "*foo/bar"},
|
|
{FilterRuleList{[]FilterRule{{"prefix", "Hello/世界"}, {"suffix", "foo/bar"}}}, "Hello/世界*foo/bar"},
|
|
}
|
|
|
|
for i, testCase := range testCases {
|
|
result := testCase.filterRuleList.Pattern()
|
|
|
|
if result != testCase.expectedResult {
|
|
t.Fatalf("test %v: data: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestQueueUnmarshalXML(t *testing.T) {
|
|
dataCase1 := []byte(`
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter></Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectAccessed:*</Event>
|
|
<Event>s3:ObjectCreated:*</Event>
|
|
<Event>s3:ObjectRemoved:*</Event>
|
|
</QueueConfiguration>`)
|
|
|
|
dataCase2 := []byte(`
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter>
|
|
<S3Key>
|
|
<FilterRule>
|
|
<Name>prefix</Name>
|
|
<Value>images/</Value>
|
|
</FilterRule>
|
|
<FilterRule>
|
|
<Name>suffix</Name>
|
|
<Value>jpg</Value>
|
|
</FilterRule>
|
|
</S3Key>
|
|
</Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectCreated:Put</Event>
|
|
</QueueConfiguration>`)
|
|
|
|
dataCase3 := []byte(`
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter>
|
|
<S3Key>
|
|
<FilterRule>
|
|
<Name>prefix</Name>
|
|
<Value>images/</Value>
|
|
</FilterRule>
|
|
<FilterRule>
|
|
<Name>suffix</Name>
|
|
<Value>jpg</Value>
|
|
</FilterRule>
|
|
</S3Key>
|
|
</Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectCreated:Put</Event>
|
|
<Event>s3:ObjectCreated:Put</Event>
|
|
</QueueConfiguration>`)
|
|
|
|
testCases := []struct {
|
|
data []byte
|
|
expectErr bool
|
|
}{
|
|
{dataCase1, false},
|
|
{dataCase2, false},
|
|
{dataCase3, true},
|
|
}
|
|
|
|
for i, testCase := range testCases {
|
|
err := xml.Unmarshal(testCase.data, &Queue{})
|
|
expectErr := (err != nil)
|
|
|
|
if expectErr != testCase.expectErr {
|
|
t.Fatalf("test %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestQueueValidate(t *testing.T) {
|
|
data := []byte(`
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter></Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectAccessed:*</Event>
|
|
<Event>s3:ObjectCreated:*</Event>
|
|
<Event>s3:ObjectRemoved:*</Event>
|
|
</QueueConfiguration>`)
|
|
queue1 := &Queue{}
|
|
if err := xml.Unmarshal(data, queue1); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
data = []byte(`
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter>
|
|
<S3Key>
|
|
<FilterRule>
|
|
<Name>prefix</Name>
|
|
<Value>images/</Value>
|
|
</FilterRule>
|
|
<FilterRule>
|
|
<Name>suffix</Name>
|
|
<Value>jpg</Value>
|
|
</FilterRule>
|
|
</S3Key>
|
|
</Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectCreated:Put</Event>
|
|
</QueueConfiguration>`)
|
|
queue2 := &Queue{}
|
|
if err := xml.Unmarshal(data, queue2); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
data = []byte(`
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter></Filter>
|
|
<Queue>arn:minio:sqs:eu-west-2:1:webhook</Queue>
|
|
<Event>s3:ObjectAccessed:*</Event>
|
|
<Event>s3:ObjectCreated:*</Event>
|
|
<Event>s3:ObjectRemoved:*</Event>
|
|
</QueueConfiguration>`)
|
|
queue3 := &Queue{}
|
|
if err := xml.Unmarshal(data, queue3); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
targetList1 := NewTargetList(context.Background())
|
|
|
|
targetList2 := NewTargetList(context.Background())
|
|
if err := targetList2.Add(&ExampleTarget{TargetID{"1", "webhook"}, false, false}); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
testCases := []struct {
|
|
queue *Queue
|
|
region string
|
|
targetList *TargetList
|
|
expectErr bool
|
|
}{
|
|
{queue1, "eu-west-1", nil, true},
|
|
{queue2, "us-east-1", targetList1, true},
|
|
{queue3, "", targetList2, false},
|
|
{queue2, "us-east-1", targetList2, false},
|
|
}
|
|
|
|
for i, testCase := range testCases {
|
|
err := testCase.queue.Validate(testCase.region, testCase.targetList)
|
|
expectErr := (err != nil)
|
|
|
|
if expectErr != testCase.expectErr {
|
|
t.Fatalf("test %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestQueueSetRegion(t *testing.T) {
|
|
data := []byte(`
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter></Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectAccessed:*</Event>
|
|
<Event>s3:ObjectCreated:*</Event>
|
|
<Event>s3:ObjectRemoved:*</Event>
|
|
</QueueConfiguration>`)
|
|
queue1 := &Queue{}
|
|
if err := xml.Unmarshal(data, queue1); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
data = []byte(`
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter>
|
|
<S3Key>
|
|
<FilterRule>
|
|
<Name>prefix</Name>
|
|
<Value>images/</Value>
|
|
</FilterRule>
|
|
<FilterRule>
|
|
<Name>suffix</Name>
|
|
<Value>jpg</Value>
|
|
</FilterRule>
|
|
</S3Key>
|
|
</Filter>
|
|
<Queue>arn:minio:sqs::1:webhook</Queue>
|
|
<Event>s3:ObjectCreated:Put</Event>
|
|
</QueueConfiguration>`)
|
|
queue2 := &Queue{}
|
|
if err := xml.Unmarshal(data, queue2); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
testCases := []struct {
|
|
queue *Queue
|
|
region string
|
|
expectedResult ARN
|
|
}{
|
|
{queue1, "eu-west-1", ARN{TargetID{"1", "webhook"}, "eu-west-1"}},
|
|
{queue1, "", ARN{TargetID{"1", "webhook"}, ""}},
|
|
{queue2, "us-east-1", ARN{TargetID{"1", "webhook"}, "us-east-1"}},
|
|
{queue2, "", ARN{TargetID{"1", "webhook"}, ""}},
|
|
}
|
|
|
|
for i, testCase := range testCases {
|
|
testCase.queue.SetRegion(testCase.region)
|
|
result := testCase.queue.ARN
|
|
|
|
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
t.Fatalf("test %v: data: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestQueueToRulesMap(t *testing.T) {
|
|
data := []byte(`
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter></Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectAccessed:*</Event>
|
|
<Event>s3:ObjectCreated:*</Event>
|
|
<Event>s3:ObjectRemoved:*</Event>
|
|
</QueueConfiguration>`)
|
|
queueCase1 := &Queue{}
|
|
if err := xml.Unmarshal(data, queueCase1); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
data = []byte(`
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter>
|
|
<S3Key>
|
|
<FilterRule>
|
|
<Name>prefix</Name>
|
|
<Value>images/</Value>
|
|
</FilterRule>
|
|
<FilterRule>
|
|
<Name>suffix</Name>
|
|
<Value>jpg</Value>
|
|
</FilterRule>
|
|
</S3Key>
|
|
</Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectCreated:Put</Event>
|
|
</QueueConfiguration>`)
|
|
queueCase2 := &Queue{}
|
|
if err := xml.Unmarshal(data, queueCase2); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
rulesMapCase1 := NewRulesMap([]Name{ObjectAccessedAll, ObjectCreatedAll, ObjectRemovedAll}, "*", TargetID{"1", "webhook"})
|
|
rulesMapCase2 := NewRulesMap([]Name{ObjectCreatedPut}, "images/*jpg", TargetID{"1", "webhook"})
|
|
|
|
testCases := []struct {
|
|
queue *Queue
|
|
expectedResult RulesMap
|
|
}{
|
|
{queueCase1, rulesMapCase1},
|
|
{queueCase2, rulesMapCase2},
|
|
}
|
|
|
|
for i, testCase := range testCases {
|
|
result := testCase.queue.ToRulesMap()
|
|
|
|
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
t.Fatalf("test %v: data: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConfigUnmarshalXML(t *testing.T) {
|
|
dataCase1 := []byte(`
|
|
<NotificationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter></Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectAccessed:*</Event>
|
|
<Event>s3:ObjectCreated:*</Event>
|
|
<Event>s3:ObjectRemoved:*</Event>
|
|
</QueueConfiguration>
|
|
</NotificationConfiguration>
|
|
`)
|
|
|
|
dataCase2 := []byte(`
|
|
<NotificationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter>
|
|
<S3Key>
|
|
<FilterRule>
|
|
<Name>prefix</Name>
|
|
<Value>images/</Value>
|
|
</FilterRule>
|
|
<FilterRule>
|
|
<Name>suffix</Name>
|
|
<Value>jpg</Value>
|
|
</FilterRule>
|
|
</S3Key>
|
|
</Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectCreated:Put</Event>
|
|
</QueueConfiguration>
|
|
</NotificationConfiguration>
|
|
`)
|
|
|
|
dataCase3 := []byte(`
|
|
<NotificationConfiguration>
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter></Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectAccessed:*</Event>
|
|
<Event>s3:ObjectCreated:*</Event>
|
|
<Event>s3:ObjectRemoved:*</Event>
|
|
</QueueConfiguration>
|
|
<QueueConfiguration>
|
|
<Id>2</Id>
|
|
<Filter>
|
|
<S3Key>
|
|
<FilterRule>
|
|
<Name>prefix</Name>
|
|
<Value>images/</Value>
|
|
</FilterRule>
|
|
<FilterRule>
|
|
<Name>suffix</Name>
|
|
<Value>jpg</Value>
|
|
</FilterRule>
|
|
</S3Key>
|
|
</Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectCreated:Put</Event>
|
|
</QueueConfiguration>
|
|
</NotificationConfiguration>
|
|
`)
|
|
|
|
dataCase4 := []byte(`
|
|
<NotificationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter></Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectAccessed:*</Event>
|
|
<Event>s3:ObjectCreated:*</Event>
|
|
<Event>s3:ObjectRemoved:*</Event>
|
|
</QueueConfiguration>
|
|
<CloudFunctionConfiguration>
|
|
<Id>1</Id>
|
|
<Filter>
|
|
<S3Key>
|
|
<FilterRule>
|
|
<Name>suffix</Name>
|
|
<Value>.jpg</Value>
|
|
</FilterRule>
|
|
</S3Key>
|
|
</Filter>
|
|
<Cloudcode>arn:aws:lambda:us-west-2:444455556666:cloud-function-A</Cloudcode>
|
|
<Event>s3:ObjectCreated:Put</Event>
|
|
</CloudFunctionConfiguration>
|
|
<TopicConfiguration>
|
|
<Topic>arn:aws:sns:us-west-2:444455556666:sns-notification-one</Topic>
|
|
<Event>s3:ObjectCreated:*</Event>
|
|
</TopicConfiguration>
|
|
</NotificationConfiguration>
|
|
`)
|
|
|
|
dataCase5 := []byte(`<NotificationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/" ></NotificationConfiguration>`)
|
|
|
|
testCases := []struct {
|
|
data []byte
|
|
expectErr bool
|
|
}{
|
|
{dataCase1, false},
|
|
{dataCase2, false},
|
|
{dataCase3, false},
|
|
{dataCase4, true},
|
|
// make sure we don't fail when queue is empty.
|
|
{dataCase5, false},
|
|
}
|
|
|
|
for i, testCase := range testCases {
|
|
err := xml.Unmarshal(testCase.data, &Config{})
|
|
expectErr := (err != nil)
|
|
|
|
if expectErr != testCase.expectErr {
|
|
t.Fatalf("test %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConfigValidate(t *testing.T) {
|
|
data := []byte(`
|
|
<NotificationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter></Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectAccessed:*</Event>
|
|
<Event>s3:ObjectCreated:*</Event>
|
|
<Event>s3:ObjectRemoved:*</Event>
|
|
</QueueConfiguration>
|
|
</NotificationConfiguration>
|
|
`)
|
|
config1 := &Config{}
|
|
if err := xml.Unmarshal(data, config1); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
data = []byte(`
|
|
<NotificationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter>
|
|
<S3Key>
|
|
<FilterRule>
|
|
<Name>prefix</Name>
|
|
<Value>images/</Value>
|
|
</FilterRule>
|
|
<FilterRule>
|
|
<Name>suffix</Name>
|
|
<Value>jpg</Value>
|
|
</FilterRule>
|
|
</S3Key>
|
|
</Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectCreated:Put</Event>
|
|
</QueueConfiguration>
|
|
</NotificationConfiguration>
|
|
`)
|
|
config2 := &Config{}
|
|
if err := xml.Unmarshal(data, config2); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
data = []byte(`
|
|
<NotificationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter></Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectAccessed:*</Event>
|
|
<Event>s3:ObjectCreated:*</Event>
|
|
<Event>s3:ObjectRemoved:*</Event>
|
|
</QueueConfiguration>
|
|
<QueueConfiguration>
|
|
<Id>2</Id>
|
|
<Filter>
|
|
<S3Key>
|
|
<FilterRule>
|
|
<Name>prefix</Name>
|
|
<Value>images/</Value>
|
|
</FilterRule>
|
|
<FilterRule>
|
|
<Name>suffix</Name>
|
|
<Value>jpg</Value>
|
|
</FilterRule>
|
|
</S3Key>
|
|
</Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectCreated:Put</Event>
|
|
</QueueConfiguration>
|
|
</NotificationConfiguration>
|
|
`)
|
|
config3 := &Config{}
|
|
if err := xml.Unmarshal(data, config3); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
targetList1 := NewTargetList(context.Background())
|
|
|
|
targetList2 := NewTargetList(context.Background())
|
|
if err := targetList2.Add(&ExampleTarget{TargetID{"1", "webhook"}, false, false}); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
testCases := []struct {
|
|
config *Config
|
|
region string
|
|
targetList *TargetList
|
|
expectErr bool
|
|
}{
|
|
{config1, "eu-west-1", nil, true},
|
|
{config2, "us-east-1", targetList1, true},
|
|
{config3, "", targetList2, false},
|
|
{config2, "us-east-1", targetList2, false},
|
|
}
|
|
|
|
for i, testCase := range testCases {
|
|
err := testCase.config.Validate(testCase.region, testCase.targetList)
|
|
expectErr := (err != nil)
|
|
|
|
if expectErr != testCase.expectErr {
|
|
t.Fatalf("test %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConfigSetRegion(t *testing.T) {
|
|
data := []byte(`
|
|
<NotificationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter></Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectAccessed:*</Event>
|
|
<Event>s3:ObjectCreated:*</Event>
|
|
<Event>s3:ObjectRemoved:*</Event>
|
|
</QueueConfiguration>
|
|
</NotificationConfiguration>
|
|
`)
|
|
config1 := &Config{}
|
|
if err := xml.Unmarshal(data, config1); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
data = []byte(`
|
|
<NotificationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter>
|
|
<S3Key>
|
|
<FilterRule>
|
|
<Name>prefix</Name>
|
|
<Value>images/</Value>
|
|
</FilterRule>
|
|
<FilterRule>
|
|
<Name>suffix</Name>
|
|
<Value>jpg</Value>
|
|
</FilterRule>
|
|
</S3Key>
|
|
</Filter>
|
|
<Queue>arn:minio:sqs::1:webhook</Queue>
|
|
<Event>s3:ObjectCreated:Put</Event>
|
|
</QueueConfiguration>
|
|
</NotificationConfiguration>
|
|
`)
|
|
config2 := &Config{}
|
|
if err := xml.Unmarshal(data, config2); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
data = []byte(`
|
|
<NotificationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter></Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectAccessed:*</Event>
|
|
<Event>s3:ObjectCreated:*</Event>
|
|
<Event>s3:ObjectRemoved:*</Event>
|
|
</QueueConfiguration>
|
|
<QueueConfiguration>
|
|
<Id>2</Id>
|
|
<Filter>
|
|
<S3Key>
|
|
<FilterRule>
|
|
<Name>prefix</Name>
|
|
<Value>images/</Value>
|
|
</FilterRule>
|
|
<FilterRule>
|
|
<Name>suffix</Name>
|
|
<Value>jpg</Value>
|
|
</FilterRule>
|
|
</S3Key>
|
|
</Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:2:amqp</Queue>
|
|
<Event>s3:ObjectCreated:Put</Event>
|
|
</QueueConfiguration>
|
|
</NotificationConfiguration>
|
|
`)
|
|
config3 := &Config{}
|
|
if err := xml.Unmarshal(data, config3); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
testCases := []struct {
|
|
config *Config
|
|
region string
|
|
expectedResult []ARN
|
|
}{
|
|
{config1, "eu-west-1", []ARN{{TargetID{"1", "webhook"}, "eu-west-1"}}},
|
|
{config1, "", []ARN{{TargetID{"1", "webhook"}, ""}}},
|
|
{config2, "us-east-1", []ARN{{TargetID{"1", "webhook"}, "us-east-1"}}},
|
|
{config2, "", []ARN{{TargetID{"1", "webhook"}, ""}}},
|
|
{config3, "us-east-1", []ARN{{TargetID{"1", "webhook"}, "us-east-1"}, {TargetID{"2", "amqp"}, "us-east-1"}}},
|
|
{config3, "", []ARN{{TargetID{"1", "webhook"}, ""}, {TargetID{"2", "amqp"}, ""}}},
|
|
}
|
|
|
|
for i, testCase := range testCases {
|
|
testCase.config.SetRegion(testCase.region)
|
|
result := []ARN{}
|
|
for _, queue := range testCase.config.QueueList {
|
|
result = append(result, queue.ARN)
|
|
}
|
|
|
|
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
t.Fatalf("test %v: data: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConfigToRulesMap(t *testing.T) {
|
|
data := []byte(`
|
|
<NotificationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter></Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectAccessed:*</Event>
|
|
<Event>s3:ObjectCreated:*</Event>
|
|
<Event>s3:ObjectRemoved:*</Event>
|
|
</QueueConfiguration>
|
|
</NotificationConfiguration>
|
|
`)
|
|
config1 := &Config{}
|
|
if err := xml.Unmarshal(data, config1); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
data = []byte(`
|
|
<NotificationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter>
|
|
<S3Key>
|
|
<FilterRule>
|
|
<Name>prefix</Name>
|
|
<Value>images/</Value>
|
|
</FilterRule>
|
|
<FilterRule>
|
|
<Name>suffix</Name>
|
|
<Value>jpg</Value>
|
|
</FilterRule>
|
|
</S3Key>
|
|
</Filter>
|
|
<Queue>arn:minio:sqs::1:webhook</Queue>
|
|
<Event>s3:ObjectCreated:Put</Event>
|
|
</QueueConfiguration>
|
|
</NotificationConfiguration>
|
|
`)
|
|
config2 := &Config{}
|
|
if err := xml.Unmarshal(data, config2); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
data = []byte(`
|
|
<NotificationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter></Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectAccessed:*</Event>
|
|
<Event>s3:ObjectCreated:*</Event>
|
|
<Event>s3:ObjectRemoved:*</Event>
|
|
</QueueConfiguration>
|
|
<QueueConfiguration>
|
|
<Id>2</Id>
|
|
<Filter>
|
|
<S3Key>
|
|
<FilterRule>
|
|
<Name>prefix</Name>
|
|
<Value>images/</Value>
|
|
</FilterRule>
|
|
<FilterRule>
|
|
<Name>suffix</Name>
|
|
<Value>jpg</Value>
|
|
</FilterRule>
|
|
</S3Key>
|
|
</Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:2:amqp</Queue>
|
|
<Event>s3:ObjectCreated:Put</Event>
|
|
</QueueConfiguration>
|
|
</NotificationConfiguration>
|
|
`)
|
|
config3 := &Config{}
|
|
if err := xml.Unmarshal(data, config3); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
rulesMapCase1 := NewRulesMap([]Name{ObjectAccessedAll, ObjectCreatedAll, ObjectRemovedAll}, "*", TargetID{"1", "webhook"})
|
|
|
|
rulesMapCase2 := NewRulesMap([]Name{ObjectCreatedPut}, "images/*jpg", TargetID{"1", "webhook"})
|
|
|
|
rulesMapCase3 := NewRulesMap([]Name{ObjectAccessedAll, ObjectCreatedAll, ObjectRemovedAll}, "*", TargetID{"1", "webhook"})
|
|
rulesMapCase3.add([]Name{ObjectCreatedPut}, "images/*jpg", TargetID{"2", "amqp"})
|
|
|
|
testCases := []struct {
|
|
config *Config
|
|
expectedResult RulesMap
|
|
}{
|
|
{config1, rulesMapCase1},
|
|
{config2, rulesMapCase2},
|
|
{config3, rulesMapCase3},
|
|
}
|
|
|
|
for i, testCase := range testCases {
|
|
result := testCase.config.ToRulesMap()
|
|
|
|
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
|
t.Fatalf("test %v: data: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseConfig(t *testing.T) {
|
|
reader1 := strings.NewReader(`
|
|
<NotificationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter></Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectAccessed:*</Event>
|
|
<Event>s3:ObjectCreated:*</Event>
|
|
<Event>s3:ObjectRemoved:*</Event>
|
|
</QueueConfiguration>
|
|
</NotificationConfiguration>
|
|
`)
|
|
|
|
reader2 := strings.NewReader(`
|
|
<NotificationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter>
|
|
<S3Key>
|
|
<FilterRule>
|
|
<Name>prefix</Name>
|
|
<Value>images/</Value>
|
|
</FilterRule>
|
|
<FilterRule>
|
|
<Name>suffix</Name>
|
|
<Value>jpg</Value>
|
|
</FilterRule>
|
|
</S3Key>
|
|
</Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectCreated:Put</Event>
|
|
</QueueConfiguration>
|
|
</NotificationConfiguration>
|
|
`)
|
|
|
|
reader3 := strings.NewReader(`
|
|
<NotificationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter></Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectAccessed:*</Event>
|
|
<Event>s3:ObjectCreated:*</Event>
|
|
<Event>s3:ObjectRemoved:*</Event>
|
|
</QueueConfiguration>
|
|
<QueueConfiguration>
|
|
<Id>2</Id>
|
|
<Filter>
|
|
<S3Key>
|
|
<FilterRule>
|
|
<Name>prefix</Name>
|
|
<Value>images/</Value>
|
|
</FilterRule>
|
|
<FilterRule>
|
|
<Name>suffix</Name>
|
|
<Value>jpg</Value>
|
|
</FilterRule>
|
|
</S3Key>
|
|
</Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectCreated:Put</Event>
|
|
</QueueConfiguration>
|
|
</NotificationConfiguration>
|
|
`)
|
|
|
|
reader4 := strings.NewReader(`
|
|
<NotificationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
|
<QueueConfiguration>
|
|
<Id>1</Id>
|
|
<Filter></Filter>
|
|
<Queue>arn:minio:sqs:us-east-1:1:webhook</Queue>
|
|
<Event>s3:ObjectAccessed:*</Event>
|
|
<Event>s3:ObjectCreated:*</Event>
|
|
<Event>s3:ObjectRemoved:*</Event>
|
|
</QueueConfiguration>
|
|
<CloudFunctionConfiguration>
|
|
<Id>1</Id>
|
|
<Filter>
|
|
<S3Key>
|
|
<FilterRule>
|
|
<Name>suffix</Name>
|
|
<Value>.jpg</Value>
|
|
</FilterRule>
|
|
</S3Key>
|
|
</Filter>
|
|
<Cloudcode>arn:aws:lambda:us-west-2:444455556666:cloud-function-A</Cloudcode>
|
|
<Event>s3:ObjectCreated:Put</Event>
|
|
</CloudFunctionConfiguration>
|
|
<TopicConfiguration>
|
|
<Topic>arn:aws:sns:us-west-2:444455556666:sns-notification-one</Topic>
|
|
<Event>s3:ObjectCreated:*</Event>
|
|
</TopicConfiguration>
|
|
</NotificationConfiguration>
|
|
`)
|
|
|
|
targetList1 := NewTargetList(context.Background())
|
|
|
|
targetList2 := NewTargetList(context.Background())
|
|
if err := targetList2.Add(&ExampleTarget{TargetID{"1", "webhook"}, false, false}); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
testCases := []struct {
|
|
reader *strings.Reader
|
|
region string
|
|
targetList *TargetList
|
|
expectErr bool
|
|
}{
|
|
{reader1, "eu-west-1", nil, true},
|
|
{reader2, "us-east-1", targetList1, true},
|
|
{reader4, "us-east-1", targetList1, true},
|
|
{reader3, "", targetList2, false},
|
|
{reader2, "us-east-1", targetList2, false},
|
|
}
|
|
|
|
for i, testCase := range testCases {
|
|
if _, err := testCase.reader.Seek(0, 0); err != nil {
|
|
panic(err)
|
|
}
|
|
_, err := ParseConfig(testCase.reader, testCase.region, testCase.targetList)
|
|
expectErr := (err != nil)
|
|
|
|
if expectErr != testCase.expectErr {
|
|
t.Fatalf("test %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
|
}
|
|
}
|
|
}
|