History

Add support for existing object replication. (#12109)

Also adding an API to allow resyncing replication when existing object replication is enabled and the remote target is entirely lost. With the `mc replicate reset` command, the objects that are eligible for replication as per the replication config will be resynced to target if existing object replication is enabled on the rule.

Poorna Krishnamoorthy committed 5 years ago

Commit dbea8d21 parent 1f262da19 files · +928 −334Browse files

Unified · Split

19 files
  1. cmd/api-errors.go +6 −0
  2. cmd/api-router.go +3 −0
  3. cmd/apierrorcode_string.go +224 −223
  4. cmd/bucket-handlers.go +89 −1
  5. cmd/bucket-replication.go +201 −43
  6. cmd/bucket-replication_test.go +209 −0
  7. cmd/data-scanner.go +50 −22
  8. cmd/data-usage-cache.go +2 −1
  9. cmd/object-api-datatypes.go +1 −0
  10. cmd/object-handlers.go +28 −15
  11. cmd/web-handlers.go +3 −3
  12. cmd/xl-storage.go +13 −1
  13. docs/bucket/replication/DESIGN.md +5 −1
  14. docs/bucket/replication/README.md +15 −0
  15. go.mod +1 −1
  16. go.sum +2 −0
  17. internal/bucket/replication/replication.go +20 −9
  18. internal/bucket/replication/rule.go +53 −14
  19. internal/http/headers.go +3 −0
cmd/api-errors.go+6−0
@@ -125,6 +125,7 @@ const ( · 124 unmodified lines
125 ErrReplicationSourceNotVersionedError125 ErrReplicationSourceNotVersionedError
126 ErrReplicationNeedsVersioningError126 ErrReplicationNeedsVersioningError
127 ErrReplicationBucketNeedsVersioningError127 ErrReplicationBucketNeedsVersioningError
128 ErrReplicationNoMatchingRuleError
128 ErrObjectRestoreAlreadyInProgress129 ErrObjectRestoreAlreadyInProgress
129 ErrNoSuchKey130 ErrNoSuchKey
130 ErrNoSuchUpload131 ErrNoSuchUpload
@@ -859,6 +860,11 @@ var errorCodes = errorCodeMap{ · 728 unmodified lines
859 Description: "Remote service connection error - please check remote service credentials and target bucket",860 Description: "Remote service connection error - please check remote service credentials and target bucket",
860 HTTPStatusCode: http.StatusNotFound,861 HTTPStatusCode: http.StatusNotFound,
861 },862 },
863 ErrReplicationNoMatchingRuleError: {
864 Code: "XMinioReplicationNoMatchingRule",
865 Description: "No matching replication rule found for this object prefix",
866 HTTPStatusCode: http.StatusBadRequest,
867 },
862 ErrBucketRemoteIdenticalToSource: {868 ErrBucketRemoteIdenticalToSource: {
863 Code: "XMinioAdminRemoteIdenticalToSource",869 Code: "XMinioAdminRemoteIdenticalToSource",
864 Description: "The remote target cannot be identical to source",870 Description: "The remote target cannot be identical to source",
cmd/api-router.go+3−0
@@ -394,6 +394,9 @@ func registerAPIRouter(router *mux.Router) { · 393 unmodified lines
394 // PutBucketNotification394 // PutBucketNotification
395 router.Methods(http.MethodPut).HandlerFunc(395 router.Methods(http.MethodPut).HandlerFunc(
396 collectAPIStats("putbucketnotification", maxClients(httpTraceAll(api.PutBucketNotificationHandler)))).Queries("notification", "")396 collectAPIStats("putbucketnotification", maxClients(httpTraceAll(api.PutBucketNotificationHandler)))).Queries("notification", "")
397 // ResetBucketReplicationState - MinIO extension API
398 router.Methods(http.MethodPut).HandlerFunc(
399 collectAPIStats("resetbucketreplicationstate", maxClients(httpTraceAll(api.ResetBucketReplicationStateHandler)))).Queries("replication-reset", "")
397 // PutBucket400 // PutBucket
398 router.Methods(http.MethodPut).HandlerFunc(401 router.Methods(http.MethodPut).HandlerFunc(
399 collectAPIStats("putbucket", maxClients(httpTraceAll(api.PutBucketHandler))))402 collectAPIStats("putbucket", maxClients(httpTraceAll(api.PutBucketHandler))))
cmd/apierrorcode_string.go+224−223
@@ -62,232 +62,233 @@ func _() { · 61 unmodified lines
62 _ = x[ErrReplicationSourceNotVersionedError-51]62 _ = x[ErrReplicationSourceNotVersionedError-51]
63 _ = x[ErrReplicationNeedsVersioningError-52]63 _ = x[ErrReplicationNeedsVersioningError-52]
64 _ = x[ErrReplicationBucketNeedsVersioningError-53]64 _ = x[ErrReplicationBucketNeedsVersioningError-53]
65 _ = x[ErrObjectRestoreAlreadyInProgress-54]65 _ = x[ErrReplicationNoMatchingRuleError-54]
66 _ = x[ErrNoSuchKey-55]66 _ = x[ErrObjectRestoreAlreadyInProgress-55]
67 _ = x[ErrNoSuchUpload-56]67 _ = x[ErrNoSuchKey-56]
68 _ = x[ErrInvalidVersionID-57]68 _ = x[ErrNoSuchUpload-57]
69 _ = x[ErrNoSuchVersion-58]69 _ = x[ErrInvalidVersionID-58]
70 _ = x[ErrNotImplemented-59]70 _ = x[ErrNoSuchVersion-59]
71 _ = x[ErrPreconditionFailed-60]71 _ = x[ErrNotImplemented-60]
72 _ = x[ErrRequestTimeTooSkewed-61]72 _ = x[ErrPreconditionFailed-61]
73 _ = x[ErrSignatureDoesNotMatch-62]73 _ = x[ErrRequestTimeTooSkewed-62]
74 _ = x[ErrMethodNotAllowed-63]74 _ = x[ErrSignatureDoesNotMatch-63]
75 _ = x[ErrInvalidPart-64]75 _ = x[ErrMethodNotAllowed-64]
76 _ = x[ErrInvalidPartOrder-65]76 _ = x[ErrInvalidPart-65]
77 _ = x[ErrAuthorizationHeaderMalformed-66]77 _ = x[ErrInvalidPartOrder-66]
78 _ = x[ErrMalformedPOSTRequest-67]78 _ = x[ErrAuthorizationHeaderMalformed-67]
79 _ = x[ErrPOSTFileRequired-68]79 _ = x[ErrMalformedPOSTRequest-68]
80 _ = x[ErrSignatureVersionNotSupported-69]80 _ = x[ErrPOSTFileRequired-69]
81 _ = x[ErrBucketNotEmpty-70]81 _ = x[ErrSignatureVersionNotSupported-70]
82 _ = x[ErrAllAccessDisabled-71]82 _ = x[ErrBucketNotEmpty-71]
83 _ = x[ErrMalformedPolicy-72]83 _ = x[ErrAllAccessDisabled-72]
84 _ = x[ErrMissingFields-73]84 _ = x[ErrMalformedPolicy-73]
85 _ = x[ErrMissingCredTag-74]85 _ = x[ErrMissingFields-74]
86 _ = x[ErrCredMalformed-75]86 _ = x[ErrMissingCredTag-75]
87 _ = x[ErrInvalidRegion-76]87 _ = x[ErrCredMalformed-76]
88 _ = x[ErrInvalidServiceS3-77]88 _ = x[ErrInvalidRegion-77]
89 _ = x[ErrInvalidServiceSTS-78]89 _ = x[ErrInvalidServiceS3-78]
90 _ = x[ErrInvalidRequestVersion-79]90 _ = x[ErrInvalidServiceSTS-79]
91 _ = x[ErrMissingSignTag-80]91 _ = x[ErrInvalidRequestVersion-80]
92 _ = x[ErrMissingSignHeadersTag-81]92 _ = x[ErrMissingSignTag-81]
93 _ = x[ErrMalformedDate-82]93 _ = x[ErrMissingSignHeadersTag-82]
94 _ = x[ErrMalformedPresignedDate-83]94 _ = x[ErrMalformedDate-83]
95 _ = x[ErrMalformedCredentialDate-84]95 _ = x[ErrMalformedPresignedDate-84]
96 _ = x[ErrMalformedCredentialRegion-85]96 _ = x[ErrMalformedCredentialDate-85]
97 _ = x[ErrMalformedExpires-86]97 _ = x[ErrMalformedCredentialRegion-86]
98 _ = x[ErrNegativeExpires-87]98 _ = x[ErrMalformedExpires-87]
99 _ = x[ErrAuthHeaderEmpty-88]99 _ = x[ErrNegativeExpires-88]
100 _ = x[ErrExpiredPresignRequest-89]100 _ = x[ErrAuthHeaderEmpty-89]
101 _ = x[ErrRequestNotReadyYet-90]101 _ = x[ErrExpiredPresignRequest-90]
102 _ = x[ErrUnsignedHeaders-91]102 _ = x[ErrRequestNotReadyYet-91]
103 _ = x[ErrMissingDateHeader-92]103 _ = x[ErrUnsignedHeaders-92]
104 _ = x[ErrInvalidQuerySignatureAlgo-93]104 _ = x[ErrMissingDateHeader-93]
105 _ = x[ErrInvalidQueryParams-94]105 _ = x[ErrInvalidQuerySignatureAlgo-94]
106 _ = x[ErrBucketAlreadyOwnedByYou-95]106 _ = x[ErrInvalidQueryParams-95]
107 _ = x[ErrInvalidDuration-96]107 _ = x[ErrBucketAlreadyOwnedByYou-96]
108 _ = x[ErrBucketAlreadyExists-97]108 _ = x[ErrInvalidDuration-97]
109 _ = x[ErrMetadataTooLarge-98]109 _ = x[ErrBucketAlreadyExists-98]
110 _ = x[ErrUnsupportedMetadata-99]110 _ = x[ErrMetadataTooLarge-99]
111 _ = x[ErrMaximumExpires-100]111 _ = x[ErrUnsupportedMetadata-100]
112 _ = x[ErrSlowDown-101]112 _ = x[ErrMaximumExpires-101]
113 _ = x[ErrInvalidPrefixMarker-102]113 _ = x[ErrSlowDown-102]
114 _ = x[ErrBadRequest-103]114 _ = x[ErrInvalidPrefixMarker-103]
115 _ = x[ErrKeyTooLongError-104]115 _ = x[ErrBadRequest-104]
116 _ = x[ErrInvalidBucketObjectLockConfiguration-105]116 _ = x[ErrKeyTooLongError-105]
117 _ = x[ErrObjectLockConfigurationNotFound-106]117 _ = x[ErrInvalidBucketObjectLockConfiguration-106]
118 _ = x[ErrObjectLockConfigurationNotAllowed-107]118 _ = x[ErrObjectLockConfigurationNotFound-107]
119 _ = x[ErrNoSuchObjectLockConfiguration-108]119 _ = x[ErrObjectLockConfigurationNotAllowed-108]
120 _ = x[ErrObjectLocked-109]120 _ = x[ErrNoSuchObjectLockConfiguration-109]
121 _ = x[ErrInvalidRetentionDate-110]121 _ = x[ErrObjectLocked-110]
122 _ = x[ErrPastObjectLockRetainDate-111]122 _ = x[ErrInvalidRetentionDate-111]
123 _ = x[ErrUnknownWORMModeDirective-112]123 _ = x[ErrPastObjectLockRetainDate-112]
124 _ = x[ErrBucketTaggingNotFound-113]124 _ = x[ErrUnknownWORMModeDirective-113]
125 _ = x[ErrObjectLockInvalidHeaders-114]125 _ = x[ErrBucketTaggingNotFound-114]
126 _ = x[ErrInvalidTagDirective-115]126 _ = x[ErrObjectLockInvalidHeaders-115]
127 _ = x[ErrInvalidEncryptionMethod-116]127 _ = x[ErrInvalidTagDirective-116]
128 _ = x[ErrInsecureSSECustomerRequest-117]128 _ = x[ErrInvalidEncryptionMethod-117]
129 _ = x[ErrSSEMultipartEncrypted-118]129 _ = x[ErrInsecureSSECustomerRequest-118]
130 _ = x[ErrSSEEncryptedObject-119]130 _ = x[ErrSSEMultipartEncrypted-119]
131 _ = x[ErrInvalidEncryptionParameters-120]131 _ = x[ErrSSEEncryptedObject-120]
132 _ = x[ErrInvalidSSECustomerAlgorithm-121]132 _ = x[ErrInvalidEncryptionParameters-121]
133 _ = x[ErrInvalidSSECustomerKey-122]133 _ = x[ErrInvalidSSECustomerAlgorithm-122]
134 _ = x[ErrMissingSSECustomerKey-123]134 _ = x[ErrInvalidSSECustomerKey-123]
135 _ = x[ErrMissingSSECustomerKeyMD5-124]135 _ = x[ErrMissingSSECustomerKey-124]
136 _ = x[ErrSSECustomerKeyMD5Mismatch-125]136 _ = x[ErrMissingSSECustomerKeyMD5-125]
137 _ = x[ErrInvalidSSECustomerParameters-126]137 _ = x[ErrSSECustomerKeyMD5Mismatch-126]
138 _ = x[ErrIncompatibleEncryptionMethod-127]138 _ = x[ErrInvalidSSECustomerParameters-127]
139 _ = x[ErrKMSNotConfigured-128]139 _ = x[ErrIncompatibleEncryptionMethod-128]
140 _ = x[ErrNoAccessKey-129]140 _ = x[ErrKMSNotConfigured-129]
141 _ = x[ErrInvalidToken-130]141 _ = x[ErrNoAccessKey-130]
142 _ = x[ErrEventNotification-131]142 _ = x[ErrInvalidToken-131]
143 _ = x[ErrARNNotification-132]143 _ = x[ErrEventNotification-132]
144 _ = x[ErrRegionNotification-133]144 _ = x[ErrARNNotification-133]
145 _ = x[ErrOverlappingFilterNotification-134]145 _ = x[ErrRegionNotification-134]
146 _ = x[ErrFilterNameInvalid-135]146 _ = x[ErrOverlappingFilterNotification-135]
147 _ = x[ErrFilterNamePrefix-136]147 _ = x[ErrFilterNameInvalid-136]
148 _ = x[ErrFilterNameSuffix-137]148 _ = x[ErrFilterNamePrefix-137]
149 _ = x[ErrFilterValueInvalid-138]149 _ = x[ErrFilterNameSuffix-138]
150 _ = x[ErrOverlappingConfigs-139]150 _ = x[ErrFilterValueInvalid-139]
151 _ = x[ErrUnsupportedNotification-140]151 _ = x[ErrOverlappingConfigs-140]
152 _ = x[ErrContentSHA256Mismatch-141]152 _ = x[ErrUnsupportedNotification-141]
153 _ = x[ErrReadQuorum-142]153 _ = x[ErrContentSHA256Mismatch-142]
154 _ = x[ErrWriteQuorum-143]154 _ = x[ErrReadQuorum-143]
155 _ = x[ErrParentIsObject-144]155 _ = x[ErrWriteQuorum-144]
156 _ = x[ErrStorageFull-145]156 _ = x[ErrParentIsObject-145]
157 _ = x[ErrRequestBodyParse-146]157 _ = x[ErrStorageFull-146]
158 _ = x[ErrObjectExistsAsDirectory-147]158 _ = x[ErrRequestBodyParse-147]
159 _ = x[ErrInvalidObjectName-148]159 _ = x[ErrObjectExistsAsDirectory-148]
160 _ = x[ErrInvalidObjectNamePrefixSlash-149]160 _ = x[ErrInvalidObjectName-149]
161 _ = x[ErrInvalidResourceName-150]161 _ = x[ErrInvalidObjectNamePrefixSlash-150]
162 _ = x[ErrServerNotInitialized-151]162 _ = x[ErrInvalidResourceName-151]
163 _ = x[ErrOperationTimedOut-152]163 _ = x[ErrServerNotInitialized-152]
164 _ = x[ErrClientDisconnected-153]164 _ = x[ErrOperationTimedOut-153]
165 _ = x[ErrOperationMaxedOut-154]165 _ = x[ErrClientDisconnected-154]
166 _ = x[ErrInvalidRequest-155]166 _ = x[ErrOperationMaxedOut-155]
167 _ = x[ErrTransitionStorageClassNotFoundError-156]167 _ = x[ErrInvalidRequest-156]
168 _ = x[ErrInvalidStorageClass-157]168 _ = x[ErrTransitionStorageClassNotFoundError-157]
169 _ = x[ErrBackendDown-158]169 _ = x[ErrInvalidStorageClass-158]
170 _ = x[ErrMalformedJSON-159]170 _ = x[ErrBackendDown-159]
171 _ = x[ErrAdminNoSuchUser-160]171 _ = x[ErrMalformedJSON-160]
172 _ = x[ErrAdminNoSuchGroup-161]172 _ = x[ErrAdminNoSuchUser-161]
173 _ = x[ErrAdminGroupNotEmpty-162]173 _ = x[ErrAdminNoSuchGroup-162]
174 _ = x[ErrAdminNoSuchPolicy-163]174 _ = x[ErrAdminGroupNotEmpty-163]
175 _ = x[ErrAdminInvalidArgument-164]175 _ = x[ErrAdminNoSuchPolicy-164]
176 _ = x[ErrAdminInvalidAccessKey-165]176 _ = x[ErrAdminInvalidArgument-165]
177 _ = x[ErrAdminInvalidSecretKey-166]177 _ = x[ErrAdminInvalidAccessKey-166]
178 _ = x[ErrAdminConfigNoQuorum-167]178 _ = x[ErrAdminInvalidSecretKey-167]
179 _ = x[ErrAdminConfigTooLarge-168]179 _ = x[ErrAdminConfigNoQuorum-168]
180 _ = x[ErrAdminConfigBadJSON-169]180 _ = x[ErrAdminConfigTooLarge-169]
181 _ = x[ErrAdminConfigDuplicateKeys-170]181 _ = x[ErrAdminConfigBadJSON-170]
182 _ = x[ErrAdminCredentialsMismatch-171]182 _ = x[ErrAdminConfigDuplicateKeys-171]
183 _ = x[ErrInsecureClientRequest-172]183 _ = x[ErrAdminCredentialsMismatch-172]
184 _ = x[ErrObjectTampered-173]184 _ = x[ErrInsecureClientRequest-173]
185 _ = x[ErrAdminBucketQuotaExceeded-174]185 _ = x[ErrObjectTampered-174]
186 _ = x[ErrAdminNoSuchQuotaConfiguration-175]186 _ = x[ErrAdminBucketQuotaExceeded-175]
187 _ = x[ErrHealNotImplemented-176]187 _ = x[ErrAdminNoSuchQuotaConfiguration-176]
188 _ = x[ErrHealNoSuchProcess-177]188 _ = x[ErrHealNotImplemented-177]
189 _ = x[ErrHealInvalidClientToken-178]189 _ = x[ErrHealNoSuchProcess-178]
190 _ = x[ErrHealMissingBucket-179]190 _ = x[ErrHealInvalidClientToken-179]
191 _ = x[ErrHealAlreadyRunning-180]191 _ = x[ErrHealMissingBucket-180]
192 _ = x[ErrHealOverlappingPaths-181]192 _ = x[ErrHealAlreadyRunning-181]
193 _ = x[ErrIncorrectContinuationToken-182]193 _ = x[ErrHealOverlappingPaths-182]
194 _ = x[ErrEmptyRequestBody-183]194 _ = x[ErrIncorrectContinuationToken-183]
195 _ = x[ErrUnsupportedFunction-184]195 _ = x[ErrEmptyRequestBody-184]
196 _ = x[ErrInvalidExpressionType-185]196 _ = x[ErrUnsupportedFunction-185]
197 _ = x[ErrBusy-186]197 _ = x[ErrInvalidExpressionType-186]
198 _ = x[ErrUnauthorizedAccess-187]198 _ = x[ErrBusy-187]
199 _ = x[ErrExpressionTooLong-188]199 _ = x[ErrUnauthorizedAccess-188]
200 _ = x[ErrIllegalSQLFunctionArgument-189]200 _ = x[ErrExpressionTooLong-189]
201 _ = x[ErrInvalidKeyPath-190]201 _ = x[ErrIllegalSQLFunctionArgument-190]
202 _ = x[ErrInvalidCompressionFormat-191]202 _ = x[ErrInvalidKeyPath-191]
203 _ = x[ErrInvalidFileHeaderInfo-192]203 _ = x[ErrInvalidCompressionFormat-192]
204 _ = x[ErrInvalidJSONType-193]204 _ = x[ErrInvalidFileHeaderInfo-193]
205 _ = x[ErrInvalidQuoteFields-194]205 _ = x[ErrInvalidJSONType-194]
206 _ = x[ErrInvalidRequestParameter-195]206 _ = x[ErrInvalidQuoteFields-195]
207 _ = x[ErrInvalidDataType-196]207 _ = x[ErrInvalidRequestParameter-196]
208 _ = x[ErrInvalidTextEncoding-197]208 _ = x[ErrInvalidDataType-197]
209 _ = x[ErrInvalidDataSource-198]209 _ = x[ErrInvalidTextEncoding-198]
210 _ = x[ErrInvalidTableAlias-199]210 _ = x[ErrInvalidDataSource-199]
211 _ = x[ErrMissingRequiredParameter-200]211 _ = x[ErrInvalidTableAlias-200]
212 _ = x[ErrObjectSerializationConflict-201]212 _ = x[ErrMissingRequiredParameter-201]
213 _ = x[ErrUnsupportedSQLOperation-202]213 _ = x[ErrObjectSerializationConflict-202]
214 _ = x[ErrUnsupportedSQLStructure-203]214 _ = x[ErrUnsupportedSQLOperation-203]
215 _ = x[ErrUnsupportedSyntax-204]215 _ = x[ErrUnsupportedSQLStructure-204]
216 _ = x[ErrUnsupportedRangeHeader-205]216 _ = x[ErrUnsupportedSyntax-205]
217 _ = x[ErrLexerInvalidChar-206]217 _ = x[ErrUnsupportedRangeHeader-206]
218 _ = x[ErrLexerInvalidOperator-207]218 _ = x[ErrLexerInvalidChar-207]
219 _ = x[ErrLexerInvalidLiteral-208]219 _ = x[ErrLexerInvalidOperator-208]
220 _ = x[ErrLexerInvalidIONLiteral-209]220 _ = x[ErrLexerInvalidLiteral-209]
221 _ = x[ErrParseExpectedDatePart-210]221 _ = x[ErrLexerInvalidIONLiteral-210]
222 _ = x[ErrParseExpectedKeyword-211]222 _ = x[ErrParseExpectedDatePart-211]
223 _ = x[ErrParseExpectedTokenType-212]223 _ = x[ErrParseExpectedKeyword-212]
224 _ = x[ErrParseExpected2TokenTypes-213]224 _ = x[ErrParseExpectedTokenType-213]
225 _ = x[ErrParseExpectedNumber-214]225 _ = x[ErrParseExpected2TokenTypes-214]
226 _ = x[ErrParseExpectedRightParenBuiltinFunctionCall-215]226 _ = x[ErrParseExpectedNumber-215]
227 _ = x[ErrParseExpectedTypeName-216]227 _ = x[ErrParseExpectedRightParenBuiltinFunctionCall-216]
228 _ = x[ErrParseExpectedWhenClause-217]228 _ = x[ErrParseExpectedTypeName-217]
229 _ = x[ErrParseUnsupportedToken-218]229 _ = x[ErrParseExpectedWhenClause-218]
230 _ = x[ErrParseUnsupportedLiteralsGroupBy-219]230 _ = x[ErrParseUnsupportedToken-219]
231 _ = x[ErrParseExpectedMember-220]231 _ = x[ErrParseUnsupportedLiteralsGroupBy-220]
232 _ = x[ErrParseUnsupportedSelect-221]232 _ = x[ErrParseExpectedMember-221]
233 _ = x[ErrParseUnsupportedCase-222]233 _ = x[ErrParseUnsupportedSelect-222]
234 _ = x[ErrParseUnsupportedCaseClause-223]234 _ = x[ErrParseUnsupportedCase-223]
235 _ = x[ErrParseUnsupportedAlias-224]235 _ = x[ErrParseUnsupportedCaseClause-224]
236 _ = x[ErrParseUnsupportedSyntax-225]236 _ = x[ErrParseUnsupportedAlias-225]
237 _ = x[ErrParseUnknownOperator-226]237 _ = x[ErrParseUnsupportedSyntax-226]
238 _ = x[ErrParseMissingIdentAfterAt-227]238 _ = x[ErrParseUnknownOperator-227]
239 _ = x[ErrParseUnexpectedOperator-228]239 _ = x[ErrParseMissingIdentAfterAt-228]
240 _ = x[ErrParseUnexpectedTerm-229]240 _ = x[ErrParseUnexpectedOperator-229]
241 _ = x[ErrParseUnexpectedToken-230]241 _ = x[ErrParseUnexpectedTerm-230]
242 _ = x[ErrParseUnexpectedKeyword-231]242 _ = x[ErrParseUnexpectedToken-231]
243 _ = x[ErrParseExpectedExpression-232]243 _ = x[ErrParseUnexpectedKeyword-232]
244 _ = x[ErrParseExpectedLeftParenAfterCast-233]244 _ = x[ErrParseExpectedExpression-233]
245 _ = x[ErrParseExpectedLeftParenValueConstructor-234]245 _ = x[ErrParseExpectedLeftParenAfterCast-234]
246 _ = x[ErrParseExpectedLeftParenBuiltinFunctionCall-235]246 _ = x[ErrParseExpectedLeftParenValueConstructor-235]
247 _ = x[ErrParseExpectedArgumentDelimiter-236]247 _ = x[ErrParseExpectedLeftParenBuiltinFunctionCall-236]
248 _ = x[ErrParseCastArity-237]248 _ = x[ErrParseExpectedArgumentDelimiter-237]
249 _ = x[ErrParseInvalidTypeParam-238]249 _ = x[ErrParseCastArity-238]
250 _ = x[ErrParseEmptySelect-239]250 _ = x[ErrParseInvalidTypeParam-239]
251 _ = x[ErrParseSelectMissingFrom-240]251 _ = x[ErrParseEmptySelect-240]
252 _ = x[ErrParseExpectedIdentForGroupName-241]252 _ = x[ErrParseSelectMissingFrom-241]
253 _ = x[ErrParseExpectedIdentForAlias-242]253 _ = x[ErrParseExpectedIdentForGroupName-242]
254 _ = x[ErrParseUnsupportedCallWithStar-243]254 _ = x[ErrParseExpectedIdentForAlias-243]
255 _ = x[ErrParseNonUnaryAgregateFunctionCall-244]255 _ = x[ErrParseUnsupportedCallWithStar-244]
256 _ = x[ErrParseMalformedJoin-245]256 _ = x[ErrParseNonUnaryAgregateFunctionCall-245]
257 _ = x[ErrParseExpectedIdentForAt-246]257 _ = x[ErrParseMalformedJoin-246]
258 _ = x[ErrParseAsteriskIsNotAloneInSelectList-247]258 _ = x[ErrParseExpectedIdentForAt-247]
259 _ = x[ErrParseCannotMixSqbAndWildcardInSelectList-248]259 _ = x[ErrParseAsteriskIsNotAloneInSelectList-248]
260 _ = x[ErrParseInvalidContextForWildcardInSelectList-249]260 _ = x[ErrParseCannotMixSqbAndWildcardInSelectList-249]
261 _ = x[ErrIncorrectSQLFunctionArgumentType-250]261 _ = x[ErrParseInvalidContextForWildcardInSelectList-250]
262 _ = x[ErrValueParseFailure-251]262 _ = x[ErrIncorrectSQLFunctionArgumentType-251]
263 _ = x[ErrEvaluatorInvalidArguments-252]263 _ = x[ErrValueParseFailure-252]
264 _ = x[ErrIntegerOverflow-253]264 _ = x[ErrEvaluatorInvalidArguments-253]
265 _ = x[ErrLikeInvalidInputs-254]265 _ = x[ErrIntegerOverflow-254]
266 _ = x[ErrCastFailed-255]266 _ = x[ErrLikeInvalidInputs-255]
267 _ = x[ErrInvalidCast-256]267 _ = x[ErrCastFailed-256]
268 _ = x[ErrEvaluatorInvalidTimestampFormatPattern-257]268 _ = x[ErrInvalidCast-257]
269 _ = x[ErrEvaluatorInvalidTimestampFormatPatternSymbolForParsing-258]269 _ = x[ErrEvaluatorInvalidTimestampFormatPattern-258]
270 _ = x[ErrEvaluatorTimestampFormatPatternDuplicateFields-259]270 _ = x[ErrEvaluatorInvalidTimestampFormatPatternSymbolForParsing-259]
271 _ = x[ErrEvaluatorTimestampFormatPatternHourClockAmPmMismatch-260]271 _ = x[ErrEvaluatorTimestampFormatPatternDuplicateFields-260]
272 _ = x[ErrEvaluatorUnterminatedTimestampFormatPatternToken-261]272 _ = x[ErrEvaluatorTimestampFormatPatternHourClockAmPmMismatch-261]
273 _ = x[ErrEvaluatorInvalidTimestampFormatPatternToken-262]273 _ = x[ErrEvaluatorUnterminatedTimestampFormatPatternToken-262]
274 _ = x[ErrEvaluatorInvalidTimestampFormatPatternSymbol-263]274 _ = x[ErrEvaluatorInvalidTimestampFormatPatternToken-263]
275 _ = x[ErrEvaluatorBindingDoesNotExist-264]275 _ = x[ErrEvaluatorInvalidTimestampFormatPatternSymbol-264]
276 _ = x[ErrMissingHeaders-265]276 _ = x[ErrEvaluatorBindingDoesNotExist-265]
277 _ = x[ErrInvalidColumnIndex-266]277 _ = x[ErrMissingHeaders-266]
278 _ = x[ErrAdminConfigNotificationTargetsFailed-267]278 _ = x[ErrInvalidColumnIndex-267]
279 _ = x[ErrAdminProfilerNotEnabled-268]279 _ = x[ErrAdminConfigNotificationTargetsFailed-268]
280 _ = x[ErrInvalidDecompressedSize-269]280 _ = x[ErrAdminProfilerNotEnabled-269]
281 _ = x[ErrAddUserInvalidArgument-270]281 _ = x[ErrInvalidDecompressedSize-270]
282 _ = x[ErrAdminAccountNotEligible-271]282 _ = x[ErrAddUserInvalidArgument-271]
283 _ = x[ErrAccountNotEligible-272]283 _ = x[ErrAdminAccountNotEligible-272]
284 _ = x[ErrAdminServiceAccountNotFound-273]284 _ = x[ErrAccountNotEligible-273]
285 _ = x[ErrPostPolicyConditionInvalidFormat-274]285 _ = x[ErrAdminServiceAccountNotFound-274]
286 _ = x[ErrPostPolicyConditionInvalidFormat-275]
286}287}
287 288
288const _APIErrorCode_name = "NoneAccessDeniedBadDigestEntityTooSmallEntityTooLargePolicyTooLargeIncompleteBodyInternalErrorInvalidAccessKeyIDInvalidBucketNameInvalidDigestInvalidRangeInvalidRangePartNumberInvalidCopyPartRangeInvalidCopyPartRangeSourceInvalidMaxKeysInvalidEncodingMethodInvalidMaxUploadsInvalidMaxPartsInvalidPartNumberMarkerInvalidPartNumberInvalidRequestBodyInvalidCopySourceInvalidMetadataDirectiveInvalidCopyDestInvalidPolicyDocumentInvalidObjectStateMalformedXMLMissingContentLengthMissingContentMD5MissingRequestBodyErrorMissingSecurityHeaderNoSuchBucketNoSuchBucketPolicyNoSuchBucketLifecycleNoSuchLifecycleConfigurationNoSuchBucketSSEConfigNoSuchCORSConfigurationNoSuchWebsiteConfigurationReplicationConfigurationNotFoundErrorRemoteDestinationNotFoundErrorReplicationDestinationMissingLockRemoteTargetNotFoundErrorReplicationRemoteConnectionErrorBucketRemoteIdenticalToSourceBucketRemoteAlreadyExistsBucketRemoteLabelInUseBucketRemoteArnTypeInvalidBucketRemoteArnInvalidBucketRemoteRemoveDisallowedRemoteTargetNotVersionedErrorReplicationSourceNotVersionedErrorReplicationNeedsVersioningErrorReplicationBucketNeedsVersioningErrorObjectRestoreAlreadyInProgressNoSuchKeyNoSuchUploadInvalidVersionIDNoSuchVersionNotImplementedPreconditionFailedRequestTimeTooSkewedSignatureDoesNotMatchMethodNotAllowedInvalidPartInvalidPartOrderAuthorizationHeaderMalformedMalformedPOSTRequestPOSTFileRequiredSignatureVersionNotSupportedBucketNotEmptyAllAccessDisabledMalformedPolicyMissingFieldsMissingCredTagCredMalformedInvalidRegionInvalidServiceS3InvalidServiceSTSInvalidRequestVersionMissingSignTagMissingSignHeadersTagMalformedDateMalformedPresignedDateMalformedCredentialDateMalformedCredentialRegionMalformedExpiresNegativeExpiresAuthHeaderEmptyExpiredPresignRequestRequestNotReadyYetUnsignedHeadersMissingDateHeaderInvalidQuerySignatureAlgoInvalidQueryParamsBucketAlreadyOwnedByYouInvalidDurationBucketAlreadyExistsMetadataTooLargeUnsupportedMetadataMaximumExpiresSlowDownInvalidPrefixMarkerBadRequestKeyTooLongErrorInvalidBucketObjectLockConfigurationObjectLockConfigurationNotFoundObjectLockConfigurationNotAllowedNoSuchObjectLockConfigurationObjectLockedInvalidRetentionDatePastObjectLockRetainDateUnknownWORMModeDirectiveBucketTaggingNotFoundObjectLockInvalidHeadersInvalidTagDirectiveInvalidEncryptionMethodInsecureSSECustomerRequestSSEMultipartEncryptedSSEEncryptedObjectInvalidEncryptionParametersInvalidSSECustomerAlgorithmInvalidSSECustomerKeyMissingSSECustomerKeyMissingSSECustomerKeyMD5SSECustomerKeyMD5MismatchInvalidSSECustomerParametersIncompatibleEncryptionMethodKMSNotConfiguredNoAccessKeyInvalidTokenEventNotificationARNNotificationRegionNotificationOverlappingFilterNotificationFilterNameInvalidFilterNamePrefixFilterNameSuffixFilterValueInvalidOverlappingConfigsUnsupportedNotificationContentSHA256MismatchReadQuorumWriteQuorumParentIsObjectStorageFullRequestBodyParseObjectExistsAsDirectoryInvalidObjectNameInvalidObjectNamePrefixSlashInvalidResourceNameServerNotInitializedOperationTimedOutClientDisconnectedOperationMaxedOutInvalidRequestTransitionStorageClassNotFoundErrorInvalidStorageClassBackendDownMalformedJSONAdminNoSuchUserAdminNoSuchGroupAdminGroupNotEmptyAdminNoSuchPolicyAdminInvalidArgumentAdminInvalidAccessKeyAdminInvalidSecretKeyAdminConfigNoQuorumAdminConfigTooLargeAdminConfigBadJSONAdminConfigDuplicateKeysAdminCredentialsMismatchInsecureClientRequestObjectTamperedAdminBucketQuotaExceededAdminNoSuchQuotaConfigurationHealNotImplementedHealNoSuchProcessHealInvalidClientTokenHealMissingBucketHealAlreadyRunningHealOverlappingPathsIncorrectContinuationTokenEmptyRequestBodyUnsupportedFunctionInvalidExpressionTypeBusyUnauthorizedAccessExpressionTooLongIllegalSQLFunctionArgumentInvalidKeyPathInvalidCompressionFormatInvalidFileHeaderInfoInvalidJSONTypeInvalidQuoteFieldsInvalidRequestParameterInvalidDataTypeInvalidTextEncodingInvalidDataSourceInvalidTableAliasMissingRequiredParameterObjectSerializationConflictUnsupportedSQLOperationUnsupportedSQLStructureUnsupportedSyntaxUnsupportedRangeHeaderLexerInvalidCharLexerInvalidOperatorLexerInvalidLiteralLexerInvalidIONLiteralParseExpectedDatePartParseExpectedKeywordParseExpectedTokenTypeParseExpected2TokenTypesParseExpectedNumberParseExpectedRightParenBuiltinFunctionCallParseExpectedTypeNameParseExpectedWhenClauseParseUnsupportedTokenParseUnsupportedLiteralsGroupByParseExpectedMemberParseUnsupportedSelectParseUnsupportedCaseParseUnsupportedCaseClauseParseUnsupportedAliasParseUnsupportedSyntaxParseUnknownOperatorParseMissingIdentAfterAtParseUnexpectedOperatorParseUnexpectedTermParseUnexpectedTokenParseUnexpectedKeywordParseExpectedExpressionParseExpectedLeftParenAfterCastParseExpectedLeftParenValueConstructorParseExpectedLeftParenBuiltinFunctionCallParseExpectedArgumentDelimiterParseCastArityParseInvalidTypeParamParseEmptySelectParseSelectMissingFromParseExpectedIdentForGroupNameParseExpectedIdentForAliasParseUnsupportedCallWithStarParseNonUnaryAgregateFunctionCallParseMalformedJoinParseExpectedIdentForAtParseAsteriskIsNotAloneInSelectListParseCannotMixSqbAndWildcardInSelectListParseInvalidContextForWildcardInSelectListIncorrectSQLFunctionArgumentTypeValueParseFailureEvaluatorInvalidArgumentsIntegerOverflowLikeInvalidInputsCastFailedInvalidCastEvaluatorInvalidTimestampFormatPatternEvaluatorInvalidTimestampFormatPatternSymbolForParsingEvaluatorTimestampFormatPatternDuplicateFieldsEvaluatorTimestampFormatPatternHourClockAmPmMismatchEvaluatorUnterminatedTimestampFormatPatternTokenEvaluatorInvalidTimestampFormatPatternTokenEvaluatorInvalidTimestampFormatPatternSymbolEvaluatorBindingDoesNotExistMissingHeadersInvalidColumnIndexAdminConfigNotificationTargetsFailedAdminProfilerNotEnabledInvalidDecompressedSizeAddUserInvalidArgumentAdminAccountNotEligibleAccountNotEligibleAdminServiceAccountNotFoundPostPolicyConditionInvalidFormat"289const _APIErrorCode_name = "NoneAccessDeniedBadDigestEntityTooSmallEntityTooLargePolicyTooLargeIncompleteBodyInternalErrorInvalidAccessKeyIDInvalidBucketNameInvalidDigestInvalidRangeInvalidRangePartNumberInvalidCopyPartRangeInvalidCopyPartRangeSourceInvalidMaxKeysInvalidEncodingMethodInvalidMaxUploadsInvalidMaxPartsInvalidPartNumberMarkerInvalidPartNumberInvalidRequestBodyInvalidCopySourceInvalidMetadataDirectiveInvalidCopyDestInvalidPolicyDocumentInvalidObjectStateMalformedXMLMissingContentLengthMissingContentMD5MissingRequestBodyErrorMissingSecurityHeaderNoSuchBucketNoSuchBucketPolicyNoSuchBucketLifecycleNoSuchLifecycleConfigurationNoSuchBucketSSEConfigNoSuchCORSConfigurationNoSuchWebsiteConfigurationReplicationConfigurationNotFoundErrorRemoteDestinationNotFoundErrorReplicationDestinationMissingLockRemoteTargetNotFoundErrorReplicationRemoteConnectionErrorBucketRemoteIdenticalToSourceBucketRemoteAlreadyExistsBucketRemoteLabelInUseBucketRemoteArnTypeInvalidBucketRemoteArnInvalidBucketRemoteRemoveDisallowedRemoteTargetNotVersionedErrorReplicationSourceNotVersionedErrorReplicationNeedsVersioningErrorReplicationBucketNeedsVersioningErrorReplicationNoMatchingRuleErrorObjectRestoreAlreadyInProgressNoSuchKeyNoSuchUploadInvalidVersionIDNoSuchVersionNotImplementedPreconditionFailedRequestTimeTooSkewedSignatureDoesNotMatchMethodNotAllowedInvalidPartInvalidPartOrderAuthorizationHeaderMalformedMalformedPOSTRequestPOSTFileRequiredSignatureVersionNotSupportedBucketNotEmptyAllAccessDisabledMalformedPolicyMissingFieldsMissingCredTagCredMalformedInvalidRegionInvalidServiceS3InvalidServiceSTSInvalidRequestVersionMissingSignTagMissingSignHeadersTagMalformedDateMalformedPresignedDateMalformedCredentialDateMalformedCredentialRegionMalformedExpiresNegativeExpiresAuthHeaderEmptyExpiredPresignRequestRequestNotReadyYetUnsignedHeadersMissingDateHeaderInvalidQuerySignatureAlgoInvalidQueryParamsBucketAlreadyOwnedByYouInvalidDurationBucketAlreadyExistsMetadataTooLargeUnsupportedMetadataMaximumExpiresSlowDownInvalidPrefixMarkerBadRequestKeyTooLongErrorInvalidBucketObjectLockConfigurationObjectLockConfigurationNotFoundObjectLockConfigurationNotAllowedNoSuchObjectLockConfigurationObjectLockedInvalidRetentionDatePastObjectLockRetainDateUnknownWORMModeDirectiveBucketTaggingNotFoundObjectLockInvalidHeadersInvalidTagDirectiveInvalidEncryptionMethodInsecureSSECustomerRequestSSEMultipartEncryptedSSEEncryptedObjectInvalidEncryptionParametersInvalidSSECustomerAlgorithmInvalidSSECustomerKeyMissingSSECustomerKeyMissingSSECustomerKeyMD5SSECustomerKeyMD5MismatchInvalidSSECustomerParametersIncompatibleEncryptionMethodKMSNotConfiguredNoAccessKeyInvalidTokenEventNotificationARNNotificationRegionNotificationOverlappingFilterNotificationFilterNameInvalidFilterNamePrefixFilterNameSuffixFilterValueInvalidOverlappingConfigsUnsupportedNotificationContentSHA256MismatchReadQuorumWriteQuorumParentIsObjectStorageFullRequestBodyParseObjectExistsAsDirectoryInvalidObjectNameInvalidObjectNamePrefixSlashInvalidResourceNameServerNotInitializedOperationTimedOutClientDisconnectedOperationMaxedOutInvalidRequestTransitionStorageClassNotFoundErrorInvalidStorageClassBackendDownMalformedJSONAdminNoSuchUserAdminNoSuchGroupAdminGroupNotEmptyAdminNoSuchPolicyAdminInvalidArgumentAdminInvalidAccessKeyAdminInvalidSecretKeyAdminConfigNoQuorumAdminConfigTooLargeAdminConfigBadJSONAdminConfigDuplicateKeysAdminCredentialsMismatchInsecureClientRequestObjectTamperedAdminBucketQuotaExceededAdminNoSuchQuotaConfigurationHealNotImplementedHealNoSuchProcessHealInvalidClientTokenHealMissingBucketHealAlreadyRunningHealOverlappingPathsIncorrectContinuationTokenEmptyRequestBodyUnsupportedFunctionInvalidExpressionTypeBusyUnauthorizedAccessExpressionTooLongIllegalSQLFunctionArgumentInvalidKeyPathInvalidCompressionFormatInvalidFileHeaderInfoInvalidJSONTypeInvalidQuoteFieldsInvalidRequestParameterInvalidDataTypeInvalidTextEncodingInvalidDataSourceInvalidTableAliasMissingRequiredParameterObjectSerializationConflictUnsupportedSQLOperationUnsupportedSQLStructureUnsupportedSyntaxUnsupportedRangeHeaderLexerInvalidCharLexerInvalidOperatorLexerInvalidLiteralLexerInvalidIONLiteralParseExpectedDatePartParseExpectedKeywordParseExpectedTokenTypeParseExpected2TokenTypesParseExpectedNumberParseExpectedRightParenBuiltinFunctionCallParseExpectedTypeNameParseExpectedWhenClauseParseUnsupportedTokenParseUnsupportedLiteralsGroupByParseExpectedMemberParseUnsupportedSelectParseUnsupportedCaseParseUnsupportedCaseClauseParseUnsupportedAliasParseUnsupportedSyntaxParseUnknownOperatorParseMissingIdentAfterAtParseUnexpectedOperatorParseUnexpectedTermParseUnexpectedTokenParseUnexpectedKeywordParseExpectedExpressionParseExpectedLeftParenAfterCastParseExpectedLeftParenValueConstructorParseExpectedLeftParenBuiltinFunctionCallParseExpectedArgumentDelimiterParseCastArityParseInvalidTypeParamParseEmptySelectParseSelectMissingFromParseExpectedIdentForGroupNameParseExpectedIdentForAliasParseUnsupportedCallWithStarParseNonUnaryAgregateFunctionCallParseMalformedJoinParseExpectedIdentForAtParseAsteriskIsNotAloneInSelectListParseCannotMixSqbAndWildcardInSelectListParseInvalidContextForWildcardInSelectListIncorrectSQLFunctionArgumentTypeValueParseFailureEvaluatorInvalidArgumentsIntegerOverflowLikeInvalidInputsCastFailedInvalidCastEvaluatorInvalidTimestampFormatPatternEvaluatorInvalidTimestampFormatPatternSymbolForParsingEvaluatorTimestampFormatPatternDuplicateFieldsEvaluatorTimestampFormatPatternHourClockAmPmMismatchEvaluatorUnterminatedTimestampFormatPatternTokenEvaluatorInvalidTimestampFormatPatternTokenEvaluatorInvalidTimestampFormatPatternSymbolEvaluatorBindingDoesNotExistMissingHeadersInvalidColumnIndexAdminConfigNotificationTargetsFailedAdminProfilerNotEnabledInvalidDecompressedSizeAddUserInvalidArgumentAdminAccountNotEligibleAccountNotEligibleAdminServiceAccountNotFoundPostPolicyConditionInvalidFormat"
289 290
290var _APIErrorCode_index = [...]uint16{0, 4, 16, 25, 39, 53, 67, 81, 94, 112, 129, 142, 154, 176, 196, 222, 236, 257, 274, 289, 312, 329, 347, 364, 388, 403, 424, 442, 454, 474, 491, 514, 535, 547, 565, 586, 614, 635, 658, 684, 721, 751, 784, 809, 841, 870, 895, 917, 943, 965, 993, 1022, 1056, 1087, 1124, 1154, 1163, 1175, 1191, 1204, 1218, 1236, 1256, 1277, 1293, 1304, 1320, 1348, 1368, 1384, 1412, 1426, 1443, 1458, 1471, 1485, 1498, 1511, 1527, 1544, 1565, 1579, 1600, 1613, 1635, 1658, 1683, 1699, 1714, 1729, 1750, 1768, 1783, 1800, 1825, 1843, 1866, 1881, 1900, 1916, 1935, 1949, 1957, 1976, 1986, 2001, 2037, 2068, 2101, 2130, 2142, 2162, 2186, 2210, 2231, 2255, 2274, 2297, 2323, 2344, 2362, 2389, 2416, 2437, 2458, 2482, 2507, 2535, 2563, 2579, 2590, 2602, 2619, 2634, 2652, 2681, 2698, 2714, 2730, 2748, 2766, 2789, 2810, 2820, 2831, 2845, 2856, 2872, 2895, 2912, 2940, 2959, 2979, 2996, 3014, 3031, 3045, 3080, 3099, 3110, 3123, 3138, 3154, 3172, 3189, 3209, 3230, 3251, 3270, 3289, 3307, 3331, 3355, 3376, 3390, 3414, 3443, 3461, 3478, 3500, 3517, 3535, 3555, 3581, 3597, 3616, 3637, 3641, 3659, 3676, 3702, 3716, 3740, 3761, 3776, 3794, 3817, 3832, 3851, 3868, 3885, 3909, 3936, 3959, 3982, 3999, 4021, 4037, 4057, 4076, 4098, 4119, 4139, 4161, 4185, 4204, 4246, 4267, 4290, 4311, 4342, 4361, 4383, 4403, 4429, 4450, 4472, 4492, 4516, 4539, 4558, 4578, 4600, 4623, 4654, 4692, 4733, 4763, 4777, 4798, 4814, 4836, 4866, 4892, 4920, 4953, 4971, 4994, 5029, 5069, 5111, 5143, 5160, 5185, 5200, 5217, 5227, 5238, 5276, 5330, 5376, 5428, 5476, 5519, 5563, 5591, 5605, 5623, 5659, 5682, 5705, 5727, 5750, 5768, 5795, 5827}291var _APIErrorCode_index = [...]uint16{0, 4, 16, 25, 39, 53, 67, 81, 94, 112, 129, 142, 154, 176, 196, 222, 236, 257, 274, 289, 312, 329, 347, 364, 388, 403, 424, 442, 454, 474, 491, 514, 535, 547, 565, 586, 614, 635, 658, 684, 721, 751, 784, 809, 841, 870, 895, 917, 943, 965, 993, 1022, 1056, 1087, 1124, 1154, 1184, 1193, 1205, 1221, 1234, 1248, 1266, 1286, 1307, 1323, 1334, 1350, 1378, 1398, 1414, 1442, 1456, 1473, 1488, 1501, 1515, 1528, 1541, 1557, 1574, 1595, 1609, 1630, 1643, 1665, 1688, 1713, 1729, 1744, 1759, 1780, 1798, 1813, 1830, 1855, 1873, 1896, 1911, 1930, 1946, 1965, 1979, 1987, 2006, 2016, 2031, 2067, 2098, 2131, 2160, 2172, 2192, 2216, 2240, 2261, 2285, 2304, 2327, 2353, 2374, 2392, 2419, 2446, 2467, 2488, 2512, 2537, 2565, 2593, 2609, 2620, 2632, 2649, 2664, 2682, 2711, 2728, 2744, 2760, 2778, 2796, 2819, 2840, 2850, 2861, 2875, 2886, 2902, 2925, 2942, 2970, 2989, 3009, 3026, 3044, 3061, 3075, 3110, 3129, 3140, 3153, 3168, 3184, 3202, 3219, 3239, 3260, 3281, 3300, 3319, 3337, 3361, 3385, 3406, 3420, 3444, 3473, 3491, 3508, 3530, 3547, 3565, 3585, 3611, 3627, 3646, 3667, 3671, 3689, 3706, 3732, 3746, 3770, 3791, 3806, 3824, 3847, 3862, 3881, 3898, 3915, 3939, 3966, 3989, 4012, 4029, 4051, 4067, 4087, 4106, 4128, 4149, 4169, 4191, 4215, 4234, 4276, 4297, 4320, 4341, 4372, 4391, 4413, 4433, 4459, 4480, 4502, 4522, 4546, 4569, 4588, 4608, 4630, 4653, 4684, 4722, 4763, 4793, 4807, 4828, 4844, 4866, 4896, 4922, 4950, 4983, 5001, 5024, 5059, 5099, 5141, 5173, 5190, 5215, 5230, 5247, 5257, 5268, 5306, 5360, 5406, 5458, 5506, 5549, 5593, 5621, 5635, 5653, 5689, 5712, 5735, 5757, 5780, 5798, 5825, 5857}
291 292
292func (i APIErrorCode) String() string {293func (i APIErrorCode) String() string {
293 if i < 0 || i >= APIErrorCode(len(_APIErrorCode_index)-1) {294 if i < 0 || i >= APIErrorCode(len(_APIErrorCode_index)-1) {
cmd/bucket-handlers.go+89−1
@@ -33,6 +33,7 @@ import ( · 32 unmodified lines
33 "strconv"33 "strconv"
34 "strings"34 "strings"
35 "sync"35 "sync"
36 "time"
36 37
37 "github.com/google/uuid"38 "github.com/google/uuid"
38 "github.com/gorilla/mux"39 "github.com/gorilla/mux"
@@ -611,7 +612,7 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter, · 572 unmodified lines
611 612
612 if replicateDeletes {613 if replicateDeletes {
613 if dobj.DeleteMarkerReplicationStatus == string(replication.Pending) || dobj.VersionPurgeStatus == Pending {614 if dobj.DeleteMarkerReplicationStatus == string(replication.Pending) || dobj.VersionPurgeStatus == Pending {
614 dv := DeletedObjectVersionInfo{615 dv := DeletedObjectReplicationInfo{
615 DeletedObject: dobj,616 DeletedObject: dobj,
616 Bucket: bucket,617 Bucket: bucket,
617 }618 }
@@ -1686,3 +1687,90 @@ func (api objectAPIHandlers) GetBucketReplicationMetricsHandler(w http.ResponseW · 1068 unmodified lines
1686 }1687 }
1687 w.(http.Flusher).Flush()1688 w.(http.Flusher).Flush()
1688}1689}
1690
1691// ResetBucketReplicationStateHandler - starts a replication reset for all objects in a bucket which
1692// qualify for replication and re-sync the object(s) to target, provided ExistingObjectReplication is
1693// enabled for the qualifying rule. This API is a MinIO only extension provided for situations where
1694// remote target is entirely lost,and previously replicated objects need to be re-synced.
1695func (api objectAPIHandlers) ResetBucketReplicationStateHandler(w http.ResponseWriter, r *http.Request) {
1696 ctx := newContext(r, w, "ResetBucketReplicationState")
1697
1698 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
1699
1700 vars := mux.Vars(r)
1701 bucket := vars["bucket"]
1702 durationStr := r.URL.Query().Get("older-than")
1703 var (
1704 days time.Duration
1705 err error
1706 )
1707 if durationStr != "" {
1708 days, err = time.ParseDuration(durationStr)
1709 if err != nil {
1710 writeErrorResponse(ctx, w, toAPIError(ctx, InvalidArgument{
1711 Bucket: bucket,
1712 Err: fmt.Errorf("invalid query parameter older-than %s for %s : %w", durationStr, bucket, err),
1713 }), r.URL, guessIsBrowserReq(r))
1714 }
1715 }
1716
1717 objectAPI := api.ObjectAPI()
1718 if objectAPI == nil {
1719 writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
1720 return
1721 }
1722
1723 if s3Error := checkRequestAuthType(ctx, r, policy.ResetBucketReplicationStateAction, bucket, ""); s3Error != ErrNone {
1724 writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
1725 return
1726 }
1727
1728 // Check if bucket exists.
1729 if _, err := objectAPI.GetBucketInfo(ctx, bucket); err != nil {
1730 writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
1731 return
1732 }
1733
1734 config, err := globalBucketMetadataSys.GetReplicationConfig(ctx, bucket)
1735 if err != nil {
1736 writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
1737 return
1738 }
1739 if !config.HasActiveRules("", true) {
1740 writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrReplicationNoMatchingRuleError), r.URL, guessIsBrowserReq(r))
1741 return
1742 }
1743 target := globalBucketTargetSys.GetRemoteBucketTargetByArn(ctx, bucket, config.RoleArn)
1744 target.ResetBeforeDate = UTCNow().AddDate(0, 0, -1*int(days/24))
1745 target.ResetID = mustGetUUID()
1746 if err = globalBucketTargetSys.SetTarget(ctx, bucket, &target, true); err != nil {
1747 switch err.(type) {
1748 case BucketRemoteConnectionErr:
1749 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrReplicationRemoteConnectionError, err), r.URL)
1750 default:
1751 writeErrorResponseJSON(ctx, w, toAPIError(ctx, err), r.URL)
1752 }
1753 return
1754 }
1755 targets, err := globalBucketTargetSys.ListBucketTargets(ctx, bucket)
1756 if err != nil {
1757 writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
1758 return
1759 }
1760 tgtBytes, err := json.Marshal(&targets)
1761 if err != nil {
1762 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminConfigBadJSON, err), r.URL)
1763 return
1764 }
1765 if err = globalBucketMetadataSys.Update(bucket, bucketTargetsFile, tgtBytes); err != nil {
1766 writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
1767 return
1768 }
1769 data, err := json.Marshal(target.ResetID)
1770 if err != nil {
1771 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
1772 return
1773 }
1774 // Write success response.
1775 writeSuccessResponseJSON(w, data)
1776}
cmd/bucket-replication.go+201−43
@@ -91,32 +91,70 @@ func validateReplicationDestination(ctx context.Context, bucket string, rCfg *re · 90 unmodified lines
91 return false, BucketRemoteTargetNotFound{Bucket: bucket}91 return false, BucketRemoteTargetNotFound{Bucket: bucket}
92}92}
93 93
94func mustReplicateWeb(ctx context.Context, r *http.Request, bucket, object string, meta map[string]string, replStatus string, permErr APIErrorCode) (replicate bool, sync bool) {94type mustReplicateOptions struct {
95 meta map[string]string
96 status replication.StatusType
97 opType replication.Type
98}
99
100func (o mustReplicateOptions) ReplicationStatus() (s replication.StatusType) {
101 if rs, ok := o.meta[xhttp.AmzBucketReplicationStatus]; ok {
102 return replication.StatusType(rs)
103 }
104 return s
105}
106func (o mustReplicateOptions) isExistingObjectReplication() bool {
107 return o.opType == replication.ExistingObjectReplicationType
108}
109
110func (o mustReplicateOptions) isMetadataReplication() bool {
111 return o.opType == replication.MetadataReplicationType
112}
113func getMustReplicateOptions(o ObjectInfo, op replication.Type) mustReplicateOptions {
114 if !op.Valid() {
115 op = replication.ObjectReplicationType
116 if o.metadataOnly {
117 op = replication.MetadataReplicationType
118 }
119 }
120 meta := cloneMSS(o.UserDefined)
121 if o.UserTags != "" {
122 meta[xhttp.AmzObjectTagging] = o.UserTags
123 }
124 return mustReplicateOptions{
125 meta: meta,
126 status: o.ReplicationStatus,
127 opType: op,
128 }
129}
130func mustReplicateWeb(ctx context.Context, r *http.Request, bucket, object string, meta map[string]string, replStatus replication.StatusType, permErr APIErrorCode) (replicate bool, sync bool) {
95 if permErr != ErrNone {131 if permErr != ErrNone {
96 return132 return
97 }133 }
98 return mustReplicater(ctx, bucket, object, meta, replStatus, false)134 return mustReplicater(ctx, bucket, object, mustReplicateOptions{
135 meta: meta,
136 status: replStatus,
137 opType: replication.ObjectReplicationType,
138 })
99}139}
100 140
101// mustReplicate returns 2 booleans - true if object meets replication criteria and true if replication is to be done in141// mustReplicate returns 2 booleans - true if object meets replication criteria and true if replication is to be done in
102// a synchronous manner.142// a synchronous manner.
103func mustReplicate(ctx context.Context, r *http.Request, bucket, object string, meta map[string]string, replStatus string, metadataOnly bool) (replicate bool, sync bool) {143func mustReplicate(ctx context.Context, r *http.Request, bucket, object string, opts mustReplicateOptions) (replicate bool, sync bool) {
104 if s3Err := isPutActionAllowed(ctx, getRequestAuthType(r), bucket, "", r, iampolicy.GetReplicationConfigurationAction); s3Err != ErrNone {144 if s3Err := isPutActionAllowed(ctx, getRequestAuthType(r), bucket, "", r, iampolicy.GetReplicationConfigurationAction); s3Err != ErrNone {
105 return145 return
106 }146 }
107 return mustReplicater(ctx, bucket, object, meta, replStatus, metadataOnly)147 return mustReplicater(ctx, bucket, object, opts)
108}148}
109 149
110// mustReplicater returns 2 booleans - true if object meets replication criteria and true if replication is to be done in150// mustReplicater returns 2 booleans - true if object meets replication criteria and true if replication is to be done in
111// a synchronous manner.151// a synchronous manner.
112func mustReplicater(ctx context.Context, bucket, object string, meta map[string]string, replStatus string, metadataOnly bool) (replicate bool, sync bool) {152func mustReplicater(ctx context.Context, bucket, object string, mopts mustReplicateOptions) (replicate bool, sync bool) {
113 if globalIsGateway {153 if globalIsGateway {
114 return replicate, sync154 return replicate, sync
115 }155 }
116 if rs, ok := meta[xhttp.AmzBucketReplicationStatus]; ok {156 replStatus := mopts.ReplicationStatus()
117 replStatus = rs157 if replStatus == replication.Replica && !mopts.isMetadataReplication() {
118 }
119 if replication.StatusType(replStatus) == replication.Replica && !metadataOnly {
120 return replicate, sync158 return replicate, sync
121 }159 }
122 cfg, err := getReplicationConfig(ctx, bucket)160 cfg, err := getReplicationConfig(ctx, bucket)
@@ -124,11 +162,12 @@ func mustReplicater(ctx context.Context, bucket, object string, meta map[string] · 1 unmodified line
124 return replicate, sync162 return replicate, sync
125 }163 }
126 opts := replication.ObjectOpts{164 opts := replication.ObjectOpts{
127 Name: object,165 Name: object,
128 SSEC: crypto.SSEC.IsEncrypted(meta),166 SSEC: crypto.SSEC.IsEncrypted(mopts.meta),
129 Replica: replication.StatusType(replStatus) == replication.Replica,167 Replica: replStatus == replication.Replica,
168 ExistingObject: mopts.isExistingObjectReplication(),
130 }169 }
131 tagStr, ok := meta[xhttp.AmzObjectTagging]170 tagStr, ok := mopts.meta[xhttp.AmzObjectTagging]
132 if ok {171 if ok {
133 opts.UserTags = tagStr172 opts.UserTags = tagStr
134 }173 }
@@ -226,7 +265,7 @@ func checkReplicateDelete(ctx context.Context, bucket string, dobj ObjectToDelet · 91 unmodified lines
226// target cluster, the object version is marked deleted on the source and hidden from listing. It is permanently265// target cluster, the object version is marked deleted on the source and hidden from listing. It is permanently
227// deleted from the source when the VersionPurgeStatus changes to "Complete", i.e after replication succeeds266// deleted from the source when the VersionPurgeStatus changes to "Complete", i.e after replication succeeds
228// on target.267// on target.
229func replicateDelete(ctx context.Context, dobj DeletedObjectVersionInfo, objectAPI ObjectLayer) {268func replicateDelete(ctx context.Context, dobj DeletedObjectReplicationInfo, objectAPI ObjectLayer) {
230 bucket := dobj.Bucket269 bucket := dobj.Bucket
231 versionID := dobj.DeleteMarkerVersionID270 versionID := dobj.DeleteMarkerVersionID
232 if versionID == "" {271 if versionID == "" {
@@ -760,7 +799,9 @@ func replicateObject(ctx context.Context, ri ReplicateObjectInfo, objectAPI Obje · 527 unmodified lines
760 if objInfo.UserTags != "" {799 if objInfo.UserTags != "" {
761 objInfo.UserDefined[xhttp.AmzObjectTagging] = objInfo.UserTags800 objInfo.UserDefined[xhttp.AmzObjectTagging] = objInfo.UserTags
762 }801 }
763 802 if ri.OpType == replication.ExistingObjectReplicationType {
803 objInfo.UserDefined[xhttp.MinIOReplicationResetStatus] = fmt.Sprintf("%s;%s", UTCNow().Format(http.TimeFormat), ri.ResetID)
804 }
764 // FIXME: add support for missing replication events805 // FIXME: add support for missing replication events
765 // - event.ObjectReplicationMissedThreshold806 // - event.ObjectReplicationMissedThreshold
766 // - event.ObjectReplicationReplicatedAfterThreshold807 // - event.ObjectReplicationReplicatedAfterThreshold
@@ -774,7 +815,8 @@ func replicateObject(ctx context.Context, ri ReplicateObjectInfo, objectAPI Obje · 7 unmodified lines
774 return815 return
775 }816 }
776 // Leave metadata in `PENDING` state if inline replication fails to save iops817 // Leave metadata in `PENDING` state if inline replication fails to save iops
777 if ri.OpType == replication.HealReplicationType || replicationStatus == replication.Completed {818 if ri.OpType == replication.HealReplicationType ||
819 replicationStatus == replication.Completed {
778 // This lower level implementation is necessary to avoid write locks from CopyObject.820 // This lower level implementation is necessary to avoid write locks from CopyObject.
779 poolIdx, err := z.getPoolIdx(ctx, bucket, object, objInfo.Size)821 poolIdx, err := z.getPoolIdx(ctx, bucket, object, objInfo.Size)
780 if err != nil {822 if err != nil {
@@ -834,10 +876,12 @@ func filterReplicationStatusMetadata(metadata map[string]string) map[string]stri · 53 unmodified lines
834 return dst876 return dst
835}877}
836 878
837// DeletedObjectVersionInfo has info on deleted object879// DeletedObjectReplicationInfo has info on deleted object
838type DeletedObjectVersionInfo struct {880type DeletedObjectReplicationInfo struct {
839 DeletedObject881 DeletedObject
840 Bucket string882 Bucket string
883 OpType replication.Type
884 ResetID string
841}885}
842 886
843var (887var (
@@ -847,35 +891,40 @@ var ( · 3 unmodified lines
847 891
848// ReplicationPool describes replication pool892// ReplicationPool describes replication pool
849type ReplicationPool struct {893type ReplicationPool struct {
850 objLayer ObjectLayer894 objLayer ObjectLayer
851 ctx context.Context895 ctx context.Context
852 mrfWorkerKillCh chan struct{}896 mrfWorkerKillCh chan struct{}
853 workerKillCh chan struct{}897 workerKillCh chan struct{}
854 replicaCh chan ReplicateObjectInfo898 replicaCh chan ReplicateObjectInfo
855 replicaDeleteCh chan DeletedObjectVersionInfo899 replicaDeleteCh chan DeletedObjectReplicationInfo
856 mrfReplicaCh chan ReplicateObjectInfo900 mrfReplicaCh chan ReplicateObjectInfo
857 workerSize int901 existingReplicaCh chan ReplicateObjectInfo
858 mrfWorkerSize int902 existingReplicaDeleteCh chan DeletedObjectReplicationInfo
859 workerWg sync.WaitGroup903 workerSize int
860 mrfWorkerWg sync.WaitGroup904 mrfWorkerSize int
861 once sync.Once905 workerWg sync.WaitGroup
862 mu sync.Mutex906 mrfWorkerWg sync.WaitGroup
907 once sync.Once
908 mu sync.Mutex
863}909}
864 910
865// NewReplicationPool creates a pool of replication workers of specified size911// NewReplicationPool creates a pool of replication workers of specified size
866func NewReplicationPool(ctx context.Context, o ObjectLayer, opts replicationPoolOpts) *ReplicationPool {912func NewReplicationPool(ctx context.Context, o ObjectLayer, opts replicationPoolOpts) *ReplicationPool {
867 pool := &ReplicationPool{913 pool := &ReplicationPool{
868 replicaCh: make(chan ReplicateObjectInfo, 100000),914 replicaCh: make(chan ReplicateObjectInfo, 100000),
869 replicaDeleteCh: make(chan DeletedObjectVersionInfo, 100000),915 replicaDeleteCh: make(chan DeletedObjectReplicationInfo, 100000),
870 mrfReplicaCh: make(chan ReplicateObjectInfo, 100000),916 mrfReplicaCh: make(chan ReplicateObjectInfo, 100000),
871 workerKillCh: make(chan struct{}, opts.Workers),917 workerKillCh: make(chan struct{}, opts.Workers),
872 mrfWorkerKillCh: make(chan struct{}, opts.FailedWorkers),918 mrfWorkerKillCh: make(chan struct{}, opts.FailedWorkers),
873 ctx: ctx,919 existingReplicaCh: make(chan ReplicateObjectInfo, 100000),
874 objLayer: o,920 existingReplicaDeleteCh: make(chan DeletedObjectReplicationInfo, 100000),
921 ctx: ctx,
922 objLayer: o,
875 }923 }
876 924
877 pool.ResizeWorkers(opts.Workers)925 pool.ResizeWorkers(opts.Workers)
878 pool.ResizeFailedWorkers(opts.FailedWorkers)926 pool.ResizeFailedWorkers(opts.FailedWorkers)
927 go pool.AddExistingObjectReplicateWorker()
879 return pool928 return pool
880}929}
881 930
@@ -921,6 +970,26 @@ func (p *ReplicationPool) AddWorker() { · 39 unmodified lines
921 970
922}971}
923 972
973// AddExistingObjectReplicateWorker adds a worker to queue existing objects that need to be sync'd
974func (p *ReplicationPool) AddExistingObjectReplicateWorker() {
975 for {
976 select {
977 case <-p.ctx.Done():
978 return
979 case oi, ok := <-p.existingReplicaCh:
980 if !ok {
981 return
982 }
983 replicateObject(p.ctx, oi, p.objLayer)
984 case doi, ok := <-p.existingReplicaDeleteCh:
985 if !ok {
986 return
987 }
988 replicateDelete(p.ctx, doi, p.objLayer)
989 }
990 }
991}
992
924// ResizeWorkers sets replication workers pool to new size993// ResizeWorkers sets replication workers pool to new size
925func (p *ReplicationPool) ResizeWorkers(n int) {994func (p *ReplicationPool) ResizeWorkers(n int) {
926 p.mu.Lock()995 p.mu.Lock()
@@ -962,6 +1031,7 @@ func (p *ReplicationPool) queueReplicaFailedTask(ri ReplicateObjectInfo) { · 35 unmodified lines
962 p.once.Do(func() {1031 p.once.Do(func() {
963 close(p.replicaCh)1032 close(p.replicaCh)
964 close(p.mrfReplicaCh)1033 close(p.mrfReplicaCh)
1034 close(p.existingReplicaCh)
965 })1035 })
966 case p.mrfReplicaCh <- ri:1036 case p.mrfReplicaCh <- ri:
967 default:1037 default:
@@ -972,27 +1042,44 @@ func (p *ReplicationPool) queueReplicaTask(ri ReplicateObjectInfo) { · 4 unmodified lines
972 if p == nil {1042 if p == nil {
973 return1043 return
974 }1044 }
1045 var ch chan ReplicateObjectInfo
1046 switch ri.OpType {
1047 case replication.ExistingObjectReplicationType:
1048 ch = p.existingReplicaCh
1049 default:
1050 ch = p.replicaCh
1051 }
975 select {1052 select {
976 case <-GlobalContext.Done():1053 case <-GlobalContext.Done():
977 p.once.Do(func() {1054 p.once.Do(func() {
978 close(p.replicaCh)1055 close(p.replicaCh)
979 close(p.mrfReplicaCh)1056 close(p.mrfReplicaCh)
1057 close(p.existingReplicaCh)
980 })1058 })
981 case p.replicaCh <- ri:1059 case ch <- ri:
982 default:1060 default:
983 }1061 }
984}1062}
985 1063
986func (p *ReplicationPool) queueReplicaDeleteTask(doi DeletedObjectVersionInfo) {1064func (p *ReplicationPool) queueReplicaDeleteTask(doi DeletedObjectReplicationInfo) {
987 if p == nil {1065 if p == nil {
988 return1066 return
989 }1067 }
1068 var ch chan DeletedObjectReplicationInfo
1069 switch doi.OpType {
1070 case replication.ExistingObjectReplicationType:
1071 ch = p.existingReplicaDeleteCh
1072 default:
1073 ch = p.replicaDeleteCh
1074 }
1075
990 select {1076 select {
991 case <-GlobalContext.Done():1077 case <-GlobalContext.Done():
992 p.once.Do(func() {1078 p.once.Do(func() {
993 close(p.replicaDeleteCh)1079 close(p.replicaDeleteCh)
1080 close(p.existingReplicaDeleteCh)
994 })1081 })
995 case p.replicaDeleteCh <- doi:1082 case ch <- doi:
996 default:1083 default:
997 }1084 }
998}1085}
@@ -1157,7 +1244,78 @@ func scheduleReplication(ctx context.Context, objInfo ObjectInfo, o ObjectLayer, · 158 unmodified lines
1157 }1244 }
1158}1245}
1159 1246
1160func scheduleReplicationDelete(ctx context.Context, dv DeletedObjectVersionInfo, o ObjectLayer, sync bool) {1247func scheduleReplicationDelete(ctx context.Context, dv DeletedObjectReplicationInfo, o ObjectLayer, sync bool) {
1161 globalReplicationPool.queueReplicaDeleteTask(dv)1248 globalReplicationPool.queueReplicaDeleteTask(dv)
1162 globalReplicationStats.Update(dv.Bucket, 0, replication.Pending, replication.StatusType(""), replication.DeleteReplicationType)1249 globalReplicationStats.Update(dv.Bucket, 0, replication.Pending, replication.StatusType(""), replication.DeleteReplicationType)
1163}1250}
1251
1252type replicationConfig struct {
1253 Config *replication.Config
1254 ResetID string
1255 ResetBeforeDate time.Time
1256}
1257
1258func (c replicationConfig) Empty() bool {
1259 return c.Config == nil
1260}
1261func (c replicationConfig) Replicate(opts replication.ObjectOpts) bool {
1262 return c.Config.Replicate(opts)
1263}
1264
1265// Resync returns true if replication reset is requested
1266func (c replicationConfig) Resync(ctx context.Context, oi ObjectInfo) bool {
1267 if c.Empty() {
1268 return false
1269 }
1270 // existing object replication does not apply to un-versioned objects
1271 if oi.VersionID == "" || oi.VersionID == nullVersionID {
1272 return false
1273 }
1274
1275 var replicate bool
1276 if oi.DeleteMarker {
1277 if c.Replicate(replication.ObjectOpts{
1278 Name: oi.Name,
1279 SSEC: crypto.SSEC.IsEncrypted(oi.UserDefined),
1280 UserTags: oi.UserTags,
1281 DeleteMarker: oi.DeleteMarker,
1282 VersionID: oi.VersionID,
1283 OpType: replication.DeleteReplicationType,
1284 ExistingObject: true}) {
1285 replicate = true
1286 }
1287 } else {
1288 // Ignore previous replication status when deciding if object can be re-replicated
1289 objInfo := oi.Clone()
1290 objInfo.ReplicationStatus = replication.StatusType("")
1291 replicate, _ = mustReplicater(ctx, oi.Bucket, oi.Name, getMustReplicateOptions(objInfo, replication.ExistingObjectReplicationType))
1292 }
1293 return c.resync(oi, replicate)
1294}
1295
1296// wrapper function for testability. Returns true if a new reset is requested on
1297// already replicated objects OR object qualifies for existing object replication
1298// and no reset requested.
1299func (c replicationConfig) resync(oi ObjectInfo, replicate bool) bool {
1300 if !replicate {
1301 return false
1302 }
1303 rs, ok := oi.UserDefined[xhttp.MinIOReplicationResetStatus]
1304 if !ok { // existing object replication is enabled and object version is unreplicated so far.
1305 if c.ResetID != "" && oi.ModTime.Before(c.ResetBeforeDate) { // trigger replication if `mc replicate reset` requested
1306 return true
1307 }
1308 return oi.ReplicationStatus != replication.Completed
1309 }
1310 if c.ResetID == "" || c.ResetBeforeDate.Equal(timeSentinel) { // no reset in progress
1311 return false
1312 }
1313 // if already replicated, return true if a new reset was requested.
1314 splits := strings.SplitN(rs, ";", 2)
1315 newReset := splits[1] != c.ResetID
1316 if !newReset && oi.ReplicationStatus == replication.Completed {
1317 // already replicated and no reset requested
1318 return false
1319 }
1320 return newReset && oi.ModTime.Before(c.ResetBeforeDate)
1321}
cmd/bucket-replication_test.go+209−0
@@ -0,0 +1,209 @@
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
18package cmd
19
20import (
21 "context"
22 "fmt"
23 "net/http"
24 "testing"
25 "time"
26
27 "github.com/minio/minio/internal/bucket/replication"
28 xhttp "github.com/minio/minio/internal/http"
29)
30
31var configs = []replication.Config{
32 { // Config0 - Replication config has no filters, existing object replication enabled
33 Rules: []replication.Rule{
34 {
35 Status: replication.Enabled,
36 Priority: 1,
37 DeleteMarkerReplication: replication.DeleteMarkerReplication{Status: replication.Enabled},
38 DeleteReplication: replication.DeleteReplication{Status: replication.Enabled},
39 Filter: replication.Filter{},
40 ExistingObjectReplication: replication.ExistingObjectReplication{Status: replication.Enabled},
41 SourceSelectionCriteria: replication.SourceSelectionCriteria{
42 ReplicaModifications: replication.ReplicaModifications{Status: replication.Enabled},
43 },
44 },
45 },
46 },
47}
48
49var replicationConfigTests = []struct {
50 info ObjectInfo
51 name string
52 rcfg replicationConfig
53 expectedSync bool
54}{
55 { //1. no replication config
56 name: "no replication config",
57 info: ObjectInfo{Size: 100},
58 rcfg: replicationConfig{Config: nil},
59 expectedSync: false,
60 },
61 { //2. existing object replication config enabled, no versioning
62 name: "existing object replication config enabled, no versioning",
63 info: ObjectInfo{Size: 100},
64 rcfg: replicationConfig{Config: &configs[0]},
65 expectedSync: false,
66 },
67 { //3. existing object replication config enabled, versioning suspended
68 name: "existing object replication config enabled, versioning suspended",
69 info: ObjectInfo{Size: 100, VersionID: nullVersionID},
70 rcfg: replicationConfig{Config: &configs[0]},
71 expectedSync: false,
72 },
73 { //4. existing object replication enabled, versioning enabled; no reset in progress
74 name: "existing object replication enabled, versioning enabled; no reset in progress",
75 info: ObjectInfo{Size: 100,
76 ReplicationStatus: replication.Completed,
77 VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
78 },
79 rcfg: replicationConfig{Config: &configs[0]},
80 expectedSync: false,
81 },
82}
83
84func TestReplicationResync(t *testing.T) {
85 ctx := context.Background()
86 for i, test := range replicationConfigTests {
87 if sync := test.rcfg.Resync(ctx, test.info); sync != test.expectedSync {
88 t.Errorf("Test%d (%s): Resync got %t , want %t", i+1, test.name, sync, test.expectedSync)
89 }
90 }
91}
92
93var start = UTCNow().AddDate(0, 0, -1)
94var replicationConfigTests2 = []struct {
95 info ObjectInfo
96 name string
97 replicate bool
98 rcfg replicationConfig
99 expectedSync bool
100}{
101 { // Cases 1-4: existing object replication enabled, versioning enabled, no reset - replication status varies
102 // 1: Pending replication
103 name: "existing object replication on object in Pending replication status",
104 info: ObjectInfo{Size: 100,
105 ReplicationStatus: replication.Pending,
106 VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
107 },
108 replicate: true,
109 expectedSync: true,
110 },
111 { // 2. replication status Failed
112 name: "existing object replication on object in Failed replication status",
113 info: ObjectInfo{Size: 100,
114 ReplicationStatus: replication.Failed,
115 VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
116 },
117 replicate: true,
118 expectedSync: true,
119 },
120 { //3. replication status unset
121 name: "existing object replication on pre-existing unreplicated object",
122 info: ObjectInfo{Size: 100,
123 ReplicationStatus: replication.StatusType(""),
124 VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
125 },
126 replicate: true,
127 expectedSync: true,
128 },
129 { //4. replication status Complete
130 name: "existing object replication on object in Completed replication status",
131 info: ObjectInfo{Size: 100,
132 ReplicationStatus: replication.Completed,
133 VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
134 },
135 replicate: true,
136 expectedSync: false,
137 },
138 { //5. existing object replication enabled, versioning enabled, replication status Pending & reset ID present
139 name: "existing object replication with reset in progress and object in Pending status",
140 info: ObjectInfo{Size: 100,
141 ReplicationStatus: replication.Pending,
142 VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
143 },
144 replicate: true,
145 expectedSync: true,
146 rcfg: replicationConfig{ResetID: "xyz", ResetBeforeDate: UTCNow()},
147 },
148 { //6. existing object replication enabled, versioning enabled, replication status Failed & reset ID present
149 name: "existing object replication with reset in progress and object in Failed status",
150 info: ObjectInfo{Size: 100,
151 ReplicationStatus: replication.Failed,
152 VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
153 },
154 replicate: true,
155 expectedSync: true,
156 rcfg: replicationConfig{ResetID: "xyz", ResetBeforeDate: UTCNow()},
157 },
158 { //7. existing object replication enabled, versioning enabled, replication status unset & reset ID present
159 name: "existing object replication with reset in progress and object never replicated before",
160 info: ObjectInfo{Size: 100,
161 ReplicationStatus: replication.StatusType(""),
162 VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
163 },
164 replicate: true,
165 expectedSync: true,
166 rcfg: replicationConfig{ResetID: "xyz", ResetBeforeDate: UTCNow()},
167 },
168 { //8. existing object replication enabled, versioning enabled, replication status Complete & reset ID present
169 name: "existing object replication enabled - reset in progress for an object in Completed status",
170 info: ObjectInfo{Size: 100,
171 ReplicationStatus: replication.Completed,
172 VersionID: "a3348c34-c352-4498-82f0-1098e8b34df8",
173 },
174 replicate: true,
175 expectedSync: true,
176 rcfg: replicationConfig{ResetID: "xyz", ResetBeforeDate: UTCNow()},
177 },
178 { //9. existing object replication enabled, versioning enabled, replication status Pending & reset ID different
179 name: "existing object replication enabled, newer reset in progress on object in Pending replication status",
180 info: ObjectInfo{Size: 100,
181 ReplicationStatus: replication.Pending,
182 VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
183 UserDefined: map[string]string{xhttp.MinIOReplicationResetStatus: fmt.Sprintf("%s;%s", UTCNow().Format(http.TimeFormat), "xyz")},
184 ModTime: UTCNow().AddDate(0, 0, -1),
185 },
186 replicate: true,
187 expectedSync: true,
188 rcfg: replicationConfig{ResetID: "abc", ResetBeforeDate: UTCNow()},
189 },
190 { //10. existing object replication enabled, versioning enabled, replication status Complete & reset done
191 name: "reset done on object in Completed Status - ineligbile for re-replication",
192 info: ObjectInfo{Size: 100,
193 ReplicationStatus: replication.Completed,
194 VersionID: "a3348c34-c352-4498-82f0-1098e8b34df9",
195 UserDefined: map[string]string{xhttp.MinIOReplicationResetStatus: fmt.Sprintf("%s;%s", start.Format(http.TimeFormat), "xyz")},
196 },
197 replicate: true,
198 expectedSync: false,
199 rcfg: replicationConfig{ResetID: "xyz", ResetBeforeDate: UTCNow()},
200 },
201}
202
203func TestReplicationResyncwrapper(t *testing.T) {
204 for i, test := range replicationConfigTests2 {
205 if sync := test.rcfg.resync(test.info, test.replicate); sync != test.expectedSync {
206 t.Errorf("%s (%s): Replicationresync got %t , want %t", fmt.Sprintf("Test%d - %s", i+1, time.Now().Format(http.TimeFormat)), test.name, sync, test.expectedSync)
207 }
208 }
209}
cmd/data-scanner.go+50−22
@@ -375,7 +375,12 @@ func (f *folderScanner) scanFolder(ctx context.Context, folder cachedFolder, int · 374 unmodified lines
375 activeLifeCycle = f.oldCache.Info.lifeCycle375 activeLifeCycle = f.oldCache.Info.lifeCycle
376 filter = nil376 filter = nil
377 }377 }
378 378 // If there are replication rules for the prefix, remove the filter.
379 var replicationCfg replicationConfig
380 if !f.oldCache.Info.replication.Empty() && f.oldCache.Info.replication.Config.HasActiveRules(prefix, true) {
381 replicationCfg = f.oldCache.Info.replication
382 filter = nil
383 }
379 // Check if we can skip it due to bloom filter...384 // Check if we can skip it due to bloom filter...
380 if filter != nil && ok && existing.Compacted {385 if filter != nil && ok && existing.Compacted {
381 // If folder isn't in filter and we have data, skip it completely.386 // If folder isn't in filter and we have data, skip it completely.
@@ -449,16 +454,16 @@ func (f *folderScanner) scanFolder(ctx context.Context, folder cachedFolder, int · 67 unmodified lines
449 454
450 // Get file size, ignore errors.455 // Get file size, ignore errors.
451 item := scannerItem{456 item := scannerItem{
452 Path: path.Join(f.root, entName),457 Path: path.Join(f.root, entName),
453 Typ: typ,458 Typ: typ,
454 bucket: bucket,459 bucket: bucket,
455 prefix: path.Dir(prefix),460 prefix: path.Dir(prefix),
456 objectName: path.Base(entName),461 objectName: path.Base(entName),
457 debug: f.dataUsageScannerDebug,462 debug: f.dataUsageScannerDebug,
458 lifeCycle: activeLifeCycle,463 lifeCycle: activeLifeCycle,
459 heal: thisHash.mod(f.oldCache.Info.NextCycle, f.healObjectSelect/folder.objectHealProbDiv) && globalIsErasure,464 replication: replicationCfg,
465 heal: thisHash.mod(f.oldCache.Info.NextCycle, f.healObjectSelect/folder.objectHealProbDiv) && globalIsErasure,
460 }466 }
461
462 // if the drive belongs to an erasure set467 // if the drive belongs to an erasure set
463 // that is already being healed, skip the468 // that is already being healed, skip the
464 // healing attempt on this drive.469 // healing attempt on this drive.
@@ -808,12 +813,13 @@ type scannerItem struct { · 343 unmodified lines
808 Path string813 Path string
809 Typ os.FileMode814 Typ os.FileMode
810 815
811 bucket string // Bucket.816 bucket string // Bucket.
812 prefix string // Only the prefix if any, does not have final object name.817 prefix string // Only the prefix if any, does not have final object name.
813 objectName string // Only the object name without prefixes.818 objectName string // Only the object name without prefixes.
814 lifeCycle *lifecycle.Lifecycle819 lifeCycle *lifecycle.Lifecycle
815 heal bool // Has the object been selected for heal check?820 replication replicationConfig
816 debug bool821 heal bool // Has the object been selected for heal check?
822 debug bool
817}823}
818 824
819type sizeSummary struct {825type sizeSummary struct {
@@ -1140,33 +1146,50 @@ func (i *scannerItem) objectPath() string { · 320 unmodified lines
1140 1146
1141// healReplication will heal a scanned item that has failed replication.1147// healReplication will heal a scanned item that has failed replication.
1142func (i *scannerItem) healReplication(ctx context.Context, o ObjectLayer, oi ObjectInfo, sizeS *sizeSummary) {1148func (i *scannerItem) healReplication(ctx context.Context, o ObjectLayer, oi ObjectInfo, sizeS *sizeSummary) {
1149 existingObjResync := i.replication.Resync(ctx, oi)
1143 if oi.DeleteMarker || !oi.VersionPurgeStatus.Empty() {1150 if oi.DeleteMarker || !oi.VersionPurgeStatus.Empty() {
1144 // heal delete marker replication failure or versioned delete replication failure1151 // heal delete marker replication failure or versioned delete replication failure
1145 if oi.ReplicationStatus == replication.Pending ||1152 if oi.ReplicationStatus == replication.Pending ||
1146 oi.ReplicationStatus == replication.Failed ||1153 oi.ReplicationStatus == replication.Failed ||
1147 oi.VersionPurgeStatus == Failed || oi.VersionPurgeStatus == Pending {1154 oi.VersionPurgeStatus == Failed || oi.VersionPurgeStatus == Pending {
1148 i.healReplicationDeletes(ctx, o, oi)1155 i.healReplicationDeletes(ctx, o, oi, existingObjResync)
1149 return1156 return
1150 }1157 }
1158 // if replication status is Complete on DeleteMarker and existing object resync required
1159 if existingObjResync && oi.ReplicationStatus == replication.Completed {
1160 i.healReplicationDeletes(ctx, o, oi, existingObjResync)
1161 return
1162 }
1163 return
1164 }
1165 roi := ReplicateObjectInfo{ObjectInfo: oi, OpType: replication.HealReplicationType}
1166 if existingObjResync {
1167 roi.OpType = replication.ExistingObjectReplicationType
1168 roi.ResetID = i.replication.ResetID
1151 }1169 }
1152 switch oi.ReplicationStatus {1170 switch oi.ReplicationStatus {
1153 case replication.Pending:1171 case replication.Pending:
1154 sizeS.pendingCount++1172 sizeS.pendingCount++
1155 sizeS.pendingSize += oi.Size1173 sizeS.pendingSize += oi.Size
1156 globalReplicationPool.queueReplicaTask(ReplicateObjectInfo{ObjectInfo: oi, OpType: replication.HealReplicationType})1174 globalReplicationPool.queueReplicaTask(roi)
1175 return
1157 case replication.Failed:1176 case replication.Failed:
1158 sizeS.failedSize += oi.Size1177 sizeS.failedSize += oi.Size
1159 sizeS.failedCount++1178 sizeS.failedCount++
1160 globalReplicationPool.queueReplicaTask(ReplicateObjectInfo{ObjectInfo: oi, OpType: replication.HealReplicationType})1179 globalReplicationPool.queueReplicaTask(roi)
1180 return
1161 case replication.Completed, "COMPLETE":1181 case replication.Completed, "COMPLETE":
1162 sizeS.replicatedSize += oi.Size1182 sizeS.replicatedSize += oi.Size
1163 case replication.Replica:1183 case replication.Replica:
1164 sizeS.replicaSize += oi.Size1184 sizeS.replicaSize += oi.Size
1165 }1185 }
1186 if existingObjResync {
1187 globalReplicationPool.queueReplicaTask(roi)
1188 }
1166}1189}
1167 1190
1168// healReplicationDeletes will heal a scanned deleted item that failed to replicate deletes.1191// healReplicationDeletes will heal a scanned deleted item that failed to replicate deletes.
1169func (i *scannerItem) healReplicationDeletes(ctx context.Context, o ObjectLayer, oi ObjectInfo) {1192func (i *scannerItem) healReplicationDeletes(ctx context.Context, o ObjectLayer, oi ObjectInfo, existingObject bool) {
1170 // handle soft delete and permanent delete failures here.1193 // handle soft delete and permanent delete failures here.
1171 if oi.DeleteMarker || !oi.VersionPurgeStatus.Empty() {1194 if oi.DeleteMarker || !oi.VersionPurgeStatus.Empty() {
1172 versionID := ""1195 versionID := ""
@@ -1176,7 +1199,7 @@ func (i *scannerItem) healReplicationDeletes(ctx context.Context, o ObjectLayer, · 3 unmodified lines
1176 } else {1199 } else {
1177 versionID = oi.VersionID1200 versionID = oi.VersionID
1178 }1201 }
1179 globalReplicationPool.queueReplicaDeleteTask(DeletedObjectVersionInfo{1202 doi := DeletedObjectReplicationInfo{
1180 DeletedObject: DeletedObject{1203 DeletedObject: DeletedObject{
1181 ObjectName: oi.Name,1204 ObjectName: oi.Name,
1182 DeleteMarkerVersionID: dmVersionID,1205 DeleteMarkerVersionID: dmVersionID,
@@ -1187,7 +1210,12 @@ func (i *scannerItem) healReplicationDeletes(ctx context.Context, o ObjectLayer, · 4 unmodified lines
1187 VersionPurgeStatus: oi.VersionPurgeStatus,1210 VersionPurgeStatus: oi.VersionPurgeStatus,
1188 },1211 },
1189 Bucket: oi.Bucket,1212 Bucket: oi.Bucket,
1190 })1213 }
1214 if existingObject {
1215 doi.OpType = replication.ExistingObjectReplicationType
1216 doi.ResetID = i.replication.ResetID
1217 }
1218 globalReplicationPool.queueReplicaDeleteTask(doi)
1191 }1219 }
1192}1220}
1193 1221
cmd/data-usage-cache.go+2−1
@@ -161,7 +161,8 @@ type dataUsageCacheInfo struct { · 160 unmodified lines
161 // optional updates channel.161 // optional updates channel.
162 // If set updates will be sent regularly to this channel.162 // If set updates will be sent regularly to this channel.
163 // Will not be closed when returned.163 // Will not be closed when returned.
164 updates chan<- dataUsageEntry `msg:"-"`164 updates chan<- dataUsageEntry `msg:"-"`
165 replication replicationConfig `msg:"-"`
165}166}
166 167
167func (e *dataUsageEntry) addSizes(summary sizeSummary) {168func (e *dataUsageEntry) addSizes(summary sizeSummary) {
cmd/object-api-datatypes.go+1−0
@@ -230,6 +230,7 @@ type ReplicateObjectInfo struct { · 229 unmodified lines
230 ObjectInfo230 ObjectInfo
231 OpType replication.Type231 OpType replication.Type
232 RetryCount uint32232 RetryCount uint32
233 ResetID string
233}234}
234 235
235// MultipartInfo captures metadata information about the uploadId236// MultipartInfo captures metadata information about the uploadId
cmd/object-handlers.go+28−15
@@ -1291,7 +1291,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re · 1290 unmodified lines
1291 if rs := r.Header.Get(xhttp.AmzBucketReplicationStatus); rs != "" {1291 if rs := r.Header.Get(xhttp.AmzBucketReplicationStatus); rs != "" {
1292 srcInfo.UserDefined[xhttp.AmzBucketReplicationStatus] = rs1292 srcInfo.UserDefined[xhttp.AmzBucketReplicationStatus] = rs
1293 }1293 }
1294 if ok, _ := mustReplicate(ctx, r, dstBucket, dstObject, srcInfo.UserDefined, srcInfo.ReplicationStatus.String(), srcInfo.metadataOnly); ok {1294 if ok, _ := mustReplicate(ctx, r, dstBucket, dstObject, getMustReplicateOptions(srcInfo, replication.UnsetReplicationType)); ok {
1295 srcInfo.UserDefined[xhttp.AmzBucketReplicationStatus] = replication.Pending.String()1295 srcInfo.UserDefined[xhttp.AmzBucketReplicationStatus] = replication.Pending.String()
1296 }1296 }
1297 // Store the preserved compression metadata.1297 // Store the preserved compression metadata.
@@ -1393,7 +1393,8 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re · 95 unmodified lines
1393 objInfo.ETag = getDecryptedETag(r.Header, objInfo, false)1393 objInfo.ETag = getDecryptedETag(r.Header, objInfo, false)
1394 response := generateCopyObjectResponse(objInfo.ETag, objInfo.ModTime)1394 response := generateCopyObjectResponse(objInfo.ETag, objInfo.ModTime)
1395 encodedSuccessResponse := encodeResponse(response)1395 encodedSuccessResponse := encodeResponse(response)
1396 if replicate, sync := mustReplicate(ctx, r, dstBucket, dstObject, objInfo.UserDefined, objInfo.ReplicationStatus.String(), objInfo.metadataOnly); replicate {1396
1397 if replicate, sync := mustReplicate(ctx, r, dstBucket, dstObject, getMustReplicateOptions(objInfo, replication.UnsetReplicationType)); replicate {
1397 scheduleReplication(ctx, objInfo.Clone(), objectAPI, sync, replication.ObjectReplicationType)1398 scheduleReplication(ctx, objInfo.Clone(), objectAPI, sync, replication.ObjectReplicationType)
1398 }1399 }
1399 1400
@@ -1637,7 +1638,9 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req · 237 unmodified lines
1637 writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL, guessIsBrowserReq(r))1638 writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL, guessIsBrowserReq(r))
1638 return1639 return
1639 }1640 }
1640 if ok, _ := mustReplicate(ctx, r, bucket, object, metadata, "", false); ok {1641 if ok, _ := mustReplicate(ctx, r, bucket, object, getMustReplicateOptions(ObjectInfo{
1642 UserDefined: metadata,
1643 }, replication.ObjectReplicationType)); ok {
1641 metadata[xhttp.AmzBucketReplicationStatus] = replication.Pending.String()1644 metadata[xhttp.AmzBucketReplicationStatus] = replication.Pending.String()
1642 }1645 }
1643 if r.Header.Get(xhttp.AmzBucketReplicationStatus) == replication.Replica.String() {1646 if r.Header.Get(xhttp.AmzBucketReplicationStatus) == replication.Replica.String() {
@@ -1720,7 +1723,9 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req · 76 unmodified lines
1720 }1723 }
1721 }1724 }
1722 }1725 }
1723 if replicate, sync := mustReplicate(ctx, r, bucket, object, metadata, "", false); replicate {1726 if replicate, sync := mustReplicate(ctx, r, bucket, object, getMustReplicateOptions(ObjectInfo{
1727 UserDefined: metadata,
1728 }, replication.ObjectReplicationType)); replicate {
1724 scheduleReplication(ctx, objInfo.Clone(), objectAPI, sync, replication.ObjectReplicationType)1729 scheduleReplication(ctx, objInfo.Clone(), objectAPI, sync, replication.ObjectReplicationType)
1725 }1730 }
1726 1731
@@ -1956,7 +1961,9 @@ func (api objectAPIHandlers) PutObjectExtractHandler(w http.ResponseWriter, r *h · 229 unmodified lines
1956 return1961 return
1957 }1962 }
1958 1963
1959 if ok, _ := mustReplicate(ctx, r, bucket, object, metadata, "", false); ok {1964 if ok, _ := mustReplicate(ctx, r, bucket, object, getMustReplicateOptions(ObjectInfo{
1965 UserDefined: metadata,
1966 }, replication.ObjectReplicationType)); ok {
1960 metadata[xhttp.AmzBucketReplicationStatus] = replication.Pending.String()1967 metadata[xhttp.AmzBucketReplicationStatus] = replication.Pending.String()
1961 }1968 }
1962 1969
@@ -2012,7 +2019,9 @@ func (api objectAPIHandlers) PutObjectExtractHandler(w http.ResponseWriter, r *h · 49 unmodified lines
2012 return2019 return
2013 }2020 }
2014 2021
2015 if replicate, sync := mustReplicate(ctx, r, bucket, object, metadata, "", false); replicate {2022 if replicate, sync := mustReplicate(ctx, r, bucket, object, getMustReplicateOptions(ObjectInfo{
2023 UserDefined: metadata,
2024 }, replication.ObjectReplicationType)); replicate {
2016 scheduleReplication(ctx, objInfo.Clone(), objectAPI, sync, replication.ObjectReplicationType)2025 scheduleReplication(ctx, objInfo.Clone(), objectAPI, sync, replication.ObjectReplicationType)
2017 2026
2018 }2027 }
@@ -2124,7 +2133,9 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r · 105 unmodified lines
2124 writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL, guessIsBrowserReq(r))2133 writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL, guessIsBrowserReq(r))
2125 return2134 return
2126 }2135 }
2127 if ok, _ := mustReplicate(ctx, r, bucket, object, metadata, "", false); ok {2136 if ok, _ := mustReplicate(ctx, r, bucket, object, getMustReplicateOptions(ObjectInfo{
2137 UserDefined: metadata,
2138 }, replication.ObjectReplicationType)); ok {
2128 metadata[xhttp.AmzBucketReplicationStatus] = replication.Pending.String()2139 metadata[xhttp.AmzBucketReplicationStatus] = replication.Pending.String()
2129 }2140 }
2130 // We need to preserve the encryption headers set in EncryptRequest,2141 // We need to preserve the encryption headers set in EncryptRequest,
@@ -3114,7 +3125,7 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite · 983 unmodified lines
3114 }3125 }
3115 3126
3116 setPutObjHeaders(w, objInfo, false)3127 setPutObjHeaders(w, objInfo, false)
3117 if replicate, sync := mustReplicate(ctx, r, bucket, object, objInfo.UserDefined, objInfo.ReplicationStatus.String(), false); replicate {3128 if replicate, sync := mustReplicate(ctx, r, bucket, object, getMustReplicateOptions(objInfo, replication.ObjectReplicationType)); replicate {
3118 scheduleReplication(ctx, objInfo.Clone(), objectAPI, sync, replication.ObjectReplicationType)3129 scheduleReplication(ctx, objInfo.Clone(), objectAPI, sync, replication.ObjectReplicationType)
3119 }3130 }
3120 3131
@@ -3288,7 +3299,7 @@ func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http. · 167 unmodified lines
3288 } else {3299 } else {
3289 versionID = objInfo.VersionID3300 versionID = objInfo.VersionID
3290 }3301 }
3291 dobj := DeletedObjectVersionInfo{3302 dobj := DeletedObjectReplicationInfo{
3292 DeletedObject: DeletedObject{3303 DeletedObject: DeletedObject{
3293 ObjectName: object,3304 ObjectName: object,
3294 VersionID: versionID,3305 VersionID: versionID,
@@ -3375,7 +3386,7 @@ func (api objectAPIHandlers) PutObjectLegalHoldHandler(w http.ResponseWriter, r · 80 unmodified lines
3375 return3386 return
3376 }3387 }
3377 objInfo.UserDefined[strings.ToLower(xhttp.AmzObjectLockLegalHold)] = strings.ToUpper(string(legalHold.Status))3388 objInfo.UserDefined[strings.ToLower(xhttp.AmzObjectLockLegalHold)] = strings.ToUpper(string(legalHold.Status))
3378 replicate, sync := mustReplicate(ctx, r, bucket, object, objInfo.UserDefined, objInfo.ReplicationStatus.String(), true)3389 replicate, sync := mustReplicate(ctx, r, bucket, object, getMustReplicateOptions(objInfo, replication.MetadataReplicationType))
3379 if replicate {3390 if replicate {
3380 objInfo.UserDefined[xhttp.AmzBucketReplicationStatus] = replication.Pending.String()3391 objInfo.UserDefined[xhttp.AmzBucketReplicationStatus] = replication.Pending.String()
3381 }3392 }
@@ -3554,7 +3565,7 @@ func (api objectAPIHandlers) PutObjectRetentionHandler(w http.ResponseWriter, r · 172 unmodified lines
3554 objInfo.UserDefined[strings.ToLower(xhttp.AmzObjectLockMode)] = ""3565 objInfo.UserDefined[strings.ToLower(xhttp.AmzObjectLockMode)] = ""
3555 objInfo.UserDefined[strings.ToLower(xhttp.AmzObjectLockRetainUntilDate)] = ""3566 objInfo.UserDefined[strings.ToLower(xhttp.AmzObjectLockRetainUntilDate)] = ""
3556 }3567 }
3557 replicate, sync := mustReplicate(ctx, r, bucket, object, objInfo.UserDefined, objInfo.ReplicationStatus.String(), true)3568 replicate, sync := mustReplicate(ctx, r, bucket, object, getMustReplicateOptions(objInfo, replication.MetadataReplicationType))
3558 if replicate {3569 if replicate {
3559 objInfo.UserDefined[xhttp.AmzBucketReplicationStatus] = replication.Pending.String()3570 objInfo.UserDefined[xhttp.AmzBucketReplicationStatus] = replication.Pending.String()
3560 }3571 }
@@ -3751,14 +3762,16 @@ func (api objectAPIHandlers) PutObjectTaggingHandler(w http.ResponseWriter, r *h · 190 unmodified lines
3751 writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))3762 writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
3752 return3763 return
3753 }3764 }
3754 replicate, sync := mustReplicate(ctx, r, bucket, object, map[string]string{xhttp.AmzObjectTagging: tags.String()}, objInfo.ReplicationStatus.String(), true)3765 tagsStr := tags.String()
3766
3767 oi := objInfo.Clone()
3768 oi.UserTags = tagsStr
3769 replicate, sync := mustReplicate(ctx, r, bucket, object, getMustReplicateOptions(oi, replication.MetadataReplicationType))
3755 if replicate {3770 if replicate {
3756 opts.UserDefined = make(map[string]string)3771 opts.UserDefined = make(map[string]string)
3757 opts.UserDefined[xhttp.AmzBucketReplicationStatus] = replication.Pending.String()3772 opts.UserDefined[xhttp.AmzBucketReplicationStatus] = replication.Pending.String()
3758 }3773 }
3759 3774
3760 tagsStr := tags.String()
3761
3762 // Put object tags3775 // Put object tags
3763 objInfo, err = objAPI.PutObjectTags(ctx, bucket, object, tagsStr, opts)3776 objInfo, err = objAPI.PutObjectTags(ctx, bucket, object, tagsStr, opts)
3764 if err != nil {3777 if err != nil {
@@ -3828,7 +3841,7 @@ func (api objectAPIHandlers) DeleteObjectTaggingHandler(w http.ResponseWriter, r · 63 unmodified lines
3828 writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))3841 writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
3829 return3842 return
3830 }3843 }
3831 replicate, sync := mustReplicate(ctx, r, bucket, object, map[string]string{xhttp.AmzObjectTagging: oi.UserTags}, oi.ReplicationStatus.String(), true)3844 replicate, sync := mustReplicate(ctx, r, bucket, object, getMustReplicateOptions(oi, replication.MetadataReplicationType))
3832 if replicate {3845 if replicate {
3833 opts.UserDefined = make(map[string]string)3846 opts.UserDefined = make(map[string]string)
3834 opts.UserDefined[xhttp.AmzBucketReplicationStatus] = replication.Pending.String()3847 opts.UserDefined[xhttp.AmzBucketReplicationStatus] = replication.Pending.String()
cmd/web-handlers.go+3−3
@@ -814,7 +814,7 @@ next: · 813 unmodified lines
814 })814 })
815 815
816 if replicateDel {816 if replicateDel {
817 dobj := DeletedObjectVersionInfo{817 dobj := DeletedObjectReplicationInfo{
818 DeletedObject: DeletedObject{818 DeletedObject: DeletedObject{
819 ObjectName: objectName,819 ObjectName: objectName,
820 DeleteMarkerVersionID: oi.VersionID,820 DeleteMarkerVersionID: oi.VersionID,
@@ -948,7 +948,7 @@ next: · 127 unmodified lines
948 Host: sourceIP,948 Host: sourceIP,
949 })949 })
950 if dobj.DeleteMarkerReplicationStatus == string(replication.Pending) || dobj.VersionPurgeStatus == Pending {950 if dobj.DeleteMarkerReplicationStatus == string(replication.Pending) || dobj.VersionPurgeStatus == Pending {
951 dv := DeletedObjectVersionInfo{951 dv := DeletedObjectReplicationInfo{
952 DeletedObject: dobj,952 DeletedObject: dobj,
953 Bucket: args.BucketName,953 Bucket: args.BucketName,
954 }954 }
@@ -1256,7 +1256,7 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) { · 301 unmodified lines
1256 }1256 }
1257 }1257 }
1258 1258
1259 mustReplicate, sync := mustReplicateWeb(ctx, r, bucket, object, metadata, "", replPerms)1259 mustReplicate, sync := mustReplicateWeb(ctx, r, bucket, object, metadata, replication.StatusType(""), replPerms)
1260 if mustReplicate {1260 if mustReplicate {
1261 metadata[xhttp.AmzBucketReplicationStatus] = string(replication.Pending)1261 metadata[xhttp.AmzBucketReplicationStatus] = string(replication.Pending)
1262 }1262 }
cmd/xl-storage.go+13−1
@@ -414,6 +414,19 @@ func (s *xlStorage) NSScanner(ctx context.Context, cache dataUsageCache, updates · 413 unmodified lines
414 }414 }
415 }415 }
416 416
417 // Check if the current bucket has replication configuration
418 if rcfg, err := globalBucketMetadataSys.GetReplicationConfig(ctx, cache.Info.Name); err == nil {
419 if rcfg.HasActiveRules("", true) {
420 tgt := globalBucketTargetSys.GetRemoteBucketTargetByArn(ctx, cache.Info.Name, rcfg.RoleArn)
421 cache.Info.replication = replicationConfig{
422 Config: rcfg,
423 ResetID: tgt.ResetID,
424 ResetBeforeDate: tgt.ResetBeforeDate}
425 if intDataUpdateTracker.debug {
426 console.Debugln(color.Green("scannerDisk:") + " replication: Active rules found")
427 }
428 }
429 }
417 // return initialized object layer430 // return initialized object layer
418 objAPI := newObjectLayerFn()431 objAPI := newObjectLayerFn()
419 // object layer not initialized, return.432 // object layer not initialized, return.
@@ -452,7 +465,6 @@ func (s *xlStorage) NSScanner(ctx context.Context, cache dataUsageCache, updates · 32 unmodified lines
452 }465 }
453 return sizeSummary{}, errSkipFile466 return sizeSummary{}, errSkipFile
454 }467 }
455
456 sizeS := sizeSummary{}468 sizeS := sizeSummary{}
457 for _, version := range fivs.Versions {469 for _, version := range fivs.Versions {
458 oi := version.ToObjectInfo(item.bucket, item.objectPath())470 oi := version.ToObjectInfo(item.bucket, item.objectPath())
docs/bucket/replication/DESIGN.md+5−1
@@ -33,7 +33,11 @@ An additional header `X-Minio-Replication-Delete-Status` is returned which would · 32 unmodified lines
33 33
34Note that synchronous replication, i.e. when remote target is configured with --sync mode in `mc admin bucket remote add` does not apply to `DELETE` operations. The version being deleted on the source cluster needs to maintain state and ensure that the operation is mirrored to the target cluster prior to completing on the source object version. Since this needs to account for the target cluster availability and the need to serialize concurrent DELETE operations on different versions of the same object during multi DELETE operations, the current implementation queues the `DELETE` operations in both sync and async modes.34Note that synchronous replication, i.e. when remote target is configured with --sync mode in `mc admin bucket remote add` does not apply to `DELETE` operations. The version being deleted on the source cluster needs to maintain state and ensure that the operation is mirrored to the target cluster prior to completing on the source object version. Since this needs to account for the target cluster availability and the need to serialize concurrent DELETE operations on different versions of the same object during multi DELETE operations, the current implementation queues the `DELETE` operations in both sync and async modes.
35 35
36Existing object replication, replica modification sync for 2-way replication and multi site replication are currently not supported.36### Existing object replication
37Existing object replication works similar to regular replication. Objects qualifying for existing object replication are detected when scanner runs, and will be replicated if existing object replication is enabled and applicable replication rules are satisfied. Because replication depends on the immutability of versions, only pre-existing objects created while versioning was enabled can be replicated. Even if replication rules are disabled and re-enabled later, the objects created during the interim will be synced as the scanner queues them. For saving iops, objects qualifying for
38existing object replication are not marked as `PENDING` prior to replication.
39
40If the remote site is fully lost and objects previously replicated need to be re-synced, the `mc replicate reset` command with optional flag of `--older-than` needs to be used to trigger re-syncing of previously replicated objects. This command generates a ResetID which is a unique UUID saved to the remote target config along with the applicable date(defaults to time of initiating the reset). All objects created prior to this date are eligible for re-replication if existing object replication is enabled for the replication rule the object satisifes. At the time of completion of replication, `X-Minio-Replication-Reset-Status` is set in the metadata with the timestamp of replication and ResetID. For saving iops, the objects which are re-replicated are not first set to `PENDING` state.
37 41
38### Internal metadata for replication42### Internal metadata for replication
39 43
docs/bucket/replication/README.md+15−0
@@ -202,6 +202,21 @@ remote replication target using the `mc admin bucket remote add` command · 201 unmodified lines
202 mc admin bucket remote add myminio/srcbucket https://accessKey:secretKey@replica-endpoint:9000/destbucket --service replication --region us-east-1 --sync --healthcheck-seconds 100202 mc admin bucket remote add myminio/srcbucket https://accessKey:secretKey@replica-endpoint:9000/destbucket --service replication --region us-east-1 --sync --healthcheck-seconds 100
203```203```
204 204
205### Existing object replication
206Existing object replication as detailed [here](https://aws.amazon.com/blogs/storage/replicating-existing-objects-between-s3-buckets/) can be enabled by passing `existing-objects` as a value to `--replicate` flag while adding or editing a replication rule.
207
208Once existing object replication is enabled, all objects or object prefixes that satisfy the replication rules and were created prior to adding replication configuration OR while replication rules were disabled will be synced to the target cluster. Depending on the number of previously existing objects, the existing objects that are now eligible to be replicated will eventually be synced to the target cluster as the scanner schedules them. This may be slower depending on the load on the cluster, latency and size of the namespace.
209
210Note that existing object replication requires that the objects being replicated were created while versioning was enabled. Objects with null version (i.e. those created when versioning is not enabled or versioning is suspended) will not be replicated.
211
212In the rare event that target DR site is entirely lost and previously replicated objects to the DR cluster need to be re-replicated, `mc replicate reset alias/bucket` can be used to initiate a reset. This would initiate a re-sync between the two clusters on a lower priority as the scanner picks up these objects to re-sync.
213
214This is an expensive operation and should be initiated only once - progress of the syncing can be monitored by looking at Prometheus metrics. If object version has been re-replicated, `mc stat --vid --debug` on this version shows an additional header `X-Minio-Replication-Reset-Status` with the replication timestamp and ResetID generated at the time of issuing the `mc replicate reset` command.
215
216Note that ExistingObjectReplication needs to be enabled in the config via `mc replicate [add|edit]` by passing `existing-objects` as one of the values to `--replicate` flag. Only those objects meeting replication rules and having existing object replication enabled will be re-synced.
217
218Multi site replication is currently not supported.
219
205## Explore Further220## Explore Further
206- [MinIO Bucket Replication Design](https://github.com/minio/minio/blob/master/docs/bucket/replication/DESIGN.md)221- [MinIO Bucket Replication Design](https://github.com/minio/minio/blob/master/docs/bucket/replication/DESIGN.md)
207- [MinIO Bucket Versioning Implementation](https://docs.minio.io/docs/minio-bucket-versioning-guide.html)222- [MinIO Bucket Versioning Implementation](https://docs.minio.io/docs/minio-bucket-versioning-guide.html)
go.mod+1−1
@@ -47,7 +47,7 @@ require ( · 46 unmodified lines
47 github.com/minio/madmin-go v1.0.947 github.com/minio/madmin-go v1.0.9
48 github.com/minio/minio-go/v7 v7.0.11-0.20210302210017-6ae69c73ce7848 github.com/minio/minio-go/v7 v7.0.11-0.20210302210017-6ae69c73ce78
49 github.com/minio/parquet-go v1.0.049 github.com/minio/parquet-go v1.0.0
50 github.com/minio/pkg v1.0.350 github.com/minio/pkg v1.0.4
51 github.com/minio/rpc v1.0.051 github.com/minio/rpc v1.0.0
52 github.com/minio/selfupdate v0.3.152 github.com/minio/selfupdate v0.3.1
53 github.com/minio/sha256-simd v1.0.053 github.com/minio/sha256-simd v1.0.0
go.sum+2−0
@@ -494,6 +494,8 @@ github.com/minio/parquet-go v1.0.0 h1:fcWsEvub04Nsl/4hiRBDWlbqd6jhacQieV07a+nhiI · 493 unmodified lines
494github.com/minio/parquet-go v1.0.0/go.mod h1:aQlkSOfOq2AtQKkuou3mosNVMwNokd+faTacxxk/oHA=494github.com/minio/parquet-go v1.0.0/go.mod h1:aQlkSOfOq2AtQKkuou3mosNVMwNokd+faTacxxk/oHA=
495github.com/minio/pkg v1.0.3 h1:tUhM6lG/BdNB0+5f2RbE4ifCAYwMs6cRJnZ/AY0WIeQ=495github.com/minio/pkg v1.0.3 h1:tUhM6lG/BdNB0+5f2RbE4ifCAYwMs6cRJnZ/AY0WIeQ=
496github.com/minio/pkg v1.0.3/go.mod h1:obU54TZ9QlMv0TRaDgQ/JTzf11ZSXxnSfLrm4tMtBP8=496github.com/minio/pkg v1.0.3/go.mod h1:obU54TZ9QlMv0TRaDgQ/JTzf11ZSXxnSfLrm4tMtBP8=
497github.com/minio/pkg v1.0.4 h1:+BmaCENP6BaMm9PsGK6L1L5MKulWDxl4qobvJYf6m/E=
498github.com/minio/pkg v1.0.4/go.mod h1:obU54TZ9QlMv0TRaDgQ/JTzf11ZSXxnSfLrm4tMtBP8=
497github.com/minio/rpc v1.0.0 h1:tJCHyLfQF6k6HlMQFpKy2FO/7lc2WP8gLDGMZp18E70=499github.com/minio/rpc v1.0.0 h1:tJCHyLfQF6k6HlMQFpKy2FO/7lc2WP8gLDGMZp18E70=
498github.com/minio/rpc v1.0.0/go.mod h1:b9xqF7J0xeMXr0cM4pnBlP7Te7PDsG5JrRxl5dG6Ldk=500github.com/minio/rpc v1.0.0/go.mod h1:b9xqF7J0xeMXr0cM4pnBlP7Te7PDsG5JrRxl5dG6Ldk=
499github.com/minio/selfupdate v0.3.1 h1:BWEFSNnrZVMUWXbXIgLDNDjbejkmpAmZvy/nCz1HlEs=501github.com/minio/selfupdate v0.3.1 h1:BWEFSNnrZVMUWXbXIgLDNDjbejkmpAmZvy/nCz1HlEs=
internal/bucket/replication/replication.go+20−9
@@ -131,23 +131,31 @@ type Type int · 130 unmodified lines
131 131
132// Types of replication132// Types of replication
133const (133const (
134 ObjectReplicationType Type = 1 + iota134 UnsetReplicationType Type = 0 + iota
135 ObjectReplicationType
135 DeleteReplicationType136 DeleteReplicationType
136 MetadataReplicationType137 MetadataReplicationType
137 HealReplicationType138 HealReplicationType
139 ExistingObjectReplicationType
138)140)
139 141
142// Valid returns true if replication type is set
143func (t Type) Valid() bool {
144 return t > 0
145}
146
140// ObjectOpts provides information to deduce whether replication147// ObjectOpts provides information to deduce whether replication
141// can be triggered on the resultant object.148// can be triggered on the resultant object.
142type ObjectOpts struct {149type ObjectOpts struct {
143 Name string150 Name string
144 UserTags string151 UserTags string
145 VersionID string152 VersionID string
146 IsLatest bool153 IsLatest bool
147 DeleteMarker bool154 DeleteMarker bool
148 SSEC bool155 SSEC bool
149 OpType Type156 OpType Type
150 Replica bool157 Replica bool
158 ExistingObject bool
151}159}
152 160
153// FilterActionableRules returns the rules actions that need to be executed161// FilterActionableRules returns the rules actions that need to be executed
@@ -191,6 +199,9 @@ func (c Config) Replicate(obj ObjectOpts) bool { · 37 unmodified lines
191 if rule.Status == Disabled {199 if rule.Status == Disabled {
192 continue200 continue
193 }201 }
202 if obj.ExistingObject && rule.ExistingObjectReplication.Status == Disabled {
203 return false
204 }
194 if obj.OpType == DeleteReplicationType {205 if obj.OpType == DeleteReplicationType {
195 switch {206 switch {
196 case obj.VersionID != "":207 case obj.VersionID != "":
internal/bucket/replication/rule.go+53−14
@@ -90,6 +90,43 @@ func (d *DeleteReplication) UnmarshalXML(dec *xml.Decoder, start xml.StartElemen · 89 unmodified lines
90 return nil90 return nil
91}91}
92 92
93// ExistingObjectReplication - whether existing object replication is enabled
94type ExistingObjectReplication struct {
95 Status Status `xml:"Status"` // should be set to "Disabled" by default
96}
97
98// IsEmpty returns true if ExistingObjectReplication is not set
99func (e ExistingObjectReplication) IsEmpty() bool {
100 return len(e.Status) == 0
101}
102
103// Validate validates whether the status is disabled.
104func (e ExistingObjectReplication) Validate() error {
105 if e.IsEmpty() {
106 return nil
107 }
108 if e.Status != Disabled && e.Status != Enabled {
109 return errInvalidExistingObjectReplicationStatus
110 }
111 return nil
112}
113
114// UnmarshalXML - decodes XML data. Default to Disabled unless specified
115func (e *ExistingObjectReplication) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) (err error) {
116 // Make subtype to avoid recursive UnmarshalXML().
117 type existingObjectReplication ExistingObjectReplication
118 erep := existingObjectReplication{}
119
120 if err := dec.DecodeElement(&erep, &start); err != nil {
121 return err
122 }
123 if len(erep.Status) == 0 {
124 erep.Status = Disabled
125 }
126 e.Status = erep.Status
127 return nil
128}
129
93// Rule - a rule for replication configuration.130// Rule - a rule for replication configuration.
94type Rule struct {131type Rule struct {
95 XMLName xml.Name `xml:"Rule" json:"Rule"`132 XMLName xml.Name `xml:"Rule" json:"Rule"`
@@ -98,22 +135,24 @@ type Rule struct { · 2 unmodified lines
98 Priority int `xml:"Priority" json:"Priority"`135 Priority int `xml:"Priority" json:"Priority"`
99 DeleteMarkerReplication DeleteMarkerReplication `xml:"DeleteMarkerReplication" json:"DeleteMarkerReplication"`136 DeleteMarkerReplication DeleteMarkerReplication `xml:"DeleteMarkerReplication" json:"DeleteMarkerReplication"`
100 // MinIO extension to replicate versioned deletes137 // MinIO extension to replicate versioned deletes
101 DeleteReplication DeleteReplication `xml:"DeleteReplication" json:"DeleteReplication"`138 DeleteReplication DeleteReplication `xml:"DeleteReplication" json:"DeleteReplication"`
102 Destination Destination `xml:"Destination" json:"Destination"`139 Destination Destination `xml:"Destination" json:"Destination"`
103 SourceSelectionCriteria SourceSelectionCriteria `xml:"SourceSelectionCriteria" json:"SourceSelectionCriteria"`140 SourceSelectionCriteria SourceSelectionCriteria `xml:"SourceSelectionCriteria" json:"SourceSelectionCriteria"`
104 Filter Filter `xml:"Filter" json:"Filter"`141 Filter Filter `xml:"Filter" json:"Filter"`
142 ExistingObjectReplication ExistingObjectReplication `xml:"ExistingObjectReplication,omitempty" json:"ExistingObjectReplication,omitempty"`
105}143}
106 144
107var (145var (
108 errInvalidRuleID = Errorf("ID must be less than 255 characters")146 errInvalidRuleID = Errorf("ID must be less than 255 characters")
109 errEmptyRuleStatus = Errorf("Status should not be empty")147 errEmptyRuleStatus = Errorf("Status should not be empty")
110 errInvalidRuleStatus = Errorf("Status must be set to either Enabled or Disabled")148 errInvalidRuleStatus = Errorf("Status must be set to either Enabled or Disabled")
111 errDeleteMarkerReplicationMissing = Errorf("DeleteMarkerReplication must be specified")149 errDeleteMarkerReplicationMissing = Errorf("DeleteMarkerReplication must be specified")
112 errPriorityMissing = Errorf("Priority must be specified")150 errPriorityMissing = Errorf("Priority must be specified")
113 errInvalidDeleteMarkerReplicationStatus = Errorf("Delete marker replication status is invalid")151 errInvalidDeleteMarkerReplicationStatus = Errorf("Delete marker replication status is invalid")
114 errDestinationSourceIdentical = Errorf("Destination bucket cannot be the same as the source bucket.")152 errDestinationSourceIdentical = Errorf("Destination bucket cannot be the same as the source bucket.")
115 errDeleteReplicationMissing = Errorf("Delete replication must be specified")153 errDeleteReplicationMissing = Errorf("Delete replication must be specified")
116 errInvalidDeleteReplicationStatus = Errorf("Delete replication is either enable|disable")154 errInvalidDeleteReplicationStatus = Errorf("Delete replication is either enable|disable")
155 errInvalidExistingObjectReplicationStatus = Errorf("Existing object replication status is invalid")
117)156)
118 157
119// validateID - checks if ID is valid or not.158// validateID - checks if ID is valid or not.
@@ -200,7 +239,7 @@ func (r Rule) Validate(bucket string, sameTarget bool) error { · 80 unmodified lines
200 if r.Destination.Bucket == bucket && sameTarget {239 if r.Destination.Bucket == bucket && sameTarget {
201 return errDestinationSourceIdentical240 return errDestinationSourceIdentical
202 }241 }
203 return nil242 return r.ExistingObjectReplication.Validate()
204}243}
205 244
206// MetadataReplicate returns true if object is not a replica or in the case of replicas,245// MetadataReplicate returns true if object is not a replica or in the case of replicas,
internal/http/headers.go+3−0
@@ -170,6 +170,9 @@ const ( · 169 unmodified lines
170 MinIOSourceProxyRequest = "X-Minio-Source-Proxy-Request"170 MinIOSourceProxyRequest = "X-Minio-Source-Proxy-Request"
171 // Header indicates that this request is a replication request to create a REPLICA171 // Header indicates that this request is a replication request to create a REPLICA
172 MinIOSourceReplicationRequest = "X-Minio-Source-Replication-Request"172 MinIOSourceReplicationRequest = "X-Minio-Source-Replication-Request"
173 // Header indicates replication reset status.
174 MinIOReplicationResetStatus = "X-Minio-Replication-Reset-Status"
175
173 // predicted date/time of transition176 // predicted date/time of transition
174 MinIOTransition = "X-Minio-Transition"177 MinIOTransition = "X-Minio-Transition"
175)178)

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t