diff --git a/cmd/api-errors.go b/cmd/api-errors.go index fabb2dc8be895..1fd6b6290c78e 100644 --- a/cmd/api-errors.go +++ b/cmd/api-errors.go @@ -109,6 +109,7 @@ const ( ErrNoSuchBucketPolicy ErrNoSuchBucketLifecycle ErrNoSuchLifecycleConfiguration + ErrInvalidLifecycleWithObjectLock ErrNoSuchBucketSSEConfig ErrNoSuchCORSConfiguration ErrNoSuchWebsiteConfiguration @@ -577,6 +578,11 @@ var errorCodes = errorCodeMap{ Description: "The lifecycle configuration does not exist", HTTPStatusCode: http.StatusNotFound, }, + ErrInvalidLifecycleWithObjectLock: { + Code: "InvalidLifecycleWithObjectLock", + Description: "The lifecycle configuration containing MaxNoncurrentVersions is not supported with object locking", + HTTPStatusCode: http.StatusBadRequest, + }, ErrNoSuchBucketSSEConfig: { Code: "ServerSideEncryptionConfigurationNotFoundError", Description: "The server side encryption configuration was not found", diff --git a/cmd/apierrorcode_string.go b/cmd/apierrorcode_string.go index 98d61d18e377b..b8b40ecd5041c 100644 --- a/cmd/apierrorcode_string.go +++ b/cmd/apierrorcode_string.go @@ -44,258 +44,259 @@ func _() { _ = x[ErrNoSuchBucketPolicy-33] _ = x[ErrNoSuchBucketLifecycle-34] _ = x[ErrNoSuchLifecycleConfiguration-35] - _ = x[ErrNoSuchBucketSSEConfig-36] - _ = x[ErrNoSuchCORSConfiguration-37] - _ = x[ErrNoSuchWebsiteConfiguration-38] - _ = x[ErrReplicationConfigurationNotFoundError-39] - _ = x[ErrRemoteDestinationNotFoundError-40] - _ = x[ErrReplicationDestinationMissingLock-41] - _ = x[ErrRemoteTargetNotFoundError-42] - _ = x[ErrReplicationRemoteConnectionError-43] - _ = x[ErrReplicationBandwidthLimitError-44] - _ = x[ErrBucketRemoteIdenticalToSource-45] - _ = x[ErrBucketRemoteAlreadyExists-46] - _ = x[ErrBucketRemoteLabelInUse-47] - _ = x[ErrBucketRemoteArnTypeInvalid-48] - _ = x[ErrBucketRemoteArnInvalid-49] - _ = x[ErrBucketRemoteRemoveDisallowed-50] - _ = x[ErrRemoteTargetNotVersionedError-51] - _ = x[ErrReplicationSourceNotVersionedError-52] - _ = x[ErrReplicationNeedsVersioningError-53] - _ = x[ErrReplicationBucketNeedsVersioningError-54] - _ = x[ErrReplicationNoMatchingRuleError-55] - _ = x[ErrObjectRestoreAlreadyInProgress-56] - _ = x[ErrNoSuchKey-57] - _ = x[ErrNoSuchUpload-58] - _ = x[ErrInvalidVersionID-59] - _ = x[ErrNoSuchVersion-60] - _ = x[ErrNotImplemented-61] - _ = x[ErrPreconditionFailed-62] - _ = x[ErrRequestTimeTooSkewed-63] - _ = x[ErrSignatureDoesNotMatch-64] - _ = x[ErrMethodNotAllowed-65] - _ = x[ErrInvalidPart-66] - _ = x[ErrInvalidPartOrder-67] - _ = x[ErrAuthorizationHeaderMalformed-68] - _ = x[ErrMalformedPOSTRequest-69] - _ = x[ErrPOSTFileRequired-70] - _ = x[ErrSignatureVersionNotSupported-71] - _ = x[ErrBucketNotEmpty-72] - _ = x[ErrAllAccessDisabled-73] - _ = x[ErrMalformedPolicy-74] - _ = x[ErrMissingFields-75] - _ = x[ErrMissingCredTag-76] - _ = x[ErrCredMalformed-77] - _ = x[ErrInvalidRegion-78] - _ = x[ErrInvalidServiceS3-79] - _ = x[ErrInvalidServiceSTS-80] - _ = x[ErrInvalidRequestVersion-81] - _ = x[ErrMissingSignTag-82] - _ = x[ErrMissingSignHeadersTag-83] - _ = x[ErrMalformedDate-84] - _ = x[ErrMalformedPresignedDate-85] - _ = x[ErrMalformedCredentialDate-86] - _ = x[ErrMalformedCredentialRegion-87] - _ = x[ErrMalformedExpires-88] - _ = x[ErrNegativeExpires-89] - _ = x[ErrAuthHeaderEmpty-90] - _ = x[ErrExpiredPresignRequest-91] - _ = x[ErrRequestNotReadyYet-92] - _ = x[ErrUnsignedHeaders-93] - _ = x[ErrMissingDateHeader-94] - _ = x[ErrInvalidQuerySignatureAlgo-95] - _ = x[ErrInvalidQueryParams-96] - _ = x[ErrBucketAlreadyOwnedByYou-97] - _ = x[ErrInvalidDuration-98] - _ = x[ErrBucketAlreadyExists-99] - _ = x[ErrMetadataTooLarge-100] - _ = x[ErrUnsupportedMetadata-101] - _ = x[ErrMaximumExpires-102] - _ = x[ErrSlowDown-103] - _ = x[ErrInvalidPrefixMarker-104] - _ = x[ErrBadRequest-105] - _ = x[ErrKeyTooLongError-106] - _ = x[ErrInvalidBucketObjectLockConfiguration-107] - _ = x[ErrObjectLockConfigurationNotFound-108] - _ = x[ErrObjectLockConfigurationNotAllowed-109] - _ = x[ErrNoSuchObjectLockConfiguration-110] - _ = x[ErrObjectLocked-111] - _ = x[ErrInvalidRetentionDate-112] - _ = x[ErrPastObjectLockRetainDate-113] - _ = x[ErrUnknownWORMModeDirective-114] - _ = x[ErrBucketTaggingNotFound-115] - _ = x[ErrObjectLockInvalidHeaders-116] - _ = x[ErrInvalidTagDirective-117] - _ = x[ErrInvalidEncryptionMethod-118] - _ = x[ErrInsecureSSECustomerRequest-119] - _ = x[ErrSSEMultipartEncrypted-120] - _ = x[ErrSSEEncryptedObject-121] - _ = x[ErrInvalidEncryptionParameters-122] - _ = x[ErrInvalidSSECustomerAlgorithm-123] - _ = x[ErrInvalidSSECustomerKey-124] - _ = x[ErrMissingSSECustomerKey-125] - _ = x[ErrMissingSSECustomerKeyMD5-126] - _ = x[ErrSSECustomerKeyMD5Mismatch-127] - _ = x[ErrInvalidSSECustomerParameters-128] - _ = x[ErrIncompatibleEncryptionMethod-129] - _ = x[ErrKMSNotConfigured-130] - _ = x[ErrNoAccessKey-131] - _ = x[ErrInvalidToken-132] - _ = x[ErrEventNotification-133] - _ = x[ErrARNNotification-134] - _ = x[ErrRegionNotification-135] - _ = x[ErrOverlappingFilterNotification-136] - _ = x[ErrFilterNameInvalid-137] - _ = x[ErrFilterNamePrefix-138] - _ = x[ErrFilterNameSuffix-139] - _ = x[ErrFilterValueInvalid-140] - _ = x[ErrOverlappingConfigs-141] - _ = x[ErrUnsupportedNotification-142] - _ = x[ErrContentSHA256Mismatch-143] - _ = x[ErrReadQuorum-144] - _ = x[ErrWriteQuorum-145] - _ = x[ErrStorageFull-146] - _ = x[ErrRequestBodyParse-147] - _ = x[ErrObjectExistsAsDirectory-148] - _ = x[ErrInvalidObjectName-149] - _ = x[ErrInvalidObjectNamePrefixSlash-150] - _ = x[ErrInvalidResourceName-151] - _ = x[ErrServerNotInitialized-152] - _ = x[ErrOperationTimedOut-153] - _ = x[ErrClientDisconnected-154] - _ = x[ErrOperationMaxedOut-155] - _ = x[ErrInvalidRequest-156] - _ = x[ErrTransitionStorageClassNotFoundError-157] - _ = x[ErrInvalidStorageClass-158] - _ = x[ErrBackendDown-159] - _ = x[ErrMalformedJSON-160] - _ = x[ErrAdminNoSuchUser-161] - _ = x[ErrAdminNoSuchGroup-162] - _ = x[ErrAdminGroupNotEmpty-163] - _ = x[ErrAdminNoSuchPolicy-164] - _ = x[ErrAdminInvalidArgument-165] - _ = x[ErrAdminInvalidAccessKey-166] - _ = x[ErrAdminInvalidSecretKey-167] - _ = x[ErrAdminConfigNoQuorum-168] - _ = x[ErrAdminConfigTooLarge-169] - _ = x[ErrAdminConfigBadJSON-170] - _ = x[ErrAdminConfigDuplicateKeys-171] - _ = x[ErrAdminCredentialsMismatch-172] - _ = x[ErrInsecureClientRequest-173] - _ = x[ErrObjectTampered-174] - _ = x[ErrSiteReplicationInvalidRequest-175] - _ = x[ErrSiteReplicationPeerResp-176] - _ = x[ErrSiteReplicationBackendIssue-177] - _ = x[ErrSiteReplicationServiceAccountError-178] - _ = x[ErrSiteReplicationBucketConfigError-179] - _ = x[ErrSiteReplicationBucketMetaError-180] - _ = x[ErrSiteReplicationIAMError-181] - _ = x[ErrAdminBucketQuotaExceeded-182] - _ = x[ErrAdminNoSuchQuotaConfiguration-183] - _ = x[ErrHealNotImplemented-184] - _ = x[ErrHealNoSuchProcess-185] - _ = x[ErrHealInvalidClientToken-186] - _ = x[ErrHealMissingBucket-187] - _ = x[ErrHealAlreadyRunning-188] - _ = x[ErrHealOverlappingPaths-189] - _ = x[ErrIncorrectContinuationToken-190] - _ = x[ErrEmptyRequestBody-191] - _ = x[ErrUnsupportedFunction-192] - _ = x[ErrInvalidExpressionType-193] - _ = x[ErrBusy-194] - _ = x[ErrUnauthorizedAccess-195] - _ = x[ErrExpressionTooLong-196] - _ = x[ErrIllegalSQLFunctionArgument-197] - _ = x[ErrInvalidKeyPath-198] - _ = x[ErrInvalidCompressionFormat-199] - _ = x[ErrInvalidFileHeaderInfo-200] - _ = x[ErrInvalidJSONType-201] - _ = x[ErrInvalidQuoteFields-202] - _ = x[ErrInvalidRequestParameter-203] - _ = x[ErrInvalidDataType-204] - _ = x[ErrInvalidTextEncoding-205] - _ = x[ErrInvalidDataSource-206] - _ = x[ErrInvalidTableAlias-207] - _ = x[ErrMissingRequiredParameter-208] - _ = x[ErrObjectSerializationConflict-209] - _ = x[ErrUnsupportedSQLOperation-210] - _ = x[ErrUnsupportedSQLStructure-211] - _ = x[ErrUnsupportedSyntax-212] - _ = x[ErrUnsupportedRangeHeader-213] - _ = x[ErrLexerInvalidChar-214] - _ = x[ErrLexerInvalidOperator-215] - _ = x[ErrLexerInvalidLiteral-216] - _ = x[ErrLexerInvalidIONLiteral-217] - _ = x[ErrParseExpectedDatePart-218] - _ = x[ErrParseExpectedKeyword-219] - _ = x[ErrParseExpectedTokenType-220] - _ = x[ErrParseExpected2TokenTypes-221] - _ = x[ErrParseExpectedNumber-222] - _ = x[ErrParseExpectedRightParenBuiltinFunctionCall-223] - _ = x[ErrParseExpectedTypeName-224] - _ = x[ErrParseExpectedWhenClause-225] - _ = x[ErrParseUnsupportedToken-226] - _ = x[ErrParseUnsupportedLiteralsGroupBy-227] - _ = x[ErrParseExpectedMember-228] - _ = x[ErrParseUnsupportedSelect-229] - _ = x[ErrParseUnsupportedCase-230] - _ = x[ErrParseUnsupportedCaseClause-231] - _ = x[ErrParseUnsupportedAlias-232] - _ = x[ErrParseUnsupportedSyntax-233] - _ = x[ErrParseUnknownOperator-234] - _ = x[ErrParseMissingIdentAfterAt-235] - _ = x[ErrParseUnexpectedOperator-236] - _ = x[ErrParseUnexpectedTerm-237] - _ = x[ErrParseUnexpectedToken-238] - _ = x[ErrParseUnexpectedKeyword-239] - _ = x[ErrParseExpectedExpression-240] - _ = x[ErrParseExpectedLeftParenAfterCast-241] - _ = x[ErrParseExpectedLeftParenValueConstructor-242] - _ = x[ErrParseExpectedLeftParenBuiltinFunctionCall-243] - _ = x[ErrParseExpectedArgumentDelimiter-244] - _ = x[ErrParseCastArity-245] - _ = x[ErrParseInvalidTypeParam-246] - _ = x[ErrParseEmptySelect-247] - _ = x[ErrParseSelectMissingFrom-248] - _ = x[ErrParseExpectedIdentForGroupName-249] - _ = x[ErrParseExpectedIdentForAlias-250] - _ = x[ErrParseUnsupportedCallWithStar-251] - _ = x[ErrParseNonUnaryAgregateFunctionCall-252] - _ = x[ErrParseMalformedJoin-253] - _ = x[ErrParseExpectedIdentForAt-254] - _ = x[ErrParseAsteriskIsNotAloneInSelectList-255] - _ = x[ErrParseCannotMixSqbAndWildcardInSelectList-256] - _ = x[ErrParseInvalidContextForWildcardInSelectList-257] - _ = x[ErrIncorrectSQLFunctionArgumentType-258] - _ = x[ErrValueParseFailure-259] - _ = x[ErrEvaluatorInvalidArguments-260] - _ = x[ErrIntegerOverflow-261] - _ = x[ErrLikeInvalidInputs-262] - _ = x[ErrCastFailed-263] - _ = x[ErrInvalidCast-264] - _ = x[ErrEvaluatorInvalidTimestampFormatPattern-265] - _ = x[ErrEvaluatorInvalidTimestampFormatPatternSymbolForParsing-266] - _ = x[ErrEvaluatorTimestampFormatPatternDuplicateFields-267] - _ = x[ErrEvaluatorTimestampFormatPatternHourClockAmPmMismatch-268] - _ = x[ErrEvaluatorUnterminatedTimestampFormatPatternToken-269] - _ = x[ErrEvaluatorInvalidTimestampFormatPatternToken-270] - _ = x[ErrEvaluatorInvalidTimestampFormatPatternSymbol-271] - _ = x[ErrEvaluatorBindingDoesNotExist-272] - _ = x[ErrMissingHeaders-273] - _ = x[ErrInvalidColumnIndex-274] - _ = x[ErrAdminConfigNotificationTargetsFailed-275] - _ = x[ErrAdminProfilerNotEnabled-276] - _ = x[ErrInvalidDecompressedSize-277] - _ = x[ErrAddUserInvalidArgument-278] - _ = x[ErrAdminAccountNotEligible-279] - _ = x[ErrAccountNotEligible-280] - _ = x[ErrAdminServiceAccountNotFound-281] - _ = x[ErrPostPolicyConditionInvalidFormat-282] + _ = x[ErrInvalidLifecycleWithObjectLock-36] + _ = x[ErrNoSuchBucketSSEConfig-37] + _ = x[ErrNoSuchCORSConfiguration-38] + _ = x[ErrNoSuchWebsiteConfiguration-39] + _ = x[ErrReplicationConfigurationNotFoundError-40] + _ = x[ErrRemoteDestinationNotFoundError-41] + _ = x[ErrReplicationDestinationMissingLock-42] + _ = x[ErrRemoteTargetNotFoundError-43] + _ = x[ErrReplicationRemoteConnectionError-44] + _ = x[ErrReplicationBandwidthLimitError-45] + _ = x[ErrBucketRemoteIdenticalToSource-46] + _ = x[ErrBucketRemoteAlreadyExists-47] + _ = x[ErrBucketRemoteLabelInUse-48] + _ = x[ErrBucketRemoteArnTypeInvalid-49] + _ = x[ErrBucketRemoteArnInvalid-50] + _ = x[ErrBucketRemoteRemoveDisallowed-51] + _ = x[ErrRemoteTargetNotVersionedError-52] + _ = x[ErrReplicationSourceNotVersionedError-53] + _ = x[ErrReplicationNeedsVersioningError-54] + _ = x[ErrReplicationBucketNeedsVersioningError-55] + _ = x[ErrReplicationNoMatchingRuleError-56] + _ = x[ErrObjectRestoreAlreadyInProgress-57] + _ = x[ErrNoSuchKey-58] + _ = x[ErrNoSuchUpload-59] + _ = x[ErrInvalidVersionID-60] + _ = x[ErrNoSuchVersion-61] + _ = x[ErrNotImplemented-62] + _ = x[ErrPreconditionFailed-63] + _ = x[ErrRequestTimeTooSkewed-64] + _ = x[ErrSignatureDoesNotMatch-65] + _ = x[ErrMethodNotAllowed-66] + _ = x[ErrInvalidPart-67] + _ = x[ErrInvalidPartOrder-68] + _ = x[ErrAuthorizationHeaderMalformed-69] + _ = x[ErrMalformedPOSTRequest-70] + _ = x[ErrPOSTFileRequired-71] + _ = x[ErrSignatureVersionNotSupported-72] + _ = x[ErrBucketNotEmpty-73] + _ = x[ErrAllAccessDisabled-74] + _ = x[ErrMalformedPolicy-75] + _ = x[ErrMissingFields-76] + _ = x[ErrMissingCredTag-77] + _ = x[ErrCredMalformed-78] + _ = x[ErrInvalidRegion-79] + _ = x[ErrInvalidServiceS3-80] + _ = x[ErrInvalidServiceSTS-81] + _ = x[ErrInvalidRequestVersion-82] + _ = x[ErrMissingSignTag-83] + _ = x[ErrMissingSignHeadersTag-84] + _ = x[ErrMalformedDate-85] + _ = x[ErrMalformedPresignedDate-86] + _ = x[ErrMalformedCredentialDate-87] + _ = x[ErrMalformedCredentialRegion-88] + _ = x[ErrMalformedExpires-89] + _ = x[ErrNegativeExpires-90] + _ = x[ErrAuthHeaderEmpty-91] + _ = x[ErrExpiredPresignRequest-92] + _ = x[ErrRequestNotReadyYet-93] + _ = x[ErrUnsignedHeaders-94] + _ = x[ErrMissingDateHeader-95] + _ = x[ErrInvalidQuerySignatureAlgo-96] + _ = x[ErrInvalidQueryParams-97] + _ = x[ErrBucketAlreadyOwnedByYou-98] + _ = x[ErrInvalidDuration-99] + _ = x[ErrBucketAlreadyExists-100] + _ = x[ErrMetadataTooLarge-101] + _ = x[ErrUnsupportedMetadata-102] + _ = x[ErrMaximumExpires-103] + _ = x[ErrSlowDown-104] + _ = x[ErrInvalidPrefixMarker-105] + _ = x[ErrBadRequest-106] + _ = x[ErrKeyTooLongError-107] + _ = x[ErrInvalidBucketObjectLockConfiguration-108] + _ = x[ErrObjectLockConfigurationNotFound-109] + _ = x[ErrObjectLockConfigurationNotAllowed-110] + _ = x[ErrNoSuchObjectLockConfiguration-111] + _ = x[ErrObjectLocked-112] + _ = x[ErrInvalidRetentionDate-113] + _ = x[ErrPastObjectLockRetainDate-114] + _ = x[ErrUnknownWORMModeDirective-115] + _ = x[ErrBucketTaggingNotFound-116] + _ = x[ErrObjectLockInvalidHeaders-117] + _ = x[ErrInvalidTagDirective-118] + _ = x[ErrInvalidEncryptionMethod-119] + _ = x[ErrInsecureSSECustomerRequest-120] + _ = x[ErrSSEMultipartEncrypted-121] + _ = x[ErrSSEEncryptedObject-122] + _ = x[ErrInvalidEncryptionParameters-123] + _ = x[ErrInvalidSSECustomerAlgorithm-124] + _ = x[ErrInvalidSSECustomerKey-125] + _ = x[ErrMissingSSECustomerKey-126] + _ = x[ErrMissingSSECustomerKeyMD5-127] + _ = x[ErrSSECustomerKeyMD5Mismatch-128] + _ = x[ErrInvalidSSECustomerParameters-129] + _ = x[ErrIncompatibleEncryptionMethod-130] + _ = x[ErrKMSNotConfigured-131] + _ = x[ErrNoAccessKey-132] + _ = x[ErrInvalidToken-133] + _ = x[ErrEventNotification-134] + _ = x[ErrARNNotification-135] + _ = x[ErrRegionNotification-136] + _ = x[ErrOverlappingFilterNotification-137] + _ = x[ErrFilterNameInvalid-138] + _ = x[ErrFilterNamePrefix-139] + _ = x[ErrFilterNameSuffix-140] + _ = x[ErrFilterValueInvalid-141] + _ = x[ErrOverlappingConfigs-142] + _ = x[ErrUnsupportedNotification-143] + _ = x[ErrContentSHA256Mismatch-144] + _ = x[ErrReadQuorum-145] + _ = x[ErrWriteQuorum-146] + _ = x[ErrStorageFull-147] + _ = x[ErrRequestBodyParse-148] + _ = x[ErrObjectExistsAsDirectory-149] + _ = x[ErrInvalidObjectName-150] + _ = x[ErrInvalidObjectNamePrefixSlash-151] + _ = x[ErrInvalidResourceName-152] + _ = x[ErrServerNotInitialized-153] + _ = x[ErrOperationTimedOut-154] + _ = x[ErrClientDisconnected-155] + _ = x[ErrOperationMaxedOut-156] + _ = x[ErrInvalidRequest-157] + _ = x[ErrTransitionStorageClassNotFoundError-158] + _ = x[ErrInvalidStorageClass-159] + _ = x[ErrBackendDown-160] + _ = x[ErrMalformedJSON-161] + _ = x[ErrAdminNoSuchUser-162] + _ = x[ErrAdminNoSuchGroup-163] + _ = x[ErrAdminGroupNotEmpty-164] + _ = x[ErrAdminNoSuchPolicy-165] + _ = x[ErrAdminInvalidArgument-166] + _ = x[ErrAdminInvalidAccessKey-167] + _ = x[ErrAdminInvalidSecretKey-168] + _ = x[ErrAdminConfigNoQuorum-169] + _ = x[ErrAdminConfigTooLarge-170] + _ = x[ErrAdminConfigBadJSON-171] + _ = x[ErrAdminConfigDuplicateKeys-172] + _ = x[ErrAdminCredentialsMismatch-173] + _ = x[ErrInsecureClientRequest-174] + _ = x[ErrObjectTampered-175] + _ = x[ErrSiteReplicationInvalidRequest-176] + _ = x[ErrSiteReplicationPeerResp-177] + _ = x[ErrSiteReplicationBackendIssue-178] + _ = x[ErrSiteReplicationServiceAccountError-179] + _ = x[ErrSiteReplicationBucketConfigError-180] + _ = x[ErrSiteReplicationBucketMetaError-181] + _ = x[ErrSiteReplicationIAMError-182] + _ = x[ErrAdminBucketQuotaExceeded-183] + _ = x[ErrAdminNoSuchQuotaConfiguration-184] + _ = x[ErrHealNotImplemented-185] + _ = x[ErrHealNoSuchProcess-186] + _ = x[ErrHealInvalidClientToken-187] + _ = x[ErrHealMissingBucket-188] + _ = x[ErrHealAlreadyRunning-189] + _ = x[ErrHealOverlappingPaths-190] + _ = x[ErrIncorrectContinuationToken-191] + _ = x[ErrEmptyRequestBody-192] + _ = x[ErrUnsupportedFunction-193] + _ = x[ErrInvalidExpressionType-194] + _ = x[ErrBusy-195] + _ = x[ErrUnauthorizedAccess-196] + _ = x[ErrExpressionTooLong-197] + _ = x[ErrIllegalSQLFunctionArgument-198] + _ = x[ErrInvalidKeyPath-199] + _ = x[ErrInvalidCompressionFormat-200] + _ = x[ErrInvalidFileHeaderInfo-201] + _ = x[ErrInvalidJSONType-202] + _ = x[ErrInvalidQuoteFields-203] + _ = x[ErrInvalidRequestParameter-204] + _ = x[ErrInvalidDataType-205] + _ = x[ErrInvalidTextEncoding-206] + _ = x[ErrInvalidDataSource-207] + _ = x[ErrInvalidTableAlias-208] + _ = x[ErrMissingRequiredParameter-209] + _ = x[ErrObjectSerializationConflict-210] + _ = x[ErrUnsupportedSQLOperation-211] + _ = x[ErrUnsupportedSQLStructure-212] + _ = x[ErrUnsupportedSyntax-213] + _ = x[ErrUnsupportedRangeHeader-214] + _ = x[ErrLexerInvalidChar-215] + _ = x[ErrLexerInvalidOperator-216] + _ = x[ErrLexerInvalidLiteral-217] + _ = x[ErrLexerInvalidIONLiteral-218] + _ = x[ErrParseExpectedDatePart-219] + _ = x[ErrParseExpectedKeyword-220] + _ = x[ErrParseExpectedTokenType-221] + _ = x[ErrParseExpected2TokenTypes-222] + _ = x[ErrParseExpectedNumber-223] + _ = x[ErrParseExpectedRightParenBuiltinFunctionCall-224] + _ = x[ErrParseExpectedTypeName-225] + _ = x[ErrParseExpectedWhenClause-226] + _ = x[ErrParseUnsupportedToken-227] + _ = x[ErrParseUnsupportedLiteralsGroupBy-228] + _ = x[ErrParseExpectedMember-229] + _ = x[ErrParseUnsupportedSelect-230] + _ = x[ErrParseUnsupportedCase-231] + _ = x[ErrParseUnsupportedCaseClause-232] + _ = x[ErrParseUnsupportedAlias-233] + _ = x[ErrParseUnsupportedSyntax-234] + _ = x[ErrParseUnknownOperator-235] + _ = x[ErrParseMissingIdentAfterAt-236] + _ = x[ErrParseUnexpectedOperator-237] + _ = x[ErrParseUnexpectedTerm-238] + _ = x[ErrParseUnexpectedToken-239] + _ = x[ErrParseUnexpectedKeyword-240] + _ = x[ErrParseExpectedExpression-241] + _ = x[ErrParseExpectedLeftParenAfterCast-242] + _ = x[ErrParseExpectedLeftParenValueConstructor-243] + _ = x[ErrParseExpectedLeftParenBuiltinFunctionCall-244] + _ = x[ErrParseExpectedArgumentDelimiter-245] + _ = x[ErrParseCastArity-246] + _ = x[ErrParseInvalidTypeParam-247] + _ = x[ErrParseEmptySelect-248] + _ = x[ErrParseSelectMissingFrom-249] + _ = x[ErrParseExpectedIdentForGroupName-250] + _ = x[ErrParseExpectedIdentForAlias-251] + _ = x[ErrParseUnsupportedCallWithStar-252] + _ = x[ErrParseNonUnaryAgregateFunctionCall-253] + _ = x[ErrParseMalformedJoin-254] + _ = x[ErrParseExpectedIdentForAt-255] + _ = x[ErrParseAsteriskIsNotAloneInSelectList-256] + _ = x[ErrParseCannotMixSqbAndWildcardInSelectList-257] + _ = x[ErrParseInvalidContextForWildcardInSelectList-258] + _ = x[ErrIncorrectSQLFunctionArgumentType-259] + _ = x[ErrValueParseFailure-260] + _ = x[ErrEvaluatorInvalidArguments-261] + _ = x[ErrIntegerOverflow-262] + _ = x[ErrLikeInvalidInputs-263] + _ = x[ErrCastFailed-264] + _ = x[ErrInvalidCast-265] + _ = x[ErrEvaluatorInvalidTimestampFormatPattern-266] + _ = x[ErrEvaluatorInvalidTimestampFormatPatternSymbolForParsing-267] + _ = x[ErrEvaluatorTimestampFormatPatternDuplicateFields-268] + _ = x[ErrEvaluatorTimestampFormatPatternHourClockAmPmMismatch-269] + _ = x[ErrEvaluatorUnterminatedTimestampFormatPatternToken-270] + _ = x[ErrEvaluatorInvalidTimestampFormatPatternToken-271] + _ = x[ErrEvaluatorInvalidTimestampFormatPatternSymbol-272] + _ = x[ErrEvaluatorBindingDoesNotExist-273] + _ = x[ErrMissingHeaders-274] + _ = x[ErrInvalidColumnIndex-275] + _ = x[ErrAdminConfigNotificationTargetsFailed-276] + _ = x[ErrAdminProfilerNotEnabled-277] + _ = x[ErrInvalidDecompressedSize-278] + _ = x[ErrAddUserInvalidArgument-279] + _ = x[ErrAdminAccountNotEligible-280] + _ = x[ErrAccountNotEligible-281] + _ = x[ErrAdminServiceAccountNotFound-282] + _ = x[ErrPostPolicyConditionInvalidFormat-283] } -const _APIErrorCode_name = "NoneAccessDeniedBadDigestEntityTooSmallEntityTooLargePolicyTooLargeIncompleteBodyInternalErrorInvalidAccessKeyIDInvalidBucketNameInvalidDigestInvalidRangeInvalidRangePartNumberInvalidCopyPartRangeInvalidCopyPartRangeSourceInvalidMaxKeysInvalidEncodingMethodInvalidMaxUploadsInvalidMaxPartsInvalidPartNumberMarkerInvalidPartNumberInvalidRequestBodyInvalidCopySourceInvalidMetadataDirectiveInvalidCopyDestInvalidPolicyDocumentInvalidObjectStateMalformedXMLMissingContentLengthMissingContentMD5MissingRequestBodyErrorMissingSecurityHeaderNoSuchBucketNoSuchBucketPolicyNoSuchBucketLifecycleNoSuchLifecycleConfigurationNoSuchBucketSSEConfigNoSuchCORSConfigurationNoSuchWebsiteConfigurationReplicationConfigurationNotFoundErrorRemoteDestinationNotFoundErrorReplicationDestinationMissingLockRemoteTargetNotFoundErrorReplicationRemoteConnectionErrorReplicationBandwidthLimitErrorBucketRemoteIdenticalToSourceBucketRemoteAlreadyExistsBucketRemoteLabelInUseBucketRemoteArnTypeInvalidBucketRemoteArnInvalidBucketRemoteRemoveDisallowedRemoteTargetNotVersionedErrorReplicationSourceNotVersionedErrorReplicationNeedsVersioningErrorReplicationBucketNeedsVersioningErrorReplicationNoMatchingRuleErrorObjectRestoreAlreadyInProgressNoSuchKeyNoSuchUploadInvalidVersionIDNoSuchVersionNotImplementedPreconditionFailedRequestTimeTooSkewedSignatureDoesNotMatchMethodNotAllowedInvalidPartInvalidPartOrderAuthorizationHeaderMalformedMalformedPOSTRequestPOSTFileRequiredSignatureVersionNotSupportedBucketNotEmptyAllAccessDisabledMalformedPolicyMissingFieldsMissingCredTagCredMalformedInvalidRegionInvalidServiceS3InvalidServiceSTSInvalidRequestVersionMissingSignTagMissingSignHeadersTagMalformedDateMalformedPresignedDateMalformedCredentialDateMalformedCredentialRegionMalformedExpiresNegativeExpiresAuthHeaderEmptyExpiredPresignRequestRequestNotReadyYetUnsignedHeadersMissingDateHeaderInvalidQuerySignatureAlgoInvalidQueryParamsBucketAlreadyOwnedByYouInvalidDurationBucketAlreadyExistsMetadataTooLargeUnsupportedMetadataMaximumExpiresSlowDownInvalidPrefixMarkerBadRequestKeyTooLongErrorInvalidBucketObjectLockConfigurationObjectLockConfigurationNotFoundObjectLockConfigurationNotAllowedNoSuchObjectLockConfigurationObjectLockedInvalidRetentionDatePastObjectLockRetainDateUnknownWORMModeDirectiveBucketTaggingNotFoundObjectLockInvalidHeadersInvalidTagDirectiveInvalidEncryptionMethodInsecureSSECustomerRequestSSEMultipartEncryptedSSEEncryptedObjectInvalidEncryptionParametersInvalidSSECustomerAlgorithmInvalidSSECustomerKeyMissingSSECustomerKeyMissingSSECustomerKeyMD5SSECustomerKeyMD5MismatchInvalidSSECustomerParametersIncompatibleEncryptionMethodKMSNotConfiguredNoAccessKeyInvalidTokenEventNotificationARNNotificationRegionNotificationOverlappingFilterNotificationFilterNameInvalidFilterNamePrefixFilterNameSuffixFilterValueInvalidOverlappingConfigsUnsupportedNotificationContentSHA256MismatchReadQuorumWriteQuorumStorageFullRequestBodyParseObjectExistsAsDirectoryInvalidObjectNameInvalidObjectNamePrefixSlashInvalidResourceNameServerNotInitializedOperationTimedOutClientDisconnectedOperationMaxedOutInvalidRequestTransitionStorageClassNotFoundErrorInvalidStorageClassBackendDownMalformedJSONAdminNoSuchUserAdminNoSuchGroupAdminGroupNotEmptyAdminNoSuchPolicyAdminInvalidArgumentAdminInvalidAccessKeyAdminInvalidSecretKeyAdminConfigNoQuorumAdminConfigTooLargeAdminConfigBadJSONAdminConfigDuplicateKeysAdminCredentialsMismatchInsecureClientRequestObjectTamperedSiteReplicationInvalidRequestSiteReplicationPeerRespSiteReplicationBackendIssueSiteReplicationServiceAccountErrorSiteReplicationBucketConfigErrorSiteReplicationBucketMetaErrorSiteReplicationIAMErrorAdminBucketQuotaExceededAdminNoSuchQuotaConfigurationHealNotImplementedHealNoSuchProcessHealInvalidClientTokenHealMissingBucketHealAlreadyRunningHealOverlappingPathsIncorrectContinuationTokenEmptyRequestBodyUnsupportedFunctionInvalidExpressionTypeBusyUnauthorizedAccessExpressionTooLongIllegalSQLFunctionArgumentInvalidKeyPathInvalidCompressionFormatInvalidFileHeaderInfoInvalidJSONTypeInvalidQuoteFieldsInvalidRequestParameterInvalidDataTypeInvalidTextEncodingInvalidDataSourceInvalidTableAliasMissingRequiredParameterObjectSerializationConflictUnsupportedSQLOperationUnsupportedSQLStructureUnsupportedSyntaxUnsupportedRangeHeaderLexerInvalidCharLexerInvalidOperatorLexerInvalidLiteralLexerInvalidIONLiteralParseExpectedDatePartParseExpectedKeywordParseExpectedTokenTypeParseExpected2TokenTypesParseExpectedNumberParseExpectedRightParenBuiltinFunctionCallParseExpectedTypeNameParseExpectedWhenClauseParseUnsupportedTokenParseUnsupportedLiteralsGroupByParseExpectedMemberParseUnsupportedSelectParseUnsupportedCaseParseUnsupportedCaseClauseParseUnsupportedAliasParseUnsupportedSyntaxParseUnknownOperatorParseMissingIdentAfterAtParseUnexpectedOperatorParseUnexpectedTermParseUnexpectedTokenParseUnexpectedKeywordParseExpectedExpressionParseExpectedLeftParenAfterCastParseExpectedLeftParenValueConstructorParseExpectedLeftParenBuiltinFunctionCallParseExpectedArgumentDelimiterParseCastArityParseInvalidTypeParamParseEmptySelectParseSelectMissingFromParseExpectedIdentForGroupNameParseExpectedIdentForAliasParseUnsupportedCallWithStarParseNonUnaryAgregateFunctionCallParseMalformedJoinParseExpectedIdentForAtParseAsteriskIsNotAloneInSelectListParseCannotMixSqbAndWildcardInSelectListParseInvalidContextForWildcardInSelectListIncorrectSQLFunctionArgumentTypeValueParseFailureEvaluatorInvalidArgumentsIntegerOverflowLikeInvalidInputsCastFailedInvalidCastEvaluatorInvalidTimestampFormatPatternEvaluatorInvalidTimestampFormatPatternSymbolForParsingEvaluatorTimestampFormatPatternDuplicateFieldsEvaluatorTimestampFormatPatternHourClockAmPmMismatchEvaluatorUnterminatedTimestampFormatPatternTokenEvaluatorInvalidTimestampFormatPatternTokenEvaluatorInvalidTimestampFormatPatternSymbolEvaluatorBindingDoesNotExistMissingHeadersInvalidColumnIndexAdminConfigNotificationTargetsFailedAdminProfilerNotEnabledInvalidDecompressedSizeAddUserInvalidArgumentAdminAccountNotEligibleAccountNotEligibleAdminServiceAccountNotFoundPostPolicyConditionInvalidFormat" +const _APIErrorCode_name = "NoneAccessDeniedBadDigestEntityTooSmallEntityTooLargePolicyTooLargeIncompleteBodyInternalErrorInvalidAccessKeyIDInvalidBucketNameInvalidDigestInvalidRangeInvalidRangePartNumberInvalidCopyPartRangeInvalidCopyPartRangeSourceInvalidMaxKeysInvalidEncodingMethodInvalidMaxUploadsInvalidMaxPartsInvalidPartNumberMarkerInvalidPartNumberInvalidRequestBodyInvalidCopySourceInvalidMetadataDirectiveInvalidCopyDestInvalidPolicyDocumentInvalidObjectStateMalformedXMLMissingContentLengthMissingContentMD5MissingRequestBodyErrorMissingSecurityHeaderNoSuchBucketNoSuchBucketPolicyNoSuchBucketLifecycleNoSuchLifecycleConfigurationInvalidLifecycleWithObjectLockNoSuchBucketSSEConfigNoSuchCORSConfigurationNoSuchWebsiteConfigurationReplicationConfigurationNotFoundErrorRemoteDestinationNotFoundErrorReplicationDestinationMissingLockRemoteTargetNotFoundErrorReplicationRemoteConnectionErrorReplicationBandwidthLimitErrorBucketRemoteIdenticalToSourceBucketRemoteAlreadyExistsBucketRemoteLabelInUseBucketRemoteArnTypeInvalidBucketRemoteArnInvalidBucketRemoteRemoveDisallowedRemoteTargetNotVersionedErrorReplicationSourceNotVersionedErrorReplicationNeedsVersioningErrorReplicationBucketNeedsVersioningErrorReplicationNoMatchingRuleErrorObjectRestoreAlreadyInProgressNoSuchKeyNoSuchUploadInvalidVersionIDNoSuchVersionNotImplementedPreconditionFailedRequestTimeTooSkewedSignatureDoesNotMatchMethodNotAllowedInvalidPartInvalidPartOrderAuthorizationHeaderMalformedMalformedPOSTRequestPOSTFileRequiredSignatureVersionNotSupportedBucketNotEmptyAllAccessDisabledMalformedPolicyMissingFieldsMissingCredTagCredMalformedInvalidRegionInvalidServiceS3InvalidServiceSTSInvalidRequestVersionMissingSignTagMissingSignHeadersTagMalformedDateMalformedPresignedDateMalformedCredentialDateMalformedCredentialRegionMalformedExpiresNegativeExpiresAuthHeaderEmptyExpiredPresignRequestRequestNotReadyYetUnsignedHeadersMissingDateHeaderInvalidQuerySignatureAlgoInvalidQueryParamsBucketAlreadyOwnedByYouInvalidDurationBucketAlreadyExistsMetadataTooLargeUnsupportedMetadataMaximumExpiresSlowDownInvalidPrefixMarkerBadRequestKeyTooLongErrorInvalidBucketObjectLockConfigurationObjectLockConfigurationNotFoundObjectLockConfigurationNotAllowedNoSuchObjectLockConfigurationObjectLockedInvalidRetentionDatePastObjectLockRetainDateUnknownWORMModeDirectiveBucketTaggingNotFoundObjectLockInvalidHeadersInvalidTagDirectiveInvalidEncryptionMethodInsecureSSECustomerRequestSSEMultipartEncryptedSSEEncryptedObjectInvalidEncryptionParametersInvalidSSECustomerAlgorithmInvalidSSECustomerKeyMissingSSECustomerKeyMissingSSECustomerKeyMD5SSECustomerKeyMD5MismatchInvalidSSECustomerParametersIncompatibleEncryptionMethodKMSNotConfiguredNoAccessKeyInvalidTokenEventNotificationARNNotificationRegionNotificationOverlappingFilterNotificationFilterNameInvalidFilterNamePrefixFilterNameSuffixFilterValueInvalidOverlappingConfigsUnsupportedNotificationContentSHA256MismatchReadQuorumWriteQuorumStorageFullRequestBodyParseObjectExistsAsDirectoryInvalidObjectNameInvalidObjectNamePrefixSlashInvalidResourceNameServerNotInitializedOperationTimedOutClientDisconnectedOperationMaxedOutInvalidRequestTransitionStorageClassNotFoundErrorInvalidStorageClassBackendDownMalformedJSONAdminNoSuchUserAdminNoSuchGroupAdminGroupNotEmptyAdminNoSuchPolicyAdminInvalidArgumentAdminInvalidAccessKeyAdminInvalidSecretKeyAdminConfigNoQuorumAdminConfigTooLargeAdminConfigBadJSONAdminConfigDuplicateKeysAdminCredentialsMismatchInsecureClientRequestObjectTamperedSiteReplicationInvalidRequestSiteReplicationPeerRespSiteReplicationBackendIssueSiteReplicationServiceAccountErrorSiteReplicationBucketConfigErrorSiteReplicationBucketMetaErrorSiteReplicationIAMErrorAdminBucketQuotaExceededAdminNoSuchQuotaConfigurationHealNotImplementedHealNoSuchProcessHealInvalidClientTokenHealMissingBucketHealAlreadyRunningHealOverlappingPathsIncorrectContinuationTokenEmptyRequestBodyUnsupportedFunctionInvalidExpressionTypeBusyUnauthorizedAccessExpressionTooLongIllegalSQLFunctionArgumentInvalidKeyPathInvalidCompressionFormatInvalidFileHeaderInfoInvalidJSONTypeInvalidQuoteFieldsInvalidRequestParameterInvalidDataTypeInvalidTextEncodingInvalidDataSourceInvalidTableAliasMissingRequiredParameterObjectSerializationConflictUnsupportedSQLOperationUnsupportedSQLStructureUnsupportedSyntaxUnsupportedRangeHeaderLexerInvalidCharLexerInvalidOperatorLexerInvalidLiteralLexerInvalidIONLiteralParseExpectedDatePartParseExpectedKeywordParseExpectedTokenTypeParseExpected2TokenTypesParseExpectedNumberParseExpectedRightParenBuiltinFunctionCallParseExpectedTypeNameParseExpectedWhenClauseParseUnsupportedTokenParseUnsupportedLiteralsGroupByParseExpectedMemberParseUnsupportedSelectParseUnsupportedCaseParseUnsupportedCaseClauseParseUnsupportedAliasParseUnsupportedSyntaxParseUnknownOperatorParseMissingIdentAfterAtParseUnexpectedOperatorParseUnexpectedTermParseUnexpectedTokenParseUnexpectedKeywordParseExpectedExpressionParseExpectedLeftParenAfterCastParseExpectedLeftParenValueConstructorParseExpectedLeftParenBuiltinFunctionCallParseExpectedArgumentDelimiterParseCastArityParseInvalidTypeParamParseEmptySelectParseSelectMissingFromParseExpectedIdentForGroupNameParseExpectedIdentForAliasParseUnsupportedCallWithStarParseNonUnaryAgregateFunctionCallParseMalformedJoinParseExpectedIdentForAtParseAsteriskIsNotAloneInSelectListParseCannotMixSqbAndWildcardInSelectListParseInvalidContextForWildcardInSelectListIncorrectSQLFunctionArgumentTypeValueParseFailureEvaluatorInvalidArgumentsIntegerOverflowLikeInvalidInputsCastFailedInvalidCastEvaluatorInvalidTimestampFormatPatternEvaluatorInvalidTimestampFormatPatternSymbolForParsingEvaluatorTimestampFormatPatternDuplicateFieldsEvaluatorTimestampFormatPatternHourClockAmPmMismatchEvaluatorUnterminatedTimestampFormatPatternTokenEvaluatorInvalidTimestampFormatPatternTokenEvaluatorInvalidTimestampFormatPatternSymbolEvaluatorBindingDoesNotExistMissingHeadersInvalidColumnIndexAdminConfigNotificationTargetsFailedAdminProfilerNotEnabledInvalidDecompressedSizeAddUserInvalidArgumentAdminAccountNotEligibleAccountNotEligibleAdminServiceAccountNotFoundPostPolicyConditionInvalidFormat" -var _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, 871, 900, 925, 947, 973, 995, 1023, 1052, 1086, 1117, 1154, 1184, 1214, 1223, 1235, 1251, 1264, 1278, 1296, 1316, 1337, 1353, 1364, 1380, 1408, 1428, 1444, 1472, 1486, 1503, 1518, 1531, 1545, 1558, 1571, 1587, 1604, 1625, 1639, 1660, 1673, 1695, 1718, 1743, 1759, 1774, 1789, 1810, 1828, 1843, 1860, 1885, 1903, 1926, 1941, 1960, 1976, 1995, 2009, 2017, 2036, 2046, 2061, 2097, 2128, 2161, 2190, 2202, 2222, 2246, 2270, 2291, 2315, 2334, 2357, 2383, 2404, 2422, 2449, 2476, 2497, 2518, 2542, 2567, 2595, 2623, 2639, 2650, 2662, 2679, 2694, 2712, 2741, 2758, 2774, 2790, 2808, 2826, 2849, 2870, 2880, 2891, 2902, 2918, 2941, 2958, 2986, 3005, 3025, 3042, 3060, 3077, 3091, 3126, 3145, 3156, 3169, 3184, 3200, 3218, 3235, 3255, 3276, 3297, 3316, 3335, 3353, 3377, 3401, 3422, 3436, 3465, 3488, 3515, 3549, 3581, 3611, 3634, 3658, 3687, 3705, 3722, 3744, 3761, 3779, 3799, 3825, 3841, 3860, 3881, 3885, 3903, 3920, 3946, 3960, 3984, 4005, 4020, 4038, 4061, 4076, 4095, 4112, 4129, 4153, 4180, 4203, 4226, 4243, 4265, 4281, 4301, 4320, 4342, 4363, 4383, 4405, 4429, 4448, 4490, 4511, 4534, 4555, 4586, 4605, 4627, 4647, 4673, 4694, 4716, 4736, 4760, 4783, 4802, 4822, 4844, 4867, 4898, 4936, 4977, 5007, 5021, 5042, 5058, 5080, 5110, 5136, 5164, 5197, 5215, 5238, 5273, 5313, 5355, 5387, 5404, 5429, 5444, 5461, 5471, 5482, 5520, 5574, 5620, 5672, 5720, 5763, 5807, 5835, 5849, 5867, 5903, 5926, 5949, 5971, 5994, 6012, 6039, 6071} +var _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, 644, 665, 688, 714, 751, 781, 814, 839, 871, 901, 930, 955, 977, 1003, 1025, 1053, 1082, 1116, 1147, 1184, 1214, 1244, 1253, 1265, 1281, 1294, 1308, 1326, 1346, 1367, 1383, 1394, 1410, 1438, 1458, 1474, 1502, 1516, 1533, 1548, 1561, 1575, 1588, 1601, 1617, 1634, 1655, 1669, 1690, 1703, 1725, 1748, 1773, 1789, 1804, 1819, 1840, 1858, 1873, 1890, 1915, 1933, 1956, 1971, 1990, 2006, 2025, 2039, 2047, 2066, 2076, 2091, 2127, 2158, 2191, 2220, 2232, 2252, 2276, 2300, 2321, 2345, 2364, 2387, 2413, 2434, 2452, 2479, 2506, 2527, 2548, 2572, 2597, 2625, 2653, 2669, 2680, 2692, 2709, 2724, 2742, 2771, 2788, 2804, 2820, 2838, 2856, 2879, 2900, 2910, 2921, 2932, 2948, 2971, 2988, 3016, 3035, 3055, 3072, 3090, 3107, 3121, 3156, 3175, 3186, 3199, 3214, 3230, 3248, 3265, 3285, 3306, 3327, 3346, 3365, 3383, 3407, 3431, 3452, 3466, 3495, 3518, 3545, 3579, 3611, 3641, 3664, 3688, 3717, 3735, 3752, 3774, 3791, 3809, 3829, 3855, 3871, 3890, 3911, 3915, 3933, 3950, 3976, 3990, 4014, 4035, 4050, 4068, 4091, 4106, 4125, 4142, 4159, 4183, 4210, 4233, 4256, 4273, 4295, 4311, 4331, 4350, 4372, 4393, 4413, 4435, 4459, 4478, 4520, 4541, 4564, 4585, 4616, 4635, 4657, 4677, 4703, 4724, 4746, 4766, 4790, 4813, 4832, 4852, 4874, 4897, 4928, 4966, 5007, 5037, 5051, 5072, 5088, 5110, 5140, 5166, 5194, 5227, 5245, 5268, 5303, 5343, 5385, 5417, 5434, 5459, 5474, 5491, 5501, 5512, 5550, 5604, 5650, 5702, 5750, 5793, 5837, 5865, 5879, 5897, 5933, 5956, 5979, 6001, 6024, 6042, 6069, 6101} func (i APIErrorCode) String() string { if i < 0 || i >= APIErrorCode(len(_APIErrorCode_index)-1) { diff --git a/cmd/bucket-lifecycle-handlers.go b/cmd/bucket-lifecycle-handlers.go index a1673324b3bd9..bcc32cc962f02 100644 --- a/cmd/bucket-lifecycle-handlers.go +++ b/cmd/bucket-lifecycle-handlers.go @@ -24,6 +24,7 @@ import ( "github.com/gorilla/mux" "github.com/minio/minio/internal/bucket/lifecycle" + "github.com/minio/minio/internal/bucket/object/lock" xhttp "github.com/minio/minio/internal/http" "github.com/minio/minio/internal/logger" "github.com/minio/pkg/bucket/policy" @@ -79,6 +80,17 @@ func (api objectAPIHandlers) PutBucketLifecycleHandler(w http.ResponseWriter, r return } + // Disallow MaxNoncurrentVersions if bucket has object locking enabled + var rCfg lock.Retention + if rCfg, err = globalBucketObjectLockSys.Get(bucket); err != nil { + writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) + return + } + if rCfg.LockEnabled && bucketLifecycle.HasMaxNoncurrentVersions() { + writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidLifecycleWithObjectLock), r.URL) + return + } + // Validate the transition storage ARNs if err = validateTransitionTier(bucketLifecycle); err != nil { writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) diff --git a/cmd/bucket-lifecycle.go b/cmd/bucket-lifecycle.go index 3744c7da4fef1..4e4e13c8a2805 100644 --- a/cmd/bucket-lifecycle.go +++ b/cmd/bucket-lifecycle.go @@ -81,40 +81,58 @@ type expiryTask struct { } type expiryState struct { - once sync.Once - expiryCh chan expiryTask + once sync.Once + byDaysCh chan expiryTask + byMaxNoncurrentCh chan maxNoncurrentTask } // PendingTasks returns the number of pending ILM expiry tasks. func (es *expiryState) PendingTasks() int { - return len(es.expiryCh) + return len(es.byDaysCh) + len(es.byMaxNoncurrentCh) } -func (es *expiryState) queueExpiryTask(oi ObjectInfo, restoredObject bool, rmVersion bool) { +// close closes work channels exactly once. +func (es *expiryState) close() { + es.once.Do(func() { + close(es.byDaysCh) + close(es.byMaxNoncurrentCh) + }) +} + +// enqueueByDays enqueues object versions expired by days for expiry. +func (es *expiryState) enqueueByDays(oi ObjectInfo, restoredObject bool, rmVersion bool) { select { case <-GlobalContext.Done(): - es.once.Do(func() { - close(es.expiryCh) - }) - case es.expiryCh <- expiryTask{objInfo: oi, versionExpiry: rmVersion, restoredObject: restoredObject}: + es.close() + case es.byDaysCh <- expiryTask{objInfo: oi, versionExpiry: rmVersion, restoredObject: restoredObject}: default: } } -var ( - globalExpiryState *expiryState -) +// enqueueByMaxNoncurrent enqueues object versions expired by +// MaxNoncurrentVersions limit for expiry. +func (es *expiryState) enqueueByMaxNoncurrent(bucket string, versions []ObjectToDelete) { + select { + case <-GlobalContext.Done(): + es.close() + case es.byMaxNoncurrentCh <- maxNoncurrentTask{bucket: bucket, versions: versions}: + default: + } +} + +var globalExpiryState *expiryState func newExpiryState() *expiryState { return &expiryState{ - expiryCh: make(chan expiryTask, 10000), + byDaysCh: make(chan expiryTask, 10000), + byMaxNoncurrentCh: make(chan maxNoncurrentTask, 10000), } } func initBackgroundExpiry(ctx context.Context, objectAPI ObjectLayer) { globalExpiryState = newExpiryState() go func() { - for t := range globalExpiryState.expiryCh { + for t := range globalExpiryState.byDaysCh { if t.objInfo.TransitionedObject.Status != "" { applyExpiryOnTransitionedObject(ctx, objectAPI, t.objInfo, t.restoredObject) } else { @@ -122,6 +140,18 @@ func initBackgroundExpiry(ctx context.Context, objectAPI ObjectLayer) { } } }() + go func() { + for t := range globalExpiryState.byMaxNoncurrentCh { + deleteObjectVersions(ctx, objectAPI, t.bucket, t.versions) + } + }() +} + +// maxNoncurrentTask encapsulates arguments required by worker to expire objects +// by MaxNoncurrentVersions +type maxNoncurrentTask struct { + bucket string + versions []ObjectToDelete } type transitionState struct { diff --git a/cmd/data-scanner.go b/cmd/data-scanner.go index dbb94d65eb165..3ca1498704e57 100644 --- a/cmd/data-scanner.go +++ b/cmd/data-scanner.go @@ -865,6 +865,7 @@ func (i *scannerItem) transformMetaDir() { } var applyActionsLogPrefix = color.Green("applyActions:") +var applyVersionActionsLogPrefix = color.Green("applyVersionActions:") func (i *scannerItem) applyHealing(ctx context.Context, o ObjectLayer, oi ObjectInfo) (size int64) { if i.debug { @@ -970,13 +971,57 @@ func (i *scannerItem) applyTierObjSweep(ctx context.Context, o ObjectLayer, oi O } +// applyMaxNoncurrentVersionLimit removes noncurrent versions older than the most recent MaxNoncurrentVersions configured. +// Note: This function doesn't update sizeSummary since it always removes versions that it doesn't return. +func (i *scannerItem) applyMaxNoncurrentVersionLimit(ctx context.Context, o ObjectLayer, fivs []FileInfo) ([]FileInfo, error) { + if i.lifeCycle == nil { + return fivs, nil + } + + lim := i.lifeCycle.NoncurrentVersionsExpirationLimit(lifecycle.ObjectOpts{Name: i.objectPath()}) + if lim == 0 || len(fivs) <= lim+1 { // fewer than lim _noncurrent_ versions + return fivs, nil + } + + overflowVersions := fivs[lim+1:] + // current version + most recent lim noncurrent versions + fivs = fivs[:lim+1] + + rcfg, _ := globalBucketObjectLockSys.Get(i.bucket) + toDel := make([]ObjectToDelete, 0, len(overflowVersions)) + for _, fi := range overflowVersions { + obj := fi.ToObjectInfo(i.bucket, i.objectPath()) + if rcfg.LockEnabled && enforceRetentionForDeletion(ctx, obj) { + if i.debug { + if obj.VersionID != "" { + console.Debugf(applyVersionActionsLogPrefix+" lifecycle: %s v(%s) is locked, not deleting\n", obj.Name, obj.VersionID) + } else { + console.Debugf(applyVersionActionsLogPrefix+" lifecycle: %s is locked, not deleting\n", obj.Name) + } + } + continue + } + toDel = append(toDel, ObjectToDelete{ + ObjectName: fi.Name, + VersionID: fi.VersionID, + }) + } + + globalExpiryState.enqueueByMaxNoncurrent(i.bucket, toDel) + return fivs, nil +} + +// applyVersionActions will apply lifecycle checks on all versions of a scanned item. Returns versions that remain +// after applying lifecycle checks configured. +func (i *scannerItem) applyVersionActions(ctx context.Context, o ObjectLayer, fivs []FileInfo) ([]FileInfo, error) { + return i.applyMaxNoncurrentVersionLimit(ctx, o, fivs) +} + // applyActions will apply lifecycle checks on to a scanned item. // The resulting size on disk will always be returned. // The metadata will be compared to consensus on the object layer before any changes are applied. // If no metadata is supplied, -1 is returned if no action is taken. func (i *scannerItem) applyActions(ctx context.Context, o ObjectLayer, oi ObjectInfo, sizeS *sizeSummary) int64 { - i.applyTierObjSweep(ctx, o, oi) - applied, size := i.applyLifecycle(ctx, o, oi) // For instance, an applied lifecycle means we remove/transitioned an object // from the current deployment, which means we don't have to call healing @@ -1093,7 +1138,7 @@ func applyExpiryOnNonTransitionedObjects(ctx context.Context, objLayer ObjectLay // Apply object, object version, restored object or restored object version action on the given object func applyExpiryRule(obj ObjectInfo, restoredObject, applyOnVersion bool) bool { - globalExpiryState.queueExpiryTask(obj, restoredObject, applyOnVersion) + globalExpiryState.enqueueByDays(obj, restoredObject, applyOnVersion) return true } diff --git a/cmd/object-handlers-common.go b/cmd/object-handlers-common.go index ac3fb3091aee2..251a0ec51fc78 100644 --- a/cmd/object-handlers-common.go +++ b/cmd/object-handlers-common.go @@ -24,7 +24,9 @@ import ( "strconv" "time" + "github.com/minio/minio/internal/event" xhttp "github.com/minio/minio/internal/http" + "github.com/minio/minio/internal/logger" ) var ( @@ -264,3 +266,41 @@ func setPutObjHeaders(w http.ResponseWriter, objInfo ObjectInfo, delete bool) { } } } + +func deleteObjectVersions(ctx context.Context, o ObjectLayer, bucket string, toDel []ObjectToDelete) { + versioned := globalBucketVersioningSys.Enabled(bucket) + versionSuspended := globalBucketVersioningSys.Suspended(bucket) + for remaining := toDel; len(remaining) > 0; toDel = remaining { + if len(toDel) > maxDeleteList { + remaining = toDel[maxDeleteList:] + toDel = toDel[:maxDeleteList] + } else { + remaining = nil + } + deletedObjs, errs := o.DeleteObjects(ctx, bucket, toDel, ObjectOptions{ + Versioned: versioned, + VersionSuspended: versionSuspended, + }) + var logged bool + for i, err := range errs { + if err != nil { + if !logged { + // log the first error + logger.LogIf(ctx, err) + logged = true + } + continue + } + dobj := deletedObjs[i] + sendEvent(eventArgs{ + EventName: event.ObjectRemovedDelete, + BucketName: bucket, + Object: ObjectInfo{ + Name: dobj.ObjectName, + VersionID: dobj.VersionID, + }, + Host: "Internal: [ILM-EXPIRY]", + }) + } + } +} diff --git a/cmd/storage-datatypes.go b/cmd/storage-datatypes.go index b4e686a75471a..a568f6ea159c6 100644 --- a/cmd/storage-datatypes.go +++ b/cmd/storage-datatypes.go @@ -93,7 +93,8 @@ type FileInfoVersions struct { // latest version. LatestModTime time.Time `msg:"lm"` - Versions []FileInfo `msg:"vs"` + Versions []FileInfo `msg:"vs"` + FreeVersions []FileInfo `msg:"fvs"` } // findVersionIndex will return the version index where the version diff --git a/cmd/storage-datatypes_gen.go b/cmd/storage-datatypes_gen.go index 8d0d9ac1063cf..5b9f4a3bcfc47 100644 --- a/cmd/storage-datatypes_gen.go +++ b/cmd/storage-datatypes_gen.go @@ -1128,8 +1128,8 @@ func (z *FileInfoVersions) DecodeMsg(dc *msgp.Reader) (err error) { err = msgp.WrapError(err) return } - if zb0001 != 4 { - err = msgp.ArrayError{Wanted: 4, Got: zb0001} + if zb0001 != 5 { + err = msgp.ArrayError{Wanted: 5, Got: zb0001} return } z.Volume, err = dc.ReadString() @@ -1165,13 +1165,31 @@ func (z *FileInfoVersions) DecodeMsg(dc *msgp.Reader) (err error) { return } } + var zb0003 uint32 + zb0003, err = dc.ReadArrayHeader() + if err != nil { + err = msgp.WrapError(err, "FreeVersions") + return + } + if cap(z.FreeVersions) >= int(zb0003) { + z.FreeVersions = (z.FreeVersions)[:zb0003] + } else { + z.FreeVersions = make([]FileInfo, zb0003) + } + for za0002 := range z.FreeVersions { + err = z.FreeVersions[za0002].DecodeMsg(dc) + if err != nil { + err = msgp.WrapError(err, "FreeVersions", za0002) + return + } + } return } // EncodeMsg implements msgp.Encodable func (z *FileInfoVersions) EncodeMsg(en *msgp.Writer) (err error) { - // array header, size 4 - err = en.Append(0x94) + // array header, size 5 + err = en.Append(0x95) if err != nil { return } @@ -1202,14 +1220,26 @@ func (z *FileInfoVersions) EncodeMsg(en *msgp.Writer) (err error) { return } } + err = en.WriteArrayHeader(uint32(len(z.FreeVersions))) + if err != nil { + err = msgp.WrapError(err, "FreeVersions") + return + } + for za0002 := range z.FreeVersions { + err = z.FreeVersions[za0002].EncodeMsg(en) + if err != nil { + err = msgp.WrapError(err, "FreeVersions", za0002) + return + } + } return } // MarshalMsg implements msgp.Marshaler func (z *FileInfoVersions) MarshalMsg(b []byte) (o []byte, err error) { o = msgp.Require(b, z.Msgsize()) - // array header, size 4 - o = append(o, 0x94) + // array header, size 5 + o = append(o, 0x95) o = msgp.AppendString(o, z.Volume) o = msgp.AppendString(o, z.Name) o = msgp.AppendTime(o, z.LatestModTime) @@ -1221,6 +1251,14 @@ func (z *FileInfoVersions) MarshalMsg(b []byte) (o []byte, err error) { return } } + o = msgp.AppendArrayHeader(o, uint32(len(z.FreeVersions))) + for za0002 := range z.FreeVersions { + o, err = z.FreeVersions[za0002].MarshalMsg(o) + if err != nil { + err = msgp.WrapError(err, "FreeVersions", za0002) + return + } + } return } @@ -1232,8 +1270,8 @@ func (z *FileInfoVersions) UnmarshalMsg(bts []byte) (o []byte, err error) { err = msgp.WrapError(err) return } - if zb0001 != 4 { - err = msgp.ArrayError{Wanted: 4, Got: zb0001} + if zb0001 != 5 { + err = msgp.ArrayError{Wanted: 5, Got: zb0001} return } z.Volume, bts, err = msgp.ReadStringBytes(bts) @@ -1269,6 +1307,24 @@ func (z *FileInfoVersions) UnmarshalMsg(bts []byte) (o []byte, err error) { return } } + var zb0003 uint32 + zb0003, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "FreeVersions") + return + } + if cap(z.FreeVersions) >= int(zb0003) { + z.FreeVersions = (z.FreeVersions)[:zb0003] + } else { + z.FreeVersions = make([]FileInfo, zb0003) + } + for za0002 := range z.FreeVersions { + bts, err = z.FreeVersions[za0002].UnmarshalMsg(bts) + if err != nil { + err = msgp.WrapError(err, "FreeVersions", za0002) + return + } + } o = bts return } @@ -1279,6 +1335,10 @@ func (z *FileInfoVersions) Msgsize() (s int) { for za0001 := range z.Versions { s += z.Versions[za0001].Msgsize() } + s += msgp.ArrayHeaderSize + for za0002 := range z.FreeVersions { + s += z.FreeVersions[za0002].Msgsize() + } return } diff --git a/cmd/storage-rest-common.go b/cmd/storage-rest-common.go index 6229a5eed27d1..2bd6e803d9bf8 100644 --- a/cmd/storage-rest-common.go +++ b/cmd/storage-rest-common.go @@ -18,7 +18,7 @@ package cmd const ( - storageRESTVersion = "v41" // Optimized DeleteVersions API + storageRESTVersion = "v42" // Added FreeVersions to FileInfoVersions storageRESTVersionPrefix = SlashSeparator + storageRESTVersion storageRESTPrefix = minioReservedBucketPath + "/storage" ) diff --git a/cmd/xl-storage-format-utils.go b/cmd/xl-storage-format-utils.go index fba0dadbf0073..fe861201ae631 100644 --- a/cmd/xl-storage-format-utils.go +++ b/cmd/xl-storage-format-utils.go @@ -26,27 +26,6 @@ import ( ) func getFileInfoVersions(xlMetaBuf []byte, volume, path string) (FileInfoVersions, error) { - fivs, err := getAllFileInfoVersions(xlMetaBuf, volume, path) - if err != nil { - return fivs, err - } - n := 0 - for _, fi := range fivs.Versions { - // Filter our tier object delete marker - if !fi.TierFreeVersion() { - fivs.Versions[n] = fi - n++ - } - } - fivs.Versions = fivs.Versions[:n] - // Update numversions - for i := range fivs.Versions { - fivs.Versions[i].NumVersions = n - } - return fivs, nil -} - -func getAllFileInfoVersions(xlMetaBuf []byte, volume, path string) (FileInfoVersions, error) { if isXL2V1Format(xlMetaBuf) { var versions []FileInfo var err error @@ -63,10 +42,25 @@ func getAllFileInfoVersions(xlMetaBuf []byte, volume, path string) (FileInfoVers return FileInfoVersions{}, err } + var freeVersions []FileInfo + n := 0 + for _, fi := range versions { + if fi.TierFreeVersion() { + freeVersions = append(freeVersions, fi) + continue + } + versions[n] = fi + n++ + } + versions = versions[:n] + for _, ver := range versions { + ver.NumVersions = n + } return FileInfoVersions{ Volume: volume, Name: path, Versions: versions, + FreeVersions: freeVersions, LatestModTime: versions[0].ModTime, }, nil } @@ -83,7 +77,7 @@ func getAllFileInfoVersions(xlMetaBuf []byte, volume, path string) (FileInfoVers } fi.IsLatest = true // No versions so current version is latest. - fi.XLV1 = true // indicates older version + fi.NumVersions = 1 // just this version return FileInfoVersions{ Volume: volume, Name: path, diff --git a/cmd/xl-storage-format-utils_test.go b/cmd/xl-storage-format-utils_test.go index ee7d48fbf2b86..0310bde4fc97c 100644 --- a/cmd/xl-storage-format-utils_test.go +++ b/cmd/xl-storage-format-utils_test.go @@ -18,8 +18,11 @@ package cmd import ( + "sort" "testing" + "time" + "github.com/minio/minio/internal/bucket/lifecycle" xhttp "github.com/minio/minio/internal/http" ) @@ -108,3 +111,95 @@ func Test_hashDeterministicString(t *testing.T) { }) } } + +func TestGetFileInfoVersions(t *testing.T) { + basefi := FileInfo{ + Volume: "volume", + Name: "object-name", + VersionID: "756100c6-b393-4981-928a-d49bbc164741", + IsLatest: true, + Deleted: false, + TransitionStatus: "", + DataDir: "bffea160-ca7f-465f-98bc-9b4f1c3ba1ef", + XLV1: false, + ModTime: time.Now().UTC(), + Size: 0, + Mode: 0, + Metadata: nil, + Parts: nil, + Erasure: ErasureInfo{ + Algorithm: ReedSolomon.String(), + DataBlocks: 4, + ParityBlocks: 2, + BlockSize: 10000, + Index: 1, + Distribution: []int{1, 2, 3, 4, 5, 6, 7, 8}, + Checksums: []ChecksumInfo{{ + PartNumber: 1, + Algorithm: HighwayHash256S, + Hash: nil, + }}, + }, + MarkDeleted: false, + NumVersions: 1, + SuccessorModTime: time.Time{}, + } + xl := xlMetaV2{} + var versions []FileInfo + var freeVersionIDs []string + for i := 0; i < 5; i++ { + fi := basefi + fi.VersionID = mustGetUUID() + fi.DataDir = mustGetUUID() + fi.ModTime = basefi.ModTime.Add(time.Duration(i) * time.Second) + if err := xl.AddVersion(fi); err != nil { + t.Fatalf("%d: Failed to add version %v", i+1, err) + } + + if i > 3 { + // Simulate transition of a version + transfi := fi + transfi.TransitionStatus = lifecycle.TransitionComplete + transfi.TransitionTier = "MINIO-TIER" + transfi.TransitionedObjName = mustGetUUID() + xl.DeleteVersion(transfi) + + fi.SetTierFreeVersionID(mustGetUUID()) + // delete this version leading to a free version + xl.DeleteVersion(fi) + freeVersionIDs = append(freeVersionIDs, fi.TierFreeVersionID()) + } else { + versions = append(versions, fi) + } + } + buf, err := xl.AppendTo(nil) + if err != nil { + t.Fatalf("Failed to serialize xlmeta %v", err) + } + fivs, err := getFileInfoVersions(buf, basefi.Volume, basefi.Name) + if err != nil { + t.Fatalf("getFileInfoVersions failed: %v", err) + } + + sort.Slice(versions, func(i, j int) bool { + if versions[i].IsLatest { + return true + } + if versions[j].IsLatest { + return false + } + return versions[i].ModTime.After(versions[j].ModTime) + }) + + for i, fi := range fivs.Versions { + if fi.VersionID != versions[i].VersionID { + t.Fatalf("getFileInfoVersions: versions don't match at %d, version id expected %s but got %s", i, fi.VersionID, versions[i].VersionID) + } + } + + for i, free := range fivs.FreeVersions { + if free.VersionID != freeVersionIDs[i] { + t.Fatalf("getFileInfoVersions: free versions don't match at %d, version id expected %s but got %s", i, free.VersionID, freeVersionIDs[i]) + } + } +} diff --git a/cmd/xl-storage.go b/cmd/xl-storage.go index 9f6c9b93d6c01..ec2595a744268 100644 --- a/cmd/xl-storage.go +++ b/cmd/xl-storage.go @@ -455,7 +455,7 @@ func (s *xlStorage) NSScanner(ctx context.Context, cache dataUsageCache, updates // Remove filename which is the meta file. item.transformMetaDir() - fivs, err := getAllFileInfoVersions(buf, item.bucket, item.objectPath()) + fivs, err := getFileInfoVersions(buf, item.bucket, item.objectPath()) if err != nil { if intDataUpdateTracker.debug { console.Debugf(color.Green("scannerBucket:")+" reading xl.meta failed: %v: %w\n", item.Path, err) @@ -468,6 +468,13 @@ func (s *xlStorage) NSScanner(ctx context.Context, cache dataUsageCache, updates sizeS.tiers = make(map[string]tierStats) } atomic.AddUint64(&globalScannerStats.accTotalObjects, 1) + fivs.Versions, err = item.applyVersionActions(ctx, objAPI, fivs.Versions) + if err != nil { + if intDataUpdateTracker.debug { + console.Debugf(color.Green("scannerBucket:")+" applying version actions failed: %v: %w\n", item.Path, err) + } + return sizeSummary{}, errSkipFile + } for _, version := range fivs.Versions { atomic.AddUint64(&globalScannerStats.accTotalVersions, 1) oi := version.ToObjectInfo(item.bucket, item.objectPath()) @@ -492,6 +499,12 @@ func (s *xlStorage) NSScanner(ctx context.Context, cache dataUsageCache, updates } sizeS.tiers[tier] = sizeS.tiers[tier].add(oi.tierStats()) } + + // apply tier sweep action on free versions + for _, freeVersion := range fivs.FreeVersions { + oi := freeVersion.ToObjectInfo(item.bucket, item.objectPath()) + item.applyTierObjSweep(ctx, objAPI, oi) + } return sizeS, nil }) diff --git a/docs/bucket/lifecycle/README.md b/docs/bucket/lifecycle/README.md index 4446717bab21c..492f43dfd46c1 100644 --- a/docs/bucket/lifecycle/README.md +++ b/docs/bucket/lifecycle/README.md @@ -81,7 +81,29 @@ e.g., To scan objects stored under `user-uploads/` prefix and remove versions ol } ``` -### 3.2 Automatic removal of delete markers with no other versions + +### 3.2 Automatic removal of noncurrent versions older than most recent + +It is possible to configure automatic removal of noncurrent versions older than the most recent `N` using MinIO specific lifecycle policy extension `MaxNoncurrentVersions`. + +e.g, To remove noncurrent versions of all objects older than most recent 5 noncurrent versions under the prefix `user-uploads/`, +``` +{ + "Rules": [ + { + "ID": "Remove noncurrent versions older than", + "Status": "Enabled", + "Filter": { + "Prefix": "users-uploads/" + }, + "NoncurrentVersionExpiration": { + "MaxNoncurrentVersions": 5 + } + } + ] +} +``` +### 3.3 Automatic removal of delete markers with no other versions When an object has only one version as a delete marker, the latter can be automatically removed after a certain number of days using the following configuration: diff --git a/internal/bucket/lifecycle/lifecycle.go b/internal/bucket/lifecycle/lifecycle.go index 1ad24d2bff0f4..c622430f3ddeb 100644 --- a/internal/bucket/lifecycle/lifecycle.go +++ b/internal/bucket/lifecycle/lifecycle.go @@ -29,10 +29,11 @@ import ( ) var ( - errLifecycleTooManyRules = Errorf("Lifecycle configuration allows a maximum of 1000 rules") - errLifecycleNoRule = Errorf("Lifecycle configuration should have at least one rule") - errLifecycleDuplicateID = Errorf("Lifecycle configuration has rule with the same ID. Rule ID must be unique.") - errXMLNotWellFormed = Errorf("The XML you provided was not well-formed or did not validate against our published schema") + errLifecycleTooManyRules = Errorf("Lifecycle configuration allows a maximum of 1000 rules") + errLifecycleNoRule = Errorf("Lifecycle configuration should have at least one rule") + errLifecycleDuplicateID = Errorf("Lifecycle configuration has rule with the same ID. Rule ID must be unique.") + errXMLNotWellFormed = Errorf("The XML you provided was not well-formed or did not validate against our published schema") + errLifecycleInvalidNoncurrentExpiration = Errorf("Exactly one of NoncurrentDays (positive integer) or MaxNoncurrentVersions should be specified in a NoncurrentExpiration rule.") ) const ( @@ -140,6 +141,9 @@ func (lc Lifecycle) HasActiveRules(prefix string, recursive bool) bool { if rule.NoncurrentVersionExpiration.NoncurrentDays > 0 { return true } + if rule.NoncurrentVersionExpiration.MaxNoncurrentVersions > 0 { + return true + } if !rule.NoncurrentVersionTransition.IsNull() { return true } @@ -234,6 +238,10 @@ func (lc Lifecycle) FilterActionableRules(obj ObjectOpts) []Rule { rules = append(rules, rule) continue } + if rule.NoncurrentVersionExpiration.MaxNoncurrentVersions > 0 { + rules = append(rules, rule) + continue + } // The NoncurrentVersionTransition action requests MinIO to transition // noncurrent versions of objects x days after the objects become // noncurrent. @@ -468,3 +476,32 @@ func (lc Lifecycle) TransitionTier(obj ObjectOpts) string { } return "" } + +// NoncurrentVersionsExpirationLimit returns the minimum limit on number of +// noncurrent versions across rules. +func (lc Lifecycle) NoncurrentVersionsExpirationLimit(obj ObjectOpts) int { + var lim int + for _, rule := range lc.FilterActionableRules(obj) { + if rule.NoncurrentVersionExpiration.MaxNoncurrentVersions == 0 { + continue + } + if lim == 0 || lim > rule.NoncurrentVersionExpiration.MaxNoncurrentVersions { + lim = rule.NoncurrentVersionExpiration.MaxNoncurrentVersions + } + } + return lim +} + +// HasMaxNoncurrentVersions returns true if there exists a rule with +// MaxNoncurrentVersions limit set. +func (lc Lifecycle) HasMaxNoncurrentVersions() bool { + for _, rule := range lc.Rules { + if rule.Status == Disabled { + continue + } + if rule.NoncurrentVersionExpiration.MaxNoncurrentVersions > 0 { + return true + } + } + return false +} diff --git a/internal/bucket/lifecycle/lifecycle_test.go b/internal/bucket/lifecycle/lifecycle_test.go index 5d53c1e0ca257..6b4133ff38591 100644 --- a/internal/bucket/lifecycle/lifecycle_test.go +++ b/internal/bucket/lifecycle/lifecycle_test.go @@ -23,6 +23,7 @@ import ( "fmt" "net/http" "net/http/httptest" + "strconv" "strings" "testing" "time" @@ -111,6 +112,12 @@ func TestParseAndValidateLifecycleConfig(t *testing.T) { expectedParsingErr: nil, expectedValidationErr: nil, }, + // Lifecycle with max noncurrent versions + { + inputConfig: `rule>Enabled5`, + expectedParsingErr: nil, + expectedValidationErr: nil, + }, } for i, tc := range testCases { @@ -619,3 +626,24 @@ func TestTransitionTier(t *testing.T) { t.Fatalf("Expected TIER-2 but got %s", got) } } + +func TestNoncurrentVersionsLimit(t *testing.T) { + // test that the lowest max noncurrent versions limit is returned among + // matching rules + var rules []Rule + for i := 1; i <= 10; i++ { + rules = append(rules, Rule{ + ID: strconv.Itoa(i), + Status: "Enabled", + NoncurrentVersionExpiration: NoncurrentVersionExpiration{ + MaxNoncurrentVersions: i, + }, + }) + } + lc := Lifecycle{ + Rules: rules, + } + if lim := lc.NoncurrentVersionsExpirationLimit(ObjectOpts{Name: "obj"}); lim != 1 { + t.Fatalf("Expected max noncurrent versions limit to be 1 but got %d", lim) + } +} diff --git a/internal/bucket/lifecycle/noncurrentversion.go b/internal/bucket/lifecycle/noncurrentversion.go index 787df0763bd85..cf9ff6a04dfc5 100644 --- a/internal/bucket/lifecycle/noncurrentversion.go +++ b/internal/bucket/lifecycle/noncurrentversion.go @@ -24,14 +24,15 @@ import ( // NoncurrentVersionExpiration - an action for lifecycle configuration rule. type NoncurrentVersionExpiration struct { - XMLName xml.Name `xml:"NoncurrentVersionExpiration"` - NoncurrentDays ExpirationDays `xml:"NoncurrentDays,omitempty"` - set bool + XMLName xml.Name `xml:"NoncurrentVersionExpiration"` + NoncurrentDays ExpirationDays `xml:"NoncurrentDays,omitempty"` + MaxNoncurrentVersions int `xml:"MaxNoncurrentVersions,omitempty"` + set bool } // MarshalXML if non-current days not set to non zero value func (n NoncurrentVersionExpiration) MarshalXML(e *xml.Encoder, start xml.StartElement) error { - if n.IsDaysNull() { + if n.IsNull() { return nil } type noncurrentVersionExpirationWrapper NoncurrentVersionExpiration @@ -51,6 +52,11 @@ func (n *NoncurrentVersionExpiration) UnmarshalXML(d *xml.Decoder, startElement return nil } +// IsNull returns if both NoncurrentDays and NoncurrentVersions are empty +func (n NoncurrentVersionExpiration) IsNull() bool { + return n.IsDaysNull() && n.MaxNoncurrentVersions == 0 +} + // IsDaysNull returns true if days field is null func (n NoncurrentVersionExpiration) IsDaysNull() bool { return n.NoncurrentDays == ExpirationDays(0) @@ -62,8 +68,17 @@ func (n NoncurrentVersionExpiration) Validate() error { return nil } val := int(n.NoncurrentDays) - if val <= 0 { + switch { + case val == 0 && n.MaxNoncurrentVersions == 0: + // both fields can't be zero return errXMLNotWellFormed + + case val > 0 && n.MaxNoncurrentVersions > 0: + // both tags can't be non-zero simultaneously + return errLifecycleInvalidNoncurrentExpiration + + case val < 0, n.MaxNoncurrentVersions < 0: + // negative values are not supported } return nil }