This is an automated email from the ASF dual-hosted git repository.
yuqi1129 pushed a commit to branch branch-1.3
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/branch-1.3 by this push:
new 5db0d9aa4c [Cherry-pick to branch-1.3] [#11353] fix(hive): fix three
ClassLoader/Kerberos bugs causing HMS connection failure (#11355) (#11438)
5db0d9aa4c is described below
commit 5db0d9aa4c9eb7e1ba909c819b0b76cd193568dc
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri Jun 5 09:16:03 2026 +0800
[Cherry-pick to branch-1.3] [#11353] fix(hive): fix three
ClassLoader/Kerberos bugs causing HMS connection failure (#11355) (#11438)
**Cherry-pick Information:**
- Original commit: 768213d3057540319856c83c0c646b6e690ebe91
- Target branch: `branch-1.3`
- Status: ✅ Clean cherry-pick (no conflicts)
Co-authored-by: weijiajun <[email protected]>
---
catalogs/hive-metastore-common/build.gradle.kts | 1 +
.../gravitino/hive/client/HiveClientFactory.java | 27 ++++++++--
.../gravitino/hive/kerberos/KerberosClient.java | 13 +++++
.../TestHive2HMSWithKerberosNoImpersonation.java | 59 ++++++++++++++++++++++
4 files changed, 95 insertions(+), 5 deletions(-)
diff --git a/catalogs/hive-metastore-common/build.gradle.kts
b/catalogs/hive-metastore-common/build.gradle.kts
index 9e2c387363..1479f18e96 100644
--- a/catalogs/hive-metastore-common/build.gradle.kts
+++ b/catalogs/hive-metastore-common/build.gradle.kts
@@ -132,6 +132,7 @@ dependencies {
testImplementation(libs.caffeine)
testImplementation(libs.junit.jupiter.api)
testImplementation(libs.mockito.core)
+ testImplementation(libs.mockito.inline)
testImplementation(libs.testcontainers)
testImplementation(libs.woodstox.core)
diff --git
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/HiveClientFactory.java
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/HiveClientFactory.java
index c80c2590ce..3a2a80898d 100644
---
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/HiveClientFactory.java
+++
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/HiveClientFactory.java
@@ -27,6 +27,7 @@ import static
org.apache.gravitino.hive.client.Util.updateConfigurationFromPrope
import com.google.common.base.Preconditions;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
+import java.security.PrivilegedExceptionAction;
import java.util.Properties;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.apache.gravitino.exceptions.GravitinoRuntimeException;
@@ -115,10 +116,13 @@ public final class HiveClientFactory {
public HiveClient createHiveClientWithBackend() {
HiveClient client = null;
HiveClientClassLoader classloader = null;
+ // Use HiveClientFactory's own ClassLoader as baseLoader so that shared
classes
+ // (e.g. HiveClient interface, UserGroupInformation) are resolved
consistently
+ // regardless of which thread calls this method (TCCL is not stable across
threads).
+ ClassLoader factoryCl = HiveClientFactory.class.getClassLoader();
try {
// Try using Hive3 first
- classloader =
- HiveClientClassLoader.createLoader(HIVE3,
Thread.currentThread().getContextClassLoader());
+ classloader = HiveClientClassLoader.createLoader(HIVE3, factoryCl);
client = createHiveClientInternal(classloader);
client.getCatalogs();
LOG.info("Connected to Hive Metastore using Hive version HIVE3");
@@ -137,10 +141,9 @@ public final class HiveClientFactory {
// Fallback to Hive2 if we can list databases
if (e.getMessage().contains("Invalid method name: 'get_catalogs'")
|| e.getMessage().contains("class not found") // caused by
MiniHiveMetastoreService
+ || e.getMessage().contains("Cannot find Hive jar directory") //
HIVE3 libs dir absent
) {
- classloader =
- HiveClientClassLoader.createLoader(
- HIVE2, Thread.currentThread().getContextClassLoader());
+ classloader = HiveClientClassLoader.createLoader(HIVE2, factoryCl);
client = createHiveClientInternal(classloader);
LOG.info("Connected to Hive Metastore using Hive version HIVE2");
backendClassLoader = classloader;
@@ -162,6 +165,9 @@ public final class HiveClientFactory {
HiveClientClassLoader.HiveVersion version, Properties properties,
ClassLoader classloader)
throws Exception {
Class<?> hiveClientImplClass =
classloader.loadClass(HiveClientImpl.class.getName());
+ // HiveVersion is a shared class (isSharedClass covers all
org.apache.gravitino.* classes),
+ // so the isolated classloader delegates to the base classloader and both
sides hold the
+ // same Class object. We can pass HiveVersion.class directly to
getConstructor().
Constructor<?> hiveClientImplCtor =
hiveClientImplClass.getConstructor(
HiveClientClassLoader.HiveVersion.class, Properties.class);
@@ -202,6 +208,17 @@ public final class HiveClientFactory {
return createProxyHiveClientImpl(
classloader.getHiveVersion(), properties, ugi, classloader);
+ } else if (enableKerberos) {
+ // UGI is a shared class (org.apache.hadoop.* delegated to
baseLoader), so the system CL
+ // and HiveClientClassLoader share the same UGI static state. The TGT
is already stored in
+ // realLoginUgi.subject by kerberosClient.login(). The only thing
needed is to bind that
+ // Subject to the current thread so GSSAPI can find the TGT during the
HMS Thrift handshake.
+ // UGI.doAs() wraps Subject.doAs() internally — same pattern as
ImpalaEngineAdapter.
+ UserGroupInformation realUgi = kerberosClient.getRealLoginUgi();
+ final HiveClientClassLoader.HiveVersion hiveVersion =
classloader.getHiveVersion();
+ return realUgi.doAs(
+ (PrivilegedExceptionAction<HiveClient>)
+ () -> createHiveClientImpl(hiveVersion, properties,
classloader));
} else {
return createHiveClientImpl(classloader.getHiveVersion(), properties,
classloader);
}
diff --git
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/KerberosClient.java
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/KerberosClient.java
index cb115048e4..a86535184a 100644
---
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/KerberosClient.java
+++
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/KerberosClient.java
@@ -198,4 +198,17 @@ public class KerberosClient implements java.io.Closeable {
public void setHiveClient(HiveClient client) {
this.hiveClient = client;
}
+
+ /**
+ * Returns the real (non-proxy) {@link UserGroupInformation} that was
obtained after Kerberos
+ * login via keytab. Used by callers that need to bind the JAAS Subject to
the current thread
+ * (e.g., via {@code ugi.doAs(...)}) before performing a Kerberos-protected
RPC call.
+ *
+ * @return the real login UGI.
+ * @throws IllegalStateException if {@link #login()} has not been called yet.
+ */
+ public UserGroupInformation getRealLoginUgi() {
+ Preconditions.checkState(realLoginUgi != null, "KerberosClient.login() has
not been called");
+ return realLoginUgi;
+ }
}
diff --git
a/catalogs/hive-metastore-common/src/test/java/org/apache/gravitino/hive/client/TestHive2HMSWithKerberosNoImpersonation.java
b/catalogs/hive-metastore-common/src/test/java/org/apache/gravitino/hive/client/TestHive2HMSWithKerberosNoImpersonation.java
new file mode 100644
index 0000000000..2e12c966b3
--- /dev/null
+++
b/catalogs/hive-metastore-common/src/test/java/org/apache/gravitino/hive/client/TestHive2HMSWithKerberosNoImpersonation.java
@@ -0,0 +1,59 @@
+/*
+ * 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.gravitino.hive.client;
+
+import java.util.Properties;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.TestInstance;
+
+/**
+ * Integration test for Kerberos-enabled Hive2 HMS with impersonation
<b>disabled</b>.
+ *
+ * <p>This test covers the fix for the non-impersonation Kerberos path in
{@link HiveClientFactory}:
+ * the path was missing a {@code realUgi.doAs()} wrapper, causing GSSAPI to
fail with "No valid
+ * credentials provided" because the JAAS Subject (containing the TGT) was
never bound to the
+ * current thread.
+ *
+ * <p>The existing {@link TestHive2HMSWithKerberos} only tests {@code
impersonation=true}. This
+ * class reuses the same Docker KDC infrastructure but overrides {@link
#createHiveProperties()} to
+ * set {@code authentication.impersonation-enable=false}, exercising the
previously broken code
+ * path. {@link TestHive2HMSWithKerberos#startHiveContainer()} calls {@link
#createHiveProperties()}
+ * via polymorphism, so no {@code startHiveContainer} override is needed here.
+ *
+ * <p>Requires Docker; run with {@code -PskipDockerTests=false}.
+ */
+@Tag("gravitino-docker-test")
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class TestHive2HMSWithKerberosNoImpersonation extends
TestHive2HMSWithKerberos {
+
+ /**
+ * Override to disable impersonation. All other Kerberos properties
(principal, keytab, sasl) are
+ * inherited from {@link TestHive2HMSWithKerberos#createHiveProperties()}.
+ *
+ * <p>Setting {@code authentication.impersonation-enable=false} forces
{@link HiveClientFactory}
+ * to take the non-impersonation Kerberos branch in {@code
createHiveClientInternal()}.
+ */
+ @Override
+ protected Properties createHiveProperties() {
+ Properties properties = super.createHiveProperties();
+ properties.setProperty("authentication.impersonation-enable", "false");
+ return properties;
+ }
+}