Skip to content

fix(karmadactl): return error from DeleteConfirmation instead of calling os.Exit#7517

Open
D3S-Gaurav wants to merge 1 commit into
karmada-io:masterfrom
D3S-Gaurav:fix/delete-confirmation-error-handling
Open

fix(karmadactl): return error from DeleteConfirmation instead of calling os.Exit#7517
D3S-Gaurav wants to merge 1 commit into
karmada-io:masterfrom
D3S-Gaurav:fix/delete-confirmation-error-handling

Conversation

@D3S-Gaurav
Copy link
Copy Markdown

What type of PR is this?
/kind bug
/kind cleanup

What this PR does / why we need it:
DeleteConfirmation() in pkg/karmadactl/util/deleteconfirmation.go called
os.Exit(1) when fmt.Scanln failed — in CI, piped stdin, or any
non-interactive environment. That's the wrong tool for a library function: callers
can't catch it, tests can't survive it, and the user gets no context about what
went wrong or which command triggered it.

Changes in this PR:

  • Signature changes from func DeleteConfirmation() bool to
    func DeleteConfirmation() (bool, error) so callers can handle stdin failures
    instead of dying
  • os.Exit(1) is replaced with return false, fmt.Errorf("failed to read user confirmation: %w", err)
  • The recursive default: return DeleteConfirmation() retry is replaced with a
    for loop; it also now prints "invalid input, please type (y)es or (n)o" so
    users aren't left wondering why nothing happened
  • Both callers (deinit/deinit.go and addons/init/disable_option.go) are
    updated to handle the (bool, error) return and propagate failures through
    cobra's RunE
  • deleteconfirmation_test.go is new: 8 table-driven tests covering y/n/yes/no,
    uppercase variants, empty stdin (the previously unkillable error path), and an
    invalid-then-valid retry sequence

Which issue(s) this PR fixes:
Fixes #7516

Note for reviewer:
The stdin error path had no test coverage before this change because hitting it
called os.Exit(1), which killed the test binary. The new tests use os.Pipe()
to swap out stdin — no subprocess, no PTY needed.

A follow-up worth considering (out of scope here): inject io.Reader and
io.Writer into DeleteConfirmation() to remove the dependency on os.Stdin
and os.Stdout entirely.

Does this PR introduce a user-facing change?:

karmadactl: `deinit` and `addons disable` no longer exit the process silently
when stdin is unavailable (e.g. in CI or piped usage). They now return an error
that cobra surfaces to the user. Passing `--force` remains the supported way to
skip confirmation in non-interactive environments.

Copilot AI review requested due to automatic review settings May 17, 2026 10:33
@karmada-bot karmada-bot added kind/bug Categorizes issue or PR as related to a bug. kind/cleanup Categorizes issue or PR as related to cleaning up code, process, or technical debt. labels May 17, 2026
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request improves the robustness of the karmadactl utility by replacing abrupt process termination with proper error handling. By updating the DeleteConfirmation function to return errors instead of calling os.Exit, the application can now gracefully handle non-interactive environments like CI pipelines. Callers have been updated to propagate these errors, and new unit tests ensure the confirmation logic behaves correctly under various input conditions.

Highlights

  • Improved Error Handling: Replaced os.Exit(1) in DeleteConfirmation with error propagation, allowing callers to handle stdin failures gracefully.
  • Refactored Logic: Updated DeleteConfirmation to use an iterative loop instead of recursion and added user feedback for invalid inputs.
  • Added Test Coverage: Introduced comprehensive table-driven tests in deleteconfirmation_test.go using os.Pipe to simulate stdin scenarios.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@karmada-bot
Copy link
Copy Markdown
Contributor

Welcome @D3S-Gaurav! It looks like this is your first PR to karmada-io/karmada 🎉

@karmada-bot karmada-bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label May 17, 2026
@karmada-bot
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign chaunceyjiang for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the DeleteConfirmation utility to return an error instead of exiting the process and replaces its recursive logic with an iterative loop. Corresponding updates were made to the callers in the addons and deinit commands to handle the new signature, and unit tests were added. Feedback suggests using bufio.Scanner instead of fmt.Scanln to more robustly handle interactive user input, such as empty lines or multiple words.

Comment on lines 19 to 45
import (
"fmt"
"os"
"strings"
)

// DeleteConfirmation delete karmada resource confirmation
func DeleteConfirmation() bool {
fmt.Print("Please type (y)es or (n)o and then press enter:")
var response string
_, err := fmt.Scanln(&response)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// DeleteConfirmation prompts the user for a yes/no confirmation.
// It returns true if the user confirms with "y" or "yes", false on "n" or "no",
// and an error if stdin cannot be read (e.g. non-interactive environment).
func DeleteConfirmation() (bool, error) {
for {
fmt.Print("Please type (y)es or (n)o and then press enter:")
var response string
_, err := fmt.Scanln(&response)
if err != nil {
return false, fmt.Errorf("failed to read user confirmation: %w", err)
}

switch strings.ToLower(response) {
case "y", "yes":
return true
case "n", "no":
return false
default:
return DeleteConfirmation()
switch strings.ToLower(response) {
case "y", "yes":
return true, nil
case "n", "no":
return false, nil
default:
fmt.Println("invalid input, please type (y)es or (n)o")
}
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation using fmt.Scanln is fragile for interactive CLI usage. It returns an error (such as "unexpected newline") if the user simply presses Enter, which causes the command to terminate immediately instead of re-prompting. It also fails if the user enters multiple words.

Using bufio.Scanner to read the entire line is more robust. It allows handling empty lines gracefully by continuing the loop and ensures that any extra input on the line is consumed.

import (
	"bufio"
	"fmt"
	"os"
	"strings"
)

// DeleteConfirmation prompts the user for a yes/no confirmation.
// It returns true if the user confirms with "y" or "yes", false on "n" or "no",
// and an error if stdin cannot be read (e.g. non-interactive environment).
func DeleteConfirmation() (bool, error) {
	scanner := bufio.NewScanner(os.Stdin)
	for {
		fmt.Print("Please type (y)es or (n)o and then press enter: ")
		if !scanner.Scan() {
			if err := scanner.Err(); err != nil {
				return false, fmt.Errorf("failed to read user confirmation: %w", err)
			}
			return false, fmt.Errorf("failed to read user confirmation: unexpected EOF")
		}

		response := strings.TrimSpace(scanner.Text())
		switch strings.ToLower(response) {
		case "y", "yes":
			return true, nil
		case "n", "no":
			return false, nil
		case "":
			continue
		default:
			fmt.Println("invalid input, please type (y)es or (n)o")
		}
	}
}

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates karmadactl’s delete confirmation helper to stop terminating the entire process on stdin read failures, and instead return an error so commands can surface a proper cobra error (improving CI/non-interactive behavior and testability).

Changes:

  • Change DeleteConfirmation signature from func() bool to func() (bool, error) and replace os.Exit(1) with an error return.
  • Replace recursive retry with an iterative for loop and add an explicit invalid-input message.
  • Update deinit and addons disable call sites to handle and propagate confirmation errors; add new table-driven tests.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
pkg/karmadactl/util/deleteconfirmation.go Return (bool, error) instead of exiting; loop-based prompting with invalid-input feedback.
pkg/karmadactl/util/deleteconfirmation_test.go New tests using os.Pipe() to simulate stdin and cover multiple input variants.
pkg/karmadactl/deinit/deinit.go Update caller to handle (bool, error) and propagate errors.
pkg/karmadactl/addons/init/disable_option.go Update caller to handle (bool, error) and propagate errors.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +29 to +34
fmt.Print("Please type (y)es or (n)o and then press enter:")
var response string
_, err := fmt.Scanln(&response)
if err != nil {
return false, fmt.Errorf("failed to read user confirmation: %w", err)
}
Comment on lines +89 to +103
{
name: "DeleteConfirmation_WithEmptyInput_ReturnsError",
input: "",
wantOk: false,
wantErr: true,
},
{
// Exercises the retry loop: first input is unrecognized,
// second input is valid. Relies on the iterative for-loop
// rather than recursion.
name: "DeleteConfirmation_WithInvalidThenYes_ReturnsTrue",
input: "maybe\nyes\n",
wantOk: true,
wantErr: false,
},
@@ -68,8 +68,14 @@ func (o *CommandAddonsDisableOption) Validate(args []string) error {
// Run start disable Karmada addons
func (o *CommandAddonsDisableOption) Run(args []string) error {
fmt.Printf("Disable Karmada addon %s\n", args)
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 17, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 53.84615% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 41.99%. Comparing base (2c918de) to head (0db0afb).

Files with missing lines Patch % Lines
pkg/karmadactl/addons/init/disable_option.go 0.00% 6 Missing ⚠️
pkg/karmadactl/deinit/deinit.go 0.00% 5 Missing and 1 partial ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #7517      +/-   ##
==========================================
- Coverage   41.99%   41.99%   -0.01%     
==========================================
  Files         879      879              
  Lines       54436    54444       +8     
==========================================
+ Hits        22863    22865       +2     
- Misses      29849    29851       +2     
- Partials     1724     1728       +4     
Flag Coverage Δ
unittests 41.99% <53.84%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…ing os.Exit

Signed-off-by: D3S-Gaurav <kumargauravrocco2724@gmail.com>
@D3S-Gaurav D3S-Gaurav force-pushed the fix/delete-confirmation-error-handling branch from 711293a to 0db0afb Compare May 17, 2026 10:51
@D3S-Gaurav
Copy link
Copy Markdown
Author

Hi @chaosi-zju,
I wanted to kindly follow up on this PR in case it’s pending review. This is my first contribution here, and I’d appreciate any feedback whenever convenient. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/bug Categorizes issue or PR as related to a bug. kind/cleanup Categorizes issue or PR as related to cleaning up code, process, or technical debt. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(karmadactl/util): stop calling os.Exit in DeleteConfirmation, return error instead

4 participants