Skip to content

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

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

pkg/shell: Preserve error types in shell command execution#1780
DaliborKr wants to merge 1 commit into
containers: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 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

DaliborKr added a commit to DaliborKr/toolbox that referenced this pull request Apr 24, 2026
Add the --arch and --arch-emulator-path flags to the init-container
command, passed from the create command when creating a
cross-architecture container. The --arch flag defaults to the host
architecture ID so that existing native containers continue to work
without changes.

When the container's architecture differs from the host, the
init-container entry point configures QEMU emulation inside the
container before any foreign-architecture binaries can run:

1. Validate QEMU emulation by running the 'true' command, which fails
with ENOEXEC if the host's binfmt_misc registration is not working
(detected via RunWithExitCode2() added in [1]), because it is necessary
to have host emulation working to emulate the binfmt_misc registration
in the following step.

2. Mount a fresh binfmt_misc filesystem inside the container via
MountBinfmtMisc() (added in [2]) to create a sandboxed binfmt_misc
registration with the C flag.

3. Validate architecture support via IsArchSupportedOnInitialization()
(added in [3]), which verifies the QEMU interpreter at the host-mounted
path under /run/host.

4. Register the QEMU interpreter with the C flag via
RegisterBinfmtMisc() (added and explained in [2])

The binfmt_misc registration is performed inside the container rather
than relying on the host's registration, as explained in [2].

Update showEntryPointLog() in run.go to propagate lines prefixed with
'Warning:' to stderr on the host, instead of treating them as errors.
This is needed because the cross-architecture initialization may emit
warnings that should be visible to the user but are not fatal.

[1] containers#1780
[2] containers#1782
[3] containers#1783

containers#1788

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
DaliborKr added a commit to DaliborKr/toolbox that referenced this pull request Apr 24, 2026
Add the --arch and --arch-emulator-path flags to the init-container
command, passed from the create command when creating a
cross-architecture container. The --arch flag defaults to the host
architecture ID so that existing native containers continue to work
without changes.

When the container's architecture differs from the host, the
init-container entry point configures QEMU emulation inside the
container before any foreign-architecture binaries can run:

1. Validate QEMU emulation by running the 'true' command, which fails
with ENOEXEC if the host's binfmt_misc registration is not working
(detected via RunWithExitCode2() added in [1]), because it is necessary
to have host emulation working to emulate the binfmt_misc registration
in the following step.

2. Mount a fresh binfmt_misc filesystem inside the container via
MountBinfmtMisc() (added in [2]) to create a sandboxed binfmt_misc
registration with the C flag.

3. Validate architecture support via IsArchSupportedOnInitialization()
(added in [3]), which verifies the QEMU interpreter at the host-mounted
path under /run/host.

4. Register the QEMU interpreter with the C flag via
RegisterBinfmtMisc() (added and explained in [2])

The binfmt_misc registration is performed inside the container rather
than relying on the host's registration, as explained in [2].

Update showEntryPointLog() in run.go to propagate lines prefixed with
'Warning:' to stderr on the host, instead of treating them as errors.
This is needed because the cross-architecture initialization may emit
warnings that should be visible to the user but are not fatal.

[1] containers#1780
[2] containers#1782
[3] containers#1783

containers#1788

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
DaliborKr added a commit to DaliborKr/toolbox that referenced this pull request Apr 24, 2026
Add the --arch and --arch-emulator-path flags to the init-container
command, passed from the create command when creating a
cross-architecture container. The --arch flag defaults to the host
architecture ID so that existing native containers continue to work
without changes.

When the container's architecture differs from the host, the
init-container entry point configures QEMU emulation inside the
container before any foreign-architecture binaries can run:

1. Validate QEMU emulation by running the 'true' command, which fails
with ENOEXEC if the host's binfmt_misc registration is not working
(detected via RunWithExitCode2() added in [1]), because it is necessary
to have host emulation working to emulate the binfmt_misc registration
in the following step.

2. Mount a fresh binfmt_misc filesystem inside the container via
MountBinfmtMisc() (added in [2]) to create a sandboxed binfmt_misc
registration with the C flag.

3. Validate architecture support via IsArchSupportedOnInitialization()
(added in [3]), which verifies the QEMU interpreter at the host-mounted
path under /run/host.

4. Register the QEMU interpreter with the C flag via
RegisterBinfmtMisc() (added and explained in [2])

The binfmt_misc registration is performed inside the container rather
than relying on the host's registration, as explained in [2].

Update showEntryPointLog() in run.go to propagate lines prefixed with
'Warning:' to stderr on the host, instead of treating them as errors.
This is needed because the cross-architecture initialization may emit
warnings that should be visible to the user but are not fatal.

[1] containers#1780
[2] containers#1782
[3] containers#1783

containers#1788

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
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