This is an automated email from the ASF dual-hosted git repository.

chia7712 pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/kafka.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 09713a38fbc KAFKA-19749 The `version-mapping` of kafka-features.sh 
should work without requiring the bootstrap (#20619)
09713a38fbc is described below

commit 09713a38fbce0a1486749292071a1d926c7fa7f4
Author: Chang-Yu Huang <[email protected]>
AuthorDate: Sat Dec 20 07:56:25 2025 -0500

    KAFKA-19749 The `version-mapping` of kafka-features.sh should work without 
requiring the bootstrap (#20619)
    
    # Description
    In kafka-features.sh, exclusive argument group of `--bootstrap-server`
    and `--bootstrap-controller` is required in the main parser, but
    `version-mapping` and `feature-dependencies` subcommands don't need the
    information.
    
    # Changes
    To avoid change in argument level, the exclusive group is not moved. It
    is now not required and only check for existence if the subcommand needs
    it.
    
    Since `FeatureCommand#mainNoExit` catches `ArgumentParserException`,
    `ArgumentParser#parseArgs` is used instead of `parseArgsOrFail`. It
    makes testing easier.
    
    Two tests are added. One tests whether not-remote subcommand can execute
    without bootstrap. Another tests whether remote subcommand cannot
    execute without bootstrap.
    
    Reviewers: Ken Huang <[email protected]>, Jhen-Yung Hsu
     <[email protected]>, TaiJuWu <[email protected]>, Chia-Ping Tsai
     <[email protected]>
---
 .../org/apache/kafka/tools/FeatureCommand.java     | 29 ++++++++++++++--------
 .../org/apache/kafka/tools/FeatureCommandTest.java | 16 ++++++++++++
 2 files changed, 34 insertions(+), 11 deletions(-)

diff --git a/tools/src/main/java/org/apache/kafka/tools/FeatureCommand.java 
b/tools/src/main/java/org/apache/kafka/tools/FeatureCommand.java
index 6c16dd0db43..fdcaf5e537f 100644
--- a/tools/src/main/java/org/apache/kafka/tools/FeatureCommand.java
+++ b/tools/src/main/java/org/apache/kafka/tools/FeatureCommand.java
@@ -77,7 +77,7 @@ public class FeatureCommand {
                 .newArgumentParser("kafka-features")
                 .defaultHelp(true)
                 .description("This tool manages feature flags in Kafka.");
-        MutuallyExclusiveGroup bootstrapGroup = 
parser.addMutuallyExclusiveGroup().required(true);
+        MutuallyExclusiveGroup bootstrapGroup = 
parser.addMutuallyExclusiveGroup();
         bootstrapGroup.addArgument("--bootstrap-server")
                 .help("A comma-separated list of host:port pairs to use for 
establishing the connection to the Kafka cluster.");
         bootstrapGroup.addArgument("--bootstrap-controller")
@@ -93,14 +93,27 @@ public class FeatureCommand {
         addVersionMappingParser(subparsers);
         addFeatureDependenciesParser(subparsers);
 
-        Namespace namespace = parser.parseArgsOrFail(args);
+        Namespace namespace = parser.parseArgs(args);
         String command = namespace.getString("command");
+        if (command.equals("version-mapping")) {
+            handleVersionMapping(namespace, Feature.PRODUCTION_FEATURES);
+            return;
+        } else if (command.equals("feature-dependencies")) {
+            handleFeatureDependencies(namespace, Feature.PRODUCTION_FEATURES);
+            return;
+        }
         String configPath = namespace.getString("command_config");
         Properties properties = (configPath == null) ? new Properties() : 
Utils.loadProps(configPath);
 
-        CommandLineUtils.initializeBootstrapProperties(properties,
-            Optional.ofNullable(namespace.getString("bootstrap_server")),
-            Optional.ofNullable(namespace.getString("bootstrap_controller")));
+        try {
+            CommandLineUtils.initializeBootstrapProperties(properties,
+                    
Optional.ofNullable(namespace.getString("bootstrap_server")),
+                    
Optional.ofNullable(namespace.getString("bootstrap_controller")));
+        } catch (Exception e) {
+            // bootstrap_server and bootstrap_controller are in a mutually 
exclusive group,
+            // so the exception happens only when both of them are missing
+            throw new ArgumentParserException(e.getMessage(), parser);
+        }
 
         try (Admin adminClient = Admin.create(properties)) {
             switch (command) {
@@ -116,12 +129,6 @@ public class FeatureCommand {
                 case "disable":
                     handleDisable(namespace, adminClient);
                     break;
-                case "version-mapping":
-                    handleVersionMapping(namespace, 
Feature.PRODUCTION_FEATURES);
-                    break;
-                case "feature-dependencies":
-                    handleFeatureDependencies(namespace, 
Feature.PRODUCTION_FEATURES);
-                    break;
                 default:
                     throw new TerseException("Unknown command " + command);
             }
diff --git a/tools/src/test/java/org/apache/kafka/tools/FeatureCommandTest.java 
b/tools/src/test/java/org/apache/kafka/tools/FeatureCommandTest.java
index b079f377b8c..ff6350693b4 100644
--- a/tools/src/test/java/org/apache/kafka/tools/FeatureCommandTest.java
+++ b/tools/src/test/java/org/apache/kafka/tools/FeatureCommandTest.java
@@ -691,6 +691,22 @@ public class FeatureCommandTest {
             MetadataVersion.MINIMUM_VERSION, MetadataVersion.latestTesting()), 
exception2.getMessage());
     }
 
+    @Test
+    public void testHandleVersionMappingWithoutBootstrap() {
+        Map.Entry<String, String> output = 
ToolsTestUtils.grabConsoleOutputAndError(() -> 
FeatureCommand.mainNoExit("version-mapping"));
+        assertEquals("", output.getValue());
+        MetadataVersion metadataVersion = MetadataVersion.latestProduction();
+        assertTrue(output.getKey().contains("metadata.version=" + 
metadataVersion.featureLevel() + " (" + metadataVersion.version() + ")"),
+                "Output did not contain expected Metadata Version: " + 
output.getKey());
+    }
+
+    @Test
+    public void testHandleRemoteCommandWithoutBootstrap() {
+        String errorMessage = ToolsTestUtils.grabConsoleError(() -> 
FeatureCommand.mainNoExit("upgrade"));
+        assertTrue(errorMessage.contains("You must specify either 
--bootstrap-controller " +
+                "or --bootstrap-server."));
+    }
+
     @Test
     public void testHandleFeatureDependenciesForFeatureWithDependencies() {
         Map<String, Object> namespace = new HashMap<>();

Reply via email to