diff --git a/hadoop-hdds/common/src/main/resources/ozone-default.xml b/hadoop-hdds/common/src/main/resources/ozone-default.xml index 5a630afc6aa..956629244df 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 f4de110a4c2..9d83afddedd 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 56d1162c7cc..bebce609e7b 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 3f1bc561058..e6530ad4335 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 cfdc357979e..1377f1294a2 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 78b6ac30352..dfa36a1a162 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(); }