This is an automated email from the ASF dual-hosted git repository.
chungen0126 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new c99d291e863 HDDS-16657. Reject lifecycle Transition actions in
PutBucketLifecycleConfiguration (#11430)
c99d291e863 is described below
commit c99d291e8636d2da0fbee4067bc51c1242d86759
Author: Priyesh Karatha <[email protected]>
AuthorDate: Wed Oct 7 22:31:28 2026 +0530
HDDS-16657. Reject lifecycle Transition actions in
PutBucketLifecycleConfiguration (#11430)
---
.../s3/endpoint/S3LifecycleConfiguration.java | 63 ++++++++++++++++++++++
.../endpoint/TestS3LifecycleConfigurationPut.java | 27 ++++++++++
2 files changed, 90 insertions(+)
diff --git
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/S3LifecycleConfiguration.java
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/S3LifecycleConfiguration.java
index 2327f9b759c..f7b628f23a6 100644
---
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/S3LifecycleConfiguration.java
+++
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/S3LifecycleConfiguration.java
@@ -83,6 +83,11 @@ public static class Rule {
@XmlElement(name = "Filter")
private Filter filter;
+ // Ozone does not support lifecycle Transition actions. This is bound only
so a Transition
+ // element is detected and rejected instead of being silently dropped by
JAXB.
+ @XmlElement(name = "Transition")
+ private List<Transition> transitions;
+
public String getId() {
return id;
}
@@ -130,6 +135,14 @@ public Filter getFilter() {
public void setFilter(Filter filter) {
this.filter = filter;
}
+
+ public List<Transition> getTransitions() {
+ return transitions;
+ }
+
+ public void setTransitions(List<Transition> transitions) {
+ this.transitions = transitions;
+ }
}
/**
@@ -181,6 +194,48 @@ public void setDaysAfterInitiation(Integer
daysAfterInitiation) {
}
}
+ /**
+ * Transition entity for lifecycle rule. Ozone does not support lifecycle
transition actions
+ * for any storage class; this is modeled only so a Transition element in
the request is
+ * detected and rejected during conversion instead of being silently ignored.
+ */
+ @XmlAccessorType(XmlAccessType.FIELD)
+ @XmlRootElement(name = "Transition")
+ public static class Transition {
+ @XmlElement(name = "Days")
+ private String days;
+
+ @XmlElement(name = "Date")
+ private String date;
+
+ @XmlElement(name = "StorageClass")
+ private String storageClass;
+
+ public Integer getDays() {
+ return parseDays(days);
+ }
+
+ public void setDays(Integer days) {
+ this.days = days == null ? null : days.toString();
+ }
+
+ public String getDate() {
+ return date;
+ }
+
+ public void setDate(String date) {
+ this.date = date;
+ }
+
+ public String getStorageClass() {
+ return storageClass;
+ }
+
+ public void setStorageClass(String storageClass) {
+ this.storageClass = storageClass;
+ }
+ }
+
/**
* Parses a lifecycle day-count element, rejecting a value that does not fit
in an int instead
* of silently overflowing it the way JAXB's built-in Integer converter
would.
@@ -343,6 +398,14 @@ private OmLCRule convertToOmRule(Rule rule) throws
OMException, OS3Exception {
if (!STATUS_ENABLED.equals(status) && !STATUS_DISABLED.equals(status)) {
throw S3ErrorTable.newError(S3ErrorTable.MALFORMED_XML);
}
+ if (rule.getTransitions() != null && !rule.getTransitions().isEmpty()) {
+ String storageClass = rule.getTransitions().get(0).getStorageClass();
+ String message = StringUtils.isNotEmpty(storageClass)
+ ? "Invalid lifecycle configuration: storage class '" + storageClass
+ + "' is not supported for lifecycle transitions"
+ : "Invalid lifecycle configuration: Transition actions are not
supported";
+ throw S3ErrorTable.newError(S3ErrorTable.NOT_IMPLEMENTED,
rule.getId()).withMessage(message);
+ }
OmLCRule.Builder builder = new OmLCRule.Builder()
.setEnabled(STATUS_ENABLED.equals(status))
diff --git
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestS3LifecycleConfigurationPut.java
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestS3LifecycleConfigurationPut.java
index f38575bf925..3b735184d25 100644
---
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestS3LifecycleConfigurationPut.java
+++
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestS3LifecycleConfigurationPut.java
@@ -21,6 +21,7 @@
import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
+import static java.net.HttpURLConnection.HTTP_NOT_IMPLEMENTED;
import static java.net.HttpURLConnection.HTTP_OK;
import static
org.apache.hadoop.ozone.s3.endpoint.EndpointTestUtils.assertErrorResponse;
import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.ACCESS_DENIED;
@@ -28,6 +29,7 @@
import static
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.INVALID_ARGUMENT;
import static
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.INVALID_REQUEST;
import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.MALFORMED_XML;
+import static
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.NOT_IMPLEMENTED;
import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.NO_SUCH_BUCKET;
import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.QUOTA_EXCEEDED;
import static
org.apache.hadoop.ozone.s3.util.S3Consts.EXPECTED_BUCKET_OWNER_HEADER;
@@ -350,6 +352,17 @@ public void
testPutLifecycleConfigurationWithAbortDaysAfterInitiationOverflow()
HTTP_BAD_REQUEST, INVALID_ARGUMENT.getCode());
}
+ @Test
+ public void testPutLifecycleConfigurationRejectsTransition() throws
Exception {
+ // Ozone does not support lifecycle Transition actions. A rule containing
one must be
+ // rejected instead of returning HTTP 200 for a rule Ozone can never
enforce.
+ OS3Exception ex = assertThrows(OS3Exception.class,
+ () -> bucketEndpoint.put("bucket1", withTransition()));
+ assertEquals(HTTP_NOT_IMPLEMENTED, ex.getHttpCode());
+ assertEquals(NOT_IMPLEMENTED.getCode(), ex.getCode());
+ assertTrue(ex.getErrorMessage().contains("EC"));
+ }
+
private static InputStream onePrefix() {
String xml = ("<LifecycleConfiguration xmlns=\"http://s3.amazonaws" +
".com/doc/2006-03-01/\">" +
@@ -817,6 +830,20 @@ private static InputStream withExpirationDaysOverflow() {
return new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
}
+ private static InputStream withTransition() {
+ String xml = "<LifecycleConfiguration
xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">" +
+ "<Rule>" +
+ "<ID>unsupported-transition</ID>" +
+ "<Prefix></Prefix>" +
+ "<Status>Enabled</Status>" +
+ "<Expiration><Date>2044-01-19T00:00:00Z</Date></Expiration>" +
+
"<Transition><Days>1</Days><StorageClass>EC</StorageClass></Transition>" +
+ "</Rule>" +
+ "</LifecycleConfiguration>";
+
+ return new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
+ }
+
private static InputStream withAbortDaysAfterInitiationOverflow() {
String xml = "<LifecycleConfiguration
xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">" +
"<Rule>" +
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]