github-actions[bot] commented on code in PR #65644:
URL: https://github.com/apache/doris/pull/65644#discussion_r3592314622
##########
fe/fe-core/src/main/java/org/apache/doris/connector/ConnectorPluginManager.java:
##########
@@ -75,6 +79,7 @@ public void loadBuiltins() {
ServiceLoader.load(ConnectorProvider.class)
.forEach(p -> {
providers.add(p);
+
PluginRegistry.getInstance().registerBuiltin(PLUGIN_FAMILY, p);
Review Comment:
[P2] Isolate the new metadata callback before publishing the provider.
registerBuiltin() invokes the overridable description() here without a
per-provider catch, after providers.add(p). One throwing implementation can
therefore abort FE startup/discovery or leave an active provider with no
inventory row; the external loader already treats this callback as a per-plugin
failure and cleans up. Please snapshot metadata under the same failure boundary
before mutating runtime state, then either reject that provider cleanly or
deliberately fall back to empty metadata.
##########
fe/fe-extension-loader/src/main/java/org/apache/doris/extension/loader/DirectoryPluginRuntimeManager.java:
##########
@@ -265,22 +270,62 @@ private PluginHandle<F> loadFromPluginDir(Path pluginDir,
ClassLoader parent, Cl
"Failed to get plugin name from discovered factory in " +
normalizedDir,
e);
}
- if (pluginName == null || pluginName.trim().isEmpty()) {
+ String nameValidationError = PluginNames.validate(pluginName);
Review Comment:
[P1] Preserve the existing external-plugin name contract. Before this change
the loader accepted every nonblank name and stored its trimmed value, while the
public SPI/guide only require a stable nonempty identity. This validator now
rejects deployed names such as acme/auth:v2 (and all non-ASCII names or names
over 64 chars) during FE startup, even though this PR is adding inventory
metadata. Please keep the existing acceptance contract, or introduce this
restriction with an explicit compatibility/migration plan.
##########
fe/fe-authentication/fe-authentication-handler/src/main/java/org/apache/doris/authentication/handler/AuthenticationPluginManager.java:
##########
@@ -88,7 +92,10 @@ public
AuthenticationPluginManager(DirectoryPluginRuntimeManager<AuthenticationP
: ClassLoadingPolicy.defaultPolicy();
ServiceLoader.load(AuthenticationPluginFactory.class)
- .forEach(factory -> factories.put(factory.name(), factory));
+ .forEach(factory -> {
+ factories.put(factory.name(), factory);
Review Comment:
[P1] Make runtime selection and inventory publication use one canonical
decision. This map uses the raw name and last-wins put, while PluginRegistry
trims/validates the name and keeps the first value; its boolean result is
ignored. With two ServiceLoader factories named ldap, getFactory() executes the
second but information_schema.plugins describes the first. A name rejected by
the registry can likewise remain executable yet absent from the table. Please
normalize/validate once and apply the same precedence atomically to both stores
(and the parallel family managers).
##########
fe/fe-extension-loader/src/test/java/org/apache/doris/extension/loader/PluginRegistryTest.java:
##########
@@ -0,0 +1,145 @@
+// 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.doris.extension.loader;
+
+import org.apache.doris.extension.loader.PluginRegistry.PluginRecord;
+import org.apache.doris.extension.loader.PluginRegistry.PluginSource;
+import org.apache.doris.extension.spi.Plugin;
+import org.apache.doris.extension.spi.PluginFactory;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+class PluginRegistryTest {
+
+ @BeforeEach
+ void setUp() {
+ PluginRegistry.getInstance().clearForTest();
Review Comment:
[P2] Anchoring here because the production registry has no textual diff:
keep PluginRegistry.java textual. Its key separator is a literal NUL byte, so
Git classifies the entire new 197-line source as binary, normal text diffs hide
it, and ordinary text search cannot inspect it. The current compile checks
pass, but the repository/tooling regression remains. Please express the runtime
separator with a Java escape (or use a structured key) so the corrected file
appears as a normal text diff.
##########
regression-test/suites/query_p0/schema_table/test_plugins_schema.groovy:
##########
@@ -0,0 +1,63 @@
+// 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.
+
+import org.junit.Assert
+
+suite("test_plugins_schema", "p0") {
+ // Schema check: fixed five columns.
+ def schema = sql "DESC information_schema.plugins"
+ def columnNames = schema.collect { it[0] }
+ Assert.assertEquals(
+ ["PLUGIN_NAME", "PLUGIN_TYPE", "PLUGIN_VERSION", "SOURCE",
"DESCRIPTION"], columnNames)
+
+ // As an admin user: built-in plugins (e.g. filesystem providers) must be
listed.
+ def rows = sql """
+ SELECT PLUGIN_NAME, PLUGIN_TYPE, SOURCE
+ FROM information_schema.plugins
+ ORDER BY PLUGIN_TYPE, PLUGIN_NAME
+ """
+ Assert.assertTrue("expect at least one built-in plugin row", rows.size() >
0)
+ rows.each { row ->
+ Assert.assertTrue(row[2] == "BUILTIN" || row[2] == "EXTERNAL")
+ }
+ def builtinRows = rows.findAll { it[2] == "BUILTIN" }
+ Assert.assertTrue("expect built-in plugins registered", builtinRows.size()
> 0)
Review Comment:
[P1] Do not require a BUILTIN row in the standard packaged cluster. FE core
removes filesystem implementations from its runtime classpath (and
authentication's password provider is test-scope); build.sh deploys
filesystem/connector implementations under output/fe/plugins, and these
managers register those rows as EXTERNAL. A correctly populated default
inventory can therefore have zero BUILTIN rows and fail here. Please assert the
packaged external inventory (or inject a controlled built-in in a unit test).
Also convert deterministic results to qt_/order_qt_, generate the .out, and
include PLUGIN_VERSION so the advertised NULL is covered.
##########
fe/fe-extension-loader/src/main/java/org/apache/doris/extension/loader/DirectoryPluginRuntimeManager.java:
##########
@@ -265,22 +270,62 @@ private PluginHandle<F> loadFromPluginDir(Path pluginDir,
ClassLoader parent, Cl
"Failed to get plugin name from discovered factory in " +
normalizedDir,
e);
}
- if (pluginName == null || pluginName.trim().isEmpty()) {
+ String nameValidationError = PluginNames.validate(pluginName);
+ if (nameValidationError != null) {
closeClassLoader(classLoader);
throw new PluginLoadException(
normalizedDir,
LoadFailure.STAGE_INSTANTIATE,
- "Plugin name is empty for directory: " + normalizedDir,
+ "Invalid plugin name for directory " + normalizedDir + ":
" + nameValidationError,
null);
}
+ String description;
+ try {
+ description = factory.description();
+ } catch (RuntimeException e) {
+ closeClassLoader(classLoader);
+ throw new PluginLoadException(
+ normalizedDir,
+ LoadFailure.STAGE_INSTANTIATE,
+ "Failed to get plugin description from discovered factory
in " + normalizedDir,
+ e);
+ }
+
+ String implementationVersion = readImplementationVersion(
Review Comment:
[P2] Look up the version in the jar that actually defined the factory.
Discovery only parses the class name from root-jar service descriptors, then
the runtime classloader can load that class from root plus lib jars. With a
root descriptor jar and the implementation in lib/plugin-impl.jar, loading
succeeds but this root-only search returns NULL. Please use the loaded class's
code source or scan all runtime jars in classloader order, and cover the split
layout.
##########
fe/fe-core/src/main/java/org/apache/doris/tablefunction/MetadataGenerator.java:
##########
@@ -795,6 +809,38 @@ private static TFetchSchemaTableDataResult
authenticationIntegrationsMetadataRes
return result;
}
+ private static TFetchSchemaTableDataResult
pluginsMetadataResult(TSchemaTableRequestParams params) {
+ if (!params.isSetCurrentUserIdent()) {
+ return errorResult("current user ident is not set.");
+ }
+ UserIdentity currentUserIdentity =
UserIdentity.fromThrift(params.getCurrentUserIdent());
+ TFetchSchemaTableDataResult result = new TFetchSchemaTableDataResult();
+ List<TRow> dataBatch = Lists.newArrayList();
+ result.setDataBatch(dataBatch);
+ result.setStatus(new TStatus(TStatusCode.OK));
+ // The plugin inventory reveals which security/storage components this
cluster
+ // runs; restrict it to ADMIN like SHOW PLUGINS.
+ if
(!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(currentUserIdentity,
PrivPredicate.ADMIN)) {
+ return result;
+ }
+
+ // Registry rows are load-time snapshots of the current FE; no plugin
code runs here.
+ for (PluginRegistry.PluginRecord record :
PluginRegistry.getInstance().list()) {
+ TRow row = new TRow();
+ row.addToColumnValue(new TCell().setStringVal(record.getName()));
+ row.addToColumnValue(new TCell().setStringVal(record.getType()));
+ if (record.getVersion() == null) {
+ row.addToColumnValue(new TCell());
Review Comment:
[P1] Preserve SQL NULL end to end for unavailable versions. An unset TCell
reaches SchemaScanner::insert_block_column(), whose VARCHAR branch inserts the
default empty string and always appends false to the null map, so
PLUGIN_VERSION IS NULL is false and the advertised NULL becomes ''. Please send
the explicit null marker and make this scanner/helper insert a nullable default
when it is set, with a result-level test for a versionless built-in.
##########
fe/fe-core/src/main/java/org/apache/doris/connector/ConnectorPluginManager.java:
##########
@@ -104,13 +109,30 @@ public void loadPlugins(List<Path> pluginRoots) {
}
for (PluginHandle<ConnectorProvider> handle : report.getSuccesses()) {
+ // Built-ins (and earlier-loaded plugins) must never be displaced
by a
+ // same-name directory jar.
+ if (hasProviderNamed(handle.getPluginName())) {
Review Comment:
[P2] Dispose the handle rejected by this family-level conflict. loadAll()
has already inserted this success into runtimeManager.handlesByName, so
continue leaves the factory and its URLClassLoader strongly retained and the
jar resources open for the FE lifetime; FileSystemPluginManager has the same
path. Please reject before publication or add an atomic remove-and-close
operation. Closing alone is insufficient while the handle remains in the
runtime map.
--
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]