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 }