2021-04-18 15:41:13 -04:00
// 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/>.
2021-03-13 13:28:35 -05:00
package replication
import (
"bytes"
"fmt"
"testing"
)
func TestParseAndValidateReplicationConfig ( t * testing . T ) {
testCases := [ ] struct {
inputConfig string
expectedParsingErr error
expectedValidationErr error
destBucket string
sameTarget bool
} {
2021-11-16 12:28:29 -05:00
{ // 1 Invalid delete marker status in replication config
2021-03-13 13:28:35 -05:00
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>string</Status></DeleteMarkerReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration> ` ,
destBucket : "destinationbucket" ,
sameTarget : false ,
expectedParsingErr : nil ,
expectedValidationErr : errInvalidDeleteMarkerReplicationStatus ,
} ,
2022-07-14 19:27:09 -04:00
// 2 No delete replication status in replication config
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration> ` ,
2021-03-13 13:28:35 -05:00
destBucket : "destinationbucket" ,
sameTarget : false ,
expectedParsingErr : nil ,
2022-07-14 19:27:09 -04:00
expectedValidationErr : nil ,
2021-03-13 13:28:35 -05:00
} ,
2021-11-16 12:28:29 -05:00
// 3 valid replication config
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration> ` ,
2021-03-13 13:28:35 -05:00
destBucket : "destinationbucket" ,
sameTarget : false ,
expectedParsingErr : nil ,
expectedValidationErr : nil ,
} ,
2021-11-16 12:28:29 -05:00
// 4 missing role in config and destination ARN is in legacy format
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration> ` ,
2021-03-13 13:28:35 -05:00
// destination bucket in config different from bucket specified
destBucket : "destinationbucket" ,
sameTarget : false ,
expectedParsingErr : nil ,
2021-09-18 16:31:35 -04:00
expectedValidationErr : errDestinationArnMissing ,
2021-03-13 13:28:35 -05:00
} ,
2021-11-16 12:28:29 -05:00
// 5 replication destination in different rules not identical
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role></Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:minio:replication:::destinationbucket</Bucket></Destination></Rule><Rule><Status>Enabled</Status><Priority>3</Priority><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:minio:replication:::destinationbucket2</Bucket></Destination></Rule></ReplicationConfiguration> ` ,
2021-03-13 13:28:35 -05:00
destBucket : "destinationbucket" ,
sameTarget : false ,
expectedParsingErr : nil ,
2021-09-18 16:31:35 -04:00
expectedValidationErr : nil ,
2021-03-13 13:28:35 -05:00
} ,
2021-11-16 12:28:29 -05:00
// 6 missing rule status in replication config
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration> ` ,
2021-03-13 13:28:35 -05:00
destBucket : "destinationbucket" ,
sameTarget : false ,
expectedParsingErr : nil ,
expectedValidationErr : errEmptyRuleStatus ,
} ,
2021-11-16 12:28:29 -05:00
// 7 invalid rule status in replication config
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enssabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration> ` ,
2021-03-13 13:28:35 -05:00
destBucket : "destinationbucket" ,
sameTarget : false ,
expectedParsingErr : nil ,
expectedValidationErr : errInvalidRuleStatus ,
} ,
2021-11-16 12:28:29 -05:00
// 8 invalid rule id exceeds length allowed in replication config
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><ID>vsUVERgOc8zZYagLSzSa5lE8qeI6nh1lyLNS4R9W052yfecrhhepGboswSWMMNO8CPcXM4GM3nKyQ72EadlMzzZBFoYWKn7ju5GoE5w9c57a0piHR1vexpdd9FrMquiruvAJ0MTGVupm0EegMVxoIOdjx7VgZhGrmi2XDvpVEFT7WmYMA9fSK297XkTHWyECaNHBySJ1Qp4vwX8tPNauKpfHx4kzUpnKe1PZbptGMWbY5qTcwlNuMhVSmgFffShq</ID><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration> ` ,
2021-03-13 13:28:35 -05:00
destBucket : "destinationbucket" ,
sameTarget : false ,
expectedParsingErr : nil ,
expectedValidationErr : errInvalidRuleID ,
} ,
2021-11-16 12:28:29 -05:00
// 9 invalid priority status in replication config
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration> ` ,
2021-03-13 13:28:35 -05:00
destBucket : "destinationbucket" ,
sameTarget : false ,
expectedParsingErr : nil ,
expectedValidationErr : errReplicationUniquePriority ,
} ,
2021-11-16 12:28:29 -05:00
// 10 no rule in replication config
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role></ReplicationConfiguration> ` ,
2021-03-13 13:28:35 -05:00
destBucket : "destinationbucket" ,
sameTarget : false ,
expectedParsingErr : nil ,
expectedValidationErr : errReplicationNoRule ,
} ,
2021-11-16 12:28:29 -05:00
// 11 no destination in replication config
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination></Destination></Rule></ReplicationConfiguration> ` ,
2021-03-13 13:28:35 -05:00
destBucket : "destinationbucket" ,
sameTarget : false ,
expectedParsingErr : Errorf ( "invalid destination '%v'" , "" ) ,
expectedValidationErr : nil ,
} ,
2021-11-16 12:28:29 -05:00
// 12 destination not matching ARN in replication config
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>destinationbucket2</Bucket></Destination></Rule></ReplicationConfiguration> ` ,
2021-03-13 13:28:35 -05:00
destBucket : "destinationbucket" ,
sameTarget : false ,
expectedParsingErr : fmt . Errorf ( "invalid destination '%v'" , "destinationbucket2" ) ,
expectedValidationErr : nil ,
} ,
2021-11-16 12:28:29 -05:00
// 13 missing role in config and destination ARN has target ARN
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:minio:replication::8320b6d18f9032b4700f1f03b50d8d1853de8f22cab86931ee794e12f190852c:destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration> ` ,
2021-09-18 16:31:35 -04:00
// destination bucket in config different from bucket specified
destBucket : "destinationbucket" ,
sameTarget : false ,
expectedParsingErr : nil ,
expectedValidationErr : nil ,
} ,
2021-11-16 12:28:29 -05:00
// 14 role absent in config and destination ARN has target ARN in invalid format
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:xx:replication::8320b6d18f9032b4700f1f03b50d8d1853de8f22cab86931ee794e12f190852c:destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration> ` ,
2021-09-18 16:31:35 -04:00
// destination bucket in config different from bucket specified
destBucket : "destinationbucket" ,
sameTarget : false ,
expectedParsingErr : fmt . Errorf ( "invalid destination '%v'" , "arn:xx:replication::8320b6d18f9032b4700f1f03b50d8d1853de8f22cab86931ee794e12f190852c:destinationbucket" ) ,
expectedValidationErr : nil ,
} ,
2021-03-13 13:28:35 -05:00
}
for i , tc := range testCases {
t . Run ( fmt . Sprintf ( "Test %d" , i + 1 ) , func ( t * testing . T ) {
cfg , err := ParseConfig ( bytes . NewReader ( [ ] byte ( tc . inputConfig ) ) )
if err != nil && tc . expectedParsingErr != nil && err . Error ( ) != tc . expectedParsingErr . Error ( ) {
t . Fatalf ( "%d: Expected '%v' during parsing but got '%v'" , i + 1 , tc . expectedParsingErr , err )
}
if err == nil && tc . expectedParsingErr != nil {
t . Fatalf ( "%d: Expected '%v' during parsing but got '%v'" , i + 1 , tc . expectedParsingErr , err )
}
if tc . expectedParsingErr != nil {
// We already expect a parsing error,
// no need to continue this test.
return
}
err = cfg . Validate ( tc . destBucket , tc . sameTarget )
if err != tc . expectedValidationErr {
t . Fatalf ( "%d: Expected %v during parsing but got %v" , i + 1 , tc . expectedValidationErr , err )
}
} )
}
}
2022-01-02 12:15:06 -05:00
2021-03-13 13:28:35 -05:00
func TestReplicate ( t * testing . T ) {
cfgs := [ ] Config {
2021-05-13 22:20:45 -04:00
{ // Config0 - Replication config has no filters, all replication enabled
2021-03-13 13:28:35 -05:00
Rules : [ ] Rule {
{
Status : Enabled ,
Priority : 3 ,
DeleteMarkerReplication : DeleteMarkerReplication { Status : Enabled } ,
DeleteReplication : DeleteReplication { Status : Enabled } ,
Filter : Filter { } ,
} ,
} ,
} ,
2021-05-13 22:20:45 -04:00
{ // Config1 - Replication config has no filters, delete,delete-marker replication disabled
2021-03-13 13:28:35 -05:00
Rules : [ ] Rule {
{
Status : Enabled ,
Priority : 3 ,
DeleteMarkerReplication : DeleteMarkerReplication { Status : Disabled } ,
DeleteReplication : DeleteReplication { Status : Disabled } ,
Filter : Filter { } ,
} ,
} ,
} ,
2021-05-13 22:20:45 -04:00
{ // Config2 - Replication config has filters and more than 1 matching rule, delete,delete-marker replication disabled
2021-03-13 13:28:35 -05:00
Rules : [ ] Rule {
{
Status : Enabled ,
Priority : 2 ,
DeleteMarkerReplication : DeleteMarkerReplication { Status : Disabled } ,
DeleteReplication : DeleteReplication { Status : Enabled } ,
Filter : Filter { Prefix : "xy" , And : And { } , Tag : Tag { Key : "k1" , Value : "v1" } } ,
} ,
{
Status : Enabled ,
Priority : 1 ,
DeleteMarkerReplication : DeleteMarkerReplication { Status : Enabled } ,
DeleteReplication : DeleteReplication { Status : Disabled } ,
Filter : Filter { Prefix : "xyz" } ,
} ,
} ,
} ,
2021-05-13 22:20:45 -04:00
{ // Config3 - Replication config has filters and no overlapping rules
2021-03-13 13:28:35 -05:00
Rules : [ ] Rule {
{
Status : Enabled ,
Priority : 2 ,
DeleteMarkerReplication : DeleteMarkerReplication { Status : Disabled } ,
DeleteReplication : DeleteReplication { Status : Enabled } ,
Filter : Filter { Prefix : "xy" , And : And { } , Tag : Tag { Key : "k1" , Value : "v1" } } ,
} ,
{
Status : Enabled ,
Priority : 1 ,
DeleteMarkerReplication : DeleteMarkerReplication { Status : Enabled } ,
DeleteReplication : DeleteReplication { Status : Disabled } ,
Filter : Filter { Prefix : "abc" } ,
} ,
} ,
} ,
2021-05-13 22:20:45 -04:00
{ // Config4 - Replication config has filters and SourceSelectionCriteria Disabled
Rules : [ ] Rule {
{
Status : Enabled ,
Priority : 2 ,
DeleteMarkerReplication : DeleteMarkerReplication { Status : Enabled } ,
DeleteReplication : DeleteReplication { Status : Enabled } ,
SourceSelectionCriteria : SourceSelectionCriteria { ReplicaModifications : ReplicaModifications { Status : Disabled } } ,
} ,
} ,
} ,
2021-03-13 13:28:35 -05:00
}
testCases := [ ] struct {
opts ObjectOpts
c Config
expectedResult bool
} {
// using config 1 - no filters, all replication enabled
2021-11-16 12:28:29 -05:00
{ ObjectOpts { } , cfgs [ 0 ] , false } , // 1. invalid ObjectOpts missing object name
{ ObjectOpts { Name : "c1test" } , cfgs [ 0 ] , true } , // 2. valid ObjectOpts passing empty Filter
{ ObjectOpts { Name : "c1test" , VersionID : "vid" } , cfgs [ 0 ] , true } , // 3. valid ObjectOpts passing empty Filter
2021-03-13 13:28:35 -05:00
2021-11-16 12:28:29 -05:00
{ ObjectOpts { Name : "c1test" , DeleteMarker : true , OpType : DeleteReplicationType } , cfgs [ 0 ] , true } , // 4. DeleteMarker version replication valid case - matches DeleteMarkerReplication status
{ ObjectOpts { Name : "c1test" , VersionID : "vid" , OpType : DeleteReplicationType } , cfgs [ 0 ] , true } , // 5. permanent delete of version, matches DeleteReplication status - valid case
{ ObjectOpts { Name : "c1test" , VersionID : "vid" , DeleteMarker : true , OpType : DeleteReplicationType } , cfgs [ 0 ] , true } , // 6. permanent delete of version, matches DeleteReplication status
{ ObjectOpts { Name : "c1test" , VersionID : "vid" , DeleteMarker : true , SSEC : true , OpType : DeleteReplicationType } , cfgs [ 0 ] , false } , // 7. permanent delete of version, disqualified by SSE-C
{ ObjectOpts { Name : "c1test" , DeleteMarker : true , SSEC : true , OpType : DeleteReplicationType } , cfgs [ 0 ] , false } , // 8. setting DeleteMarker on SSE-C encrypted object, disqualified by SSE-C
{ ObjectOpts { Name : "c1test" , SSEC : true } , cfgs [ 0 ] , false } , // 9. replication of SSE-C encrypted object, disqualified
2021-03-13 13:28:35 -05:00
// using config 2 - no filters, only replication of object, metadata enabled
2021-11-16 12:28:29 -05:00
{ ObjectOpts { Name : "c2test" } , cfgs [ 1 ] , true } , // 10. valid ObjectOpts passing empty Filter
{ ObjectOpts { Name : "c2test" , DeleteMarker : true , OpType : DeleteReplicationType } , cfgs [ 1 ] , false } , // 11. DeleteMarker version replication not allowed due to DeleteMarkerReplication status
{ ObjectOpts { Name : "c2test" , VersionID : "vid" , OpType : DeleteReplicationType } , cfgs [ 1 ] , false } , // 12. permanent delete of version, disallowed by DeleteReplication status
{ ObjectOpts { Name : "c2test" , VersionID : "vid" , DeleteMarker : true , OpType : DeleteReplicationType } , cfgs [ 1 ] , false } , // 13. permanent delete of DeleteMarker version, disallowed by DeleteReplication status
{ ObjectOpts { Name : "c2test" , VersionID : "vid" , DeleteMarker : true , SSEC : true , OpType : DeleteReplicationType } , cfgs [ 1 ] , false } , // 14. permanent delete of version, disqualified by SSE-C & DeleteReplication status
{ ObjectOpts { Name : "c2test" , DeleteMarker : true , SSEC : true , OpType : DeleteReplicationType } , cfgs [ 1 ] , false } , // 15. setting DeleteMarker on SSE-C encrypted object, disqualified by SSE-C & DeleteMarkerReplication status
{ ObjectOpts { Name : "c2test" , SSEC : true } , cfgs [ 1 ] , false } , // 16. replication of SSE-C encrypted object, disqualified by default
2021-03-13 13:28:35 -05:00
// using config 2 - has more than one rule with overlapping prefixes
2021-11-16 12:28:29 -05:00
{ ObjectOpts { Name : "xy/c3test" , UserTags : "k1=v1" } , cfgs [ 2 ] , true } , // 17. matches rule 1 for replication of content/metadata
{ ObjectOpts { Name : "xyz/c3test" , UserTags : "k1=v1" } , cfgs [ 2 ] , true } , // 18. matches rule 1 for replication of content/metadata
{ ObjectOpts { Name : "xyz/c3test" , UserTags : "k1=v1" , DeleteMarker : true , OpType : DeleteReplicationType } , cfgs [ 2 ] , false } , // 19. matches rule 1 - DeleteMarker replication disallowed by rule
{ ObjectOpts { Name : "xyz/c3test" , UserTags : "k1=v1" , DeleteMarker : true , VersionID : "vid" , OpType : DeleteReplicationType } , cfgs [ 2 ] , true } , // 20. matches rule 1 - DeleteReplication allowed by rule for permanent delete of DeleteMarker
{ ObjectOpts { Name : "xyz/c3test" , UserTags : "k1=v1" , VersionID : "vid" , OpType : DeleteReplicationType } , cfgs [ 2 ] , true } , // 21. matches rule 1 - DeleteReplication allowed by rule for permanent delete of version
{ ObjectOpts { Name : "xyz/c3test" } , cfgs [ 2 ] , true } , // 22. matches rule 2 for replication of content/metadata
{ ObjectOpts { Name : "xy/c3test" , UserTags : "k1=v2" } , cfgs [ 2 ] , false } , // 23. does not match rule1 because tag value does not pass filter
{ ObjectOpts { Name : "xyz/c3test" , DeleteMarker : true , OpType : DeleteReplicationType } , cfgs [ 2 ] , true } , // 24. matches rule 2 - DeleteMarker replication allowed by rule
{ ObjectOpts { Name : "xyz/c3test" , DeleteMarker : true , VersionID : "vid" , OpType : DeleteReplicationType } , cfgs [ 2 ] , false } , // 25. matches rule 2 - DeleteReplication disallowed by rule for permanent delete of DeleteMarker
{ ObjectOpts { Name : "xyz/c3test" , VersionID : "vid" , OpType : DeleteReplicationType } , cfgs [ 2 ] , false } , // 26. matches rule 1 - DeleteReplication disallowed by rule for permanent delete of version
{ ObjectOpts { Name : "abc/c3test" } , cfgs [ 2 ] , false } , // 27. matches no rule because object prefix does not match
2021-03-13 13:28:35 -05:00
// using config 3 - has no overlapping rules
2021-11-16 12:28:29 -05:00
{ ObjectOpts { Name : "xy/c4test" , UserTags : "k1=v1" } , cfgs [ 3 ] , true } , // 28. matches rule 1 for replication of content/metadata
{ ObjectOpts { Name : "xa/c4test" , UserTags : "k1=v1" } , cfgs [ 3 ] , false } , // 29. no rule match object prefix not in rules
{ ObjectOpts { Name : "xyz/c4test" , DeleteMarker : true , OpType : DeleteReplicationType } , cfgs [ 3 ] , false } , // 30. rule 1 not matched because of tags filter
{ ObjectOpts { Name : "xyz/c4test" , UserTags : "k1=v1" , DeleteMarker : true , OpType : DeleteReplicationType } , cfgs [ 3 ] , false } , // 31. matches rule 1 - DeleteMarker replication disallowed by rule
{ ObjectOpts { Name : "xyz/c4test" , UserTags : "k1=v1" , DeleteMarker : true , VersionID : "vid" , OpType : DeleteReplicationType } , cfgs [ 3 ] , true } , // 32. matches rule 1 - DeleteReplication allowed by rule for permanent delete of DeleteMarker
{ ObjectOpts { Name : "xyz/c4test" , UserTags : "k1=v1" , VersionID : "vid" , OpType : DeleteReplicationType } , cfgs [ 3 ] , true } , // 33. matches rule 1 - DeleteReplication allowed by rule for permanent delete of version
{ ObjectOpts { Name : "abc/c4test" } , cfgs [ 3 ] , true } , // 34. matches rule 2 for replication of content/metadata
{ ObjectOpts { Name : "abc/c4test" , UserTags : "k1=v2" } , cfgs [ 3 ] , true } , // 35. matches rule 2 for replication of content/metadata
{ ObjectOpts { Name : "abc/c4test" , DeleteMarker : true , OpType : DeleteReplicationType } , cfgs [ 3 ] , true } , // 36. matches rule 2 - DeleteMarker replication allowed by rule
{ ObjectOpts { Name : "abc/c4test" , DeleteMarker : true , VersionID : "vid" , OpType : DeleteReplicationType } , cfgs [ 3 ] , false } , // 37. matches rule 2 - DeleteReplication disallowed by rule for permanent delete of DeleteMarker
{ ObjectOpts { Name : "abc/c4test" , VersionID : "vid" , OpType : DeleteReplicationType } , cfgs [ 3 ] , false } , // 38. matches rule 2 - DeleteReplication disallowed by rule for permanent delete of version
2021-05-13 22:20:45 -04:00
// using config 4 - with replica modification sync disabled.
2021-11-16 12:28:29 -05:00
{ ObjectOpts { Name : "xy/c5test" , UserTags : "k1=v1" , Replica : true } , cfgs [ 4 ] , false } , // 39. replica syncing disabled, this object is a replica
{ ObjectOpts { Name : "xa/c5test" , UserTags : "k1=v1" , Replica : false } , cfgs [ 4 ] , true } , // 40. replica syncing disabled, this object is NOT a replica
2021-03-13 13:28:35 -05:00
}
2022-09-28 14:25:46 -04:00
for _ , testCase := range testCases {
testCase := testCase
t . Run ( testCase . opts . Name , func ( t * testing . T ) {
result := testCase . c . Replicate ( testCase . opts )
if result != testCase . expectedResult {
t . Errorf ( "expected: %v, got: %v" , testCase . expectedResult , result )
}
} )
2021-03-13 13:28:35 -05:00
}
}
func TestHasActiveRules ( t * testing . T ) {
testCases := [ ] struct {
inputConfig string
prefix string
expectedNonRec bool
expectedRec bool
} {
// case 1 - only one rule which is in Disabled status
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Disabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration> ` ,
2021-03-13 13:28:35 -05:00
prefix : "miss/prefix" ,
expectedNonRec : false ,
expectedRec : false ,
} ,
// case 2 - only one rule which matches prefix filter
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Filter><Prefix>key/prefix</Prefix></Filter><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration> ` ,
2021-03-13 13:28:35 -05:00
prefix : "key/prefix1" ,
expectedNonRec : true ,
expectedRec : true ,
} ,
// case 3 - empty prefix
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration> ` ,
2021-03-13 13:28:35 -05:00
prefix : "key-prefix" ,
expectedNonRec : true ,
expectedRec : true ,
} ,
// case 4 - has Filter based on prefix
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Filter><Prefix>testdir/dir1/</Prefix></Filter><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration> ` ,
2021-03-13 13:28:35 -05:00
prefix : "testdir/" ,
expectedNonRec : false ,
expectedRec : true ,
} ,
2021-11-16 12:28:29 -05:00
// case 5 - has filter with prefix and tags, here we are not matching on tags
2022-01-02 12:15:06 -05:00
{
inputConfig : ` < ReplicationConfiguration xmlns = "http://s3.amazonaws.com/doc/2006-03-01/" > < Role > arn : aws : iam : : AcctID : role / role - name < / Role > < Rule > < Status > Enabled < / Status > < DeleteMarkerReplication > < Status > Disabled < / Status > < / DeleteMarkerReplication > < DeleteReplication > < Status > Disabled < / Status > < / DeleteReplication > < Filter >
2021-03-13 13:28:35 -05:00
< And > < Prefix > key - prefix < / Prefix > < Tag > < Key > key1 < / Key > < Value > value1 < / Value > < / Tag > < Tag > < Key > key2 < / Key > < Value > value2 < / Value > < / Tag > < / And > < / Filter > < Destination > < Bucket > arn : aws : s3 : : : destinationbucket < / Bucket > < / Destination > < / Rule > < / ReplicationConfiguration > ` ,
prefix : "testdir/" ,
expectedNonRec : true ,
expectedRec : true ,
} ,
}
for i , tc := range testCases {
tc := tc
t . Run ( fmt . Sprintf ( "Test_%d" , i + 1 ) , func ( t * testing . T ) {
cfg , err := ParseConfig ( bytes . NewReader ( [ ] byte ( tc . inputConfig ) ) )
if err != nil {
t . Fatalf ( "Got unexpected error: %v" , err )
}
if got := cfg . HasActiveRules ( tc . prefix , false ) ; got != tc . expectedNonRec {
t . Fatalf ( "Expected result with recursive set to false: `%v`, got: `%v`" , tc . expectedNonRec , got )
}
if got := cfg . HasActiveRules ( tc . prefix , true ) ; got != tc . expectedRec {
t . Fatalf ( "Expected result with recursive set to true: `%v`, got: `%v`" , tc . expectedRec , got )
}
} )
}
}
2021-09-18 16:31:35 -04:00
func TestFilterActionableRules ( t * testing . T ) {
testCases := [ ] struct {
inputConfig string
prefix string
ExpectedRules [ ] Rule
} {
// case 1 - only one rule
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>prefix</Prefix><Priority>1</Priority><Destination><Bucket>arn:minio:replication:xxx::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration> ` ,
2021-09-18 16:31:35 -04:00
prefix : "prefix" ,
ExpectedRules : [ ] Rule { { Status : Enabled , Priority : 1 , DeleteMarkerReplication : DeleteMarkerReplication { Status : Enabled } , DeleteReplication : DeleteReplication { Status : Disabled } , Destination : Destination { Bucket : "destinationbucket" , ARN : "arn:minio:replication:xxx::destinationbucket" } } } ,
} ,
// case 2 - multiple rules for same target, overlapping rules with different priority
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>prefix</Prefix><Priority>3</Priority><Destination><Bucket>arn:minio:replication:xxx::destinationbucket</Bucket></Destination></Rule><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>prefix</Prefix><Priority>1</Priority><Destination><Bucket>arn:minio:replication:xxx::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration> ` ,
prefix : "prefix" ,
2021-09-18 16:31:35 -04:00
ExpectedRules : [ ] Rule {
{ Status : Enabled , Priority : 3 , DeleteMarkerReplication : DeleteMarkerReplication { Status : Enabled } , DeleteReplication : DeleteReplication { Status : Disabled } , Destination : Destination { Bucket : "destinationbucket" , ARN : "arn:minio:replication:xxx::destinationbucket" } } ,
{ Status : Enabled , Priority : 1 , DeleteMarkerReplication : DeleteMarkerReplication { Status : Enabled } , DeleteReplication : DeleteReplication { Status : Disabled } , Destination : Destination { Bucket : "destinationbucket" , ARN : "arn:minio:replication:xxx::destinationbucket" } } ,
} ,
} ,
// case 3 - multiple rules for different target, overlapping rules on a target
2022-01-02 12:15:06 -05:00
{
inputConfig : ` <ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>prefix</Prefix><Priority>2</Priority><Destination><Bucket>arn:minio:replication:xxx::destinationbucket2</Bucket></Destination></Rule><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>prefix</Prefix><Priority>4</Priority><Destination><Bucket>arn:minio:replication:xxx::destinationbucket2</Bucket></Destination></Rule><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>prefix</Prefix><Priority>3</Priority><Destination><Bucket>arn:minio:replication:xxx::destinationbucket</Bucket></Destination></Rule><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>prefix</Prefix><Priority>1</Priority><Destination><Bucket>arn:minio:replication:xxx::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration> ` ,
prefix : "prefix" ,
2021-09-18 16:31:35 -04:00
ExpectedRules : [ ] Rule {
{ Status : Enabled , Priority : 4 , DeleteMarkerReplication : DeleteMarkerReplication { Status : Enabled } , DeleteReplication : DeleteReplication { Status : Disabled } , Destination : Destination { Bucket : "destinationbucket2" , ARN : "arn:minio:replication:xxx::destinationbucket2" } } ,
{ Status : Enabled , Priority : 2 , DeleteMarkerReplication : DeleteMarkerReplication { Status : Enabled } , DeleteReplication : DeleteReplication { Status : Disabled } , Destination : Destination { Bucket : "destinationbucket2" , ARN : "arn:minio:replication:xxx::destinationbucket2" } } ,
{ Status : Enabled , Priority : 3 , DeleteMarkerReplication : DeleteMarkerReplication { Status : Enabled } , DeleteReplication : DeleteReplication { Status : Disabled } , Destination : Destination { Bucket : "destinationbucket" , ARN : "arn:minio:replication:xxx::destinationbucket" } } ,
{ Status : Enabled , Priority : 1 , DeleteMarkerReplication : DeleteMarkerReplication { Status : Enabled } , DeleteReplication : DeleteReplication { Status : Disabled } , Destination : Destination { Bucket : "destinationbucket" , ARN : "arn:minio:replication:xxx::destinationbucket" } } ,
} ,
} ,
}
for _ , tc := range testCases {
tc := tc
cfg , err := ParseConfig ( bytes . NewReader ( [ ] byte ( tc . inputConfig ) ) )
if err != nil {
t . Fatalf ( "Got unexpected error: %v" , err )
}
got := cfg . FilterActionableRules ( ObjectOpts { Name : tc . prefix } )
if len ( got ) != len ( tc . ExpectedRules ) {
t . Fatalf ( "Expected matching number of actionable rules: `%v`, got: `%v`" , tc . ExpectedRules , got )
}
for i := range got {
if got [ i ] . Destination . ARN != tc . ExpectedRules [ i ] . Destination . ARN || got [ i ] . Priority != tc . ExpectedRules [ i ] . Priority {
t . Fatalf ( "Expected order of filtered rules to be identical: `%v`, got: `%v`" , tc . ExpectedRules , got )
}
}
}
}