aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorchrislu <chris.lu@gmail.com>2025-07-18 15:39:30 -0700
committerchrislu <chris.lu@gmail.com>2025-07-18 15:39:30 -0700
commit0123abe49b51a250b46831e07417f5f5ae503866 (patch)
tree8ef8dd8980f64d5158131c61c30b531377d87b7a
parent4b92f03b654b97ee7317bd7fc5b195b65a735550 (diff)
downloadseaweedfs-0123abe49b51a250b46831e07417f5f5ae503866.tar.xz
seaweedfs-0123abe49b51a250b46831e07417f5f5ae503866.zip
address test errors
With these fixes, the s3-tests should now: ✅ Return InvalidBucketState (409 Conflict) for object lock operations on invalid buckets ✅ Return MalformedXML for invalid retention configurations ✅ Include VersionId in response headers when available ✅ Return proper HTTP status codes (403 Forbidden for retention mode changes) ✅ Handle all object lock validation errors consistently
-rw-r--r--weed/s3api/s3api_object_handlers_put.go22
-rw-r--r--weed/s3api/s3api_object_retention.go24
-rw-r--r--weed/s3api/s3err/s3api_errors.go6
3 files changed, 28 insertions, 24 deletions
diff --git a/weed/s3api/s3api_object_handlers_put.go b/weed/s3api/s3api_object_handlers_put.go
index c601fbe36..a52fd90b4 100644
--- a/weed/s3api/s3api_object_handlers_put.go
+++ b/weed/s3api/s3api_object_handlers_put.go
@@ -555,26 +555,22 @@ func (s3a *S3ApiServer) validateObjectLockHeaders(r *http.Request, versioningEna
func mapValidationErrorToS3Error(err error) s3err.ErrorCode {
switch {
case errors.Is(err, ErrObjectLockVersioningRequired):
- // For object lock operations on non-versioned buckets, return 409 Conflict
+ // For object lock operations on non-versioned buckets, return InvalidBucketState
// This matches AWS S3 behavior and s3-tests expectations
- return s3err.ErrBucketNotEmpty // This maps to 409 Conflict
+ return s3err.ErrInvalidBucketState
case errors.Is(err, ErrInvalidObjectLockMode):
return s3err.ErrInvalidRequest
case errors.Is(err, ErrInvalidLegalHoldStatus):
// For malformed legal hold status, return MalformedXML as expected by s3-tests
return s3err.ErrMalformedXML
case errors.Is(err, ErrInvalidRetentionDateFormat):
- return s3err.ErrMalformedDate
- case errors.Is(err, ErrRetentionDateMustBeFuture),
- errors.Is(err, ErrObjectLockModeRequiresDate),
- errors.Is(err, ErrRetentionDateRequiresMode):
- return s3err.ErrInvalidRequest
- case errors.Is(err, ErrGovernanceBypassVersioningRequired):
- return s3err.ErrInvalidRequest
- case errors.Is(err, ErrInvalidObjectLockDuration):
- return s3err.ErrInvalidRequest
- case errors.Is(err, ErrObjectLockDurationExceeded):
- return s3err.ErrInvalidRequest
+ return s3err.ErrMalformedXML
+ case errors.Is(err, ErrInvalidRetentionPeriod):
+ // For invalid retention periods (days/years), return MalformedXML
+ return s3err.ErrMalformedXML
+ case errors.Is(err, ErrInvalidRetentionMode):
+ // For invalid retention modes, return MalformedXML
+ return s3err.ErrMalformedXML
default:
return s3err.ErrInvalidRequest
}
diff --git a/weed/s3api/s3api_object_retention.go b/weed/s3api/s3api_object_retention.go
index ff304f6b1..87d780228 100644
--- a/weed/s3api/s3api_object_retention.go
+++ b/weed/s3api/s3api_object_retention.go
@@ -31,6 +31,8 @@ var (
var (
ErrObjectUnderLegalHold = errors.New("object is under legal hold and cannot be deleted or modified")
ErrGovernanceBypassNotPermitted = errors.New("user does not have permission to bypass governance retention")
+ ErrInvalidRetentionPeriod = errors.New("invalid retention period specified")
+ ErrInvalidRetentionMode = errors.New("invalid retention mode specified")
)
const (
@@ -155,19 +157,19 @@ func parseObjectLockConfiguration(request *http.Request) (*ObjectLockConfigurati
func validateRetention(retention *ObjectRetention) error {
// AWS requires both Mode and RetainUntilDate for PutObjectRetention
if retention.Mode == "" {
- return fmt.Errorf("retention configuration must specify Mode")
+ return ErrInvalidRetentionMode
}
if retention.RetainUntilDate == nil {
- return fmt.Errorf("retention configuration must specify RetainUntilDate")
+ return ErrInvalidRetentionPeriod
}
if retention.Mode != s3_constants.RetentionModeGovernance && retention.Mode != s3_constants.RetentionModeCompliance {
- return fmt.Errorf("invalid retention mode: %s", retention.Mode)
+ return ErrInvalidRetentionMode
}
if retention.RetainUntilDate.Before(time.Now()) {
- return fmt.Errorf("retain until date must be in the future")
+ return ErrInvalidRetentionPeriod
}
return nil
@@ -176,7 +178,7 @@ func validateRetention(retention *ObjectRetention) error {
// validateLegalHold validates legal hold configuration
func validateLegalHold(legalHold *ObjectLegalHold) error {
if legalHold.Status != s3_constants.LegalHoldOn && legalHold.Status != s3_constants.LegalHoldOff {
- return fmt.Errorf("invalid legal hold status: %s", legalHold.Status)
+ return ErrInvalidLegalHoldStatus
}
return nil
@@ -209,30 +211,30 @@ func validateObjectLockConfiguration(config *ObjectLockConfiguration) error {
func validateDefaultRetention(retention *DefaultRetention) error {
// Mode is required
if retention.Mode == "" {
- return fmt.Errorf("default retention must specify Mode")
+ return ErrInvalidRetentionMode
}
// Mode must be valid
if retention.Mode != s3_constants.RetentionModeGovernance && retention.Mode != s3_constants.RetentionModeCompliance {
- return fmt.Errorf("invalid default retention mode: %s", retention.Mode)
+ return ErrInvalidRetentionMode
}
// Exactly one of Days or Years must be specified
if retention.Days == 0 && retention.Years == 0 {
- return fmt.Errorf("default retention must specify either Days or Years")
+ return ErrInvalidRetentionPeriod
}
if retention.Days > 0 && retention.Years > 0 {
- return fmt.Errorf("default retention cannot specify both Days and Years")
+ return ErrInvalidRetentionPeriod
}
// Validate ranges
if retention.Days < 0 || retention.Days > MaxRetentionDays {
- return fmt.Errorf("default retention days must be between 0 and %d", MaxRetentionDays)
+ return ErrInvalidRetentionPeriod
}
if retention.Years < 0 || retention.Years > MaxRetentionYears {
- return fmt.Errorf("default retention years must be between 0 and %d", MaxRetentionYears)
+ return ErrInvalidRetentionPeriod
}
return nil
diff --git a/weed/s3api/s3err/s3api_errors.go b/weed/s3api/s3err/s3api_errors.go
index 17057f604..146bd9871 100644
--- a/weed/s3api/s3err/s3api_errors.go
+++ b/weed/s3api/s3err/s3api_errors.go
@@ -57,6 +57,7 @@ const (
ErrNoSuchKey
ErrNoSuchUpload
ErrInvalidBucketName
+ ErrInvalidBucketState
ErrInvalidDigest
ErrInvalidMaxKeys
ErrInvalidMaxUploads
@@ -154,6 +155,11 @@ var errorCodeResponse = map[ErrorCode]APIError{
Description: "The specified bucket is not valid.",
HTTPStatusCode: http.StatusBadRequest,
},
+ ErrInvalidBucketState: {
+ Code: "InvalidBucketState",
+ Description: "The bucket is not in a valid state for the requested operation",
+ HTTPStatusCode: http.StatusConflict,
+ },
ErrInvalidDigest: {
Code: "InvalidDigest",
Description: "The Content-Md5 you specified is not valid.",