diff --git a/hadoop-hdds/common/src/main/resources/ozone-default.xml b/hadoop-hdds/common/src/main/resources/ozone-default.xml
index 5a630afc6aa3..956629244df0 100644
--- a/hadoop-hdds/common/src/main/resources/ozone-default.xml
+++ b/hadoop-hdds/common/src/main/resources/ozone-default.xml
@@ -1791,6 +1791,14 @@
Mostly used for system maintenance.
+
+ ozone.s3g.allow-delete
+ false
+ OZONE, S3GATEWAY
+ Whether the S3Gateway accepts DELETE as an exception in readonly mode.
+ Only effective in readonly mode.
+
+
ozone.om.save.metrics.interval
5m
diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/Gateway.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/Gateway.java
index f4de110a4c27..9d83afddedda 100644
--- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/Gateway.java
+++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/Gateway.java
@@ -42,6 +42,8 @@
import static org.apache.hadoop.ozone.s3.S3GatewayConfigKeys.OZONE_S3G_KERBEROS_KEYTAB_FILE_KEY;
import static org.apache.hadoop.ozone.s3.S3GatewayConfigKeys.OZONE_S3G_KERBEROS_PRINCIPAL_KEY;
import static org.apache.hadoop.ozone.s3.S3GatewayConfigKeys.OZONE_S3G_READONLY;
+import static org.apache.hadoop.ozone.s3.S3GatewayConfigKeys.OZONE_S3G_ALLOW_DELETE;
+
/**
* This class is used to start/stop S3 compatible rest server.
@@ -95,6 +97,8 @@ public void start() throws IOException {
HddsServerUtil.initializeMetrics(ozoneConfiguration, "S3Gateway");
LOG.info("S3 Gateway Readonly mode: {}={}", OZONE_S3G_READONLY,
ozoneConfiguration.get(OZONE_S3G_READONLY));
+ LOG.info("S3 Gateway allow-delete: {}={}", OZONE_S3G_ALLOW_DELETE,
+ ozoneConfiguration.get(OZONE_S3G_ALLOW_DELETE));
httpServer.start();
}
diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/S3GatewayConfigKeys.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/S3GatewayConfigKeys.java
index 56d1162c7cc7..bebce609e7ba 100644
--- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/S3GatewayConfigKeys.java
+++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/S3GatewayConfigKeys.java
@@ -66,6 +66,9 @@ public final class S3GatewayConfigKeys {
public static final String OZONE_S3G_READONLY = "ozone.s3g.readonly";
public static final boolean OZONE_S3G_READONLY_DEFAULT = false;
+
+ public static final String OZONE_S3G_ALLOW_DELETE = "ozone.s3g.allow-delete";
+ public static final boolean OZONE_S3G_ALLOW_DELETE_DEFAULT = false;
/**
* Never constructed.
*/
diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java
index 3f1bc561058d..e6530ad43357 100644
--- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java
+++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java
@@ -246,7 +246,7 @@ public Response put(@PathParam("bucket") String bucketName,
S3GAction s3GAction = S3GAction.CREATE_BUCKET;
// Check if the S3Gateway status is readonly
- Optional checkResult = checkIfReadonly();
+ Optional checkResult = checkIfReadonly(false);
if (checkResult.isPresent()) {
return checkResult.get();
}
@@ -356,7 +356,7 @@ public Response delete(@PathParam("bucket") String bucketName)
S3GAction s3GAction = S3GAction.DELETE_BUCKET;
// Check if the S3Gateway status is readonly
- Optional checkResult = checkIfReadonly();
+ Optional checkResult = checkIfReadonly(true);
if (checkResult.isPresent()) {
return checkResult.get();
}
@@ -407,7 +407,7 @@ public MultiDeleteResponse multiDelete(@PathParam("bucket") String bucketName,
MultiDeleteResponse result = new MultiDeleteResponse();
// Check if the S3Gateway status is readonly
- Optional checkResult = checkIfReadonly();
+ Optional checkResult = checkIfReadonly(false);
if (checkResult.isPresent()) {
Response res = checkResult.get();
result.addError(new Error("", res.getStatusInfo().getReasonPhrase(),
diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/EndpointBase.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/EndpointBase.java
index cfdc357979ec..1377f1294a2a 100644
--- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/EndpointBase.java
+++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/EndpointBase.java
@@ -37,6 +37,8 @@
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.hdds.conf.ConfigurationSource;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.audit.AuditAction;
import org.apache.hadoop.ozone.audit.AuditEventStatus;
@@ -383,14 +385,36 @@ protected boolean isAccessDenied(OMException ex) {
|| result == ResultCodes.INVALID_TOKEN;
}
- protected Optional checkIfReadonly() {
+ /**
+ * Boolean logic combination behaviour
+ * | (READONLY, ALLOW_DELETE) | (F, _) | (T, F) | (T, T) |
+ * +--------------------------+--------+--------+--------+
+ * | PUT,POST | ok(a) | fail(d)| fail(g)|
+ * | GET,HEAD | ok(b) | ok(e) | ok(h) |
+ * | DELETE | ok(c) | fail(f)| ok(i) |
+ *
+ * Maybe we shall rename the method?
+ *
+ * @param isDeletion
+ * @return
+ */
+ protected Optional checkIfReadonly(boolean isDeletion) {
// Check if the S3Gateway is in read-only mode or not.
- if (getClient().getConfiguration().getBoolean(
+ ConfigurationSource conf = getClient().getConfiguration();
+ if (conf.getBoolean(
S3GatewayConfigKeys.OZONE_S3G_READONLY,
S3GatewayConfigKeys.OZONE_S3G_READONLY_DEFAULT)) {
+ if (isDeletion && conf.getBoolean(
+ S3GatewayConfigKeys.OZONE_S3G_ALLOW_DELETE,
+ S3GatewayConfigKeys.OZONE_S3G_ALLOW_DELETE_DEFAULT)) {
+ // case (i)
+ return Optional.empty();
+ }
+ // case (d, e, f, g, h)
return Optional.of(Response.status(HttpStatus.SC_METHOD_NOT_ALLOWED).
- header("Allow", "GET,HEAD").build());
+ header("Allow", "GET,HEAD").build());
}
+ // case (a, b, c)
return Optional.empty();
}
}
diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/ObjectEndpoint.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/ObjectEndpoint.java
index 78b6ac30352d..dfa36a1a1623 100644
--- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/ObjectEndpoint.java
+++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/ObjectEndpoint.java
@@ -177,7 +177,7 @@ public Response put(
InputStream body) throws IOException, OS3Exception {
// Check if the S3Gateway status is readonly
- Optional checkResult = checkIfReadonly();
+ Optional checkResult = checkIfReadonly(false);
if (checkResult.isPresent()) {
return checkResult.get();
}
@@ -535,7 +535,7 @@ public Response delete(
IOException, OS3Exception {
// Check if the S3Gateway status is readonly
- Optional checkResult = checkIfReadonly();
+ Optional checkResult = checkIfReadonly(true);
if (checkResult.isPresent()) {
return checkResult.get();
}
@@ -606,7 +606,7 @@ public Response initializeMultipartUpload(
throws IOException, OS3Exception {
// Check if the S3Gateway status is readonly
- Optional checkResult = checkIfReadonly();
+ Optional checkResult = checkIfReadonly(false);
if (checkResult.isPresent()) {
return checkResult.get();
}
@@ -680,7 +680,7 @@ public Response completeMultipartUpload(@PathParam("bucket") String bucket,
throws IOException, OS3Exception {
// Check if the S3Gateway status is readonly
- Optional checkResult = checkIfReadonly();
+ Optional checkResult = checkIfReadonly(false);
if (checkResult.isPresent()) {
return checkResult.get();
}