From 95000c7fbdc14a0d5dd05fff7d81a46e168e0898 Mon Sep 17 00:00:00 2001 From: Devesh Singh Date: Fri, 11 Sep 2026 13:47:27 +0530 Subject: [PATCH] HDDS-16039. Introduce S3StorageClass mapping to OzoneStoragePolicy. --- .../apache/hadoop/ozone/s3/util/S3Consts.java | 5 + .../hadoop/ozone/s3/util/S3StorageClass.java | 100 ++++++++++++++++++ .../ozone/s3/util/TestS3StorageClass.java | 69 ++++++++++++ 3 files changed, 174 insertions(+) create mode 100644 hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3StorageClass.java create mode 100644 hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/util/TestS3StorageClass.java diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3Consts.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3Consts.java index 8477d8c64a1d..a971dc578b79 100644 --- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3Consts.java +++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3Consts.java @@ -131,6 +131,11 @@ public final class S3Consts { /** Request header carrying the list of object attributes to return. */ public static final String OBJECT_ATTRIBUTES_HEADER = "x-amz-object-attributes"; + // S3 storage class values passed via the `x-amz-storage-class` request header. + public static final String S3_STORAGE_CLASS_STANDARD = "STANDARD"; + public static final String S3_STORAGE_CLASS_STANDARD_IA = "STANDARD_IA"; + public static final String S3_STORAGE_CLASS_GLACIER = "GLACIER"; + //Never Constructed private S3Consts() { diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3StorageClass.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3StorageClass.java new file mode 100644 index 000000000000..27825e3c3379 --- /dev/null +++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3StorageClass.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.ozone.s3.util; + +import static org.apache.hadoop.hdds.client.OzoneStoragePolicy.COLD; +import static org.apache.hadoop.hdds.client.OzoneStoragePolicy.HOT; +import static org.apache.hadoop.hdds.client.OzoneStoragePolicy.WARM; +import static org.apache.hadoop.ozone.s3.util.S3Consts.S3_STORAGE_CLASS_GLACIER; +import static org.apache.hadoop.ozone.s3.util.S3Consts.S3_STORAGE_CLASS_STANDARD; +import static org.apache.hadoop.ozone.s3.util.S3Consts.S3_STORAGE_CLASS_STANDARD_IA; + +import org.apache.hadoop.hdds.client.OzoneStoragePolicy; +import org.apache.hadoop.hdds.client.StoragePolicy; + +/** + * Maps S3 storage class values to Ozone StoragePolicy. + */ +public enum S3StorageClass { + + STANDARD(S3_STORAGE_CLASS_STANDARD, HOT), + STANDARD_IA(S3_STORAGE_CLASS_STANDARD_IA, WARM), + GLACIER(S3_STORAGE_CLASS_GLACIER, COLD); + + private final String s3StorageClass; + private final StoragePolicy storagePolicy; + + S3StorageClass(String s3StorageClass, StoragePolicy storagePolicy) { + this.s3StorageClass = s3StorageClass; + this.storagePolicy = storagePolicy; + } + + public StoragePolicy getStoragePolicy() { + return storagePolicy; + } + + public String getS3StorageClass() { + return s3StorageClass; + } + + /** + * Look up the S3StorageClass matching an S3 storage-class header value. + * + * @param s3StorageClass the S3 storage class name (case-insensitive). + * @return the matching S3StorageClass. + * @throws IllegalArgumentException if the value is null or does not map to + * a supported storage class. + */ + public static S3StorageClass fromS3StorageClass(String s3StorageClass) { + if (s3StorageClass == null) { + throw new IllegalArgumentException("Not supported s3StorageClass: null"); + } + try { + return S3StorageClass.valueOf(s3StorageClass.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException( + "Not supported s3StorageClass: " + s3StorageClass); + } + } + + /** + * Look up the S3StorageClass matching an Ozone StoragePolicy. + * + * @param storagePolicy the Ozone StoragePolicy. + * @return the matching S3StorageClass. + * @throws IllegalArgumentException if the policy is null or does not map to + * a supported storage class. + */ + public static S3StorageClass fromStoragePolicy(StoragePolicy storagePolicy) { + if (!(storagePolicy instanceof OzoneStoragePolicy)) { + throw new IllegalArgumentException( + "Not supported storagePolicy: " + storagePolicy); + } + switch ((OzoneStoragePolicy) storagePolicy) { + case HOT: + return STANDARD; + case WARM: + return STANDARD_IA; + case COLD: + return GLACIER; + default: + throw new IllegalArgumentException( + "Not supported storagePolicy: " + storagePolicy); + } + } +} diff --git a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/util/TestS3StorageClass.java b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/util/TestS3StorageClass.java new file mode 100644 index 000000000000..d6234618ed7d --- /dev/null +++ b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/util/TestS3StorageClass.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.ozone.s3.util; + +import static org.apache.hadoop.ozone.s3.util.S3Consts.S3_STORAGE_CLASS_GLACIER; +import static org.apache.hadoop.ozone.s3.util.S3Consts.S3_STORAGE_CLASS_STANDARD; +import static org.apache.hadoop.ozone.s3.util.S3Consts.S3_STORAGE_CLASS_STANDARD_IA; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.apache.hadoop.hdds.client.OzoneStoragePolicy; +import org.junit.jupiter.api.Test; + +/** Test the S3 storage-class ↔ Ozone StoragePolicy mapping. */ +class TestS3StorageClass { + + @Test + void testFromS3StorageClass() { + // S3 storage-class string → S3StorageClass + assertEquals(S3StorageClass.STANDARD, + S3StorageClass.fromS3StorageClass(S3_STORAGE_CLASS_STANDARD)); + assertEquals(S3StorageClass.STANDARD_IA, + S3StorageClass.fromS3StorageClass(S3_STORAGE_CLASS_STANDARD_IA)); + assertEquals(S3StorageClass.GLACIER, + S3StorageClass.fromS3StorageClass(S3_STORAGE_CLASS_GLACIER)); + assertThrows(IllegalArgumentException.class, + () -> S3StorageClass.fromS3StorageClass("INVALID")); + assertThrows(IllegalArgumentException.class, + () -> S3StorageClass.fromS3StorageClass(null)); + } + + @Test + void testFromStoragePolicy() { + // OzoneStoragePolicy → S3StorageClass + assertEquals(S3StorageClass.STANDARD, + S3StorageClass.fromStoragePolicy(OzoneStoragePolicy.HOT)); + assertEquals(S3StorageClass.STANDARD_IA, + S3StorageClass.fromStoragePolicy(OzoneStoragePolicy.WARM)); + assertEquals(S3StorageClass.GLACIER, + S3StorageClass.fromStoragePolicy(OzoneStoragePolicy.COLD)); + assertThrows(IllegalArgumentException.class, + () -> S3StorageClass.fromStoragePolicy(null)); + } + + @Test + void testMappingConsistency() { + for (S3StorageClass s3StorageClass : S3StorageClass.values()) { + assertEquals(s3StorageClass, + S3StorageClass.fromS3StorageClass(s3StorageClass.getS3StorageClass())); + assertEquals(s3StorageClass, + S3StorageClass.fromStoragePolicy(s3StorageClass.getStoragePolicy())); + } + } +}