diff --git a/cmd/bucket-metadata.go b/cmd/bucket-metadata.go
index 2909ced4e..fa4f34b7a 100644
--- a/cmd/bucket-metadata.go
+++ b/cmd/bucket-metadata.go
@@ -377,15 +377,6 @@ func (b *BucketMetadata) parseAllConfigs(ctx context.Context, objectAPI ObjectLa
b.corsConfig = nil
}
- if bytes.Equal(b.ObjectLockConfigXML, enabledBucketObjectLockConfig) {
- // A locked bucket needs plain Enabled versioning; suspended or
- // prefix-excluded configurations are not honored for it.
- config, versioningErr := versioning.ParseConfig(bytes.NewReader(b.VersioningConfigXML))
- if versioningErr != nil || !config.Enabled() || config.PrefixesExcluded() {
- b.VersioningConfigXML = enabledBucketVersioningConfig
- }
- }
-
if len(b.ObjectLockConfigXML) != 0 {
b.objectLockConfig, err = objectlock.ParseObjectLockConfig(bytes.NewReader(b.ObjectLockConfigXML))
if err != nil {
@@ -394,6 +385,15 @@ func (b *BucketMetadata) parseAllConfigs(ctx context.Context, objectAPI ObjectLa
} else {
b.objectLockConfig = nil
}
+ if b.objectLockConfig != nil {
+ // Object Lock requires every object to be versioned. Whatever the lock
+ // document contains, a suspended or prefix-excluded versioning document
+ // is replaced by plain Enabled versioning; Save persists the result.
+ config, versioningErr := versioning.ParseConfig(bytes.NewReader(b.VersioningConfigXML))
+ if versioningErr != nil || !config.Enabled() || config.PrefixesExcluded() {
+ b.VersioningConfigXML = enabledBucketVersioningConfig
+ }
+ }
if len(b.VersioningConfigXML) != 0 {
b.versioningConfig, err = versioning.ParseConfig(bytes.NewReader(b.VersioningConfigXML))
diff --git a/cmd/site-replication-bucket-adoption_test.go b/cmd/site-replication-bucket-adoption_test.go
index 2c056c5ab..09f494bd5 100644
--- a/cmd/site-replication-bucket-adoption_test.go
+++ b/cmd/site-replication-bucket-adoption_test.go
@@ -190,3 +190,48 @@ func testPeerBucketAdoptionBootstrapsMissingConfigs(_ ObjectLayer, instanceType,
after.ObjectLockConfigUpdatedAt, after.VersioningConfigUpdatedAt, before.Created)
}
}
+
+// TestLockedBucketNormalizesVersioningOnSave covers the metadata boundary
+// itself: whatever writer stores a suspended or prefix-excluded versioning
+// document on a bucket that carries an Object Lock configuration, including
+// one with a default retention rule, Save replaces it with plain Enabled
+// versioning.
+func TestLockedBucketNormalizesVersioningOnSave(t *testing.T) {
+ defer DetectTestLeak(t)()
+ ExecObjectLayerAPITest(ExecObjectLayerAPITestArgs{
+ t: t,
+ objAPITest: testLockedBucketNormalizesVersioningOnSave,
+ makeBucketOptions: MakeBucketOptions{LockEnabled: true},
+ })
+}
+
+func testLockedBucketNormalizesVersioningOnSave(_ ObjectLayer, instanceType, bucketName string,
+ _ http.Handler, _ auth.Credentials, t *testing.T,
+) {
+ lockWithRule := []byte(`EnabledGOVERNANCE30`)
+ if _, err := globalBucketMetadataSys.Update(t.Context(), bucketName, objectLockConfig, lockWithRule); err != nil {
+ t.Fatal(err)
+ }
+ for name, versioningXML := range map[string][]byte{
+ "prefix-excluded": []byte(`Enabledtruetemporary/`),
+ "suspended": []byte(`Suspended`),
+ } {
+ if _, err := globalBucketMetadataSys.Update(t.Context(), bucketName, bucketVersioningConfig, versioningXML); err != nil {
+ t.Fatal(err)
+ }
+ meta, err := globalBucketMetadataSys.Get(bucketName)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(meta.VersioningConfigXML, enabledBucketVersioningConfig) {
+ t.Fatalf("%s/%s: locked bucket kept versioning %q", instanceType, name, meta.VersioningConfigXML)
+ }
+ reloaded, err := loadBucketMetadata(t.Context(), newObjectLayerFn(), bucketName)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(reloaded.VersioningConfigXML, enabledBucketVersioningConfig) {
+ t.Fatalf("%s/%s: locked bucket persisted versioning %q", instanceType, name, reloaded.VersioningConfigXML)
+ }
+ }
+}
diff --git a/cmd/site-replication-object-lock_test.go b/cmd/site-replication-object-lock_test.go
index 48d1d35f5..bf28df9fa 100644
--- a/cmd/site-replication-object-lock_test.go
+++ b/cmd/site-replication-object-lock_test.go
@@ -159,8 +159,10 @@ func testPeerBucketObjectLockMetadataWithoutLockEnabled(_ ObjectLayer, instanceT
if err != nil {
t.Fatal(err)
}
- if meta.objectLockConfig == nil || len(meta.VersioningConfigXML) != 0 {
- t.Fatalf("%s: unlocked bucket metadata = objectLock:%v versioning:%q", instanceType, meta.objectLockConfig, meta.VersioningConfigXML)
+ // A lock configuration implies versioning: the bucket was created without
+ // lock, so receiving the configuration turns plain Enabled versioning on.
+ if meta.objectLockConfig == nil || !bytes.Equal(meta.VersioningConfigXML, enabledBucketVersioningConfig) {
+ t.Fatalf("%s: bucket metadata = objectLock:%v versioning:%q", instanceType, meta.objectLockConfig, meta.VersioningConfigXML)
}
}