From 73942de69ad8f7afb8c45bc5496797d26cc9b6c6 Mon Sep 17 00:00:00 2001 From: Michael Smithhisler Date: Tue, 2 Dec 2025 10:59:07 -0500 Subject: [PATCH 01/10] qemu: add emulator allowlist to driver config and add emulator validation --- drivers/qemu/driver.go | 39 +++++++++++++++++++++++++++++++++-- drivers/qemu/driver_test.go | 41 +++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/drivers/qemu/driver.go b/drivers/qemu/driver.go index 4b81c3de2ed..268ebe9e95c 100644 --- a/drivers/qemu/driver.go +++ b/drivers/qemu/driver.go @@ -83,8 +83,10 @@ var ( // configSpec is the hcl specification returned by the ConfigSchema RPC configSpec = hclspec.NewObject(map[string]*hclspec.Spec{ - "image_paths": hclspec.NewAttr("image_paths", "list(string)", false), - "args_allowlist": hclspec.NewAttr("args_allowlist", "list(string)", false), + "image_paths": hclspec.NewAttr("image_paths", "list(string)", false), + "args_allowlist": hclspec.NewAttr("args_allowlist", "list(string)", false), + "emulator_allowlist": hclspec.NewAttr("emulator_allowlist", "list(string)", false), + "fingerprint_emulator": hclspec.NewAttr("fingerprint_emulator", "string", false), }) // taskConfigSpec is the hcl specification for the driver config section of @@ -150,6 +152,11 @@ type Config struct { // prevent access to devices ArgsAllowList []string `codec:"args_allowlist"` + // EmulatorAllowList is an allow-list of emulator binaries the + // jobspec and FingerprintEmulator can use, so that cluster + // operators can control which emulators job authors can use. + EmulatorAllowList []string `codec:"emulator_allowlist"` + // FingerprintEmulator specifies which QEMU binary is used // for fingerprinting FingerprintEmulator string `codec:"fingerprint_emulator"` @@ -251,6 +258,13 @@ func (d *Driver) buildFingerprint() *drivers.Fingerprint { if d.config.FingerprintEmulator != "" { fpEmulator = d.config.FingerprintEmulator } + + if err := validateEmulator(fpEmulator, d.config.EmulatorAllowList); err != nil { + fingerprint.Health = drivers.HealthStateUndetected + fingerprint.HealthDescription = fmt.Sprintf("Fingerprint emulator is invalid: %v", err) + return fingerprint + } + outBytes, err := exec.Command(fpEmulator, "--version").Output() if err != nil { // return no error, as it isn't an error to not find qemu, it just means we @@ -382,6 +396,23 @@ func isAllowedDriveInterface(driveInterface string) bool { return false } +func validateEmulator(emulator string, allowedEmulators []string) error { + if len(allowedEmulators) > 0 { + if !slices.Contains(allowedEmulators, emulator) { + return fmt.Errorf("emulator '%s' is not an allowed emulator", emulator) + } + } else { + match, err := regexp.MatchString("qemu-system-*", emulator) + if err != nil { + return err + } + if !match { + return fmt.Errorf("emulator '%s' is not valid", emulator) + } + } + return nil +} + // validateArgs ensures that all QEMU command line params are in the // allowlist. This function must be called after all interpolation has // taken place. @@ -419,6 +450,10 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive handle := drivers.NewTaskHandle(taskHandleVersion) handle.Config = cfg + if err := validateEmulator(driverConfig.Emulator, d.config.EmulatorAllowList); err != nil { + return nil, nil, err + } + if err := validateArgs(d.config.ArgsAllowList, driverConfig.Args); err != nil { return nil, nil, err } diff --git a/drivers/qemu/driver_test.go b/drivers/qemu/driver_test.go index 936898cfebe..8b25eff25be 100644 --- a/drivers/qemu/driver_test.go +++ b/drivers/qemu/driver_test.go @@ -5,6 +5,7 @@ package qemu import ( "context" + "errors" "io" "os" "path/filepath" @@ -289,6 +290,46 @@ config { must.Eq(t, expected, tc) } +func TestValidateEmulator(t *testing.T) { + testcases := []struct { + name string + validEmulators []string + requestedEmulator string + exp error + }{ + { + name: "empty valid emulators, valid request", + validEmulators: nil, + requestedEmulator: "qemu-system-x86_64", + exp: nil, + }, + { + name: "empty valid emulators, invalid request", + validEmulators: nil, + requestedEmulator: "some-binary", + exp: errors.New("emulator 'some-binary' is not valid"), + }, + { + name: "non-empty valid emulators, valid request", + validEmulators: []string{"qemu-system-x86_64"}, + requestedEmulator: "qemu-system-x86_64", + exp: nil, + }, + { + name: "non-empty valid emulators, invalid request", + validEmulators: []string{"qemu-system-x86_64"}, + requestedEmulator: "qemu-system-aarch64", + exp: errors.New("emulator 'qemu-system-aarch64' is not an allowed emulator"), + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + must.Eq(t, validateEmulator(tc.requestedEmulator, tc.validEmulators), tc.exp) + }) + } +} + func TestIsAllowedDriveInterface(t *testing.T) { validInterfaces := []string{"ide", "scsi", "sd", "mtd", "floppy", "pflash", "virtio", "none"} invalidInterfaces := []string{"foo", "virtio-foo"} From 190f906a1dd1401db6465fbd51a924cf45a638bb Mon Sep 17 00:00:00 2001 From: Michael Smithhisler Date: Tue, 2 Dec 2025 11:05:57 -0500 Subject: [PATCH 02/10] add changelog --- .changelog/27182.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/27182.txt diff --git a/.changelog/27182.txt b/.changelog/27182.txt new file mode 100644 index 00000000000..67549118dad --- /dev/null +++ b/.changelog/27182.txt @@ -0,0 +1,3 @@ +```release-note:improvement +qemu: adds an emulator allowlist to qemu plugin config +``` From 128f250dbdc79184ac4c7308056c40409cbf79b1 Mon Sep 17 00:00:00 2001 From: Michael Smithhisler Date: Tue, 2 Dec 2025 13:52:36 -0500 Subject: [PATCH 03/10] fingerprint all available emulators or those defined in the allowlist This change removed the fingerprint emulator config parameter and instead fingerprints all available emulators or those defined in the allowlist, and adds them to the drivers attribute, allowing placement contraints if nodes have different emulators installed. --- drivers/qemu/driver.go | 95 ++++++++++++++++++++++++------------- drivers/qemu/driver_test.go | 70 +++++++++++++++++++++------ 2 files changed, 117 insertions(+), 48 deletions(-) diff --git a/drivers/qemu/driver.go b/drivers/qemu/driver.go index 268ebe9e95c..0b391468a65 100644 --- a/drivers/qemu/driver.go +++ b/drivers/qemu/driver.go @@ -38,8 +38,9 @@ const ( fingerprintPeriod = 30 * time.Second // The key populated in Node Attributes to indicate presence of the Qemu driver - driverAttr = "driver.qemu" - driverVersionAttr = "driver.qemu.version" + driverAttr = "driver.qemu" + driverVersionAttr = "driver.qemu.version" + driverEmulatorsAttr = "driver.qemu.emulators" // Represents an ACPI shutdown request to the VM (emulates pressing a physical power button) // Reference: https://en.wikibooks.org/wiki/QEMU/Monitor @@ -83,10 +84,9 @@ var ( // configSpec is the hcl specification returned by the ConfigSchema RPC configSpec = hclspec.NewObject(map[string]*hclspec.Spec{ - "image_paths": hclspec.NewAttr("image_paths", "list(string)", false), - "args_allowlist": hclspec.NewAttr("args_allowlist", "list(string)", false), - "emulator_allowlist": hclspec.NewAttr("emulator_allowlist", "list(string)", false), - "fingerprint_emulator": hclspec.NewAttr("fingerprint_emulator", "string", false), + "image_paths": hclspec.NewAttr("image_paths", "list(string)", false), + "args_allowlist": hclspec.NewAttr("args_allowlist", "list(string)", false), + "emulators_allowlist": hclspec.NewAttr("emulators_allowlist", "list(string)", false), }) // taskConfigSpec is the hcl specification for the driver config section of @@ -152,14 +152,10 @@ type Config struct { // prevent access to devices ArgsAllowList []string `codec:"args_allowlist"` - // EmulatorAllowList is an allow-list of emulator binaries the + // EmulatorsAllowList is an allow-list of emulator binaries the // jobspec and FingerprintEmulator can use, so that cluster // operators can control which emulators job authors can use. - EmulatorAllowList []string `codec:"emulator_allowlist"` - - // FingerprintEmulator specifies which QEMU binary is used - // for fingerprinting - FingerprintEmulator string `codec:"fingerprint_emulator"` + EmulatorsAllowList []string `codec:"emulators_allowlist"` } // Driver is a driver for running images via Qemu @@ -254,23 +250,20 @@ func (d *Driver) buildFingerprint() *drivers.Fingerprint { HealthDescription: drivers.DriverHealthy, } - fpEmulator := "qemu-system-x86_64" - if d.config.FingerprintEmulator != "" { - fpEmulator = d.config.FingerprintEmulator - } + emulators := findEmulators(d.config.EmulatorsAllowList) - if err := validateEmulator(fpEmulator, d.config.EmulatorAllowList); err != nil { + if len(emulators) == 0 { fingerprint.Health = drivers.HealthStateUndetected - fingerprint.HealthDescription = fmt.Sprintf("Fingerprint emulator is invalid: %v", err) + fingerprint.HealthDescription = "" return fingerprint } - outBytes, err := exec.Command(fpEmulator, "--version").Output() + // Just fetch the version of the first emulator. If a system has many emulators, it can take a while + // to get the version of each one, and is likely a waste of compute. + outBytes, err := exec.Command(fmt.Sprintf("qemu-system-%s", emulators[0]), "--version").Output() if err != nil { - // return no error, as it isn't an error to not find qemu, it just means we - // can't use it. fingerprint.Health = drivers.HealthStateUndetected - fingerprint.HealthDescription = "" + fingerprint.HealthDescription = fmt.Sprintf("Failed to execute qemu binary: %v", err) return fingerprint } out := strings.TrimSpace(string(outBytes)) @@ -282,8 +275,11 @@ func (d *Driver) buildFingerprint() *drivers.Fingerprint { return fingerprint } currentQemuVersion := matches[1] - fingerprint.Attributes[driverAttr] = pstructs.NewBoolAttribute(true) + fingerprint.Attributes[driverVersionAttr] = pstructs.NewStringAttribute(currentQemuVersion) + fingerprint.Attributes[driverAttr] = pstructs.NewBoolAttribute(true) + fingerprint.Attributes[driverEmulatorsAttr] = pstructs.NewStringAttribute(strings.Join(emulators, ",")) + return fingerprint } @@ -358,6 +354,45 @@ func (d *Driver) RecoverTask(handle *drivers.TaskHandle) error { return nil } +func findEmulators(allowList []string) []string { + var ( + glob string = "qemu-system-*" + emulators []string + bins []string + err error + ) + + pathEnv := os.Getenv("PATH") + dirs := filepath.SplitList(pathEnv) + + for _, dir := range dirs { + fullPattern := filepath.Join(dir, glob) + bins, err = filepath.Glob(fullPattern) + if err != nil { + continue + } + + // once the qemu binaries are found, break + if len(bins) > 0 { + break + } + } + + for _, f := range bins { + em := strings.TrimPrefix(filepath.Base(f), "qemu-system-") + + if len(allowList) > 0 { + if slices.Contains(allowList, em) { + emulators = append(emulators, em) + } + } else { + emulators = append(emulators, em) + } + } + + return emulators +} + func isAllowedImagePath(allowedPaths []string, allocDir, imagePath string) bool { if !filepath.IsAbs(imagePath) { imagePath = filepath.Join(allocDir, imagePath) @@ -401,14 +436,6 @@ func validateEmulator(emulator string, allowedEmulators []string) error { if !slices.Contains(allowedEmulators, emulator) { return fmt.Errorf("emulator '%s' is not an allowed emulator", emulator) } - } else { - match, err := regexp.MatchString("qemu-system-*", emulator) - if err != nil { - return err - } - if !match { - return fmt.Errorf("emulator '%s' is not valid", emulator) - } } return nil } @@ -450,7 +477,7 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive handle := drivers.NewTaskHandle(taskHandleVersion) handle.Config = cfg - if err := validateEmulator(driverConfig.Emulator, d.config.EmulatorAllowList); err != nil { + if err := validateEmulator(driverConfig.Emulator, d.config.EmulatorsAllowList); err != nil { return nil, nil, err } @@ -471,7 +498,7 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive // Parse configuration arguments // Create the base arguments - emulator := "qemu-system-x86_64" + emulator := "x86_64" if driverConfig.Emulator != "" { emulator = driverConfig.Emulator @@ -491,7 +518,7 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive } mem := fmt.Sprintf("%dM", mb) - absPath, err := GetAbsolutePath(emulator) + absPath, err := GetAbsolutePath(fmt.Sprintf("qemu-system-%s", emulator)) if err != nil { return nil, nil, err } diff --git a/drivers/qemu/driver_test.go b/drivers/qemu/driver_test.go index 8b25eff25be..3cbdd4511a5 100644 --- a/drivers/qemu/driver_test.go +++ b/drivers/qemu/driver_test.go @@ -9,6 +9,7 @@ import ( "io" "os" "path/filepath" + "strings" "testing" "time" @@ -239,22 +240,63 @@ func TestQemuDriver_Fingerprint(t *testing.T) { ctestutil.QemuCompatible(t) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + t.Run("fingerpints all emulators", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() - d := NewQemuDriver(ctx, testlog.HCLogger(t)) - harness := dtestutil.NewDriverHarness(t, d) + d := NewQemuDriver(ctx, testlog.HCLogger(t)) + harness := dtestutil.NewDriverHarness(t, d) - fingerCh, err := harness.Fingerprint(context.Background()) - must.NoError(t, err) - select { - case finger := <-fingerCh: - must.Eq(t, drivers.HealthStateHealthy, finger.Health) - ok, _ := finger.Attributes["driver.qemu"].GetBool() - must.True(t, ok) - case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): - t.Fatal("timeout receiving fingerprint") - } + fingerCh, err := harness.Fingerprint(context.Background()) + must.NoError(t, err) + select { + case finger := <-fingerCh: + must.Eq(t, drivers.HealthStateHealthy, finger.Health) + ok, _ := finger.Attributes["driver.qemu"].GetBool() + must.True(t, ok) + + emulators, _ := finger.Attributes[driverEmulatorAttr].GetString() + // Don't want to hardcode the exact amount of emulators installed by qemu-system + // but it's definitely more than 5 + must.True(t, len(strings.Split(emulators, ",")) > 5) + case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): + t.Fatal("timeout receiving fingerprint") + } + }) + + t.Run("fingerprints only allowed emulators", func(t *testing.T) { + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + allowedEms := []string{"x86_64", "aarch64"} + d := NewQemuDriver(ctx, testlog.HCLogger(t)) + config := &Config{ + EmulatorsAllowList: allowedEms, + } + + var data []byte + must.NoError(t, base.MsgPackEncode(&data, config)) + baseConfig := &base.Config{ + PluginConfig: data, + } + harness := dtestutil.NewDriverHarness(t, d) + harness.SetConfig(baseConfig) + + fingerCh, err := harness.Fingerprint(context.Background()) + must.NoError(t, err) + select { + case finger := <-fingerCh: + must.Eq(t, drivers.HealthStateHealthy, finger.Health) + ok, _ := finger.Attributes[driverAttr].GetBool() + must.True(t, ok) + + emulators, _ := finger.Attributes[driverEmulatorAttr].GetString() + must.SliceContainsAll(t, allowedEms, strings.Split(emulators, ",")) + case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): + t.Fatal("timeout receiving fingerprint") + } + }) } func TestConfig_ParseAllHCL(t *testing.T) { From bcbf9dd9e13d86f3b0c5b3e4981ffbfa87c6d023 Mon Sep 17 00:00:00 2001 From: Michael Smithhisler Date: Tue, 2 Dec 2025 13:56:34 -0500 Subject: [PATCH 04/10] fix build --- drivers/qemu/driver_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/qemu/driver_test.go b/drivers/qemu/driver_test.go index 3cbdd4511a5..b65a96009c3 100644 --- a/drivers/qemu/driver_test.go +++ b/drivers/qemu/driver_test.go @@ -255,7 +255,7 @@ func TestQemuDriver_Fingerprint(t *testing.T) { ok, _ := finger.Attributes["driver.qemu"].GetBool() must.True(t, ok) - emulators, _ := finger.Attributes[driverEmulatorAttr].GetString() + emulators, _ := finger.Attributes[driverEmulatorsAttr].GetString() // Don't want to hardcode the exact amount of emulators installed by qemu-system // but it's definitely more than 5 must.True(t, len(strings.Split(emulators, ",")) > 5) @@ -291,7 +291,7 @@ func TestQemuDriver_Fingerprint(t *testing.T) { ok, _ := finger.Attributes[driverAttr].GetBool() must.True(t, ok) - emulators, _ := finger.Attributes[driverEmulatorAttr].GetString() + emulators, _ := finger.Attributes[driverEmulatorsAttr].GetString() must.SliceContainsAll(t, allowedEms, strings.Split(emulators, ",")) case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): t.Fatal("timeout receiving fingerprint") From 54a9ebace6c881706bf2c999b8b8bb6e8c5016bb Mon Sep 17 00:00:00 2001 From: Michael Smithhisler Date: Tue, 2 Dec 2025 14:15:51 -0500 Subject: [PATCH 05/10] fix validateEmulators test --- drivers/qemu/driver.go | 2 +- drivers/qemu/driver_test.go | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/qemu/driver.go b/drivers/qemu/driver.go index 0b391468a65..2636dc4b7de 100644 --- a/drivers/qemu/driver.go +++ b/drivers/qemu/driver.go @@ -434,7 +434,7 @@ func isAllowedDriveInterface(driveInterface string) bool { func validateEmulator(emulator string, allowedEmulators []string) error { if len(allowedEmulators) > 0 { if !slices.Contains(allowedEmulators, emulator) { - return fmt.Errorf("emulator '%s' is not an allowed emulator", emulator) + return fmt.Errorf("'%s' is not an allowed emulator", emulator) } } return nil diff --git a/drivers/qemu/driver_test.go b/drivers/qemu/driver_test.go index b65a96009c3..b8f548c7ffa 100644 --- a/drivers/qemu/driver_test.go +++ b/drivers/qemu/driver_test.go @@ -345,12 +345,6 @@ func TestValidateEmulator(t *testing.T) { requestedEmulator: "qemu-system-x86_64", exp: nil, }, - { - name: "empty valid emulators, invalid request", - validEmulators: nil, - requestedEmulator: "some-binary", - exp: errors.New("emulator 'some-binary' is not valid"), - }, { name: "non-empty valid emulators, valid request", validEmulators: []string{"qemu-system-x86_64"}, @@ -361,7 +355,7 @@ func TestValidateEmulator(t *testing.T) { name: "non-empty valid emulators, invalid request", validEmulators: []string{"qemu-system-x86_64"}, requestedEmulator: "qemu-system-aarch64", - exp: errors.New("emulator 'qemu-system-aarch64' is not an allowed emulator"), + exp: errors.New("'qemu-system-aarch64' is not an allowed emulator"), }, } From d40fd385107d4358890c0a119b644a4abb916237 Mon Sep 17 00:00:00 2001 From: Michael Smithhisler Date: Tue, 2 Dec 2025 14:21:55 -0500 Subject: [PATCH 06/10] add godocs --- drivers/qemu/driver.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/qemu/driver.go b/drivers/qemu/driver.go index 2636dc4b7de..545088fd153 100644 --- a/drivers/qemu/driver.go +++ b/drivers/qemu/driver.go @@ -354,6 +354,8 @@ func (d *Driver) RecoverTask(handle *drivers.TaskHandle) error { return nil } +// findEmulators searches the $PATH for qemu-system binaries until they are found +// and returns a slice of emulators that comply with the emulators allowlist. func findEmulators(allowList []string) []string { var ( glob string = "qemu-system-*" @@ -380,12 +382,7 @@ func findEmulators(allowList []string) []string { for _, f := range bins { em := strings.TrimPrefix(filepath.Base(f), "qemu-system-") - - if len(allowList) > 0 { - if slices.Contains(allowList, em) { - emulators = append(emulators, em) - } - } else { + if err := validateEmulator(em, allowList); err == nil { emulators = append(emulators, em) } } @@ -431,6 +428,7 @@ func isAllowedDriveInterface(driveInterface string) bool { return false } +// validateEmulator validate whether the specified emulator is in allowedEmulators func validateEmulator(emulator string, allowedEmulators []string) error { if len(allowedEmulators) > 0 { if !slices.Contains(allowedEmulators, emulator) { From 6a27079fa2385502646c32cdb11312b74de37a7d Mon Sep 17 00:00:00 2001 From: Michael Smithhisler Date: Tue, 2 Dec 2025 14:23:03 -0500 Subject: [PATCH 07/10] use != nil --- drivers/qemu/driver.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/qemu/driver.go b/drivers/qemu/driver.go index 545088fd153..f343843b95c 100644 --- a/drivers/qemu/driver.go +++ b/drivers/qemu/driver.go @@ -382,9 +382,10 @@ func findEmulators(allowList []string) []string { for _, f := range bins { em := strings.TrimPrefix(filepath.Base(f), "qemu-system-") - if err := validateEmulator(em, allowList); err == nil { - emulators = append(emulators, em) + if err := validateEmulator(em, allowList); err != nil { + continue } + emulators = append(emulators, em) } return emulators From 818e4d34d6e1920b7238ae7a7195bc1bb10f17b9 Mon Sep 17 00:00:00 2001 From: Michael Smithhisler Date: Tue, 2 Dec 2025 16:18:30 -0500 Subject: [PATCH 08/10] update test to skip if aarch64 not detected --- client/testutil/driver_compatible.go | 17 ++++++++++++----- drivers/qemu/driver_test.go | 15 +++++++-------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/client/testutil/driver_compatible.go b/client/testutil/driver_compatible.go index f1ef1b8c2a6..775230a2cf6 100644 --- a/client/testutil/driver_compatible.go +++ b/client/testutil/driver_compatible.go @@ -113,14 +113,21 @@ func JavaCompatible(t *testing.T) { } // QemuCompatible skips tests unless: -// - "qemu-system-x86_64" executable is detected on $PATH (!windows) -// - "qemu-img" executable is detected on on $PATH (windows) -func QemuCompatible(t *testing.T) { +// - "qemu-system-x86_64" executable is detected on $PATH +func QemuCompatible_x86_64(t *testing.T) { // Check if qemu exists bin := "qemu-system-x86_64" - if runtime.GOOS == "windows" { - bin = "qemu-img" + _, err := exec.Command(bin, "--version").CombinedOutput() + if err != nil { + t.Skipf("Test requires QEMU (%s)", bin) } +} + +// QemuCompatible skips tests unless: +// - "qemu-system-aarch64" executable is detected on $PATH +func QemuCompatible_aarch64(t *testing.T) { + // Check if qemu exists + bin := "qemu-system-aarch64" _, err := exec.Command(bin, "--version").CombinedOutput() if err != nil { t.Skipf("Test requires QEMU (%s)", bin) diff --git a/drivers/qemu/driver_test.go b/drivers/qemu/driver_test.go index b8f548c7ffa..3de3933ef45 100644 --- a/drivers/qemu/driver_test.go +++ b/drivers/qemu/driver_test.go @@ -34,7 +34,7 @@ import ( // Verifies starting a qemu image and stopping it func TestQemuDriver_Start_Wait_Stop(t *testing.T) { ci.Parallel(t) - ctestutil.QemuCompatible(t) + ctestutil.QemuCompatible_x86_64(t) ctestutil.CgroupsCompatible(t) ctx, cancel := context.WithCancel(context.Background()) @@ -112,7 +112,7 @@ func copyFile(src, dst string, t *testing.T) { // Verifies starting a qemu image and stopping it func TestQemuDriver_User(t *testing.T) { ci.Parallel(t) - ctestutil.QemuCompatible(t) + ctestutil.QemuCompatible_x86_64(t) ctestutil.CgroupsCompatible(t) ctx, cancel := context.WithCancel(context.Background()) @@ -157,7 +157,7 @@ func TestQemuDriver_User(t *testing.T) { // TestQemuDriver_Stats verifies we can get resources usage stats func TestQemuDriver_Stats(t *testing.T) { ci.Parallel(t) - ctestutil.QemuCompatible(t) + ctestutil.QemuCompatible_x86_64(t) ctestutil.CgroupsCompatible(t) ctx, cancel := context.WithCancel(context.Background()) @@ -238,7 +238,8 @@ func TestQemuDriver_Stats(t *testing.T) { func TestQemuDriver_Fingerprint(t *testing.T) { ci.Parallel(t) - ctestutil.QemuCompatible(t) + ctestutil.QemuCompatible_x86_64(t) + ctestutil.QemuCompatible_aarch64(t) t.Run("fingerpints all emulators", func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) @@ -256,9 +257,7 @@ func TestQemuDriver_Fingerprint(t *testing.T) { must.True(t, ok) emulators, _ := finger.Attributes[driverEmulatorsAttr].GetString() - // Don't want to hardcode the exact amount of emulators installed by qemu-system - // but it's definitely more than 5 - must.True(t, len(strings.Split(emulators, ",")) > 5) + must.Greater(t, 1, len(strings.Split(emulators, ","))) case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): t.Fatal("timeout receiving fingerprint") } @@ -269,7 +268,7 @@ func TestQemuDriver_Fingerprint(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - allowedEms := []string{"x86_64", "aarch64"} + allowedEms := []string{"x86_64"} d := NewQemuDriver(ctx, testlog.HCLogger(t)) config := &Config{ EmulatorsAllowList: allowedEms, From 501c9283068d67421f6a81d5973f9f638556ca1b Mon Sep 17 00:00:00 2001 From: Michael Smithhisler Date: Tue, 2 Dec 2025 16:28:36 -0500 Subject: [PATCH 09/10] update test --- drivers/qemu/driver_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/qemu/driver_test.go b/drivers/qemu/driver_test.go index 3de3933ef45..6eed4d119d9 100644 --- a/drivers/qemu/driver_test.go +++ b/drivers/qemu/driver_test.go @@ -360,7 +360,12 @@ func TestValidateEmulator(t *testing.T) { for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { - must.Eq(t, validateEmulator(tc.requestedEmulator, tc.validEmulators), tc.exp) + err := validateEmulator(tc.requestedEmulator, tc.validEmulators) + if tc.exp != nil { + must.ErrorContains(t, err, tc.exp.Error()) + } else { + must.NoError(t, err) + } }) } } From 86b04bd9094ce5106a4748e5f206c984f7a61a11 Mon Sep 17 00:00:00 2001 From: Michael Smithhisler Date: Tue, 2 Dec 2025 16:52:48 -0500 Subject: [PATCH 10/10] fix godoc --- client/testutil/driver_compatible.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/testutil/driver_compatible.go b/client/testutil/driver_compatible.go index 775230a2cf6..36640f545a4 100644 --- a/client/testutil/driver_compatible.go +++ b/client/testutil/driver_compatible.go @@ -112,7 +112,7 @@ func JavaCompatible(t *testing.T) { } } -// QemuCompatible skips tests unless: +// QemuCompatible_x86_64 skips tests unless: // - "qemu-system-x86_64" executable is detected on $PATH func QemuCompatible_x86_64(t *testing.T) { // Check if qemu exists @@ -123,7 +123,7 @@ func QemuCompatible_x86_64(t *testing.T) { } } -// QemuCompatible skips tests unless: +// QemuCompatible_aarch64 skips tests unless: // - "qemu-system-aarch64" executable is detected on $PATH func QemuCompatible_aarch64(t *testing.T) { // Check if qemu exists