This is an automated email from the ASF dual-hosted git repository.

sammichen pushed a commit to branch HDDS-8342
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/HDDS-8342 by this push:
     new 1fe66180880 HDDS-13548. Prevent OM startup failure from past lifecycle 
expiration dates (#8953)
1fe66180880 is described below

commit 1fe661808805f2acf33cda6025148b3281cf3fe3
Author: XiChen <32928346+xiche...@users.noreply.github.com>
AuthorDate: Wed Sep 3 12:55:05 2025 +0800

    HDDS-13548. Prevent OM startup failure from past lifecycle expiration dates 
(#8953)
---
 .../apache/hadoop/ozone/om/helpers/OmLCAction.java |  3 +-
 .../hadoop/ozone/om/helpers/OmLCExpiration.java    | 21 ++++----
 .../apache/hadoop/ozone/om/helpers/OmLCFilter.java | 10 +---
 .../apache/hadoop/ozone/om/helpers/OmLCRule.java   | 18 +++----
 .../ozone/om/helpers/OmLifecycleConfiguration.java | 16 +++---
 .../om/helpers/OmLifecycleRuleAndOperator.java     |  9 +---
 .../ozone/om/helpers/TestOmLCExpiration.java       | 63 +++++++++++-----------
 .../hadoop/ozone/om/helpers/TestOmLCFilter.java    | 15 +++---
 .../hadoop/ozone/om/helpers/TestOmLCRule.java      | 22 +++++---
 .../om/helpers/TestOmLifeCycleConfiguration.java   | 48 +++++++++++++++--
 .../om/helpers/TestOmLifecycleRuleAndOperator.java |  8 +--
 .../src/main/proto/OmClientProtocol.proto          |  2 +-
 .../apache/hadoop/ozone/om/OMMetadataManager.java  |  3 +-
 .../hadoop/ozone/om/OmMetadataManagerImpl.java     | 13 ++---
 .../OMLifecycleConfigurationSetRequest.java        |  3 +-
 .../ozone/om/service/KeyLifecycleService.java      | 17 +++++-
 .../TestOMLifecycleConfigurationRequest.java       |  1 +
 .../ozone/om/service/TestKeyLifecycleService.java  |  2 +-
 .../hadoop/ozone/s3/endpoint/BucketEndpoint.java   |  4 +-
 .../s3/endpoint/S3LifecycleConfiguration.java      |  2 +-
 20 files changed, 168 insertions(+), 112 deletions(-)

diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLCAction.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLCAction.java
index 427bf87908b..4db13e20743 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLCAction.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLCAction.java
@@ -36,9 +36,10 @@ public interface OmLCAction {
    * Validates the action configuration.
    * Each concrete action implementation must define its own validation logic.
    *
+   * @param creationTime The creation time of the lifecycle configuration in 
milliseconds since epoch
    * @throws OMException if the validation fails
    */
-  void valid() throws OMException;
+  void valid(long creationTime) throws OMException;
 
   /**
    * Returns the action type.
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLCExpiration.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLCExpiration.java
index bb15b447691..b5e78eb6241 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLCExpiration.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLCExpiration.java
@@ -87,10 +87,11 @@ public ActionType getActionType() {
    * - The date value must be in the future
    * - The date value must be at midnight UTC (00:00:00Z)
    *
+   * @param creationTime The creation time of the lifecycle configuration in 
milliseconds since epoch
    * @throws OMException if the validation fails
    */
   @Override
-  public void valid() throws OMException {
+  public void valid(long creationTime) throws OMException {
     boolean hasDays = days != null;
     boolean hasDate = !StringUtils.isBlank(date);
 
@@ -106,7 +107,7 @@ public void valid() throws OMException {
       daysInMilli = TimeUnit.DAYS.toMillis(days);
     }
     if (hasDate) {
-      validateExpirationDate(date);
+      validateExpirationDate(date, creationTime);
     }
   }
 
@@ -118,19 +119,21 @@ public void valid() throws OMException {
    * - Represents midnight UTC (00:00:00Z) when converted to UTC.
    *
    * @param expirationDate The date string to validate
+   * @param creationTime The creation time to compare against in milliseconds 
since epoch
    * @throws OMException if the date is invalid
    */
-  private void validateExpirationDate(String expirationDate) throws 
OMException {
+  private void validateExpirationDate(String expirationDate, long 
creationTime) throws OMException {
     try {
       ZonedDateTime parsedDate = ZonedDateTime.parse(expirationDate, 
DateTimeFormatter.ISO_DATE_TIME);
-      ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
       // Convert to UTC for validation
       ZonedDateTime dateInUTC = parsedDate.withZoneSameInstant(ZoneOffset.UTC);
       // The date value must conform to the ISO 8601 format, be in the future.
-      if (dateInUTC.isBefore(now)) {
-        throw new OMException("Invalid lifecycle configuration: 'Date' must be 
in the future " + now + "," + dateInUTC,
-            OMException.ResultCodes.INVALID_REQUEST);
+      ZonedDateTime createDate = 
ZonedDateTime.ofInstant(Instant.ofEpochMilli(creationTime), ZoneOffset.UTC);
+      if (dateInUTC.isBefore(createDate)) {
+        throw new OMException("Invalid lifecycle configuration: 'Date' must be 
in the future " + createDate + "," +
+            dateInUTC, OMException.ResultCodes.INVALID_REQUEST);
       }
+
       // Verify that the time is midnight UTC (00:00:00Z)
       if (!test && (dateInUTC.getHour() != 0 ||
           dateInUTC.getMinute() != 0 ||
@@ -202,9 +205,7 @@ public Builder setDate(String lcDate) {
     }
 
     public OmLCExpiration build() throws OMException {
-      OmLCExpiration omLCExpiration = new OmLCExpiration(this);
-      omLCExpiration.valid();
-      return omLCExpiration;
+      return new OmLCExpiration(this);
     }
   }
 
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLCFilter.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLCFilter.java
index d6da6f45cd9..ff6f36b2cc5 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLCFilter.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLCFilter.java
@@ -205,7 +205,6 @@ public static class Builder {
     private String tagKey = null;
     private String tagValue = null;
     private OmLifecycleRuleAndOperator andOperator = null;
-    private BucketLayout bucketLayout;
 
     public Builder setPrefix(String lcPrefix) {
       this.prefix = lcPrefix;
@@ -223,15 +222,8 @@ public Builder setAndOperator(OmLifecycleRuleAndOperator 
andOp) {
       return this;
     }
 
-    public Builder setBucketLayout(BucketLayout layout) {
-      this.bucketLayout = layout;
-      return this;
-    }
-
     public OmLCFilter build() throws OMException {
-      OmLCFilter omLCFilter = new OmLCFilter(this);
-      omLCFilter.valid(bucketLayout);
-      return omLCFilter;
+      return new OmLCFilter(this);
     }
   }
 }
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLCRule.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLCRule.java
index b8a63865513..f22eec1beee 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLCRule.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLCRule.java
@@ -152,9 +152,11 @@ public boolean isTagEnable() {
    * - Filter must be valid
    * - There must be at most one Expiration action per rule
    *
+   * @param bucketLayout The bucket layout for validation
+   * @param creationTime The creation time of the lifecycle configuration in 
milliseconds since epoch
    * @throws OMException if the validation fails
    */
-  public void valid(BucketLayout bucketLayout) throws OMException {
+  public void valid(BucketLayout bucketLayout, Long creationTime) throws 
OMException {
     if (id.length() > LC_ID_MAX_LENGTH) {
       throw new OMException("ID length should not exceed allowed limit of " + 
LC_ID_MAX_LENGTH,
           OMException.ResultCodes.INVALID_REQUEST);
@@ -175,7 +177,7 @@ public void valid(BucketLayout bucketLayout) throws 
OMException {
         throw new OMException("A rule can have at most one Expiration action.",
             OMException.ResultCodes.INVALID_REQUEST);
       }
-      action.valid();
+      action.valid(creationTime);
     }
 
     if (prefix != null && filter != null) {
@@ -304,7 +306,7 @@ public static OmLCRule getFromProtobuf(LifecycleRule 
lifecycleRule, BucketLayout
       builder.setFilter(OmLCFilter.getFromProtobuf(lifecycleRule.getFilter(), 
layout));
     }
 
-    return builder.setBucketLayout(layout).build();
+    return builder.build();
   }
 
   @Override
@@ -329,7 +331,6 @@ public static class Builder {
     private boolean enabled;
     private List<OmLCAction> actions = new ArrayList<>();
     private OmLCFilter filter;
-    private BucketLayout bucketLayout;
 
     public Builder setId(String lcId) {
       this.id = lcId;
@@ -378,15 +379,8 @@ public OmLCFilter getFilter() {
       return filter;
     }
 
-    public Builder setBucketLayout(BucketLayout layout) {
-      this.bucketLayout = layout;
-      return this;
-    }
-
     public OmLCRule build() throws OMException {
-      OmLCRule omLCRule = new OmLCRule(this);
-      omLCRule.valid(bucketLayout);
-      return omLCRule;
+      return new OmLCRule(this);
     }
   }
 }
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLifecycleConfiguration.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLifecycleConfiguration.java
index d711e3cb937..ad6d31d7cf1 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLifecycleConfiguration.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLifecycleConfiguration.java
@@ -130,7 +130,7 @@ public void valid() throws OMException {
     }
 
     for (OmLCRule rule : rules) {
-      rule.valid(bucketLayout);
+      rule.valid(bucketLayout, creationTime);
     }
   }
 
@@ -213,9 +213,7 @@ public static OmLifecycleConfiguration getFromProtobuf(
         .setBucketLayout(layout)
         .setRules(rulesList);
 
-    if (lifecycleConfiguration.hasCreationTime()) {
-      builder.setCreationTime(lifecycleConfiguration.getCreationTime());
-    }
+    builder.setCreationTime(lifecycleConfiguration.getCreationTime());
     if (lifecycleConfiguration.hasObjectID()) {
       builder.setObjectID(lifecycleConfiguration.getObjectID());
     }
@@ -258,8 +256,8 @@ public Builder setBucketLayout(BucketLayout layout) {
       return this;
     }
 
-    public Builder setCreationTime(long ctime) {
-      this.creationTime = ctime;
+    public Builder setCreationTime(long creationTime) {
+      this.creationTime = creationTime;
       return this;
     }
 
@@ -286,7 +284,11 @@ public Builder setUpdateID(long uID) {
     }
 
     public OmLifecycleConfiguration build() throws OMException {
-      OmLifecycleConfiguration omLifecycleConfiguration = new 
OmLifecycleConfiguration(this);
+      return new OmLifecycleConfiguration(this);
+    }
+
+    public OmLifecycleConfiguration buildAndValid() throws OMException {
+      OmLifecycleConfiguration omLifecycleConfiguration = build();
       omLifecycleConfiguration.valid();
       return omLifecycleConfiguration;
     }
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLifecycleRuleAndOperator.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLifecycleRuleAndOperator.java
index d28eaea1418..522fc6ca930 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLifecycleRuleAndOperator.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmLifecycleRuleAndOperator.java
@@ -138,7 +138,6 @@ public boolean match(OmKeyInfo omKeyInfo, String keyPath) {
   public static class Builder {
     private Map<String, String> tags = new HashMap<>();
     private String prefix;
-    private BucketLayout bucketLayout;
 
     public Builder setPrefix(String lcPrefix) {
       this.prefix = lcPrefix;
@@ -156,15 +155,9 @@ public Builder setTags(Map<String, String> lcTags) {
       }
       return this;
     }
-    public Builder setBucketLayout(BucketLayout layout) {
-      this.bucketLayout = layout;
-      return this;
-    }
 
     public OmLifecycleRuleAndOperator build() throws OMException {
-      OmLifecycleRuleAndOperator omLifecycleRuleAndOperator = new 
OmLifecycleRuleAndOperator(this);
-      omLifecycleRuleAndOperator.valid(bucketLayout);
-      return omLifecycleRuleAndOperator;
+      return new OmLifecycleRuleAndOperator(this);
     }
   }
 
diff --git 
a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLCExpiration.java
 
b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLCExpiration.java
index a2fe3951cb0..1aa3e2b5011 100644
--- 
a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLCExpiration.java
+++ 
b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLCExpiration.java
@@ -37,51 +37,52 @@ class TestOmLCExpiration {
   public void testCreateValidOmLCExpiration() {
     OmLCExpiration.Builder exp1 = new OmLCExpiration.Builder()
         .setDays(30);
-    assertDoesNotThrow(exp1::build);
+    long currentTime = System.currentTimeMillis();
+    assertDoesNotThrow(() -> exp1.build().valid(currentTime));
 
     OmLCExpiration.Builder exp2 = new OmLCExpiration.Builder()
         .setDate("2099-10-10T00:00:00Z");
-    assertDoesNotThrow(exp2::build);
+    assertDoesNotThrow(() -> exp2.build().valid(currentTime));
 
     OmLCExpiration.Builder exp3 = new OmLCExpiration.Builder()
         .setDays(1);
-    assertDoesNotThrow(exp3::build);
+    assertDoesNotThrow(() -> exp3.build().valid(currentTime));
 
     OmLCExpiration.Builder exp4 = new OmLCExpiration.Builder()
         .setDate("2099-12-31T00:00:00Z");
-    assertDoesNotThrow(exp4::build);
+    assertDoesNotThrow(() -> exp4.build().valid(currentTime));
 
     OmLCExpiration.Builder exp5 = new OmLCExpiration.Builder()
         .setDate("2099-02-15T00:00:00.000Z");
-    assertDoesNotThrow(exp5::build);
+    assertDoesNotThrow(() -> exp5.build().valid(currentTime));
 
     OmLCExpiration.Builder exp6 = new OmLCExpiration.Builder()
         .setDate("2042-04-02T00:00:00Z");
-    assertDoesNotThrow(exp6::build);
+    assertDoesNotThrow(() -> exp6.build().valid(currentTime));
 
     OmLCExpiration.Builder exp7 = new OmLCExpiration.Builder()
         .setDate("2042-04-02T00:00:00+00:00");
-    assertDoesNotThrow(exp7::build);
+    assertDoesNotThrow(() -> exp7.build().valid(currentTime));
 
     OmLCExpiration.Builder exp8 = new OmLCExpiration.Builder()
         .setDate("2099-12-31T00:00:00+00:00");
-    assertDoesNotThrow(exp8::build);
+    assertDoesNotThrow(() -> exp8.build().valid(currentTime));
 
     OmLCExpiration.Builder exp9 = new OmLCExpiration.Builder()
         .setDate("2099-12-31T23:00:00-01:00");
-    assertDoesNotThrow(exp9::build);
+    assertDoesNotThrow(() -> exp9.build().valid(currentTime));
 
     OmLCExpiration.Builder exp10 = new OmLCExpiration.Builder()
         .setDate("2100-01-01T01:00:00+01:00");
-    assertDoesNotThrow(exp10::build);
+    assertDoesNotThrow(() -> exp10.build().valid(currentTime));
 
     OmLCExpiration.Builder exp11 = new OmLCExpiration.Builder()
         .setDate("2099-12-31T12:00:00-12:00");
-    assertDoesNotThrow(exp11::build);
+    assertDoesNotThrow(() -> exp11.build().valid(currentTime));
 
     OmLCExpiration.Builder exp12 = new OmLCExpiration.Builder()
         .setDate("2100-01-01T12:00:00+12:00");
-    assertDoesNotThrow(exp12::build);
+    assertDoesNotThrow(() -> exp12.build().valid(currentTime));
   }
 
   @Test
@@ -89,84 +90,86 @@ public void testCreateInValidOmLCExpiration() {
     OmLCExpiration.Builder exp1 = new OmLCExpiration.Builder()
         .setDays(30)
         .setDate(getFutureDateString(100));
-    assertOMException(exp1::build, INVALID_REQUEST,
+    long currentTime = System.currentTimeMillis();
+    assertOMException(() -> exp1.build().valid(currentTime), INVALID_REQUEST,
         "Either 'days' or 'date' should be specified, but not both or 
neither.");
 
     OmLCExpiration.Builder exp2 = new OmLCExpiration.Builder()
         .setDays(-1);
-    assertOMException(exp2::build, INVALID_REQUEST,
+    assertOMException(() -> exp2.build().valid(currentTime), INVALID_REQUEST,
         "'Days' for Expiration action must be a positive integer");
 
     OmLCExpiration.Builder exp3 = new OmLCExpiration.Builder()
         .setDate(null);
-    assertOMException(exp3::build, INVALID_REQUEST,
+    assertOMException(() -> exp3.build().valid(currentTime), INVALID_REQUEST,
         "Either 'days' or 'date' should be specified, but not both or 
neither.");
 
     OmLCExpiration.Builder exp4 = new OmLCExpiration.Builder()
         .setDate("");
-    assertOMException(exp4::build, INVALID_REQUEST,
+    assertOMException(() -> exp4.build().valid(currentTime), INVALID_REQUEST,
         "Either 'days' or 'date' should be specified, but not both or 
neither.");
 
     OmLCExpiration.Builder exp5 = new OmLCExpiration.Builder();
-    assertOMException(exp5::build, INVALID_REQUEST,
+    assertOMException(() -> exp5.build().valid(currentTime), INVALID_REQUEST,
         "Either 'days' or 'date' should be specified, but not both or 
neither.");
 
     OmLCExpiration.Builder exp6 = new OmLCExpiration.Builder()
         .setDate("10-10-2099");
-    assertOMException(exp6::build, INVALID_REQUEST,
+    assertOMException(() -> exp6.build().valid(currentTime), INVALID_REQUEST,
         "'Date' must be in ISO 8601 format");
 
     OmLCExpiration.Builder exp7 = new OmLCExpiration.Builder()
         .setDate("2099-12-31T00:00:00");
-    assertOMException(exp7::build, INVALID_REQUEST,
+    assertOMException(() -> exp7.build().valid(currentTime), INVALID_REQUEST,
         "'Date' must be in ISO 8601 format");
 
-    // Testing for date in the past
+    // Testing for date in the past with creation time
     OmLCExpiration.Builder exp8 = new OmLCExpiration.Builder()
         .setDate(getFutureDateString(-1));
-    assertOMException(exp8::build, INVALID_REQUEST,
+    assertOMException(() -> exp8.build().valid(currentTime), INVALID_REQUEST,
         "'Date' must be in the future");
 
     OmLCExpiration.Builder exp9 = new OmLCExpiration.Builder()
         .setDays(0);
-    assertOMException(exp9::build, INVALID_REQUEST,
+    assertOMException(() -> exp9.build().valid(currentTime), INVALID_REQUEST,
         "'Days' for Expiration action must be a positive integer");
 
-    // 1 minute ago
+    // 1 minute ago with creation time
     OmLCExpiration.Builder exp10 = new OmLCExpiration.Builder()
         .setDate(getFutureDateString(0, 0, -1));
-    assertOMException(exp10::build, INVALID_REQUEST,
+    assertOMException(() -> exp10.build().valid(currentTime), INVALID_REQUEST,
         "'Date' must be in the future");
   }
 
   @Test
   public void testDateMustBeAtMidnightUTC() {
     // Acceptable date - midnight UTC
+    long currentTime = System.currentTimeMillis();
     OmLCExpiration.Builder validExp = new OmLCExpiration.Builder()
         .setDate("2099-10-10T00:00:00Z");
-    assertDoesNotThrow(validExp::build);
+    assertDoesNotThrow(() -> validExp.build().valid(currentTime));
 
     // Non-midnight UTC dates should be rejected
     OmLCExpiration.Builder exp1 = new OmLCExpiration.Builder()
         .setDate("2099-10-10T10:00:00Z");
-    assertOMException(exp1::build, INVALID_REQUEST, "'Date' must represent 
midnight UTC");
+    assertOMException(() -> exp1.build().valid(currentTime), INVALID_REQUEST, 
"'Date' must represent midnight UTC");
 
     OmLCExpiration.Builder exp2 = new OmLCExpiration.Builder()
         .setDate("2099-10-10T00:30:00Z");
-    assertOMException(exp2::build, INVALID_REQUEST, "'Date' must represent 
midnight UTC");
+    assertOMException(() -> exp2.build().valid(currentTime), INVALID_REQUEST, 
"'Date' must represent midnight UTC");
 
     OmLCExpiration.Builder exp3 = new OmLCExpiration.Builder()
         .setDate("2099-10-10T00:00:30Z");
-    assertOMException(exp3::build, INVALID_REQUEST, "'Date' must represent 
midnight UTC");
+    assertOMException(() -> exp3.build().valid(currentTime), INVALID_REQUEST, 
"'Date' must represent midnight UTC");
 
     OmLCExpiration.Builder exp4 = new OmLCExpiration.Builder()
         .setDate("2099-10-10T00:00:00.123Z");
-    assertOMException(exp4::build, INVALID_REQUEST, "'Date' must represent 
midnight UTC");
+    assertOMException(() -> exp4.build().valid(currentTime), INVALID_REQUEST, 
"'Date' must represent midnight UTC");
 
     // Non-UTC timezone should be rejected
     OmLCExpiration.Builder exp5 = new OmLCExpiration.Builder()
         .setDate("2099-10-10T00:00:00+01:00");
-    assertOMException(exp5::build, INVALID_REQUEST, "'Date' must represent 
midnight UTC");
+    assertOMException(() -> exp5.build().valid(currentTime), INVALID_REQUEST, 
"'Date' must represent midnight UTC");
   }
 
   @Test
diff --git 
a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLCFilter.java
 
b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLCFilter.java
index d073dad71d8..bd3809ba10a 100644
--- 
a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLCFilter.java
+++ 
b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLCFilter.java
@@ -42,11 +42,14 @@ class TestOmLCFilter {
 
   @Test
   public void testInValidOmLCRulePrefixFilterCoExist() throws OMException {
+    long currentTime = System.currentTimeMillis();
     OmLCRule.Builder rule1 = getOmLCRuleBuilder("id", "prefix", true, 1, 
VALID_OM_LC_FILTER);
-    assertOMException(rule1::build, INVALID_REQUEST, "Filter and Prefix cannot 
be used together");
+    assertOMException(() -> rule1.build().valid(BucketLayout.DEFAULT, 
currentTime), INVALID_REQUEST,
+        "Filter and Prefix cannot be used together");
 
     OmLCRule.Builder rule2 = getOmLCRuleBuilder("id", "", true, 1, 
VALID_OM_LC_FILTER);
-    assertOMException(rule2::build, INVALID_REQUEST, "Filter and Prefix cannot 
be used together");
+    assertOMException(() -> rule2.build().valid(BucketLayout.DEFAULT, 
currentTime), INVALID_REQUEST,
+        "Filter and Prefix cannot be used together");
   }
 
   @Test
@@ -70,19 +73,19 @@ public void testValidFilter() throws OMException {
   @Test
   public void testInValidFilter() {
     OmLCFilter.Builder lcFilter1 = getOmLCFilterBuilder("prefix", 
Pair.of("key", "value"), VALID_OM_LC_AND_OPERATOR);
-    assertOMException(lcFilter1::build, INVALID_REQUEST,
+    assertOMException(() -> lcFilter1.build().valid(BucketLayout.DEFAULT), 
INVALID_REQUEST,
         "Only one of 'Prefix', 'Tag', or 'AndOperator' should be specified");
 
     OmLCFilter.Builder lcFilter2 = getOmLCFilterBuilder("prefix", 
Pair.of("key", "value"), null);
-    assertOMException(lcFilter2::build, INVALID_REQUEST,
+    assertOMException(() -> lcFilter2.build().valid(BucketLayout.DEFAULT), 
INVALID_REQUEST,
         "Only one of 'Prefix', 'Tag', or 'AndOperator' should be specified");
 
     OmLCFilter.Builder lcFilter3 = getOmLCFilterBuilder("prefix", null, 
VALID_OM_LC_AND_OPERATOR);
-    assertOMException(lcFilter3::build, INVALID_REQUEST,
+    assertOMException(() -> lcFilter3.build().valid(BucketLayout.DEFAULT), 
INVALID_REQUEST,
         "Only one of 'Prefix', 'Tag', or 'AndOperator' should be specified");
 
     OmLCFilter.Builder lcFilter4 = getOmLCFilterBuilder(null, Pair.of("key", 
"value"), VALID_OM_LC_AND_OPERATOR);
-    assertOMException(lcFilter4::build, INVALID_REQUEST,
+    assertOMException(() -> lcFilter4.build().valid(BucketLayout.DEFAULT), 
INVALID_REQUEST,
         "Only one of 'Prefix', 'Tag', or 'AndOperator' should be specified");
 
   }
diff --git 
a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLCRule.java
 
b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLCRule.java
index 4e4736c257e..0a17af7b3e7 100644
--- 
a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLCRule.java
+++ 
b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLCRule.java
@@ -47,6 +47,7 @@ class TestOmLCRule {
 
   @Test
   public void testCreateValidOmLCRule() throws OMException {
+    long currentTime = System.currentTimeMillis();
     OmLCExpiration exp = new OmLCExpiration.Builder()
         .setDays(30)
         .build();
@@ -56,13 +57,14 @@ public void testCreateValidOmLCRule() throws OMException {
         .setEnabled(true)
         .setPrefix("/spark/logs")
         .setAction(exp);
-    assertDoesNotThrow(r1::build);
+    assertDoesNotThrow(() -> r1.build().valid(BucketLayout.DEFAULT, 
currentTime));
 
     OmLCRule.Builder r2 = new OmLCRule.Builder()
         .setEnabled(true)
         .setPrefix("")
         .setAction(exp);
     OmLCRule omLCRule = assertDoesNotThrow(r2::build);
+    assertDoesNotThrow(() -> omLCRule.valid(BucketLayout.DEFAULT, 
currentTime));
 
     // Empty id should generate a 48 (default) bit one.
     assertEquals(OmLCRule.LC_ID_LENGTH, omLCRule.getId().length(),
@@ -71,6 +73,7 @@ public void testCreateValidOmLCRule() throws OMException {
 
   @Test
   public void testCreateInValidOmLCRule() throws OMException {
+    long currentTime = System.currentTimeMillis();
     OmLCExpiration exp = new OmLCExpiration.Builder()
         .setDays(30)
         .build();
@@ -81,26 +84,28 @@ public void testCreateInValidOmLCRule() throws OMException {
     OmLCRule.Builder r1 = new OmLCRule.Builder()
         .setId(new String(id))
         .setAction(exp);
-    assertOMException(r1::build, INVALID_REQUEST, "ID length should not exceed 
allowed limit of 255");
+    assertOMException(() -> r1.build().valid(BucketLayout.DEFAULT, 
currentTime), INVALID_REQUEST,
+        "ID length should not exceed allowed limit of 255");
 
     OmLCRule.Builder r2 = new OmLCRule.Builder()
         .setId("remove Spark logs after 30 days")
         .setEnabled(true)
         .setPrefix("/spark/logs")
         .setAction(null);
-    assertOMException(r2::build, INVALID_REQUEST,
+    assertOMException(() -> r2.build().valid(BucketLayout.DEFAULT, 
currentTime), INVALID_REQUEST,
         "At least one action needs to be specified in a rule");
 
     OmLCRule.Builder r3 = new OmLCRule.Builder()
         .setEnabled(true)
         .setAction(exp);
 
-    assertOMException(r3::build, INVALID_REQUEST,
+    assertOMException(() -> r3.build().valid(BucketLayout.DEFAULT, 
currentTime), INVALID_REQUEST,
         "Filter and Prefix cannot both be null.");
   }
 
   @Test
   public void testMultipleActionsInRule() throws OMException {
+    long currentTime = System.currentTimeMillis();
     OmLCExpiration expiration1 = new OmLCExpiration.Builder()
         .setDays(30)
         .build();
@@ -118,11 +123,13 @@ public void testMultipleActionsInRule() throws 
OMException {
 
     OmLCRule.Builder rule = builder.setActions(actions);
 
-    assertOMException(rule::build, INVALID_REQUEST, "A rule can have at most 
one Expiration action");
+    assertOMException(() -> rule.build().valid(BucketLayout.DEFAULT, 
currentTime), INVALID_REQUEST,
+        "A rule can have at most one Expiration action");
   }
 
   @Test
   public void testRuleWithAndOperatorFilter() throws OMException {
+    long currentTime = System.currentTimeMillis();
     Map<String, String> tags = ImmutableMap.of("app", "hadoop", "env", "test");
     OmLifecycleRuleAndOperator andOperator = 
getOmLCAndOperatorBuilder("/logs/", tags).build();
     OmLCFilter filter = getOmLCFilterBuilder(null, null, andOperator).build();
@@ -134,12 +141,14 @@ public void testRuleWithAndOperatorFilter() throws 
OMException {
         .setAction(new OmLCExpiration.Builder().setDays(30).build());
 
     OmLCRule rule = assertDoesNotThrow(builder::build);
+    assertDoesNotThrow(() -> rule.valid(BucketLayout.DEFAULT, currentTime));
     assertTrue(rule.isPrefixEnable());
     assertTrue(rule.isTagEnable());
   }
 
   @Test
   public void testRuleWithTagFilter() throws OMException {
+    long currentTime = System.currentTimeMillis();
     OmLCFilter filter = getOmLCFilterBuilder(null, Pair.of("app", "hadoop"), 
null).build();
 
     OmLCRule.Builder builder = new OmLCRule.Builder()
@@ -149,6 +158,7 @@ public void testRuleWithTagFilter() throws OMException {
         .setAction(new OmLCExpiration.Builder().setDays(30).build());
 
     OmLCRule rule = assertDoesNotThrow(builder::build);
+    assertDoesNotThrow(() -> rule.valid(BucketLayout.DEFAULT, currentTime));
     assertFalse(rule.isPrefixEnable());
     assertTrue(rule.isTagEnable());
   }
@@ -174,7 +184,7 @@ public void testDuplicateRuleIDs() throws OMException {
         .setBucket("bucket")
         .setRules(rules);
 
-    assertOMException(config::build, INVALID_REQUEST, "Duplicate rule IDs 
found");
+    assertOMException(() -> config.build().valid(), INVALID_REQUEST, 
"Duplicate rule IDs found");
   }
 
   @Test
diff --git 
a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLifeCycleConfiguration.java
 
b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLifeCycleConfiguration.java
index c05d4743223..3afb45e84db 100644
--- 
a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLifeCycleConfiguration.java
+++ 
b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLifeCycleConfiguration.java
@@ -30,6 +30,8 @@
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
 import com.google.common.collect.ImmutableMap;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -71,14 +73,14 @@ public void testCreateInValidLCConfiguration() throws 
OMException {
     List<OmLCRule> rules = Collections.singletonList(rule);
 
     OmLifecycleConfiguration.Builder lcc0 = getOmLifecycleConfiguration(null, 
"bucket", rules);
-    assertOMException(lcc0::build, INVALID_REQUEST, "Volume cannot be blank");
+    assertOMException(() -> lcc0.buildAndValid(), INVALID_REQUEST, "Volume 
cannot be blank");
 
     OmLifecycleConfiguration.Builder lcc1 = 
getOmLifecycleConfiguration("volume", null, rules);
-    assertOMException(lcc1::build, INVALID_REQUEST, "Bucket cannot be blank");
+    assertOMException(() -> lcc1.buildAndValid(), INVALID_REQUEST, "Bucket 
cannot be blank");
 
     OmLifecycleConfiguration.Builder lcc3 = getOmLifecycleConfiguration(
         "volume", "bucket", Collections.emptyList());
-    assertOMException(lcc3::build, INVALID_REQUEST,
+    assertOMException(() -> lcc3.buildAndValid(), INVALID_REQUEST,
         "At least one rules needs to be specified in a lifecycle 
configuration");
 
     List<OmLCRule> rules4 = new ArrayList<>(
@@ -92,7 +94,7 @@ public void testCreateInValidLCConfiguration() throws 
OMException {
       rules4.add(r);
     }
     OmLifecycleConfiguration.Builder lcc4 = 
getOmLifecycleConfiguration("volume", "bucket", rules4);
-    assertOMException(lcc4::build, INVALID_REQUEST,
+    assertOMException(() -> lcc4.buildAndValid(), INVALID_REQUEST,
         "The number of lifecycle rules must not exceed the allowed limit of");
   }
 
@@ -193,7 +195,7 @@ public void testDisabledRule() throws OMException {
         .build();
 
     assertFalse(rule.isEnabled());
-    assertDoesNotThrow(() -> rule.valid(BucketLayout.DEFAULT));
+    assertDoesNotThrow(() -> rule.valid(BucketLayout.DEFAULT, 
System.currentTimeMillis()));
   }
 
   @Test
@@ -225,4 +227,40 @@ public void testProtobufConversion() throws OMException {
     assertEquals(30, ruleFromProto.getExpiration().getDays());
   }
 
+  @Test
+  public void testOMStartupWithPastExpirationDate() throws OMException {
+    // Simulate a lifecycle configuration with expiration date in the past
+    // This scenario can happen when OM restarts after some time has passed
+    // since the lifecycle configuration was created.
+    
+    // Create a rule with expiration date that is in the past (simulating old 
config)
+    String pastDate = getFutureDateString(-1); // A date clearly in the past 
(1 day ago)
+    OmLCExpiration pastExpiration = new OmLCExpiration.Builder()
+        .setDate(pastDate)
+        .build();
+    
+    OmLCRule ruleWithPastDate = new OmLCRule.Builder()
+        .setId("test-rule-past-date")
+        .setPrefix("/old-logs/")
+        .setEnabled(true)
+        .addAction(pastExpiration)
+        .build();
+    
+    OmLifecycleConfiguration config = new OmLifecycleConfiguration.Builder()
+        .setVolume("test-volume")
+        .setBucket("test-bucket")
+        .setBucketLayout(BucketLayout.DEFAULT)
+        // An Expiration was created two days ago and expired 1 day ago, 
should be valid
+        
.setCreationTime(ZonedDateTime.now(ZoneOffset.UTC).minusDays(2).toInstant().toEpochMilli())
+        .addRule(ruleWithPastDate)
+        .setObjectID(123456L)
+        .setUpdateID(78910L)
+        .build();
+    
+    LifecycleConfiguration proto = config.getProtobuf();
+    OmLifecycleConfiguration configFromProto = assertDoesNotThrow(() ->
+        OmLifecycleConfiguration.getFromProtobuf(proto));
+    assertDoesNotThrow(configFromProto::valid);
+  }
+
 }
diff --git 
a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLifecycleRuleAndOperator.java
 
b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLifecycleRuleAndOperator.java
index 5b77a625980..43c7ffbc41b 100644
--- 
a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLifecycleRuleAndOperator.java
+++ 
b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmLifecycleRuleAndOperator.java
@@ -59,15 +59,17 @@ public void testValidAndOperator() throws OMException {
   @Test
   public void testInValidAndOperator() {
     OmLifecycleRuleAndOperator.Builder andOperator1 = 
getOmLCAndOperatorBuilder("prefix", null);
-    assertOMException(andOperator1::build, INVALID_REQUEST, "'Prefix' alone is 
not allowed");
+    assertOMException(() -> andOperator1.build().valid(BucketLayout.DEFAULT), 
INVALID_REQUEST,
+        "'Prefix' alone is not allowed");
 
     OmLifecycleRuleAndOperator.Builder andOperator2 =
         getOmLCAndOperatorBuilder(null, Collections.singletonMap("tag1", 
"value1"));
-    assertOMException(andOperator2::build, INVALID_REQUEST,
+    assertOMException(() -> andOperator2.build().valid(BucketLayout.DEFAULT), 
INVALID_REQUEST,
         "If 'Tags' are specified without 'Prefix', there should be more than 
one tag");
 
     OmLifecycleRuleAndOperator.Builder andOperator3 = 
getOmLCAndOperatorBuilder(null, null);
-    assertOMException(andOperator3::build, INVALID_REQUEST, "Either 'Tags' or 
'Prefix' must be specified.");
+    assertOMException(() -> andOperator3.build().valid(BucketLayout.DEFAULT), 
INVALID_REQUEST,
+        "Either 'Tags' or 'Prefix' must be specified.");
   }
 
   @Test
diff --git 
a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto 
b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
index 4823e81eaa0..1a83bf4ff58 100644
--- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
+++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
@@ -2390,7 +2390,7 @@ message LifecycleConfiguration {
   required string volume = 1;
   required string bucket = 2;
   required BucketLayoutProto bucketLayout = 3;
-  optional uint64 creationTime = 4;
+  required uint64 creationTime = 4;
   repeated LifecycleRule rules = 5;
   optional uint64 objectID = 6;
   optional uint64 updateID = 7;
diff --git 
a/hadoop-ozone/interface-storage/src/main/java/org/apache/hadoop/ozone/om/OMMetadataManager.java
 
b/hadoop-ozone/interface-storage/src/main/java/org/apache/hadoop/ozone/om/OMMetadataManager.java
index e790d370f83..98299fb2ba4 100644
--- 
a/hadoop-ozone/interface-storage/src/main/java/org/apache/hadoop/ozone/om/OMMetadataManager.java
+++ 
b/hadoop-ozone/interface-storage/src/main/java/org/apache/hadoop/ozone/om/OMMetadataManager.java
@@ -35,6 +35,7 @@
 import org.apache.hadoop.hdds.utils.db.cache.CacheKey;
 import org.apache.hadoop.hdds.utils.db.cache.CacheValue;
 import org.apache.hadoop.ozone.common.BlockGroup;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
 import org.apache.hadoop.ozone.om.helpers.BucketLayout;
 import org.apache.hadoop.ozone.om.helpers.ListKeysResult;
 import org.apache.hadoop.ozone.om.helpers.ListOpenFilesResult;
@@ -474,7 +475,7 @@ String getMultipartKeyFSO(String volume, String bucket, 
String key, String
   /**
    * @return list all LifecycleConfigurations.
    */
-  List<OmLifecycleConfiguration> listLifecycleConfigurations();
+  List<OmLifecycleConfiguration> listLifecycleConfigurations() throws 
OMException;
 
   /**
    * Fetches the lifecycle configuration by bucketName.
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
index e9b7dc84559..1dd6833df1f 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
@@ -2126,22 +2126,23 @@ public List<OmLifecycleConfiguration> 
listLifecycleConfigurations() {
   public OmLifecycleConfiguration getLifecycleConfiguration(String volumeName,
       String bucketName) throws IOException {
     Preconditions.checkNotNull(bucketName);
+    OmLifecycleConfiguration value = null;
     try {
       String bucketKey = getBucketKey(volumeName, bucketName);
-      OmLifecycleConfiguration value = 
getLifecycleConfigurationTable().get(bucketKey);
-
+      value = getLifecycleConfigurationTable().get(bucketKey);
       if (value == null) {
         LOG.debug("lifecycle configuration of bucket /{}/{} not found.",
             volumeName, bucketName);
         throw new OMException("Lifecycle configuration not found",
             LIFECYCLE_CONFIGURATION_NOT_FOUND);
       }
+      value.valid();
       return value;
     } catch (IOException ex) {
-      if (!(ex instanceof OMException)) {
-        LOG.error("Exception while getting lifecycle configuration for " +
-            "bucket: /{}/{}", volumeName, bucketName, ex);
-      }
+      LOG.error("Exception while getting lifecycle configuration for " +
+          "bucket: /{}/{}, LifecycleConfiguration {}", volumeName, bucketName,
+          value != null ? value.getProtobuf() : "", ex);
+
       throw ex;
     }
   }
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/lifecycle/OMLifecycleConfigurationSetRequest.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/lifecycle/OMLifecycleConfigurationSetRequest.java
index 91f4567dcce..da395fa30c1 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/lifecycle/OMLifecycleConfigurationSetRequest.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/lifecycle/OMLifecycleConfigurationSetRequest.java
@@ -47,7 +47,6 @@
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.UserInfo;
 import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
 import org.apache.hadoop.ozone.security.acl.OzoneObj;
-import org.apache.hadoop.util.Time;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -100,7 +99,7 @@ public OMRequest preExecute(OzoneManager ozoneManager) 
throws IOException {
             .setVolume(resolvedBucket.realVolume())
             .setBucket(resolvedBucket.realBucket());
 
-    newLifecycleConfiguration.setCreationTime(Time.now());
+    newLifecycleConfiguration.setCreationTime(System.currentTimeMillis());
     newCreateRequest.setLifecycleConfiguration(newLifecycleConfiguration);
 
     return omRequest.toBuilder().setUserInfo(getUserInfo())
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/service/KeyLifecycleService.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/service/KeyLifecycleService.java
index f707979746b..80d267cc84f 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/service/KeyLifecycleService.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/service/KeyLifecycleService.java
@@ -128,9 +128,22 @@ public BackgroundTaskQueue getTasks() {
       return queue;
     }
 
-    List<OmLifecycleConfiguration> lifecycleConfigurationList =
-        omMetadataManager.listLifecycleConfigurations();
+    List<OmLifecycleConfiguration> lifecycleConfigurationList = null;
+    try {
+      lifecycleConfigurationList = 
omMetadataManager.listLifecycleConfigurations();
+    } catch (OMException e) {
+      LOG.error("Failed to list lifecycle configurations", e);
+      return queue;
+    }
     for (OmLifecycleConfiguration lifecycleConfiguration : 
lifecycleConfigurationList) {
+      try {
+        lifecycleConfiguration.valid();
+      } catch (OMException e) {
+        LOG.error("Skip invalid lifecycle configuration for {}/{}: 
LifecycleConfiguration:\n {}",
+            lifecycleConfiguration.getVolume(), 
lifecycleConfiguration.getBucket(),
+            lifecycleConfiguration.getProtobuf(), e);
+        continue;
+      }
       String bucketKey = 
omMetadataManager.getBucketKey(lifecycleConfiguration.getVolume(),
           lifecycleConfiguration.getBucket());
       if (lifecycleConfiguration.getRules().stream().anyMatch(r -> 
r.isEnabled())) {
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/lifecycle/TestOMLifecycleConfigurationRequest.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/lifecycle/TestOMLifecycleConfigurationRequest.java
index 03075091b94..5f54c24a064 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/lifecycle/TestOMLifecycleConfigurationRequest.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/lifecycle/TestOMLifecycleConfigurationRequest.java
@@ -123,6 +123,7 @@ public OMRequest setLifecycleConfigurationRequest(String 
volumeName,
     String prefix = "prefix/";
     LifecycleConfiguration.Builder builder = 
LifecycleConfiguration.newBuilder()
         .setBucketLayout(BucketLayoutProto.OBJECT_STORE)
+        .setCreationTime(System.currentTimeMillis())
         .setVolume(volumeName)
         .setBucket(bucketName);
 
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/service/TestKeyLifecycleService.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/service/TestKeyLifecycleService.java
index 26b82c0c5fa..7df7e244b76 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/service/TestKeyLifecycleService.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/service/TestKeyLifecycleService.java
@@ -1504,7 +1504,6 @@ private void createLifecyclePolicy(String volume, String 
bucket, BucketLayout la
             .setEnabled(enabled)
             .setPrefix(prefix)
             .setFilter(filter)
-            .setBucketLayout(layout)
             .setAction(new OmLCExpiration.Builder()
                 .setDate(date)
                 .build())
@@ -1513,6 +1512,7 @@ private void createLifecyclePolicy(String volume, String 
bucket, BucketLayout la
     String key = "/" + volume + "/" + bucket;
     LifecycleConfiguration lcProto = lcc.getProtobuf();
     OmLifecycleConfiguration canonicalLcc = 
OmLifecycleConfiguration.getFromProtobuf(lcProto);
+    canonicalLcc.valid();
     metadataManager.getLifecycleConfigurationTable().put(key, lcc);
     metadataManager.getLifecycleConfigurationTable().addCacheEntry(
         new CacheKey<>(key), CacheValue.get(1L, canonicalLcc));
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 cb9b79c858f..b2b90cc3a2b 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
@@ -813,7 +813,9 @@ public Response putBucketLifecycleConfiguration(
       if (ex.getResult() == ResultCodes.ACCESS_DENIED) {
         throw S3ErrorTable.newError(S3ErrorTable.ACCESS_DENIED, bucketName);
       } else if (ex.getResult() == ResultCodes.INVALID_REQUEST) {
-        throw S3ErrorTable.newError(S3ErrorTable.INVALID_REQUEST, bucketName);
+        OS3Exception invalidException = S3ErrorTable.INVALID_REQUEST;
+        invalidException.setErrorMessage(ex.getMessage());
+        throw S3ErrorTable.newError(invalidException, bucketName);
       }
     }
     return Response.ok().build();
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 54009f28491..8aa3e52f433 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
@@ -263,7 +263,7 @@ public OmLifecycleConfiguration 
toOmLifecycleConfiguration(OzoneBucket ozoneBuck
         builder.addRule(convertToOmRule(rule));
       }
 
-      return builder.build();
+      return builder.buildAndValid();
     } catch (Exception ex) {
       if (ex instanceof IllegalStateException) {
         throw S3ErrorTable.newError(S3ErrorTable.INVALID_REQUEST, 
ozoneBucket.getName(), ex);


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@ozone.apache.org
For additional commands, e-mail: commits-h...@ozone.apache.org


Reply via email to