github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/bucket-replication_test.go (about)

     1  // Copyright (c) 2015-2021 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package cmd
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"net/http"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/minio/madmin-go/v3"
    28  	"github.com/minio/minio/internal/bucket/replication"
    29  	xhttp "github.com/minio/minio/internal/http"
    30  )
    31  
    32  var configs = []replication.Config{
    33  	{ // Config0 - Replication config has no filters, existing object replication enabled
    34  		Rules: []replication.Rule{
    35  			{
    36  				Status:                    replication.Enabled,
    37  				Priority:                  1,
    38  				DeleteMarkerReplication:   replication.DeleteMarkerReplication{Status: replication.Enabled},
    39  				DeleteReplication:         replication.DeleteReplication{Status: replication.Enabled},
    40  				Filter:                    replication.Filter{},
    41  				ExistingObjectReplication: replication.ExistingObjectReplication{Status: replication.Enabled},
    42  				SourceSelectionCriteria: replication.SourceSelectionCriteria{
    43  					ReplicaModifications: replication.ReplicaModifications{Status: replication.Enabled},
    44  				},
    45  			},
    46  		},
    47  	},
    48  }
    49  
    50  var replicationConfigTests = []struct {
    51  	info         ObjectInfo
    52  	name         string
    53  	rcfg         replicationConfig
    54  	dsc          ReplicateDecision
    55  	tgtStatuses  map[string]replication.StatusType
    56  	expectedSync bool
    57  }{
    58  	{ // 1. no replication config
    59  		name:         "no replication config",
    60  		info:         ObjectInfo{Size: 100},
    61  		rcfg:         replicationConfig{Config: nil},
    62  		expectedSync: false,
    63  	},
    64  	{ // 2. existing object replication config enabled, no versioning
    65  		name:         "existing object replication config enabled, no versioning",
    66  		info:         ObjectInfo{Size: 100},
    67  		rcfg:         replicationConfig{Config: &configs[0]},
    68  		expectedSync: false,
    69  	},
    70  	{ // 3. existing object replication config enabled, versioning suspended
    71  		name:         "existing object replication config enabled, versioning suspended",
    72  		info:         ObjectInfo{Size: 100, VersionID: nullVersionID},
    73  		rcfg:         replicationConfig{Config: &configs[0]},
    74  		expectedSync: false,
    75  	},
    76  	{ // 4. existing object replication enabled, versioning enabled; no reset in progress
    77  		name: "existing object replication enabled, versioning enabled; no reset in progress",
    78  		info: ObjectInfo{
    79  			Size:              100,
    80  			ReplicationStatus: replication.Completed,
    81  			VersionID:         "a3348c34-c352-4498-82f0-1098e8b34df9",
    82  		},
    83  		rcfg:         replicationConfig{Config: &configs[0]},
    84  		expectedSync: false,
    85  	},
    86  }
    87  
    88  func TestReplicationResync(t *testing.T) {
    89  	ctx := context.Background()
    90  	for i, test := range replicationConfigTests {
    91  		if sync := test.rcfg.Resync(ctx, test.info, test.dsc, test.tgtStatuses); sync.mustResync() != test.expectedSync {
    92  			t.Errorf("Test%d (%s): Resync  got %t , want %t", i+1, test.name, sync.mustResync(), test.expectedSync)
    93  		}
    94  	}
    95  }
    96  
    97  var (
    98  	start                   = UTCNow().AddDate(0, 0, -1)
    99  	replicationConfigTests2 = []struct {
   100  		info         ObjectInfo
   101  		name         string
   102  		rcfg         replicationConfig
   103  		dsc          ReplicateDecision
   104  		tgtStatuses  map[string]replication.StatusType
   105  		expectedSync bool
   106  	}{
   107  		{ // Cases 1-4: existing object replication enabled, versioning enabled, no reset - replication status varies
   108  			// 1: Pending replication
   109  			name: "existing object replication on object in Pending replication status",
   110  			info: ObjectInfo{
   111  				Size:                      100,
   112  				ReplicationStatusInternal: "arn1:PENDING;",
   113  				ReplicationStatus:         replication.Pending,
   114  				VersionID:                 "a3348c34-c352-4498-82f0-1098e8b34df9",
   115  			},
   116  			rcfg: replicationConfig{remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
   117  				Arn: "arn1",
   118  			}}}},
   119  			dsc:          ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", true, false)}},
   120  			expectedSync: true,
   121  		},
   122  
   123  		{ // 2. replication status Failed
   124  			name: "existing object replication on object in Failed replication status",
   125  			info: ObjectInfo{
   126  				Size:                      100,
   127  				ReplicationStatusInternal: "arn1:FAILED",
   128  				ReplicationStatus:         replication.Failed,
   129  				VersionID:                 "a3348c34-c352-4498-82f0-1098e8b34df9",
   130  			},
   131  			dsc: ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", true, false)}},
   132  			rcfg: replicationConfig{remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
   133  				Arn: "arn1",
   134  			}}}},
   135  			expectedSync: true,
   136  		},
   137  		{ // 3. replication status unset
   138  			name: "existing object replication on pre-existing unreplicated object",
   139  			info: ObjectInfo{
   140  				Size:              100,
   141  				ReplicationStatus: replication.StatusType(""),
   142  				VersionID:         "a3348c34-c352-4498-82f0-1098e8b34df9",
   143  			},
   144  			rcfg: replicationConfig{remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
   145  				Arn: "arn1",
   146  			}}}},
   147  			dsc:          ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", true, false)}},
   148  			expectedSync: true,
   149  		},
   150  		{ // 4. replication status Complete
   151  			name: "existing object replication on object in Completed replication status",
   152  			info: ObjectInfo{
   153  				Size:                      100,
   154  				ReplicationStatusInternal: "arn1:COMPLETED",
   155  				ReplicationStatus:         replication.Completed,
   156  				VersionID:                 "a3348c34-c352-4498-82f0-1098e8b34df9",
   157  			},
   158  			dsc: ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", false, false)}},
   159  			rcfg: replicationConfig{remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
   160  				Arn: "arn1",
   161  			}}}},
   162  			expectedSync: false,
   163  		},
   164  		{ // 5. existing object replication enabled, versioning enabled, replication status Pending & reset ID present
   165  			name: "existing object replication with reset in progress and object in Pending status",
   166  			info: ObjectInfo{
   167  				Size:                      100,
   168  				ReplicationStatusInternal: "arn1:PENDING;",
   169  				ReplicationStatus:         replication.Pending,
   170  				VersionID:                 "a3348c34-c352-4498-82f0-1098e8b34df9",
   171  				UserDefined:               map[string]string{xhttp.MinIOReplicationResetStatus: fmt.Sprintf("%s;abc", UTCNow().AddDate(0, -1, 0).String())},
   172  			},
   173  			expectedSync: true,
   174  			dsc:          ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", true, false)}},
   175  			rcfg: replicationConfig{
   176  				remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
   177  					Arn:             "arn1",
   178  					ResetID:         "xyz",
   179  					ResetBeforeDate: UTCNow(),
   180  				}}},
   181  			},
   182  		},
   183  		{ // 6. existing object replication enabled, versioning enabled, replication status Failed & reset ID present
   184  			name: "existing object replication with reset in progress and object in Failed status",
   185  			info: ObjectInfo{
   186  				Size:                      100,
   187  				ReplicationStatusInternal: "arn1:FAILED;",
   188  				ReplicationStatus:         replication.Failed,
   189  				VersionID:                 "a3348c34-c352-4498-82f0-1098e8b34df9",
   190  				UserDefined:               map[string]string{xhttp.MinIOReplicationResetStatus: fmt.Sprintf("%s;abc", UTCNow().AddDate(0, -1, 0).String())},
   191  			},
   192  			dsc: ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", true, false)}},
   193  			rcfg: replicationConfig{
   194  				remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
   195  					Arn:             "arn1",
   196  					ResetID:         "xyz",
   197  					ResetBeforeDate: UTCNow(),
   198  				}}},
   199  			},
   200  			expectedSync: true,
   201  		},
   202  		{ // 7. existing object replication enabled, versioning enabled, replication status unset & reset ID present
   203  			name: "existing object replication with reset in progress and object never replicated before",
   204  			info: ObjectInfo{
   205  				Size:              100,
   206  				ReplicationStatus: replication.StatusType(""),
   207  				VersionID:         "a3348c34-c352-4498-82f0-1098e8b34df9",
   208  				UserDefined:       map[string]string{xhttp.MinIOReplicationResetStatus: fmt.Sprintf("%s;abc", UTCNow().AddDate(0, -1, 0).String())},
   209  			},
   210  			dsc: ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", true, false)}},
   211  			rcfg: replicationConfig{
   212  				remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
   213  					Arn:             "arn1",
   214  					ResetID:         "xyz",
   215  					ResetBeforeDate: UTCNow(),
   216  				}}},
   217  			},
   218  
   219  			expectedSync: true,
   220  		},
   221  
   222  		{ // 8. existing object replication enabled, versioning enabled, replication status Complete & reset ID present
   223  			name: "existing object replication enabled - reset in progress for an object in Completed status",
   224  			info: ObjectInfo{
   225  				Size:                      100,
   226  				ReplicationStatusInternal: "arn1:COMPLETED;",
   227  				ReplicationStatus:         replication.Completed,
   228  				VersionID:                 "a3348c34-c352-4498-82f0-1098e8b34df8",
   229  				UserDefined:               map[string]string{xhttp.MinIOReplicationResetStatus: fmt.Sprintf("%s;abc", UTCNow().AddDate(0, -1, 0).String())},
   230  			},
   231  			expectedSync: true,
   232  			dsc:          ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", true, false)}},
   233  			rcfg: replicationConfig{
   234  				remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
   235  					Arn:             "arn1",
   236  					ResetID:         "xyz",
   237  					ResetBeforeDate: UTCNow(),
   238  				}}},
   239  			},
   240  		},
   241  		{ // 9. existing object replication enabled, versioning enabled, replication status Pending & reset ID different
   242  			name: "existing object replication enabled, newer reset in progress on object in Pending replication status",
   243  			info: ObjectInfo{
   244  				Size:                      100,
   245  				ReplicationStatusInternal: "arn1:PENDING;",
   246  
   247  				ReplicationStatus: replication.Pending,
   248  				VersionID:         "a3348c34-c352-4498-82f0-1098e8b34df9",
   249  				UserDefined:       map[string]string{xhttp.MinIOReplicationResetStatus: fmt.Sprintf("%s;%s", UTCNow().AddDate(0, 0, -1).Format(http.TimeFormat), "abc")},
   250  				ModTime:           UTCNow().AddDate(0, 0, -2),
   251  			},
   252  			expectedSync: true,
   253  			dsc:          ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", true, false)}},
   254  			rcfg: replicationConfig{
   255  				remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
   256  					Arn:             "arn1",
   257  					ResetID:         "xyz",
   258  					ResetBeforeDate: UTCNow(),
   259  				}}},
   260  			},
   261  		},
   262  		{ // 10. existing object replication enabled, versioning enabled, replication status Complete & reset done
   263  			name: "reset done on object in Completed Status - ineligbile for re-replication",
   264  			info: ObjectInfo{
   265  				Size:                      100,
   266  				ReplicationStatusInternal: "arn1:COMPLETED;",
   267  				ReplicationStatus:         replication.Completed,
   268  				VersionID:                 "a3348c34-c352-4498-82f0-1098e8b34df9",
   269  				UserDefined:               map[string]string{xhttp.MinIOReplicationResetStatus: fmt.Sprintf("%s;%s", start.Format(http.TimeFormat), "xyz")},
   270  			},
   271  			expectedSync: false,
   272  			dsc:          ReplicateDecision{targetsMap: map[string]replicateTargetDecision{"arn1": newReplicateTargetDecision("arn1", true, false)}},
   273  			rcfg: replicationConfig{
   274  				remotes: &madmin.BucketTargets{Targets: []madmin.BucketTarget{{
   275  					Arn:             "arn1",
   276  					ResetID:         "xyz",
   277  					ResetBeforeDate: start,
   278  				}}},
   279  			},
   280  		},
   281  	}
   282  )
   283  
   284  func TestReplicationResyncwrapper(t *testing.T) {
   285  	for i, test := range replicationConfigTests2 {
   286  		if sync := test.rcfg.resync(test.info, test.dsc, test.tgtStatuses); sync.mustResync() != test.expectedSync {
   287  			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)
   288  		}
   289  	}
   290  }