diff --git a/go.mod b/go.mod index 7a943f7..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.0 + 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 23e0dcb..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.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.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) + } +} 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 {