github-actions[bot] commented on code in PR #66890:
URL: https://github.com/apache/doris/pull/66890#discussion_r3804998145


##########
fe/fe-connector/fe-connector-paimon/src/main/java/org/apache/doris/connector/paimon/PaimonConnector.java:
##########
@@ -489,14 +512,78 @@ private Catalog createCatalogFromContext(CatalogContext 
catalogContext, String f
         ClassLoader previous = Thread.currentThread().getContextClassLoader();
         try {
             
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
-            return context.executeAuthenticated(() -> 
CatalogFactory.createCatalog(catalogContext));
+            return context.executeAuthenticated(() -> {
+                Catalog catalog = PaimonCatalogProperties.HMS.equals(flavor)
+                        ? createHmsCatalog(catalogContext, hmsAuth, 
catalogProps.getRaw(),
+                                storageHadoopConfig)
+                        : CatalogFactory.createCatalog(catalogContext);
+                return catalog;
+            });
         } catch (Exception e) {
             throw new RuntimeException(failureMessage + " (flavor=" + flavor + 
"): " + e.getMessage(), e);
         } finally {
             Thread.currentThread().setContextClassLoader(previous);
         }
     }
 
+    static Catalog createHmsCatalog(CatalogContext catalogContext, 
HadoopAuthenticator hmsAuth,
+            Map<String, String> properties, Map<String, String> 
storageHadoopConfig) {
+        HiveConf hiveConf = HiveCatalog.createHiveConf(catalogContext);
+        Options options = catalogContext.options();
+        String warehouse = options.get(CatalogOptions.WAREHOUSE);
+        if (warehouse == null) {
+            warehouse = 
hiveConf.get(HiveConf.ConfVars.METASTOREWAREHOUSE.varname,
+                    HiveConf.ConfVars.METASTOREWAREHOUSE.defaultStrVal);
+        }
+        Path warehousePath = new Path(warehouse);
+        Path fileIoPath = warehousePath.toUri().getScheme() == null
+                ? new Path(FileSystem.getDefaultUri(hiveConf)) : warehousePath;
+        try {
+            FileIO fileIO = FileIO.get(fileIoPath, catalogContext);
+            // Paimon checks or creates the warehouse eagerly; it must retain 
the outer storage identity.
+            fileIO.checkOrMkdirs(warehousePath);
+            String clientClass = 
options.get(HiveCatalogOptions.METASTORE_CLIENT_CLASS);
+            Catalog catalog = hmsAuth == null
+                    ? new HiveCatalog(fileIO, hiveConf, clientClass, options, 
warehousePath.toUri().toString())
+                    : hmsAuth.doAs(() -> new HiveCatalog(
+                            fileIO, hiveConf, clientClass, options, 
warehousePath.toUri().toString()));
+            catalog = PaimonHmsClientPool.install(catalog, hmsAuth);

Review Comment:
   **[P1] Compute Paimon format-table owners under the HMS identity**
   
   Installing the boundary only on `HiveCatalog.clients` is too late for 
`type=format-table`. In Paimon 1.3.1, `createFormatTable` evaluates 
`createHiveFormatTable(...)` before `clients.execute(...)`, and `newHmsTable` 
captures `UserGroupInformation.getCurrentUser()` at that earlier point, under 
Doris's outer storage/process identity. The subsequent create RPC is 
authenticated correctly, but the persisted owner is wrong. Normal Paimon tables 
are safe because `createHiveTable(...)` is evaluated inside the client-pool 
action. Please scope the format-table owner computation to the HMS UGI without 
moving `initialTableLocation`/FileIO work, and add a mixed-identity real 
format-table test that checks both owner and storage UGI.



##########
fe/fe-connector/fe-connector-hms/src/main/java/org/apache/doris/connector/hms/HmsConfHelper.java:
##########
@@ -58,21 +82,95 @@ public static HiveConf createHiveConf(Map<String, String> 
properties) {
         // (fixes SecurityUtil.<clinit>) but cannot fix this conf-cached CL. 
Pinning here keeps the whole
         // hive-metastore class graph in one loader.
         hiveConf.setClassLoader(HmsConfHelper.class.getClassLoader());
+        addConfResources(hiveConf, confResources);
         for (Map.Entry<String, String> entry : properties.entrySet()) {
+            // A blank username was ignored by the legacy copy-if-present 
path; preserving a resource value (or
+            // the "hadoop" default) avoids createRemoteUser("") failing 
before the first HMS RPC.
+            if ("hadoop.username".equals(entry.getKey()) && 
isBlank(entry.getValue())) {
+                continue;
+            }
             hiveConf.set(entry.getKey(), entry.getValue());
         }
         // A kerberized HMS requires SASL transport on the metastore Thrift 
connection. The legacy fe-core
         // HMSBaseProperties.initHadoopAuthenticator auto-enabled 
hive.metastore.sasl.enabled whenever the
         // metastore/hadoop auth was kerberos; preserve that here so a catalog 
that only declares kerberos auth
         // (without an explicit hive.metastore.sasl.enabled) still negotiates 
SASL, instead of opening a plain
         // TSocket that a kerberized metastore drops with TTransportException.
-        if 
("kerberos".equalsIgnoreCase(properties.get("hadoop.security.authentication"))
-                || 
"kerberos".equalsIgnoreCase(properties.get("hive.metastore.authentication.type")))
 {
+        String hmsAuthType = 
properties.get("hive.metastore.authentication.type");
+        boolean explicitSimple = "simple".equalsIgnoreCase(hmsAuthType);
+        if (explicitSimple) {
+            // The explicit HMS mode is authoritative even when a base 
hive-site.xml enables SASL.
+            hiveConf.set("hive.metastore.sasl.enabled", "false");
+        } else if ("kerberos".equalsIgnoreCase(hmsAuthType)
+                || (!explicitSimple
+                        && 
"kerberos".equalsIgnoreCase(properties.get("hadoop.security.authentication")))) 
{
             hiveConf.set("hive.metastore.sasl.enabled", "true");
         }
         return hiveConf;
     }
 
+    /**
+     * Creates the lightweight Hadoop configuration used only for UGI 
resolution.
+     */
+    public static Configuration createHadoopConfWithResources(String 
confResources,
+            Map<String, String> properties) {
+        Configuration conf = new Configuration();
+        conf.setClassLoader(HmsConfHelper.class.getClassLoader());
+        addConfResources(conf, confResources);
+        for (Map.Entry<String, String> entry : properties.entrySet()) {
+            if ("hadoop.username".equals(entry.getKey()) && 
isBlank(entry.getValue())) {
+                continue;
+            }
+            conf.set(entry.getKey(), entry.getValue());
+        }
+        return conf;
+    }
+
+    /**
+     * Preserves connector-agnostic passthrough keys while applying canonical 
HMS overrides last.
+     */
+    public static Map<String, String> mergeCatalogProperties(Map<String, 
String> raw,
+            Map<String, String> overrides) {
+        Map<String, String> merged = new LinkedHashMap<>(raw);
+        // Canonical parsing deliberately omits a blank username; remove the 
raw value so it cannot reappear
+        // merely because the HMS client also preserves unrelated custom 
configuration keys.
+        if (isBlank(merged.get("hadoop.username"))) {
+            merged.remove("hadoop.username");
+        }
+        merged.putAll(overrides);
+        return merged;
+    }
+
+    private static void addConfResources(Configuration conf, String 
confResources) {
+        if (isBlank(confResources)) {
+            return;
+        }
+        String baseDir = resolveHadoopConfigDir();
+        for (String resource : confResources.split(",")) {
+            File file = new File(baseDir, resource.trim());
+            if (!file.isFile()) {
+                throw new IllegalArgumentException("Config resource file does 
not exist: " + file);
+            }
+            conf.addResource(new Path(file.toURI()));
+        }
+    }
+
+    private static String resolveHadoopConfigDir() {
+        String configured = System.getProperty("doris.hadoop.config.dir");

Review Comment:
   **[P1] Initialize the custom Hadoop config directory before loading 
resources**
   
   `resolveHadoopConfigDir()` can run before the engine publishes 
`Config.hadoop_config_dir`: the only production writer of 
`doris.hadoop.config.dir` is the lazy 
`FileSystemFactory.bindAllStorageProperties` path. Hive builds its HMS client 
without touching storage, and Hudi evaluates `getOrCreateClient()` before 
`storageHadoopConfig(context)`, so on a clean FE with a non-default 
`hadoop_config_dir` the first `hive.conf.resources=hive-site.xml` access looks 
under `$DORIS_HOME/plugins/hadoop_conf` and fails; it starts working only after 
an unrelated storage/Iceberg/Paimon path initializes the bridge. The new tests 
pre-set the system property and miss this ordering. Please initialize/pass the 
configured directory deterministically before resource lookup and cover 
first-access Hive and Hudi with the property initially unset.



##########
fe/fe-connector/fe-connector-iceberg/src/main/java/org/apache/doris/connector/iceberg/IcebergConnector.java:
##########
@@ -969,8 +976,43 @@ private Catalog createCatalog() {
 
         LOG.info("Creating Iceberg catalog '{}' flavor='{}' impl='{}'",
                 catalogName, flavor, 
catalogOptions.get(CatalogProperties.CATALOG_IMPL));
-        return buildCatalogAuthenticated(flavor,
-                () -> CatalogUtil.buildIcebergCatalog(catalogName, 
catalogOptions, conf));
+        return buildCatalogAuthenticated(flavor, () -> {
+            if (!IcebergCatalogProperties.TYPE_HMS.equals(flavor)) {
+                return CatalogUtil.buildIcebergCatalog(catalogName, 
catalogOptions, conf);
+            }
+            HadoopAuthenticator hmsAuth = buildHmsAuthenticator(properties, 
storageHadoopConfig);
+            applyHmsTableOwner(catalogOptions, hmsAuth);
+            Catalog catalog = CatalogUtil.buildIcebergCatalog(catalogName, 
catalogOptions, conf);
+            return IcebergHmsClientPool.install(catalog, hmsAuth);
+        });
+    }
+
+    static String appendHmsCacheKeys(String existing) {
+        String keys = appendCacheKey(existing, "conf:hadoop.username");
+        keys = appendCacheKey(keys, "conf:hive.metastore.client.principal");
+        return appendCacheKey(keys, "conf:hadoop.kerberos.principal");

Review Comment:
   **[P1] Include the HMS transport settings in static pool keys**
   
   These keys still omit both `hive.metastore.kerberos.principal` and the 
effective `hive.metastore.sasl.enabled`, even though the first inner 
`HiveClientPool` captures those values in its HiveConf. Iceberg's and Paimon's 
caches are JVM-static, so a same-URI `ALTER CATALOG` that corrects or rotates 
only the service principal reuses the old target. Likewise, explicit SIMPLE 
over Kerberos storage and the storage-Kerberos HMS fallback can have identical 
current key values while requiring plain versus SASL transports, so whichever 
pool is created first wins. This is separate from the client-UGI and 
case-suffix fixes. Please append the exact canonical 
`conf:hive.metastore.kerberos.principal` and `conf:hive.metastore.sasl.enabled` 
keys here and in Paimon, and test same-URI rebuilds for both target-principal 
and false-vs-true SASL changes.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to