This is an automated email from the ASF dual-hosted git repository.
sshenoy pushed a commit to branch HDDS-6517-Snapshot
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/HDDS-6517-Snapshot by this
push:
new 52103f273a HDDS-6984. [Snapshot] Prevent bucket from being deleted if
snapshot exists (#4015)
52103f273a is described below
commit 52103f273a186b1b3f1b0390e2363af3d45c2618
Author: Sadanand Shenoy <[email protected]>
AuthorDate: Wed Jan 18 00:57:21 2023 +0530
HDDS-6984. [Snapshot] Prevent bucket from being deleted if snapshot exists
(#4015)
---
.../hadoop/ozone/om/exceptions/OMException.java | 4 +-
.../org/apache/hadoop/ozone/om/TestOmSnapshot.java | 27 +++++++++++
.../src/main/proto/OmClientProtocol.proto | 2 +
.../om/request/bucket/OMBucketDeleteRequest.java | 56 ++++++++++++++++++++++
4 files changed, 88 insertions(+), 1 deletion(-)
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java
index 4b4a1698ec..fb920583b8 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java
@@ -259,6 +259,8 @@ public class OMException extends IOException {
FEATURE_NOT_ENABLED,
- INVALID_SNAPSHOT_ERROR
+ INVALID_SNAPSHOT_ERROR,
+
+ CONTAINS_SNAPSHOT,
}
}
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOmSnapshot.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOmSnapshot.java
index 719b07c222..ee5ca09866 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOmSnapshot.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOmSnapshot.java
@@ -46,6 +46,7 @@ import org.junit.Assert;
import org.junit.AfterClass;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
import org.junit.rules.Timeout;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -65,6 +66,7 @@ import java.util.concurrent.TimeoutException;
import static org.apache.hadoop.ozone.OzoneConsts.OM_DB_NAME;
import static org.apache.hadoop.ozone.OzoneConsts.OM_KEY_PREFIX;
import static org.apache.hadoop.ozone.OzoneConsts.OM_SNAPSHOT_DIR;
+import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.CONTAINS_SNAPSHOT;
import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.KEY_NOT_FOUND;
import static
org.apache.hadoop.ozone.om.helpers.BucketLayout.FILE_SYSTEM_OPTIMIZED;
import static org.apache.hadoop.ozone.om.helpers.BucketLayout.OBJECT_STORE;
@@ -399,6 +401,31 @@ public class TestOmSnapshot {
() -> createSnapshot(volume, bucket));
}
+ @Test
+ public void testBucketDeleteIfSnapshotExists() throws Exception {
+ String volume1 = "vol-" + RandomStringUtils.randomNumeric(5);
+ String bucket1 = "buc-" + RandomStringUtils.randomNumeric(5);
+ String bucket2 = "buc-" + RandomStringUtils.randomNumeric(5);
+ store.createVolume(volume1);
+ OzoneVolume volume = store.getVolume(volume1);
+ volume.createBucket(bucket1);
+ volume.createBucket(bucket2);
+ OzoneBucket bucketWithSnapshot = volume.getBucket(bucket1);
+ OzoneBucket bucketWithoutSnapshot = volume.getBucket(bucket2);
+ String key = "key-";
+ createFileKey(bucketWithSnapshot, key);
+ createFileKey(bucketWithoutSnapshot, key);
+ createSnapshot(volume1, bucket1);
+ deleteKeys(bucketWithSnapshot);
+ deleteKeys(bucketWithoutSnapshot);
+ OMException omException = Assertions.assertThrows(OMException.class,
+ () -> volume.deleteBucket(bucket1));
+ Assertions.assertEquals(CONTAINS_SNAPSHOT, omException.getResult());
+ // TODO: Delete snapshot then delete bucket1 when deletion is implemented
+ // no exception for bucket without snapshot
+ volume.deleteBucket(bucket2);
+ }
+
@Test
public void testSnapDiff() throws Exception {
String volume = "vol-" + RandomStringUtils.randomNumeric(5);
diff --git
a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
index 2aff916854..b810a41bb0 100644
--- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
+++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
@@ -457,6 +457,8 @@ enum Status {
FEATURE_NOT_ENABLED = 86;
INVALID_SNAPSHOT_ERROR = 87;
+
+ CONTAINS_SNAPSHOT = 88;
}
/**
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/bucket/OMBucketDeleteRequest.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/bucket/OMBucketDeleteRequest.java
index 12dfd4d3ca..ec6cb3b86a 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/bucket/OMBucketDeleteRequest.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/bucket/OMBucketDeleteRequest.java
@@ -19,12 +19,16 @@
package org.apache.hadoop.ozone.om.request.bucket;
import java.io.IOException;
+import java.util.Iterator;
import java.util.Map;
import com.google.common.base.Optional;
+import org.apache.hadoop.hdds.utils.db.Table;
+import org.apache.hadoop.hdds.utils.db.TableIterator;
import org.apache.hadoop.ozone.om.helpers.BucketLayout;
import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs;
+import org.apache.hadoop.ozone.om.helpers.SnapshotInfo;
import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
import org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator;
@@ -60,6 +64,7 @@ import org.apache.hadoop.hdds.utils.db.cache.CacheKey;
import org.apache.hadoop.hdds.utils.db.cache.CacheValue;
import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.BUCKET_NOT_FOUND;
+import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.CONTAINS_SNAPSHOT;
import static
org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.BUCKET_LOCK;
import static
org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK;
@@ -135,6 +140,19 @@ public class OMBucketDeleteRequest extends OMClientRequest
{
throw new OMException("Bucket is not empty",
OMException.ResultCodes.BUCKET_NOT_EMPTY);
}
+
+ // appending '/' to end to eliminate cases where 2 buckets start with
same
+ // characters.
+ String snapshotBucketKey = bucketKey + OzoneConsts.OM_KEY_PREFIX;
+
+ if (bucketContainsSnapshot(omMetadataManager, snapshotBucketKey)) {
+ LOG.debug("Bucket '{}' can't be deleted when it has snapshots",
+ bucketName);
+ throw new OMException(
+ "Bucket " + bucketName + " can't be deleted when it has snapshots",
+ CONTAINS_SNAPSHOT);
+ }
+
if (omBucketInfo.getBucketLayout().isFileSystemOptimized()) {
omMetrics.incNumFSOBucketDeletes();
}
@@ -198,6 +216,44 @@ public class OMBucketDeleteRequest extends OMClientRequest
{
}
}
+ private boolean bucketContainsSnapshot(OMMetadataManager omMetadataManager,
+ String snapshotBucketKey) throws IOException {
+ return bucketContainsSnapshotInCache(omMetadataManager, snapshotBucketKey)
+ || bucketContainsSnapshotInTable(omMetadataManager, snapshotBucketKey);
+ }
+
+ private boolean bucketContainsSnapshotInTable(
+ OMMetadataManager omMetadataManager, String snapshotBucketKey)
+ throws IOException {
+ try (
+ TableIterator<String, ? extends Table.KeyValue<String, SnapshotInfo>>
+ snapshotIterator = omMetadataManager
+ .getSnapshotInfoTable().iterator()) {
+ snapshotIterator.seek(snapshotBucketKey);
+ if (snapshotIterator.hasNext()) {
+ return snapshotIterator.next().getKey().startsWith(snapshotBucketKey);
+ }
+ }
+ return false;
+ }
+
+ private boolean bucketContainsSnapshotInCache(
+ OMMetadataManager omMetadataManager, String snapshotBucketKey) {
+ Iterator<Map.Entry<CacheKey<String>, CacheValue<SnapshotInfo>>> cacheIter =
+ omMetadataManager.getSnapshotInfoTable().cacheIterator();
+ while (cacheIter.hasNext()) {
+ Map.Entry<CacheKey<String>, CacheValue<SnapshotInfo>> cacheKeyValue =
+ cacheIter.next();
+ String key = cacheKeyValue.getKey().getCacheKey();
+ // TODO: Revisit when delete snapshot gets implemented as entry
+ // in cache/table could be null.
+ if (key.startsWith(snapshotBucketKey)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Validates bucket delete requests.
* Handles the cases where an older client attempts to delete a bucket
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]