mirror of
https://github.com/versity/versitygw.git
synced 2026-09-19 06:32:10 +00:00
Merge pull request #2293 from lhjnano/fix/getbucketversioning-empty-config
fix(posix): return empty config for GetBucketVersioning when unconfigured
This commit is contained in:
@@ -890,10 +890,6 @@ func (p *Posix) GetBucketVersioning(ctx context.Context, bucket string) (s3respo
|
||||
}
|
||||
defer release()
|
||||
|
||||
if !p.versioningEnabled() {
|
||||
return s3response.GetBucketVersioningOutput{}, s3err.GetAPIError(s3err.ErrVersioningNotConfigured)
|
||||
}
|
||||
|
||||
if !p.isBucketValid(bucket) {
|
||||
return s3response.GetBucketVersioningOutput{}, s3err.GetBucketErr(s3err.ErrInvalidBucketName, bucket)
|
||||
}
|
||||
@@ -906,6 +902,15 @@ func (p *Posix) GetBucketVersioning(ctx context.Context, bucket string) (s3respo
|
||||
return s3response.GetBucketVersioningOutput{}, fmt.Errorf("stat bucket: %w", err)
|
||||
}
|
||||
|
||||
if !p.versioningEnabled() {
|
||||
// No versioning directory is configured: report the bucket as
|
||||
// never-versioned (200 with an empty configuration) to match AWS,
|
||||
// instead of failing with VersioningNotConfigured. GUI clients
|
||||
// (S3 Browser, CloudBerry, ...) poll this API while browsing
|
||||
// buckets. PutBucketVersioning still rejects the request.
|
||||
return s3response.GetBucketVersioningOutput{}, nil
|
||||
}
|
||||
|
||||
vData, err := p.meta.RetrieveAttribute(nil, bucket, "", versioningKey)
|
||||
if errors.Is(err, meta.ErrNoSuchKey) {
|
||||
return s3response.GetBucketVersioningOutput{}, nil
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright 2026 Versity Software
|
||||
// This file is licensed under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package posix
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/versity/versitygw/backend/meta"
|
||||
"github.com/versity/versitygw/s3err"
|
||||
)
|
||||
|
||||
// newUnversionedGateway creates a Posix backend over a temporary root
|
||||
// directory with no versioning directory configured, i.e. gateway-level
|
||||
// versioning disabled, matching the default gateway configuration.
|
||||
func newUnversionedGateway(t *testing.T) *Posix {
|
||||
t.Helper()
|
||||
|
||||
p, err := New(t.TempDir(), meta.XattrMeta{}, PosixOpts{
|
||||
ValidateBucketNames: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("init posix backend: %v", err)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// TestVersioningUnconfigured covers the bucket versioning behavior when the
|
||||
// gateway has no versioning directory configured: bucket validation still
|
||||
// applies first, GetBucketVersioning returns an empty configuration, and
|
||||
// PutBucketVersioning is rejected.
|
||||
func TestVersioningUnconfigured(t *testing.T) {
|
||||
// New() chdirs into the gateway root; restore the original working
|
||||
// directory when the test completes.
|
||||
t.Chdir(t.TempDir())
|
||||
|
||||
t.Run("get bucket versioning invalid bucket name", func(t *testing.T) {
|
||||
p := newUnversionedGateway(t)
|
||||
|
||||
_, err := p.GetBucketVersioning(context.Background(), "bad/bucket")
|
||||
if !errors.Is(err, s3err.GetAPIError(s3err.ErrInvalidBucketName)) {
|
||||
t.Errorf("expected InvalidBucketName, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("get bucket versioning no such bucket", func(t *testing.T) {
|
||||
p := newUnversionedGateway(t)
|
||||
|
||||
_, err := p.GetBucketVersioning(context.Background(), "does-not-exist")
|
||||
if !errors.Is(err, s3err.GetAPIError(s3err.ErrNoSuchBucket)) {
|
||||
t.Errorf("expected NoSuchBucket, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("get bucket versioning returns empty config", func(t *testing.T) {
|
||||
p := newUnversionedGateway(t)
|
||||
|
||||
err := os.Mkdir("bucket", 0o755)
|
||||
assert.NoError(t, err)
|
||||
|
||||
res, err := p.GetBucketVersioning(context.Background(), "bucket")
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, res.Status)
|
||||
})
|
||||
|
||||
t.Run("put bucket versioning invalid bucket name", func(t *testing.T) {
|
||||
p := newUnversionedGateway(t)
|
||||
|
||||
err := p.PutBucketVersioning(context.Background(), "bad/bucket", types.BucketVersioningStatusEnabled)
|
||||
if !errors.Is(err, s3err.GetAPIError(s3err.ErrInvalidBucketName)) {
|
||||
t.Errorf("expected InvalidBucketName, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("put bucket versioning not configured", func(t *testing.T) {
|
||||
p := newUnversionedGateway(t)
|
||||
|
||||
err := os.Mkdir("bucket", 0o755)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = p.PutBucketVersioning(context.Background(), "bucket", types.BucketVersioningStatusEnabled)
|
||||
if !errors.Is(err, s3err.GetAPIError(s3err.ErrVersioningNotConfigured)) {
|
||||
t.Errorf("expected VersioningNotConfigured, got %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1846,6 +1846,7 @@ func TestVersioning(ts *TestState) {
|
||||
|
||||
func TestVersioningDisabled(ts *TestState) {
|
||||
ts.Run(VersioningDisabled_GetBucketVersioning_not_configured)
|
||||
ts.Run(VersioningDisabled_GetBucketVersioning_no_such_bucket)
|
||||
ts.Run(VersioningDisabled_PutBucketVersioning_not_configured)
|
||||
}
|
||||
|
||||
|
||||
@@ -3620,8 +3620,32 @@ func Versioning_AccessControl_GetObjectAttributes_policy(s *S3Conf) error {
|
||||
func VersioningDisabled_GetBucketVersioning_not_configured(s *S3Conf) error {
|
||||
testName := "VersioningDisabled_GetBucketVersioning_not_configured"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
err := putBucketVersioningStatus(s3client, bucket, types.BucketVersioningStatusEnabled)
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrVersioningNotConfigured)); err != nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
res, err := s3client.GetBucketVersioning(ctx, &s3.GetBucketVersioningInput{
|
||||
Bucket: &bucket,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if res.Status != "" {
|
||||
return fmt.Errorf("expected empty versioning status when versioning is not configured, instead got %v",
|
||||
res.Status)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func VersioningDisabled_GetBucketVersioning_no_such_bucket(s *S3Conf) error {
|
||||
testName := "VersioningDisabled_GetBucketVersioning_no_such_bucket"
|
||||
return actionHandlerNoSetup(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.GetBucketVersioning(ctx, &s3.GetBucketVersioningInput{
|
||||
Bucket: &bucket,
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchBucket)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3632,11 +3656,7 @@ func VersioningDisabled_GetBucketVersioning_not_configured(s *S3Conf) error {
|
||||
func VersioningDisabled_PutBucketVersioning_not_configured(s *S3Conf) error {
|
||||
testName := "VersioningDisabled_PutBucketVersioning_not_configured"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.GetBucketVersioning(ctx, &s3.GetBucketVersioningInput{
|
||||
Bucket: &bucket,
|
||||
})
|
||||
cancel()
|
||||
err := putBucketVersioningStatus(s3client, bucket, types.BucketVersioningStatusEnabled)
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrVersioningNotConfigured)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user