From b73f6647d0670211fd5f6efb70509c437590425e Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 04:59:22 +0000 Subject: [PATCH 1/3] Update hypeman-go SDK to 0872a65 and show vendor VFIO vGPUs in ps Bumps github.com/kernel/hypeman-go to v0.28.1-0.20260902045311-0872a65a3733, which integrates vendor VFIO vGPUs into the instance lifecycle. The SDK now documents InstanceGPU.MdevUuid as populated on mdev hosts only, and adds InstanceGPU.DevicePath for the sysfs path of an assigned vGPU device. `hypeman ps` gated its GPU column on MdevUuid alone, so an instance with a vendor VFIO vGPU and no profile name rendered as "-". formatGPU now also checks DevicePath. A full enumeration of api.md methods and CLI commands found no other coverage gaps: every SDK method has a CLI command and every param field has a corresponding flag. Co-authored-by: Cursor --- go.mod | 2 +- go.sum | 4 ++-- pkg/cmd/ps.go | 5 +++-- pkg/cmd/ps_test.go | 7 +++++++ 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 7a943f7..9da5b2f 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/google/go-containerregistry v0.20.7 github.com/gorilla/websocket v1.5.3 github.com/itchyny/json2yaml v0.1.4 - github.com/kernel/hypeman-go v0.28.0 + github.com/kernel/hypeman-go v0.28.1-0.20260902045311-0872a65a3733 github.com/knadh/koanf/parsers/yaml v1.1.0 github.com/knadh/koanf/providers/env v1.1.0 github.com/knadh/koanf/providers/file v1.2.1 diff --git a/go.sum b/go.sum index 23e0dcb..1cbc63c 100644 --- a/go.sum +++ b/go.sum @@ -78,8 +78,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnV github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/itchyny/json2yaml v0.1.4 h1:/pErVOXGG5iTyXHi/QKR4y3uzhLjGTEmmJIy97YT+k8= github.com/itchyny/json2yaml v0.1.4/go.mod h1:6iudhBZdarpjLFRNj+clWLAkGft+9uCcjAZYXUH9eGI= -github.com/kernel/hypeman-go v0.28.0 h1:1flQG6NfifaqrZsoU5LHydnVEUzaVdWt64/9JvEaOk8= -github.com/kernel/hypeman-go v0.28.0/go.mod h1:of8qI/nef2OPLzt0EMlIRbMdJHEvuc4yWG8g/ioNg48= +github.com/kernel/hypeman-go v0.28.1-0.20260902045311-0872a65a3733 h1:qJLI83DX+7jnEVUgBF8edK5OYwTOwI4OqER0R+bARgY= +github.com/kernel/hypeman-go v0.28.1-0.20260902045311-0872a65a3733/go.mod h1:of8qI/nef2OPLzt0EMlIRbMdJHEvuc4yWG8g/ioNg48= github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpbo= diff --git a/pkg/cmd/ps.go b/pkg/cmd/ps.go index 558f02c..e8ef2df 100644 --- a/pkg/cmd/ps.go +++ b/pkg/cmd/ps.go @@ -121,8 +121,9 @@ func formatGPU(gpu hypeman.InstanceGPU) string { if gpu.Profile != "" { return gpu.Profile } - // Check if mdev UUID is set (indicates vGPU without profile name shown) - if gpu.MdevUuid != "" { + // A vGPU is attached without a profile name shown. mdev_uuid is only populated on + // mdev hosts; vendor VFIO hosts report device_path instead. + if gpu.MdevUuid != "" || gpu.DevicePath != "" { return "vgpu" } return "-" diff --git a/pkg/cmd/ps_test.go b/pkg/cmd/ps_test.go index ffa94d0..6ac67e3 100644 --- a/pkg/cmd/ps_test.go +++ b/pkg/cmd/ps_test.go @@ -34,6 +34,13 @@ func TestFormatGPU(t *testing.T) { }, expected: "vgpu", }, + { + name: "vGPU without profile but with device path", + gpu: hypeman.InstanceGPU{ + DevicePath: "/sys/bus/pci/devices/0000:41:00.4", + }, + expected: "vgpu", + }, } for _, tt := range tests { From 4ad8ab1c3f0e83989506f5c7189f876366af3994 Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 14:38:30 +0000 Subject: [PATCH 2/3] Update hypeman-go SDK to e6c2b7b and add swtpm log source Bumps github.com/kernel/hypeman-go to v0.28.1-0.20260902143136-e6c2b7bc0171, which adds the "swtpm" value to InstanceLogsParamsSource as part of the Windows hypervisor primitives. Teaches `hypeman logs --source` about swtpm and validates the flag up front instead of forwarding an arbitrary string to the API, matching the parseInstanceWaitState / parseSnapshotTargetHypervisor pattern. Co-authored-by: Cursor --- go.mod | 2 +- go.sum | 4 ++-- pkg/cmd/coveragecmd_test.go | 13 +++++++++++++ pkg/cmd/logs.go | 24 ++++++++++++++++++++++-- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 9da5b2f..49efb56 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/google/go-containerregistry v0.20.7 github.com/gorilla/websocket v1.5.3 github.com/itchyny/json2yaml v0.1.4 - github.com/kernel/hypeman-go v0.28.1-0.20260902045311-0872a65a3733 + github.com/kernel/hypeman-go v0.28.1-0.20260902143136-e6c2b7bc0171 github.com/knadh/koanf/parsers/yaml v1.1.0 github.com/knadh/koanf/providers/env v1.1.0 github.com/knadh/koanf/providers/file v1.2.1 diff --git a/go.sum b/go.sum index 1cbc63c..08e9135 100644 --- a/go.sum +++ b/go.sum @@ -78,8 +78,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnV github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/itchyny/json2yaml v0.1.4 h1:/pErVOXGG5iTyXHi/QKR4y3uzhLjGTEmmJIy97YT+k8= github.com/itchyny/json2yaml v0.1.4/go.mod h1:6iudhBZdarpjLFRNj+clWLAkGft+9uCcjAZYXUH9eGI= -github.com/kernel/hypeman-go v0.28.1-0.20260902045311-0872a65a3733 h1:qJLI83DX+7jnEVUgBF8edK5OYwTOwI4OqER0R+bARgY= -github.com/kernel/hypeman-go v0.28.1-0.20260902045311-0872a65a3733/go.mod h1:of8qI/nef2OPLzt0EMlIRbMdJHEvuc4yWG8g/ioNg48= +github.com/kernel/hypeman-go v0.28.1-0.20260902143136-e6c2b7bc0171 h1:psPDtnXBaPABqymQW5lu1o9YOF7gyyxJZeQOCqcuxyo= +github.com/kernel/hypeman-go v0.28.1-0.20260902143136-e6c2b7bc0171/go.mod h1:of8qI/nef2OPLzt0EMlIRbMdJHEvuc4yWG8g/ioNg48= github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpbo= diff --git a/pkg/cmd/coveragecmd_test.go b/pkg/cmd/coveragecmd_test.go index 49ef742..8a972e6 100644 --- a/pkg/cmd/coveragecmd_test.go +++ b/pkg/cmd/coveragecmd_test.go @@ -81,6 +81,19 @@ func TestParseInstanceWaitState(t *testing.T) { }) } +func TestParseInstanceLogsSource(t *testing.T) { + t.Run("accepts mixed-case source names", func(t *testing.T) { + source, err := parseInstanceLogsSource("SwTpM") + require.NoError(t, err) + assert.Equal(t, hypeman.InstanceLogsParamsSourceSwtpm, source) + }) + + t.Run("rejects unsupported source names", func(t *testing.T) { + _, err := parseInstanceLogsSource("kernel") + require.EqualError(t, err, "invalid source: kernel (must be app, vmm, hypeman, or swtpm)") + }) +} + func TestParseAutoStandbyPorts(t *testing.T) { t.Run("parses valid port values", func(t *testing.T) { ports, err := parseAutoStandbyPorts([]string{"80", " 443 "}, "ignore-destination-port") diff --git a/pkg/cmd/logs.go b/pkg/cmd/logs.go index d461aae..43a8e73 100644 --- a/pkg/cmd/logs.go +++ b/pkg/cmd/logs.go @@ -3,6 +3,7 @@ package cmd import ( "context" "fmt" + "strings" "github.com/kernel/hypeman-go" "github.com/kernel/hypeman-go/option" @@ -27,7 +28,7 @@ var logsCmd = cli.Command{ &cli.StringFlag{ Name: "source", Aliases: []string{"s"}, - Usage: "Log source: app (default), vmm (Cloud Hypervisor), or hypeman (operations log)", + Usage: "Log source: app (default), vmm (Cloud Hypervisor), hypeman (operations log), or swtpm (software TPM emulator)", }, }, Action: handleLogs, @@ -56,7 +57,11 @@ func handleLogs(ctx context.Context, cmd *cli.Command) error { params.Tail = hypeman.Opt(int64(cmd.Int("tail"))) } if cmd.IsSet("source") { - params.Source = hypeman.InstanceLogsParamsSource(cmd.String("source")) + source, err := parseInstanceLogsSource(cmd.String("source")) + if err != nil { + return err + } + params.Source = source } var opts []option.RequestOption @@ -78,3 +83,18 @@ func handleLogs(ctx context.Context, cmd *cli.Command) error { return stream.Err() } + +func parseInstanceLogsSource(raw string) (hypeman.InstanceLogsParamsSource, error) { + switch strings.ToLower(raw) { + case "app": + return hypeman.InstanceLogsParamsSourceApp, nil + case "vmm": + return hypeman.InstanceLogsParamsSourceVmm, nil + case "hypeman": + return hypeman.InstanceLogsParamsSourceHypeman, nil + case "swtpm": + return hypeman.InstanceLogsParamsSourceSwtpm, nil + default: + return "", fmt.Errorf("invalid source: %s (must be app, vmm, hypeman, or swtpm)", raw) + } +} From 22e9c3151efc2d08e59912edf81618f20cdbc7d2 Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 16:11:07 +0000 Subject: [PATCH 3/3] Update hypeman-go SDK to 134587a Bumps github.com/kernel/hypeman-go to v0.28.1-0.20261001160305-134587a222ac, which carries the SDK's 2026-09-30 vulnerability remediation. This commit is on main, whereas the branch previously pinned e6c2b7b from the SDK's next branch. The Windows hypervisor primitives generated there are therefore absent, dropping InstanceLogsParamsSourceSwtpm and InstanceGPU.DevicePath. Both values are still specified by the API, so rather than regress `hypeman logs --source swtpm` and the vendor VFIO vGPU column in `hypeman ps`, read them without the generated bindings: the log source is a string-backed enum, and device_path arrives in JSON.ExtraFields. Both can go back to typed access once next lands on main. Co-authored-by: Cursor --- go.mod | 2 +- go.sum | 4 ++-- pkg/cmd/coveragecmd_test.go | 2 +- pkg/cmd/logs.go | 6 +++++- pkg/cmd/ps.go | 20 +++++++++++++++++++- pkg/cmd/ps_test.go | 17 +++++++++++++---- 6 files changed, 41 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index c138457..dfb88b6 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/google/go-containerregistry v0.20.7 github.com/gorilla/websocket v1.5.3 github.com/itchyny/json2yaml v0.1.4 - github.com/kernel/hypeman-go v0.28.1-0.20260902143136-e6c2b7bc0171 + github.com/kernel/hypeman-go v0.28.1-0.20261001160305-134587a222ac github.com/knadh/koanf/parsers/yaml v1.1.0 github.com/knadh/koanf/providers/env v1.1.0 github.com/knadh/koanf/providers/file v1.2.1 diff --git a/go.sum b/go.sum index 3188de9..79b7750 100644 --- a/go.sum +++ b/go.sum @@ -80,8 +80,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnV github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/itchyny/json2yaml v0.1.4 h1:/pErVOXGG5iTyXHi/QKR4y3uzhLjGTEmmJIy97YT+k8= github.com/itchyny/json2yaml v0.1.4/go.mod h1:6iudhBZdarpjLFRNj+clWLAkGft+9uCcjAZYXUH9eGI= -github.com/kernel/hypeman-go v0.28.1-0.20260902143136-e6c2b7bc0171 h1:psPDtnXBaPABqymQW5lu1o9YOF7gyyxJZeQOCqcuxyo= -github.com/kernel/hypeman-go v0.28.1-0.20260902143136-e6c2b7bc0171/go.mod h1:of8qI/nef2OPLzt0EMlIRbMdJHEvuc4yWG8g/ioNg48= +github.com/kernel/hypeman-go v0.28.1-0.20261001160305-134587a222ac h1:dAwrSu1y5hCNcqsnNq8ovzIYnJq+RcB7hjmr+qr8hy8= +github.com/kernel/hypeman-go v0.28.1-0.20261001160305-134587a222ac/go.mod h1:C3X0483PGbvn6ySpvPrgSH2joOWqacu9KUYd1gIE5cI= github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpbo= diff --git a/pkg/cmd/coveragecmd_test.go b/pkg/cmd/coveragecmd_test.go index 8a972e6..3405ca0 100644 --- a/pkg/cmd/coveragecmd_test.go +++ b/pkg/cmd/coveragecmd_test.go @@ -85,7 +85,7 @@ func TestParseInstanceLogsSource(t *testing.T) { t.Run("accepts mixed-case source names", func(t *testing.T) { source, err := parseInstanceLogsSource("SwTpM") require.NoError(t, err) - assert.Equal(t, hypeman.InstanceLogsParamsSourceSwtpm, source) + assert.Equal(t, instanceLogsSourceSwtpm, source) }) t.Run("rejects unsupported source names", func(t *testing.T) { diff --git a/pkg/cmd/logs.go b/pkg/cmd/logs.go index 43a8e73..fda64d1 100644 --- a/pkg/cmd/logs.go +++ b/pkg/cmd/logs.go @@ -10,6 +10,10 @@ import ( "github.com/urfave/cli/v3" ) +// instanceLogsSourceSwtpm is the "swtpm" value accepted by the API's log source +// enum. The generated SDK does not define a constant for it yet. +const instanceLogsSourceSwtpm hypeman.InstanceLogsParamsSource = "swtpm" + var logsCmd = cli.Command{ Name: "logs", Usage: "Fetch the logs of an instance", @@ -93,7 +97,7 @@ func parseInstanceLogsSource(raw string) (hypeman.InstanceLogsParamsSource, erro case "hypeman": return hypeman.InstanceLogsParamsSourceHypeman, nil case "swtpm": - return hypeman.InstanceLogsParamsSourceSwtpm, nil + return instanceLogsSourceSwtpm, nil default: return "", fmt.Errorf("invalid source: %s (must be app, vmm, hypeman, or swtpm)", raw) } diff --git a/pkg/cmd/ps.go b/pkg/cmd/ps.go index e8ef2df..dff175d 100644 --- a/pkg/cmd/ps.go +++ b/pkg/cmd/ps.go @@ -2,6 +2,7 @@ package cmd import ( "context" + "encoding/json" "fmt" "os" @@ -123,12 +124,29 @@ func formatGPU(gpu hypeman.InstanceGPU) string { } // A vGPU is attached without a profile name shown. mdev_uuid is only populated on // mdev hosts; vendor VFIO hosts report device_path instead. - if gpu.MdevUuid != "" || gpu.DevicePath != "" { + if gpu.MdevUuid != "" || instanceGPUDevicePath(gpu) != "" { return "vgpu" } return "-" } +// instanceGPUDevicePath returns the sysfs path of the assigned vGPU device, +// which the API reports on vendor VFIO hosts. The generated SDK has no typed +// field for it yet, so it arrives as an extra field. +func instanceGPUDevicePath(gpu hypeman.InstanceGPU) string { + // Valid reports false for extra fields, so decode Raw instead; it is empty + // when the field is absent. + raw := gpu.JSON.ExtraFields["device_path"].Raw() + if raw == "" { + return "" + } + var devicePath string + if err := json.Unmarshal([]byte(raw), &devicePath); err != nil { + return "" + } + return devicePath +} + // formatHypervisor returns a short abbreviation for the hypervisor func formatHypervisor(hv hypeman.InstanceHypervisor) string { switch hv { diff --git a/pkg/cmd/ps_test.go b/pkg/cmd/ps_test.go index 6ac67e3..b68ed52 100644 --- a/pkg/cmd/ps_test.go +++ b/pkg/cmd/ps_test.go @@ -1,6 +1,7 @@ package cmd import ( + "encoding/json" "testing" "github.com/kernel/hypeman-go" @@ -35,10 +36,8 @@ func TestFormatGPU(t *testing.T) { expected: "vgpu", }, { - name: "vGPU without profile but with device path", - gpu: hypeman.InstanceGPU{ - DevicePath: "/sys/bus/pci/devices/0000:41:00.4", - }, + name: "vGPU without profile but with device path", + gpu: instanceGPUFromJSON(t, `{"device_path":"/sys/bus/pci/devices/0000:41:00.4"}`), expected: "vgpu", }, } @@ -51,6 +50,16 @@ func TestFormatGPU(t *testing.T) { } } +// instanceGPUFromJSON decodes an InstanceGPU the way the API delivers it, so +// fields the SDK does not type yet land in JSON.ExtraFields. +func instanceGPUFromJSON(t *testing.T, raw string) hypeman.InstanceGPU { + t.Helper() + + var gpu hypeman.InstanceGPU + require.NoError(t, json.Unmarshal([]byte(raw), &gpu)) + return gpu +} + func TestFormatHypervisor(t *testing.T) { tests := []struct { name string