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]