bib: detect missing qemu-user early
This commit checks early if cross architecture building support via `qemu-user-static` (or similar tooling) is missing and errors in a more user friendly way. Note that there is no integration test right now because testing this for real requires mutating the very global state of `echo 0 > /proc/sys/fs/binfmt_misc/qemu-aarch64` which would make the test non-parallelizable and even risks failing other cross-arch tests running on the same host (because binfmt-misc is not namespaced (yet)).
This commit is contained in:
parent
b3ef264353
commit
4fa4ad34a0
3 changed files with 100 additions and 1 deletions
3
bib/internal/setup/export_test.go
Normal file
3
bib/internal/setup/export_test.go
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
package setup
|
||||
|
||||
var ValidateCanRunTargetArch = validateCanRunTargetArch
|
||||
|
|
@ -3,10 +3,14 @@ package setup
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/osbuild/bootc-image-builder/bib/internal/podmanutil"
|
||||
"github.com/osbuild/bootc-image-builder/bib/internal/util"
|
||||
)
|
||||
|
|
@ -76,7 +80,7 @@ func EnsureEnvironment(storePath string) error {
|
|||
|
||||
// Validate checks that the environment is supported (e.g. caller set up the
|
||||
// container correctly)
|
||||
func Validate() error {
|
||||
func Validate(targetArch string) error {
|
||||
isRootless, err := podmanutil.IsRootless()
|
||||
if err != nil {
|
||||
return fmt.Errorf("checking rootless: %w", err)
|
||||
|
|
@ -95,6 +99,11 @@ func Validate() error {
|
|||
return fmt.Errorf("this command requires a privileged container")
|
||||
}
|
||||
|
||||
// Try to run the cross arch binary
|
||||
if err := validateCanRunTargetArch(targetArch); err != nil {
|
||||
return fmt.Errorf("cannot run binary in target arch: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -115,3 +124,27 @@ func ValidateHasContainerStorageMounted() error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateCanRunTargetArch(targetArch string) error {
|
||||
if targetArch == runtime.GOARCH || targetArch == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
canaryCmd := fmt.Sprintf("bib-canary-%s", targetArch)
|
||||
if _, err := exec.LookPath(canaryCmd); err != nil {
|
||||
// we could error here but in principle with a working qemu-user
|
||||
// any arch should work so let's just warn. the common case
|
||||
// (arm64/amd64) is covered properly
|
||||
logrus.Warningf("cannot check architecture support for %v: no canary binary found", targetArch)
|
||||
return nil
|
||||
}
|
||||
output, err := exec.Command(canaryCmd).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot run canary binary for %q, do you have 'qemu-user-static' installed?\n%s", targetArch, err)
|
||||
}
|
||||
if string(output) != "ok\n" {
|
||||
return fmt.Errorf("internal error: unexpected output from cross-architecture canary: %q", string(output))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
63
bib/internal/setup/setup_test.go
Normal file
63
bib/internal/setup/setup_test.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package setup_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/osbuild/bootc-image-builder/bib/internal/setup"
|
||||
)
|
||||
|
||||
func TestValidateCanRunTargetArchTrivial(t *testing.T) {
|
||||
for _, arch := range []string{runtime.GOARCH, ""} {
|
||||
err := setup.ValidateCanRunTargetArch(arch)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateCanRunTargetArchUnsupportedCanary(t *testing.T) {
|
||||
var logbuf bytes.Buffer
|
||||
logrus.SetOutput(&logbuf)
|
||||
|
||||
err := setup.ValidateCanRunTargetArch("unsupported-arch")
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, logbuf.String(), `level=warning msg="cannot check architecture support for unsupported-arch: no canary binary found"`)
|
||||
}
|
||||
|
||||
func makeFakeCanary(t *testing.T, content string) {
|
||||
tmpdir := t.TempDir()
|
||||
t.Setenv("PATH", os.Getenv("PATH")+":"+tmpdir)
|
||||
err := os.WriteFile(filepath.Join(tmpdir, "bib-canary-fakearch"), []byte(content), 0755)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestValidateCanRunTargetArchHappy(t *testing.T) {
|
||||
var logbuf bytes.Buffer
|
||||
logrus.SetOutput(&logbuf)
|
||||
|
||||
makeFakeCanary(t, "#!/bin/sh\necho ok")
|
||||
|
||||
err := setup.ValidateCanRunTargetArch("fakearch")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "", logbuf.String())
|
||||
}
|
||||
|
||||
func TestValidateCanRunTargetArchExecFormatError(t *testing.T) {
|
||||
makeFakeCanary(t, "")
|
||||
|
||||
err := setup.ValidateCanRunTargetArch("fakearch")
|
||||
assert.ErrorContains(t, err, `cannot run canary binary for "fakearch", do you have 'qemu-user-static' installed?`)
|
||||
assert.ErrorContains(t, err, `: exec format error`)
|
||||
}
|
||||
|
||||
func TestValidateCanRunTargetArchUnexpectedOutput(t *testing.T) {
|
||||
makeFakeCanary(t, "#!/bin/sh\necho xxx")
|
||||
|
||||
err := setup.ValidateCanRunTargetArch("fakearch")
|
||||
assert.ErrorContains(t, err, `internal error: unexpected output`)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue