2021-06-01 22:59:11 -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/>.
|
|
|
|
|
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2023-06-19 20:53:08 -04:00
|
|
|
"github.com/minio/madmin-go/v3"
|
2021-06-01 22:59:11 -04:00
|
|
|
"github.com/minio/minio/internal/bucket/replication"
|
|
|
|
xhttp "github.com/minio/minio/internal/http"
|
|
|
|
)
|
|
|
|
|
|
|
|
var configs = []replication.Config{
|
|
|
|
{ // Config0 - Replication config has no filters, existing object replication enabled
|
|
|
|
Rules: []replication.Rule{
|
|
|
|
{
|
|
|
|
Status: replication.Enabled,
|
|
|
|
Priority: 1,
|
|
|
|
DeleteMarkerReplication: replication.DeleteMarkerReplication{Status: replication.Enabled},
|
|
|
|
DeleteReplication: replication.DeleteReplication{Status: replication.Enabled},
|
|
|
|
Filter: replication.Filter{},
|
|
|
|
ExistingObjectReplication: replication.ExistingObjectReplication{Status: replication.Enabled},
|
|
|
|
SourceSelectionCriteria: replication.SourceSelectionCriteria{
|
|
|
|
ReplicaModifications: replication.ReplicaModifications{Status: replication.Enabled},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var replicationConfigTests = []struct {
|
|
|
|
info ObjectInfo
|
|
|
|
name string
|
|
|
|
rcfg replicationConfig
|
2021-09-18 16:31:35 -04:00
|
|
|
dsc ReplicateDecision
|
|
|
|
tgtStatuses map[string]replication.StatusType
|
2021-06-01 22:59:11 -04:00
|
|
|
expectedSync bool
|
|
|
|
}{
|
2021-11-16 12:28:29 -05:00
|
|
|
{ // 1. no replication config
|
2021-06-01 22:59:11 -04:00
|
|
|
name: "no replication config",
|
|
|
|
info: ObjectInfo{Size: 100},
|
|
|
|
rcfg: replicationConfig{Config: nil},
|
|
|
|
expectedSync: false,
|
|
|
|
},
|
2021-11-16 12:28:29 -05:00
|
|
|
{ // 2. existing object replication config enabled, no versioning
|
2021-06-01 22:59:11 -04:00
|
|
|
name: "existing object replication config enabled, no versioning",
|
|
|
|
info: ObjectInfo{Size: 100},
|
|
|
|
rcfg: replicationConfig{Config: &configs[0]},
|
|
|
|
expectedSync: false,
|
|
|
|
},
|
2021-11-16 12:28:29 -05:00
|
|
|
{ // 3. existing object replication config enabled, versioning suspended
|
2021-06-01 22:59:11 -04:00
|
|
|
name: "existing object replication config enabled, versioning suspended",
|
|
|
|
info: ObjectInfo{Size: 100, VersionID: nullVersionID},
|
|
|
|
rcfg: replicationConfig{Config: &configs[0]},
|
|
|
|
expectedSync: false,
|
|
|
|
},
|
2021-11-16 12:28:29 -05:00
|
|
|
{ // 4. existing object replication enabled, versioning enabled; no reset in progress
|
2021-06-01 22:59:11 -04:00
|
|
|
name: "existing object replication enabled, versioning enabled; no reset in progress",
|
2022-01-02 12:15:06 -05:00
|
|
|
info: ObjectInfo{
|
|
|
|
Size: 100,
|
2021-06-01 22:59:11 -04:00
|
|
|
ReplicationStatus: replication.Completed,
|
|
|
|
VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
|
|
|
|
},
|
|
|
|
rcfg: replicationConfig{Config: &configs[0]},
|
|
|
|
expectedSync: false,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestReplicationResync(t *testing.T) {
|
|
|
|
ctx := context.Background()
|
|
|
|
for i, test := range replicationConfigTests {
|
2023-09-16 05:28:06 -04:00
|
|
|
if sync := test.rcfg.Resync(ctx, test.info, test.dsc, test.tgtStatuses); sync.mustResync() != test.expectedSync {
|
2021-09-18 16:31:35 -04:00
|
|
|
t.Errorf("Test%d (%s): Resync got %t , want %t", i+1, test.name, sync.mustResync(), test.expectedSync)
|
2021-06-01 22:59:11 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-02 12:15:06 -05:00
|
|
|
var (
|
|
|
|
start = UTCNow().AddDate(0, 0, -1)
|
|
|
|
replicationConfigTests2 = []struct {
|
|
|
|
info ObjectInfo
|
|
|
|
name string
|
|
|
|
rcfg replicationConfig
|
|
|
|
dsc ReplicateDecision
|
|
|
|
tgtStatuses map[string]replication.StatusType
|
|
|
|
expectedSync bool
|
|
|
|
}{
|
|
|
|
{ // Cases 1-4: existing object replication enabled, versioning enabled, no reset - replication status varies
|
|
|
|
// 1: Pending replication
|
|
|
|
name: "existing object replication on object in Pending replication status",
|
|
|
|
info: ObjectInfo{
|
|
|
|
Size: 100,
|
|
|
|
ReplicationStatusInternal: "arn1:PENDING;",
|
|
|
|
ReplicationStatus: replication.Pending,
|
|
|
|
VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
|
|
|
|
},
|
|
|
|
rcfg: replicationConfig{remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
|
|
|
|
Arn: "arn1",
|
|
|
|
}}}},
|
|
|
|
dsc: ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", true, false)}},
|
|
|
|
expectedSync: true,
|
2021-06-01 22:59:11 -04:00
|
|
|
},
|
2021-09-18 16:31:35 -04:00
|
|
|
|
2022-01-02 12:15:06 -05:00
|
|
|
{ // 2. replication status Failed
|
|
|
|
name: "existing object replication on object in Failed replication status",
|
|
|
|
info: ObjectInfo{
|
|
|
|
Size: 100,
|
|
|
|
ReplicationStatusInternal: "arn1:FAILED",
|
|
|
|
ReplicationStatus: replication.Failed,
|
|
|
|
VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
|
|
|
|
},
|
|
|
|
dsc: ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", true, false)}},
|
|
|
|
rcfg: replicationConfig{remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
|
|
|
|
Arn: "arn1",
|
|
|
|
}}}},
|
|
|
|
expectedSync: true,
|
2021-09-18 16:31:35 -04:00
|
|
|
},
|
2022-01-02 12:15:06 -05:00
|
|
|
{ // 3. replication status unset
|
|
|
|
name: "existing object replication on pre-existing unreplicated object",
|
|
|
|
info: ObjectInfo{
|
|
|
|
Size: 100,
|
|
|
|
ReplicationStatus: replication.StatusType(""),
|
|
|
|
VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
|
|
|
|
},
|
|
|
|
rcfg: replicationConfig{remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
|
|
|
|
Arn: "arn1",
|
|
|
|
}}}},
|
|
|
|
dsc: ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", true, false)}},
|
|
|
|
expectedSync: true,
|
2021-09-18 16:31:35 -04:00
|
|
|
},
|
2022-01-02 12:15:06 -05:00
|
|
|
{ // 4. replication status Complete
|
|
|
|
name: "existing object replication on object in Completed replication status",
|
|
|
|
info: ObjectInfo{
|
|
|
|
Size: 100,
|
|
|
|
ReplicationStatusInternal: "arn1:COMPLETED",
|
|
|
|
ReplicationStatus: replication.Completed,
|
|
|
|
VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
|
|
|
|
},
|
|
|
|
dsc: ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", false, false)}},
|
|
|
|
rcfg: replicationConfig{remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
|
|
|
|
Arn: "arn1",
|
|
|
|
}}}},
|
|
|
|
expectedSync: false,
|
2021-06-01 22:59:11 -04:00
|
|
|
},
|
2022-01-02 12:15:06 -05:00
|
|
|
{ // 5. existing object replication enabled, versioning enabled, replication status Pending & reset ID present
|
|
|
|
name: "existing object replication with reset in progress and object in Pending status",
|
|
|
|
info: ObjectInfo{
|
|
|
|
Size: 100,
|
|
|
|
ReplicationStatusInternal: "arn1:PENDING;",
|
|
|
|
ReplicationStatus: replication.Pending,
|
|
|
|
VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
|
|
|
|
UserDefined: map[string]string{xhttp.MinIOReplicationResetStatus: fmt.Sprintf("%s;abc", UTCNow().AddDate(0, -1, 0).String())},
|
|
|
|
},
|
|
|
|
expectedSync: true,
|
|
|
|
dsc: ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", true, false)}},
|
|
|
|
rcfg: replicationConfig{
|
|
|
|
remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
|
|
|
|
Arn: "arn1",
|
|
|
|
ResetID: "xyz",
|
|
|
|
ResetBeforeDate: UTCNow(),
|
|
|
|
}}},
|
|
|
|
},
|
2021-06-01 22:59:11 -04:00
|
|
|
},
|
2022-01-02 12:15:06 -05:00
|
|
|
{ // 6. existing object replication enabled, versioning enabled, replication status Failed & reset ID present
|
|
|
|
name: "existing object replication with reset in progress and object in Failed status",
|
|
|
|
info: ObjectInfo{
|
|
|
|
Size: 100,
|
|
|
|
ReplicationStatusInternal: "arn1:FAILED;",
|
|
|
|
ReplicationStatus: replication.Failed,
|
|
|
|
VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
|
|
|
|
UserDefined: map[string]string{xhttp.MinIOReplicationResetStatus: fmt.Sprintf("%s;abc", UTCNow().AddDate(0, -1, 0).String())},
|
|
|
|
},
|
|
|
|
dsc: ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", true, false)}},
|
|
|
|
rcfg: replicationConfig{
|
|
|
|
remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
|
|
|
|
Arn: "arn1",
|
|
|
|
ResetID: "xyz",
|
|
|
|
ResetBeforeDate: UTCNow(),
|
|
|
|
}}},
|
|
|
|
},
|
|
|
|
expectedSync: true,
|
2021-09-18 16:31:35 -04:00
|
|
|
},
|
2022-01-02 12:15:06 -05:00
|
|
|
{ // 7. existing object replication enabled, versioning enabled, replication status unset & reset ID present
|
|
|
|
name: "existing object replication with reset in progress and object never replicated before",
|
|
|
|
info: ObjectInfo{
|
|
|
|
Size: 100,
|
|
|
|
ReplicationStatus: replication.StatusType(""),
|
|
|
|
VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
|
|
|
|
UserDefined: map[string]string{xhttp.MinIOReplicationResetStatus: fmt.Sprintf("%s;abc", UTCNow().AddDate(0, -1, 0).String())},
|
|
|
|
},
|
|
|
|
dsc: ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", true, false)}},
|
|
|
|
rcfg: replicationConfig{
|
|
|
|
remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
|
|
|
|
Arn: "arn1",
|
|
|
|
ResetID: "xyz",
|
|
|
|
ResetBeforeDate: UTCNow(),
|
|
|
|
}}},
|
|
|
|
},
|
2021-09-18 16:31:35 -04:00
|
|
|
|
2022-01-02 12:15:06 -05:00
|
|
|
expectedSync: true,
|
2021-09-18 16:31:35 -04:00
|
|
|
},
|
|
|
|
|
2022-01-02 12:15:06 -05:00
|
|
|
{ // 8. existing object replication enabled, versioning enabled, replication status Complete & reset ID present
|
|
|
|
name: "existing object replication enabled - reset in progress for an object in Completed status",
|
|
|
|
info: ObjectInfo{
|
|
|
|
Size: 100,
|
|
|
|
ReplicationStatusInternal: "arn1:COMPLETED;",
|
|
|
|
ReplicationStatus: replication.Completed,
|
|
|
|
VersionID: "a3348c34-c352-4498-82f0-1098e8b34df8",
|
|
|
|
UserDefined: map[string]string{xhttp.MinIOReplicationResetStatus: fmt.Sprintf("%s;abc", UTCNow().AddDate(0, -1, 0).String())},
|
|
|
|
},
|
|
|
|
expectedSync: true,
|
|
|
|
dsc: ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", true, false)}},
|
|
|
|
rcfg: replicationConfig{
|
|
|
|
remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
|
|
|
|
Arn: "arn1",
|
|
|
|
ResetID: "xyz",
|
|
|
|
ResetBeforeDate: UTCNow(),
|
|
|
|
}}},
|
|
|
|
},
|
2021-09-18 16:31:35 -04:00
|
|
|
},
|
2022-01-02 12:15:06 -05:00
|
|
|
{ // 9. existing object replication enabled, versioning enabled, replication status Pending & reset ID different
|
|
|
|
name: "existing object replication enabled, newer reset in progress on object in Pending replication status",
|
|
|
|
info: ObjectInfo{
|
|
|
|
Size: 100,
|
|
|
|
ReplicationStatusInternal: "arn1:PENDING;",
|
|
|
|
|
|
|
|
ReplicationStatus: replication.Pending,
|
|
|
|
VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
|
|
|
|
UserDefined: map[string]string{xhttp.MinIOReplicationResetStatus: fmt.Sprintf("%s;%s", UTCNow().AddDate(0, 0, -1).Format(http.TimeFormat), "abc")},
|
|
|
|
ModTime: UTCNow().AddDate(0, 0, -2),
|
|
|
|
},
|
|
|
|
expectedSync: true,
|
|
|
|
dsc: ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", true, false)}},
|
|
|
|
rcfg: replicationConfig{
|
|
|
|
remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
|
|
|
|
Arn: "arn1",
|
|
|
|
ResetID: "xyz",
|
|
|
|
ResetBeforeDate: UTCNow(),
|
|
|
|
}}},
|
|
|
|
},
|
2021-06-01 22:59:11 -04:00
|
|
|
},
|
2022-01-02 12:15:06 -05:00
|
|
|
{ // 10. existing object replication enabled, versioning enabled, replication status Complete & reset done
|
|
|
|
name: "reset done on object in Completed Status - ineligbile for re-replication",
|
|
|
|
info: ObjectInfo{
|
|
|
|
Size: 100,
|
|
|
|
ReplicationStatusInternal: "arn1:COMPLETED;",
|
|
|
|
ReplicationStatus: replication.Completed,
|
|
|
|
VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
|
|
|
|
UserDefined: map[string]string{xhttp.MinIOReplicationResetStatus: fmt.Sprintf("%s;%s", start.Format(http.TimeFormat), "xyz")},
|
|
|
|
},
|
|
|
|
expectedSync: false,
|
|
|
|
dsc: ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", true, false)}},
|
|
|
|
rcfg: replicationConfig{
|
|
|
|
remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
|
|
|
|
Arn: "arn1",
|
|
|
|
ResetID: "xyz",
|
|
|
|
ResetBeforeDate: start,
|
|
|
|
}}},
|
|
|
|
},
|
2021-09-18 16:31:35 -04:00
|
|
|
},
|
2022-01-02 12:15:06 -05:00
|
|
|
}
|
|
|
|
)
|
2021-06-01 22:59:11 -04:00
|
|
|
|
|
|
|
func TestReplicationResyncwrapper(t *testing.T) {
|
|
|
|
for i, test := range replicationConfigTests2 {
|
2023-09-16 05:28:06 -04:00
|
|
|
if sync := test.rcfg.resync(test.info, test.dsc, test.tgtStatuses); sync.mustResync() != test.expectedSync {
|
2021-09-18 16:31:35 -04:00
|
|
|
t.Errorf("%s (%s): Replicationresync got %t , want %t", fmt.Sprintf("Test%d - %s", i+1, time.Now().Format(http.TimeFormat)), test.name, sync.mustResync(), test.expectedSync)
|
2021-06-01 22:59:11 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|