This is an automated email from the ASF dual-hosted git repository.
lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git
The following commit(s) were added to refs/heads/rocketmq-studio by this push:
new 61d1b178d fix(nameserver): preserve named IPv6 zone identifiers (#4522)
61d1b178d is described below
commit 61d1b178d8ab95a7963471fc63b7c9ae6b975488
Author: jokerzsd <[email protected]>
AuthorDate: Mon Sep 21 21:00:07 2026 +0800
fix(nameserver): preserve named IPv6 zone identifiers (#4522)
`NamesrvAddrParser.normalizeSegment` lowercased the whole bracketed IPv6
literal, zone identifier included, so registering `[FE80::ABCD%ProdNIC]:9876`
persisted `%prodnic` in `rmq_nameserver.namesrv_addr`. The address reaches that
line because `commons-validator`'s `isValidInet6Address` splits off the `%zone`
suffix and validates it separately, so a zoned literal passes
`isValidIpv6Literal`. A named zone is a local interface name that the JDK
matches against `NetworkInterface.getName( [...]
The literal is now split at the first `%`: only the address part is
lowercased and the zone keeps its exact spelling. The DNS branch still
lowercases hostnames, which are case-insensitive, and
`NameServerConfigDiffService.canonicalAddressKey` already had its own `%`
guard. The registry create and update tests became parameterized so both cover
a named-zone address end to end.
Fixes #4520
---
.../cluster/nameserver/NamesrvAddrParser.java | 6 ++++-
.../nameserver/NameserverRegistryServiceTest.java | 30 ++++++++++++++--------
.../cluster/nameserver/NamesrvAddrParserTest.java | 12 +++++++++
3 files changed, 37 insertions(+), 11 deletions(-)
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/cluster/nameserver/NamesrvAddrParser.java
b/server/src/main/java/org/apache/rocketmq/studio/cluster/nameserver/NamesrvAddrParser.java
index a064f5969..9bebcfd4d 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/cluster/nameserver/NamesrvAddrParser.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/cluster/nameserver/NamesrvAddrParser.java
@@ -65,7 +65,11 @@ public final class NamesrvAddrParser {
if (!isValidIpv6Literal(ipv6)) {
throw new BusinessException(400, "namesrvAddr segment has a
malformed IPv6 literal: " + segment);
}
- normalizedHost = "[" + ipv6.toLowerCase(Locale.ROOT) + "]";
+ int zoneStart = ipv6.indexOf('%');
+ String address = zoneStart < 0 ? ipv6 : ipv6.substring(0,
zoneStart);
+ // Named zones identify local interfaces and must retain their
exact spelling.
+ String zone = zoneStart < 0 ? "" : ipv6.substring(zoneStart);
+ normalizedHost = "[" + address.toLowerCase(Locale.ROOT) + zone +
"]";
} else {
if (host.isEmpty()) {
throw new BusinessException(400, "namesrvAddr segment is
missing a host: " + segment);
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/cluster/nameserver/NameserverRegistryServiceTest.java
b/server/src/test/java/org/apache/rocketmq/studio/cluster/nameserver/NameserverRegistryServiceTest.java
index 29146acf5..29a50f398 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/cluster/nameserver/NameserverRegistryServiceTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/cluster/nameserver/NameserverRegistryServiceTest.java
@@ -22,6 +22,8 @@ import
org.apache.rocketmq.studio.persistence.entity.RmqNameserver;
import org.apache.rocketmq.studio.persistence.mapper.RmqNameserverMapper;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
@@ -153,8 +155,12 @@ class NameserverRegistryServiceTest {
verify(nameserverMapper, never()).insert(any(RmqNameserver.class));
}
- @Test
- void createShouldNormalizeAddrBeforePersistTest() {
+ @ParameterizedTest
+ @CsvSource({
+ "' NS1:9876 ; ns2:9876 ', 'ns1:9876,ns2:9876'",
+ "'[FE80::ABCD%ProdNIC]:9876', '[fe80::abcd%ProdNIC]:9876'"
+ })
+ void createShouldNormalizeAddrBeforePersistTest(String input, String
expected) {
when(nameserverMapper.selectCount(any())).thenReturn(0L);
when(nameserverMapper.insert(any(RmqNameserver.class))).thenAnswer(invocation
-> {
RmqNameserver entity = invocation.getArgument(0);
@@ -164,17 +170,17 @@ class NameserverRegistryServiceTest {
RmqNameserver stored = new RmqNameserver();
stored.setId(11L);
stored.setName("prod");
- stored.setNamesrvAddr("ns1:9876,ns2:9876");
+ stored.setNamesrvAddr(expected);
when(nameserverMapper.selectById(11L)).thenReturn(stored);
service.create(CreateNameserverRegistryDTO.builder()
.name("prod")
- .namesrvAddr(" NS1:9876 ; ns2:9876 ")
+ .namesrvAddr(input)
.build());
ArgumentCaptor<RmqNameserver> captor =
ArgumentCaptor.forClass(RmqNameserver.class);
verify(nameserverMapper).insert(captor.capture());
-
assertThat(captor.getValue().getNamesrvAddr()).isEqualTo("ns1:9876,ns2:9876");
+ assertThat(captor.getValue().getNamesrvAddr()).isEqualTo(expected);
}
@Test
@@ -220,8 +226,12 @@ class NameserverRegistryServiceTest {
.hasMessageContaining("deleted concurrently");
}
- @Test
- void updateShouldPersistAndReturnStoredEntryTest() {
+ @ParameterizedTest
+ @CsvSource({
+ "rocketmq1-nameserver.svc:9876, rocketmq1-nameserver.svc:9876",
+ "[FE80::ABCD%ProdNIC]:9876, [fe80::abcd%ProdNIC]:9876"
+ })
+ void updateShouldPersistAndReturnStoredEntryTest(String input, String
expected) {
RmqNameserver existing = new RmqNameserver();
existing.setId(1L);
existing.setName("rocketmq1");
@@ -232,13 +242,13 @@ class NameserverRegistryServiceTest {
NameserverRegistryVO updated =
service.update(UpdateNameserverRegistryDTO.builder()
.id(1L)
.name("rocketmq1")
- .namesrvAddr("rocketmq1-nameserver.svc:9876")
+ .namesrvAddr(input)
.k8sNamespace("rocketmq1")
.build());
-
assertThat(updated.getNamesrvAddr()).isEqualTo("rocketmq1-nameserver.svc:9876");
+ assertThat(updated.getNamesrvAddr()).isEqualTo(expected);
verify(nameserverMapper).updateById(existing);
-
assertThat(existing.getNamesrvAddr()).isEqualTo("rocketmq1-nameserver.svc:9876");
+ assertThat(existing.getNamesrvAddr()).isEqualTo(expected);
}
@Test
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/cluster/nameserver/NamesrvAddrParserTest.java
b/server/src/test/java/org/apache/rocketmq/studio/cluster/nameserver/NamesrvAddrParserTest.java
index 24ffd8687..b5180fc39 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/cluster/nameserver/NamesrvAddrParserTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/cluster/nameserver/NamesrvAddrParserTest.java
@@ -59,6 +59,18 @@ class NamesrvAddrParserTest {
assertThat(NamesrvAddrParser.normalize("[2001:DB8::1]:9876")).isEqualTo("[2001:db8::1]:9876");
}
+ @Test
+ void preservesNamedIpv6ZoneWhileNormalizingAddressAndSeparatorsTest() {
+ assertThat(NamesrvAddrParser.normalize(" [FE80::ABCD%ProdNIC]:9876 ;
NS1.Example.COM:9876 "))
+ .isEqualTo("[fe80::abcd%ProdNIC]:9876,ns1.example.com:9876");
+ }
+
+ @Test
+ void preservesNumericIpv6ZoneTest() {
+ assertThat(NamesrvAddrParser.normalize("[FE80::ABCD%12]:9876"))
+ .isEqualTo("[fe80::abcd%12]:9876");
+ }
+
@Test
void rejectsBlankInputTest() {
assertThatThrownBy(() -> NamesrvAddrParser.normalize(" "))