Merge pull request #1755 from versity/sis/missing-bucket-lock-err

fix: fixes missing bucket object lock config error
This commit is contained in:
Ben McClelland
2026-01-14 07:59:05 -08:00
committed by GitHub
11 changed files with 126 additions and 18 deletions

View File

@@ -364,6 +364,9 @@ func (az *Azure) PutObject(ctx context.Context, po s3response.PutObjectInput) (s
if po.ObjectLockLegalHoldStatus == types.ObjectLockLegalHoldStatusOn {
err := az.PutObjectLegalHold(ctx, *po.Bucket, *po.Key, "", true)
if err != nil {
if errors.Is(err, s3err.GetAPIError(s3err.ErrMissingObjectLockConfiguration)) {
err = s3err.GetAPIError(s3err.ErrMissingObjectLockConfigurationNoSpaces)
}
return s3response.PutObjectOutput{}, err
}
}
@@ -380,6 +383,9 @@ func (az *Azure) PutObject(ctx context.Context, po s3response.PutObjectInput) (s
}
err = az.PutObjectRetention(ctx, *po.Bucket, *po.Key, "", retParsed)
if err != nil {
if errors.Is(err, s3err.GetAPIError(s3err.ErrMissingObjectLockConfiguration)) {
err = s3err.GetAPIError(s3err.ErrMissingObjectLockConfigurationNoSpaces)
}
return s3response.PutObjectOutput{}, err
}
}
@@ -980,6 +986,9 @@ func (az *Azure) CopyObject(ctx context.Context, input s3response.CopyObjectInpu
if input.ObjectLockLegalHoldStatus != "" {
err = az.PutObjectLegalHold(ctx, *input.Bucket, *input.Key, "", input.ObjectLockLegalHoldStatus == types.ObjectLockLegalHoldStatusOn)
if err != nil {
if errors.Is(err, s3err.GetAPIError(s3err.ErrMissingObjectLockConfiguration)) {
err = s3err.GetAPIError(s3err.ErrMissingObjectLockConfigurationNoSpaces)
}
return s3response.CopyObjectOutput{}, azureErrToS3Err(err)
}
}
@@ -998,6 +1007,9 @@ func (az *Azure) CopyObject(ctx context.Context, input s3response.CopyObjectInpu
}
err = az.PutObjectRetention(ctx, *input.Bucket, *input.Key, "", retParsed)
if err != nil {
if errors.Is(err, s3err.GetAPIError(s3err.ErrMissingObjectLockConfiguration)) {
err = s3err.GetAPIError(s3err.ErrMissingObjectLockConfigurationNoSpaces)
}
return s3response.CopyObjectOutput{}, azureErrToS3Err(err)
}
}
@@ -1140,7 +1152,7 @@ func (az *Azure) CreateMultipartUpload(ctx context.Context, input s3response.Cre
}
if len(bucketLock) == 0 {
return s3response.InitiateMultipartUploadResult{}, s3err.GetAPIError(s3err.ErrInvalidBucketObjectLockConfiguration)
return s3response.InitiateMultipartUploadResult{}, s3err.GetAPIError(s3err.ErrMissingObjectLockConfigurationNoSpaces)
}
var bucketLockConfig auth.BucketLockConfig
@@ -1149,7 +1161,7 @@ func (az *Azure) CreateMultipartUpload(ctx context.Context, input s3response.Cre
}
if !bucketLockConfig.Enabled {
return s3response.InitiateMultipartUploadResult{}, s3err.GetAPIError(s3err.ErrInvalidBucketObjectLockConfiguration)
return s3response.InitiateMultipartUploadResult{}, s3err.GetAPIError(s3err.ErrMissingObjectLockConfigurationNoSpaces)
}
}
@@ -1839,7 +1851,7 @@ func (az *Azure) isBucketObjectLockEnabled(ctx context.Context, bucket string) e
}
if len(cfg) == 0 {
return s3err.GetAPIError(s3err.ErrInvalidBucketObjectLockConfiguration)
return s3err.GetAPIError(s3err.ErrMissingObjectLockConfiguration)
}
var bucketLockConfig auth.BucketLockConfig
@@ -1848,7 +1860,7 @@ func (az *Azure) isBucketObjectLockEnabled(ctx context.Context, bucket string) e
}
if !bucketLockConfig.Enabled {
return s3err.GetAPIError(s3err.ErrInvalidBucketObjectLockConfiguration)
return s3err.GetAPIError(s3err.ErrMissingObjectLockConfiguration)
}
return nil

View File

@@ -1359,6 +1359,9 @@ func (p *Posix) CreateMultipartUpload(ctx context.Context, mpu s3response.Create
if mpu.ObjectLockLegalHoldStatus == types.ObjectLockLegalHoldStatusOn {
err := p.PutObjectLegalHold(ctx, bucket, filepath.Join(objdir, uploadID), "", true)
if err != nil {
if errors.Is(err, s3err.GetAPIError(s3err.ErrMissingObjectLockConfiguration)) {
err = s3err.GetAPIError(s3err.ErrMissingObjectLockConfigurationNoSpaces)
}
// cleanup object if returning error
os.RemoveAll(filepath.Join(tmppath, uploadID))
os.Remove(tmppath)
@@ -1381,6 +1384,9 @@ func (p *Posix) CreateMultipartUpload(ctx context.Context, mpu s3response.Create
}
err = p.PutObjectRetention(ctx, bucket, filepath.Join(objdir, uploadID), "", retParsed)
if err != nil {
if errors.Is(err, s3err.GetAPIError(s3err.ErrMissingObjectLockConfiguration)) {
err = s3err.GetAPIError(s3err.ErrMissingObjectLockConfigurationNoSpaces)
}
// cleanup object if returning error
os.RemoveAll(filepath.Join(tmppath, uploadID))
os.Remove(tmppath)
@@ -3238,6 +3244,9 @@ func (p *Posix) PutObjectWithPostFunc(ctx context.Context, po s3response.PutObje
if po.ObjectLockLegalHoldStatus == types.ObjectLockLegalHoldStatusOn {
err := p.PutObjectLegalHold(ctx, *po.Bucket, *po.Key, "", true)
if err != nil {
if errors.Is(err, s3err.GetAPIError(s3err.ErrMissingObjectLockConfiguration)) {
err = s3err.GetAPIError(s3err.ErrMissingObjectLockConfigurationNoSpaces)
}
return s3response.PutObjectOutput{}, err
}
}
@@ -3254,6 +3263,9 @@ func (p *Posix) PutObjectWithPostFunc(ctx context.Context, po s3response.PutObje
}
err = p.PutObjectRetention(ctx, *po.Bucket, *po.Key, "", retParsed)
if err != nil {
if errors.Is(err, s3err.GetAPIError(s3err.ErrMissingObjectLockConfiguration)) {
err = s3err.GetAPIError(s3err.ErrMissingObjectLockConfigurationNoSpaces)
}
return s3response.PutObjectOutput{}, err
}
}
@@ -5108,7 +5120,7 @@ func (p *Posix) isBucketObjectLockEnabled(bucket string) error {
return s3err.GetAPIError(s3err.ErrNoSuchBucket)
}
if errors.Is(err, meta.ErrNoSuchKey) {
return s3err.GetAPIError(s3err.ErrInvalidBucketObjectLockConfiguration)
return s3err.GetAPIError(s3err.ErrMissingObjectLockConfiguration)
}
if err != nil {
return fmt.Errorf("get object lock config: %w", err)
@@ -5120,7 +5132,7 @@ func (p *Posix) isBucketObjectLockEnabled(bucket string) error {
}
if !bucketLockConfig.Enabled {
return s3err.GetAPIError(s3err.ErrInvalidBucketObjectLockConfiguration)
return s3err.GetAPIError(s3err.ErrMissingObjectLockConfiguration)
}
return nil

View File

@@ -138,7 +138,8 @@ const (
ErrInvalidURI
ErrObjectLockConfigurationNotFound
ErrNoSuchObjectLockConfiguration
ErrInvalidBucketObjectLockConfiguration
ErrMissingObjectLockConfiguration
ErrMissingObjectLockConfigurationNoSpaces
ErrObjectLockConfigurationNotAllowed
ErrObjectLocked
ErrInvalidRetainUntilDate
@@ -600,9 +601,14 @@ var errorCodeResponse = map[ErrorCode]APIError{
Description: "The specified object does not have a ObjectLock configuration.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrInvalidBucketObjectLockConfiguration: {
ErrMissingObjectLockConfiguration: {
Code: "InvalidRequest",
Description: "Bucket is missing Object Lock Configuration.",
Description: "Bucket is missing Object Lock Configuration",
HTTPStatusCode: http.StatusBadRequest,
},
ErrMissingObjectLockConfigurationNoSpaces: {
Code: "InvalidRequest",
Description: "Bucket is missing ObjectLockConfiguration",
HTTPStatusCode: http.StatusBadRequest,
},
ErrObjectLockConfigurationNotAllowed: {

View File

@@ -746,6 +746,45 @@ func CopyObject_should_replace_meta_props(s *S3Conf) error {
})
}
func CopyObject_missing_bucket_lock(s *S3Conf) error {
testName := "CopyObject_missing_bucket_lock"
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
srcObj, dstObj := "source-object", "dst-object"
_, err := putObjectWithData(10, &s3.PutObjectInput{
Bucket: &bucket,
Key: &srcObj,
}, s3client)
if err != nil {
return err
}
// with retention
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
_, err = s3client.CopyObject(ctx, &s3.CopyObjectInput{
Bucket: &bucket,
Key: &dstObj,
CopySource: getPtr(fmt.Sprintf("%s/%s", bucket, srcObj)),
ObjectLockMode: types.ObjectLockModeGovernance,
ObjectLockRetainUntilDate: getPtr(time.Now().AddDate(0, 1, 0)),
})
cancel()
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrMissingObjectLockConfigurationNoSpaces)); err != nil {
return err
}
// with legal hold
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
_, err = s3client.CopyObject(ctx, &s3.CopyObjectInput{
Bucket: &bucket,
Key: &dstObj,
CopySource: getPtr(fmt.Sprintf("%s/%s", bucket, srcObj)),
ObjectLockLegalHoldStatus: types.ObjectLockLegalHoldStatusOn,
})
cancel()
return checkApiErr(err, s3err.GetAPIError(s3err.ErrMissingObjectLockConfigurationNoSpaces))
})
}
func CopyObject_invalid_legal_hold(s *S3Conf) error {
testName := "CopyObject_invalid_legal_hold"
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {

View File

@@ -209,18 +209,29 @@ func CreateMultipartUpload_with_object_lock_not_enabled(s *S3Conf) error {
testName := "CreateMultipartUpload_with_object_lock_not_enabled"
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
obj := "my-obj"
// with retention
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
_, err := s3client.CreateMultipartUpload(ctx, &s3.CreateMultipartUploadInput{
Bucket: &bucket,
Key: &obj,
ObjectLockMode: types.ObjectLockModeGovernance,
ObjectLockRetainUntilDate: getPtr(time.Now().AddDate(1, 0, 0)),
})
cancel()
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrMissingObjectLockConfigurationNoSpaces)); err != nil {
return err
}
// with legal hold
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
_, err = s3client.CreateMultipartUpload(ctx, &s3.CreateMultipartUploadInput{
Bucket: &bucket,
Key: &obj,
ObjectLockLegalHoldStatus: types.ObjectLockLegalHoldStatusOn,
})
cancel()
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidBucketObjectLockConfiguration)); err != nil {
return err
}
return nil
return checkApiErr(err, s3err.GetAPIError(s3err.ErrMissingObjectLockConfigurationNoSpaces))
})
}

View File

@@ -72,7 +72,7 @@ func GetObjectLegalHold_disabled_lock(s *S3Conf) error {
Key: &key,
})
cancel()
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidBucketObjectLockConfiguration)); err != nil {
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrMissingObjectLockConfiguration)); err != nil {
return err
}

View File

@@ -73,7 +73,7 @@ func GetObjectRetention_disabled_lock(s *S3Conf) error {
Key: &key,
})
cancel()
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidBucketObjectLockConfiguration)); err != nil {
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrMissingObjectLockConfiguration)); err != nil {
return err
}

View File

@@ -274,6 +274,30 @@ func PutObject_with_object_lock(s *S3Conf) error {
}, withLock())
}
func PutObject_missing_bucket_lock(s *S3Conf) error {
testName := "PutObject_missing_bucket_lock"
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
// with retention
_, err := putObjectWithData(3, &s3.PutObjectInput{
Bucket: &bucket,
Key: getPtr("my-object"),
ObjectLockMode: types.ObjectLockModeGovernance,
ObjectLockRetainUntilDate: getPtr(time.Now().AddDate(0, 0, 2)),
}, s3client)
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrMissingObjectLockConfigurationNoSpaces)); err != nil {
return err
}
// with legal hold
_, err = putObjectWithData(2, &s3.PutObjectInput{
Bucket: &bucket,
Key: getPtr("my-object"),
ObjectLockLegalHoldStatus: types.ObjectLockLegalHoldStatusOn,
}, s3client)
return checkApiErr(err, s3err.GetAPIError(s3err.ErrMissingObjectLockConfigurationNoSpaces))
})
}
func PutObject_invalid_legal_hold(s *S3Conf) error {
testName := "PutObject_invalid_legal_hold"
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {

View File

@@ -115,7 +115,7 @@ func PutObjectLegalHold_unset_bucket_object_lock_config(s *S3Conf) error {
},
})
cancel()
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidBucketObjectLockConfiguration)); err != nil {
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrMissingObjectLockConfiguration)); err != nil {
return err
}

View File

@@ -84,7 +84,7 @@ func PutObjectRetention_unset_bucket_object_lock_config(s *S3Conf) error {
},
})
cancel()
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidBucketObjectLockConfiguration)); err != nil {
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrMissingObjectLockConfiguration)); err != nil {
return err
}

View File

@@ -163,6 +163,7 @@ func TestPutObject(ts *TestState) {
ts.Run(PutObject_tagging)
ts.Run(PutObject_missing_object_lock_retention_config)
ts.Run(PutObject_with_object_lock)
ts.Run(PutObject_missing_bucket_lock)
ts.Run(PutObject_invalid_legal_hold)
ts.Run(PutObject_invalid_object_lock_mode)
ts.Run(PutObject_past_retain_until_date)
@@ -328,6 +329,7 @@ func TestCopyObject(ts *TestState) {
ts.Run(CopyObject_non_existing_dir_object)
ts.Run(CopyObject_should_copy_meta_props)
ts.Run(CopyObject_should_replace_meta_props)
ts.Run(CopyObject_missing_bucket_lock)
ts.Run(CopyObject_invalid_legal_hold)
ts.Run(CopyObject_invalid_object_lock_mode)
ts.Run(CopyObject_with_legal_hold)
@@ -1201,6 +1203,7 @@ func GetIntTests() IntTests {
"PutObject_missing_object_lock_retention_config": PutObject_missing_object_lock_retention_config,
"PutObject_name_too_long": PutObject_name_too_long,
"PutObject_with_object_lock": PutObject_with_object_lock,
"PutObject_missing_bucket_lock": PutObject_missing_bucket_lock,
"PutObject_invalid_legal_hold": PutObject_invalid_legal_hold,
"PutObject_invalid_object_lock_mode": PutObject_invalid_object_lock_mode,
"PutObject_past_retain_until_date": PutObject_past_retain_until_date,
@@ -1373,6 +1376,7 @@ func GetIntTests() IntTests {
"CopyObject_non_existing_dir_object": CopyObject_non_existing_dir_object,
"CopyObject_should_copy_meta_props": CopyObject_should_copy_meta_props,
"CopyObject_should_replace_meta_props": CopyObject_should_replace_meta_props,
"CopyObject_missing_bucket_lock": CopyObject_missing_bucket_lock,
"CopyObject_invalid_legal_hold": CopyObject_invalid_legal_hold,
"CopyObject_invalid_object_lock_mode": CopyObject_invalid_object_lock_mode,
"CopyObject_with_legal_hold": CopyObject_with_legal_hold,