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

smengcl 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 5dfed2ce426 HDDS-15775. Support IPv6 literal authorities in OzoneFS 
URI parsing (#10933)
5dfed2ce426 is described below

commit 5dfed2ce4267ab63e0e0ee8d8d567961bb2ea127
Author: KUAN-HAO HUANG <[email protected]>
AuthorDate: Wed Aug 12 08:03:52 2026 +0800

    HDDS-15775. Support IPv6 literal authorities in OzoneFS URI parsing (#10933)
---
 .../hadoop/ozone/client/OzoneClientFactory.java    |  5 ++-
 .../main/java/org/apache/hadoop/ozone/OmUtils.java |  6 ++-
 .../java/org/apache/hadoop/ozone/TestOmUtils.java  | 19 +++++++++
 .../fs/ozone/BasicRootedOzoneFileSystem.java       | 29 +++++++------
 .../hadoop/fs/ozone/TestBasicOzoneFileSystems.java | 49 ++++++++++++++++++++++
 5 files changed, 91 insertions(+), 17 deletions(-)

diff --git 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/OzoneClientFactory.java
 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/OzoneClientFactory.java
index 2e4edee5c6b..4d1a92f1788 100644
--- 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/OzoneClientFactory.java
+++ 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/OzoneClientFactory.java
@@ -100,7 +100,10 @@ public static OzoneClient getRpcClient(String omHost, 
Integer omRpcPort,
     Objects.requireNonNull(omRpcPort, "omRpcPort == null");
     Objects.requireNonNull(config, "config == null");
     OmUtils.resolveOmHost(omHost, omRpcPort);
-    config.set(OZONE_OM_ADDRESS_KEY, omHost + ":" + omRpcPort);
+    // getHostPortString brackets IPv6 literals; a plain host + ":" + port 
would
+    // produce an ambiguous address such as ::1:9862 for an IPv6 OM host.
+    config.set(OZONE_OM_ADDRESS_KEY,
+        HddsUtils.getHostPortString(omHost, omRpcPort));
     return getRpcClient(getClientProtocol(config), config);
   }
 
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
index 08b1d8af5a8..9e9622c6411 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
@@ -19,6 +19,7 @@
 
 import static org.apache.hadoop.hdds.HddsUtils.getHostName;
 import static org.apache.hadoop.hdds.HddsUtils.getHostNameFromConfigKeys;
+import static org.apache.hadoop.hdds.HddsUtils.getHostPortString;
 import static org.apache.hadoop.hdds.HddsUtils.getPortNumberFromConfigKeys;
 import static org.apache.hadoop.ozone.OzoneConsts.DOUBLE_SLASH_OM_KEY_PREFIX;
 import static org.apache.hadoop.ozone.OzoneConsts.OM_KEY_PREFIX;
@@ -1143,7 +1144,10 @@ public static List<List<String>> format(
    */
   public static void resolveOmHost(String omHost, int omPort)
       throws IOException {
-    InetSocketAddress omHostAddress = NetUtils.createSocketAddr(omHost, 
omPort);
+    // Combine via getHostPortString so an IPv6 literal is bracketed; passing a
+    // bare ::1 to createSocketAddr fails with "not a valid host:port 
authority".
+    InetSocketAddress omHostAddress =
+        NetUtils.createSocketAddr(getHostPortString(omHost, omPort));
     if (omHostAddress.isUnresolved()) {
       throw new IOException(
           "Cannot resolve OM host " + omHost + " in the URI",
diff --git 
a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/TestOmUtils.java 
b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/TestOmUtils.java
index a7af3c6d42e..1521cdd24ee 100644
--- a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/TestOmUtils.java
+++ b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/TestOmUtils.java
@@ -384,4 +384,23 @@ public void testShouldSendToFollowerImpliesIsReadOnly() {
       }
     }
   }
+
+  @Test
+  public void testResolveOmHostAcceptsIpv6Literal() {
+    // A bracketed or bare IPv6 literal must parse into a host:port authority.
+    // Before HDDS-15775 the bare form made createSocketAddr throw
+    // IllegalArgumentException ("not a valid host:port authority"). 
Reachability
+    // is environment-dependent, so an IOException here is acceptable; only the
+    // parse failure must not happen.
+    for (String host : new String[] {"::1", "[::1]"}) {
+      try {
+        OmUtils.resolveOmHost(host, 9862);
+      } catch (IllegalArgumentException e) {
+        fail("IPv6 literal '" + host + "' should parse into an address: "
+            + e.getMessage());
+      } catch (IOException ignored) {
+        // Unresolved or unreachable in the test environment is acceptable.
+      }
+    }
+  }
 }
diff --git 
a/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicRootedOzoneFileSystem.java
 
b/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicRootedOzoneFileSystem.java
index 6be8ebbddb6..089f0c453e6 100644
--- 
a/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicRootedOzoneFileSystem.java
+++ 
b/hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicRootedOzoneFileSystem.java
@@ -37,6 +37,7 @@
 
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
+import com.google.common.net.HostAndPort;
 import io.opentelemetry.api.trace.Span;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -158,21 +159,19 @@ public void initialize(URI name, Configuration conf) 
throws IOException {
       throw new IllegalArgumentException(URI_EXCEPTION_TEXT);
     }
 
-    String omHostOrServiceId;
-    int omPort = -1;
-    // Parse hostname and port
-    String[] parts = authority.split(":");
-    if (parts.length > 2) {
-      throw new IllegalArgumentException(URI_EXCEPTION_TEXT);
-    }
-    omHostOrServiceId = parts[0];
-    if (parts.length == 2) {
-      try {
-        omPort = Integer.parseInt(parts[1]);
-      } catch (NumberFormatException e) {
-        throw new IllegalArgumentException(URI_EXCEPTION_TEXT);
-      }
+    // Parse hostname and port. HostAndPort is bracket-aware, so IPv6 literal
+    // authorities (for example [::1]:9862) are split correctly instead of on
+    // every colon.
+    final HostAndPort hostAndPort;
+    try {
+      hostAndPort = HostAndPort.fromString(authority);
+    } catch (IllegalArgumentException e) {
+      throw new IllegalArgumentException(URI_EXCEPTION_TEXT, e);
     }
+    int omPort = hostAndPort.hasPort() ? hostAndPort.getPort() : -1;
+    // Pass the bare host; the adapter builds the OM address via
+    // getHostPortString, which brackets IPv6 literals.
+    String host = hostAndPort.getHost();
 
     try {
       uri = new URIBuilder().setScheme(OZONE_OFS_URI_SCHEME)
@@ -183,7 +182,7 @@ public void initialize(URI name, Configuration conf) throws 
IOException {
       ConfigurationSource source = getConfSource();
       this.hsyncEnabled = OzoneFSUtils.canEnableHsync(source, true);
       LOG.debug("hsyncEnabled = {}", hsyncEnabled);
-      this.adapter = createAdapter(source, omHostOrServiceId, omPort);
+      this.adapter = createAdapter(source, host, omPort);
       this.adapterImpl = (BasicRootedOzoneClientAdapterImpl) this.adapter;
 
       try {
diff --git 
a/hadoop-ozone/ozonefs-common/src/test/java/org/apache/hadoop/fs/ozone/TestBasicOzoneFileSystems.java
 
b/hadoop-ozone/ozonefs-common/src/test/java/org/apache/hadoop/fs/ozone/TestBasicOzoneFileSystems.java
index 71142d7b19e..6cab32c9ea0 100644
--- 
a/hadoop-ozone/ozonefs-common/src/test/java/org/apache/hadoop/fs/ozone/TestBasicOzoneFileSystems.java
+++ 
b/hadoop-ozone/ozonefs-common/src/test/java/org/apache/hadoop/fs/ozone/TestBasicOzoneFileSystems.java
@@ -22,15 +22,20 @@
 import static org.apache.hadoop.ozone.OzoneConsts.OM_SNAPSHOT_INDICATOR;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import java.io.IOException;
+import java.net.URI;
 import java.util.Arrays;
 import java.util.Collection;
 import org.apache.hadoop.conf.Configuration;
@@ -38,8 +43,11 @@
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.hdds.conf.OzoneConfiguration;
 import org.apache.hadoop.hdds.conf.StorageSize;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
 import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.ArgumentCaptor;
 
 /**
  * Unit test for Basic*OzoneFileSystem.
@@ -137,6 +145,47 @@ public void testCreateSnapshotReturnPath(
     }
   }
 
+  @ParameterizedTest
+  @CsvSource(value = {
+      // hostname / IPv4 authority (behaviour unchanged)
+      "ofs://host:9862/, host, 9862",
+      "ofs://omservice1/, omservice1, -1",
+      // service id with an underscore: HostAndPort tolerates it (URI.getHost 
does not)
+      "ofs://om_service/, om_service, -1",
+      // IPv6 literal authority, with and without a port; the bare literal is
+      // passed to the adapter (which re-brackets it), while the filesystem URI
+      // keeps the bracketed authority.
+      "ofs://[::1]:9862/, ::1, 9862",
+      "ofs://[2001:db8::1]/, 2001:db8::1, -1",
+  })
+  public void testRootedAuthorityParsing(String uri, String expectedHost,
+      int expectedPort) throws Exception {
+    BasicRootedOzoneFileSystem ofs = spy(new BasicRootedOzoneFileSystem());
+    BasicRootedOzoneClientAdapterImpl adapter =
+        mock(BasicRootedOzoneClientAdapterImpl.class);
+    doReturn(adapter).when(ofs).createAdapter(any(), anyString(), anyInt());
+
+    ofs.initialize(new URI(uri), new OzoneConfiguration());
+
+    ArgumentCaptor<String> hostCaptor = ArgumentCaptor.forClass(String.class);
+    ArgumentCaptor<Integer> portCaptor = 
ArgumentCaptor.forClass(Integer.class);
+    verify(ofs).createAdapter(any(), hostCaptor.capture(),
+        portCaptor.capture());
+    assertEquals(expectedHost, hostCaptor.getValue());
+    assertEquals(expectedPort, portCaptor.getValue().intValue());
+
+    // The rebuilt filesystem URI keeps the (bracketed) IPv6 authority intact.
+    assertEquals(new URI(uri).getAuthority(), ofs.getUri().getAuthority());
+  }
+
+  @Test
+  public void testRootedAuthorityRejectsOutOfRangePort() {
+    BasicRootedOzoneFileSystem ofs = spy(new BasicRootedOzoneFileSystem());
+    // HostAndPort rejects ports outside 0-65535, unlike a bare 
Integer.parseInt.
+    assertThrows(IllegalArgumentException.class,
+        () -> ofs.initialize(new URI("ofs://host:99999/"), new 
OzoneConfiguration()));
+  }
+
   private void assertDefaultBlockSize(long expected, FileSystem subject) {
     assertEquals(expected, subject.getDefaultBlockSize());
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to