You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by vj...@apache.org on 2026/09/12 22:42:38 UTC
(hbase) branch master updated: HBASE-30355 setupMiniKdc port-conflict retry never triggers because Kerby throws KrbException, not BindException (#8604)
This is an automated email from the ASF dual-hosted git repository.
virajjasani pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hbase.git
The following commit(s) were added to refs/heads/master by this push:
new 90c4d776bb0 HBASE-30355 setupMiniKdc port-conflict retry never triggers because Kerby throws KrbException, not BindException (#8604)
90c4d776bb0 is described below
commit 90c4d776bb0a78147bb32c56285365e98b3d6b3e
Author: Aman Poonia <ap...@salesforce.com>
AuthorDate: Sun Sep 13 04:12:31 2026 +0530
HBASE-30355 setupMiniKdc port-conflict retry never triggers because Kerby throws KrbException, not BindException (#8604)
Signed-off-by: Viraj Jasani <vj...@apache.org>
---
.../apache/hadoop/hbase/http/log/TestLogLevel.java | 58 ++++++++++++++++++++--
.../org/apache/hadoop/hbase/HBaseTestingUtil.java | 37 ++++++++++++--
2 files changed, 89 insertions(+), 6 deletions(-)
diff --git a/hbase-http/src/test/java/org/apache/hadoop/hbase/http/log/TestLogLevel.java b/hbase-http/src/test/java/org/apache/hadoop/hbase/http/log/TestLogLevel.java
index 9e470025c39..41ed421be91 100644
--- a/hbase-http/src/test/java/org/apache/hadoop/hbase/http/log/TestLogLevel.java
+++ b/hbase-http/src/test/java/org/apache/hadoop/hbase/http/log/TestLogLevel.java
@@ -30,6 +30,7 @@ import java.net.BindException;
import java.net.SocketException;
import java.net.URI;
import java.security.PrivilegedExceptionAction;
+import java.util.Locale;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -153,20 +154,71 @@ public class TestLogLevel {
dir = new File(HTU.getDataTestDir("kdc").toUri().getPath());
kdc = new MiniKdc(conf, dir);
kdc.start();
- } catch (BindException e) {
- FileUtils.deleteDirectory(dir); // clean directory
+ } catch (Exception e) {
+ // Catch Exception, not BindException/KrbException: Kerby wraps the bind failure in a
+ // KrbException whose shape varies by version (see isBindException), so we recognise the
+ // port conflict via that predicate rather than a type. We also avoid importing kerby types
+ // here (see HBASE-29117).
+ FileUtils.deleteDirectory(dir); // clean directory regardless of failure type
+ if (!isBindException(e)) {
+ throw e; // not a port conflict, do not mask the real failure behind a retry
+ }
numTries++;
if (numTries == 3) {
log.error("Failed setting up MiniKDC. Tried " + numTries + " times.");
throw e;
}
- log.error("BindException encountered when setting up MiniKdc. Trying again.");
+ log.error("Bind conflict encountered when setting up MiniKdc, retrying (attempt " + numTries
+ + ").");
bindException = true;
}
} while (bindException);
return kdc;
}
+ /**
+ * The Kerby-backed {@link MiniKdc} wraps a failure to bind the KDC port in a
+ * {@code org.apache.kerby...KrbException} rather than surfacing a {@link BindException} directly,
+ * so we inspect the whole cause chain plus the message to recognise a port conflict. Both checks
+ * are load-bearing across Kerby versions: kerby 1.x preserves the original {@link BindException}
+ * as the cause (caught by the cause-chain check) while kerby 2.x drops the cause and only appends
+ * the bind message (caught by the message check). The message match is lower-cased since the
+ * wording is JDK/OS specific.
+ */
+ static boolean isBindException(Throwable t) {
+ for (Throwable cause = t; cause != null; cause = cause.getCause()) {
+ if (cause instanceof BindException) {
+ return true;
+ }
+ String msg = cause.getMessage();
+ if (msg != null && msg.toLowerCase(Locale.ROOT).contains("address already in use")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Deterministic regression test for the retry predicate. A port conflict raised by the
+ * Kerby-backed {@link MiniKdc} must be recognised as a bind failure whether the
+ * {@link BindException} is preserved in the cause chain or only reflected in the message;
+ * unrelated failures must not be. We model the Kerby wrapper with a plain {@link Exception}
+ * rather than constructing a real Kerby exception, so the test does not import kerby types (see
+ * HBASE-29117).
+ */
+ @Test
+ public void testIsBindExceptionRecognizesKerbyWrappedBindFailure() {
+ assertTrue(
+ isBindException(new Exception("Failed to start DefaultKrbServer",
+ new BindException("Address already in use"))),
+ "a bind failure preserved in the cause chain should be recognised");
+ assertTrue(
+ isBindException(new Exception("Failed to start DefaultKrbServer. Address already in use")),
+ "a wrapper whose message reports the bind conflict should be recognised");
+ assertFalse(isBindException(new RuntimeException("boom")),
+ "an unrelated failure must not be treated as a retryable bind conflict");
+ }
+
static private void setupSSL(File base) throws Exception {
clientConf.set(DFSConfigKeys.DFS_HTTP_POLICY_KEY, HttpConfig.Policy.HTTPS_ONLY.name());
clientConf.set(DFSConfigKeys.DFS_NAMENODE_HTTPS_ADDRESS_KEY, "localhost:0");
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtil.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtil.java
index 8bf46059177..1bb05119fb7 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtil.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtil.java
@@ -43,6 +43,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.NavigableSet;
import java.util.Properties;
@@ -3731,14 +3732,22 @@ public class HBaseTestingUtil extends HBaseZKTestingUtil {
dir = new File(getDataTestDir("kdc").toUri().getPath());
kdc = new MiniKdc(conf, dir);
kdc.start();
- } catch (BindException e) {
- FileUtils.deleteDirectory(dir); // clean directory
+ } catch (Exception e) {
+ // Catch Exception, not BindException/KrbException: Kerby wraps the bind failure in a
+ // KrbException whose shape varies by version (see isBindException), so we recognise the
+ // port conflict via that predicate rather than a type. We also avoid importing kerby types
+ // here (see HBASE-29117).
+ FileUtils.deleteDirectory(dir); // clean directory regardless of failure type
+ if (!isBindException(e)) {
+ throw e; // not a port conflict, do not mask the real failure behind a retry
+ }
numTries++;
if (numTries == 3) {
LOG.error("Failed setting up MiniKDC. Tried " + numTries + " times.");
throw e;
}
- LOG.error("BindException encountered when setting up MiniKdc. Trying again.");
+ LOG.error("Bind conflict encountered when setting up MiniKdc, retrying (attempt " + numTries
+ + ").");
bindException = true;
}
} while (bindException);
@@ -3746,6 +3755,28 @@ public class HBaseTestingUtil extends HBaseZKTestingUtil {
return kdc;
}
+ /**
+ * The Kerby-backed {@link MiniKdc} wraps a failure to bind the KDC port in a
+ * {@code org.apache.kerby...KrbException} rather than surfacing a {@link BindException} directly,
+ * so we inspect the whole cause chain plus the message to recognise a port conflict. Both checks
+ * are load-bearing across Kerby versions: kerby 1.x preserves the original {@link BindException}
+ * as the cause (caught by the cause-chain check) while kerby 2.x drops the cause and only appends
+ * the bind message (caught by the message check). The message match is lower-cased since the
+ * wording is JDK/OS specific.
+ */
+ static boolean isBindException(Throwable t) {
+ for (Throwable cause = t; cause != null; cause = cause.getCause()) {
+ if (cause instanceof BindException) {
+ return true;
+ }
+ String msg = cause.getMessage();
+ if (msg != null && msg.toLowerCase(Locale.ROOT).contains("address already in use")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public int getNumHFiles(final TableName tableName, final byte[] family) {
int numHFiles = 0;
for (RegionServerThread regionServerThread : getMiniHBaseCluster().getRegionServerThreads()) {