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]

Reply via email to