regarmukesh3g opened a new pull request, #23349:
URL: https://github.com/apache/kafka/pull/23349

   ## Problem
   
   `GroupsCommand.execute()` calls `Exit.exit(exitCode)` in a `finally` block:
   
   ```java
   } catch (Throwable t) {
       printException(t);
       exitCode = 1;
   } finally {
       Exit.exit(exitCode);
   }
   ```
   
   Every other tool in this package (`FeatureCommand`, 
`DelegationTokenCommand`, `MetadataQuorumCommand`, `GetOffsetShell`, 
`LeaderElectionCommand`) calls `Exit.exit` exactly once, from `main()`, and 
lets `execute()` propagate failures to `mainNoExit()`. `GroupsCommand` is the 
only one that exits from inside `execute()`.
   
   This has two consequences:
   
   - **Under the default exit procedure**, `execute()` terminates the JVM 
directly, so `mainNoExit()` never returns and the exit code it computes is 
unreachable.
   - **Under a replaced exit procedure** (for example the `MockExitProcedure` 
used in tests, which records the code and returns), `execute()` swallows the 
exception and returns normally, so `mainNoExit()` reports **success with code 0 
for a command that actually failed**.
   
   The `mainNoExit()` / `execute()` pair exists precisely so the exit code can 
be observed without killing the JVM, and this defeats it.
   
   ## Fix
   
   Remove the `Exit.exit()` call and the `exitCode` bookkeeping from 
`execute()`, letting the exception propagate so `mainNoExit()` maps it to a 
non-zero code and `main()` passes that to `Exit.exit()`. `ExecutionException` 
is still unwrapped so the underlying cause is what surfaces to the user. Error 
reporting in `mainNoExit()` now goes through `printException()` so the message 
and stack trace are rendered the same way on both paths.
   
   ## Testing
   
   Added two tests covering `mainNoExit()` and `execute()` on the failure path, 
which had no coverage — the gap that let this go unnoticed. Both fail before 
this change:
   
   ```
   testMainNoExitReturnsNonZeroWithoutExitingOnFailure() :: expected: <1> but 
was: <0>
   testExecuteDoesNotExitOnFailure() :: Expected java.lang.Exception to be 
thrown, but nothing was thrown.
   ```
   
   and pass after it. The tests make the admin client creation *inside* the 
`try` block fail (via an invalid security protocol in the command config), so 
they exercise the affected path rather than failing earlier during option 
parsing.
   
   `GroupsCommandTest` passes in full (33 tests), as do 
`LeaderElectionCommandTest` and `TopicCommandTest` (146 tests total). 
`./gradlew :tools:checkstyleMain :tools:checkstyleTest :tools:spotlessCheck` is 
clean.
   
   ### Committer Checklist (excluded from commit message)
   - [ ] Verify design and implementation
   - [ ] Verify test coverage and CI build status
   - [ ] Verify documentation (including upgrade notes)
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   
   https://claude.ai/code/session_01GvbzZgK7eH63iPswf8tV6v


-- 
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]

Reply via email to