Skip to content

pkg/shell: Preserve error types in shell command execution#1780

Open
DaliborKr wants to merge 1 commit intocontainers:mainfrom
DaliborKr:pr01-shell-error-types
Open

pkg/shell: Preserve error types in shell command execution#1780
DaliborKr wants to merge 1 commit intocontainers:mainfrom
DaliborKr:pr01-shell-error-types

Conversation

@DaliborKr
Copy link
Copy Markdown
Collaborator

@DaliborKr DaliborKr commented Apr 22, 2026

This is PR 1/10 in a series adding cross-architecture container support using QEMU and binfmt_misc.

Summary

  • Add RunContextWithExitCode2() and RunWithExitCode2() to the shell package
  • These variants preserve original error types (exec.ErrNotFound, exec.ExitError, syscall.ENOEXEC, etc.) instead of wrapping them in generic messages
  • Allows its callers to use errors.Is() and errors.As() to handle specific failure modes
  • Needed by cross-architecture code to distinguish "binary not found" from "binary failed"

These new functions are meant to replace its original versions in the future.

@DaliborKr DaliborKr requested a review from debarshiray as a code owner April 22, 2026 20:35
The existing RunContextWithExitCode() wraps all errors from
exec.Command in generic "failed to invoke" messages, which prevents
callers from distinguishing between actual error types.

Add RunContextWithExitCode2() and RunWithExitCode2() that return
errors with their original types intact, including for ExitError.
This allows callers to use errors.Is() and errors.As() to handle
specific failure modes. For example, detecting a missing skopeo binary
(exec.ErrNotFound) or an ENOEXEC error from inside non native
containers, when an emulator is not set correctly.

These new functions are meant to replace its original versions in the
future.

containers#1780

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
@DaliborKr DaliborKr force-pushed the pr01-shell-error-types branch from 588461c to d28cb8c Compare April 22, 2026 20:40
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 introduces RunContextWithExitCode2 and RunWithExitCode2 to the shell package. The feedback suggests refactoring the code to reduce duplication between the new and existing functions, as well as simplifying the error handling logic to be more idiomatic by removing redundant variables.

Comment thread src/pkg/shell/shell.go
Comment on lines +84 to +117
func RunContextWithExitCode2(ctx context.Context,
name string,
stdin io.Reader,
stdout, stderr io.Writer,
arg ...string) (int, error) {

logLevel := logrus.GetLevel()
if stderr == nil && logLevel >= logrus.DebugLevel {
stderr = os.Stderr
}

cmd := exec.CommandContext(ctx, name, arg...)
cmd.Stdin = stdin
cmd.Stdout = stdout
cmd.Stderr = stderr

if err := cmd.Run(); err != nil {
exitCode := 1

if ctxErr := ctx.Err(); ctxErr != nil {
return 1, ctxErr
}

var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
exitCode = exitErr.ExitCode()
return exitCode, err
}

return exitCode, err
}

return 0, nil
}
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 implementation of RunContextWithExitCode2 duplicates much of the logic found in RunContextWithExitCode, specifically the command initialization and logging setup. Consider refactoring these common steps into a private helper function to improve maintainability and reduce code duplication.

Comment thread src/pkg/shell/shell.go
Comment on lines +101 to +113
exitCode := 1

if ctxErr := ctx.Err(); ctxErr != nil {
return 1, ctxErr
}

var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
exitCode = exitErr.ExitCode()
return exitCode, err
}

return exitCode, err
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 error handling logic can be simplified by removing the redundant exitCode variable and returning values directly. This makes the function more idiomatic and easier to read.

		if ctxErr := ctx.Err(); ctxErr != nil {
			return 1, ctxErr
		}

		var exitErr *exec.ExitError
		if errors.As(err, &exitErr) {
			return exitErr.ExitCode(), err
		}

		return 1, err

@softwarefactory-project-zuul
Copy link
Copy Markdown

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant