From bd2c6e897dcb3585733710eea31abb25b43a1aac Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Fri, 14 Aug 2026 13:20:27 -0400 Subject: [PATCH 01/11] Add authenticated host capabilities endpoint --- cmd/api/api/capabilities.go | 243 ++++ cmd/api/api/capabilities_test.go | 186 +++ lib/builds/manager_test.go | 4 + lib/hypervisor/vz/client.go | 6 +- lib/hypervisor/vz/save_restore_support.go | 40 + .../vz/save_restore_support_darwin.go | 26 + .../vz/save_restore_support_darwin_test.go | 25 + .../vz/save_restore_support_test.go | 33 + lib/instances/manager.go | 9 + lib/instances/wait_test.go | 4 + lib/network/default_network_test.go | 19 + lib/network/model_darwin.go | 16 + lib/network/model_linux.go | 16 + lib/oapi/oapi.go | 1008 +++++++++++------ lib/scopes/scopes.go | 3 +- openapi.yaml | 168 +++ stainless.yaml | 11 + 17 files changed, 1473 insertions(+), 344 deletions(-) create mode 100644 cmd/api/api/capabilities.go create mode 100644 cmd/api/api/capabilities_test.go create mode 100644 lib/hypervisor/vz/save_restore_support.go create mode 100644 lib/hypervisor/vz/save_restore_support_darwin.go create mode 100644 lib/hypervisor/vz/save_restore_support_darwin_test.go create mode 100644 lib/hypervisor/vz/save_restore_support_test.go create mode 100644 lib/network/default_network_test.go create mode 100644 lib/network/model_darwin.go create mode 100644 lib/network/model_linux.go diff --git a/cmd/api/api/capabilities.go b/cmd/api/api/capabilities.go new file mode 100644 index 000000000..30c294561 --- /dev/null +++ b/cmd/api/api/capabilities.go @@ -0,0 +1,243 @@ +package api + +import ( + "context" + "runtime" + "slices" + "sync" + + "github.com/kernel/hypeman/lib/hypervisor" + "github.com/kernel/hypeman/lib/images" + "github.com/kernel/hypeman/lib/logger" + "github.com/kernel/hypeman/lib/network" + "github.com/kernel/hypeman/lib/oapi" +) + +// Stable feature IDs reported by the capabilities endpoint. Clients gate +// behavior on these (and the structured runtime booleans) rather than on +// hypervisor names. +const ( + featureInstances = "instances" + featureImages = "images" + featureBuilds = "builds" + featureVolumes = "volumes" + featureIngress = "ingress" + featureExec = "exec" + featureLogs = "logs" + featureStandby = "standby" + featureSnapshots = "snapshots" + featureFork = "fork" + featurePause = "pause" + featureHotplugMemory = "hotplug-memory" + featureBalloonControl = "balloon-control" + featureVsock = "vsock" + featureGPUPassthrough = "gpu-passthrough" + featureDiskIOLimit = "disk-io-limit" + featureDiskResize = "disk-resize" + featureDevices = "devices" + featureRosettaEmulation = "rosetta-emulation" +) + +// apiVersion is the API contract version from the embedded OpenAPI document. +// The decoded spec is cached: decoding it per request is needlessly expensive. +var apiVersion = sync.OnceValue(func() string { + spec, err := oapi.GetSwagger() + if err != nil || spec.Info == nil { + return "unknown" + } + return spec.Info.Version +}) + +// GetCapabilities reports host, runtime, network, and image capabilities. +func (s *ApiService) GetCapabilities(ctx context.Context, _ oapi.GetCapabilitiesRequestObject) (oapi.GetCapabilitiesResponseObject, error) { + log := logger.FromContext(ctx) + + defaultRuntime := hypervisor.TypeCloudHypervisor + if s.InstanceManager != nil { + defaultRuntime = s.InstanceManager.DefaultHypervisor() + } + supported := supportedRuntimes(runtime.GOOS) + caps, capsKnown := hypervisor.CapabilitiesForType(defaultRuntime) + if capsKnown && !slices.Contains(supported, string(defaultRuntime)) { + // The configured default runtime cannot run on this host platform; + // advertising its features would overstate support. + capsKnown = false + } + if !capsKnown { + // Report zeroed features rather than guessing. + log.WarnContext(ctx, "default runtime is not usable on this host platform", + "runtime", string(defaultRuntime), "host_os", runtime.GOOS) + caps = hypervisor.Capabilities{} + } + + emulation := emulationSupported(runtime.GOOS, runtime.GOARCH, supported) + + networkCaps, err := s.networkCapabilities(ctx) + if err != nil { + log.ErrorContext(ctx, "failed to resolve network capabilities", "error", err) + return oapi.GetCapabilities500JSONResponse{ + Code: "internal_error", + Message: "failed to resolve network capabilities", + }, nil + } + + resp := oapi.Capabilities{ + Server: oapi.CapabilitiesServer{ + Version: s.Config.Version, + ApiVersion: apiVersion(), + }, + Host: oapi.CapabilitiesHost{ + Os: runtime.GOOS, + Arch: runtime.GOARCH, + }, + Runtime: oapi.CapabilitiesRuntime{ + Default: string(defaultRuntime), + Supported: supported, + Snapshot: caps.SupportsSnapshot, + Standby: standbySupported(caps), + Pause: caps.SupportsPause, + HotplugMemory: caps.SupportsHotplugMemory, + BalloonControl: caps.SupportsBalloonControl, + Vsock: caps.SupportsVsock, + GpuPassthrough: caps.SupportsGPUPassthrough, + DiskIoLimit: caps.SupportsDiskIOLimit, + DiskResize: caps.SupportsDiskResize, + }, + Network: *networkCaps, + Images: oapi.CapabilitiesImages{ + Platforms: imagePlatforms(runtime.GOARCH, emulation), + DefaultPlatform: images.HostPlatformString(), + }, + Features: assembleFeatures(runtime.GOOS, caps, emulation), + } + + return oapi.GetCapabilities200JSONResponse(resp), nil +} + +// networkCapabilities resolves the guest networking model and the +// guest-visible host gateway from the network manager's effective default +// network. +func (s *ApiService) networkCapabilities(ctx context.Context) (*oapi.CapabilitiesNetwork, error) { + caps := &oapi.CapabilitiesNetwork{ + Model: oapi.CapabilitiesNetworkModel(network.NetworkModel()), + GuestToGuest: false, + } + if s.NetworkManager == nil { + return caps, nil + } + nw, err := s.NetworkManager.EffectiveDefaultNetwork() + if err != nil { + return nil, err + } + if nw == nil { + return caps, nil + } + caps.Gateway = nw.Gateway + if nw.Subnet != "" { + subnet := nw.Subnet + caps.Subnet = &subnet + } + caps.GuestToGuest = network.GuestToGuestEnabled(nw) + return caps, nil +} + +// standbySupported reports whether standby (pause + memory snapshot) and +// later restore are supported. Standby requires both snapshot and pause. +func standbySupported(caps hypervisor.Capabilities) bool { + return caps.SupportsSnapshot && caps.SupportsPause +} + +// supportedRuntimes returns the runtime identifiers usable on a host OS. +// This is a platform floor, not a registry listing: a runtime whose package +// registered capabilities but that cannot run on the host (e.g. firecracker +// on macOS) is not listed. +func supportedRuntimes(goos string) []string { + switch goos { + case "darwin": + return []string{string(hypervisor.TypeVZ)} + default: + return []string{ + string(hypervisor.TypeCloudHypervisor), + string(hypervisor.TypeFirecracker), + string(hypervisor.TypeQEMU), + } + } +} + +// emulationSupported reports whether the host can boot images built for the +// other CPU architecture. This is a host capability rather than a property of +// the configured default: vz can use Rosetta on Apple Silicon whenever it is +// available on the host. +func emulationSupported(goos, goarch string, supported []string) bool { + return goos == "darwin" && + goarch == "arm64" && + slices.Contains(supported, string(hypervisor.TypeVZ)) +} + +// imagePlatforms returns the image platforms (os/arch) the host can run: +// the host-native platform plus the emulated one when available. +func imagePlatforms(goarch string, emulation bool) []string { + if goarch == "" { + goarch = runtime.GOARCH + } + platforms := []string{"linux/" + goarch} + if emulation { + switch goarch { + case "arm64": + platforms = append(platforms, "linux/amd64") + case "amd64": + platforms = append(platforms, "linux/arm64") + } + } + return platforms +} + +// assembleFeatures builds the stable feature ID list: always-present base API +// surfaces plus conditional entries derived from the effective default +// runtime's capabilities and the host platform. +func assembleFeatures(goos string, caps hypervisor.Capabilities, emulation bool) []string { + features := []string{ + featureInstances, + featureImages, + featureBuilds, + featureVolumes, + featureIngress, + featureExec, + featureLogs, + } + if standbySupported(caps) { + features = append(features, featureStandby) + } + if caps.SupportsSnapshot { + features = append(features, featureSnapshots, featureFork) + } + if caps.SupportsPause { + features = append(features, featurePause) + } + if caps.SupportsHotplugMemory { + features = append(features, featureHotplugMemory) + } + if caps.SupportsBalloonControl { + features = append(features, featureBalloonControl) + } + if caps.SupportsVsock { + features = append(features, featureVsock) + } + if caps.SupportsGPUPassthrough { + features = append(features, featureGPUPassthrough) + } + if caps.SupportsDiskIOLimit { + features = append(features, featureDiskIOLimit) + } + if caps.SupportsDiskResize { + features = append(features, featureDiskResize) + } + // Device passthrough (GPU/PCI) is only meaningful on Linux hosts. + if goos == "linux" { + features = append(features, featureDevices) + } + if emulation { + features = append(features, featureRosettaEmulation) + } + return features +} diff --git a/cmd/api/api/capabilities_test.go b/cmd/api/api/capabilities_test.go new file mode 100644 index 000000000..d29737d8f --- /dev/null +++ b/cmd/api/api/capabilities_test.go @@ -0,0 +1,186 @@ +package api + +import ( + "context" + "runtime" + "testing" + + "github.com/kernel/hypeman/lib/hypervisor" + "github.com/kernel/hypeman/lib/network" + "github.com/kernel/hypeman/lib/oapi" + "github.com/stretchr/testify/require" +) + +// stubCapabilitiesNetworkManager overrides EffectiveDefaultNetwork with a fixed +// network so the handler test is hermetic (no bridge/netlink). +type stubCapabilitiesNetworkManager struct { + network.Manager + nw *network.Network + err error +} + +func (s *stubCapabilitiesNetworkManager) EffectiveDefaultNetwork() (*network.Network, error) { + return s.nw, s.err +} + +func cloudHypervisorCaps(t *testing.T) hypervisor.Capabilities { + t.Helper() + caps, ok := hypervisor.CapabilitiesForType(hypervisor.TypeCloudHypervisor) + require.True(t, ok, "cloud-hypervisor capabilities must be registered") + return caps +} + +func TestGetCapabilities(t *testing.T) { + t.Parallel() + svc := newTestService(t) + svc.Config.Version = "testsha123" + svc.NetworkManager = &stubCapabilitiesNetworkManager{ + nw: &network.Network{ + Name: "default", + Subnet: "10.100.0.0/16", + Gateway: "10.100.0.1", + Isolated: true, + Default: true, + }, + } + + resp, err := svc.GetCapabilities(ctx(), oapi.GetCapabilitiesRequestObject{}) + require.NoError(t, err) + + okResp, ok := resp.(oapi.GetCapabilities200JSONResponse) + require.True(t, ok, "expected 200 response, got %T", resp) + caps := oapi.Capabilities(okResp) + + // Server identity and version serialization + require.Equal(t, "testsha123", caps.Server.Version) + require.Equal(t, apiVersion(), caps.Server.ApiVersion) + require.NotEmpty(t, caps.Server.ApiVersion) + require.NotEqual(t, "unknown", caps.Server.ApiVersion) + + // Host identity + require.Equal(t, runtime.GOOS, caps.Host.Os) + require.Equal(t, runtime.GOARCH, caps.Host.Arch) + + // Effective default runtime and its capabilities. The test-service manager + // defaults to cloud-hypervisor; on macOS that default cannot run, so the + // endpoint must report zeroed runtime features rather than overstate. + require.Equal(t, string(hypervisor.TypeCloudHypervisor), caps.Runtime.Default) + require.NotEmpty(t, caps.Runtime.Supported) + if runtime.GOOS == "darwin" { + require.Equal(t, []string{string(hypervisor.TypeVZ)}, caps.Runtime.Supported) + require.False(t, caps.Runtime.Standby, "unusable default runtime must not advertise standby") + require.False(t, caps.Runtime.Snapshot) + } else { + require.Contains(t, caps.Runtime.Supported, caps.Runtime.Default) + chCaps := cloudHypervisorCaps(t) + require.Equal(t, chCaps.SupportsSnapshot, caps.Runtime.Snapshot) + require.Equal(t, chCaps.SupportsPause, caps.Runtime.Pause) + require.True(t, caps.Runtime.Standby) + } + + // Network model and guest-visible gateway + require.Equal(t, oapi.CapabilitiesNetworkModel(network.NetworkModel()), caps.Network.Model) + require.Equal(t, "10.100.0.1", caps.Network.Gateway) + require.NotNil(t, caps.Network.Subnet) + require.Equal(t, "10.100.0.0/16", *caps.Network.Subnet) + require.False(t, caps.Network.GuestToGuest, "isolated default network must report guest_to_guest=false") + + // Image platforms always include the host-native guest platform + require.Contains(t, caps.Images.Platforms, caps.Images.DefaultPlatform) + + // Base feature IDs are always present + for _, f := range []string{"instances", "images", "builds", "volumes", "ingress", "exec", "logs"} { + require.Contains(t, caps.Features, f) + } + if runtime.GOOS == "darwin" { + require.NotContains(t, caps.Features, "devices") + require.NotContains(t, caps.Features, "standby") + } else { + require.Contains(t, caps.Features, "standby") + require.Contains(t, caps.Features, "devices") + } +} + +func TestGetCapabilitiesNetworkError(t *testing.T) { + t.Parallel() + svc := newTestService(t) + svc.NetworkManager = &stubCapabilitiesNetworkManager{err: context.DeadlineExceeded} + + resp, err := svc.GetCapabilities(ctx(), oapi.GetCapabilitiesRequestObject{}) + require.NoError(t, err) + _, ok := resp.(oapi.GetCapabilities500JSONResponse) + require.True(t, ok, "expected 500 response when network resolution fails, got %T", resp) +} + +func TestSupportedRuntimes(t *testing.T) { + t.Parallel() + require.Equal(t, + []string{"cloud-hypervisor", "firecracker", "qemu"}, + supportedRuntimes("linux")) + require.Equal(t, []string{"vz"}, supportedRuntimes("darwin")) +} + +func TestEmulationSupported(t *testing.T) { + t.Parallel() + require.True(t, emulationSupported("darwin", "arm64", []string{"vz"})) + require.False(t, emulationSupported("darwin", "arm64", []string{"cloud-hypervisor"})) + require.False(t, emulationSupported("darwin", "amd64", []string{"vz"})) + require.False(t, emulationSupported("linux", "arm64", []string{"vz"})) + require.False(t, emulationSupported("linux", "amd64", []string{"cloud-hypervisor"})) +} + +func TestImagePlatforms(t *testing.T) { + t.Parallel() + require.Equal(t, []string{"linux/amd64"}, imagePlatforms("amd64", false)) + require.Equal(t, []string{"linux/arm64"}, imagePlatforms("arm64", false)) + require.Equal(t, + []string{"linux/arm64", "linux/amd64"}, + imagePlatforms("arm64", true), + "Apple Silicon macOS with vz advertises Rosetta-emulated amd64") +} + +func TestAssembleFeatures(t *testing.T) { + t.Parallel() + + base := []string{"instances", "images", "builds", "volumes", "ingress", "exec", "logs"} + + // Zero capabilities (unknown runtime on this host): base features only. + features := assembleFeatures("linux", hypervisor.Capabilities{}, false) + require.ElementsMatch(t, append(base, "devices"), features) + + // Full Linux cloud-hypervisor capabilities. + features = assembleFeatures("linux", cloudHypervisorCaps(t), false) + require.Subset(t, features, base) + require.Contains(t, features, "standby") + require.Contains(t, features, "snapshots") + require.Contains(t, features, "fork") + require.Contains(t, features, "pause") + require.Contains(t, features, "hotplug-memory") + require.Contains(t, features, "devices") + require.NotContains(t, features, "rosetta-emulation") + + // macOS: no devices feature; emulation adds rosetta. + vzCaps := hypervisor.Capabilities{SupportsSnapshot: true, SupportsPause: true, SupportsBalloonControl: true, SupportsVsock: true} + features = assembleFeatures("darwin", vzCaps, true) + require.NotContains(t, features, "devices") + require.Contains(t, features, "standby") + require.Contains(t, features, "rosetta-emulation") + require.NotContains(t, features, "hotplug-memory") + require.NotContains(t, features, "gpu-passthrough") +} + +func TestStandbyRequiresSnapshotAndPause(t *testing.T) { + t.Parallel() + require.False(t, standbySupported(hypervisor.Capabilities{SupportsSnapshot: true, SupportsPause: false})) + require.False(t, standbySupported(hypervisor.Capabilities{SupportsSnapshot: false, SupportsPause: true})) + require.True(t, standbySupported(hypervisor.Capabilities{SupportsSnapshot: true, SupportsPause: true})) +} + +// TestAPIVersionMatchesSpec guards the version contract: the endpoint must +// serialize the same version as the embedded OpenAPI document. +func TestAPIVersionMatchesSpec(t *testing.T) { + t.Parallel() + spec, err := oapi.GetSwagger() + require.NoError(t, err) + require.Equal(t, spec.Info.Version, apiVersion()) +} diff --git a/lib/builds/manager_test.go b/lib/builds/manager_test.go index 7be58b0fc..dd48d6c96 100644 --- a/lib/builds/manager_test.go +++ b/lib/builds/manager_test.go @@ -75,6 +75,10 @@ func (m *mockInstanceManager) CreateInstance(ctx context.Context, req instances. return inst, nil } +func (m *mockInstanceManager) DefaultHypervisor() hypervisor.Type { + return hypervisor.TypeCloudHypervisor +} + func (m *mockInstanceManager) GetInstance(ctx context.Context, id string) (*instances.Instance, error) { if m.getFunc != nil { return m.getFunc(ctx, id) diff --git a/lib/hypervisor/vz/client.go b/lib/hypervisor/vz/client.go index 2fb0a62af..f0116bb97 100644 --- a/lib/hypervisor/vz/client.go +++ b/lib/hypervisor/vz/client.go @@ -10,7 +10,6 @@ import ( "io" "net" "net/http" - "runtime" "time" "github.com/kernel/hypeman/lib/hypervisor" @@ -84,7 +83,10 @@ func (c *Client) Capabilities() hypervisor.Capabilities { func capabilities() hypervisor.Capabilities { return hypervisor.Capabilities{ - SupportsSnapshot: runtime.GOARCH == "arm64", + // Snapshot/standby support is runtime-derived: it requires Apple + // Silicon AND macOS 14+. An arm64-only check would overstate + // support on macOS 13 hosts. + SupportsSnapshot: saveRestoreSupported(), SupportsHotplugMemory: false, SupportsBalloonControl: true, SupportsPause: true, diff --git a/lib/hypervisor/vz/save_restore_support.go b/lib/hypervisor/vz/save_restore_support.go new file mode 100644 index 000000000..a62644183 --- /dev/null +++ b/lib/hypervisor/vz/save_restore_support.go @@ -0,0 +1,40 @@ +package vz + +import ( + "strconv" + "strings" +) + +// saveRestoreMinMacOSMajor is the minimum macOS major version whose +// Virtualization.framework exposes VM save/restore (VZVirtualMachine +// saveMachineStateToURL / restoreMachineStateFromURL). +const saveRestoreMinMacOSMajor = 14 + +// SaveRestoreSupported reports whether Virtualization.framework VM +// save/restore (snapshots, and therefore standby) is available on a host with +// the given GOOS, GOARCH, and macOS product version (e.g. "14.5.1"). The +// product version is only consulted on darwin/arm64; an empty or unparsable +// version is treated as unsupported so a failed probe never overstates +// support. +func SaveRestoreSupported(goos, goarch, productVersion string) bool { + if goos != "darwin" || goarch != "arm64" { + return false + } + major, ok := parseMacOSMajorVersion(productVersion) + return ok && major >= saveRestoreMinMacOSMajor +} + +// parseMacOSMajorVersion extracts the major component from a macOS product +// version string like "14", "14.5", or "14.5.1". +func parseMacOSMajorVersion(productVersion string) (int, bool) { + productVersion = strings.TrimSpace(productVersion) + if productVersion == "" { + return 0, false + } + majorStr, _, _ := strings.Cut(productVersion, ".") + major, err := strconv.Atoi(majorStr) + if err != nil || major < 0 { + return 0, false + } + return major, true +} diff --git a/lib/hypervisor/vz/save_restore_support_darwin.go b/lib/hypervisor/vz/save_restore_support_darwin.go new file mode 100644 index 000000000..6b8833e60 --- /dev/null +++ b/lib/hypervisor/vz/save_restore_support_darwin.go @@ -0,0 +1,26 @@ +//go:build darwin + +package vz + +import ( + "runtime" + + "golang.org/x/sys/unix" +) + +// macOSProductVersion returns the host macOS product version (e.g. "14.5.1"). +// It is a variable so tests can stub the probe. +var macOSProductVersion = func() string { + v, err := unix.Sysctl("kern.osproductversion") + if err != nil { + return "" + } + return v +} + +// saveRestoreSupported reports whether this host supports VZ VM save/restore. +// Detection is runtime-derived (Apple Silicon + macOS 14+) so static +// capabilities never overstate snapshot/standby support on older macOS. +func saveRestoreSupported() bool { + return SaveRestoreSupported(runtime.GOOS, runtime.GOARCH, macOSProductVersion()) +} diff --git a/lib/hypervisor/vz/save_restore_support_darwin_test.go b/lib/hypervisor/vz/save_restore_support_darwin_test.go new file mode 100644 index 000000000..5e180be6b --- /dev/null +++ b/lib/hypervisor/vz/save_restore_support_darwin_test.go @@ -0,0 +1,25 @@ +//go:build darwin + +package vz + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +// TestCapabilitiesDeriveSnapshotFromHost proves the hypervisor's advertised +// capabilities track the probed macOS version instead of a static arch check. +func TestCapabilitiesDeriveSnapshotFromHost(t *testing.T) { + orig := macOSProductVersion + t.Cleanup(func() { macOSProductVersion = orig }) + + macOSProductVersion = func() string { return "13.6" } + require.False(t, capabilities().SupportsSnapshot, "macOS 13 must not advertise snapshot/standby support") + + macOSProductVersion = func() string { return "14.5" } + require.Equal(t, saveRestoreSupported(), capabilities().SupportsSnapshot) + + macOSProductVersion = func() string { return "" } + require.False(t, capabilities().SupportsSnapshot, "failed version probe must not advertise snapshot support") +} diff --git a/lib/hypervisor/vz/save_restore_support_test.go b/lib/hypervisor/vz/save_restore_support_test.go new file mode 100644 index 000000000..6b3d4c687 --- /dev/null +++ b/lib/hypervisor/vz/save_restore_support_test.go @@ -0,0 +1,33 @@ +package vz + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSaveRestoreSupported(t *testing.T) { + tests := []struct { + name string + goos string + goarch string + productVersion string + want bool + }{ + {name: "linux arm64 is never vz", goos: "linux", goarch: "arm64", productVersion: "14.5", want: false}, + {name: "darwin amd64 unsupported", goos: "darwin", goarch: "amd64", productVersion: "14.5", want: false}, + {name: "macOS 13 overstates nothing", goos: "darwin", goarch: "arm64", productVersion: "13.6", want: false}, + {name: "macOS 14 minimum", goos: "darwin", goarch: "arm64", productVersion: "14.0", want: true}, + {name: "macOS 14 patch", goos: "darwin", goarch: "arm64", productVersion: "14.5.1", want: true}, + {name: "macOS 15", goos: "darwin", goarch: "arm64", productVersion: "15.2", want: true}, + {name: "macOS 26", goos: "darwin", goarch: "arm64", productVersion: "26.0", want: true}, + {name: "empty version is unsupported", goos: "darwin", goarch: "arm64", productVersion: "", want: false}, + {name: "unparsable version is unsupported", goos: "darwin", goarch: "arm64", productVersion: "unknown", want: false}, + {name: "major-only version", goos: "darwin", goarch: "arm64", productVersion: "14", want: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, SaveRestoreSupported(tt.goos, tt.goarch, tt.productVersion)) + }) + } +} diff --git a/lib/instances/manager.go b/lib/instances/manager.go index 8e8e25f3f..98fe43fc7 100644 --- a/lib/instances/manager.go +++ b/lib/instances/manager.go @@ -27,6 +27,9 @@ import ( type Manager interface { ListInstances(ctx context.Context, filter *ListInstancesFilter) ([]Instance, error) + // DefaultHypervisor returns the effective default hypervisor type used for + // launches that do not specify one. + DefaultHypervisor() hypervisor.Type ListSnapshots(ctx context.Context, filter *ListSnapshotsFilter) ([]Snapshot, error) GetSnapshot(ctx context.Context, snapshotID string) (*Snapshot, error) CreateInstance(ctx context.Context, req CreateInstanceRequest) (*Instance, error) @@ -690,6 +693,12 @@ func (m *manager) UpdateInstance(ctx context.Context, id string, req UpdateInsta return inst, err } +// DefaultHypervisor returns the effective default hypervisor type used for +// launches that do not specify one. +func (m *manager) DefaultHypervisor() hypervisor.Type { + return m.defaultHypervisor +} + // ListInstances returns instances, optionally filtered by the given criteria. // Pass nil to return all instances. func (m *manager) ListInstances(ctx context.Context, filter *ListInstancesFilter) ([]Instance, error) { diff --git a/lib/instances/wait_test.go b/lib/instances/wait_test.go index bab6f06d7..640a60056 100644 --- a/lib/instances/wait_test.go +++ b/lib/instances/wait_test.go @@ -28,6 +28,10 @@ func (s *stubManager) GetInstance(ctx context.Context, id string) (*Instance, er return nil, ErrNotFound } +func (s *stubManager) DefaultHypervisor() hypervisor.Type { + return hypervisor.TypeCloudHypervisor +} + // Unused interface methods — only GetInstance and SubscribeLifecycleEvents are needed. func (s *stubManager) ListInstances(context.Context, *ListInstancesFilter) ([]Instance, error) { return nil, nil diff --git a/lib/network/default_network_test.go b/lib/network/default_network_test.go new file mode 100644 index 000000000..bed817c7b --- /dev/null +++ b/lib/network/default_network_test.go @@ -0,0 +1,19 @@ +package network + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGuestToGuestEnabled(t *testing.T) { + t.Parallel() + require.False(t, GuestToGuestEnabled(&Network{Isolated: true}), + "isolated default network blocks direct guest-to-guest traffic") + if NetworkModel() == "bridge" { + require.True(t, GuestToGuestEnabled(&Network{Isolated: false})) + } else { + require.False(t, GuestToGuestEnabled(&Network{Isolated: false}), + "NAT networking never provides direct guest-to-guest reachability") + } +} diff --git a/lib/network/model_darwin.go b/lib/network/model_darwin.go new file mode 100644 index 000000000..129ffa356 --- /dev/null +++ b/lib/network/model_darwin.go @@ -0,0 +1,16 @@ +//go:build darwin + +package network + +// NetworkModel identifies the guest networking model in use on this host. +// macOS hosts use Virtualization.framework-provided NAT. +func NetworkModel() string { + return "nat" +} + +// GuestToGuestEnabled reports whether direct VM-to-VM traffic is permitted on +// the given default network. Each vz guest sits behind its own NAT context, +// so direct guest-to-guest reachability is not provided. +func GuestToGuestEnabled(_ *Network) bool { + return false +} diff --git a/lib/network/model_linux.go b/lib/network/model_linux.go new file mode 100644 index 000000000..7a74a0f40 --- /dev/null +++ b/lib/network/model_linux.go @@ -0,0 +1,16 @@ +//go:build linux + +package network + +// NetworkModel identifies the guest networking model in use on this host. +// Linux hosts use a bridge with per-VM TAP devices and port isolation. +func NetworkModel() string { + return "bridge" +} + +// GuestToGuestEnabled reports whether direct VM-to-VM traffic is permitted on +// the given default network. Linux default networks use per-TAP port +// isolation, so guests cannot reach each other directly. +func GuestToGuestEnabled(n *Network) bool { + return n != nil && !n.Isolated +} diff --git a/lib/oapi/oapi.go b/lib/oapi/oapi.go index f2c406528..3aafe8a66 100644 --- a/lib/oapi/oapi.go +++ b/lib/oapi/oapi.go @@ -81,6 +81,12 @@ const ( BuilderStatusReady BuilderStatus = "ready" ) +// Defines values for CapabilitiesNetworkModel. +const ( + Bridge CapabilitiesNetworkModel = "bridge" + Nat CapabilitiesNetworkModel = "nat" +) + // Defines values for CreateInstanceRequestHypervisor. const ( CreateInstanceRequestHypervisorCloudHypervisor CreateInstanceRequestHypervisor = "cloud-hypervisor" @@ -490,6 +496,110 @@ type Builder struct { // BuilderStatus Builder lifecycle status type BuilderStatus string +// Capabilities defines model for Capabilities. +type Capabilities struct { + // Features Stable feature IDs clients can gate behavior on. Base IDs are always + // present: "instances", "images", "builds", "volumes", "ingress", + // "exec", "logs". Conditional IDs reflect the effective default + // runtime and host platform: "standby", "snapshots", "fork", + // "pause", "hotplug-memory", "balloon-control", "vsock", + // "gpu-passthrough", "disk-io-limit", "disk-resize", "devices", + // "rosetta-emulation". + Features []string `json:"features"` + Host CapabilitiesHost `json:"host"` + Images CapabilitiesImages `json:"images"` + Network CapabilitiesNetwork `json:"network"` + Runtime CapabilitiesRuntime `json:"runtime"` + Server CapabilitiesServer `json:"server"` +} + +// CapabilitiesHost defines model for CapabilitiesHost. +type CapabilitiesHost struct { + // Arch Host CPU architecture + Arch string `json:"arch"` + + // Os Host operating system + Os string `json:"os"` +} + +// CapabilitiesImages defines model for CapabilitiesImages. +type CapabilitiesImages struct { + // DefaultPlatform Image platform selected when a create request omits one + DefaultPlatform string `json:"default_platform"` + + // Platforms Image platforms (os/arch) this host can run. Includes emulated + // platforms (e.g. linux/amd64 via Rosetta on Apple Silicon macOS). + Platforms []string `json:"platforms"` +} + +// CapabilitiesNetwork defines model for CapabilitiesNetwork. +type CapabilitiesNetwork struct { + // Gateway Guest-visible host gateway IP. Guests reach host services (including + // host ingress) through this address. + Gateway string `json:"gateway"` + + // GuestToGuest Whether direct VM-to-VM traffic is permitted on the default network + GuestToGuest bool `json:"guest_to_guest"` + + // Model Guest networking model. "bridge" is a Linux bridge with per-VM TAP + // devices; "nat" is hypervisor-provided NAT (macOS). + Model CapabilitiesNetworkModel `json:"model"` + + // Subnet Guest subnet CIDR + Subnet *string `json:"subnet,omitempty"` +} + +// CapabilitiesNetworkModel Guest networking model. "bridge" is a Linux bridge with per-VM TAP +// devices; "nat" is hypervisor-provided NAT (macOS). +type CapabilitiesNetworkModel string + +// CapabilitiesRuntime defines model for CapabilitiesRuntime. +type CapabilitiesRuntime struct { + // BalloonControl Runtime memory balloon control is supported by the effective default runtime + BalloonControl bool `json:"balloon_control"` + + // Default Effective default runtime used for launches that do not name one. + // Reported as a capability-bearing runtime identifier; clients should + // gate behavior on the boolean features below rather than this value. + Default string `json:"default"` + + // DiskIoLimit Disk I/O rate limiting is supported by the effective default runtime + DiskIoLimit bool `json:"disk_io_limit"` + + // DiskResize Live disk resize is supported by the effective default runtime + DiskResize bool `json:"disk_resize"` + + // GpuPassthrough GPU/PCI device passthrough is supported by the effective default runtime + GpuPassthrough bool `json:"gpu_passthrough"` + + // HotplugMemory Live memory hotplug resize is supported by the effective default runtime + HotplugMemory bool `json:"hotplug_memory"` + + // Pause Pause/resume is supported by the effective default runtime + Pause bool `json:"pause"` + + // Snapshot Snapshot/restore is supported by the effective default runtime on this host + Snapshot bool `json:"snapshot"` + + // Standby Standby (pause + memory snapshot) and restore are supported on this host + Standby bool `json:"standby"` + + // Supported Runtime identifiers that can run on this host platform + Supported []string `json:"supported"` + + // Vsock Vsock guest communication is supported by the effective default runtime + Vsock bool `json:"vsock"` +} + +// CapabilitiesServer defines model for CapabilitiesServer. +type CapabilitiesServer struct { + // ApiVersion API contract version (matches the OpenAPI document info version) + ApiVersion string `json:"api_version"` + + // Version Server build version (short git revision, with "-dirty" suffix for uncommitted builds, or "unknown") + Version string `json:"version"` +} + // CreateBuilderRequest defines model for CreateBuilderRequest. type CreateBuilderRequest struct { // DiskSizeGb Cache disk size in gigabytes. Omit to use the server default. @@ -2132,6 +2242,9 @@ type ClientInterface interface { // GetBuildEvents request GetBuildEvents(ctx context.Context, id string, params *GetBuildEventsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetCapabilities request + GetCapabilities(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + // ListDevices request ListDevices(ctx context.Context, params *ListDevicesParams, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -2450,6 +2563,18 @@ func (c *Client) GetBuildEvents(ctx context.Context, id string, params *GetBuild return c.Client.Do(req) } +func (c *Client) GetCapabilities(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetCapabilitiesRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) ListDevices(ctx context.Context, params *ListDevicesParams, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewListDevicesRequest(c.Server, params) if err != nil { @@ -3647,6 +3772,33 @@ func NewGetBuildEventsRequest(server string, id string, params *GetBuildEventsPa return req, nil } +// NewGetCapabilitiesRequest generates requests for GetCapabilities +func NewGetCapabilitiesRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/capabilities") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + // NewListDevicesRequest generates requests for ListDevices func NewListDevicesRequest(server string, params *ListDevicesParams) (*http.Request, error) { var err error @@ -5941,6 +6093,9 @@ type ClientWithResponsesInterface interface { // GetBuildEventsWithResponse request GetBuildEventsWithResponse(ctx context.Context, id string, params *GetBuildEventsParams, reqEditors ...RequestEditorFn) (*GetBuildEventsResponse, error) + // GetCapabilitiesWithResponse request + GetCapabilitiesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetCapabilitiesResponse, error) + // ListDevicesWithResponse request ListDevicesWithResponse(ctx context.Context, params *ListDevicesParams, reqEditors ...RequestEditorFn) (*ListDevicesResponse, error) @@ -6375,6 +6530,29 @@ func (r GetBuildEventsResponse) StatusCode() int { return 0 } +type GetCapabilitiesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *Capabilities + JSON500 *Error +} + +// Status returns HTTPResponse.Status +func (r GetCapabilitiesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetCapabilitiesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type ListDevicesResponse struct { Body []byte HTTPResponse *http.Response @@ -7735,6 +7913,15 @@ func (c *ClientWithResponses) GetBuildEventsWithResponse(ctx context.Context, id return ParseGetBuildEventsResponse(rsp) } +// GetCapabilitiesWithResponse request returning *GetCapabilitiesResponse +func (c *ClientWithResponses) GetCapabilitiesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetCapabilitiesResponse, error) { + rsp, err := c.GetCapabilities(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetCapabilitiesResponse(rsp) +} + // ListDevicesWithResponse request returning *ListDevicesResponse func (c *ClientWithResponses) ListDevicesWithResponse(ctx context.Context, params *ListDevicesParams, reqEditors ...RequestEditorFn) (*ListDevicesResponse, error) { rsp, err := c.ListDevices(ctx, params, reqEditors...) @@ -8778,6 +8965,39 @@ func ParseGetBuildEventsResponse(rsp *http.Response) (*GetBuildEventsResponse, e return response, nil } +// ParseGetCapabilitiesResponse parses an HTTP response from a GetCapabilitiesWithResponse call +func ParseGetCapabilitiesResponse(rsp *http.Response) (*GetCapabilitiesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetCapabilitiesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest Capabilities + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + // ParseListDevicesResponse parses an HTTP response from a ListDevicesWithResponse call func ParseListDevicesResponse(rsp *http.Response) (*ListDevicesResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) @@ -11116,6 +11336,9 @@ type ServerInterface interface { // Stream build events (SSE) // (GET /builds/{id}/events) GetBuildEvents(w http.ResponseWriter, r *http.Request, id string, params GetBuildEventsParams) + // Get host capabilities + // (GET /capabilities) + GetCapabilities(w http.ResponseWriter, r *http.Request) // List registered devices // (GET /devices) ListDevices(w http.ResponseWriter, r *http.Request, params ListDevicesParams) @@ -11335,6 +11558,12 @@ func (_ Unimplemented) GetBuildEvents(w http.ResponseWriter, r *http.Request, id w.WriteHeader(http.StatusNotImplemented) } +// Get host capabilities +// (GET /capabilities) +func (_ Unimplemented) GetCapabilities(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + // List registered devices // (GET /devices) func (_ Unimplemented) ListDevices(w http.ResponseWriter, r *http.Request, params ListDevicesParams) { @@ -11953,6 +12182,26 @@ func (siw *ServerInterfaceWrapper) GetBuildEvents(w http.ResponseWriter, r *http handler.ServeHTTP(w, r) } +// GetCapabilities operation middleware +func (siw *ServerInterfaceWrapper) GetCapabilities(w http.ResponseWriter, r *http.Request) { + + ctx := r.Context() + + ctx = context.WithValue(ctx, BearerAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetCapabilities(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // ListDevices operation middleware func (siw *ServerInterfaceWrapper) ListDevices(w http.ResponseWriter, r *http.Request) { @@ -13740,6 +13989,9 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/builds/{id}/events", wrapper.GetBuildEvents) }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/capabilities", wrapper.GetCapabilities) + }) r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/devices", wrapper.ListDevices) }) @@ -14355,6 +14607,31 @@ func (response GetBuildEvents500JSONResponse) VisitGetBuildEventsResponse(w http return json.NewEncoder(w).Encode(response) } +type GetCapabilitiesRequestObject struct { +} + +type GetCapabilitiesResponseObject interface { + VisitGetCapabilitiesResponse(w http.ResponseWriter) error +} + +type GetCapabilities200JSONResponse Capabilities + +func (response GetCapabilities200JSONResponse) VisitGetCapabilitiesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetCapabilities500JSONResponse Error + +func (response GetCapabilities500JSONResponse) VisitGetCapabilitiesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + type ListDevicesRequestObject struct { Params ListDevicesParams } @@ -16611,6 +16888,9 @@ type StrictServerInterface interface { // Stream build events (SSE) // (GET /builds/{id}/events) GetBuildEvents(ctx context.Context, request GetBuildEventsRequestObject) (GetBuildEventsResponseObject, error) + // Get host capabilities + // (GET /capabilities) + GetCapabilities(ctx context.Context, request GetCapabilitiesRequestObject) (GetCapabilitiesResponseObject, error) // List registered devices // (GET /devices) ListDevices(ctx context.Context, request ListDevicesRequestObject) (ListDevicesResponseObject, error) @@ -17066,6 +17346,30 @@ func (sh *strictHandler) GetBuildEvents(w http.ResponseWriter, r *http.Request, } } +// GetCapabilities operation middleware +func (sh *strictHandler) GetCapabilities(w http.ResponseWriter, r *http.Request) { + var request GetCapabilitiesRequestObject + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetCapabilities(ctx, request.(GetCapabilitiesRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetCapabilities") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetCapabilitiesResponseObject); ok { + if err := validResponse.VisitGetCapabilitiesResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // ListDevices operation middleware func (sh *strictHandler) ListDevices(w http.ResponseWriter, r *http.Request, params ListDevicesParams) { var request ListDevicesRequestObject @@ -18493,347 +18797,369 @@ func (sh *strictHandler) GetVolume(w http.ResponseWriter, r *http.Request, id st // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/lib/scopes/scopes.go b/lib/scopes/scopes.go index 84c7c592a..4773437a0 100644 --- a/lib/scopes/scopes.go +++ b/lib/scopes/scopes.go @@ -222,8 +222,9 @@ var RouteScopes = map[string]Scope{ "DELETE /devices/{id}": DeviceDelete, "GET /devices/{id}": DeviceRead, - // Health & Resources + // Health, Capabilities & Resources "GET /health": ResourceRead, + "GET /capabilities": ResourceRead, "GET /resources": ResourceRead, "POST /resources/memory/reclaim": ResourceWrite, diff --git a/openapi.yaml b/openapi.yaml index fcaa132f6..fe818d0ce 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -1425,6 +1425,145 @@ components: type: string enum: [ok] example: ok + + Capabilities: + type: object + required: [server, host, runtime, network, images, features] + properties: + server: + $ref: "#/components/schemas/CapabilitiesServer" + host: + $ref: "#/components/schemas/CapabilitiesHost" + runtime: + $ref: "#/components/schemas/CapabilitiesRuntime" + network: + $ref: "#/components/schemas/CapabilitiesNetwork" + images: + $ref: "#/components/schemas/CapabilitiesImages" + features: + type: array + items: + type: string + description: | + Stable feature IDs clients can gate behavior on. Base IDs are always + present: "instances", "images", "builds", "volumes", "ingress", + "exec", "logs". Conditional IDs reflect the effective default + runtime and host platform: "standby", "snapshots", "fork", + "pause", "hotplug-memory", "balloon-control", "vsock", + "gpu-passthrough", "disk-io-limit", "disk-resize", "devices", + "rosetta-emulation". + + CapabilitiesServer: + type: object + required: [version, api_version] + properties: + version: + type: string + description: Server build version (short git revision, with "-dirty" suffix for uncommitted builds, or "unknown") + example: 76a2fdb3 + api_version: + type: string + description: API contract version (matches the OpenAPI document info version) + example: 0.2.0 + + CapabilitiesHost: + type: object + required: [os, arch] + properties: + os: + type: string + description: Host operating system + example: linux + arch: + type: string + description: Host CPU architecture + example: arm64 + + CapabilitiesRuntime: + type: object + required: [default, supported, snapshot, standby, pause, hotplug_memory, balloon_control, vsock, gpu_passthrough, disk_io_limit, disk_resize] + properties: + default: + type: string + description: | + Effective default runtime used for launches that do not name one. + Reported as a capability-bearing runtime identifier; clients should + gate behavior on the boolean features below rather than this value. + example: cloud-hypervisor + supported: + type: array + items: + type: string + description: Runtime identifiers that can run on this host platform + example: [cloud-hypervisor, firecracker, qemu] + snapshot: + type: boolean + description: Snapshot/restore is supported by the effective default runtime on this host + standby: + type: boolean + description: Standby (pause + memory snapshot) and restore are supported on this host + pause: + type: boolean + description: Pause/resume is supported by the effective default runtime + hotplug_memory: + type: boolean + description: Live memory hotplug resize is supported by the effective default runtime + balloon_control: + type: boolean + description: Runtime memory balloon control is supported by the effective default runtime + vsock: + type: boolean + description: Vsock guest communication is supported by the effective default runtime + gpu_passthrough: + type: boolean + description: GPU/PCI device passthrough is supported by the effective default runtime + disk_io_limit: + type: boolean + description: Disk I/O rate limiting is supported by the effective default runtime + disk_resize: + type: boolean + description: Live disk resize is supported by the effective default runtime + + CapabilitiesNetwork: + type: object + required: [model, gateway, guest_to_guest] + properties: + model: + type: string + enum: [bridge, nat] + description: | + Guest networking model. "bridge" is a Linux bridge with per-VM TAP + devices; "nat" is hypervisor-provided NAT (macOS). + gateway: + type: string + description: | + Guest-visible host gateway IP. Guests reach host services (including + host ingress) through this address. + example: 10.100.0.1 + subnet: + type: string + description: Guest subnet CIDR + example: 10.100.0.0/16 + guest_to_guest: + type: boolean + description: Whether direct VM-to-VM traffic is permitted on the default network + + CapabilitiesImages: + type: object + required: [platforms, default_platform] + properties: + platforms: + type: array + items: + type: string + description: | + Image platforms (os/arch) this host can run. Includes emulated + platforms (e.g. linux/amd64 via Rosetta on Apple Silicon macOS). + example: [linux/arm64, linux/amd64] + default_platform: + type: string + description: Image platform selected when a create request omits one + example: linux/arm64 IngressMatch: type: object @@ -2263,6 +2402,35 @@ paths: schema: $ref: "#/components/schemas/Health" + /capabilities: + get: + summary: Get host capabilities + description: | + Returns machine-readable host capabilities: server and API version, host + OS/architecture, effective default-runtime features, guest networking model + and host gateway, supported image platforms, and stable feature IDs. + + Runtime-derived values reflect the actual host (for example, snapshot and + standby support on macOS is gated on the host OS version), so clients can + gate behavior on capabilities without selecting or naming a concrete + hypervisor. + operationId: getCapabilities + security: + - bearerAuth: [] + responses: + 200: + description: Host capabilities + content: + application/json: + schema: + $ref: "#/components/schemas/Capabilities" + 500: + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /resources: get: summary: Get host resource capacity and allocations diff --git a/stainless.yaml b/stainless.yaml index 3c40590a8..79ecffc08 100644 --- a/stainless.yaml +++ b/stainless.yaml @@ -65,6 +65,17 @@ resources: methods: check: get /health + capabilities: + models: + capabilities: "#/components/schemas/Capabilities" + capabilities_server: "#/components/schemas/CapabilitiesServer" + capabilities_host: "#/components/schemas/CapabilitiesHost" + capabilities_runtime: "#/components/schemas/CapabilitiesRuntime" + capabilities_network: "#/components/schemas/CapabilitiesNetwork" + capabilities_images: "#/components/schemas/CapabilitiesImages" + methods: + get: get /capabilities + images: # Configure the models--named types--defined in the resource. Each key in the # object is the name of the model and the value is either the name of a schema in From f8a1ca57272d7d65387d7ac0826048ae3d7d45f4 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sat, 15 Aug 2026 14:57:06 -0400 Subject: [PATCH 02/11] Redesign capabilities endpoint around multi-runtime registry enumeration - Report every runtime available on the host (registry-driven), each with its own feature IDs; retain configured default identity + availability - Platform-gate backend capability registration (linux backends via build-tagged files, vz already darwin-only); qemu-microvm registers only where its board resolves, so it appears automatically on linux/amd64 - Add hypervisor.RegisteredRuntimes deterministic enumeration and Capabilities.FeatureIDs/SupportsStandby/SupportsFork beside the struct - One canonical per-runtime representation (feature IDs, no boolean twins) - Optional network gateway/subnet (never empty-string), typed network.Model contract, truthful guest-to-guest docs - Simplify emulation detection to host os/arch; drop dead imagePlatforms fallback; align OpenAPI descriptions; map CapabilitiesDefaultRuntime in stainless.yaml --- cmd/api/api/capabilities.go | 180 ++-- cmd/api/api/capabilities_darwin_test.go | 32 + cmd/api/api/capabilities_linux_test.go | 33 + cmd/api/api/capabilities_test.go | 221 +++-- lib/hypervisor/cloudhypervisor/process.go | 1 - .../cloudhypervisor/register_linux.go | 11 + lib/hypervisor/features.go | 86 ++ lib/hypervisor/features_test.go | 126 +++ lib/hypervisor/firecracker/process.go | 1 - lib/hypervisor/firecracker/register_linux.go | 11 + lib/hypervisor/hypervisor.go | 41 +- lib/hypervisor/qemu/process.go | 1 - lib/hypervisor/qemu/register_linux.go | 17 + lib/hypervisor/qemu/register_linux_test.go | 26 + lib/network/default_network_test.go | 12 +- lib/network/model.go | 13 + lib/network/model_darwin.go | 9 +- lib/network/model_linux.go | 12 +- lib/oapi/oapi.go | 827 +++++++++--------- openapi.yaml | 116 +-- stainless.yaml | 1 + 21 files changed, 1110 insertions(+), 667 deletions(-) create mode 100644 cmd/api/api/capabilities_darwin_test.go create mode 100644 cmd/api/api/capabilities_linux_test.go create mode 100644 lib/hypervisor/cloudhypervisor/register_linux.go create mode 100644 lib/hypervisor/features.go create mode 100644 lib/hypervisor/features_test.go create mode 100644 lib/hypervisor/firecracker/register_linux.go create mode 100644 lib/hypervisor/qemu/register_linux.go create mode 100644 lib/hypervisor/qemu/register_linux_test.go create mode 100644 lib/network/model.go diff --git a/cmd/api/api/capabilities.go b/cmd/api/api/capabilities.go index 30c294561..4ad07c8dd 100644 --- a/cmd/api/api/capabilities.go +++ b/cmd/api/api/capabilities.go @@ -3,7 +3,6 @@ package api import ( "context" "runtime" - "slices" "sync" "github.com/kernel/hypeman/lib/hypervisor" @@ -13,9 +12,10 @@ import ( "github.com/kernel/hypeman/lib/oapi" ) -// Stable feature IDs reported by the capabilities endpoint. Clients gate -// behavior on these (and the structured runtime booleans) rather than on -// hypervisor names. +// Server-level feature IDs: API surfaces this server exposes regardless of +// which runtime backs an instance. Per-runtime feature IDs are owned by +// lib/hypervisor (hypervisor.Capabilities.FeatureIDs) so adding a runtime +// capability never requires touching this handler. const ( featureInstances = "instances" featureImages = "images" @@ -24,16 +24,6 @@ const ( featureIngress = "ingress" featureExec = "exec" featureLogs = "logs" - featureStandby = "standby" - featureSnapshots = "snapshots" - featureFork = "fork" - featurePause = "pause" - featureHotplugMemory = "hotplug-memory" - featureBalloonControl = "balloon-control" - featureVsock = "vsock" - featureGPUPassthrough = "gpu-passthrough" - featureDiskIOLimit = "disk-io-limit" - featureDiskResize = "disk-resize" featureDevices = "devices" featureRosettaEmulation = "rosetta-emulation" ) @@ -56,22 +46,29 @@ func (s *ApiService) GetCapabilities(ctx context.Context, _ oapi.GetCapabilities if s.InstanceManager != nil { defaultRuntime = s.InstanceManager.DefaultHypervisor() } - supported := supportedRuntimes(runtime.GOOS) - caps, capsKnown := hypervisor.CapabilitiesForType(defaultRuntime) - if capsKnown && !slices.Contains(supported, string(defaultRuntime)) { - // The configured default runtime cannot run on this host platform; - // advertising its features would overstate support. - capsKnown = false + + // The capability registry is platform-gated at registration time, so its + // contents are exactly the runtimes this host can launch — including ones + // added after this handler was written. + registered := hypervisor.RegisteredRuntimes() + runtimes := make([]oapi.CapabilitiesRuntime, 0, len(registered)) + defaultAvailable := false + for _, rt := range registered { + if rt.Type == defaultRuntime { + defaultAvailable = true + } + runtimes = append(runtimes, oapi.CapabilitiesRuntime{ + Name: string(rt.Type), + Features: rt.Capabilities.FeatureIDs(), + }) } - if !capsKnown { - // Report zeroed features rather than guessing. - log.WarnContext(ctx, "default runtime is not usable on this host platform", - "runtime", string(defaultRuntime), "host_os", runtime.GOOS) - caps = hypervisor.Capabilities{} + if !defaultAvailable { + // Ordinary launches use the default runtime and will fail on this + // host; surface that in logs as well as in the response. + log.WarnContext(ctx, "configured default runtime is not available on this host", + "runtime", string(defaultRuntime), "host_os", runtime.GOOS, "host_arch", runtime.GOARCH) } - emulation := emulationSupported(runtime.GOOS, runtime.GOARCH, supported) - networkCaps, err := s.networkCapabilities(ctx) if err != nil { log.ErrorContext(ctx, "failed to resolve network capabilities", "error", err) @@ -81,6 +78,8 @@ func (s *ApiService) GetCapabilities(ctx context.Context, _ oapi.GetCapabilities }, nil } + emulation := emulationSupported(runtime.GOOS, runtime.GOARCH) + resp := oapi.Capabilities{ Server: oapi.CapabilitiesServer{ Version: s.Config.Version, @@ -90,25 +89,17 @@ func (s *ApiService) GetCapabilities(ctx context.Context, _ oapi.GetCapabilities Os: runtime.GOOS, Arch: runtime.GOARCH, }, - Runtime: oapi.CapabilitiesRuntime{ - Default: string(defaultRuntime), - Supported: supported, - Snapshot: caps.SupportsSnapshot, - Standby: standbySupported(caps), - Pause: caps.SupportsPause, - HotplugMemory: caps.SupportsHotplugMemory, - BalloonControl: caps.SupportsBalloonControl, - Vsock: caps.SupportsVsock, - GpuPassthrough: caps.SupportsGPUPassthrough, - DiskIoLimit: caps.SupportsDiskIOLimit, - DiskResize: caps.SupportsDiskResize, + DefaultRuntime: oapi.CapabilitiesDefaultRuntime{ + Name: string(defaultRuntime), + Available: defaultAvailable, }, - Network: *networkCaps, + Runtimes: runtimes, + Network: *networkCaps, Images: oapi.CapabilitiesImages{ Platforms: imagePlatforms(runtime.GOARCH, emulation), DefaultPlatform: images.HostPlatformString(), }, - Features: assembleFeatures(runtime.GOOS, caps, emulation), + Features: serverFeatures(runtime.GOOS, emulation), } return oapi.GetCapabilities200JSONResponse(resp), nil @@ -116,10 +107,11 @@ func (s *ApiService) GetCapabilities(ctx context.Context, _ oapi.GetCapabilities // networkCapabilities resolves the guest networking model and the // guest-visible host gateway from the network manager's effective default -// network. +// network. Gateway and subnet are omitted (not serialized as empty strings) +// when no default network has been resolved. func (s *ApiService) networkCapabilities(ctx context.Context) (*oapi.CapabilitiesNetwork, error) { caps := &oapi.CapabilitiesNetwork{ - Model: oapi.CapabilitiesNetworkModel(network.NetworkModel()), + Model: oapiNetworkModel(network.NetworkModel()), GuestToGuest: false, } if s.NetworkManager == nil { @@ -132,7 +124,10 @@ func (s *ApiService) networkCapabilities(ctx context.Context) (*oapi.Capabilitie if nw == nil { return caps, nil } - caps.Gateway = nw.Gateway + if nw.Gateway != "" { + gateway := nw.Gateway + caps.Gateway = &gateway + } if nw.Subnet != "" { subnet := nw.Subnet caps.Subnet = &subnet @@ -141,61 +136,43 @@ func (s *ApiService) networkCapabilities(ctx context.Context) (*oapi.Capabilitie return caps, nil } -// standbySupported reports whether standby (pause + memory snapshot) and -// later restore are supported. Standby requires both snapshot and pause. -func standbySupported(caps hypervisor.Capabilities) bool { - return caps.SupportsSnapshot && caps.SupportsPause -} - -// supportedRuntimes returns the runtime identifiers usable on a host OS. -// This is a platform floor, not a registry listing: a runtime whose package -// registered capabilities but that cannot run on the host (e.g. firecracker -// on macOS) is not listed. -func supportedRuntimes(goos string) []string { - switch goos { - case "darwin": - return []string{string(hypervisor.TypeVZ)} - default: - return []string{ - string(hypervisor.TypeCloudHypervisor), - string(hypervisor.TypeFirecracker), - string(hypervisor.TypeQEMU), - } - } +// oapiNetworkModel maps the network package's typed model onto the API enum, +// keeping lib/network free of oapi dependencies. +func oapiNetworkModel(m network.Model) oapi.CapabilitiesNetworkModel { + switch m { + case network.ModelBridge: + return oapi.Bridge + case network.ModelNAT: + return oapi.Nat + } + // A new network.Model must also be added to the OpenAPI enum; surface it + // verbatim rather than misreporting it as a known model. + return oapi.CapabilitiesNetworkModel(m) } // emulationSupported reports whether the host can boot images built for the -// other CPU architecture. This is a host capability rather than a property of -// the configured default: vz can use Rosetta on Apple Silicon whenever it is -// available on the host. -func emulationSupported(goos, goarch string, supported []string) bool { - return goos == "darwin" && - goarch == "arm64" && - slices.Contains(supported, string(hypervisor.TypeVZ)) +// other CPU architecture. Only Apple Silicon macOS hosts qualify (vz with +// Rosetta). Rosetta installation itself is verified at instance start — a +// launch fails with installation guidance when it is missing — so this +// reports platform eligibility, and the OpenAPI description says so. +func emulationSupported(goos, goarch string) bool { + return goos == "darwin" && goarch == "arm64" } -// imagePlatforms returns the image platforms (os/arch) the host can run: -// the host-native platform plus the emulated one when available. +// imagePlatforms returns the image platforms (os/arch) the host can run: the +// host-native Linux guest platform, plus Rosetta-emulated linux/amd64 on +// Apple Silicon. func imagePlatforms(goarch string, emulation bool) []string { - if goarch == "" { - goarch = runtime.GOARCH - } platforms := []string{"linux/" + goarch} if emulation { - switch goarch { - case "arm64": - platforms = append(platforms, "linux/amd64") - case "amd64": - platforms = append(platforms, "linux/arm64") - } + platforms = append(platforms, "linux/amd64") } return platforms } -// assembleFeatures builds the stable feature ID list: always-present base API -// surfaces plus conditional entries derived from the effective default -// runtime's capabilities and the host platform. -func assembleFeatures(goos string, caps hypervisor.Capabilities, emulation bool) []string { +// serverFeatures builds the server-level feature ID list: API surfaces that +// are always present plus host-platform conditionals. +func serverFeatures(goos string, emulation bool) []string { features := []string{ featureInstances, featureImages, @@ -205,34 +182,7 @@ func assembleFeatures(goos string, caps hypervisor.Capabilities, emulation bool) featureExec, featureLogs, } - if standbySupported(caps) { - features = append(features, featureStandby) - } - if caps.SupportsSnapshot { - features = append(features, featureSnapshots, featureFork) - } - if caps.SupportsPause { - features = append(features, featurePause) - } - if caps.SupportsHotplugMemory { - features = append(features, featureHotplugMemory) - } - if caps.SupportsBalloonControl { - features = append(features, featureBalloonControl) - } - if caps.SupportsVsock { - features = append(features, featureVsock) - } - if caps.SupportsGPUPassthrough { - features = append(features, featureGPUPassthrough) - } - if caps.SupportsDiskIOLimit { - features = append(features, featureDiskIOLimit) - } - if caps.SupportsDiskResize { - features = append(features, featureDiskResize) - } - // Device passthrough (GPU/PCI) is only meaningful on Linux hosts. + // Device (GPU/PCI) passthrough management is only available on Linux hosts. if goos == "linux" { features = append(features, featureDevices) } diff --git a/cmd/api/api/capabilities_darwin_test.go b/cmd/api/api/capabilities_darwin_test.go new file mode 100644 index 000000000..6ddf450fd --- /dev/null +++ b/cmd/api/api/capabilities_darwin_test.go @@ -0,0 +1,32 @@ +//go:build darwin + +package api + +import ( + "testing" + + "github.com/kernel/hypeman/lib/hypervisor" + "github.com/stretchr/testify/require" +) + +// TestRegisteredRuntimesDarwin pins the macOS registration boundary: only vz +// can genuinely launch VMs on macOS (the Linux backends require KVM and +// kernel AF_VSOCK), so the capability registry contains exactly vz even +// though the cloud-hypervisor and qemu packages are linked into the binary. +func TestRegisteredRuntimesDarwin(t *testing.T) { + t.Parallel() + + registered := hypervisor.RegisteredRuntimes() + require.Len(t, registered, 1) + require.Equal(t, hypervisor.TypeVZ, registered[0].Type) + + for _, linuxOnly := range []hypervisor.Type{ + hypervisor.TypeCloudHypervisor, + hypervisor.TypeFirecracker, + hypervisor.TypeQEMU, + hypervisor.TypeQEMUMicroVM, + } { + _, ok := hypervisor.CapabilitiesForType(linuxOnly) + require.False(t, ok, "%s must not register capabilities on macOS", linuxOnly) + } +} diff --git a/cmd/api/api/capabilities_linux_test.go b/cmd/api/api/capabilities_linux_test.go new file mode 100644 index 000000000..fd26b243d --- /dev/null +++ b/cmd/api/api/capabilities_linux_test.go @@ -0,0 +1,33 @@ +//go:build linux + +package api + +import ( + "runtime" + "testing" + + "github.com/kernel/hypeman/lib/hypervisor" + "github.com/stretchr/testify/require" +) + +// TestRegisteredRuntimesLinux pins the Linux registration boundary: the +// capability registry contains exactly the runtimes launchable on this host, +// in deterministic sorted order. qemu-microvm appears only where its x86 +// board exists, and vz never registers off macOS. +func TestRegisteredRuntimesLinux(t *testing.T) { + t.Parallel() + + expected := []string{"cloud-hypervisor", "firecracker", "qemu"} + if runtime.GOARCH == "amd64" { + expected = append(expected, "qemu-microvm") + } + + names := make([]string, 0, len(expected)) + for _, rt := range hypervisor.RegisteredRuntimes() { + names = append(names, string(rt.Type)) + } + require.Equal(t, expected, names) + + _, vzRegistered := hypervisor.CapabilitiesForType(hypervisor.TypeVZ) + require.False(t, vzRegistered, "vz must not register capabilities on Linux") +} diff --git a/cmd/api/api/capabilities_test.go b/cmd/api/api/capabilities_test.go index d29737d8f..d27d7f9d2 100644 --- a/cmd/api/api/capabilities_test.go +++ b/cmd/api/api/capabilities_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/kernel/hypeman/lib/hypervisor" + "github.com/kernel/hypeman/lib/instances" "github.com/kernel/hypeman/lib/network" "github.com/kernel/hypeman/lib/oapi" "github.com/stretchr/testify/require" @@ -23,11 +24,31 @@ func (s *stubCapabilitiesNetworkManager) EffectiveDefaultNetwork() (*network.Net return s.nw, s.err } -func cloudHypervisorCaps(t *testing.T) hypervisor.Capabilities { +// stubDefaultRuntimeInstanceManager overrides the configured default runtime. +type stubDefaultRuntimeInstanceManager struct { + instances.Manager + defaultRuntime hypervisor.Type +} + +func (s *stubDefaultRuntimeInstanceManager) DefaultHypervisor() hypervisor.Type { + return s.defaultRuntime +} + +func getCapabilities(t *testing.T, svc *ApiService) oapi.Capabilities { t.Helper() - caps, ok := hypervisor.CapabilitiesForType(hypervisor.TypeCloudHypervisor) - require.True(t, ok, "cloud-hypervisor capabilities must be registered") - return caps + resp, err := svc.GetCapabilities(ctx(), oapi.GetCapabilitiesRequestObject{}) + require.NoError(t, err) + okResp, ok := resp.(oapi.GetCapabilities200JSONResponse) + require.True(t, ok, "expected 200 response, got %T", resp) + return oapi.Capabilities(okResp) +} + +func runtimeNames(caps oapi.Capabilities) []string { + names := make([]string, 0, len(caps.Runtimes)) + for _, rt := range caps.Runtimes { + names = append(names, rt.Name) + } + return names } func TestGetCapabilities(t *testing.T) { @@ -44,12 +65,7 @@ func TestGetCapabilities(t *testing.T) { }, } - resp, err := svc.GetCapabilities(ctx(), oapi.GetCapabilitiesRequestObject{}) - require.NoError(t, err) - - okResp, ok := resp.(oapi.GetCapabilities200JSONResponse) - require.True(t, ok, "expected 200 response, got %T", resp) - caps := oapi.Capabilities(okResp) + caps := getCapabilities(t, svc) // Server identity and version serialization require.Equal(t, "testsha123", caps.Server.Version) @@ -61,26 +77,32 @@ func TestGetCapabilities(t *testing.T) { require.Equal(t, runtime.GOOS, caps.Host.Os) require.Equal(t, runtime.GOARCH, caps.Host.Arch) - // Effective default runtime and its capabilities. The test-service manager - // defaults to cloud-hypervisor; on macOS that default cannot run, so the - // endpoint must report zeroed runtime features rather than overstate. - require.Equal(t, string(hypervisor.TypeCloudHypervisor), caps.Runtime.Default) - require.NotEmpty(t, caps.Runtime.Supported) - if runtime.GOOS == "darwin" { - require.Equal(t, []string{string(hypervisor.TypeVZ)}, caps.Runtime.Supported) - require.False(t, caps.Runtime.Standby, "unusable default runtime must not advertise standby") - require.False(t, caps.Runtime.Snapshot) + // Every host-available runtime is reported, with its own feature IDs + // derived from the registered capability set — no handler-owned mapping. + registered := hypervisor.RegisteredRuntimes() + require.Len(t, caps.Runtimes, len(registered)) + for i, rt := range registered { + require.Equal(t, string(rt.Type), caps.Runtimes[i].Name) + require.Equal(t, rt.Capabilities.FeatureIDs(), caps.Runtimes[i].Features) + require.NotNil(t, caps.Runtimes[i].Features, "features must serialize as [], not null") + } + + // The configured default identity is retained. The test-service manager + // defaults to cloud-hypervisor, which is available on Linux only. + require.Equal(t, string(hypervisor.TypeCloudHypervisor), caps.DefaultRuntime.Name) + if runtime.GOOS == "linux" { + require.True(t, caps.DefaultRuntime.Available) + require.Contains(t, runtimeNames(caps), caps.DefaultRuntime.Name) } else { - require.Contains(t, caps.Runtime.Supported, caps.Runtime.Default) - chCaps := cloudHypervisorCaps(t) - require.Equal(t, chCaps.SupportsSnapshot, caps.Runtime.Snapshot) - require.Equal(t, chCaps.SupportsPause, caps.Runtime.Pause) - require.True(t, caps.Runtime.Standby) + require.False(t, caps.DefaultRuntime.Available, + "cloud-hypervisor default must not be reported available on non-Linux hosts") + require.NotContains(t, runtimeNames(caps), caps.DefaultRuntime.Name) } // Network model and guest-visible gateway - require.Equal(t, oapi.CapabilitiesNetworkModel(network.NetworkModel()), caps.Network.Model) - require.Equal(t, "10.100.0.1", caps.Network.Gateway) + require.Equal(t, oapiNetworkModel(network.NetworkModel()), caps.Network.Model) + require.NotNil(t, caps.Network.Gateway) + require.Equal(t, "10.100.0.1", *caps.Network.Gateway) require.NotNil(t, caps.Network.Subnet) require.Equal(t, "10.100.0.0/16", *caps.Network.Subnet) require.False(t, caps.Network.GuestToGuest, "isolated default network must report guest_to_guest=false") @@ -88,19 +110,102 @@ func TestGetCapabilities(t *testing.T) { // Image platforms always include the host-native guest platform require.Contains(t, caps.Images.Platforms, caps.Images.DefaultPlatform) - // Base feature IDs are always present + // Server-level feature IDs are always present; per-runtime IDs never + // appear at the server level. for _, f := range []string{"instances", "images", "builds", "volumes", "ingress", "exec", "logs"} { require.Contains(t, caps.Features, f) } - if runtime.GOOS == "darwin" { + require.NotContains(t, caps.Features, hypervisor.FeatureStandby) + require.NotContains(t, caps.Features, hypervisor.FeatureSnapshots) + if runtime.GOOS == "linux" { + require.Contains(t, caps.Features, "devices") + } else { require.NotContains(t, caps.Features, "devices") - require.NotContains(t, caps.Features, "standby") + } +} + +// TestGetCapabilitiesEachRuntimeIndependent proves runtimes report their own +// features independently: the runtimes on one host genuinely differ. +func TestGetCapabilitiesEachRuntimeIndependent(t *testing.T) { + t.Parallel() + svc := newTestService(t) + svc.NetworkManager = &stubCapabilitiesNetworkManager{} + + caps := getCapabilities(t, svc) + + features := make(map[string][]string, len(caps.Runtimes)) + for _, rt := range caps.Runtimes { + features[rt.Name] = rt.Features + } + + if runtime.GOOS != "linux" { + t.Skipf("linux-only runtime matrix (GOOS=%s)", runtime.GOOS) + } + + // cloud-hypervisor supports memory hotplug; firecracker and qemu do not. + require.Contains(t, features["cloud-hypervisor"], hypervisor.FeatureHotplugMemory) + require.NotContains(t, features["firecracker"], hypervisor.FeatureHotplugMemory) + require.NotContains(t, features["qemu"], hypervisor.FeatureHotplugMemory) + + // firecracker has no GPU passthrough; qemu (standard board) does. + require.NotContains(t, features["firecracker"], hypervisor.FeatureGPUPassthrough) + require.Contains(t, features["qemu"], hypervisor.FeatureGPUPassthrough) + + // Lifecycle features hold for all Linux runtimes. + for name, ids := range features { + require.Contains(t, ids, hypervisor.FeatureSnapshots, "runtime %s", name) + require.Contains(t, ids, hypervisor.FeatureStandby, "runtime %s", name) + require.Contains(t, ids, hypervisor.FeatureVsock, "runtime %s", name) + } + + // qemu-microvm appears automatically on amd64 and, unlike standard qemu, + // must not advertise PCI passthrough. + if runtime.GOARCH == "amd64" { + require.Contains(t, features, "qemu-microvm") + require.NotContains(t, features["qemu-microvm"], hypervisor.FeatureGPUPassthrough) } else { - require.Contains(t, caps.Features, "standby") - require.Contains(t, caps.Features, "devices") + require.NotContains(t, features, "qemu-microvm") } } +// TestGetCapabilitiesDefaultNotAvailable pins the contract when the configured +// default runtime cannot run on this host: the default identity is retained, +// available=false, and the available runtime list is unaffected. +func TestGetCapabilitiesDefaultNotAvailable(t *testing.T) { + t.Parallel() + svc := newTestService(t) + svc.NetworkManager = &stubCapabilitiesNetworkManager{} + unavailable := hypervisor.TypeVZ + if runtime.GOOS == "darwin" { + unavailable = hypervisor.TypeFirecracker + } + svc.InstanceManager = &stubDefaultRuntimeInstanceManager{defaultRuntime: unavailable} + + caps := getCapabilities(t, svc) + + require.Equal(t, string(unavailable), caps.DefaultRuntime.Name) + require.False(t, caps.DefaultRuntime.Available) + require.NotContains(t, runtimeNames(caps), string(unavailable)) + require.Equal(t, len(hypervisor.RegisteredRuntimes()), len(caps.Runtimes), + "an unavailable default must not hide the runtimes that are available") +} + +// TestGetCapabilitiesNoDefaultNetwork pins the gateway-absence contract: when +// no default network resolves, gateway and subnet are omitted rather than +// serialized as empty strings. +func TestGetCapabilitiesNoDefaultNetwork(t *testing.T) { + t.Parallel() + svc := newTestService(t) + svc.NetworkManager = &stubCapabilitiesNetworkManager{nw: nil} + + caps := getCapabilities(t, svc) + + require.Nil(t, caps.Network.Gateway) + require.Nil(t, caps.Network.Subnet) + require.False(t, caps.Network.GuestToGuest) + require.Equal(t, oapiNetworkModel(network.NetworkModel()), caps.Network.Model) +} + func TestGetCapabilitiesNetworkError(t *testing.T) { t.Parallel() svc := newTestService(t) @@ -112,21 +217,18 @@ func TestGetCapabilitiesNetworkError(t *testing.T) { require.True(t, ok, "expected 500 response when network resolution fails, got %T", resp) } -func TestSupportedRuntimes(t *testing.T) { +func TestOapiNetworkModel(t *testing.T) { t.Parallel() - require.Equal(t, - []string{"cloud-hypervisor", "firecracker", "qemu"}, - supportedRuntimes("linux")) - require.Equal(t, []string{"vz"}, supportedRuntimes("darwin")) + require.Equal(t, oapi.Bridge, oapiNetworkModel(network.ModelBridge)) + require.Equal(t, oapi.Nat, oapiNetworkModel(network.ModelNAT)) } func TestEmulationSupported(t *testing.T) { t.Parallel() - require.True(t, emulationSupported("darwin", "arm64", []string{"vz"})) - require.False(t, emulationSupported("darwin", "arm64", []string{"cloud-hypervisor"})) - require.False(t, emulationSupported("darwin", "amd64", []string{"vz"})) - require.False(t, emulationSupported("linux", "arm64", []string{"vz"})) - require.False(t, emulationSupported("linux", "amd64", []string{"cloud-hypervisor"})) + require.True(t, emulationSupported("darwin", "arm64")) + require.False(t, emulationSupported("darwin", "amd64")) + require.False(t, emulationSupported("linux", "arm64")) + require.False(t, emulationSupported("linux", "amd64")) } func TestImagePlatforms(t *testing.T) { @@ -136,44 +238,17 @@ func TestImagePlatforms(t *testing.T) { require.Equal(t, []string{"linux/arm64", "linux/amd64"}, imagePlatforms("arm64", true), - "Apple Silicon macOS with vz advertises Rosetta-emulated amd64") + "Apple Silicon macOS advertises Rosetta-emulated amd64") } -func TestAssembleFeatures(t *testing.T) { +func TestServerFeatures(t *testing.T) { t.Parallel() base := []string{"instances", "images", "builds", "volumes", "ingress", "exec", "logs"} - // Zero capabilities (unknown runtime on this host): base features only. - features := assembleFeatures("linux", hypervisor.Capabilities{}, false) - require.ElementsMatch(t, append(base, "devices"), features) - - // Full Linux cloud-hypervisor capabilities. - features = assembleFeatures("linux", cloudHypervisorCaps(t), false) - require.Subset(t, features, base) - require.Contains(t, features, "standby") - require.Contains(t, features, "snapshots") - require.Contains(t, features, "fork") - require.Contains(t, features, "pause") - require.Contains(t, features, "hotplug-memory") - require.Contains(t, features, "devices") - require.NotContains(t, features, "rosetta-emulation") - - // macOS: no devices feature; emulation adds rosetta. - vzCaps := hypervisor.Capabilities{SupportsSnapshot: true, SupportsPause: true, SupportsBalloonControl: true, SupportsVsock: true} - features = assembleFeatures("darwin", vzCaps, true) - require.NotContains(t, features, "devices") - require.Contains(t, features, "standby") - require.Contains(t, features, "rosetta-emulation") - require.NotContains(t, features, "hotplug-memory") - require.NotContains(t, features, "gpu-passthrough") -} - -func TestStandbyRequiresSnapshotAndPause(t *testing.T) { - t.Parallel() - require.False(t, standbySupported(hypervisor.Capabilities{SupportsSnapshot: true, SupportsPause: false})) - require.False(t, standbySupported(hypervisor.Capabilities{SupportsSnapshot: false, SupportsPause: true})) - require.True(t, standbySupported(hypervisor.Capabilities{SupportsSnapshot: true, SupportsPause: true})) + require.Equal(t, append(base, "devices"), serverFeatures("linux", false)) + require.Equal(t, base, serverFeatures("darwin", false)) + require.Equal(t, append(base, "rosetta-emulation"), serverFeatures("darwin", true)) } // TestAPIVersionMatchesSpec guards the version contract: the endpoint must diff --git a/lib/hypervisor/cloudhypervisor/process.go b/lib/hypervisor/cloudhypervisor/process.go index 3caa36843..14b306be1 100644 --- a/lib/hypervisor/cloudhypervisor/process.go +++ b/lib/hypervisor/cloudhypervisor/process.go @@ -56,7 +56,6 @@ func GetDefaultVersion() vmm.CHVersion { func init() { hypervisor.RegisterSocketName(hypervisor.TypeCloudHypervisor, "ch.sock") - hypervisor.RegisterCapabilities(hypervisor.TypeCloudHypervisor, capabilities()) hypervisor.RegisterClientFactory(hypervisor.TypeCloudHypervisor, func(socketPath string) (hypervisor.Hypervisor, error) { return New(socketPath) }) diff --git a/lib/hypervisor/cloudhypervisor/register_linux.go b/lib/hypervisor/cloudhypervisor/register_linux.go new file mode 100644 index 000000000..a9c023ee2 --- /dev/null +++ b/lib/hypervisor/cloudhypervisor/register_linux.go @@ -0,0 +1,11 @@ +//go:build linux + +package cloudhypervisor + +import "github.com/kernel/hypeman/lib/hypervisor" + +// Cloud Hypervisor requires KVM, so capability registration — and therefore +// its presence in hypervisor.RegisteredRuntimes — is Linux-only. +func init() { + hypervisor.RegisterCapabilities(hypervisor.TypeCloudHypervisor, capabilities()) +} diff --git a/lib/hypervisor/features.go b/lib/hypervisor/features.go new file mode 100644 index 000000000..a83e873ac --- /dev/null +++ b/lib/hypervisor/features.go @@ -0,0 +1,86 @@ +package hypervisor + +// Stable per-runtime feature IDs derived from Capabilities. These are part of +// the public capabilities API contract: clients gate behavior on these IDs +// rather than on hypervisor names. Adding a client-visible Capabilities field +// means adding its ID here, deriving it in FeatureIDs, and documenting it in +// the OpenAPI CapabilitiesRuntime schema — HTTP handlers never derive feature +// lists themselves. +const ( + // FeatureSnapshots: snapshot/restore of instance state. + FeatureSnapshots = "snapshots" + // FeatureStandby: pause + memory snapshot, with later restore. + FeatureStandby = "standby" + // FeatureFork: cloning an instance by restoring a snapshot of it. + FeatureFork = "fork" + // FeaturePause: pause/resume of a running instance. + FeaturePause = "pause" + // FeatureHotplugMemory: live memory resize. + FeatureHotplugMemory = "hotplug-memory" + // FeatureBalloonControl: runtime balloon target changes. + FeatureBalloonControl = "balloon-control" + // FeatureVsock: guest vsock communication. + FeatureVsock = "vsock" + // FeatureGPUPassthrough: GPU/PCI device passthrough. + FeatureGPUPassthrough = "gpu-passthrough" + // FeatureDiskIOLimit: disk I/O rate limiting. + FeatureDiskIOLimit = "disk-io-limit" + // FeatureDiskResize: live disk resize. + FeatureDiskResize = "disk-resize" +) + +// SupportsStandby reports whether standby is genuinely available. Standby +// pauses the VM and then snapshots its memory, so it requires both pause and +// snapshot support — neither alone is sufficient. +func (c Capabilities) SupportsStandby() bool { + return c.SupportsSnapshot && c.SupportsPause +} + +// SupportsFork reports whether instance fork is available. Forking a running +// or standby instance restores a snapshot of the source into the new VM, so +// fork tracks snapshot support rather than being assumed from the backend's +// name. +func (c Capabilities) SupportsFork() bool { + return c.SupportsSnapshot +} + +// FeatureIDs returns the stable feature IDs implied by this capability set, +// in a fixed deterministic order. Only client-visible features are included; +// internal lifecycle hints (snapshot base reuse, pager detachability, host +// snapshot version pinning, graceful VMM shutdown) are implementation details +// and deliberately have no IDs. The result is always non-nil so it serializes +// as an empty JSON array rather than null. +func (c Capabilities) FeatureIDs() []string { + ids := make([]string, 0, 10) + if c.SupportsSnapshot { + ids = append(ids, FeatureSnapshots) + } + if c.SupportsStandby() { + ids = append(ids, FeatureStandby) + } + if c.SupportsFork() { + ids = append(ids, FeatureFork) + } + if c.SupportsPause { + ids = append(ids, FeaturePause) + } + if c.SupportsHotplugMemory { + ids = append(ids, FeatureHotplugMemory) + } + if c.SupportsBalloonControl { + ids = append(ids, FeatureBalloonControl) + } + if c.SupportsVsock { + ids = append(ids, FeatureVsock) + } + if c.SupportsGPUPassthrough { + ids = append(ids, FeatureGPUPassthrough) + } + if c.SupportsDiskIOLimit { + ids = append(ids, FeatureDiskIOLimit) + } + if c.SupportsDiskResize { + ids = append(ids, FeatureDiskResize) + } + return ids +} diff --git a/lib/hypervisor/features_test.go b/lib/hypervisor/features_test.go new file mode 100644 index 000000000..443632ed6 --- /dev/null +++ b/lib/hypervisor/features_test.go @@ -0,0 +1,126 @@ +package hypervisor + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func fullCapabilities() Capabilities { + return Capabilities{ + SupportsSnapshot: true, + SupportsHotplugMemory: true, + SupportsBalloonControl: true, + SupportsPause: true, + SupportsVsock: true, + SupportsGPUPassthrough: true, + SupportsDiskIOLimit: true, + SupportsDiskResize: true, + } +} + +func TestFeatureIDs(t *testing.T) { + t.Parallel() + + t.Run("zero capabilities yield an empty non-nil list", func(t *testing.T) { + t.Parallel() + ids := Capabilities{}.FeatureIDs() + require.NotNil(t, ids, "must serialize as [], not null") + require.Empty(t, ids) + }) + + t.Run("full capabilities yield every client-visible ID in fixed order", func(t *testing.T) { + t.Parallel() + require.Equal(t, []string{ + FeatureSnapshots, + FeatureStandby, + FeatureFork, + FeaturePause, + FeatureHotplugMemory, + FeatureBalloonControl, + FeatureVsock, + FeatureGPUPassthrough, + FeatureDiskIOLimit, + FeatureDiskResize, + }, fullCapabilities().FeatureIDs()) + }) + + t.Run("internal lifecycle hints have no feature IDs", func(t *testing.T) { + t.Parallel() + ids := Capabilities{ + SupportsGracefulVMMShutdown: true, + SupportsSnapshotBaseReuse: true, + RequiresHostSnapshotVersion: true, + SupportsConcurrentForkPrepare: true, + UsesDetachableSnapshotMemoryPager: true, + }.FeatureIDs() + require.Empty(t, ids) + }) + + t.Run("snapshot without pause yields snapshots and fork but not standby", func(t *testing.T) { + t.Parallel() + ids := Capabilities{SupportsSnapshot: true}.FeatureIDs() + require.Equal(t, []string{FeatureSnapshots, FeatureFork}, ids) + }) + + t.Run("pause without snapshot yields pause only", func(t *testing.T) { + t.Parallel() + ids := Capabilities{SupportsPause: true}.FeatureIDs() + require.Equal(t, []string{FeaturePause}, ids) + }) +} + +// TestStandbySemantics pins that standby requires both snapshot and pause: a +// standby transition pauses the VM and then snapshots its memory. +func TestStandbySemantics(t *testing.T) { + t.Parallel() + require.False(t, Capabilities{SupportsSnapshot: true}.SupportsStandby()) + require.False(t, Capabilities{SupportsPause: true}.SupportsStandby()) + require.True(t, Capabilities{SupportsSnapshot: true, SupportsPause: true}.SupportsStandby()) +} + +// TestForkSemantics pins that fork tracks snapshot support: forking restores +// a snapshot of the source instance into the new VM. +func TestForkSemantics(t *testing.T) { + t.Parallel() + require.False(t, Capabilities{}.SupportsFork()) + require.False(t, Capabilities{SupportsPause: true}.SupportsFork()) + require.True(t, Capabilities{SupportsSnapshot: true}.SupportsFork()) +} + +// TestEnumerateRuntimes exercises registry enumeration semantics against a +// local map so the global registry is never mutated by tests. +func TestEnumerateRuntimes(t *testing.T) { + t.Parallel() + + t.Run("empty registry yields an empty non-nil list", func(t *testing.T) { + t.Parallel() + runtimes := enumerateRuntimes(map[Type]Capabilities{}) + require.NotNil(t, runtimes) + require.Empty(t, runtimes) + }) + + t.Run("entries are sorted by type name for deterministic output", func(t *testing.T) { + t.Parallel() + byType := map[Type]Capabilities{ + TypeQEMU: {SupportsPause: true}, + TypeCloudHypervisor: {SupportsSnapshot: true}, + TypeQEMUMicroVM: {}, + TypeFirecracker: {SupportsVsock: true}, + } + runtimes := enumerateRuntimes(byType) + require.Equal(t, []Type{TypeCloudHypervisor, TypeFirecracker, TypeQEMU, TypeQEMUMicroVM}, + []Type{runtimes[0].Type, runtimes[1].Type, runtimes[2].Type, runtimes[3].Type}) + require.True(t, runtimes[0].Capabilities.SupportsSnapshot) + require.True(t, runtimes[1].Capabilities.SupportsVsock) + require.True(t, runtimes[2].Capabilities.SupportsPause) + }) + + t.Run("results are value copies of the registry", func(t *testing.T) { + t.Parallel() + byType := map[Type]Capabilities{TypeQEMU: {SupportsPause: true}} + runtimes := enumerateRuntimes(byType) + runtimes[0].Capabilities.SupportsPause = false + require.True(t, byType[TypeQEMU].SupportsPause, "mutating enumeration output must not affect the registry") + }) +} diff --git a/lib/hypervisor/firecracker/process.go b/lib/hypervisor/firecracker/process.go index ac7f6b340..01fd8ce2e 100644 --- a/lib/hypervisor/firecracker/process.go +++ b/lib/hypervisor/firecracker/process.go @@ -25,7 +25,6 @@ const ( func init() { hypervisor.RegisterSocketName(hypervisor.TypeFirecracker, "fc.sock") - hypervisor.RegisterCapabilities(hypervisor.TypeFirecracker, capabilities()) hypervisor.RegisterClientFactory(hypervisor.TypeFirecracker, func(socketPath string) (hypervisor.Hypervisor, error) { return New(socketPath) }) diff --git a/lib/hypervisor/firecracker/register_linux.go b/lib/hypervisor/firecracker/register_linux.go new file mode 100644 index 000000000..675d35729 --- /dev/null +++ b/lib/hypervisor/firecracker/register_linux.go @@ -0,0 +1,11 @@ +//go:build linux + +package firecracker + +import "github.com/kernel/hypeman/lib/hypervisor" + +// Firecracker requires KVM, so capability registration — and therefore its +// presence in hypervisor.RegisteredRuntimes — is Linux-only. +func init() { + hypervisor.RegisterCapabilities(hypervisor.TypeFirecracker, capabilities()) +} diff --git a/lib/hypervisor/hypervisor.go b/lib/hypervisor/hypervisor.go index 419286137..69b77dfda 100644 --- a/lib/hypervisor/hypervisor.go +++ b/lib/hypervisor/hypervisor.go @@ -4,10 +4,12 @@ package hypervisor import ( + "cmp" "context" "errors" "fmt" "net" + "slices" "time" "github.com/kernel/hypeman/lib/paths" @@ -48,7 +50,9 @@ var socketNames = make(map[Type]string) var vsockSocketNames = make(map[Type]string) // capabilitiesByType maps hypervisor types to their static capabilities. -// Registered by each hypervisor package's init() function. +// Registered by each hypervisor package's init() function. Registration is +// platform-gated (see RegisterCapabilities), so the map's keys double as the +// set of runtimes available on the current host. var capabilitiesByType = make(map[Type]Capabilities) // RegisterSocketName registers the socket filename for a hypervisor type. @@ -81,16 +85,51 @@ func VsockSocketNameForType(t Type) string { } // RegisterCapabilities registers static capabilities for a hypervisor type. +// Backends must register only on platforms where they can genuinely launch +// VMs: the Linux backends (cloud-hypervisor, firecracker, qemu) register from +// linux-only files, and vz registers from its darwin-only package. That keeps +// RegisteredRuntimes truthful without a hand-maintained platform switch. func RegisterCapabilities(t Type, caps Capabilities) { capabilitiesByType[t] = caps } // CapabilitiesForType returns static capabilities for a hypervisor type. +// It reports ok=false for runtimes that cannot run on the current host +// platform, because such runtimes never register capabilities. func CapabilitiesForType(t Type) (Capabilities, bool) { caps, ok := capabilitiesByType[t] return caps, ok } +// RegisteredRuntime pairs a runtime identifier with its registered +// capabilities. +type RegisteredRuntime struct { + Type Type + Capabilities Capabilities +} + +// RegisteredRuntimes enumerates every runtime with registered capabilities, +// sorted by type name so output is deterministic. Because capability +// registration is platform-gated, the result is the set of runtimes the +// linked backends can actually launch on the current host. +func RegisteredRuntimes() []RegisteredRuntime { + return enumerateRuntimes(capabilitiesByType) +} + +// enumerateRuntimes returns the map's entries sorted by type name. Split out +// from RegisteredRuntimes so enumeration semantics are testable without +// mutating the global registry. +func enumerateRuntimes(byType map[Type]Capabilities) []RegisteredRuntime { + runtimes := make([]RegisteredRuntime, 0, len(byType)) + for t, caps := range byType { + runtimes = append(runtimes, RegisteredRuntime{Type: t, Capabilities: caps}) + } + slices.SortFunc(runtimes, func(a, b RegisteredRuntime) int { + return cmp.Compare(a.Type, b.Type) + }) + return runtimes +} + // VMStarter handles the full VM startup sequence. // Each hypervisor implements its own startup flow: // - Cloud Hypervisor: starts process, configures via HTTP API, boots via HTTP API diff --git a/lib/hypervisor/qemu/process.go b/lib/hypervisor/qemu/process.go index ab6438eb2..e4f08bd83 100644 --- a/lib/hypervisor/qemu/process.go +++ b/lib/hypervisor/qemu/process.go @@ -49,7 +49,6 @@ func init() { for _, profile := range []profile{StandardProfile{}, MicroVMProfile{}} { hypervisorType := profile.hypervisorType() hypervisor.RegisterSocketName(hypervisorType, "qemu.sock") - hypervisor.RegisterCapabilities(hypervisorType, profile.capabilities()) t := hypervisorType hypervisor.RegisterClientFactory(t, func(socketPath string) (hypervisor.Hypervisor, error) { return NewForType(socketPath, t) diff --git a/lib/hypervisor/qemu/register_linux.go b/lib/hypervisor/qemu/register_linux.go new file mode 100644 index 000000000..798484955 --- /dev/null +++ b/lib/hypervisor/qemu/register_linux.go @@ -0,0 +1,17 @@ +//go:build linux + +package qemu + +import "github.com/kernel/hypeman/lib/hypervisor" + +// QEMU guests launch with KVM acceleration and kernel AF_VSOCK, so capability +// registration — and therefore presence in hypervisor.RegisteredRuntimes — is +// Linux-only. The microvm board additionally exists only for x86, so +// qemu-microvm registers only where its machine type resolves (linux/amd64); +// the same resolver rejects it at launch time. +func init() { + hypervisor.RegisterCapabilities(hypervisor.TypeQEMU, StandardProfile{}.capabilities()) + if _, err := microVMMachineType(); err == nil { + hypervisor.RegisterCapabilities(hypervisor.TypeQEMUMicroVM, MicroVMProfile{}.capabilities()) + } +} diff --git a/lib/hypervisor/qemu/register_linux_test.go b/lib/hypervisor/qemu/register_linux_test.go new file mode 100644 index 000000000..da70047fc --- /dev/null +++ b/lib/hypervisor/qemu/register_linux_test.go @@ -0,0 +1,26 @@ +//go:build linux + +package qemu + +import ( + "runtime" + "testing" + + "github.com/kernel/hypeman/lib/hypervisor" + "github.com/stretchr/testify/require" +) + +// TestMicroVMRegistrationMatchesBoardSupport pins that qemu-microvm's presence +// in the capability registry tracks the same machine-type resolver that gates +// launches, so the registry never advertises a board the host cannot boot. +func TestMicroVMRegistrationMatchesBoardSupport(t *testing.T) { + t.Parallel() + + _, qemuRegistered := hypervisor.CapabilitiesForType(hypervisor.TypeQEMU) + require.True(t, qemuRegistered, "standard qemu must register on Linux") + + _, boardErr := resolveMachineTypeForPlatform(MachineTypeMicroVM, runtime.GOOS, runtime.GOARCH) + _, microVMRegistered := hypervisor.CapabilitiesForType(hypervisor.TypeQEMUMicroVM) + require.Equal(t, boardErr == nil, microVMRegistered, + "qemu-microvm registration must match microvm board support on %s/%s", runtime.GOOS, runtime.GOARCH) +} diff --git a/lib/network/default_network_test.go b/lib/network/default_network_test.go index bed817c7b..16b38897c 100644 --- a/lib/network/default_network_test.go +++ b/lib/network/default_network_test.go @@ -6,12 +6,20 @@ import ( "github.com/stretchr/testify/require" ) +func TestNetworkModelIsTyped(t *testing.T) { + t.Parallel() + model := NetworkModel() + require.Contains(t, []Model{ModelBridge, ModelNAT}, model) +} + func TestGuestToGuestEnabled(t *testing.T) { t.Parallel() + require.False(t, GuestToGuestEnabled(nil)) require.False(t, GuestToGuestEnabled(&Network{Isolated: true}), "isolated default network blocks direct guest-to-guest traffic") - if NetworkModel() == "bridge" { - require.True(t, GuestToGuestEnabled(&Network{Isolated: false})) + if NetworkModel() == ModelBridge { + require.True(t, GuestToGuestEnabled(&Network{Isolated: false}), + "a non-isolated bridge network permits direct guest-to-guest traffic") } else { require.False(t, GuestToGuestEnabled(&Network{Isolated: false}), "NAT networking never provides direct guest-to-guest reachability") diff --git a/lib/network/model.go b/lib/network/model.go new file mode 100644 index 000000000..d26dbe667 --- /dev/null +++ b/lib/network/model.go @@ -0,0 +1,13 @@ +package network + +// Model identifies the guest networking model a host provides. Each platform +// backend returns its model from NetworkModel; API layers map it onto their +// own wire enums so this package stays free of API dependencies. +type Model string + +const ( + // ModelBridge is a Linux bridge with per-VM TAP devices. + ModelBridge Model = "bridge" + // ModelNAT is hypervisor-provided NAT (Virtualization.framework on macOS). + ModelNAT Model = "nat" +) diff --git a/lib/network/model_darwin.go b/lib/network/model_darwin.go index 129ffa356..24fc9db50 100644 --- a/lib/network/model_darwin.go +++ b/lib/network/model_darwin.go @@ -4,13 +4,14 @@ package network // NetworkModel identifies the guest networking model in use on this host. // macOS hosts use Virtualization.framework-provided NAT. -func NetworkModel() string { - return "nat" +func NetworkModel() Model { + return ModelNAT } // GuestToGuestEnabled reports whether direct VM-to-VM traffic is permitted on -// the given default network. Each vz guest sits behind its own NAT context, -// so direct guest-to-guest reachability is not provided. +// the given network. Each vz guest sits behind its own NAT context, so direct +// guest-to-guest reachability is never provided regardless of the network's +// isolation flag. func GuestToGuestEnabled(_ *Network) bool { return false } diff --git a/lib/network/model_linux.go b/lib/network/model_linux.go index 7a74a0f40..4ef0e909d 100644 --- a/lib/network/model_linux.go +++ b/lib/network/model_linux.go @@ -3,14 +3,16 @@ package network // NetworkModel identifies the guest networking model in use on this host. -// Linux hosts use a bridge with per-VM TAP devices and port isolation. -func NetworkModel() string { - return "bridge" +// Linux hosts use a bridge with per-VM TAP devices. +func NetworkModel() Model { + return ModelBridge } // GuestToGuestEnabled reports whether direct VM-to-VM traffic is permitted on -// the given default network. Linux default networks use per-TAP port -// isolation, so guests cannot reach each other directly. +// the given network. Hypeman provisions its default networks with per-TAP +// port isolation (Isolated=true), which blocks direct guest-to-guest traffic. +// The flag is read from the network rather than assumed so a non-isolated +// network is reported truthfully. func GuestToGuestEnabled(n *Network) bool { return n != nil && !n.Isolated } diff --git a/lib/oapi/oapi.go b/lib/oapi/oapi.go index 3aafe8a66..fe8dc4f93 100644 --- a/lib/oapi/oapi.go +++ b/lib/oapi/oapi.go @@ -498,19 +498,37 @@ type BuilderStatus string // Capabilities defines model for Capabilities. type Capabilities struct { - // Features Stable feature IDs clients can gate behavior on. Base IDs are always - // present: "instances", "images", "builds", "volumes", "ingress", - // "exec", "logs". Conditional IDs reflect the effective default - // runtime and host platform: "standby", "snapshots", "fork", - // "pause", "hotplug-memory", "balloon-control", "vsock", - // "gpu-passthrough", "disk-io-limit", "disk-resize", "devices", - // "rosetta-emulation". + DefaultRuntime CapabilitiesDefaultRuntime `json:"default_runtime"` + + // Features Stable server-level feature IDs: API surfaces this server exposes + // regardless of which runtime backs an instance. Always present: + // "instances", "images", "builds", "volumes", "ingress", "exec", + // "logs". Host-conditional: "devices" (device passthrough + // management, Linux hosts only) and "rosetta-emulation" (Apple + // Silicon macOS hosts). Per-runtime features are reported under each + // runtimes[] entry. Features []string `json:"features"` Host CapabilitiesHost `json:"host"` Images CapabilitiesImages `json:"images"` Network CapabilitiesNetwork `json:"network"` - Runtime CapabilitiesRuntime `json:"runtime"` - Server CapabilitiesServer `json:"server"` + + // Runtimes Every runtime available on this host, each with its own feature + // IDs. Hosts commonly support several runtimes at once (for example + // cloud-hypervisor, firecracker, qemu, and qemu-microvm on + // linux/amd64). Entries are sorted by name. + Runtimes []CapabilitiesRuntime `json:"runtimes"` + Server CapabilitiesServer `json:"server"` +} + +// CapabilitiesDefaultRuntime defines model for CapabilitiesDefaultRuntime. +type CapabilitiesDefaultRuntime struct { + // Available Whether the default runtime is available on this host (appears in + // runtimes). When false, launches that rely on the default will fail + // until the server is reconfigured with an available runtime. + Available bool `json:"available"` + + // Name Runtime used for launches that do not name one + Name string `json:"name"` } // CapabilitiesHost defines model for CapabilitiesHost. @@ -527,16 +545,19 @@ type CapabilitiesImages struct { // DefaultPlatform Image platform selected when a create request omits one DefaultPlatform string `json:"default_platform"` - // Platforms Image platforms (os/arch) this host can run. Includes emulated - // platforms (e.g. linux/amd64 via Rosetta on Apple Silicon macOS). + // Platforms Image platforms (os/arch) this host can run. On Apple Silicon macOS + // this includes linux/amd64, which vz runs via Rosetta; Rosetta + // installation itself is verified at instance start, and launches + // fail with guidance when it is missing. Platforms []string `json:"platforms"` } // CapabilitiesNetwork defines model for CapabilitiesNetwork. type CapabilitiesNetwork struct { // Gateway Guest-visible host gateway IP. Guests reach host services (including - // host ingress) through this address. - Gateway string `json:"gateway"` + // host ingress) through this address. Omitted when no default network + // has been resolved on this host yet. + Gateway *string `json:"gateway,omitempty"` // GuestToGuest Whether direct VM-to-VM traffic is permitted on the default network GuestToGuest bool `json:"guest_to_guest"` @@ -555,40 +576,21 @@ type CapabilitiesNetworkModel string // CapabilitiesRuntime defines model for CapabilitiesRuntime. type CapabilitiesRuntime struct { - // BalloonControl Runtime memory balloon control is supported by the effective default runtime - BalloonControl bool `json:"balloon_control"` - - // Default Effective default runtime used for launches that do not name one. - // Reported as a capability-bearing runtime identifier; clients should - // gate behavior on the boolean features below rather than this value. - Default string `json:"default"` - - // DiskIoLimit Disk I/O rate limiting is supported by the effective default runtime - DiskIoLimit bool `json:"disk_io_limit"` - - // DiskResize Live disk resize is supported by the effective default runtime - DiskResize bool `json:"disk_resize"` - - // GpuPassthrough GPU/PCI device passthrough is supported by the effective default runtime - GpuPassthrough bool `json:"gpu_passthrough"` - - // HotplugMemory Live memory hotplug resize is supported by the effective default runtime - HotplugMemory bool `json:"hotplug_memory"` - - // Pause Pause/resume is supported by the effective default runtime - Pause bool `json:"pause"` - - // Snapshot Snapshot/restore is supported by the effective default runtime on this host - Snapshot bool `json:"snapshot"` - - // Standby Standby (pause + memory snapshot) and restore are supported on this host - Standby bool `json:"standby"` - - // Supported Runtime identifiers that can run on this host platform - Supported []string `json:"supported"` - - // Vsock Vsock guest communication is supported by the effective default runtime - Vsock bool `json:"vsock"` + // Features Stable feature IDs supported by this runtime on this host: + // "snapshots" (snapshot/restore), "standby" (pause + memory + // snapshot, with later restore), "fork" (clone an instance by + // restoring a snapshot of it), "pause" (pause/resume), + // "hotplug-memory" (live memory resize), "balloon-control" (runtime + // balloon target changes), "vsock" (guest vsock communication), + // "gpu-passthrough" (GPU/PCI device passthrough), "disk-io-limit" + // (disk I/O rate limiting), "disk-resize" (live disk resize). + // Values are host-truthful: for example vz omits snapshots, standby, + // and fork on macOS 13, which lacks Virtualization.framework VM + // save/restore. + Features []string `json:"features"` + + // Name Runtime identifier + Name string `json:"name"` } // CapabilitiesServer defines model for CapabilitiesServer. @@ -18797,369 +18799,374 @@ func (sh *strictHandler) GetVolume(w http.ResponseWriter, r *http.Request, id st // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "H4sIAAAAAAAC/+y9+3IbOZI3+ioInt1oaYakqKttdXR8Ry253dpuWVrJ9ny7TR8KrAJJjKqAagBFie7w", - "v/MA84jzJCeQAOqKIkuyLta0YzemZVYVLolEIjOR+cs/OgGPE84IU7Kz/0dHBjMSY/jzQCkczD7wKI3J", - "Ofk9JVLpnxPBEyIUJfBSzFOmRglWM/2vkMhA0ERRzjr7nTOsZuh6RgRBc2gFyRlPoxCNCYLvSNjpdsgN", - "jpOIdPY7GzFTGyFWuNPtqEWif5JKUDbtfO52BMEhZ9HCdDPBaaQ6+xMcSdKtdHuim0ZYIv1JD77J2htz", - "HhHMOp+hxd9TKkjY2f+tOI2P2ct8/HcSKN35Qar4hcIsHC/OeESDRX2yv1KW3kBvCKeKx1jRAEnzDUrg", - "IzTGkoSIM4QDRecEUTbmKQvRu8MzFHDGSKAbk0PGx5KIOQnRRPAYqRlBMy4VvKMEDq6QwuOI9Ies062s", - "B2H6SbiaSn+bETUjwjNYKpFtBU24QGpGJaJMPw1Iv7hgSqSkTtluh4YRGSkaE56qOqF+5tco4mwK03Lt", - "ojiVCs3wnKBPRHD0e4ojOllQNm0m0phMuCDo50VCYsxQEuGASEQVokxxNxtDo5zHdmMfc9Ep44KMQiIV", - "ZVi3P0q4MDuiPPpT+ANHqPAuDA3eR2qGleNyxhW6IiQpTxRf46syGX/b2uq+GgwGH7sdqkhsthW+oXEa", - "d/b3dne3d7udmDLz781s9JQpMiVCD9/+goXAi8J0JE9FQEYBDcWymQQRJUyhw+Oj8ztOoLM56MP/bbzs", - "dDubr7b6m3sv4d+be53itGqEL4/88/Ktd6GwSmVdBpndNLKMMiowSX3Wb9N4TATiExSkQhCmogWCLUXC", - "FkxXmvbAtxQBZxM6TYXbgr4tVyLnDEuEmREavYq8yBtrte8CLcRCfs1GgsSYMk3j2iDO3SOkdyiym0gP", - "KeBMCR5FWigoReJESbeLulqMM4STJKIBiJ7SptqJB7LT7bA0ivTDygjz1SYRnVJ4oRVpqCwskvsWKY4I", - "U0RkO7wNaUpisanjnNze1cjlYqsuZzwKRylTNKr3+l7/bGla7IlKNCNRiPhk0kV0gjDSreifDY+Xyb41", - "2NrrDXZ6g713my/2B6/2B7v/2+l2JlzEWHX2OyFWpKdXuc3agNCWlAX+1WFVFon1gSRIYFYnO7BKCzgm", - "AY8J0k0vHfnOl4+88bg5zBdVv4jsi/nJ6lnqfuXIaMfdEZYqE0KwXlQtRtgzpnc0JlLhONFySI+hQMwm", - "KeQarK6Do/xSAm9+EYEZuVEjSyHvfHz8QW4SEugTkTtpkikYuj3L3pnIehQeFwRLPWCtJunD9LdOymSa", - "6KObhKMkwkq3q3UqYINRTKXUn2Y/hFQaOdLtOCYfMa5GImXMvMiIuubiqvimbWVEk063M8NyNJ8maae7", - "7NgqMzV0QSKcSGjPrrgYESG46BjVeDGacOEWSZ+5OQmXNFWjkMyOWA+FOt1OiQCZOHdzcePOVtU7OOgF", - "eEkYq8KYATCZ+sCLbdWHmw1tuWA3p4hRot0yI/uxLEuAkOIp41LRQLYS86A86OWNeegRnUdZc4iGhCk6", - "oURYvZogoc+HmCDXCNKNIMpQKiv7IFP9R2SubbXRfGekgqROlIphU1y8gm6Sn4iFUzlb/mynrGDS8ty9", - "htMcU9iTR2ROzdFS1t3s0oxCQedEeMR3pgAYUWjeQ2t6r2sRwjgj6yVKsTkNKW4jDkIY04h6uOfs8BiZ", - "x+j4CK3NyE25k60X45ed5iYZjj288HMaY9bTG0IPy7UP7xbb/nXHa6LwOE5HU8HTpN7y8enJyXsEDxED", - "DbfY4sstn6aaBHSEw1AQKf3zdw+LYxsMBoN9vLU/GPQHvlHOCQu5aCSpeewn6eYgJEuabEVS236NpG8/", - "HB8dH6BDLhIuwGZbuXGK5CnOq8g25VXx8f+PKY3COteP9c+kgU5ESCqVllc/mteQIMaUQ9czLgkKcDAj", - "aGyMFjDPob02LJ917E4v3wiOqEw4yHn04STX5sA0JDckSFWp3+9RSLUtHFilqjC5FiMKuF4jff76NAug", - "ALLvaFNbOdXpzkpAIAhe0Z1+o1Vn9f2fGvYaxbKpdfeKFvMxjSIqScBZKIt9UKb2dponU9jF5tisdfVa", - "/4xiIiWeErQGbikwN4yE19rWBNOIhOvtNOymyfydjwvnWmnPARv08DjY3Nr2CrQYT8kopFPrV6zyoP5d", - "68e6HYXgbf9EQMNoNw/oUpBJvb+f4DyBTgSZEEE0x39hd4ngc8KwNan+A/rt/D8bucN1w3pbN4CYZ/nr", - "n7ud31OSklHCJTUjrIkJ+0SzEZAawRf+McOjZWtd4CipsFi+P+CNe9iJubK5kjbW9aP1LTxd+ck7/U5V", - "oIM4yhScghRolNuvtablUVk4U/ZBxQXMpyiizJhBWn6btQBlb5GQHyI+Xe/cGx0y8tc3vx73HYSX+aGh", - "Nf2sm1kFEZ8WqTkjWKgxKRGz4Vy1DeWjayT/WWn7VA5QLMlouQQ5o4yREHzudmObN7Vu7bV9YBddUTWa", - "6xPYt+dgWL9QhewbjU1FPLia0IiMZljOrJMyDKlxuJ6VZuJRIUuXGRicBK5BUG3AqL74+WBrdw/ZDjw0", - "tN5f/UJ9JoWvdfNWvVBYjHEUeXmjmd1uf0bXOcTPAbnDt+nsyTjQMaaRdB27mtZ4T+XM/AWyW48Kzj4t", - "BjR7RfrvGuN2Ozc93WpvjgVQXTdfGNZ/u54Kv/2Yd1r49Szrv/DjuR1K4aef3KgKvx3mA3RUMWaS1wUO", - "k/ZrlZwy1aMMlkAr4NbzZI6O3M1qPRlIL2Om4cHJ/kWK1WFNpUJr5z8dbm9vv1pvr1xReTWS9BMZTcdL", - "9WY7aKso68+Q/kwflVM6xeOFIrKPDjFjXKExQcEMsykJEZ4o/ZUda8kA3x2suoRpVJKIaFKR1Kcd/Orl", - "zQ1Wr/botXz1KR6L6d+3vfef4F3U8qbZA4ftaupXkcDMs4h3O6hjfKMtf8MlvsvPE3NdhbKX7BrI6hD6", - "KDfoJ/RG01yhzRKlvcT1W3/ZFRbjrJcy+nsKq51EeFG3ARXBcQ84wkdeIzfMBpKrt4/U+8d842Z6janS", - "Oye7M7VT7iIehfrcmVAhVaf9bditFAMi7ktFKu0yv8ZU54gqARsP9WycjXslohMSLIKI1IW7E9+JSK27", - "NST6zIE/MwdiWzHuhlKSxO7Hs6yL0s9HeX+l31+bzj93O4c4wWMaUSeZy3J6QrBKBfFM/wKu9JF9AVjM", - "XMxKFGCGplgRNCYzPKdcIM766Eet2OjXsCAIR9d4IYcsEUQSpvbRMHNQy2Gnq/+pVSD7t1kk87eJzHAv", - "sakgUv9jyIYdbeub3yM+lcNOHx1y5rQY6FqQSUQCBScJmUyICWuwgQdD5tybmIVl36sen3W4mg4kw4mc", - "cWXHMeHiyg4iwakk5tcZV0mUTnsxibmwH2p9hXPWszcPdk6SB+77aZL2EiylmgmeTmfmBc3jPcp7EY2p", - "KvwkiGZ8+wO4eRwtBJdEKdwjcRrB6TDsmNCC9rtZU2DVtixyz8/6fWe1ytt8eWy++JxdR9zm47f2Ey0b", - "zPrd5utz+4mWXnBLcZuPL8wXNR+2+dlSMB9WPr2MSN18h/lEUI28dUVKBDNfzIpU6PDsPdKPqSKB7qJ0", - "umAR73n9tlw2NKd7xXBiyIVUJC41F1GW3qz0UXI9YRjxqskeZzxUnq7dqvndV921DKZTfmlC9IYnob28", - "NsoSQfYmB/GYKn3ok/psNhpJ5BqXq3qXaI3LDT3hdXPEmqgozLTa2kfHLIjSkEhkNikJh6zwJelP+8iO", - "JA73dtCcYnRutrVWUw6SJCLogkY04AzFODi9WK9ED/1WmUmhtVuGuZQ8zdn0u/X1WLWwb/MNXl5ZfWBc", - "Y4+u9kYvVG9OJURTAAXtu+j4rI/gMXi5gpl5qrefloRojQKBKZsOGTyxx4VeDBCuZlGs17waebU56G8O", - "Bv1Bf9PHA1Pd7Ujx0TT1WvPuOi+kQh84H056ivc+nCAl8GRCA0QlSoiIqVImwk6fSJaYKJcS9eu7mIck", - "aiCS+9BdzEV9feAIGk7JsAMX1gii/pD5DV1TNdOj0ON6d3A2ZPYI+R4NOwwr881skWh6Si56ieBzGpIQ", - "vT14h9ZKHGcVHtOwFnRe10q3I9MxI6pp/OYpRHb5lwICtFZJGUOibsZRtcVaxaPn+TFS9eLA4T2yh7cn", - "WMkqEObER/Z9F2agyZndb6Lxwq+GoPy4qC9/FiNZ83o1NQM+H1DyI5yyYEZszFzIIV5Oq5ha/vWH7JzY", - "kWHNKYEjyKI3JlgTOmsxtw6/z3Q+E4I3ZFW9zxjtZgJOWZRoTCJ+jQS2N97YGl1zHKWkug+DiKdhL2fD", - "Rkub8hHoR94Loit0vHGqeyQIXoJwlS9fDt2vUcJ8EbZza8ybN+6hv2mSjgrKoWcfnb3fKNwFF969h96t", - "Pjsy3N0wYcv69t37mzro1b6Y7VSSDUFkGt9HL06v99g79onuS3Fxy84yv4JVCT0922gTn6EFgR5rQAH0", - "V0dhN9R1MFfcqLRxlQ9rda/NASnntd1uJYfVX0qNo0IwUkH98OzdiT4QIYBV/+t3Eqe3UUW6xlaqj/aD", - "/hmBfEcBj+OU0cBeW34hU1ROFyeBu6VQlYxv8oV0LFvbN93aOeJmVd/gVdFWFjmrDrKLzKKpGA0Jbb40", - "ODg7NgcWDvKLg7UYK3t2EHSaEKbfCnmQxoRpvWrC3avlIIlBf6sp8KKhezNm6xnMupczLhSaUoUE0aog", - "Z12jvgw7vZAKtRh2kEwnE3oDR13KNA8Y5co4D7qICzTspOyK8Ws27JSH+WIPb03C8fZK3cINu1uioXcd", - "wNCwTpfGdJTlLuLDpf7g05gqpLg+4GFZjNnpOPoevMF5xD2OIiJ6muEjCoGpTiR0TTDalDBtHOpHE1Ar", - "nKrY4NHseXXqB3ea3srR2LCiJjCscUFXzGEacb35F8hNJI+p6qPjMu26CBv1jMoqlbNg4ELck7EYu2jY", - "SQLaGwwGgx7e6g0GvUGV26OdnonoTLBSROgB/n+/4d6ng97/DnqvPuZ/jvq9j3/9D68J3DIYy13b2Hmu", - "ub3URW6wxQit6kBXRW/d2XFcHH7z5gVjvnGlA0FgG+Bo5RjOUjk7LLzezOuHx/WgDkOpkOsjs0/5RkTH", - "AovFBptSdrMfYUWkKtNt+bvLXBqe2G8spqQQhYolsj6N3zbmWFDM1EfrrBgWHQzDznoXwamhVe0jGBHq", - "9Vw7Vny5wNKSDtFHb7lC2F64aNtyH16q9zsVOI6xQMZ2oCFsDiMHe5KGxpNLGTwz1oVEgqhUMLQzGLgn", - "o8KgWLRAXLqpDDvmlNHdIuOF4QKBO8UEgAckgeAyHuJKFH7R11LbaXqb/XUj+2vN/dn/+Nf1//Mf98rq", - "wGhLeNy4RG4pzyphjCCl1iJ+TUSAJUER0bOVXRTSKVWyC8sQws0/4ixafK91SC3qTGQOF4iw0FIa3iuz", - "c7zo4YT2rPfG3Ob8SthUzTr7e9te4q7ZP3of/+J+aiCsSCPf9cY5T5WxeSMii3mFbgyZxrpsRRx10wg8", - "zDFlx+azzbpi+2Ur7CaybKXN/UrjUuszZlQwQ5YNpJ5g+rnbCWKfEjEnQuitaOTa4ckRWovoFbECDSyJ", - "YToYbAfwAvxJ7C9aicMsNL+tlzUeGyLjUXZ+65BgxiHKJ4r47ayLilBvCoJZekHgo3Yu/eshMz9zqXox", - "ZnhKIJbTvojGgl8RcLxANgclEl2RhTFipiWnKGFzNMcmjrg/ZO8g6tbYQvaRhGwdsM+1nQS5pzMO95dG", - "LHbR9YxGRpEUBEe5tIwxZcZ/2pMBT7SOx+xrxod+Sdj8EsU4gW2uDVAII4uxIoLiiH7KPZwxCalxdBPY", - "GCjBes8HARchpPhxBH7cnArfSXRpon0uoflLyjRbX5qNWfEV/dE5ff/ux9P3b49Gp2ev3x4cj355/T/6", - "Z/NRZ/+3PzomVzyLK/iRYEEE+o8/YL6fTWwYhKx0DlI144J+MvHX9kIMrmNxQvs8IQzTfsC1yfuX4j8/", - "fv7ooplMYspcbwPPwD571UzrhPV4sVx8v0Q2ZtxlK2mSaRHlvD9lC7J4HWA0v1ttCWeAjhPZyrOW66Gb", - "g8HJjxv2inTX/WO9j47MroXhaxnEhVWP5UyzT5Z2DjdYUcSdJT9pyjB1XfkEPGFKLBJOfRGQFeGUv1qX", - "Ub1e/vQWomhjTNmG1MvQC25Hd+CbO8fhvWZzKjgD8xzUpXFEZHmvvD09ej16/fZDZ18fBGEa2DyDs9Pz", - "d539zvZgMOj4GFRz0AoZ+ObsvcljhG1DcKRmo2BGgpUXuz/Du4fwasHj6HewHmSkcT4xvQmc53FtVlZS", - "zJZHsK7DzvabHw1fbr4BlnTraRPGslZMw5Ucvzc/+hit4OaqK0zZM5MHwULHPHXIAvQ7iVP9zHhbile4", - "PYbBawWnNBYhGnMswu/hi15MA8Hnsfnyv1+fvP9OIjD6cWTvfYwGC98YlYwTCSandWWh3HUsuxUSgAMl", - "NrnXmCFCpzOF5lTv3F4cU+6+K+9u65Nw2zaPU7MXRu0chOY/boKdbmf+qZx51+aOAHZpK5NrhS2Fo4Qy", - "ssSY+kp05msuriKOQ3C23KfKXAjSqIAVmAfl5c5sPMfgNQyQMWbhNQ3VbBTya6aH7Dlo7BOUvZydNjd6", - "Jjj61z/++eEk9ytsvhkn9ujZ3Nr9wqOnctjopr0R2dlE0sQ/jfeJfxIfTv71j3+6mTztJIxmdict167/", - "a9NCFcbA3lyamM2G5NdMncmzuHjD3fiKTFffycXnRER4UThO7Jg6mwOQ6ZVRCWqC3Ox3BU/s8sNFt+a0", - "njdVp9bWwH98fHPBfOUuGEFA9I6SDFFp2QY5N2/n1rGH6Tw8B1GS7oavBadljLa5dWL/3KqznJ/j5BVN", - "TEjECE+zDKFlIEwXVzSxtid8YbZpZCIMUZiCtTrmXPWHzIAs6L1prtxuSAC8IBVW6ODsWKJrGkWgfsCh", - "UVeEtCVbQOeA16XS/ytS1kXjVGnzlCuCrKPAZnvqscDLY4JShl1Kd8VYtBP03IlqslwRwUg0MsagbEkZ", - "8xGyHzUSB6Y6wdKCwgiVJmV6Hf1ycoHWjhYMxzRAv5hWT3iYRgRdGFVtvUy9LoTSzrXNzKaQ20Jtv3yC", - "eKp6fNJTghA3xBgay+4MbL7x/M3Ze5uxLtchIkQTlrDQRpA4jSILIWHfaYlMwnKzxf4rRG9CJXD3py03", - "l7uMz3dXe+9ZtzMPkrS8pFvdRtglreKmONJnacl+8uao2+Bkz/W0eVA44Oy5lsNUqXIeb1sXo2kZMORW", - "Ru5ZT6HRhFt7Cgu+q5rP0DlW/mg32BXtHzM3kKWe0ty38gV9XZhGarG75ueum9kdqHSc0aTiX70f8hzI", - "gi+qFdybOdnN8SfR2iVOaN/ycT/g8WUXXf6l9IPe+84g1vrjNTLUAHnC9E/F9mUt6nSFf+zOkadY3n09", - "DmQjWAeabyIlMJMmYniGE9JHP4MQR4rEWjWphYsxfv094kZpdZ8OmR6aRGtaVlpyZF5SSaeMsuk6hM+M", - "CcJhaFypkxQSKFxcg/ShJDp3ZU0zNKOzWAeASWhiXgm6dC7Ny7JSVHd41h0Z1gNas2ANScByBdtebcSp", - "0t3rCWeKJk+VwT6xUy/j0lTcqqsCLuxYskywO6z/RSYuqtCTc48JC/GQk9wPXnDIN/m9raLi98lfkYWJ", - "mLL+d1zzwBdd734HuSCSR3MXuVl03o8BXJMbxSn32xsPvHW66+1fDar0uaNXLYWmV2vyl01Bb0ZBz002", - "5xhr3TlYMyeF9ORMf+CFkgSID6blPgJ17LJrbGEClgdimlkiF3xdbR4CwiEf4NL+0retXepNrnWUe4Eq", - "BeQ/UNqLS4sKK+vUPmhGT42boKVuWTe4IiSRqyel1Wt7U+O5ThLkWmRxrw4Xq6V6RtiEi4DE1kj4MsfA", - "60JjSwJt2jZRD4U29C2M2SEiYhu/BGtv1sNGCBaAEqt4wWHFajMJ5+UuL3EUXaI1+9I6EuTvACZn14px", - "ljP7u8MzxwJZGM+Hk67mSC0FLmdKJSP9P3Kkd/FltTH7rdvhOZbrywHYVzs72+WgfDPgSrNl/6k3ib55", - "ac5SOXuguJgGL60DzTWeWncCJamcoTWFp3rrWLQEfU6sf6kHlzJJglSQ1Xv9ANQjm7EPY4owZb2f3707", - "Q4JMqW6ziHdQRGIDt04DtIs1bPMwIDddxctuvK3tnd29Fy9f9cMr0SeB6KeyR7BUvc0+jvEnzvC11GrX", - "RrzASbI/31wp580SZONrFvnOCmtmBR5rkewCPdtYdIf5J/kN0hVlYdsGftHvrg4mdAbnQ/vhE0F6aTIV", - "GMDC7tMLf+d4EaBm86quAFBfHiiaSsXjInLg2vL40DKx5jzqhVjh24SHmuHWI0HjhWnKmORe91dT2O1F", - "Nda2lO7vhVH+0uAdNxbfsjRBERpHAglHinsSEZ3ucHwEyKX23VaYa4CcqPhoPqF8OVClDessAR8brcS6", - "N3QTvSSg1qsEqm4wM7BUhghgO3w4KcYs9IesB1rYPjrKOsiazZo0aQ84NNe+a1wUBmEgKdF4sY4w+nDS", - "R++y0X4nkb1FtdiMMyzRmBCGUrhhAqWoZ1Sy4gBSCbqTqn5uXWgGxnEdQjO4fdbPwN7BWZdB10ME8JhW", - "5mMyVGGh7FmiD7zcGdrKebkM1OTcnEfLgE1KQLa7vcFmb3P33eZgf6D//3/bI5/cP1Klr62DsmyxMdVF", - "6XP4/vhoy/rM1++MZXLvWJZ+UXaUB4OjtVSSQsYlF94Q8EKkdUOI950jtx8oEDtH7Vr2rqHEO/3mQ4B0", - "+mBELM7X7WE0qwJzJVZbYXJ1h84iAfdDvksKirwN2g+oN7X2iMqrHwXBVwA+XD+3IaN+ZM4zfxwXZIqO", - "F4jcWCeX4FxNJMqwCnLn9+bOi52X23s7LwcDDwxkneF5QEeQltFqAKeHxyjCiwwRaQ3utUM0jvi4zOi7", - "23svXwxebW61HYe5yW1Hh8z+dl+hNUuRv7oCMe5JaVBbWy/2tre3B3t7WzutRmWvDVoNyl0xlFSSF9sv", - "djZfbu20ooLPrnvtYDmrCrwPI/rAlFXQ/+rJhAR0QgMEcDZIfwCp4TPKSHZpWd6TYxw6JG3/2aEwjeTS", - "cC/TmX3T+FvjNFI0iYh5BgvS6koCZn4ELXlLkzCWIZffriULZroyEMjNJXsF1JmQjNPp1CIFZaQ7MdDs", - "BeWJkijcR5nJtlTOwWrmA/vYxAd2Di254VdtOvUiMidRkQnM0WWgCARBGZ+YRSvNysUAUJakXpZoJOVP", - "qTAoC9AowmOeWnAfCzWfd2KQORjcvqYsbOfu+ImLq5Vx8/okzhD1WzoMJtZjB6c4zlDsLK5hQenLboXN", - "3bl9LtG5+cI4CvOfk7RcTqgLPVmHInNZwmHmN7bNtNUu/XoL+MxdGInpL5edjxTi1puYqKD7tbDFlEAl", - "CbVSY9GcYuJ0LuD11mk4+sOVjpQWdGfk+jGIDolkPc22Pclw8jAUXxZJm/ka8pfgFBY0JH0EuwuC3xyo", - "cGWnXSieJCTM/D/9IXMJ9hmml7lI0x8aOqgZoQJxQae03HHZz/qIsay34UzHXHfmzuKHTbFpMrsG9MkA", - "g1M5MaGhBj69CJNn16TTdVgHnW7HCqYyac6zsiU1iuQR5z5MjNtGpCaCT6iv7hNEyNin1lBzsZq/7gwu", - "epv/baLJNfuBxkaZiaqJeVipkGHfb3cQvTl7f9Y0pqxmBSqOrjanLA5qWZGxDIQvh3oYEwuaFbrdoM+Z", - "rJNcFX/lU20nAsdknE4mRIxij6/tJ/0cmReyJPeTH8vqrVaj2xrRZ6XFASt6ggPKpuutqe/xz1Wm0S1Q", - "86N/uc5tDYQm+Ei9VFmdBIMc2Udvsyoh6M3Ze4ny2DWP466KjNaQNnQ2W0ga4Mi0aCC+KStBw2jmbK0w", - "n+UfWs+kR232F5dxGwGtzadJCtvw4rx3fPphIw7JvFsaE8SbzXhE9LjXC9Ji7nLX8xynkpCYNzk+DGPI", - "thuoQKtsB7cmUmG/eqijuMLRSEbcF8LzTj9E8BCtffjJJJ/qEXRRUlpK/XuBCiX+3vPuGIDjbej2Ajqs", - "elBLG9xrSlaRtzrl6ZU69W0Vk/FTV3nqlZ34VXmh+dXqakKmkeZ+D11SUsXHbfVKZHKXEOQuuagEZD41", - "TmzrKZEkwQIrEi2MopEdfWV0WFK/YCY3JLhFVtRr/fpngwieCjJSM0HkjEflsITtbr2qnITQ2DmxhTTM", - "nAp+eMVRjMUVHIxOr0YpMxQoR1Zvr0ITmSmV3GJSP797d2aMbUXEHEfV2HxZu3A/IhFeoDFR14QwNxXA", - "LXvDs4ol1RRB2VDMQKhRQgTlZRp2tj39XphwXTQVOCDIfOVqUtolMfi3bUlpe/EA5QUBkbJhfTeXra/9", - "dJJG7dbYN6zNlSVcg9ss8LvDMwfKnxU9dGTeqlP5jIie2XKu+uHypd2Sy8tDuK6YAfqsKQxjAvUibEJE", - "MV/RhRtBPQz9eSlHsCAcZDGrzfYDu8CQqmv2+cdWyl51u/vu1WPMQl/xSBMHbxKxp4DOBEHHIoWrJBqa", - "WCSTG27U8mI4vyA4pIxIWcliDVIRdbqd3sTOan9jI+IBjmZcqv2d7c2XG8ujOpeG89ropVFIl5l7LsbJ", - "RMG4ZE9TxBEmXWaJDZwkLRxiho4rzgcQT/XwQSgYqc+2gobn4skHg1pC7g0OlCtxAx6y0g0oLm7bRLNk", - "aT7QYFZkeffVq+L+HHivpPMa5479N2q8D2EqEGKoecR4Hyp0NEz+yatRceGrY8KFsolpY+JiELPz0EX4", - "OdDV0l3Z4GVxlq1KSYOwqWxzu+88UzVvl1NvSYHcdv/aBiBaqqxzuC29vBSbpssKntISscZSywnKE8Ju", - "Rc/dne2t29Gz7USOXZRWRS75AEAOT46MThRwpjBlRKCYKGzr5heEDLiWtJQBoHxMYshemXy/XLQ0hDMU", - "ET3uWuXjvi7DGwr+nJuo3hDFmNGJFsj2zWLPcoa3dvf2TVmykEx2dvf6/f5tcQ5e58AGrZZiw8TMFSAP", - "+nL2ZevwAHAGbebyR+fs4N3PWpClUphDa0OOKdsv/Dv7Z/4A/jD/HFPmhUFoVcmOTmoV7MrhYdrgN7/v", - "Fwq3O72nVbVjv28YgoUBe8ULKlYKmnw49LCMx2kZlv0WKaxLUjq1unLKokUjbe5clC4v16oKxeiKyV8t", - "CtPRT8uvu527C96xfUIB9rxmX/2i+05VF+XSwlS1uiUJYVkpqigyfwWczfV29dWlKh2R7lmLsiZwjNj6", - "JVmXxR+z3gs/HhYHUvjdlUUp/GQLVH28ZYTKUoX0b3U9dLUUcurois3s9z1mp0LbYoAWdcwbmv3EZ+Fd", - "orjKvZ9O/+v3/yvPXvx98/dfP3z4n/mb/zp6S//nQ3R2+kVQHMsB554UNe7egOJMgdsiWlxbVjrBKvD4", - "6LT510Bh+8R4HFQwg/JlaEz2h6yHfqWKCBzto2Gnkos47KA1ApYSfKXVXSg+YtLA1vXHZ+aCUX/8h1OD", - "P1fbCG3utLALkkFiyHQc8hhTtj5kQ2bbQm4iEuwC/VeIApwYsHrKkLZ/F2gscJDnTOedd9EfOEk+rw+Z", - "LTlssJMTLFQGgOp6AKawozJRqfZ1kqEjmEuXIctO6wxxzVz79TPlH6I/qqldfqIst9+s5fRy4MOmg/QS", - "vZARlKtD2QUalcDoWd4LejlYr9tzK2yMjIeWsJ+9Fzf5hwepz13clLN4REIagFxxaXszm9iZZUwaTrNO", - "vETwmwWszbnJJQsRTtVMyyIT8dRFAedXlHRhSbtoInAMgRjwZdfWjkp640VvxpMMCwALE3yCzQV12cj+", - "vz070d4HIujE9uSFzdYs4lE6YcvYmZkMwMy7UJvYO71wkjCt+syJfd3UI4GChKGNrFapYDlk+sHZsQGd", - "yLiP+HTyakUINAN4NaWb8SGkdQZZBgseByGZVP9djjDY2t0DC9b9e3urdQKpId0yLksjj00bO9HXQmIb", - "MQkDMOrByDnCV8QE6RPQViYCP4Xi8N8L5BrKd1wmSMztlElpkybWQUWykMy27s06stvAIluMsN1Nbc6j", - "2i4sJTG1aMEEC8BnUQtMjdcmefLdrxdIERG7dPa1QK8O7BJioqKplKmWnxSjg8OT1+v91TD+ZlmXJjmV", - "B+2BPrDBA00xEbmPBseki46PIHnVHiu5LQbZBj9xgSJzKuaH0T56b6HdS00hE+xsGCNa5BEIRm0ZdtZd", - "i0n1eNtH55kJiLOhZGmIOW+5JvPDBJq18WgmFaLWeiVrFe7HrPlnz2NIfMAqk8VaTWw+v9r7HB1Ckj6o", - "Kh6yWx9IxaCQRv9XYe3vG0T3/nX07dvp6PYOeJTMsK/iitZZ8ltNeKm27sVr5LK4F80hPKV+R5J695aJ", - "h5yVjiFF9DlX+rxfJcdeb7Db29x8t7lze/fdbfFPy4hNBTS3DAK1PXbpQ2CA1l1ZN1SNGmO9kX5sI7ud", - "X+TDCZphyb5T8LDiHdncftHGKQG9to2SLsZH84kZUialHPxTFt1rgLCuaBQZBUbSKcMReoXWLo7f/HL8", - "66/rqIdOT0+qS7HsC28VuNVQqE5UvDl7DzfjWI6aiwy53Dqc56eSGyqVrKNjtYrX/RLoVfNpu5K+bpKm", - "jbyy73L81p9LAKNeuLP1BwBedcHWNXI+ASbpUya1fX14qEsRTL8UhtQa4g+EQtoo830InpUM/t0m8X93", - "PNEHGU4JAMUntooqgMs4vjOAZ7dDPdmWB1JLZhKi47O88E3urXfNV+b0aqu/ufcSikhuDtpVkA+W9H1y", - "cNi+88GW0Tj28Xg/CPfBor3rpY5lbKOjmxraUPnZ1K/uGCuwYP4Vtm1WtqxFAEodJ/VusKhVPacB+BT0", - "PRfZI0fx0tILLdLpqiBfpgT2nKCYRhGVJOAslGUlcoYlkolBtDQlCDIVd8hggF2UVZNFpkh4EIg0981Z", - "9dMqxGli+X7IBJEJZ1pJ1sYO+oUsJIop3BJm3UNsoERZ1kY4ZGvCZfhkqTxQTy7UP0C8fNfFZeuhUQWl", - "FvQHQyZnqdJCbB0qoMs0JsIVFB5TuFJZRzI1Nh+MF6ix0AJT0pCIIdOveTAz/8j09/29wWAwKJQt3Nb/", - "Hvi46UFvB/sWE9bkqAI6HbPosAB7J1KGUhYSkZVOziqi97/kZvELwWDd5+3UK/t5rlf5N+Yq7Nh2oLBf", - "isQJQ20wYCF88g7W626jubZSkrbKkXH6q82OsV+NbnPFT1DA0yjUJtFYn3bGY0VC66eTRBnpbN6lEr03", - "pQjLU7exuYqj31MiFujDyUkpLkCQiZYB7SYOUqJhHXhyq2XYWuFEWDmaO+KzPgYma1VTKWiI947AWryC", - "c0m7hkNbXMXlhqQ3s4IyszSaT5bMqeLeDsl8lKY+Q0Q/clAd798fH5WYA+O9zZeDl696L8ebe72dcLDZ", - "w5vbe72tXTyYbAcvtje3tpdkYbTIrLp7spTXGvUE0WYh0yMXuu2LqG0KnK+c+zYY+JqykF+XjhZvdGax", - "dxv5uar7elx36yF4s0EiLJVxDTRIiRM4QEmg2zbR0DZ7z7kgG7xse+8Gmyu8bCvlBQyuQf6+Eykzd3om", - "3zzzX8eFARcXqzzOu4lTGJDLulhFrWLn7Yk22N99tb/7pURzmQOrxlhlp0dc3KZwJIfHW0lNcOlxBR+N", - "w6/sWH3DuJRtJkOn28mSLeBvOGgrgbzZ41YZRE0btusXI8vkd0Mi7XHJFoAwCYPHFu5rLSDLwRynCmXp", - "2lq9OIx4GqKCn8vAU8El0HHBLtDNwJ2MdX8ZuEmTCaDtB8A3BnR9yrQghssv3YjNut1Hb+BdeIRjYzLZ", - "QZgaD8V7HxwuTLCG3l+ua2PALB/yhbVd4BttyCD9L5i2JoN1hy5vwmg+++gth28yS4rxql/VvA4mTP31", - "qg92zabDOhQF6Myqcfvop0x1y5Q/q+ytSWL/HFmBlYOXrJdSyO2KdzS35CtXSIfumoL3kCttCQVp0/UE", - "6vc519f2X5EVfVFMBEewl/ME1VTRyEI2w0yoVDSQNnNBL26TfmHLz5DQVj9vCog0WY/WeMk+curLhxO0", - "Bqh8f0XWaaz/tZ4FT5bOuq1XO6/2Xmy92muFvZMPcLXaeQg5ufXBrdRBgyQdWb9D09QPz94bv0JgLPYs", - "8OLDSRHqIBFcix49c9dgsfNX/VdFyKGQp+OocKtl8ckMwiksmBdVK5NFDUF4v9NoTicT9vun4Grr74LG", - "mzd7cmu82QClajryu7SOizfbNf8vGfdMKRk/KgwwlJCNwEnnRMIM0AVRCPinh3AApkOWSmtZzsErWYp7", - "GWtne3v75YvdrVZ8ZUdX2DgjcHB5DmU7gsIWgzfR2vnFBdooMJxp0+ELANo1s2alf58hW051UFZI+5uD", - "bR+XNBzcOdfYtudxI8k/WNPMTsoSHTKCM7Ottsu91N7eHrzY2X25224bW9frSNwslzAuX8aQx4KyF1d+", - "DbTJdwdnCLJRJzgo+01ceNKtRqVuNSooKGCAwG8xsJcv9nZ3trc22yGA+SIeLLZdacOWZZdn03mYwrMa", - "HlLURW+36bTwqVOGwc5JEGEaHwQuvr9y+hjA75Ewr+WL0OZgsN7+2sHV4ttWjqPMHWSyQ4xqwAVKWVZm", - "or/6uvNebi2bpbY5HlZLdV9OCNPUs1A1pszUHUiZCDKnPJX30BBXJmFzEnEubvVtk8FyTmQaKXO1SCX6", - "cPIdyBTNa0gqkpRtKMuNSwB97ji5W+3nEov4mbyJWK1Wo83SL5twt2HXdpehOZSkQSOMVqglV8pWRx4e", - "4ihIobAKztZTzwoQcCAfPUmihQksjyLOGQpmmMEFhLBlnNgUYTTjUdj3hmHqJ6OJN2KBX6OIGwDgK0IS", - "W3PEDEJ/plUYOidorZAPjwwrVWpD7sZGyNiqEmVu3I39Re6w9GVKZXnYmp5Y8QI2rfmk5HKM+FSCUagg", - "ZL5fhURPsDCR8JiZGjrz2NiSnmhfzxArwtx3opqTlE+sgWtVDshyNpTEgeBSIhLRKdRr+XBSSZ5dknCV", - "pdCujiYsD7YF65rLQs9RBkecbF1qy3c+epJJvuSEBB6GhLUlcXrOORljlkIVkgIjk5uECsMe7WLxZlyq", - "UQaKdMvBSjWC0gKpIDlyWpbynfmD3Dvec9GJtruQywa93unrGlf5m2oaYLNM9VLUT61uxoM+Nq7DQi1F", - "osqhrao4RrcBLsux6KmEVmkBMwutQcJFQSwV8NTX2wSg+E1W3U/NWrUVFH/dGVy0xRRbDiF2htXsmE24", - "B3jiFreSzhNtQyUTImIKxTVQSBglobMls+tJ6+qCbOVIEhSmxFLO6KcCW4Jjs70BPII5Hxll04qsr3bY", - "xj1sxrC88gD0a19sE0ok/dmc70QKtDKxgBLhPK+zVYAllSP/dVa9YUGmaYQFquLmLRmyXMQRZVdtWpeL", - "eMwjGiD9QfXOecKjiF+P9CP5A8xlvdXs9AejpjI1F2ZwNinNLEil33wKP+hZrldSYsETs2G+3wC4kjaR", - "Wd4w5Z9oRCy03HtGbwqMXobm3tkaNKVqNzRaStKuwxLeVnJblvXu+FR6EtuWajmuwA4JLV66UXuSVJpa", - "Hy1OJQfI6a4A73ajU85a+DJcjEMjryuoGGhMIOnETa0uNVqIxTZT8ZYZSOUM/Z2Pyw7RtiG1nuJVayzH", - "ZxBk4g0uhxVd6pA2b9RoUljd2wAggFjVE4WPbokrsKrMVx671CRPzmsVr2bEkoy6OZrqVy2qObjYiix3", - "3vbaPom+WpvMEwsMEClSLaDKJ5RWWRQK7kk05kIAGrHWcDhzswHMD63zaFo70CX0bkYWSJAYUzZklGVO", - "UkDyIoiRORGFFE0utJE1JWEf/a1g4gF+c5yohQUGB+f5dxLxa5aNcciKg9SNp1K3c8CMZ1GkUFA9f0k3", - "C1afZhRImYVLMCWow4SdCCJnxbn76idqHe+ai7CxMM0CuVeg3gncsSLFrwgrirKsGa9paBoama/qEXKm", - "+Ck8tfYnKtUjRdV6o8v7SyURfiUxm1L2SqvQlcJWKRgnBoEE4Digzpz9y4j4DIKjBeBG3vx/uybzn86y", - "xsu/VV4rgGo4jNsD47b1umADkyJTCfYp36StDFWBHKxlUCr1uwS05sKTXbGOsiZQKJrR6pxslwZWDcR3", - "o9mQJCj3vvNy98Vey6olX3RZZ6Cj7vtqbh4vuZJrWKmTNvc+L3dfvnq1vbP7autWNywuZ6NhfZryNorr", - "g9bIjdKbNfrXP/754aRy67ML8c2DWw3KZG34h9SQuVEe0IeTf/3jn25Udx6QT9DU4akb7u0bo3Si4kq6", - "QIHyFV67S7Il9v1ByUmAMzGD1shkQsANOjJ06+WDqWBQtNOCcYIDqhYeQY6vTSR59koFZrnNdVB5sD6V", - "17RtITm15JLpOM94XHOdo7+Yu+EKL7xsXfxIpuOme+jTaq/mFjq/tSjGOLQIMcjL9Ncd3Nl8rrEsBUvr", - "vwPIaXDZW/VMFvPGcsjXapoBRLHYGl+FUEAfVHhFn7QfFZe/spyFe8uSW6dK8Y9L9mHzFryV19dzInuc", - "vsHqtNWKfLAH4N2+Go2LZcmW1n0r1TDLT93b99siM7cO0p+dYLfvr5CMeJsPq4C0wI92DJbkedvdEks0", - "cFMhz8TjQOMR6WWBejYJBcnU3AjqPW8xzj3ZkcEVn0zKQKu7zcDcgDkDiVSuF6yUtky6iNw4n0UV1dkA", - "zAw7u3LY0SbAsLMZDzuVaytvamKMb0a2gzKwyGAZUnZWkLM6SOlmMI54cGUqbkEh5z4aoJhgJlHKYPNX", - "btU2B8tvh7qdpLA2GS41MSFONbEFYxqTGZ5TKIdg71SmpUBMckOVhIBRaGcfhdxADZXKjdoZ6tdM4uB+", - "Pmk4dDBb2IZ1g/o9zlxEa/4uOPgmUOSUfSKCd22mvJbYp6cnXRPAAKGHZmCl+EY3UTMCLSCzLirY/vnv", - "/vjhcURGMO4qVnxcp2Mx3xtuVgWRREkLHp2zQ4UJUMBTpqog8nE7Q66cslU/klIGwX42/ANAwWzvtlZ9", - "SALYkbK+F8uMfgfmruQNWEr7Ege2fSwMmwLukvx3xef2Qrg6AONsKFQKNu0U47rNLeFIKm5LS2W7ekRu", - "AkLCKtqk/5W2sfL2S2+s/K/YAtRkRXzt2xDvXJ9d/+GSp2CsTdQuxvQzznoAjeGW1MJYGFw6C5RSZrQS", - "7nUBLmLkw/b0vdAmm5ncLKf1W3KjAJw7TCODuOZnXSuq7GG0iuJ3zhps2tBcrK55/wAlzEy8+Z2KmNlQ", - "9aeoY2bfepDaZbXVuSDKvXth2ai5gn2pykjpSssF/LtXyjE2hpW6yB7waDNer7DgzszvFbGIsC3zHxmO", - "ySgRZEJvljCPecEYxmXIkHwjZRkMBtxyLcY3aOcFCmZYyMrYGZ3OVLQoB+DseIB8vqjAnyCKMOcobLPy", - "+Wq6D+vRbnY5i637lOOLAuxOrZ6GVUlHy0CbD/PbNuudT/ACvDiNl4QvtncGg+2twZ1Qm92wbkGuw/wT", - "Ww6v3E5TSl3hO3vRX4pSLbaQJTDXa6xeCwp50BmZpBIEx/uQeJPggKCITAChLattvfpmsdr18sFbhcqC", - "vWT87xbKrpu7gy/Xa8m6soDXbhodd7lYxvcpPl9xIdogZoIanpsn5267N9h7t7m9v7u3v7n5EEjLGZGa", - "sj1efNq8fhFt4clO9HLx4vfN2YvpVrzttcOuqClL04ZXf9HvNkbZ5IdkGSeoJNLQmp1DQkS1eG616LQk", - "EWWkJ7MMqdVpiktkgbl/X7n/b+fnNzNYqjtclCdZVCGwyolT4qynwbayk1l6d1GdzfHR8lncKQOpOhA/", - "v1WHAuzVbjBQHmGz84WoBylreQy9L7zY+iBamhW36ijy3bDDTveucgPFfexdkpOlDbfsAK8fch7f6ZQL", - "qmbx8tMiey3DsIa46U9ShWUspT46njKonF38OQuTKxpR+uNOtxN92invGft7e1QtC3+bMaBd6qJW0CKM", - "DAqzL6cCvJIbHsJEsmtbXY/5h83e5iuIQ4g+7fww6L0qRxx0DbWK5Nt0b5d+HbShYbH+nKtbtPnqVhHX", - "jp7LOOgX6quelp/LFhjX8nhep9gdHS7htrTA+ePaGldQchoV0C/V9OzhNioqTSGJ8MIHjF5w1MqK9Vhk", - "MjQmU8pkG7/t9iBz3O7Gw04fHVh0arBl86r0peahHnmBT2gck5BqHdOY/s0ZDFstfXFVW+J2hTHcVx5t", - "re9X116thkhYlXC16pjsf0E+7hdZv+0s3mXoHeBXcyYq4HPBi11EJwizSnVMyuY4oqFNpIfESIhX23cg", - "aDnLWhkgcz3Q+Um6aMoVylPoW/rbUtbsF8zGT27A37oEM8MwxNa9AKJk4Fx0mfg6PkKJ4GEa5PmjEQw6", - "R/wQaQX+bImSvzok9yH9G5CYPeECrfZvNDk02vknm9a74pvUDNu81JuD1Uv9IE6RbidNwtUyzLzUToLd", - "CjZ8RQqix0VTJntFEyxM5mMLiX5epGDd5jW+5ECrRGniLlg0T9U5yXPdAlcMvrjeIxIRfUzVG0E8CvMs", - "CSpzKbpapG7uvZw1XXHCjVR9IL8QkmhbBfCPoL8Ys4V3YK7mZXaWrA1cPVtpLrx6plaOpVZ5cC9WamKN", - "S1V04Tbh9xspX/F5G7yUq4L7O8PPLqpmdQQUJ/BLStp5M/68/dKFvTX6jx/CLfeUStqpvXqoQKI6wN4M", - "edz1n8cCa7WuzLw7vut5H1u8s5ZxE8xrNQu06HU+6P2v8TKjUX9/44e//r+9j3/xepsrdrMkoheSCQQa", - "XZFFz1S+0TZ6vwxyCrD7WpmeWlYhOAYfEgCI281YHO/uIBMai7c4rk0BIrQKZWs2V07or//RHN9UION7", - "kJMrWfaLq1I8RPVOxd1xtBYTMXWx5C6RbL0/ZJCbdkUWEhWKYVmVxjHqdzL7pBCBji6NGtgnbH6JxhSq", - "C8oh01YtDgKSaGvC1lehpkQ2B+kjCI6K7diiXC7x215ImngCgj6c1BByT9+/+/H0/duj0enZ67cHx6Nf", - "Xv8PhHhc90wPYU/z3s7uni2MXaTkpmeJvwDD/4tAan3sZqAuPfwFSZZQbdyjMFMJEAku3KDwMlqDZAVX", - "adPlaq7fDnrzIGvQG+x2zwVVBq/uo+jh+6VVDuc86mmNugH43uvANLTwBmtDUyYIvtPk556OPWqj9SZO", - "6RR7XNs+F/a9FCd0A1qZpFNb/8YqTf7Q+aNqVQAjDQypKij2FbtUql5zZH2sFalRXoG9HK+RMpsuSQvh", - "XOXcyJipDVtE1AfREHLApl6WIJvvMgfI14OPVud9LlXlCzMrjKR5bU6cxlrRqZcQ6EyT5npGBCksBHyQ", - "o63fkmQ2FaQF8IcpwpYQkYdJujwSrQjBDadEa5mzwZEgS3Cte2CXo+mf4JusB/DeY1m78oJ55OVuNt/8", - "CMjn567EH524JmAYFXvCj/Nd5qJlNHFcVV+MIlfV523e9248K6uWSL+mvVVhzryPEmv6+PFvmKqfuAAL", - "pBlm48HhwsG6CYkA2LEqGHgrJG0ak3DEU7V8/9sK4hZjIysDmZcRddYWBiYOShmkTbLAAUHkY6hTWpOD", - "BKmgaqHNc6sMjyHzztXuBEJCR/Bz3jHUS/z8GfyUE0+OwhvCiKABVKPU+zHGDJR09OGkUJTM1KerQYSC", - "enl6eGwtXIcyCxYLVcB6LtTv4Oy40+3MiTBWXmfQ3+4PYDMnhOGEdvY72/3N/qADivwMprgBFcRtyq5N", - "cc1spePQakI/upf0lwLHRMEXv3mSzyHUzb4OWi+eFuyWBFNhDZckgqR4wzBUfw3o8e5A3TencteQvbWb", - "DjJbIeGCJKd2cT+CUgl7B6a5NRhYLG1lj19IFzEx6ht/twGLeb+ttDpLIg+Yes2ycLplRvrP3c7OYPNW", - "Y1o2FNi7vo7fM2zzRgkYhLu3JMSdOj1mJhPM5vXaCJzijgNGKu613z7qNZNpHGOxcAQrUivhskkxJhJh", - "966B+FUSBVpUQMmXPjplxJbNxwphEywrUgalZN2HmkPLu8C07RY5w8X5kYeLeyNhqQ9nFn8uizO9XT7X", - "+Pn+eCdj4/pC2kcO1Nlw7SMw0I84q4P8ZDtlZ/Dq4Ts95GwS0UChXsbANgSWSogyiQCi2sHdcIF+T7nC", - "KIsgf0Zb2uqs44zduvlRtPEHDT+b7R0Rn+f1jIgYMxOPb95Zselr29l4wfPtvPRUc4wP1STgpHK4L+ag", - "AkWuvEWLx1ZVGawfRzuepH/bp5le+ISMv/MIO9xONiuF+ZRbDuorolSS57Sd7K3OOFdCvLrcG6K+Fp4f", - "POaRZXHr/4S76Lkw8BuSaXj5atUOhY1EpMwYwF4N8DzPkbPffVdW/t7lTwqBGeBK101DBQVlrvJwuOgj", - "R1Nj9KsFoPoIAvMM68fKmR7e17LDth5jh8GMs8uJb8fUt2Nq2S433OKmABuzsMtb+CBu5YH48/kfbu19", - "+OZ7aO97aOV5YOTaehf+zsd9ZIMgoQ69nPE0CtGYIAOx48IdFBb96SeERTCjcwI4alAXLI0UTbCAYIYY", - "hVhhc23b6JhY6pbImtvQzfVc6FtO4Cp0giQjgH4bNUEe5kFvlDESIv2JRYvLEexq1aHN3vc62LMG86MR", - "Xc+4JBmEHFOF0xwyaqWxjqHZ/pC9s9iimoAQv+tkjSQRIKQu8f9whvCQ2Q++dyLExR5JHOeSCwuAqaMG", - "DNEsSz2bSo90JAPug3d5RxhmqicTEtAJDey0rsjChhB6G2xV6kcP2I3zw0mWI4C21v0QYYAI6MeDPcqe", - "IctJ5fsbBnG3QZSG+SWXQ63BYoyjyFsLYhrxMY5Ghj5XxHMn+AbesEQpVpF3t0mMh8RUBE8WasaZ+Tsd", - "p0yl5u+x4NeSiGFnvT9kEPtvaU3Cbq4gomuoHRYnXO8zwWPT54YZ4sYfV2TxuT9kB2FMmeMI+ARHkiNy", - "A99BSSWAaTDSq4EfzG7y34MfplLxuAi26fjODJOnKkmVTWKQRHV9QJNDpjj6w8EJft74I+/xM1wWExxq", - "Pim8YqYEunXTqOUI69mP4FXPdTsBAgw7+iAddvTfU4GZMkiRGR4imhaXdC0D5NebdL1K4QAzlPDEFDMA", - "ppphzXKlNgAeAEcRUrCV3LdacYeVbJiPRXuLx41Qbwabq7KNKEMnPxY202DnpX8/SRII4oso+a+L07cI", - "TmW9Bua1PELIZBEwrTCgMIWrUyfTXuNghsxFFdSvG3ZoOOxk17nhOow1lTZhvteDO8Uf9NB+MN10afhD", - "v6+bMteV++i3P0wr+3ovJbGBnhx2PndR4cGUqlk6zp599BO0CTHroiQI0Jo55tZBkmAK4CaFE98ckZiF", - "iNtTIFogjHIJVAxcGVOGxWJZ7pqH9JaCfGKC5wrE+GMIwXLDzv7QhcsNO91hh7A5/GZj6oadz34K2FvL", - "5mJpcJ5ll5sZE+0NBuurwZctfT13li0uBu7ZBmy0irJKj3oFLfLnn+t+4N/a/syufjDTnedoOMbwd873", - "Z3gBUdDYi5ao5wqionZjFpDIqd2rHT2Pf3mgFysgUfTYDPpU7Jldj2Xg8M+KHWGx8m201H3/xBw3eKxD", - "peS2fxr+fXb+c4/33PrOydyFOvtLYwDsiTWlkXkZYYkuYEy9C218v4Zf+/a/zvYDGL/LiE8v943pjiI+", - "RRFlNgS9EKis1QNLS/jIIJ9k31kgFFeXbM1oEv/6xz9hUJRN//WPf1o48X/945+w3TcMoheUNb6cESzU", - "mGB1uY9+ISTp4YjOiZsMFB4lcyIWaHtgff7wCBWqq1stTQ7ZkJ0TlQpWCNU3JcKkbdBeFej5UJYSaZFj", - "9It0YuuXmNhGj9/G7WVDykfd0V0PAh/MoDABfSo6HgD4MmpqO1tLtON3mZo5l5ym1TDNWrDeavmiyI0y", - "3NszA7ylgAES+/YdPLCTRmsXF6/X+wisLcMVUKMGbIe8GWtG9L/JpNUyyUiUskABKhvZFOAEj2lEncux", - "ocCG2YIxDmaUkTy+OIO3dk3su5FqGXNwdoxsIGQXXh2y04sNcLEqEqhUkC7KEJFd1lhPWIDLCcH6Fdm1", - "2KIWy1bvi5iHJBoy3QeMYIoVucaLLsrKn7qCGRFW2mCVXRiQQf90LaPjI22BDpmF1Oy52FqbuCPIJNLG", - "KsTEm9rP0NuatvRtzHGh1Dtm4ZBlmXZmIIgzTbTTC83GU7DNuPHQQUunF44+610kOQoiCusTYDZkUwjN", - "cQiu2jgv0DnLKjIuVZvtyIzYwFoaBoIoMmRVqL6a/DssMsADahmlfjys/nOVlZ7bwT+rT0BvMINXtvxW", - "7ci+0+5azbb4Z7pXs3UBb3GxZryrgFrqVuDbJVuLSzY/3dyFm+/W68gB8j1cNK7p4omCcR3v1WlunhRI", - "9hTeNrTmkE7gxoILdHZ4jHAYCiLl+r+3L07P1HBprpvpk1KL4qcIC7FjMScxyXwhZQZ5LuLg3I4aYTev", - "arnV4vm2UarF0njSZWVZ8iPv4U+PSqe3OUbykrA5r307SVYGwlIZcKg6l3NLD1SjiGTqS7ZPi1y0yuNr", - "QmyzI2epumTF8/GR25CP5/u1XaesejY8glA8qgjEJxSE5TToYhHl58TN77NVdODDS1zDXxdrDh5PC3ps", - "N7GPzZ+TuRhWyKaloAHsaDxA3xBlYDoe0k63PXgmfkGE29Wu5j3MOpuW+RQZvBGYEHhhltu+x+aVdqav", - "ae/PZPkCeW6jsViSf1NRWhi7Oa2WGbjHtiLww9m30MOtzNv7CymxDOYhMnhQx+5KCLyqa1guWLD+Lark", - "3jnaxBzmRqxw8yZh5sU2SEeZnfVYet0BK5af1jqdtWspQ5OITmfKXDCGdAJxdKpYzhlGufUIo8zKJgus", - "iA0ffI45uWeayPaGdk6EQqeHx4b+xSN14w8IKF1tKjnhtfR0fX/+a4+wgIfZxUmzTmqf3LPBZPi/lGf7", - "+LvuGeaaUqceNCmMX7D+JtAbmdj0PuX/ufVTRMcCi8V/bv2Eo4Qy8p/bBxFWRKr1B2OWwWOddI9twDxj", - "5tP2Cy0TDUQTmwKC6AqFP3urpc7v3v9Tqf1m0rdS/DO6ftP92+j+RXItVf/tUjyoAWD6eKIbrozZfNSG", - "R9/gZh7BaWo5sgA3U7pFygFnZlwqePT8co9twDfNOK54bLT0/ucbcunx4Vj3+KgLhIQCw1DgwKb2PdJd", - "gBvHoyu3tt/Hvwg4iMd0mvJUFrMGY6yCGZE2ozYiZQH83NTu/HhuVLy/Yi4dPObR8eh69Te+fyCNv7qg", - "RnibC71VOr97q63Ob9/XOr9BG7VZx7YKQ9dV6FlvCIJ2eKNt2bgEy1oPzvaNy2eLoPfaUMnNBQQWxP6Q", - "/R9tf/ymCI4//uDSG9PBYGsPfids/vEHl+HIThyrEKYEtQXVDt4ewS3qFIBaoeZankxdHYcp4Qys51Dm", - "/+0MpPwiub2F5Ljwm4XUykIqkGu5hWTX4mFNpHKlike3kRy/+Qhu8b7/nFbSn/x6pGTByXQyoQElDOp9", - "QNK4rMUDGkvu283IHZOFmb2PLAQTlTSR1mZkJrVWaOh5ieFHDyQ7zmsqPbb16KoZP8+kJ57Y8qDWXsu1", - "hWaD7Wvjh8Hjnl6Pb6g9ZxYzFlGddIlWuj1Ve0y9qjg1KWg5zhdEGSNhzJqsxT46zOAdbC6ZNDWvwEIw", - "VXFn2kLw1ccql7zy1biCuk6UyO6QQdVj/djA1GxckYWpaEU5y9LMspnawlC+ZLJyRbEn3Ub3r4T6y6W1", - "UkIfeRvbAphPp4Q+meh4FHXvuFRXeC3bGGBxj0m2k3mWrU0/UTZdf1YRz0ZYZXMroBJ6VK0NnCres5mv", - "GzNuAMr8GI1nEQ4AolG/ZtDDbPq/gQssNgU5/YJHEREGFS5JlSufN2TZ4CgrlAe3tWoudfOjlCkaXZrU", - "X4PDIhFmCwuNNGSlzrBSJE4UJARD+jmMUJDEjLhSN1APmvJUwluQu1vsEuHoGi/kkNkUYvM51FgVJDAA", - "ilHURz9zwFNAeIopsyn++k1Tde87OWSXNIzIyMIhXCIqkZxxoQgjIYr5nMhyvwSLiBIBkzjEmnISxXgB", - "uGQGotHQhyeEZZnCGegC1//GLKRQk073nE15f8gw2hoMUEwwk4hCXr7EE6K/sm0gGERpQN8jjHYGr+xX", - "lXUD7FxH/jW9X4Qgcx7gcbRARHMxnIhqHRYw219Q3lUv34QKadYrcy/a4l+lhaXSlakNuyhlAVRRTYX+", - "FxcoZVnOuF4ulQoG87SXcISKrPqgxcUYkwBrejJe7gcQCXkQpN5Ma73UhfqY/45KZmF6F0Aqf5p3BBa7", - "1k30mjOuZrCnOWyl9e8buCpnqj/HIePdJFwgjAp8nTsUoJQ8m6I1QPC7zCvvMVe89XL9e7d39Pa1gsBt", - "f4Oh91zOJ2AiPpmUNuDqo8ls4GXpFXUW/rPu00NXcrUo4kKKp4xLRQMnDKtFwb8Zj62Nx+WU9XLzhIur", - "om5V5t+fuLhqa31ZXFD6vIyw4gy/wnsAPTzAYH766wBwRhtDRTPNoxtoVf7KdikoXVRJF2fMUcTZVO+i", - "3Cn+6F77ikUXRCmo5c6Uc04QbYSM7I+maquejK2JCR7+wLb61LJI9/4Id0FvuUI0TiISE6jq2jPMphc7", - "R2KCoutUohyJ6HayUu+qYuqwsQWluf7vOnUI+Mot2Bpo7/Xl8grViE9X4/FlnTvwOQ8g35CZovDEVZC/", - "RJkM1gqtgWpC1zMazACcD+xW3b7B7sNJcpnhEq/vozewkYvwzND5msG817wmeUQM5t48ji/363VLP5yc", - "wEcGl89UKL3cR65WaXZ+SP1WEWxPzyLCUqG3FkJwLTPGYUUvFdb2Zja/dQvDl+NGD5kPko+Ra9sgnaDL", - "AjrfZQM8lZO3v/Lpkylj3Wa0fzMXxZE1HYE3CQs7TTEWNPID820OBj4Q6pYggWYYD4wRWBvMr3yaVRoo", - "sTJOkrbsa4cJXDyP4yU8jNZyCYKkCnmq/ipVSISAjy13NzE3WsOBrTKFrzSjMiOV3MZeB/bzRhIZ6G8v", - "qbRQ7XQ7hKVxZ/83+695HHe6HTueAmT4LZT7FWCL1QbrES96ZQqIit/U8ttgJZaFfQEssXJyWHO6WSM/", - "Ny/86W8Wnc/uCdkQ9IOKE/drUkEL4y07fBjPICafF3qUdTVVtLZmV42bZU8PL0xdKZw2IRwX9tML9+VX", - "YP2uiuxwY0Zuuo8e4lEfwXPOhJW12Uy4qEIOrYr9+OoZ6f6WpDbVNhzyjTdv7+drxZhaU6+LCPtBaEqj", - "4VTxGCsaQFmeYMa5LLB9hhRsCmhZ923GmeDcMHamDaG/1Kx6aR3Bl1aR37dOK4SLj2wfffjcBt77v3CP", - "8i9+KtjlmcTvOuUboOuhbrigZIISnEqi9ao0JihYBFoqmjpMBAczFOBEpYJAiTmCYsponMZF0Ge9YnMM", - "IBWXm/FlF41ThSIspmAXmYcmnF6QgMcxYSEBD9mQzQieU23UCRRhRViw6EkCpWnnBF1zcRVxHIKRb6Nw", - "TGk7QTQHAsJ1TBQOscKgalzqHT8yWTyXWbVaY1gzcpNzQzhkImXfG7h93eylG+glIgBYTeUsq2oY4JCw", - "wItlf/F1i7H79wZfEFWd6BPF5dxJlj5loE7R6+mG83XE8DyzYGQu7DK2EfNLlF7ZbESW0x8cG/17bmkz", - "VzfHJ7riyUi8bBd/HXc7GdN9Nfc7T3+BwwUKU9NdYVcCm/9Zb2UygVIMd4LUSrOMd72aycq3ZWS+lczb", - "+MP9eXwHb9pXIgm7jYZ9U6GgfNJfg8i1VL2TzH0iN6L1JRW8Yk8ogl1M1ZOpT1wUpNxzcXdagW22Zia3", - "i9JJCQzWF2ffxHZVbNuQg7uKbeebrV2qFwQ5ZT2I0vRLcOvGbRTV1nXwb5oLUpldQWQ+uYjM7w4eTSwe", - "Z4LQiMYELyKOwz9DmO6SG5yAC2HwHwBR4jnhjxa8hsUAffDN5fXOui638sPJyXqTlBBqqYwQ6hlLiEJS", - "jP4s9pTlP50TIWhoYTrR4cmRDZilEomU9dFpTKFw/xUhSZ5TAkAefT0/h4RRr3ZegrzodghTYpFwytTK", - "UeSvPsxgPt+pRvojy0kLaP3tQrr1hTR49p+fOAMpA1kTZgLLLVOF1coimJRNuIiNXobHPNWtaxmkyaTX", - "01SrnNCIyIVUJDZxgZM0gu0GxRFscVr7nVnlLkTF6p1jEtYSImIqJeVMDpnN1kiI0H3rz3X7hRAn74WA", - "wpl8PTNC8usIn9ODMRFjWDVRDTCLoMRmZ7+zgZNkI8QKN4Ro2eF9wZB+gng4JBfxmEc0QBFlVxKtRfTK", - "mCdoLlGk/1hfGlA3gu/uu/Tu3XeWpvQxm3Bv8TTDsxkz/6nyqqxYcxeTz06svSHFzeLkDyy0X6ytLu4r", - "CI56UHnXIdegVNGIfjKiTjdCpaKBSfrJIQs+nOSoBUN2QpTQ72BILosiEijnsNlIBA82hulgsB0kFODP", - "tgkMDgRe8+MYejw8e28SQUnMxaJryv9Cw+8Ozszt7gRbb0JhoLZsMDreOF0RYnwBZPo3jtEzE1yKHeBd", - "8G9XgrdHBGncQ7Jhi/JkmanEkz99EKnV4L75FZ6nXwEgmbLZrE0FDkAplrNUhfya+X0Icx6lsf6H+eN4", - "FbCXwsHsA7z61Wi7Zjgru3ETfBab0s4pJKa445NcehiCPdeYVU04NwVQYkrRgN5T4ED9Gbn7/t33RTp+", - "hdedlqKucOpXs7ce++SzY3AYF0V6PJdtbjjNzUTx5d6na0ybvU8/Rjy4khYMpeg21HYbAIzrH3NAaHtF", - "CGoC5GYiCyKEyE1CBSC/VRyQBnNHIowUETFlONqAOZtGANraebHwnFNIkQ4iCklqNATUogjQ6a5nhCE9", - "G3BUuQYKN7rSlpYqvlO8jFQcjUnAY+Lgvtd9ptvfMFU/cVHG7v5a5OK7Av31fPRU9TxXwJU39/hF8OUn", - "+AZCpcPUXii7Ea294fmPxhXURbA2w872QA47XTTsbMXDjl6BQwwuVKzQLoopSxWRfXRk/FuQBLs3QJIE", - "nIXSoY47D972QDalxBq2bMiv3IPvHlPtsVwFpDy3nfjEg34P6e8haQetFTec3ZNhFzZdiHiqjLvf7iv7", - "VkgUuEfWH/2utrBHvtn2bST53+z2LckoWGUtLgtLbyR7ksoZaXa5/Woq+aRqDGjWrrqmnKG/87HsIkau", - "jTdcSNWvyT399Znp4DGQ9nVXt0HZt3P/BrHfAmI/p5UfLtEEWOoj2XGHwUwkNwYRFru0d8tDYEkAdgMP", - "cIROD4+HLNCiyID7CRJzkE4WENycwgd/u0CvD8+76AgqPaKf0/F6H52yaOHqbZs7miEzmpgRXgFmaGy4", - "loS+49mMHbjnIYPFdQdPVDrZ7AzPzYpbKxck3u3MCA5BI/mj8ys3nXkwgs9/1RtIr5/9Mlv2zlLlo3NO", - "lFj0DiaKiHqzJzZPimWoFfaQdiBwVnEz0JO6Q+mwz/I+jW5gwCm2tzoerIrP36oePHyF0Me5JTNxIqbe", - "3DgFrE8GSQY4XDyvWCY5Q5lw9InA4nGd1Q1oyhK2smypgQFdNkV+f0Uu96Wyq4QE/++6u2Cmz/aiKSmt", - "k2birN7Iyptelxw8M4DE9qIqwAkOqFp0EY4ie0bZkyCLSOll6u9YEHwV8mvWH7LzrNKJTehFh2fvu+6i", - "FoVUXpkW7F1sH53OiZDpOBscgo1mbo2B5iQcMsVRgKMgjbS6QSYTEkAuLhQwkQ13udlQOg+4d/JOvNVW", - "ClHt6bMr8ubnCVi9nC2qHLdhlnpDkCDCNG6G/7aKGgQcQqjBWDfKGaJsEtmQqkBwKZFtqkciOqXjyAYI", - "yT56NyNI4pgMWRJhxohAqTRR8XrovUQQKVOT4K0bAJhcw1FdlEP7JYIrG5oQcS6kiSbQHP7hBElFkiVs", - "dm5aPoE5P5Buaxq3PT2Rk7oyhmZXiH0F6QUxnGIIrvkojVwA46OGopsBPbWW+Fw2/jtBp1Mi9K7ARsia", - "cDyzrR05zaYvZSw3Fny8yN5qV/Axa7WQlVjI2FsKzTbK0a7Dzu2i/jydX9FG9D776HZZxL/oj1r2Xc5W", - "9Q/CPvrCWf5Z6uhfFJIE2zqwcg5/bu6kwshLW7WUaLsaVqt1Zu1DZrq2xs96Mtis54yWhUvps00G79fH", - "CIPHRXl47JJoz5u3SmhXJdu0IeV/NZ79V8GBDwNk/8QoJ3cAsv+q8u4Bafzp8E+8G/Wp8uhLd8+u2uyf", - "Hov+odLnDSA9wLE1pc8bqWeDV5caSh/sO+3MJNvin0mDt/GOt9DfHdm/Wf0tTIYCsVZdQWuGJ3GiFi6g", - "zd5V5kFnkn4i/YaL4Cxu9eGugu8Q0nl/7OH4tDGg889ZHP5JYkZt8T4q0fGRp+r6M8MYLO650sGyoU+d", - "HhbBjM5Js9O9vIMtiRJBeglP4HIlNASz9HBnmcKiP/2EbPMWc9X+C6o/Alg+CVFIBQlUtDCVOLVEMH18", - "J5Hg2hKA51wsmqNEzBb5SfD4wM5mxXlo95R1huVxhvGiF2KFe3MnbZa40L4gutPFU2qBhyhDb35Ea+RG", - "CVNjAk205YPoJCOpKbcvgSfXiwPeHDR4NuknMpqO24xySbWQU1uNBQWpVDx2a398hNag+tiUML0WWtWf", - "gCabCD6nIQlLY+zMeWSoutlA0Nv6XbVSkZWOc8aFGdyT6DBtDqTpJ5qUxUIWEjOmDMPgVtblKO8pk8Sv", - "+8OUuQAcu0ZuFN+OMGv5rTljR3MiVMK0RFScG4jn9W/H3HM+5orJUO5MK512LjxnufO6XX5Uy7Slhyj8", - "kOXOPa7b+sPXk9JD5bPM5rGu83lmkDa5zb8uFhw83vnw2O7yD884BfQNccZ3wVUODegWfQzzK8R0h2RO", - "Ip7EUJEc3u10O6mIOvudmVLJ/sYGxH7PuFT7O69ebHc+f/z8/wcAAP//Mz8yvfvkAQA=", + "H4sIAAAAAAAC/+y9/XLbOJY3fCsovbvV9owkyx9xEnd1Pa/bTqe93U68cZJ5dlt5ZYiEJIxJgA2AspWu", + "/DsXMJc4V/IWDgASpECJdmI7nk7t1rQjkvg8ODifv/NHJ+JpxhlhSnYO/ujIaEZSDH8eKoWj2Xue5Cl5", + "Q37PiVT650zwjAhFCbyU8pypUYbVTP8rJjISNFOUs85B5wyrGbqaEUHQHFpBcsbzJEZjguA7Ene6HXKN", + "0ywhnYPOVsrUVowV7nQ7apHpn6QSlE07n7odQXDMWbIw3UxwnqjOwQQnknRr3Z7qphGWSH/Sg2+K9sac", + "JwSzzido8fecChJ3Dn7zp/GheJmP/04ipTs/zBU/V5jF48UZT2i0WJ7sr5Tl19AbwrniKVY0QtJ8gzL4", + "CI2xJDHiDOFI0TlBlI15zmL09ugMRZwxEunG5JDxsSRiTmI0ETxFakbQjEsF7yiBo0uk8Dgh/SHrdGv7", + "QZh+Eq9fpb/NiJoRERgslci2giZcIDWjElGmn0ak72+YEjlZXtluh8YJGSmaEp6r5YX6mV+hhLMpTMu1", + "i9JcKjTDc4I+EsHR7zlO6GRB2bR5kcZkwgVBPy8ykmKGsgRHRCKqEGWKu9mYNSpp7EkaIi46ZVyQUUyk", + "ogzr9kcZF+ZEVEf/Gv7ACfLehaHB+0jNsHJUzrhCl4Rk1YniK3xZXcbfdna6zweDwYduhyqSmmOFr2ma", + "p52D/SdPdp90Oyll5t/bxegpU2RKhB6+/QULgRfedCTPRURGEY3FqplECSVMoaOT4ze3nEBne9CH/9t6", + "1ul2tp/v9Lf3n8G/t/c7/rSWFr468k+rj965wiqXyzzInKaRJZSRRyTLs36Vp2MiEJ+gKBeCMJUsEBwp", + "Ercgusq0B6GtiDib0Gku3BEMHbnKcs6wRJgZptGr8YuysVbnLtJMLOZXbCRIiinTa7w0iDfuEdInFNlD", + "pIcUcaYETxLNFJQiaaakO0VdzcYZwlmW0AhYT+VQ7aUD2el2WJ4k+mFthOVuk4ROKbzQammo9DbJfYsU", + "R4QpIooT3mZpKmyxqeNyuYO7UfLFVl3OeBKPcqZostzrO/2zXVO/JyrRjCQx4pNJF9EJwki3on82NF5d", + "9p3Bzn5vsNcb7L/dfnoweH4wePK/nW5nwkWKVeegE2NFenqX2+wNMG1JWRTeHVYnkVRfSIJEZneKC6uy", + "gWMS8ZQg3fTKke99/sgbr5ujclP1i8i+WN6sga3u166MdtSdYKkKJgT7RdVihANjektTIhVOM82H9Bi8", + "xWziQq7B+j64lV+5wNuftcCMXKuRXaHgfEL0Qa4zEukbkTtuUggYuj1L3gXLuhcaFwRLPWAtJunL9LdO", + "zmSe6aubxKMswUq3q2UqIINRSqXUnxY/xFQaPtLtOCIfMa5GImfMvMiIuuLi0n/TtjKiWafbmWE5mk+z", + "vNNddW1ViRq6IAnOJLRnd1yMiBBcdIxovBhNuHCbpO/ccglXNLW0QrK4YgMr1Ol2KgtQsHM3FzfuYleD", + "g4NegJaE0SqMGgCTWR6439bycIuhrWbs5hYxQrTbZmQ/llUOEFM8ZVwqGslWbB6EB729KY8DrPO4aA7R", + "mDBFJ5QIK1cTJPT9kBLkGkG6EUQZymXtHBSi/4jMta42mu+NVJQtL0pNsfE3z5NNyhvRu5WL7S9Oyhoi", + "rc49qDjNMYUzeUzm1FwtVdnNbs0oFnRORIB9FwKAYYXmPbShz7pmIYwzsllZKTanMcVt2EEMYxrRAPWc", + "HZ0g8xidHKONGbmudrLzdPys09wkw2mAFn7OU8x6+kDoYbn24V2/7V/3gioKT9N8NBU8z5ZbPnl9evoO", + "wUPEQML1W3y2E5JUs4iOcBwLImV4/u6hP7bBYDA4wDsHg0F/EBrlnLCYi8YlNY/DS7o9iMmKJlstqW1/", + "aUlfvT85PjlER1xkXIDOtvbg+Mvjz8snm+quhOj/x5wm8TLVj/XPpGGdiJBUKs2vfjSvIUGMKoeuZlwS", + "FOFoRtDYKC2gnkN7bUi+6NjdXqERHFOZceDz6P1pKc2BakiuSZSrSr/fo5hqXTiyQpU3uRYjirjeI33/", + "hiQLWAFk39GqtnKi062FgEgQvKY7/UarzpbPf27Ia5TKptbdK5rNpzRJqCQRZ7H0+6BM7e81T8Y7xeba", + "XOrqhf4ZpURKPCVoA8xSoG4YDq+lrQmmCYk320nYTZP5Ox9791rlzAEZ9PA42t7ZDTK0FE/JKKZTa1es", + "06D+XcvHuh2F4O3wREDCaDcP6FKQyXJ/P8F9Ap0IMiGCaIr/zO4yweeEYatS/Qf02/l/tkqD65a1tm7B", + "Yp6Vr3/qdn7PSU5GGZfUjHCJTdgnmoxgqRF8ER4zPFq11x5FSYXF6vMBb3yBk1gKm2vXxpp+tLyFp2s/", + "eavfqTN0YEeFgONxgUa+/UJLWgGRhTNlH9RMwHyKEsqMGqT5t9kLEPYWGfkh4dPNzhdbh2L5lw+/Hvct", + "mJf5oaE1/axbaAUJn/qrOSNYqDGpLGbDvWobKkfXuPxnleNTu0CxJKPVHOSMMkZisLnbg23e1LJ1UPeB", + "U3RJ1Wiub+DQmYNh/UIVsm80NpXw6HJCEzKaYTmzRso4psbgelaZSUCErDgzMBgJXIMg2oBSff7z4c6T", + "fWQ7CKyhtf7qF5Zn4n2tm7fihcJijJMkSBvN5HbzO3qZQsIUUBp8m+6eggIdYRpO17G7aZX3XM7MX8C7", + "9ajg7tNsQJNXov9eItxu57qnW+3NsYBV1817w/pv15P3249lp96vZ0X/3o9v7FC8n35yo/J+OyoH6FbF", + "qElBEzhMOixVcspUjzLYAi2AW8uTuTpKM6u1ZCC9jYWEBzf7ZwlWR0siFdp489PR7u7u8832whWVlyNJ", + "P5LRdLxSbraDtoKy/gzpz/RVOaVTPF4oIvvoCDPGFRoTFM0wm5IY4YnSX9mxVhTwJ4N1TphGIYmIJhFJ", + "fdzDz59dX2P1fJ9eyecf07GY/n036P8E66LmN80WOGx3U7+KBGaBTbzdRZ3ia635GyoJOT9PjbsKFS/Z", + "PZD1IfRRqdBP6LVec4W2KysdXNyw9le4sBhnvZzR33PY7SzBi2UdUBGc9oAiQstr+IY5QHL98ZH6/Jhv", + "3EyvMFX65BQ+UzvlLuJJrO+dCRVSddp7w24kGBDxpUSkyikLS0zLFFFfwMZLvRhn41lJ6IREiyghy8zd", + "se9M5NbcGhN958CfhQGxLRt3Q6lwYvfjWdFF5efjsr/K7y9M55+6nSOc4TFNqOPMVT5tffIja/hbt0t+", + "Y8fm0zf2y0/dzoRglQsSWMtziA9Axk7cS8icJMi+rYn3AB2enSCZiwm4zIFazbuIXGdcEjlkgkyxiBMi", + "gdSvZjSaFebKMY4uJcKsDAtAh8kVXkiUCSIJUwdDNiws5HLY6aKhUb7s34ZKzN8mNMS9xKaCSPsPck2i", + "Yaer20r4VA47ffQzl6qn1WUrSx2gobXHyGEHbViTWoalVDPB8+lsyFLM8JSkhKkugiANMANrzpQsNhFm", + "MRp2BJdEKdwjaZ4A79eNHWZZQobsnCY04gylOHp9br7d7KMzInpuOdw+ICy09mjsrShnmpoJjmZDZt+U", + "v31AhClhoxLaMwLd601oRS9TofDKm3x5Yr74VHgybvLxK/uJZit2xkF9QiwKUsLOQFzcFHquXVg3dEXV", + "DFG9V1fMrfKQnRxLQwhSC50p6FjWzI0kmROBE9e81NcLB1Ves2V7GQxZlPA87s0WGRFzKrnoauZMIogD", + "EF30O0nzLpCG/quX0kjweYo4G7JEU9AWTuP9vc0+esGUoHbjpdn2sbl6ajvcdgW90710GcABvUlj5+aL", + "JZeA+dlSVXeJJ3mbV1JBQUse2wkx+RUca1lodXu/2ntjx1eQDJUNVIM2cJYRLCSirDxzm30E0pGJgkIJ", + "zlk0IzbSRZBkYdoo+7miSQLWsSED9z08tOwR7D9enABQKGbegGy3Zv+X3UVhQcYuEeiTIEBURxlziMXR", + "3yLOqmJNnZTXGratYFQu/rpt/JmHAv+wiGah4C6p0NHZO6QfU0UiTSmV8WKR7gcdHFw2NKd7xSBayYVU", + "JK00B+dx7Zy5plsY8brJnhQcM3xxF07iZR8M2BhK7yJJSKQZgonyMFqFvh7A5Yl4CnyttpmWuzQtkWtc", + "rutdog0ut/SEN73jEWGmybOPXjMElxuq3G1DZkP9oiSPiUQeq+taAWD+UTcg0Zxi9Mbcmd+7P4YMLvwk", + "sdZtJUky0QdmToRWfUDUL5wJYEM0PNbR+pDpQ2eO1DSnMbwHq0eVbsf60WsRfb/VFs0b9Q1Dzyren2Kl", + "u8tbv46GXpU3Z5WIpliRKxzQn15qmujNqYQIJ9gs+y46OesjeAyW52hmnmp2pIUetGG2i7LpkMETK0Hp", + "fQcByOy/9WT10euUqoIqGS+YnuXzQzbDEo0JYeB0SuYmXrWkoQVR9ZDK7UF/ezDoD/rbIZqd6rGPFB9N", + "86CZznH6WF/ACr0/7Snee3+KlMCTCY30xmdE2FHXOHV5OS0z2pTHJGlYafeh87gnfS2TChpPybAD94uV", + "FM1vhiQzIvS43h6eDZkVOb9Hww7DynxTsuBeJvicxiRGrw7fog04XJt21awmYxrW92vQZtrtyHzMiGoa", + "v3kKIZvhrYDIy3Vc0SzR0hatI+/G+3ytPuKpIKgITNASE9CXu999egNFQjKcyRlXIOK7f2wJIhUXZFNr", + "CjZ4Qz/PcC4J+itKScrFYsjc+12ziwlWxqFafDvRZN9BG1HCGfGVGjReaDVIv6kJBSPXlFaIqIKPobei", + "Wz2mPCWboLLMuMqSfNqzA+mgjYTOiR2XHgH9aAYwxknCOevZ4Cn9pl2KIbPPkMJiSpQ1VEn4bC55BAOH", + "XUPwTxCIc0Yj4MFmHNMs7/kKUQdtvDx7t+VFOHhPoeWYysse5b2EplQNO0O2ASa0k63XSOg7DH6nbFq+", + "bGZTzBFetzPsD9l7nORWSNZ72lMiV7NJnhwgTyjXd4u5FIvt7hbhp0OmLwq9U6jQxLZ33a2UgDr6ngqV", + "44R+NMa7icAp0YccvT8dMonnxFHMTZWv1SJbg2nvtmJZa+H6vFAHanJZRpsdGFrtBzLDUenE2EixsrIm", + "Qa8zwvRbMY9yrTMjyibcvVoN2Bj0d5qCQBq6N2O2VsqieznTmtuUamlcX4Gc2bM67PRiKpQ+OzKfTOg1", + "0EvONJGb+8DYEbqICzTs5OyS8Ss27FSH+XQf70zi8e7aHXDD7lbWMLgPIMtZA1Bjasxqc/XRStu0vqaR", + "4loh8NUPe/F9Act0Gf2Pk4SInmbHCYUgWUfPXRMYNyVMy9/60QTUEHe7NVhXe0Ex4M4NuDcyejbsqAlS", + "a9zQNXOYJlyz6wVyEynju/ropLp2XYSNOkdlfZWLwGQvBgttkP60DxdORHuDwWDQwzu9waA3qFN7stcz", + "0aUZVooIPcD/7zfc+3jY+99B7/mH8s9Rv/fhr/8R1DJaBoY5F5Kd54Y7S13kButHi9UHui6S7NZGbH/4", + "zYcX9KXGnY4EgWOAk7VjOMvl7Mh7vZnWj06WA0zMSsU8uiSiT/lWQscCi8UWm1J2faCFFamq67b63VVa", + "YyAO3UgVhc6KJbJq429bcywoZuqDGSMa+orVsLPZRXBraMHoGEaEej3XjmVfLsi1EnXbR6+4Qtg6f7Q4", + "fAAvLfc7FThNMZhc5jihMRwOa9GWNCagO1IGz/QbWsQQROWCob3BwD0ZeYNiyQJx6aYy7FjrjYhmCKal", + "rxFQI00wekQyCHTjMa5lBPg65tJJ08fsr1vFXxvuz/6Hv27+n//4oqQOhLaCxo0qeEN+VgupBC61kfAr", + "IiIsCUqInq3sophOqZbR9DbEEIUAJvXvUWT8qiZKSAt4zNnJ4L0qOaeLHs5oz2qtxrP0K2FTNesc7O8G", + "F3fD/tH78Bf3U8PCijwJaSNveA7mJHjs5zi6MbQy27rVzRMw16aUnZjPtpdFyM/bYTeRVTttVJbGrdZ3", + "TBGAv2Ygy8mun7qdKA0JEXMihD6Khq8dnR5rBeCSWIamNTo0zAeD3QhegD+J/UULcZjF5rfNqsRjw3UC", + "ws5vHRLNOEQcJQm/iYGnW2fqTQE5K63rodUuuf9y+A74q4wHCuJK7YtoLPglAbUSMksokeiSLIwyPK0Y", + "gwibozkWzqnwFiKAjcpnH4FJmoBuqVVAyIOdcfClGrYIelJiBElBcFJyyxRTZuxGPRnxTMt4zL5mzJQX", + "hM0vUIozOOZagwN3S6q1aKqVrdIok5KYah49ZAQOBsqwPvNRxEUM6YbcOHXKVfhOogsTeXQBzV9Qpsn6", + "whzMmpnpj87rd29/fP3u1fHo9dmLV4cno19e/I/+2XzUOfjtj47JWy9iHH4kWBCB/uMPmO8nE6cG4TOd", + "w1zNuLC6ovOwgWsYZ7TPM8Iw7Uc87XQ7f/H/+eHTBxdZZZJk5voYBAb2KShmWrtRINzV5RpIZOPXXeaU", + "XjLNopzO7inrNTOokfxudCRARaF8NM5kMAy8rvSXcuj2YHD645Z11D5x/9jsI+v0geFrHsSFFY/lTJNP", + "kQIPToIk4ZE1F0+asl1dVyEGD87UjNNQNGaNOZWvLvOoXq98egNWtDWmbEvqbehFN1t3oJtbxwS+YHMq", + "OAP1HMSlcUJk9ay8en38YvTi1fvOgb4I4jyyOQ9nr9+87Rx0dgeDQSdEoJqC1vDAl2fvTE4lHBuCEzUb", + "RTMSrfUU/wzvHsGrcOLAPgYacsBSUSyNs5jpQ2C/QRuzqpBijjyCfR12dl/+aOhy+yWQpNtPm7xWtGIa", + "ruUbvvwxRGieIWdZYCqemZwMFjviWYZPAIeyfmasLb6XrMcwgELALY1FjMYci/j7qgsavvzvF6fvvpMI", + "lH6cWFO1kWDhGyOScSJB5XSu8dLgJ7u1JQADSmrywDFDhE5nCs2pPrm9NKXcfVc93dYm4Y5tGTNnbdwB", + "E5jnZe90O3pm9j9ugp1uZ/6xmgW43pJmfdOtVK41uhROMsrICmXqK5GZr7i4TDiOwdjyJUVmL+qjBpxg", + "HlS3u9DxHIEv4ZGMMYuvaKxmo5hfMT3kwEVjn6Di5eK2udYzwcm//vHP96elXWH75TizV8/2zpPPvHpq", + "l41uOhgdXkwkz8LTeJeFJ/H+9F//+KebycNOwkhmt5Jy7f6/MC3UIRUs0oyJH20I5SjEmTKjjDe489Zk", + "3YZuLj4nIsEL7zqxY+psD4Cn10YlqPFJ2e88S+zqy0W35qSel3Wj1s4gfH18M8F85SYYQYD1jrIC3WnV", + "AXlj3i614wDRBWjuR30XWGmmDaUVhLa9c2r/3FkmuTDFyUuaGX/uCE+LbKVVgFDnlzSzuid8gSrhHHEO", + "2uqYc9UfMgP4oM8mUA65JhHQglRYocOzE2mCqPSVBpfGsiCkNVkPKQRel0r/r8hZF41zpdVTrgiyhgKb", + "earHAi+PCcpZEcRUUxbtBJejAmBZLolgJBkZZVC2XBnzEbIfNS4OTHWCpQWoESrPqut1/MvpOdo4XjCc", + "0gj9Ylo95XGeEHRuRLXN6up1hywTkPWvO9HkSG2/fIJ4rnp80lOCEDfEFBorfAY293n+8uydzZ6Xm/0h", + "e0P0whIW24gzJ1EUIWfsO82RSVxt1u+/tuhNCAnOn9vycJ3b18vT1d561u3MoyyvbulOtxECam5cxvou", + "rehPwXx5G6e8zMINQp6vr9t7rYTMUtWc4rYmRtMy4NmtjViylkIjCbe2FHq2qyWboTOs/NEyjHV1+yfM", + "DWSlpbS0rXxGX+emkaXAV/Nz183sFqt0UqxJzb76ZZbnUHq2qFbQc+ZmN9efRBsXOKN9S8f9iKcXXXTx", + "l8oP+uw7hVjLj1fIrAbwE6Z/8tuXS9F2a+xjt464w/L2+3EoG4FD0HwbKYGZNEGZM5yRPvoZmDhSJNWi", + "CSAt+QFJjF99j7gRWt2nQ6aHJitx5IWVVNIpo2y6CRGWY4JwHBtT6iSHeCcX1yBDiI3OXLkkGZrRWdwF", + "wEe0oZnowpk0L6pC0bLBc9mQYS2gSxqsWRLQXEG3V1tprnT3esKFoMlzZXBY7NSrGDk1s+q6gAs7liIr", + "7Rb7f16wizoM5jygwkL89KS0g3sG+Sa7txVUwjb5S7IwQbXW/o6XLPC+6T1sILdhlvba9Y33YwD65DY4", + "rrDbGwu8Nbrr41+PxwyZo9dthV6v1stfVQWDQds9N9mSYqx25yDWHBfSkzP9gRVKElh8UC0PTMz+Rdfo", + "wgQ0D8Q0sSQuXrTePATCQnDrhf2lb1u70IdcyyhfBDYVUAhBaPe3Fnk768Q+aEZPjZugpW5VNrgkJJPr", + "J6XFa+upCbiTBLkS1DEyh9HVUjwjbMJFBAlSn28YeOE1tiLQpm0Ty8lDZn29MTt0Rmzjl2DvzX6AX6EC", + "2ljHLo5rWptJfq92eYGT5AJt2Jc2kSB/B2A7u1eMs5LY3x6dORIownjen3Y1RWoucDFTKhvp/5EjfYov", + "6o3Zb90JL3Flnw1Av9rb263GEZsB15qt2k+DCf3NW3OWy9kdxcU0WGkdgK+x1LobKMvlDG0oPNVHxyI3", + "6Hti83MtuJRJEuWCrD/rhyAeWfQAGFOCKev9/PbtGRJkSiUkfwWjz028bgPMjFVsyzAgN13Fq2a8nd29", + "J/tPnz3vx5eiTyLRz2WPYKl6232c4o+c4Supxa6tdIGz7GC+vZbPmy0oxtfM8p0W1kwKPNUs2QV6ttHo", + "jspPSg/SJWVx2wZ+0e+uDyYs4rTv2A6fCdLLs6nAAFz2Ja3wt44XgdVs3tU1YO6rA0VzqXjqoxhurI4P", + "rS7WnCe9GCt8k/BQM9zlSNB0YZoyKnnQ/NUUdntej7WtQA8EIZ0/N3jHjSW0LU2wiMaQQOKR4oFcLyc7", + "nBwDiqp9txX+G6A4Kj6aTyhfl3YJIQkVEGYjlVjzhm6il0XUWpVcND4Iq2YRQHd4f+rHLPSHrAdS2AE6", + "Ljoomi2axKBi4Ni4fTe48AZh4DHReLGJMHp/2kdvi9F+J5H1olqcyCKbKQcPEwhFPSOS+QPIJchOqv65", + "NaEZSMlNCM3g9lm/AJ4HY10Bow8RwGNam49JAoSNsneJnz3f0ni5CmDljbmPVoGsVEB1n/QG273tJ2+3", + "BwcD/f//2x6F5cujZobaOqzyFhtT7XOfo3cnxzvWZr55a1yVL46rGWZlx2UwONrIJfGSxLgIhoB7kdYN", + "Id63jty+o0DsEkFs1btmJd7qN+8CMDQEaWIxx24O6VlnmGtx47zJLRt0FhmYH8pT4gnyNmg/osFswGMq", + "L38UBF8CEPLyvQ1JyyNzn4XjuCCzfLwAfA8wcgnO1USiIqu/NH5v7z3de7a7v/dsMAhAUi4TPI/oCNIy", + "Wg3g9dEJSvCiQGfaAL92jMYJH1cJ/cnu/rOng+fbO23HYTy57dah0L/dV2jDrshfXbEa96QyqJ2dp/u7", + "u7uD/f2dvVajsm6DVoNyLoaKSPJ09+ne9rOdvVarENLrXjiI0LoAH8KrPjQlHvS/ejIjEZ3QCAG0DtIf", + "QDbrjDJSOC2rZ3KMY4fqHb47FKaJXBnuZTqzbxp7a5onimYJMc9gQ1q5JGDmx9BSsEwKYwWK+s1assCq", + "awOB3FyKV0Ccick4n04talGxdKcmvd0TnihJ4gNUqGwr+RzsZjmwD010YOfQkhp+1aqTxQ/yiMBcXSZ7", + "WhBU0InZtMqsXAwAZVkeJInGpfwpFyYxHBpFeMxzZbzNFva+7MSAHzDwvuYsbmfu+ImLy7Vx8/omLtD9", + "WxoMJtZiB7c4LhD1LMaiJ/QVXmHjO7fPJXpjvjCGwvLnLK+WNupCT9agyFxOc1zYjW0zbaXLsNwCNnMX", + "RmL6K3nnPYW49SYmKujLathiSqCqhVorsWhKMXE65/B66zQc/eFaQ0qLdWfk6j4WHRLJeppse5Lh7G5W", + "fFUkbWFrKF+CW1jQGCDG9MxjVAIc107aueJZRuLC/tMfMpvHUuaUG0caZJLDOqgZoQJxQae02nHVznqP", + "saw3oUxHXLemTv/Dptg0WbgBQzzAYGZOTGiogXL3IfvsnnS6nfOi/IdlTNWleVOUUFlakTLifBkO4+zd", + "TSNSM8EnNIQ5BREy9qlV1Fys5q97g/Pe9n+baHJNfiCxUWaialIe16p12PfbXUQvz96dNY2pqJ+B/NEt", + "zWkFklYZ7VLg8RlXo/VVW4XGnQZ9zxSdlKL485BoCxAL43wyIWKUBmxtP+nnyLxQJLmf/lgVb7UY3VaJ", + "PqtsDmjRExxRNt1svfohyIXqNNZBY708e/fG1mNogrLUW1XUbDAoln30qqhYgl6evfMwzPoBw10dfKoh", + "behstpA0wolp0cCNU+bb24A4WwvMZ+WH1jIZEJvDhW7cQUAb82mWwzE8f9M7ef1+K43JvFsZE8SbzXhC", + "9Lg3PW4xd7nrZY5ThUnMmwwfhjBk2wPkrVVxglsvkndeA6ujuMLJSCY8FMLzVj9E8BBtvP/JJJ/qEXRR", + "VtlK/bsPy+LT937wxAA0cEO359Bh3YJaOeBBVbIOFtSpTq/SaeiomIyfZZFnucoUv6xuNL9cX9nINNLc", + "75FLSqrZuK1ciUzuEoLcJReVgMynxohtLSWSZFhgRZKFETSKq6+KVEuWHczkmkQ3yIp6oV//ZNDJc0FG", + "aiaInPGkGpaw212ucCchNHZObFEPMyfPDq84SrG4hIvRydUoZ2YFqpHVu+vQRGZKZTeY1M9v354ZZVsR", + "McdJPTZfLjncj0mCF2hM1BUhzE0FS4TRS15UT6mnCMqGwgpCjTIiKK+uYWc30O+5CddFU4EjgsxXrj6m", + "3RIJt2bbpbS9BLC9oohI2bC/26v21346yZN2exwa1vbacrLRTTb47dGZKxBQFGB0y7yzvMpnRPTMkXOV", + "GFdv7Y5cXarCdcUMluKSwDAmULvCJkT4+You3Ahqc+jPKzmCHnOQflab7QdOgVmqrjnnH1oJe/XjHvKr", + "p5jFoUKWJg7eJGJPAZ0Jgo5FDq4kGptYJJMbbsRyP5xfEBxTRqSsZbFGuUg63U5vYmd1sLWV8AgnAL+2", + "t7v9bGt1VOfKcF4bvTSK6Sp1z8U4mSgYl+xpCkrCpKsksYWzrIVBzKzjmvsB2NNy+CAUr9R3myfhuXjy", + "wWApIfcaR8qV2wELWcUDiv1jm2mSrMwHGiwKPj95/tw/n4OgS7qst+7If2uJ9iFMBUIMNY0Y60NtHQ2R", + "fwxKVFyEaqpwoWxi2pi4GMTiPnQRfg5ssuIrGzzzZ9mqrDUwm9oxt+cuMFXzdjX1lnjLbc+vbQCipaoy", + "hzvSq8vC6XVZQ1OaIy6R1OoF5RlhN1rPJ3u7Ozdbz7YTOXFRWjW+FAIAOTo9NjJRxJnClBGBUqKwreHv", + "MRkwLWkuA6D9mKSQvTL5fjVraQhn8BE9bltx5Es5wxuKD71x4KkpZnSiGbJ90+9ZzvDOk/0DUyItJpO9", + "J/v9fv+mOAcvSmCDVluxZWLmPMiDvpx93j7cAZxBm7n80Tk7fPuzZmS5FObS2pJjyg68fxf/LB/AH+af", + "Y8qCMAitqurRyVI1vWp4WG5xxEl84BWRd3JPq8rLYdswBAsD9koQVKwSNHl36GEFjdMq8vUNUlhXpHRq", + "ceU1SxaNa3PrAnll6VjlFcbzk79aFMmjH1e7u525C96xfRo0+aJ+4LKj+1YVIOXKIllLNVQywoqyWEli", + "/oo4m+vjGqqRVbki3bMWJVbgGrG1VIou/R+L3r0fj/yBeL+7Ei3eT7ZY1ocbRqisFEj/tiyHrudCThxd", + "c5jDtsfiVmhbmNCijgVDsx/4LrxNFFe199fT//r9/8qzp3/f/v3X9+//Z/7yv45f0f95n5y9/iwojtWA", + "cw+KGvfFgOJMsV0fLa4tKZ1iFQVsdFr9a1hh+8RYHFQ0g1JqaEwOhqyHfqWKCFOYp5aLOOygDQKaEnyl", + "xV2o72DSwDb1x2fGwag//sOJwZ/qbcQ2d1rYDSkgMWQ+jnmKKdscsiGzbSE3EQl6gf4rRhHOTMUeypDW", + "fxdoLKAUkvX4lJ130R84yz5tDpktf2ywkzMsVAGA6noAorCjMlGp9nVSoCMYp8uQFbd1gbhm3H79QviH", + "6I96ald4UVbrb1ZzejYIYdNBeoneyARK56HCgQZ47Akp8l7Qs8Hmsj63RscoaGgF+Vm/uMk/PMxD5uKm", + "nMVjEtMI+IpL25vZxM4iY9JQmjXiZYJfL2Bv3phcshjhXM00LzIRT10UcX5JSdcWIZoInEIgBnxp3Osz", + "nvXGi96MZwUWABYm+AQbB3VVyf6/PTvR3nuogxE15EZ2O0AiAaETjoydmckALKwLSxN7a0p5MS36zIl9", + "3ZRQgOKIsY2sVrlgDnqfQDUwAJ0oqI+EZPLvUZRQsDrJGc+TGM0AXk3pZkIIaZ1BkcGCx1FMJvV/VyMM", + "dp7sgwbr/r270zqB1CzdKirLk4BOmzrW14JjGzYJAzDiwcgZwtfEBOkb0BZ/ATuF4vDfc+QaKk9cwUiM", + "d8qktEkT66AS6SWzbQazjuwxsMgWI2xPU5v7aOkUVpKYWrRgggXgs6QFpsYLkzz59tdzpIhIXTr7RqR3", + "B04JMVHRVMpc80+K0eHR6YvNfieIB1RxVcFWrUxyqg46AH1ggweaYiJKGw1OSRedHEPyqr1WSl0Msg1+", + "4gIl5lYsL6MD9M5Cu1eaQibY2RBGsigjEIzYMuxsuhaz+vV2gN4UKiAuhlKkIZa05ZosLxNo1sajmVSI", + "pdZrWavgH7Pqn72PIfEBSmoZXlxWwQrdX+1tjg4hSV9UNQvZjS8kPyik0f7l7f2XBtH98jL67s1kdOsD", + "HmUzLEPUPfO9mvDS0r77buQquxfNITyVfkeSBs+WiYecVa4hRfQ9V/m8X1+O/d7gSW97++323s3NdzfF", + "P60iNnlobgUEanvs0rvAAF02ZV1TNWqM9Ub6sY3sdnaR96dohiX7TsHDmnVke/dpG6ME9No2StqPj+YT", + "M6SCSzn4pyK61wBhXdIkMQKMpFOGE/QcbZyfvPzl5NdfN1EPvX59Wt+KVV8EC1eth0J1rOLl2TvwjGM5", + "coGGzbl1Xh0hck2lksvoWK3idT8HetV82q68sJukaaOsMrwav/XnCsBoEO5s8w6AV12w9dJyPgAm6UMm", + "tX19eKgrEUw/F4bUKuJ3hELayPNDCJ61DP4nTez/9niidzKcCgBKiG35IoDLOL41gGe3QwPZlodSc2YS", + "o5OzsvBNaa13zdfm9Hynv73/DOrebQ/aVbOPVvR9enjUvvPBjpE4DvD4IIoPQKO9rVPHEraR0bEppz10", + "WtSwY7RAT/3zjq3VtFoFoCzjpN4OFrUu5zQAn4K85yJ75ChdWXqhRTpdHeTLFOyeE5TSJKGSRJzFsipE", + "zrBEMjOIlqYEQSHiDhkMsIuKKprIVKWOIpGXtjkrflqBOM8s3UN1wIwzLSRrZQf9QhYSpRS8hH6lU0Uk", + "KrI24iHbEC7Dp0jlgfqBsf4B4uW7Li5bD40qKLWgPxgyOcuVZmKbfXTEmcxTIqzZEo0puFQ2kcyNzgfj", + "hdVYaIYpaUzEkOnXApiZfxTy+8H+YDAYdDuFqrOr/z0IUdOdegf7FhPW5KgCOh2z6LAAeydyZqus2xK0", + "qKjf3v8cz+JngsG6z9uJV/bzUq4KH8x12LHtQGE/F4kThtqgwEL45C201yeN6tpaTtoqR8bJrzY7xn41", + "uomLn6CI50msVaKxvu2MxYrE1k4niTLc2bxLJXpnShFWp25jcxVHv+dELND709NKXIAgE80D2k0cuETD", + "PvDsRtuws8aIsHY0t8RnvQ9M1rqk4kmIXxyB1XfBuaRdQ6EtXHGlIhnMrKDMbI2mkxVzqpm3YzIf5XlI", + "EdGPHFTHu3cnx9US7Xh/+9ng2fPes/H2fm8vHmz38Pbufm/nCR5MdqOnu9s7uyuyMFpkVt0+WSqojQaC", + "aIuQ6ZEL3Q5F1DYFztfufRsMfEVZzK8qV0swOtPv3UZ+rut+Oa679RCC2SAJlsqYBhq4xClcoCTSbZto", + "aJu950yQDVa2/beD7TVWtrX8AgbXwH/fipwZn57JNy/s16k3YH+zquO8HTuFAbmsi3Wr5XfeftEGB0+e", + "Hzz53EVzmQPrxlgnp3vc3KZwJIfHW0tNcOlxno3G4Vd2rLxhTMo2k6HT7RTJFvA3XLS1QN7icasMoqYD", + "2w2zkVX8uyGR9qSiC0CYhMFjiw+0FFDkYI5zhYp0bS1eHCU8j5Fn5zLwVOAEOvH0At0MMiXUwfxl4CZN", + "JoDWHwDfGND1KdOMGJxfuhGbdXuATF15eIRTozLZQZgaD77fB8cLE6yhz5fr2igwq4d8bnUX+EYrMkj/", + "C6atl8GaQ1c3YSSfA/SKwzeFJsV43a5qXgcVZvn1ug12w6bDOhQF6MyKcQfop0J0K4Q/K+xtSGL/HFmG", + "VYKXbFZSyO2OdzS1lDvnpUN3O2ZFO92OWyhIm15OoH5XUv3S+fNJMRTFRHACZ7lMUM0VdVXTYSZUKhpJ", + "m7mgN7dJvrDlZ0g8MopJU0CkyXq0ykvxkRNf3p+iDUDl+yuyRmP9r80ieLJy1+0833u+/3Tn+X4r7J1y", + "gOvFziPIyV0e3FoZNMrykbU7NE396OydsStERmMvAi/en/pQB5ngmvXombsG/c6f95/7kEMxz8eJ59Wy", + "+GQG4RQ2LIiqVfCihiC832kyp5MJ+/1jdLnzd0HT7et9uTPeboBSNR2FTVonvmd7yf5Lxj1TSiaMCgME", + "JWQjcNIbImEG6JwoBPTTQzgC1aFIpbUk5+CV7IoHCWtvd3f32dMnO63oyo7OOzgjMHAFLmU7Au+IwZto", + "4835OdryCM606fAFAO2aWbUyfM6QLac6qAqk/e3BbohKGi7ukmps2/O0ccnfW9XMTsouOmQEF2rb0ikP", + "rvbu7uDp3pNnT9odY2t6HYnr1RzG5cuY5bGg7P7Ob4A0+fbwDEE26gRHVbuJC0+60ajUjUYFBQUMEPgN", + "Bvbs6f6Tvd2d7XYIYKGIB4ttVzmwVd4VOHQBogjsRmAplllvt+m2CIlThsDekCjBND2MXHx/7fYxgN8j", + "YV4rN6HNxWCt/UsXV4tvWxmOCnOQyQ4xogEXKGdFmYn+enfnF/FaNnNtcz2s5+qhnBCmV89C1ZgyU7dY", + "ykyQOeW5/AINcWUSNicJ5+JG3zYpLG+IzBNlXItUoven3wFP0bSGpCJZVYey1LgC0OeWk7vRea6QSJjI", + "mxar1W602fpVE+42nNruKjSHCjdohNGKNefK2frIwyOcRDkUVsHFfupZAQIO5KNnWbIwgeVJwjlD0Qwz", + "cEAIW8aJTRFGM57E/WAYpn4ymgQjFvgVSrgBAL4kJLM1R8wg9GdahKFzgja8fHhkSKlWG/JJapiMrSpR", + "pcYnabjIHZahTKkiD1uvJ1bcw6Y1n1RMjgmfSlAKFYTM9+uQ6BkWJhIeM1NDZ54aXTIQ7RsYYo2Zh25U", + "c5PyiVVwrcgBWc5mJXEkuJSIJHQK9Vren9aSZ1ckXBUptOujCauDbUG6xlkYuMrgipOtS22F7sdAMsnn", + "3JBAw5CwtiJOzxknU8xyqELiETK5zqgw5NEuFm/GpRoVoEg3HKxUIygtkAtSIqcVKd+FPci9E7wXHWu7", + "zXLZoNdbfb1EVeGmmgbYzFODKxperW5BgyEyXoaFWolEVUJb1XGMbgJcVmLRUwmtUg8zC21AwoXHljw8", + "9c02AShhlVX3s6St2gqKv+4Nzttiiq2GEDvDanbCJjwAPHEDr6SzRNtQyYyIlEJxDRQTRknsdMnCPWlN", + "XZCtnEiC4pzYlTPyqcB2wbE53gAewZyNjLJpjdfXO2xjHjZjWF15APq1L7YJJZLhbM63Ioe1MrGAEuEy", + "r7NVgCWVo7A7a7lhQaZ5ggWq4+atGLJcpAlll21al4t0zBMaIf1B3ec84UnCr0b6kfwB5rLZanb6g1FT", + "mZpzMziblGY2pNZvOYUf9Cw3aymxYInZMt9vAVxJm8isYJjyTzQhFlruHaPXHqFXobn3dgZNqdoNjVaS", + "tJdhCW/KuS3JBk98LgOJbSulHFdgh8QWL92IPVkuTa2PFreSA+R0LsDbeXSqWQufh4txZPh1DRUDjQkk", + "nbipLXONFmyxzVSCZQZyOUN/5+OqQbRtSG2geNUGK/EZBJkEg8thR1capM0bS2vi7e5NABCAreqJwkc3", + "xBVYV+arjF1q4idvlipezYhdMurmaKpftajm4GIritx522v7JPp6bbJALDBApEi1gCqfUFpl4RXck2jM", + "hQA0Yi3hcOZmA5gfWubRa+1Al9DbGVkgQVJM2ZBRVhhJAcmLIEbmRHgpmlxoJWtK4j76m6fiAX5zmqmF", + "BQYH4/l3EvErVoxxyPxB6sZzqds5ZMayKHIoqF6+pJsFrU8TCqTMghNMCeowYSeCyJk/91D9RC3jXXER", + "NxamWSD3CtQ7AR8rUvySMJ+VFc0EVUPT0Mh8tRwhZ4qfwlOrf6JKPVJUrze6ur9cEhEWEospFa+0Cl3x", + "joqnnBgEEoDjgDpz9i/D4gsIjhaAG2Xz/+2aLH86Kxqv/lZ7zQPVcBi3h8ZsGzTBRiZFphbsU/WkrQ1V", + "gRysVVAqy74EtOHCk12xjqok4BXNaHVPtksDqwfiu9FsSRJVe9979uTpfsuqJZ/lrDPQUV/aNTdPV7jk", + "GnbqtI3f59mTZ8+f7+49eb5zIw+Ly9lo2J+mvA1/f9AGuVb6sCb/+sc/35/WvD5PIL55cKNBmayN8JAa", + "MjeqA3p/+q9//NON6tYDCjGaZXjqBr99Y5RO4u+kCxSouvDaOclW6PeHFSMBLtgM2iCTCQEz6MisW68c", + "TA2Dop0UjDMcUbUIMHJ8ZSLJi1dqMMtt3EHVwYZEXtO2heTUnEvm4zLjccN1jv5ifMM1WnjWuviRzMdN", + "fujX9V6NF7r0WvgxDi1CDMoy/csG7mI+V1hWgqX13xHkNLjsreVMFvPGasjXepoBRLHYGl9eKGAIKrwm", + "T9qP/O2vbafnt6yYdeor/mHFOWw+gjey+gZu5IDRN1qftlrjD/YCvN1Xo7Fflmxl3bdKDbPy1r15vy0y", + "c5dB+osb7Ob9ecmIN/mwDkgL9GjHYJe8bLtbIYkGavLyTAIGNJ6QXhGoZ5NQkMyNR1CfeYtxHsiOjC75", + "ZFIFWn3SDMwNmDOQSOV6wUppzaSLyLWzWdRRnQ3AzLDzRA47WgUYdrbTYafmtgqmJqb4emQ7qAKLDFYh", + "ZRcFOeuDlG4G44RHl6biFhRy7qMBSglmEuUMDn/Nq7Y9WO0d6nYyb28KXGpiQpyW2BaMaUxmeE6hHIL1", + "qUwrgZjkmioJAaPQzgGKuYEaqpQbtTPUr5nEwYNy0nDpYLawDesG9XucuYjW8l0w8E2gyCn7SATv2kx5", + "zbFfvz7tmgAGCD00A6vEN7qJmhFoBll0UcP2L38Pxw+PEzKCcdex4tPldfTzvcGzKogkSlrw6JIcakSA", + "Ip4zVQeRT9spctWUreUrKWcQ7GfDPwAUzPZua9XHJIITKZfPYpXQb0HctbwBu9KhxIHdEAnDoQBfUthX", + "/MY6hOsDMMYGr1KwaceP6zZewpFU3JaWKk71iFxHhMR1tMnwK21j5e2XwVj5X7EFqCmK+Nq3Id55eXb9", + "u0uegrE2rbYf08846wE0httSC2NhcOksUEqV0Cq41x5cxCiE7Rl6oU02M7levdavyLUCcO44TwziWph0", + "Lauyl9G6Fb911mDTgeZifc37OyhhZuLNb1XEzIaqP0QdM/vWndQuW9qdc6Lcu+eWjJor2FeqjFRcWi7g", + "371SjbExpNRF9oJH2+lmjQT3ZmGriEWEbZn/yHBKRpkgE3q9gnjMC0YxrkKGlAepyGAw4JYbKb5Ge09R", + "NMNC1sbO6HSmkkU1AGcvAOTzWQX+BFGEOUNhm50vd9N9uBztZrfTbz0kHJ97sDtL9TSsSDpaBdp8VHrb", + "rHU+wwuw4jQ6CZ/u7g0GuzuDW6E2u2HdYLmOyk9sObxqO00pdd531tFfiVL1WygSmJdrrF4JCnnQxTJJ", + "JQhODyDxJsMRQQmZAEJbUdt6vWex3vXqwVuByoK9FPTvNsrum/PBV+u1FF1ZwGs3jY5zLlbxffznaxyi", + "DWwmWsJzC+Tc7fYG+2+3dw+e7B9sb98F0nKxSE3ZHk8/bl89TXbwZC95tnj6+/bs6XQn3Q3qYZfUlKVp", + "Q6u/6Hcbo2zKS7KKE1RhaWjDziEjol48t150WpKEMtKTRYbU+jTFFbzA+N/Xnv+b2fnNDFbKDufVSfoi", + "BFbl4lQo62GwrexkVvou6rM5OV49i1tlINUHEqa3+lCAvNoNBsojbHc+E/UgZy2voXfei60vopVZceuu", + "opCHHU56cJcbVjxE3hU+WTlwqy7w5UsuYDudckHVLF19WxSvFRjWEDf9Uaq4iqXURydTBpWz/Z+LMDlf", + "idIfd7qd5ONe9czY39ujaln424IA7Vb7UkGLMDIozL56FeCVUvEQJpJd6+p6zD9s97afQxxC8nHvh0Hv", + "eTXioGtWy1++bfd25ddBmzX068+5ukXbz28Uce3WcxUF/UJD1dPKe9kC41oaL+sUu6vDJdxWNrh8vLTH", + "NZScRgH0cyU9e7mNfKEpJglehIDRPUOtrGmPPpGhMZlSJtvYbXcHheH2STrs9NGhRacGXbasSl9pHuqR", + "e3RC05TEVMuYRvVvzmDYaWmLq+sSNyuM4b4KSGv9sLj2fD1EwrqEq3XXZP8z8nE/S/ttp/GuQu8Au5pT", + "UQGfC17sIjpBmNWqY1I2xwmNbSI9JEZCvNqBA0ErSdbyAFnKgc5O0kVTrlCZQt/S3pazZrtgMX5yDfbW", + "FZgZhiB2vgggSgHORVexr5NjlAke51GZP5rAoEvED5HX4M9WCPnrQ3Lv0r4BidkTLtB6+0aTQaOdfbJp", + "v2u2SU2wzVu9PVi/1XdiFOl28ixez8PMS+042I1gw9ekIAZMNNVlr0mC3mQ+tODob/wVXNZ5jS050iJR", + "njkHi6apZUoKuFvAxRCK6z0mCdHX1HIjiCdxmSVBZclF17PU7f1nsyYXJ3iklgfyCyGZ1lUA/wj6SzFb", + "BAfmal4Wd8nGwNWzlcbh1TO1cuxqVQf3dK0k1rhVvgm3Cb/fcPmazdvgpVx65u8CP9sXzZYRUBzDrwhp", + "b5rx5+2XLuyt0X58F2a5hxTSXlvXQw0S1QH2Fsjjrv8yFliLdVXi3Qu550Nk8dZqxk0wr/UsUN/qfNj7", + "X2NlRqP+wdYPf/1/ex/+ErQ21/RmSUQvJhMINLoki56pfKN19H4V5BRg97UwPbWkQnAKNiQAELeH0R/v", + "k0HBNBavcLo0BYjQ8srWbK+d0F//ozm+yVvGd8An15LsZ1eluIvqnYq762gjJWLqYsldItlmf8ggN+2S", + "LCTyimFZkcYR6ney+MSLQEcXRgzsEza/QGMK1QXlkGmtFkcRybQ2YeurUFMimwP3EQQnfju2KJdL/LYO", + "SRNPQND70yWE3Nfv3v74+t2r49HrsxevDk9Gv7z4HwjxuOqZHuKepr29J/u2MLa/ktuBLf4MDP/PAqkN", + "kZuBugzQFyRZQrXxgMBMJUAkuHAD72W0AckKrtKmy9XcvBn05mHRYDDY7QsXVBk8/xJFD9+trHI450lP", + "S9QNwPdBA6ZZi2CwNjRlguA7TXbu6TggNlpr4pROccC0HTJhf5HihG5Aa5N0lva/sUpTOHT+uF4VwHAD", + "s1Q1FPuaXipVrzmyPtWC1KiswF6N18iZTZekXjhXNTcyZWrLFhENQTTEHLCpVyXIlqfMAfL14KP1eZ8r", + "RXlvZt5Imvfm1EmsNZl6xQKd6aW5mhFBvI2AD0q09RsumU0FaQH8YYqwZUSUYZIuj0QLQuDhlGijMDa4", + "JSgSXJctsKvR9E/xddEDWO+xXHJ5wTzKcjfbL38E5PM3rsQfnbgmYBg1fSKM812lolVr4qhqeTN8qlqe", + "t3k/ePAsr1rB/ZrOVo04yz4qpBmix79hqn7iAjSQZpiNO4cLB+0mJgJgx+pg4K2QtGlK4hHP1erzbyuI", + "W4yNogxkWUbUaVsYiDiqZJA28QIHBFGOYXml9XKQKBdULbR6boXhMWTeudqdsJDQEfxcdgz1Ej99Ajvl", + "JJCj8JIwImgE1Sj1eUwxAyEdvT/1ipKZ+nRLEKEgXr4+OrEarkOZBY2FKiA9F+p3eHbS6XbmRBgtrzPo", + "7/YHcJgzwnBGOwed3f52f9ABQX4GU9yCCuI2ZdemuBa60klsJaEf3Uv6S4FTouCL3wLJ5xDqZl8HqRdP", + "Pb0lw1RYxSVLICneEAzVXwN6vLtQD8yt3DXL3tpMB5mtkHBBstd2cz+AUAlnB6a5MxhYLG1lr19IFzEx", + "6lt/twGLZb+tpDq7RAEw9SXNwsmWxdJ/6nb2Bts3GtOqocDZDXX8jmGbN0pAIXxyw4W4VacnzGSC2bxe", + "G4HjnzggJP+s/fZB75nM0xSLhVswf7UyLpsEYyIRdu8aiF8lUaRZBZR86aPXjNiy+VghbIJlRc6glKz7", + "UFNo9RSYtt0mF7g4P/J48cWWsNKHU4s/VdmZPi6fluj5y9FOQcbLG2kfOVBnQ7X3QEA/4qIO8oOdlL3B", + "87vv9IizSUIjhXoFAdsQWCohyiQBiGoHd8MF+j3nCqMigvwRHWkrs44LcuuWV9HWHzT+ZI53QkKW1zMi", + "UsxMPL55Z82hXzrOxgpeHueVt5ojfKgmATeVw30xFxUIctUj6l9bdWFw+TraCyT92z7N9OIHJPy9ezjh", + "drJFKcyHPHJQXxHlkjym42S9OuNSCAnKci+J+lpofnCfV5bFrf8TnqLHQsAvSSHhlbu1dClsZSJnRgEO", + "SoBvyhw5+913VeHvbfnEC8wAU7puGiooKOPKw/Gij9yaGqVfLQDVRxCYZ7x8rZzp4X0tJ2znPk4YzLhw", + "Tny7pr5dU6tOuaEWNwU4mN4pb2GDuJEF4s9nf7ix9eGb7aG97aGV5YGRK2td+Dsf95ENgoQ69HLG8yRG", + "Y4IMxI4Ld1BY9KcfERbRjM4J4KhBXbA8UTTDAoIZUhRjhY3bttEwsdIsUTS3pZvrudC3coHr0AmSjAD6", + "bdQEeVgGvVHGSIz0JxYtrkSwW6oObc5+0MBeNFhejehqxiUpIOSY8m5zyKiVRjuGZvtD9tZii+oFhPhd", + "x2skSQAhdYX9hzOEh8x+8L1jIS72SOK05FxYAEwdNWCIZluWs6n0SEcy4iF4l7eEYaZ6MiMRndDITuuS", + "LGwIYbDBVqV+9IDdON+fFjkCaGczDBEGiIBhPNjj4hmylFT13zCIu42SPC6dXA61BosxTpJgLYhpwsc4", + "GZn1uSQBn+BLeMMuil9F3nmTGI+JqQieLdSMM/N3Ps6Zys3fY8GvJBHDzmZ/yCD23641ibulgIiuoHZY", + "mnF9zgRPTZ9bZohbf1ySxaf+kB3GKWWOIuATnEiOyDV8ByWVAKbBcK8GejCnKewHP8ql4qkPtunozgyT", + "5yrLlU1ikER1Q0CTQ6Y4+sPBCX7a+qPs8RM4iwmONZ14r5gpgWzdNGo5wnr2I3g14G4nsADDjr5Ihx39", + "91RgpgxSZIGHiKb+lm4UgPz6kG7WVzjCDGU8M8UMgKhmWJNcpQ2AB8BJghQcJfetFtxhJxvmY9He0nEj", + "1JvB5qodI8rQ6Y/eYRrsPQufJ0kiQUIRJf91/voVgltZ74F5rYwQMlkETAsMKM7Bdep42gsczZBxVEH9", + "umGHxsNO4c6NN2GsubQJ870e+BR/0EP7wXTTpfEP/b5uyrgrD9Bvf5hWDvRZylIDPTnsfOoi78GUqlk+", + "Lp59CC9oE2LWeYURoA1zzW0CJ8EUwE28G99ckZjFiNtbIFkgjEoO5AeujCnDYrEqdy2w9HYF+cQEz3mL", + "8ccQguWGnYOhC5cbdrrDDmFz+M3G1A07n8IrYL2WzcXS4D4rnJsFEe0PBpvrwZft+gZ8li0cA19YB2zU", + "iopKj3oHLfLnn8s/8G+tfxauH8x05yUajlH8nfH9ETogPInd10QDLoia2I1ZRBIndq839Ny/80BvVkSS", + "5L4J9KHIs3CPFeDwj4ocYbPKY7TSfP/AFDe4r0ulYrZ/GPp9dPbzgPXc2s7J3IU6h0tjAOyJVaWReRlh", + "ic5hTL1zrXy/gF/79r9O9wMYv4uETy8OjOqOEj5FCWU2BN0LVNbigV1L+MggnxTfWSAUV5dsw0gS//rH", + "P2FQlE3/9Y9/Wjjxf/3jn3DctwyiF5Q1vpgRLNSYYHVxgH4hJOvhhM6JmwwUHiVzIhZod2Bt/vAIedXV", + "rZQmh2zI3hCVC+aF6psSYdI2aF0Fej6U5URa5Bj9Ip3Y+iUmtjFgt3Fn2SzlvZ7obgCBD2bgTUDfio4G", + "AL6MmtrOVhPthE2mZs4Vo2k9THMpWG89f1HkWhnq7ZkB3pDBwBKHzh08sJNGG+fnLzb7CLQtQxVQowZ0", + "h7IZq0b0v/Gk9TzJcJQqQ4FVNrwpwhke04Q6k2NDgQ1zBFMczSgjZXxxAW/tmjhwI9U85vDsBNlAyC68", + "OmSvz7fAxKpIpHJBupYTCAtqWVbg4jb9EXoA/kUVRIf17LtDNiEYsBZOjg0T8HCfixS0omEG2BEQ42oK", + "1hdddYfMgJdasFx98FIekwQ+gv6nWJErvOiioryqK8iRYKUVYtnVLw+ZgRe1a9ADdAzkDbMP/MwMqeci", + "eW2akCCTRKvGEIFvKk1D3xsTLpCNcPYKy7vuTF6fGZZetBRHr8/1/KagCXJjD4SWXp+73djsIslRlFCg", + "hgizIZtCIJDDi+WssqtFDtMMi7gXcX0J+PhBl4xfJSSeNvHYI5/I7lCSqfQTOE4/18n1sQkXs+UJ6ENs", + "MNFWe+6O7TvtXHe2xT+T787WHryB885YcInhN2Z1vznyWjjywuvmnHohz9qxA/27u4hf08UDBfw62lte", + "c/PEW7KHsOihDYemAl4RLtDZ0QnCcSyIlJv/3vY+PVNDpaX8p+9HzYofIvTEjoULizNn7S1VAnks7OCN", + "HTXCbl71kq7+/bZVqffSeNMVpV/KK+/ub49apze5Rkqht6S1bzfJ2mBbKiMOle1KaumBaJSQQnwpzqlP", + "ReusyiaMt7hyVopLlj2fHLsDeX/2Zdt1zup3wz0wxeMaQ3xARlhNtfYLNT8man5X7KIDOF5hfv66SHNw", + "f1LQfZuiQ2T+mNTFuLZsmgsaUJDGC/QlUQYK5C71dNtDYOLnRLhT7erqw6yLaZlPkcE0gQmBJWa17nti", + "Xmmn+pr2/kyaLyzPTSQWu+TfRJQWym65VqsU3BNbdfju9Fvo4Ubq7ZcLW7EEFlhksKKOndsJLKsbWC5Y", + "tPktcuWLU7SJayyVWOHmTeLCkm3QlAo9677kukPml7jWMp3VaylDk4ROZ9YJENMJxOopv2Q0jHLnHkZZ", + "lGYWWBEbovgY837P9CJbL/CcCIVeH52Y9fev1K0/IGh1varkmNfK2/Xdm197hEU8LpwnzTKpffKFFSZD", + "/5Vc3vs/dY8wn5U68aBJYPyM/TfB5MjEv/cp/8+dnxI6Flgs/nPnJ5xklJH/3D1MsCJSbd4ZsQzu66a7", + "bwXmEROf1l9oddGANbEpoJSuEfiLt1rK/O79P5XYbyZ9I8G/WNdvsn8b2d9frpXiv92KO1UATB8P5OEq", + "iC202vDoG6TNPRhNLUV6kDYVL1IJajPjUsGjx5ffbIPKaUFx/rXR0vpfHsiV14cj3ZPjLiwkFDGGIgo2", + "ffCefAFuHPcu3Np+798RcJiO6TTnufQzE1OsohmRNms3IVUG/NjE7vJ6bhS8v2IqHdzn1XHvcvU3ur8j", + "ib++oYZ5G4feOpnfvdVW5rfva5nfIJrazGZb6aHrqgBtNgRaO0zTtmRcgX5dDgAPjSuki6B3WlEp1QUE", + "GsTBkP0frX/8pghOP/zgUijzwWBnH34nbP7hB5dFyU4dqRCmBLVF2w5fHYMXdQqBslDXrUzYro/DlIkG", + "0nNI9v92ClLpSG6vITkq/KYhtdKQvOVarSHZvbhbFalaDePedSRHb6EFt5jif04t6U/uHqlocDKfTGhE", + "CYOaIpCYLpfiAY0m980zcsuEZGb9kV4wUUUSaa1GFlxrjYReljG+90Cyk7Ju031rj65i8uNMrOKZLUFq", + "9bVSWmhW2L42ehjc7+11/4raYyYxoxEtL12mhe5AZSBTEyvNTRZaiSUGUcZIGLWmaLGPjgoICZtBJk1d", + "LdAQTOXdmdYQQjW4qmW1QnW0oHYUJbI7ZFBZWT82UDhbl2RhqmZRzorksmKmtvhUKJmsWrXsQY/RlxdC", + "wyXZWgmh93yMbZHNhxNCH4x13Iu4d1KpXbxRHAzQuMekOMm8yAinHymbbj6qiGfDrIq5eciHAVFrC+eK", + "92y+69aMGxC0MA7kWYIjgIHUrxmEMgsxYCAJ/aYAN0DwJCHCIM9luXIl+oasGBxlXglyWw/nQjc/ypmi", + "yUXXRNMAfIhEmC0s/NKQVTrDSpE0U5AGDCnuMEJBMjPiWm1CPWjKcwlvQcau3yXCyRVeyCGzicPmc6jj", + "KkhkQBqTpI9+5oDZgPAUU2ZhBPSbprLfd3LILmickJGFXLhAVCI540IRRmKU8jmR1X4JFgklAiZxhPXK", + "SZTiBWCfGRhIsz48IwZfrALswPW/MYsp1L3TPRdTPhgyjHYGA5QSzKRN05Z4QvRXtg0Eg6gM6HuE0d7g", + "uf2qtm+Az+uWf0OfFyHInEd4nCwQ0VQMN6LahA0szheUkNXbN6FCmv0qzIu2wFhlY6l0pXDjLspZmYgO", + "pvacFXnjertULhjM0zrhCBVFhUOLvTEmEdbryXi1H0A95FGUi9DlqLfaq8H57yhketM7h6UKp3knoLFr", + "2UTvOeNqBmeaw1Ha/L6Bqkqi+nNcMsFDwgXCyKPr0qAA5erZFG0ASuBFWd2PuQKxF5vfu7Ojj69lBO74", + "G5y+x3I/ARHxyaRyANdfTeYAr0qvWCbhP+s5PXJlXX0WF1M8ZVwqGjlmWC88/k15bK08rl7ZIDVPuLj0", + "Zasq/f7ExWVb7ctij9LHpYT5M/wK/QB6eIDz/PDuADBGG0VFE829K2h1+ipOKQhdVEkXZ8xRwtlUn6LS", + "KH7vVvuaRhclOYjlTpVzRhCthIzsj6YyrJ6MrbsJFv7ItvrQvEj3fg++oFdcIZpmCUkJVI7tGWLTm12i", + "MUFhdyo9TKKb8Up9qvzUYaMLSuP+7zpxCOjKbdgGSO/L2xVkqgmfrsf8Kzp3AHcB0L8hM4XniatSf4EK", + "HqwFWoOwj65mNJoBACDorbp9gw+Is+yiwD7ePEAv4SD7ENDQ+YbB1de0JnlCDK7fPE0vDpZro74/PYWP", + "DPafqYJ6cYBcPdTi/pD6LR/QT88iwVKhVxamcKNQxmFHLxTW+mYxv00L9VdiUw9ZCPaPkSvbIJ2gCw8B", + "8KIBnsrx21/59MGEsW5zRQEzF8WRVR2BNgmLO00xFjQJg/9tDwYhoOuWQIRmGHeMQ7g0mF/5tKhmUCFl", + "nGVtydcOE6h4nqYraBhteKhmUsU8V3+VKiZCwMeWupuIG23gyFaywpeaUC2GnTvYm0B+wUgiAy8eXCrN", + "VDvdDmF52jn4zf5rnqadbseOx4Mlv4FwvwbQsd7gcsSL3hkPtfGbWH4TPMYqs/cAGWs3h1WnmyXyN+aF", + "P71n0dnsHpAMQT6oGXG/JhHUG2/V4MN4ASz5uNCjrKmpJrU1m2rcLHt6eHHuyu20CeE4t5+euy+/Au13", + "XWSHGzNy0733EI/lETzmTFi5NJsJF3XIoXWxH189IX25LVmaahsK+UabN7fztSJMLakvswj7QWzKr+Fc", + "8RQrGkHpn2jGufTIvsAHNkW6rPm2oEwwbhg904bQX2hSvbCG4AsryB9YoxXC/iPbRx8+t4H34S/co/KL", + "nzy9vOD4XSd8Azw+1CYXlExQhnNJtFyVpwRFi0hzRVPrieBohiKcqVwQKGNHUEoZTfPUB37WOzbHAFJx", + "sZ1edNE4VyjBYgp6kXlowukFiXiaEhYTsJAN2YzgOdVKnUAJVoRFi54kUP52TtAVF5cJxzEo+TYKx5TP", + "E0RTIKBop0ThGCsMosaFPvEjk8VzUVTENYo1I9clNcRDJnL2vYH0181euIFeIAKY1VTOisqJEY4Ji4JY", + "zudfNxv78tbgc6LqE32guJxb8dKHDNTxrZ5uOF9HDM8jC0bmwm5jGza/QuiVzUpkNf3BkdG/55E2c3Vz", + "fCAXT7HEq07x1+HbKYjuq/HvPLwDhwsU56Y771QCmf9ZvTIFQ/HDnSC10mzjbV0zRYm4YplvxPO2/nB/", + "ntzCmvaVcMJuo2LfVIyonPTXwHLtqt6K5z6QGdHakjyr2AOyYBdT9WDiExcel3ss5k7LsM3RLPi2z52U", + "wKB9cfaNbdfZtg05uC3bdrbZJae6x8gp60GUZpiDWzNuI6u2poN/01yQ2uw8lvngLLL0HdwbWzwpGKFh", + "jRleJBzHf4Yw3RUenIgLYfAfAFHiMeGPelZDP0AfbHNllbOuy618f3q62cQlhFrJI4R6xBzCS4rRn6WB", + "0v+v50QIGluYTnR0emwDZqlEImd99DqlCimOLgnJypwSAPLo6/k5JIzliuoVyItuhzAlFhmnTK0dRfnq", + "3Qzm063qsN8zn7SA1t8c0q0d0mDZf3zsDLgMZE2YCazWTBVWawttUjbhIjVyGR7zXLeueZBeJr2fpmDl", + "hCZELqQiqYkLnOQJHDcojmAL4NrvzC53ISpWnxyTsJYRkVIpKWdyyGy2RkaE7lt/rtv3QpyCDgGFC/56", + "Zpjk1xE+pwdjIsawalo1wCyCwpqdg84WzrKtGCvcEKJlh/cZQ/oJ4uGQXKRjntAIJZRdSrSR0EujnqC5", + "RIn+Y3NlQN0IvvvS5X1vf7L0Sp+wCQ8WTzM0WxDznyqvyrI155h8dGztJfEPi+M/sNFhtra+gLAgOOlB", + "QV6HXINyRRP60bA63QiVikYm6aeELHh/WqIWDNkpUUK/gyG5LElIpJzBZisTPNoa5oPBbpRRgD/bJTA4", + "YHjNj1Po8ejsnUkEJSkXi+6Q6X9Aw28Pz4x3d4KtNcEbqK0cjE62Xq8JMT6HZfo3jtEzE1yJHRDc8G8u", + "wZsjgjSeIdlwRHm2SlXi2Z8+iNRKcN/sCo/TrgCQTMVsNqYCRyAUy1muYn7FwjaEOU/yVP/D/HGyDthL", + "4Wj2Hl79aqRdM5y13bgJPopDaecUE1Pc8UGcHmbBHmvMql44NwUQYirRgMFb4FD9Gan7y5vv/XX8Ct2d", + "dkVd4dSv5mzd981nx+AwLvz1eCzH3FCam4niq61PV5g2W59+THh0KS0Yim821HobAIzrH0tAaOsiBDEB", + "cjORBRFC5DqjApDfagZIg7kjEUaKiJQynGzBnE0jAG3trFh4zimkSEcJhSQ1GgNqUQLodFczwpCeDRiq", + "XAOeR1fa0lL+O74zUnE0JhFPiYP73gypbn/DVP3ERRW7+2vhi2+99dfz0VPV81wDV97c42fBl5/iawiV", + "jnPrUHYj2njJyx+NKaiLYG+Gnd2BHHa6aNjZSYcdvQNHGEyoWKEnKKUsV0T20bGxb0ES7P4ASRJxFkuH", + "Ou4seLsD2ZQSa8iyIb9yH767T7HHUhUs5RvbSYg96PeQ/h6SdtCGf+DsmYy7cOhixHNlzP32XNm3YqLA", + "PLJ5775a74x80+3bcPK/2eNb4VGwy5pdeltvOHuWyxlpNrn9air55GoMaNauuqacob/zsewiRq6MNVxI", + "1V/ie/rrM9PBfSDt665ugrJv5/4NYr8FxH65VmG4RBNgqa9kRx0GM5FcG0RY7NLeLQ2BJgHYDTzCCXp9", + "dDJkkWZFBtxPkJQDd7KA4OYWPvzbOXpx9KaLjqHSI/o5H2/20WuWLFy9beOjGTIjiRnmFWGGxoZqSRy6", + "ns3YgXruMlhcd/BApZPNyQh4VtxeuSDxbmdGcAwSyR+dX7npLIAR/OZXfYD0/tkvi23vrBQ+Om+IEove", + "4UQRsdzsqc2TYgVqhb2kHQicFdwM9KTuUDrss7JPIxsYcIrdnU4Aq+LTt6oHd18h9H68ZCZOxNSbG+eA", + "9ckgyQDHi8cVyyRnqGCOIRboX9dF3YCmLGHLy1YqGNBlU+T3V2RyX8m7Kkjw/66nC2b6aB1NWWWfNBEX", + "9UbWenpdcvDMABJbR1WEMxxRteginCT2jrI3QRGR0ivE37Eg+DLmV6w/ZG+KSic2oRcdnb3rOkctiqm8", + "NC1YX2wfvZ4TIfNxMTgEB814jWHNSTxkiqMIJ1GeaHGDTCYkglxcKGAiG3y5xVA6d3h2yk6C1Va8qPb8", + "0RV5C9ME7F5JFnWK2zJbvSVIlGCaNsN/W0ENAg4h1GCsG+UMUTZJbEhVJLiUyDbVIwmd0nFiA4RkH72d", + "ESRxSoYsSzBjRKBcmqh4PfReJoiUuUnw1g0ATK6hqC4qof0ywZUNTUg4F9JEE2gKf3+KpCLZCjJ7Y1o+", + "hTnfkWxrGrc9PZCRujaGZlOIfQXpDTGUYhZc01GeuADGew1FNwN6aCnxsRz8t4JOp0ToU4ENkzXheOZY", + "u+U0h76SsdxY8PG8eKtdwceiVS8r0cvYWwnNNirRruPOzaL+Ap1f0kb0PvvoZlnEv+iPWvZdzVYND8I+", + "+sxZ/lnq6J97SYJtDVglhT82c5I38spRrSTarofVap1Ze5eZrq3xsx4MNusxo2XhSvpsk8L79RHC4H5R", + "Hu67JNrjpq0K2lVFN21I+V+PZ/9VUODdANk/MMrJLYDsv6q8e0Aafzj8k+BBfag8+orv2VWb/dNj0d9V", + "+rwBpAc4tqb0ecP1bPDqSkXpvX2nnZpkW/wzSfA23vEG8rtb9m9afwuVwVusdS5oTfAkzdTCBbRZX2UZ", + "dCbpR9JvcAQXcat35wq+RUjnlyMPR6eNAZ1/zuLwDxIzaov3UYlOjgNV1x8ZxqB/5ioXy5a+dXpYRDM6", + "J81G9+oJtkuUCdLLeAbOldgsmF0Pd5cpLPrTj8g2bzFX7b+g+iOA5ZMYxVSQSCULU4lTcwTTx3cSCa41", + "AXjOxaI5SsQckZ8ETw/tbNbch/ZMWWNYGWeYLnoxVrg3d9xmhQntM6I7XTylZniIMvTyR7RBrpUwNSbQ", + "RGs+iE6KJTXl9iXQ5KY/4O1Bg2WTfiSj6bjNKFdUC3ltq7GgKJeKp27vT47RBlQfmxKm90KL+hOQZDPB", + "5zQmcWWMnTlPzKpuNyzoTe2uWqgoSsc55cIM7kFkmDYX0vQjzapsoQiJGVOGYXBr63JUz5RJ4tf9Ycpc", + "AI7dIzeKb1eY1fw2nLKjKREqYdpFVJwbiOfNb9fcY77m/GQod6dVbjsXnrPaeN0uP6pl2tJdFH4ocufu", + "12z9/utJ6aHyUWbzWNP5vFBIm8zmXxcJDu7vfrhvc/n7R5wC+pI45dszlUMDusUQwfwKMd0xmZOEZylU", + "JId3O91OLpLOQWemVHawtQWx3zMu1cHe86e7nU8fPv3/AQAA//8g9C566+UBAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/openapi.yaml b/openapi.yaml index fe818d0ce..1fe854105 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -1428,14 +1428,23 @@ components: Capabilities: type: object - required: [server, host, runtime, network, images, features] + required: [server, host, default_runtime, runtimes, network, images, features] properties: server: $ref: "#/components/schemas/CapabilitiesServer" host: $ref: "#/components/schemas/CapabilitiesHost" - runtime: - $ref: "#/components/schemas/CapabilitiesRuntime" + default_runtime: + $ref: "#/components/schemas/CapabilitiesDefaultRuntime" + runtimes: + type: array + items: + $ref: "#/components/schemas/CapabilitiesRuntime" + description: | + Every runtime available on this host, each with its own feature + IDs. Hosts commonly support several runtimes at once (for example + cloud-hypervisor, firecracker, qemu, and qemu-microvm on + linux/amd64). Entries are sorted by name. network: $ref: "#/components/schemas/CapabilitiesNetwork" images: @@ -1445,13 +1454,13 @@ components: items: type: string description: | - Stable feature IDs clients can gate behavior on. Base IDs are always - present: "instances", "images", "builds", "volumes", "ingress", - "exec", "logs". Conditional IDs reflect the effective default - runtime and host platform: "standby", "snapshots", "fork", - "pause", "hotplug-memory", "balloon-control", "vsock", - "gpu-passthrough", "disk-io-limit", "disk-resize", "devices", - "rosetta-emulation". + Stable server-level feature IDs: API surfaces this server exposes + regardless of which runtime backs an instance. Always present: + "instances", "images", "builds", "volumes", "ingress", "exec", + "logs". Host-conditional: "devices" (device passthrough + management, Linux hosts only) and "rosetta-emulation" (Apple + Silicon macOS hosts). Per-runtime features are reported under each + runtimes[] entry. CapabilitiesServer: type: object @@ -1479,54 +1488,49 @@ components: description: Host CPU architecture example: arm64 - CapabilitiesRuntime: + CapabilitiesDefaultRuntime: type: object - required: [default, supported, snapshot, standby, pause, hotplug_memory, balloon_control, vsock, gpu_passthrough, disk_io_limit, disk_resize] + required: [name, available] properties: - default: + name: type: string + description: Runtime used for launches that do not name one + example: cloud-hypervisor + available: + type: boolean description: | - Effective default runtime used for launches that do not name one. - Reported as a capability-bearing runtime identifier; clients should - gate behavior on the boolean features below rather than this value. + Whether the default runtime is available on this host (appears in + runtimes). When false, launches that rely on the default will fail + until the server is reconfigured with an available runtime. + + CapabilitiesRuntime: + type: object + required: [name, features] + properties: + name: + type: string + description: Runtime identifier example: cloud-hypervisor - supported: + features: type: array items: type: string - description: Runtime identifiers that can run on this host platform - example: [cloud-hypervisor, firecracker, qemu] - snapshot: - type: boolean - description: Snapshot/restore is supported by the effective default runtime on this host - standby: - type: boolean - description: Standby (pause + memory snapshot) and restore are supported on this host - pause: - type: boolean - description: Pause/resume is supported by the effective default runtime - hotplug_memory: - type: boolean - description: Live memory hotplug resize is supported by the effective default runtime - balloon_control: - type: boolean - description: Runtime memory balloon control is supported by the effective default runtime - vsock: - type: boolean - description: Vsock guest communication is supported by the effective default runtime - gpu_passthrough: - type: boolean - description: GPU/PCI device passthrough is supported by the effective default runtime - disk_io_limit: - type: boolean - description: Disk I/O rate limiting is supported by the effective default runtime - disk_resize: - type: boolean - description: Live disk resize is supported by the effective default runtime + description: | + Stable feature IDs supported by this runtime on this host: + "snapshots" (snapshot/restore), "standby" (pause + memory + snapshot, with later restore), "fork" (clone an instance by + restoring a snapshot of it), "pause" (pause/resume), + "hotplug-memory" (live memory resize), "balloon-control" (runtime + balloon target changes), "vsock" (guest vsock communication), + "gpu-passthrough" (GPU/PCI device passthrough), "disk-io-limit" + (disk I/O rate limiting), "disk-resize" (live disk resize). + Values are host-truthful: for example vz omits snapshots, standby, + and fork on macOS 13, which lacks Virtualization.framework VM + save/restore. CapabilitiesNetwork: type: object - required: [model, gateway, guest_to_guest] + required: [model, guest_to_guest] properties: model: type: string @@ -1538,7 +1542,8 @@ components: type: string description: | Guest-visible host gateway IP. Guests reach host services (including - host ingress) through this address. + host ingress) through this address. Omitted when no default network + has been resolved on this host yet. example: 10.100.0.1 subnet: type: string @@ -1557,8 +1562,10 @@ components: items: type: string description: | - Image platforms (os/arch) this host can run. Includes emulated - platforms (e.g. linux/amd64 via Rosetta on Apple Silicon macOS). + Image platforms (os/arch) this host can run. On Apple Silicon macOS + this includes linux/amd64, which vz runs via Rosetta; Rosetta + installation itself is verified at instance start, and launches + fail with guidance when it is missing. example: [linux/arm64, linux/amd64] default_platform: type: string @@ -2407,13 +2414,14 @@ paths: summary: Get host capabilities description: | Returns machine-readable host capabilities: server and API version, host - OS/architecture, effective default-runtime features, guest networking model - and host gateway, supported image platforms, and stable feature IDs. + OS/architecture, every runtime available on this host with its per-runtime + feature IDs, the configured default runtime and whether it is available, + guest networking model and host gateway, supported image platforms, and + stable server-level feature IDs. Runtime-derived values reflect the actual host (for example, snapshot and standby support on macOS is gated on the host OS version), so clients can - gate behavior on capabilities without selecting or naming a concrete - hypervisor. + gate behavior on capabilities without hard-coding hypervisor knowledge. operationId: getCapabilities security: - bearerAuth: [] diff --git a/stainless.yaml b/stainless.yaml index 79ecffc08..031a5f51b 100644 --- a/stainless.yaml +++ b/stainless.yaml @@ -70,6 +70,7 @@ resources: capabilities: "#/components/schemas/Capabilities" capabilities_server: "#/components/schemas/CapabilitiesServer" capabilities_host: "#/components/schemas/CapabilitiesHost" + capabilities_default_runtime: "#/components/schemas/CapabilitiesDefaultRuntime" capabilities_runtime: "#/components/schemas/CapabilitiesRuntime" capabilities_network: "#/components/schemas/CapabilitiesNetwork" capabilities_images: "#/components/schemas/CapabilitiesImages" From cbc919c3120113ec05f5d0c5ad667495a1eebc79 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sat, 15 Aug 2026 15:25:51 -0400 Subject: [PATCH 03/11] Make capability reporting launch-truthful and config-aware Address GPT-5.6 review round 1 (three important findings): 1. Runtime availability now reflects launch prerequisites, not just platform registration. The registry accepts a per-runtime LaunchCheck; QEMU (standard and microvm) probes the same system-binary lookup that launches use, since hypeman deliberately starts without it. The endpoint gains a required per-runtime "available" field (supported = listed; available = launchable now), and default_runtime.available uses the same verdict. 2. Capabilities are resolved per registry read instead of being frozen at package init. Cloud Hypervisor resolves from the configured default version (SetDefaultVersion), so pinning v49.0 no longer advertises the v51.1-only disk-resize feature. Covered by a v49/v51 boundary test. 3. Fork support is an explicit Capabilities.SupportsFork field instead of being inferred from SupportsSnapshot, keeping each backend's PrepareFork implementation and its advertised feature aligned. A snapshot-without-fork test pins that snapshots alone never advertise fork; vz gates fork on the same save/restore probe as snapshots. Regenerated lib/oapi via make oapi-generate. --- cmd/api/api/capabilities.go | 19 +- cmd/api/api/capabilities_test.go | 38 +- .../cloudhypervisor/cloudhypervisor.go | 9 +- .../cloudhypervisor/register_linux.go | 15 +- .../cloudhypervisor/register_linux_test.go | 53 ++ lib/hypervisor/features.go | 14 +- lib/hypervisor/features_test.go | 94 ++- lib/hypervisor/firecracker/firecracker.go | 4 +- lib/hypervisor/firecracker/register_linux.go | 9 +- lib/hypervisor/hypervisor.go | 98 ++- lib/hypervisor/qemu/machine_test.go | 8 + lib/hypervisor/qemu/profile.go | 5 +- lib/hypervisor/qemu/register_linux.go | 22 +- lib/hypervisor/qemu/register_linux_test.go | 25 + lib/hypervisor/vz/client.go | 6 +- .../vz/save_restore_support_darwin_test.go | 4 + lib/hypervisor/vz/starter.go | 7 +- lib/instances/fork_test.go | 12 +- lib/oapi/oapi.go | 759 +++++++++--------- openapi.yaml | 36 +- 20 files changed, 777 insertions(+), 460 deletions(-) create mode 100644 lib/hypervisor/cloudhypervisor/register_linux_test.go diff --git a/cmd/api/api/capabilities.go b/cmd/api/api/capabilities.go index 4ad07c8dd..e34ee0b04 100644 --- a/cmd/api/api/capabilities.go +++ b/cmd/api/api/capabilities.go @@ -48,18 +48,27 @@ func (s *ApiService) GetCapabilities(ctx context.Context, _ oapi.GetCapabilities } // The capability registry is platform-gated at registration time, so its - // contents are exactly the runtimes this host can launch — including ones - // added after this handler was written. + // contents are exactly the runtimes this build supports on this host — + // including ones added after this handler was written. Capabilities and + // launch prerequisites are resolved per request, so configuration applied + // after init (e.g. a pinned cloud-hypervisor version) and host state + // (e.g. an installed QEMU binary) are reflected without a restart. registered := hypervisor.RegisteredRuntimes() runtimes := make([]oapi.CapabilitiesRuntime, 0, len(registered)) defaultAvailable := false for _, rt := range registered { + available := rt.Available() if rt.Type == defaultRuntime { - defaultAvailable = true + defaultAvailable = available + } + if !available { + log.WarnContext(ctx, "runtime is registered but missing launch prerequisites", + "runtime", string(rt.Type), "error", rt.LaunchErr) } runtimes = append(runtimes, oapi.CapabilitiesRuntime{ - Name: string(rt.Type), - Features: rt.Capabilities.FeatureIDs(), + Name: string(rt.Type), + Available: available, + Features: rt.Capabilities.FeatureIDs(), }) } if !defaultAvailable { diff --git a/cmd/api/api/capabilities_test.go b/cmd/api/api/capabilities_test.go index d27d7f9d2..695a52a16 100644 --- a/cmd/api/api/capabilities_test.go +++ b/cmd/api/api/capabilities_test.go @@ -77,18 +77,22 @@ func TestGetCapabilities(t *testing.T) { require.Equal(t, runtime.GOOS, caps.Host.Os) require.Equal(t, runtime.GOARCH, caps.Host.Arch) - // Every host-available runtime is reported, with its own feature IDs - // derived from the registered capability set — no handler-owned mapping. + // Every host-supported runtime is reported, with availability from its + // launch-prerequisite check and feature IDs derived from the registered + // capability set — no handler-owned mapping. registered := hypervisor.RegisteredRuntimes() require.Len(t, caps.Runtimes, len(registered)) for i, rt := range registered { require.Equal(t, string(rt.Type), caps.Runtimes[i].Name) + require.Equal(t, rt.Available(), caps.Runtimes[i].Available, + "runtime %s availability must come from the registry's launch check", rt.Type) require.Equal(t, rt.Capabilities.FeatureIDs(), caps.Runtimes[i].Features) require.NotNil(t, caps.Runtimes[i].Features, "features must serialize as [], not null") } // The configured default identity is retained. The test-service manager - // defaults to cloud-hypervisor, which is available on Linux only. + // defaults to cloud-hypervisor, which is available on Linux only (its + // binaries are embedded, so registration implies launchability). require.Equal(t, string(hypervisor.TypeCloudHypervisor), caps.DefaultRuntime.Name) if runtime.GOOS == "linux" { require.True(t, caps.DefaultRuntime.Available) @@ -168,6 +172,34 @@ func TestGetCapabilitiesEachRuntimeIndependent(t *testing.T) { } } +// TestGetCapabilitiesDefaultAvailabilityTracksLaunchCheck pins that the +// default runtime's availability is the same launch-prerequisite verdict as +// its runtimes[] entry — a registered default (e.g. qemu without a system +// binary) must not be reported available just because it is registered. +func TestGetCapabilitiesDefaultAvailabilityTracksLaunchCheck(t *testing.T) { + t.Parallel() + if runtime.GOOS != "linux" { + t.Skipf("qemu registers on Linux only (GOOS=%s)", runtime.GOOS) + } + svc := newTestService(t) + svc.NetworkManager = &stubCapabilitiesNetworkManager{} + svc.InstanceManager = &stubDefaultRuntimeInstanceManager{defaultRuntime: hypervisor.TypeQEMU} + + var qemuAvailable, found bool + for _, rt := range hypervisor.RegisteredRuntimes() { + if rt.Type == hypervisor.TypeQEMU { + qemuAvailable = rt.Available() + found = true + } + } + require.True(t, found, "qemu must be registered on Linux") + + caps := getCapabilities(t, svc) + require.Equal(t, string(hypervisor.TypeQEMU), caps.DefaultRuntime.Name) + require.Equal(t, qemuAvailable, caps.DefaultRuntime.Available, + "a qemu default must report the launch-prerequisite verdict, not registration") +} + // TestGetCapabilitiesDefaultNotAvailable pins the contract when the configured // default runtime cannot run on this host: the default identity is retained, // available=false, and the available runtime list is unaffected. diff --git a/lib/hypervisor/cloudhypervisor/cloudhypervisor.go b/lib/hypervisor/cloudhypervisor/cloudhypervisor.go index 1d3a7f14f..83646e39d 100644 --- a/lib/hypervisor/cloudhypervisor/cloudhypervisor.go +++ b/lib/hypervisor/cloudhypervisor/cloudhypervisor.go @@ -48,8 +48,11 @@ func (c *CloudHypervisor) Capabilities() hypervisor.Capabilities { return capabilities() } +// capabilities resolves the effective capability set from the configured +// default version (see GetDefaultVersion), matching what the capability +// registry reports, rather than the compile-time vmm.DefaultVersion. func capabilities() hypervisor.Capabilities { - return CapabilitiesForVersion(vmm.DefaultVersion) + return CapabilitiesForVersion(GetDefaultVersion()) } // CapabilitiesForVersion returns capabilities for a specific CH version. @@ -57,7 +60,9 @@ func capabilities() hypervisor.Capabilities { // aren't supported on previous versions. func CapabilitiesForVersion(v vmm.CHVersion) hypervisor.Capabilities { caps := hypervisor.Capabilities{ - SupportsSnapshot: true, + SupportsSnapshot: true, + // PrepareFork rewrites snapshot config for fork identity (fork.go). + SupportsFork: true, SupportsHotplugMemory: true, SupportsBalloonControl: true, SupportsPause: true, diff --git a/lib/hypervisor/cloudhypervisor/register_linux.go b/lib/hypervisor/cloudhypervisor/register_linux.go index a9c023ee2..fe03fbac6 100644 --- a/lib/hypervisor/cloudhypervisor/register_linux.go +++ b/lib/hypervisor/cloudhypervisor/register_linux.go @@ -4,8 +4,17 @@ package cloudhypervisor import "github.com/kernel/hypeman/lib/hypervisor" -// Cloud Hypervisor requires KVM, so capability registration — and therefore -// its presence in hypervisor.RegisteredRuntimes — is Linux-only. +// Cloud Hypervisor requires KVM, so registration — and therefore its +// presence in hypervisor.RegisteredRuntimes — is Linux-only. Capabilities +// are resolved per registry read from the configured default version (which +// SetDefaultVersion may change after init, e.g. pinning v49.0 without +// disk-resize) rather than frozen from the compile-time default. No +// LaunchCheck: the binaries for every supported version ship embedded in +// hypeman. func init() { - hypervisor.RegisterCapabilities(hypervisor.TypeCloudHypervisor, capabilities()) + hypervisor.RegisterRuntime(hypervisor.TypeCloudHypervisor, hypervisor.RuntimeRegistration{ + Capabilities: func() hypervisor.Capabilities { + return CapabilitiesForVersion(GetDefaultVersion()) + }, + }) } diff --git a/lib/hypervisor/cloudhypervisor/register_linux_test.go b/lib/hypervisor/cloudhypervisor/register_linux_test.go new file mode 100644 index 000000000..6d2189588 --- /dev/null +++ b/lib/hypervisor/cloudhypervisor/register_linux_test.go @@ -0,0 +1,53 @@ +//go:build linux + +package cloudhypervisor + +import ( + "testing" + + "github.com/kernel/hypeman/lib/hypervisor" + "github.com/kernel/hypeman/lib/vmm" + "github.com/stretchr/testify/require" +) + +// TestRegisteredCapabilitiesTrackConfiguredDefaultVersion pins the v49/v51 +// boundary: the capability registry must resolve cloud-hypervisor +// capabilities from the configured default version at read time, not from a +// value frozen at package init. v49.0 lacks live disk resize; advertising it +// would misreport what ordinary configured launches can do. +// +// Not parallel: it mutates the process-wide configured default version. +func TestRegisteredCapabilitiesTrackConfiguredDefaultVersion(t *testing.T) { + t.Cleanup(func() { require.NoError(t, SetDefaultVersion("")) }) + + require.NoError(t, SetDefaultVersion(string(vmm.V49_0))) + caps, ok := hypervisor.CapabilitiesForType(hypervisor.TypeCloudHypervisor) + require.True(t, ok, "cloud-hypervisor must be registered on Linux") + require.False(t, caps.SupportsDiskResize, "a v49.0 default must not advertise disk-resize") + require.NotContains(t, caps.FeatureIDs(), hypervisor.FeatureDiskResize) + + require.NoError(t, SetDefaultVersion(string(vmm.V51_1))) + caps, ok = hypervisor.CapabilitiesForType(hypervisor.TypeCloudHypervisor) + require.True(t, ok) + require.True(t, caps.SupportsDiskResize, "a v51.1 default must advertise disk-resize") + require.Contains(t, caps.FeatureIDs(), hypervisor.FeatureDiskResize) + + // The registry enumeration used by the capabilities endpoint must agree + // with the per-type lookup. + for _, rt := range hypervisor.RegisteredRuntimes() { + if rt.Type == hypervisor.TypeCloudHypervisor { + require.True(t, rt.Capabilities.SupportsDiskResize) + require.NoError(t, rt.LaunchErr, "cloud-hypervisor binaries are embedded; no launch prerequisites") + } + } +} + +// TestCapabilitiesAdvertiseForkOnEveryVersion pins that fork support is +// explicit and version-independent for cloud-hypervisor, matching the +// PrepareFork implementation in fork.go. +func TestCapabilitiesAdvertiseForkOnEveryVersion(t *testing.T) { + t.Parallel() + for _, v := range vmm.SupportedVersions { + require.True(t, CapabilitiesForVersion(v).SupportsFork, "version %s", v) + } +} diff --git a/lib/hypervisor/features.go b/lib/hypervisor/features.go index a83e873ac..8fdf2a3fb 100644 --- a/lib/hypervisor/features.go +++ b/lib/hypervisor/features.go @@ -11,7 +11,9 @@ const ( FeatureSnapshots = "snapshots" // FeatureStandby: pause + memory snapshot, with later restore. FeatureStandby = "standby" - // FeatureFork: cloning an instance by restoring a snapshot of it. + // FeatureFork: cloning an instance by restoring a snapshot of it. Derived + // from the explicit SupportsFork capability, which each backend keeps + // aligned with its VMStarter.PrepareFork implementation. FeatureFork = "fork" // FeaturePause: pause/resume of a running instance. FeaturePause = "pause" @@ -36,14 +38,6 @@ func (c Capabilities) SupportsStandby() bool { return c.SupportsSnapshot && c.SupportsPause } -// SupportsFork reports whether instance fork is available. Forking a running -// or standby instance restores a snapshot of the source into the new VM, so -// fork tracks snapshot support rather than being assumed from the backend's -// name. -func (c Capabilities) SupportsFork() bool { - return c.SupportsSnapshot -} - // FeatureIDs returns the stable feature IDs implied by this capability set, // in a fixed deterministic order. Only client-visible features are included; // internal lifecycle hints (snapshot base reuse, pager detachability, host @@ -58,7 +52,7 @@ func (c Capabilities) FeatureIDs() []string { if c.SupportsStandby() { ids = append(ids, FeatureStandby) } - if c.SupportsFork() { + if c.SupportsFork { ids = append(ids, FeatureFork) } if c.SupportsPause { diff --git a/lib/hypervisor/features_test.go b/lib/hypervisor/features_test.go index 443632ed6..2eef497ca 100644 --- a/lib/hypervisor/features_test.go +++ b/lib/hypervisor/features_test.go @@ -1,6 +1,7 @@ package hypervisor import ( + "errors" "testing" "github.com/stretchr/testify/require" @@ -9,6 +10,7 @@ import ( func fullCapabilities() Capabilities { return Capabilities{ SupportsSnapshot: true, + SupportsFork: true, SupportsHotplugMemory: true, SupportsBalloonControl: true, SupportsPause: true, @@ -57,16 +59,25 @@ func TestFeatureIDs(t *testing.T) { require.Empty(t, ids) }) - t.Run("snapshot without pause yields snapshots and fork but not standby", func(t *testing.T) { + t.Run("pause without snapshot yields pause only", func(t *testing.T) { t.Parallel() + ids := Capabilities{SupportsPause: true}.FeatureIDs() + require.Equal(t, []string{FeaturePause}, ids) + }) + + t.Run("snapshot support alone must not advertise fork", func(t *testing.T) { + t.Parallel() + // A snapshot-capable backend may still reject PrepareFork with + // ErrNotSupported, so fork is an explicit capability — never inferred + // from snapshots. ids := Capabilities{SupportsSnapshot: true}.FeatureIDs() - require.Equal(t, []string{FeatureSnapshots, FeatureFork}, ids) + require.Equal(t, []string{FeatureSnapshots}, ids) }) - t.Run("pause without snapshot yields pause only", func(t *testing.T) { + t.Run("explicit fork support yields the fork ID", func(t *testing.T) { t.Parallel() - ids := Capabilities{SupportsPause: true}.FeatureIDs() - require.Equal(t, []string{FeaturePause}, ids) + ids := Capabilities{SupportsSnapshot: true, SupportsFork: true}.FeatureIDs() + require.Equal(t, []string{FeatureSnapshots, FeatureFork}, ids) }) } @@ -79,13 +90,9 @@ func TestStandbySemantics(t *testing.T) { require.True(t, Capabilities{SupportsSnapshot: true, SupportsPause: true}.SupportsStandby()) } -// TestForkSemantics pins that fork tracks snapshot support: forking restores -// a snapshot of the source instance into the new VM. -func TestForkSemantics(t *testing.T) { - t.Parallel() - require.False(t, Capabilities{}.SupportsFork()) - require.False(t, Capabilities{SupportsPause: true}.SupportsFork()) - require.True(t, Capabilities{SupportsSnapshot: true}.SupportsFork()) +// staticRegistration wraps a fixed capability set for enumeration tests. +func staticRegistration(caps Capabilities) RuntimeRegistration { + return RuntimeRegistration{Capabilities: func() Capabilities { return caps }} } // TestEnumerateRuntimes exercises registry enumeration semantics against a @@ -95,18 +102,18 @@ func TestEnumerateRuntimes(t *testing.T) { t.Run("empty registry yields an empty non-nil list", func(t *testing.T) { t.Parallel() - runtimes := enumerateRuntimes(map[Type]Capabilities{}) + runtimes := enumerateRuntimes(map[Type]RuntimeRegistration{}) require.NotNil(t, runtimes) require.Empty(t, runtimes) }) t.Run("entries are sorted by type name for deterministic output", func(t *testing.T) { t.Parallel() - byType := map[Type]Capabilities{ - TypeQEMU: {SupportsPause: true}, - TypeCloudHypervisor: {SupportsSnapshot: true}, - TypeQEMUMicroVM: {}, - TypeFirecracker: {SupportsVsock: true}, + byType := map[Type]RuntimeRegistration{ + TypeQEMU: staticRegistration(Capabilities{SupportsPause: true}), + TypeCloudHypervisor: staticRegistration(Capabilities{SupportsSnapshot: true}), + TypeQEMUMicroVM: staticRegistration(Capabilities{}), + TypeFirecracker: staticRegistration(Capabilities{SupportsVsock: true}), } runtimes := enumerateRuntimes(byType) require.Equal(t, []Type{TypeCloudHypervisor, TypeFirecracker, TypeQEMU, TypeQEMUMicroVM}, @@ -116,11 +123,58 @@ func TestEnumerateRuntimes(t *testing.T) { require.True(t, runtimes[2].Capabilities.SupportsPause) }) + t.Run("capabilities are resolved on every enumeration, not frozen", func(t *testing.T) { + t.Parallel() + // Mirrors config applied after init, e.g. a pinned cloud-hypervisor + // default version changing the effective capability set. + effective := Capabilities{SupportsDiskResize: true} + byType := map[Type]RuntimeRegistration{ + TypeCloudHypervisor: {Capabilities: func() Capabilities { return effective }}, + } + require.True(t, enumerateRuntimes(byType)[0].Capabilities.SupportsDiskResize) + effective.SupportsDiskResize = false + require.False(t, enumerateRuntimes(byType)[0].Capabilities.SupportsDiskResize, + "enumeration must reflect the provider's current value") + }) + + t.Run("launch checks gate availability", func(t *testing.T) { + t.Parallel() + launchErr := errors.New("qemu-system-x86_64 not found") + byType := map[Type]RuntimeRegistration{ + TypeCloudHypervisor: staticRegistration(Capabilities{}), + TypeQEMU: { + Capabilities: func() Capabilities { return Capabilities{} }, + LaunchCheck: func() error { return launchErr }, + }, + } + runtimes := enumerateRuntimes(byType) + require.True(t, runtimes[0].Available(), "nil LaunchCheck means registration implies launchability") + require.NoError(t, runtimes[0].LaunchErr) + require.False(t, runtimes[1].Available(), "a failing launch check must mark the runtime unavailable") + require.ErrorIs(t, runtimes[1].LaunchErr, launchErr) + }) + + t.Run("launch checks are re-evaluated on every enumeration", func(t *testing.T) { + t.Parallel() + // Installing the missing binary must flip availability without a + // server restart. + var launchErr error = errors.New("binary missing") + byType := map[Type]RuntimeRegistration{ + TypeQEMU: { + Capabilities: func() Capabilities { return Capabilities{} }, + LaunchCheck: func() error { return launchErr }, + }, + } + require.False(t, enumerateRuntimes(byType)[0].Available()) + launchErr = nil + require.True(t, enumerateRuntimes(byType)[0].Available()) + }) + t.Run("results are value copies of the registry", func(t *testing.T) { t.Parallel() - byType := map[Type]Capabilities{TypeQEMU: {SupportsPause: true}} + byType := map[Type]RuntimeRegistration{TypeQEMU: staticRegistration(Capabilities{SupportsPause: true})} runtimes := enumerateRuntimes(byType) runtimes[0].Capabilities.SupportsPause = false - require.True(t, byType[TypeQEMU].SupportsPause, "mutating enumeration output must not affect the registry") + require.True(t, byType[TypeQEMU].Capabilities().SupportsPause, "mutating enumeration output must not affect the registry") }) } diff --git a/lib/hypervisor/firecracker/firecracker.go b/lib/hypervisor/firecracker/firecracker.go index 1ef3edc06..8f068bc19 100644 --- a/lib/hypervisor/firecracker/firecracker.go +++ b/lib/hypervisor/firecracker/firecracker.go @@ -56,7 +56,9 @@ func (f *Firecracker) Capabilities() hypervisor.Capabilities { func capabilities() hypervisor.Capabilities { return hypervisor.Capabilities{ - SupportsSnapshot: true, + SupportsSnapshot: true, + // PrepareFork persists restore metadata overrides for forks (fork.go). + SupportsFork: true, SupportsHotplugMemory: false, SupportsBalloonControl: true, SupportsPause: true, diff --git a/lib/hypervisor/firecracker/register_linux.go b/lib/hypervisor/firecracker/register_linux.go index 675d35729..c0162437b 100644 --- a/lib/hypervisor/firecracker/register_linux.go +++ b/lib/hypervisor/firecracker/register_linux.go @@ -4,8 +4,11 @@ package firecracker import "github.com/kernel/hypeman/lib/hypervisor" -// Firecracker requires KVM, so capability registration — and therefore its -// presence in hypervisor.RegisteredRuntimes — is Linux-only. +// Firecracker requires KVM, so registration — and therefore its presence in +// hypervisor.RegisteredRuntimes — is Linux-only. No LaunchCheck: the default +// binary ships embedded in hypeman. func init() { - hypervisor.RegisterCapabilities(hypervisor.TypeFirecracker, capabilities()) + hypervisor.RegisterRuntime(hypervisor.TypeFirecracker, hypervisor.RuntimeRegistration{ + Capabilities: capabilities, + }) } diff --git a/lib/hypervisor/hypervisor.go b/lib/hypervisor/hypervisor.go index 69b77dfda..fc8c1562d 100644 --- a/lib/hypervisor/hypervisor.go +++ b/lib/hypervisor/hypervisor.go @@ -49,11 +49,11 @@ var socketNames = make(map[Type]string) // Registered by hypervisor packages when they use socket-based vsock routing. var vsockSocketNames = make(map[Type]string) -// capabilitiesByType maps hypervisor types to their static capabilities. +// runtimeRegistrations maps hypervisor types to their registrations. // Registered by each hypervisor package's init() function. Registration is -// platform-gated (see RegisterCapabilities), so the map's keys double as the -// set of runtimes available on the current host. -var capabilitiesByType = make(map[Type]Capabilities) +// platform-gated (see RegisterRuntime), so the map's keys double as the set +// of runtimes supported by this build on the current host platform. +var runtimeRegistrations = make(map[Type]RuntimeRegistration) // RegisterSocketName registers the socket filename for a hypervisor type. // Called by each hypervisor implementation's init() function. @@ -84,45 +84,86 @@ func VsockSocketNameForType(t Type) string { return "vsock.sock" } -// RegisterCapabilities registers static capabilities for a hypervisor type. +// RuntimeRegistration describes how a hypervisor backend reports itself in +// the capability registry. +type RuntimeRegistration struct { + // Capabilities resolves the runtime's effective capability set. It is + // evaluated on every registry read — never cached at registration time — + // so capabilities that depend on configuration applied after package init + // (for example the configured cloud-hypervisor default version) stay + // truthful. Required. + Capabilities func() Capabilities + + // LaunchCheck verifies host launch prerequisites that platform-gated + // registration cannot express, such as a required system-installed binary + // (QEMU). nil means registration alone implies launchability (backends + // that ship an embedded VMM binary). Like Capabilities, it is evaluated + // on every registry read so prerequisites fixed after startup are + // reflected without a restart. + LaunchCheck func() error +} + +// RegisterRuntime registers a hypervisor backend in the capability registry. // Backends must register only on platforms where they can genuinely launch // VMs: the Linux backends (cloud-hypervisor, firecracker, qemu) register from // linux-only files, and vz registers from its darwin-only package. That keeps // RegisteredRuntimes truthful without a hand-maintained platform switch. -func RegisterCapabilities(t Type, caps Capabilities) { - capabilitiesByType[t] = caps +func RegisterRuntime(t Type, reg RuntimeRegistration) { + if reg.Capabilities == nil { + panic(fmt.Sprintf("hypervisor: RegisterRuntime(%s) requires a Capabilities resolver", t)) + } + runtimeRegistrations[t] = reg } -// CapabilitiesForType returns static capabilities for a hypervisor type. -// It reports ok=false for runtimes that cannot run on the current host -// platform, because such runtimes never register capabilities. +// CapabilitiesForType returns the effective capabilities for a hypervisor +// type, resolved at call time. It reports ok=false for runtimes that cannot +// run on the current host platform, because such runtimes never register. func CapabilitiesForType(t Type) (Capabilities, bool) { - caps, ok := capabilitiesByType[t] - return caps, ok + reg, ok := runtimeRegistrations[t] + if !ok { + return Capabilities{}, false + } + return reg.Capabilities(), true } -// RegisteredRuntime pairs a runtime identifier with its registered -// capabilities. +// RegisteredRuntime is one capability-registry entry resolved at read time. type RegisteredRuntime struct { Type Type Capabilities Capabilities + + // LaunchErr is non-nil when the runtime is supported by this build on + // this platform but its launch prerequisites are not currently met (for + // example QEMU registered on Linux without a system QEMU binary + // installed). Ordinary launches of the runtime will fail while it is + // non-nil. + LaunchErr error } -// RegisteredRuntimes enumerates every runtime with registered capabilities, -// sorted by type name so output is deterministic. Because capability -// registration is platform-gated, the result is the set of runtimes the -// linked backends can actually launch on the current host. +// Available reports whether ordinary launches of this runtime can succeed on +// this host: it is registered for the platform and its launch-prerequisite +// check passed. +func (r RegisteredRuntime) Available() bool { return r.LaunchErr == nil } + +// RegisteredRuntimes enumerates every registered runtime, sorted by type name +// so output is deterministic. Because registration is platform-gated, the +// result is the set of runtimes the linked backends support on the current +// host; per-entry Available/LaunchErr additionally reflect whether launch +// prerequisites are met right now. func RegisteredRuntimes() []RegisteredRuntime { - return enumerateRuntimes(capabilitiesByType) + return enumerateRuntimes(runtimeRegistrations) } -// enumerateRuntimes returns the map's entries sorted by type name. Split out -// from RegisteredRuntimes so enumeration semantics are testable without -// mutating the global registry. -func enumerateRuntimes(byType map[Type]Capabilities) []RegisteredRuntime { +// enumerateRuntimes resolves the map's entries (capabilities and launch +// checks) and sorts them by type name. Split out from RegisteredRuntimes so +// enumeration semantics are testable without mutating the global registry. +func enumerateRuntimes(byType map[Type]RuntimeRegistration) []RegisteredRuntime { runtimes := make([]RegisteredRuntime, 0, len(byType)) - for t, caps := range byType { - runtimes = append(runtimes, RegisteredRuntime{Type: t, Capabilities: caps}) + for t, reg := range byType { + rt := RegisteredRuntime{Type: t, Capabilities: reg.Capabilities()} + if reg.LaunchCheck != nil { + rt.LaunchErr = reg.LaunchCheck() + } + runtimes = append(runtimes, rt) } slices.SortFunc(runtimes, func(a, b RegisteredRuntime) int { return cmp.Compare(a.Type, b.Type) @@ -273,6 +314,13 @@ type Capabilities struct { // SupportsSnapshot indicates if Snapshot/Restore are available SupportsSnapshot bool + // SupportsFork indicates if snapshot-based instance forking is available. + // It is explicit rather than derived from SupportsSnapshot because + // VMStarter.PrepareFork allows a snapshot-capable backend to return + // ErrNotSupported; the advertised feature must track what PrepareFork + // actually implements, not what snapshots imply. + SupportsFork bool + // SupportsHotplugMemory indicates if ResizeMemory is available SupportsHotplugMemory bool diff --git a/lib/hypervisor/qemu/machine_test.go b/lib/hypervisor/qemu/machine_test.go index b086bbc6d..9998d9280 100644 --- a/lib/hypervisor/qemu/machine_test.go +++ b/lib/hypervisor/qemu/machine_test.go @@ -73,6 +73,14 @@ func TestMicroVMCapabilitiesExcludePCIPassthrough(t *testing.T) { assert.True(t, (MicroVMProfile{}).capabilities().RequiresHostSnapshotVersion) } +// TestQEMUCapabilitiesAdvertiseFork pins that both QEMU boards advertise fork +// explicitly, matching the shared PrepareFork implementation in fork.go. +func TestQEMUCapabilitiesAdvertiseFork(t *testing.T) { + t.Parallel() + assert.True(t, (StandardProfile{}).capabilities().SupportsFork) + assert.True(t, (MicroVMProfile{}).capabilities().SupportsFork) +} + func TestValidateConfigMicroVM(t *testing.T) { t.Parallel() if _, err := microVMMachineType(); err != nil { diff --git a/lib/hypervisor/qemu/profile.go b/lib/hypervisor/qemu/profile.go index 08ec41222..2ac3a2cbf 100644 --- a/lib/hypervisor/qemu/profile.go +++ b/lib/hypervisor/qemu/profile.go @@ -67,7 +67,10 @@ func (MicroVMProfile) requiresStoredVersion() bool { return true } func qemuCapabilities(supportsPCI bool) hypervisor.Capabilities { return hypervisor.Capabilities{ - SupportsSnapshot: true, + SupportsSnapshot: true, + // PrepareFork rewrites the saved QEMU VM config for forks (fork.go); + // both boards share the implementation. + SupportsFork: true, SupportsHotplugMemory: false, SupportsBalloonControl: true, SupportsPause: true, diff --git a/lib/hypervisor/qemu/register_linux.go b/lib/hypervisor/qemu/register_linux.go index 798484955..2b7265391 100644 --- a/lib/hypervisor/qemu/register_linux.go +++ b/lib/hypervisor/qemu/register_linux.go @@ -4,14 +4,30 @@ package qemu import "github.com/kernel/hypeman/lib/hypervisor" -// QEMU guests launch with KVM acceleration and kernel AF_VSOCK, so capability +// QEMU guests launch with KVM acceleration and kernel AF_VSOCK, so // registration — and therefore presence in hypervisor.RegisteredRuntimes — is // Linux-only. The microvm board additionally exists only for x86, so // qemu-microvm registers only where its machine type resolves (linux/amd64); // the same resolver rejects it at launch time. +// +// Unlike the other Linux backends, QEMU uses the host-installed system binary +// and hypeman deliberately starts without it (cmd/api/main.go only warns), so +// both QEMU registrations carry a LaunchCheck probing the same binary lookup +// that launches use. Registration means "supported by this build"; the check +// determines availability. func init() { - hypervisor.RegisterCapabilities(hypervisor.TypeQEMU, StandardProfile{}.capabilities()) + launchCheck := func() error { + _, err := (&Starter{}).GetBinaryPath(nil, "") + return err + } + hypervisor.RegisterRuntime(hypervisor.TypeQEMU, hypervisor.RuntimeRegistration{ + Capabilities: StandardProfile{}.capabilities, + LaunchCheck: launchCheck, + }) if _, err := microVMMachineType(); err == nil { - hypervisor.RegisterCapabilities(hypervisor.TypeQEMUMicroVM, MicroVMProfile{}.capabilities()) + hypervisor.RegisterRuntime(hypervisor.TypeQEMUMicroVM, hypervisor.RuntimeRegistration{ + Capabilities: MicroVMProfile{}.capabilities, + LaunchCheck: launchCheck, + }) } } diff --git a/lib/hypervisor/qemu/register_linux_test.go b/lib/hypervisor/qemu/register_linux_test.go index da70047fc..9d764a383 100644 --- a/lib/hypervisor/qemu/register_linux_test.go +++ b/lib/hypervisor/qemu/register_linux_test.go @@ -24,3 +24,28 @@ func TestMicroVMRegistrationMatchesBoardSupport(t *testing.T) { require.Equal(t, boardErr == nil, microVMRegistered, "qemu-microvm registration must match microvm board support on %s/%s", runtime.GOOS, runtime.GOARCH) } + +// TestQEMUAvailabilityTracksSystemBinary pins that registered QEMU runtimes +// report availability from the same binary lookup that launches use: hypeman +// deliberately starts without a system QEMU (cmd/api/main.go only warns), so +// registration alone must not imply launchability. +func TestQEMUAvailabilityTracksSystemBinary(t *testing.T) { + t.Parallel() + + _, binErr := (&Starter{}).GetBinaryPath(nil, "") + checked := 0 + for _, rt := range hypervisor.RegisteredRuntimes() { + if rt.Type != hypervisor.TypeQEMU && rt.Type != hypervisor.TypeQEMUMicroVM { + continue + } + checked++ + require.Equal(t, binErr == nil, rt.Available(), + "%s availability must match the launch-path binary lookup", rt.Type) + if binErr != nil { + require.Error(t, rt.LaunchErr) + } else { + require.NoError(t, rt.LaunchErr) + } + } + require.GreaterOrEqual(t, checked, 1, "standard qemu must be registered on Linux") +} diff --git a/lib/hypervisor/vz/client.go b/lib/hypervisor/vz/client.go index f0116bb97..6fbbcee4a 100644 --- a/lib/hypervisor/vz/client.go +++ b/lib/hypervisor/vz/client.go @@ -86,7 +86,11 @@ func capabilities() hypervisor.Capabilities { // Snapshot/standby support is runtime-derived: it requires Apple // Silicon AND macOS 14+. An arm64-only check would overstate // support on macOS 13 hosts. - SupportsSnapshot: saveRestoreSupported(), + SupportsSnapshot: saveRestoreSupported(), + // PrepareFork rewrites the snapshot manifest (fork.go), but forking + // restores a snapshot of the source, so fork is gated by the same + // save/restore probe as snapshots. + SupportsFork: saveRestoreSupported(), SupportsHotplugMemory: false, SupportsBalloonControl: true, SupportsPause: true, diff --git a/lib/hypervisor/vz/save_restore_support_darwin_test.go b/lib/hypervisor/vz/save_restore_support_darwin_test.go index 5e180be6b..a057419d9 100644 --- a/lib/hypervisor/vz/save_restore_support_darwin_test.go +++ b/lib/hypervisor/vz/save_restore_support_darwin_test.go @@ -16,10 +16,14 @@ func TestCapabilitiesDeriveSnapshotFromHost(t *testing.T) { macOSProductVersion = func() string { return "13.6" } require.False(t, capabilities().SupportsSnapshot, "macOS 13 must not advertise snapshot/standby support") + require.False(t, capabilities().SupportsFork, "fork restores a snapshot, so macOS 13 must not advertise it") macOSProductVersion = func() string { return "14.5" } require.Equal(t, saveRestoreSupported(), capabilities().SupportsSnapshot) + require.Equal(t, capabilities().SupportsSnapshot, capabilities().SupportsFork, + "vz PrepareFork rewrites snapshot manifests, so fork must track the same save/restore probe") macOSProductVersion = func() string { return "" } require.False(t, capabilities().SupportsSnapshot, "failed version probe must not advertise snapshot support") + require.False(t, capabilities().SupportsFork, "failed version probe must not advertise fork support") } diff --git a/lib/hypervisor/vz/starter.go b/lib/hypervisor/vz/starter.go index 72efefa1e..d62026b53 100644 --- a/lib/hypervisor/vz/starter.go +++ b/lib/hypervisor/vz/starter.go @@ -24,7 +24,12 @@ import ( func init() { hypervisor.RegisterSocketName(hypervisor.TypeVZ, "vz.sock") - hypervisor.RegisterCapabilities(hypervisor.TypeVZ, capabilities()) + // Capabilities are resolved per registry read: snapshot/standby/fork + // support depends on the host macOS version probe, not on init order. + // No LaunchCheck: the vz-shim binary ships embedded in hypeman. + hypervisor.RegisterRuntime(hypervisor.TypeVZ, hypervisor.RuntimeRegistration{ + Capabilities: capabilities, + }) hypervisor.RegisterVsockSocketName(hypervisor.TypeVZ, "vz.vsock") hypervisor.RegisterVsockDialerFactory(hypervisor.TypeVZ, NewVsockDialer) hypervisor.RegisterClientFactory(hypervisor.TypeVZ, func(socketPath string) (hypervisor.Hypervisor, error) { diff --git a/lib/instances/fork_test.go b/lib/instances/fork_test.go index 2763eab4b..d51a687ef 100644 --- a/lib/instances/fork_test.go +++ b/lib/instances/fork_test.go @@ -222,7 +222,11 @@ func TestForkInstanceStoppedSourceUsesReadLock(t *testing.T) { manager, _ := setupTestManager(t) ctx := context.Background() hvType := hypervisor.Type("concurrent-fork-test") - hypervisor.RegisterCapabilities(hvType, hypervisor.Capabilities{SupportsConcurrentForkPrepare: true}) + hypervisor.RegisterRuntime(hvType, hypervisor.RuntimeRegistration{ + Capabilities: func() hypervisor.Capabilities { + return hypervisor.Capabilities{SupportsConcurrentForkPrepare: true} + }, + }) manager.vmStarters[hvType] = concurrentForkPrepareTestStarter{} sourceID := "fork-stopped-read-lock-source" @@ -288,7 +292,11 @@ func TestForkInstanceStandbyRunningTargetSkipsSourceWriteLockWithoutAlias(t *tes manager, _ := setupTestManager(t) ctx := context.Background() hvType := hypervisor.Type("concurrent-fork-restore-test") - hypervisor.RegisterCapabilities(hvType, hypervisor.Capabilities{SupportsConcurrentForkPrepare: true}) + hypervisor.RegisterRuntime(hvType, hypervisor.RuntimeRegistration{ + Capabilities: func() hypervisor.Capabilities { + return hypervisor.Capabilities{SupportsConcurrentForkPrepare: true} + }, + }) hypervisor.RegisterClientFactory(hvType, func(string) (hypervisor.Hypervisor, error) { return lifecycleNoopHypervisor{state: hypervisor.StateRunning}, nil }) diff --git a/lib/oapi/oapi.go b/lib/oapi/oapi.go index fe8dc4f93..26893844a 100644 --- a/lib/oapi/oapi.go +++ b/lib/oapi/oapi.go @@ -512,19 +512,24 @@ type Capabilities struct { Images CapabilitiesImages `json:"images"` Network CapabilitiesNetwork `json:"network"` - // Runtimes Every runtime available on this host, each with its own feature - // IDs. Hosts commonly support several runtimes at once (for example + // Runtimes Every runtime this server build supports on this host platform, + // each with its own availability flag and feature IDs. Hosts + // commonly support several runtimes at once (for example // cloud-hypervisor, firecracker, qemu, and qemu-microvm on - // linux/amd64). Entries are sorted by name. + // linux/amd64). A listed runtime is only launchable when its + // "available" flag is true. Entries are sorted by name. Runtimes []CapabilitiesRuntime `json:"runtimes"` Server CapabilitiesServer `json:"server"` } // CapabilitiesDefaultRuntime defines model for CapabilitiesDefaultRuntime. type CapabilitiesDefaultRuntime struct { - // Available Whether the default runtime is available on this host (appears in - // runtimes). When false, launches that rely on the default will fail - // until the server is reconfigured with an available runtime. + // Available Whether the default runtime can launch on this host: it appears in + // runtimes and its launch prerequisites are met (matches that + // entry's "available"). When false, launches that rely on the + // default will fail until the server is reconfigured with an + // available runtime or the missing prerequisite (for example the + // QEMU system binary) is installed. Available bool `json:"available"` // Name Runtime used for launches that do not name one @@ -576,6 +581,14 @@ type CapabilitiesNetworkModel string // CapabilitiesRuntime defines model for CapabilitiesRuntime. type CapabilitiesRuntime struct { + // Available Whether this runtime's launch prerequisites are currently met on + // this host. Listed runtimes are supported by this server build on + // this platform; available=false means a host prerequisite is + // missing (for example qemu requires a system-installed QEMU + // binary) and launches naming this runtime will fail until it is + // installed. + Available bool `json:"available"` + // Features Stable feature IDs supported by this runtime on this host: // "snapshots" (snapshot/restore), "standby" (pause + memory // snapshot, with later restore), "fork" (clone an instance by @@ -584,9 +597,11 @@ type CapabilitiesRuntime struct { // balloon target changes), "vsock" (guest vsock communication), // "gpu-passthrough" (GPU/PCI device passthrough), "disk-io-limit" // (disk I/O rate limiting), "disk-resize" (live disk resize). - // Values are host-truthful: for example vz omits snapshots, standby, - // and fork on macOS 13, which lacks Virtualization.framework VM - // save/restore. + // Values are host- and configuration-truthful: vz omits snapshots, + // standby, and fork on macOS 13, which lacks + // Virtualization.framework VM save/restore, and cloud-hypervisor + // reports "disk-resize" only when the configured default version + // supports it. Features []string `json:"features"` // Name Runtime identifier @@ -18799,374 +18814,378 @@ func (sh *strictHandler) GetVolume(w http.ResponseWriter, r *http.Request, id st // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/openapi.yaml b/openapi.yaml index 1fe854105..44cb2f845 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -1441,10 +1441,12 @@ components: items: $ref: "#/components/schemas/CapabilitiesRuntime" description: | - Every runtime available on this host, each with its own feature - IDs. Hosts commonly support several runtimes at once (for example + Every runtime this server build supports on this host platform, + each with its own availability flag and feature IDs. Hosts + commonly support several runtimes at once (for example cloud-hypervisor, firecracker, qemu, and qemu-microvm on - linux/amd64). Entries are sorted by name. + linux/amd64). A listed runtime is only launchable when its + "available" flag is true. Entries are sorted by name. network: $ref: "#/components/schemas/CapabilitiesNetwork" images: @@ -1499,18 +1501,30 @@ components: available: type: boolean description: | - Whether the default runtime is available on this host (appears in - runtimes). When false, launches that rely on the default will fail - until the server is reconfigured with an available runtime. + Whether the default runtime can launch on this host: it appears in + runtimes and its launch prerequisites are met (matches that + entry's "available"). When false, launches that rely on the + default will fail until the server is reconfigured with an + available runtime or the missing prerequisite (for example the + QEMU system binary) is installed. CapabilitiesRuntime: type: object - required: [name, features] + required: [name, available, features] properties: name: type: string description: Runtime identifier example: cloud-hypervisor + available: + type: boolean + description: | + Whether this runtime's launch prerequisites are currently met on + this host. Listed runtimes are supported by this server build on + this platform; available=false means a host prerequisite is + missing (for example qemu requires a system-installed QEMU + binary) and launches naming this runtime will fail until it is + installed. features: type: array items: @@ -1524,9 +1538,11 @@ components: balloon target changes), "vsock" (guest vsock communication), "gpu-passthrough" (GPU/PCI device passthrough), "disk-io-limit" (disk I/O rate limiting), "disk-resize" (live disk resize). - Values are host-truthful: for example vz omits snapshots, standby, - and fork on macOS 13, which lacks Virtualization.framework VM - save/restore. + Values are host- and configuration-truthful: vz omits snapshots, + standby, and fork on macOS 13, which lacks + Virtualization.framework VM save/restore, and cloud-hypervisor + reports "disk-resize" only when the configured default version + supports it. CapabilitiesNetwork: type: object From c7ed2fef8a0f7019022df737b94b1a3e1bdd91fa Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sat, 15 Aug 2026 16:18:37 -0400 Subject: [PATCH 04/11] Address round-2 review: probe Rosetta availability, check firecracker override, restore public API compatibility - Rosetta emulation reporting now uses the same Virtualization.framework availability probe the vz-shim enforces at launch (LinuxRosettaDirectoryShareAvailability): hosts without Rosetta installed or on macOS < 13 no longer advertise the linux/amd64 image platform or the rosetta-emulation feature. cmd/api already links the framework on macOS, so this adds no build or runtime requirement; off darwin/arm64 the probe is a constant false. - Firecracker registers a LaunchCheck validating the active hypervisor.firecracker_binary_path override with the same executable check the launch path applies, since the override takes precedence over the embedded binaries on every launch. Regression test covers no-override, missing, non-executable, and valid overrides, including per-read re-evaluation. - Public Go source compatibility restored: hypervisor.RegisterCapabilities is back as a deprecated wrapper over RegisterRuntime, and instances.Manager no longer gains DefaultHypervisor \u2014 the capabilities handler type-asserts a narrow defaultHypervisorProvider accessor instead, falling back to the same cloud-hypervisor default lib/instances applies. Reverted the mock widening in lib/builds and lib/instances tests. --- cmd/api/api/capabilities.go | 36 +- cmd/api/api/capabilities_darwin_test.go | 25 + .../api/capabilities_rosetta_darwin_arm64.go | 18 + cmd/api/api/capabilities_rosetta_other.go | 8 + cmd/api/api/capabilities_test.go | 37 +- lib/builds/manager_test.go | 4 - lib/hypervisor/firecracker/binaries.go | 19 + lib/hypervisor/firecracker/register_linux.go | 7 +- .../firecracker/register_linux_test.go | 67 ++ lib/hypervisor/hypervisor.go | 15 + lib/hypervisor/register_compat_test.go | 34 + lib/instances/manager.go | 8 +- lib/instances/wait_test.go | 4 - lib/oapi/oapi.go | 757 +++++++++--------- openapi.yaml | 12 +- 15 files changed, 638 insertions(+), 413 deletions(-) create mode 100644 cmd/api/api/capabilities_rosetta_darwin_arm64.go create mode 100644 cmd/api/api/capabilities_rosetta_other.go create mode 100644 lib/hypervisor/firecracker/register_linux_test.go create mode 100644 lib/hypervisor/register_compat_test.go diff --git a/cmd/api/api/capabilities.go b/cmd/api/api/capabilities.go index e34ee0b04..dd204359b 100644 --- a/cmd/api/api/capabilities.go +++ b/cmd/api/api/capabilities.go @@ -38,13 +38,25 @@ var apiVersion = sync.OnceValue(func() string { return spec.Info.Version }) +// defaultHypervisorProvider is the narrow accessor this handler needs from +// the instance manager. The concrete instances manager implements it; it is +// type-asserted rather than added to instances.Manager so alternate Manager +// implementations (mocks, wrappers) compiled against the public module keep +// building without a new method. +type defaultHypervisorProvider interface { + DefaultHypervisor() hypervisor.Type +} + // GetCapabilities reports host, runtime, network, and image capabilities. func (s *ApiService) GetCapabilities(ctx context.Context, _ oapi.GetCapabilitiesRequestObject) (oapi.GetCapabilitiesResponseObject, error) { log := logger.FromContext(ctx) + // Fall back to the compile-time default when the manager does not expose + // its configured default — the same fallback lib/instances applies when + // no default hypervisor is configured. defaultRuntime := hypervisor.TypeCloudHypervisor - if s.InstanceManager != nil { - defaultRuntime = s.InstanceManager.DefaultHypervisor() + if p, ok := s.InstanceManager.(defaultHypervisorProvider); ok { + defaultRuntime = p.DefaultHypervisor() } // The capability registry is platform-gated at registration time, so its @@ -87,7 +99,7 @@ func (s *ApiService) GetCapabilities(ctx context.Context, _ oapi.GetCapabilities }, nil } - emulation := emulationSupported(runtime.GOOS, runtime.GOARCH) + emulation := emulationAvailable(runtime.GOOS, runtime.GOARCH, rosettaInstalled()) resp := oapi.Capabilities{ Server: oapi.CapabilitiesServer{ @@ -159,18 +171,20 @@ func oapiNetworkModel(m network.Model) oapi.CapabilitiesNetworkModel { return oapi.CapabilitiesNetworkModel(m) } -// emulationSupported reports whether the host can boot images built for the -// other CPU architecture. Only Apple Silicon macOS hosts qualify (vz with -// Rosetta). Rosetta installation itself is verified at instance start — a -// launch fails with installation guidance when it is missing — so this -// reports platform eligibility, and the OpenAPI description says so. -func emulationSupported(goos, goarch string) bool { - return goos == "darwin" && goarch == "arm64" +// emulationAvailable reports whether the host can boot images built for the +// other CPU architecture right now. Only Apple Silicon macOS hosts qualify +// (vz with Rosetta), and only when the Rosetta availability probe — the same +// Virtualization.framework check the vz-shim enforces at launch — reports it +// installed. Platform eligibility alone (darwin/arm64) is deliberately not +// enough: a macOS 11/12 host or one without Rosetta installed would advertise +// launches that lib/hypervisor/vz rejects. +func emulationAvailable(goos, goarch string, rosettaInstalled bool) bool { + return goos == "darwin" && goarch == "arm64" && rosettaInstalled } // imagePlatforms returns the image platforms (os/arch) the host can run: the // host-native Linux guest platform, plus Rosetta-emulated linux/amd64 on -// Apple Silicon. +// Apple Silicon with Rosetta installed. func imagePlatforms(goarch string, emulation bool) []string { platforms := []string{"linux/" + goarch} if emulation { diff --git a/cmd/api/api/capabilities_darwin_test.go b/cmd/api/api/capabilities_darwin_test.go index 6ddf450fd..79326ea07 100644 --- a/cmd/api/api/capabilities_darwin_test.go +++ b/cmd/api/api/capabilities_darwin_test.go @@ -3,12 +3,37 @@ package api import ( + "runtime" + "slices" "testing" "github.com/kernel/hypeman/lib/hypervisor" "github.com/stretchr/testify/require" ) +// TestGetCapabilitiesRosettaTracksProbe pins that Rosetta emulation reporting +// follows the live Virtualization.framework availability probe on this host +// — the same check the vz-shim enforces at launch — rather than treating +// every Apple Silicon host as emulation-capable: a host without Rosetta +// installed (or on macOS < 13) must advertise neither the rosetta-emulation +// feature nor the linux/amd64 image platform. +func TestGetCapabilitiesRosettaTracksProbe(t *testing.T) { + t.Parallel() + if runtime.GOARCH != "arm64" { + t.Skipf("rosetta emulation exists only on Apple Silicon (GOARCH=%s)", runtime.GOARCH) + } + svc := newTestService(t) + svc.NetworkManager = &stubCapabilitiesNetworkManager{} + + caps := getCapabilities(t, svc) + + want := rosettaInstalled() + require.Equal(t, want, slices.Contains(caps.Features, "rosetta-emulation"), + "rosetta-emulation feature must track the launch-path availability probe") + require.Equal(t, want, slices.Contains(caps.Images.Platforms, "linux/amd64"), + "linux/amd64 image platform must track the launch-path availability probe") +} + // TestRegisteredRuntimesDarwin pins the macOS registration boundary: only vz // can genuinely launch VMs on macOS (the Linux backends require KVM and // kernel AF_VSOCK), so the capability registry contains exactly vz even diff --git a/cmd/api/api/capabilities_rosetta_darwin_arm64.go b/cmd/api/api/capabilities_rosetta_darwin_arm64.go new file mode 100644 index 000000000..a52b3f047 --- /dev/null +++ b/cmd/api/api/capabilities_rosetta_darwin_arm64.go @@ -0,0 +1,18 @@ +//go:build darwin && arm64 + +package api + +import "github.com/Code-Hex/vz/v3" + +// rosettaInstalled reports whether Rosetta translation for Linux guests is +// installed and usable right now, using the same Virtualization.framework +// probe the vz-shim enforces when a launch requests Rosetta +// (cmd/vz-shim/rosetta_arm64.go): NotInstalled (Rosetta missing) and +// NotSupported (macOS < 13) both fail launches, so capability reporting must +// not advertise emulation in either state. Evaluated per request, so +// installing Rosetta (softwareupdate --install-rosetta) is reflected without +// a restart. cmd/api already links Virtualization.framework on macOS +// (checkHypervisorAccess), so this adds no build or runtime requirement. +var rosettaInstalled = func() bool { + return vz.LinuxRosettaDirectoryShareAvailability() == vz.LinuxRosettaAvailabilityInstalled +} diff --git a/cmd/api/api/capabilities_rosetta_other.go b/cmd/api/api/capabilities_rosetta_other.go new file mode 100644 index 000000000..af40c2ef5 --- /dev/null +++ b/cmd/api/api/capabilities_rosetta_other.go @@ -0,0 +1,8 @@ +//go:build !(darwin && arm64) + +package api + +// rosettaInstalled is never true off Apple Silicon macOS: Rosetta emulation +// exists only under vz. Kept as a var with the same shape as the +// darwin/arm64 probe so the handler code is identical on every platform. +var rosettaInstalled = func() bool { return false } diff --git a/cmd/api/api/capabilities_test.go b/cmd/api/api/capabilities_test.go index 695a52a16..656e2c1cd 100644 --- a/cmd/api/api/capabilities_test.go +++ b/cmd/api/api/capabilities_test.go @@ -24,7 +24,8 @@ func (s *stubCapabilitiesNetworkManager) EffectiveDefaultNetwork() (*network.Net return s.nw, s.err } -// stubDefaultRuntimeInstanceManager overrides the configured default runtime. +// stubDefaultRuntimeInstanceManager overrides the configured default runtime +// via the optional defaultHypervisorProvider accessor. type stubDefaultRuntimeInstanceManager struct { instances.Manager defaultRuntime hypervisor.Type @@ -34,6 +35,13 @@ func (s *stubDefaultRuntimeInstanceManager) DefaultHypervisor() hypervisor.Type return s.defaultRuntime } +// stubOpaqueInstanceManager implements instances.Manager without the optional +// DefaultHypervisor accessor, standing in for alternate Manager +// implementations compiled against the public module. +type stubOpaqueInstanceManager struct { + instances.Manager +} + func getCapabilities(t *testing.T, svc *ApiService) oapi.Capabilities { t.Helper() resp, err := svc.GetCapabilities(ctx(), oapi.GetCapabilitiesRequestObject{}) @@ -222,6 +230,21 @@ func TestGetCapabilitiesDefaultNotAvailable(t *testing.T) { "an unavailable default must not hide the runtimes that are available") } +// TestGetCapabilitiesDefaultWithoutAccessor pins the fallback contract for +// instance managers that do not expose the optional DefaultHypervisor +// accessor (it is deliberately not part of instances.Manager): the handler +// must fall back to the same cloud-hypervisor default lib/instances applies +// when no default is configured, not fail. +func TestGetCapabilitiesDefaultWithoutAccessor(t *testing.T) { + t.Parallel() + svc := newTestService(t) + svc.NetworkManager = &stubCapabilitiesNetworkManager{} + svc.InstanceManager = &stubOpaqueInstanceManager{} + + caps := getCapabilities(t, svc) + require.Equal(t, string(hypervisor.TypeCloudHypervisor), caps.DefaultRuntime.Name) +} + // TestGetCapabilitiesNoDefaultNetwork pins the gateway-absence contract: when // no default network resolves, gateway and subnet are omitted rather than // serialized as empty strings. @@ -255,12 +278,14 @@ func TestOapiNetworkModel(t *testing.T) { require.Equal(t, oapi.Nat, oapiNetworkModel(network.ModelNAT)) } -func TestEmulationSupported(t *testing.T) { +func TestEmulationAvailable(t *testing.T) { t.Parallel() - require.True(t, emulationSupported("darwin", "arm64")) - require.False(t, emulationSupported("darwin", "amd64")) - require.False(t, emulationSupported("linux", "arm64")) - require.False(t, emulationSupported("linux", "amd64")) + require.True(t, emulationAvailable("darwin", "arm64", true)) + require.False(t, emulationAvailable("darwin", "arm64", false), + "an Apple Silicon host without Rosetta installed must not advertise emulation") + require.False(t, emulationAvailable("darwin", "amd64", true)) + require.False(t, emulationAvailable("linux", "arm64", true)) + require.False(t, emulationAvailable("linux", "amd64", true)) } func TestImagePlatforms(t *testing.T) { diff --git a/lib/builds/manager_test.go b/lib/builds/manager_test.go index dd48d6c96..7be58b0fc 100644 --- a/lib/builds/manager_test.go +++ b/lib/builds/manager_test.go @@ -75,10 +75,6 @@ func (m *mockInstanceManager) CreateInstance(ctx context.Context, req instances. return inst, nil } -func (m *mockInstanceManager) DefaultHypervisor() hypervisor.Type { - return hypervisor.TypeCloudHypervisor -} - func (m *mockInstanceManager) GetInstance(ctx context.Context, id string) (*instances.Instance, error) { if m.getFunc != nil { return m.getFunc(ctx, id) diff --git a/lib/hypervisor/firecracker/binaries.go b/lib/hypervisor/firecracker/binaries.go index d9e2e1048..acb3aafc5 100644 --- a/lib/hypervisor/firecracker/binaries.go +++ b/lib/hypervisor/firecracker/binaries.go @@ -51,6 +51,25 @@ func getCustomBinaryPath() string { return customBinaryPath } +// checkLaunchPrerequisites verifies the launch prerequisites that +// platform-gated registration cannot express. The default binaries ship +// embedded in hypeman, but a configured hypervisor.firecracker_binary_path +// override takes precedence over them on every launch (resolveBinaryPath), +// so a missing or non-executable override means every launch fails and the +// runtime must not report available. Used as the capability registry's +// LaunchCheck; it is evaluated per registry read, so an override fixed after +// startup is reflected without a restart. +func checkLaunchPrerequisites() error { + path := getCustomBinaryPath() + if path == "" { + return nil + } + if err := validateExecutable(path); err != nil { + return fmt.Errorf("invalid firecracker custom binary path %q: %w", path, err) + } + return nil +} + func resolveBinaryPath(p *paths.Paths, version string) (string, error) { if path := getCustomBinaryPath(); path != "" { if err := validateExecutable(path); err != nil { diff --git a/lib/hypervisor/firecracker/register_linux.go b/lib/hypervisor/firecracker/register_linux.go index c0162437b..0a4f71777 100644 --- a/lib/hypervisor/firecracker/register_linux.go +++ b/lib/hypervisor/firecracker/register_linux.go @@ -5,10 +5,13 @@ package firecracker import "github.com/kernel/hypeman/lib/hypervisor" // Firecracker requires KVM, so registration — and therefore its presence in -// hypervisor.RegisteredRuntimes — is Linux-only. No LaunchCheck: the default -// binary ships embedded in hypeman. +// hypervisor.RegisteredRuntimes — is Linux-only. The default binary ships +// embedded in hypeman, but hypervisor.firecracker_binary_path overrides it +// on every launch, so the LaunchCheck validates the active override (if any) +// with the same executable check the launch path applies. func init() { hypervisor.RegisterRuntime(hypervisor.TypeFirecracker, hypervisor.RuntimeRegistration{ Capabilities: capabilities, + LaunchCheck: checkLaunchPrerequisites, }) } diff --git a/lib/hypervisor/firecracker/register_linux_test.go b/lib/hypervisor/firecracker/register_linux_test.go new file mode 100644 index 000000000..a88e09056 --- /dev/null +++ b/lib/hypervisor/firecracker/register_linux_test.go @@ -0,0 +1,67 @@ +//go:build linux + +package firecracker + +import ( + "os" + "path/filepath" + "testing" + + "github.com/kernel/hypeman/lib/hypervisor" + "github.com/stretchr/testify/require" +) + +// registeredFirecracker returns firecracker's capability-registry entry, +// re-resolved on every call so launch checks reflect the current +// configuration. +func registeredFirecracker(t *testing.T) hypervisor.RegisteredRuntime { + t.Helper() + for _, rt := range hypervisor.RegisteredRuntimes() { + if rt.Type == hypervisor.TypeFirecracker { + return rt + } + } + t.Fatal("firecracker must be registered on Linux") + return hypervisor.RegisteredRuntime{} +} + +// TestFirecrackerAvailabilityTracksBinaryOverride pins that firecracker's +// registered availability follows the configured binary override, which +// takes precedence over the embedded binaries on every launch +// (resolveBinaryPath): an invalid hypervisor.firecracker_binary_path means +// every launch fails, so the runtime must not report available; clearing or +// fixing the override must flip availability back without re-registration. +// Not parallel: it mutates the process-wide custom binary path. +func TestFirecrackerAvailabilityTracksBinaryOverride(t *testing.T) { + SetCustomBinaryPath("") + t.Cleanup(func() { SetCustomBinaryPath("") }) + + // No override: the embedded binary implies launchability. + rt := registeredFirecracker(t) + require.NoError(t, rt.LaunchErr) + require.True(t, rt.Available(), "firecracker with embedded binaries must be available") + + // Missing override: launches would fail, so availability must drop. + SetCustomBinaryPath("/does/not/exist/firecracker") + rt = registeredFirecracker(t) + require.Error(t, rt.LaunchErr) + require.False(t, rt.Available(), "a missing binary override must make firecracker unavailable") + require.Contains(t, rt.LaunchErr.Error(), "invalid firecracker custom binary path", + "the launch check must surface the same override error launches report") + + // Non-executable override: same verdict as the launch path's check. + nonExec := filepath.Join(t.TempDir(), "firecracker") + require.NoError(t, os.WriteFile(nonExec, []byte("#!/bin/sh\nexit 0\n"), 0644)) + SetCustomBinaryPath(nonExec) + rt = registeredFirecracker(t) + require.Error(t, rt.LaunchErr) + require.False(t, rt.Available(), "a non-executable binary override must make firecracker unavailable") + + // Valid executable override: available again, per registry read. + valid := filepath.Join(t.TempDir(), "firecracker") + require.NoError(t, os.WriteFile(valid, []byte("#!/bin/sh\nexit 0\n"), 0755)) + SetCustomBinaryPath(valid) + rt = registeredFirecracker(t) + require.NoError(t, rt.LaunchErr) + require.True(t, rt.Available(), "a valid binary override must restore availability without a restart") +} diff --git a/lib/hypervisor/hypervisor.go b/lib/hypervisor/hypervisor.go index fc8c1562d..31e3981ff 100644 --- a/lib/hypervisor/hypervisor.go +++ b/lib/hypervisor/hypervisor.go @@ -115,6 +115,21 @@ func RegisterRuntime(t Type, reg RuntimeRegistration) { runtimeRegistrations[t] = reg } +// RegisterCapabilities registers a static capability set for a hypervisor +// type with no launch check, so registration alone implies launchability. +// +// Deprecated: Use RegisterRuntime, which resolves capabilities per registry +// read (so configuration applied after init stays truthful) and supports an +// optional LaunchCheck for host launch prerequisites. RegisterCapabilities +// is kept so custom backends built against earlier versions of this module +// keep compiling; it wraps RegisterRuntime with a resolver that returns the +// given set unchanged. +func RegisterCapabilities(t Type, caps Capabilities) { + RegisterRuntime(t, RuntimeRegistration{ + Capabilities: func() Capabilities { return caps }, + }) +} + // CapabilitiesForType returns the effective capabilities for a hypervisor // type, resolved at call time. It reports ok=false for runtimes that cannot // run on the current host platform, because such runtimes never register. diff --git a/lib/hypervisor/register_compat_test.go b/lib/hypervisor/register_compat_test.go new file mode 100644 index 000000000..17de58770 --- /dev/null +++ b/lib/hypervisor/register_compat_test.go @@ -0,0 +1,34 @@ +package hypervisor + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +// TestRegisterCapabilitiesCompat pins the deprecated RegisterCapabilities +// wrapper that keeps custom backends built against earlier versions of this +// module compiling: it must register a runtime whose capabilities resolve to +// exactly the given static set and which reports available (no launch +// check), matching the old registration semantics. +func TestRegisterCapabilitiesCompat(t *testing.T) { + t.Parallel() + typ := Type("register-capabilities-compat-test") + want := Capabilities{SupportsSnapshot: true, SupportsPause: true, SupportsVsock: true} + RegisterCapabilities(typ, want) + + got, ok := CapabilitiesForType(typ) + require.True(t, ok, "RegisterCapabilities must register the type") + require.Equal(t, want, got) + + for _, rt := range RegisteredRuntimes() { + if rt.Type != typ { + continue + } + require.NoError(t, rt.LaunchErr) + require.True(t, rt.Available(), "static registration implies launchability") + require.Equal(t, want, rt.Capabilities) + return + } + t.Fatal("RegisterCapabilities must add the runtime to the registry") +} diff --git a/lib/instances/manager.go b/lib/instances/manager.go index 98fe43fc7..5d595d823 100644 --- a/lib/instances/manager.go +++ b/lib/instances/manager.go @@ -27,9 +27,6 @@ import ( type Manager interface { ListInstances(ctx context.Context, filter *ListInstancesFilter) ([]Instance, error) - // DefaultHypervisor returns the effective default hypervisor type used for - // launches that do not specify one. - DefaultHypervisor() hypervisor.Type ListSnapshots(ctx context.Context, filter *ListSnapshotsFilter) ([]Snapshot, error) GetSnapshot(ctx context.Context, snapshotID string) (*Snapshot, error) CreateInstance(ctx context.Context, req CreateInstanceRequest) (*Instance, error) @@ -694,7 +691,10 @@ func (m *manager) UpdateInstance(ctx context.Context, id string, req UpdateInsta } // DefaultHypervisor returns the effective default hypervisor type used for -// launches that do not specify one. +// launches that do not specify one. It is deliberately not part of the +// Manager interface — widening that interface would break alternate +// implementations (mocks, wrappers) compiled against this public module — so +// callers that need it type-assert for this method instead. func (m *manager) DefaultHypervisor() hypervisor.Type { return m.defaultHypervisor } diff --git a/lib/instances/wait_test.go b/lib/instances/wait_test.go index 640a60056..bab6f06d7 100644 --- a/lib/instances/wait_test.go +++ b/lib/instances/wait_test.go @@ -28,10 +28,6 @@ func (s *stubManager) GetInstance(ctx context.Context, id string) (*Instance, er return nil, ErrNotFound } -func (s *stubManager) DefaultHypervisor() hypervisor.Type { - return hypervisor.TypeCloudHypervisor -} - // Unused interface methods — only GetInstance and SubscribeLifecycleEvents are needed. func (s *stubManager) ListInstances(context.Context, *ListInstancesFilter) ([]Instance, error) { return nil, nil diff --git a/lib/oapi/oapi.go b/lib/oapi/oapi.go index 26893844a..de427c557 100644 --- a/lib/oapi/oapi.go +++ b/lib/oapi/oapi.go @@ -505,8 +505,9 @@ type Capabilities struct { // "instances", "images", "builds", "volumes", "ingress", "exec", // "logs". Host-conditional: "devices" (device passthrough // management, Linux hosts only) and "rosetta-emulation" (Apple - // Silicon macOS hosts). Per-runtime features are reported under each - // runtimes[] entry. + // Silicon macOS hosts with Rosetta currently installed, per the same + // availability probe launches enforce). Per-runtime features are + // reported under each runtimes[] entry. Features []string `json:"features"` Host CapabilitiesHost `json:"host"` Images CapabilitiesImages `json:"images"` @@ -551,9 +552,10 @@ type CapabilitiesImages struct { DefaultPlatform string `json:"default_platform"` // Platforms Image platforms (os/arch) this host can run. On Apple Silicon macOS - // this includes linux/amd64, which vz runs via Rosetta; Rosetta - // installation itself is verified at instance start, and launches - // fail with guidance when it is missing. + // this includes linux/amd64 only when Rosetta is currently installed + // — probed via the same Virtualization.framework availability check + // launches enforce — so a listed platform is launchable right now. + // Install Rosetta (softwareupdate --install-rosetta) to enable it. Platforms []string `json:"platforms"` } @@ -18814,378 +18816,379 @@ func (sh *strictHandler) GetVolume(w http.ResponseWriter, r *http.Request, id st // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/openapi.yaml b/openapi.yaml index 44cb2f845..2e24a667c 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -1461,8 +1461,9 @@ components: "instances", "images", "builds", "volumes", "ingress", "exec", "logs". Host-conditional: "devices" (device passthrough management, Linux hosts only) and "rosetta-emulation" (Apple - Silicon macOS hosts). Per-runtime features are reported under each - runtimes[] entry. + Silicon macOS hosts with Rosetta currently installed, per the same + availability probe launches enforce). Per-runtime features are + reported under each runtimes[] entry. CapabilitiesServer: type: object @@ -1579,9 +1580,10 @@ components: type: string description: | Image platforms (os/arch) this host can run. On Apple Silicon macOS - this includes linux/amd64, which vz runs via Rosetta; Rosetta - installation itself is verified at instance start, and launches - fail with guidance when it is missing. + this includes linux/amd64 only when Rosetta is currently installed + — probed via the same Virtualization.framework availability check + launches enforce — so a listed platform is launchable right now. + Install Rosetta (softwareupdate --install-rosetta) to enable it. example: [linux/arm64, linux/amd64] default_platform: type: string From 610529c1f186cba5c64c27aa6bb77d6ae76d90f3 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sat, 15 Aug 2026 16:55:08 -0400 Subject: [PATCH 05/11] Address round-3 review: config-seeded default fallback, full QEMU launch prerequisites MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Round-3 finding 1: a valid manager wrapper embedding instances.Manager hides the concrete manager's DefaultHypervisor method, so the handler's hardcoded cloud-hypervisor fallback could misreport a configured Firecracker/QEMU default while launches still used it. The fallback is now seeded from config.Hypervisor.Default (the value the wrapped manager was constructed from), normalizing only an empty value to cloud-hypervisor — mirroring lib/instances.NewManagerWithConfigE — with the optional accessor still authoritative when exposed. The opaque-manager test now pins a configured non-default runtime, plus unconfigured normalization and accessor precedence. Round-3 finding 2: QEMU's LaunchCheck only called GetBinaryPath, whose fixed /usr/bin and /usr/local/bin candidates are accepted on a bare os.Stat even if non-executable or broken, and it never verified the host vhost-vsock device — required because every created instance gets a nonzero vsock CID and buildArgs unconditionally attaches a vhost-vsock device (API startup validates KVM only). checkLaunchPrerequisites now validates the resolved binary's execute bit, runs the same --version probe ResolveVersion persists on every cold start, and stats /dev/vhost-vsock; all evaluated per registry read and side-effect-free. Unavailable cases (missing/non-executable/broken/ unparseable binary, missing device) are covered with fake binaries and device paths, and the availability test tracks the full prerequisite check. OpenAPI description updated and regenerated via make oapi-generate. --- cmd/api/api/capabilities.go | 16 +- cmd/api/api/capabilities_test.go | 47 +- lib/hypervisor/qemu/process.go | 27 + lib/hypervisor/qemu/register_linux.go | 64 ++- lib/hypervisor/qemu/register_linux_test.go | 91 ++- lib/oapi/oapi.go | 629 +++++++++++---------- openapi.yaml | 6 +- 7 files changed, 533 insertions(+), 347 deletions(-) diff --git a/cmd/api/api/capabilities.go b/cmd/api/api/capabilities.go index dd204359b..e97d030ab 100644 --- a/cmd/api/api/capabilities.go +++ b/cmd/api/api/capabilities.go @@ -51,10 +51,18 @@ type defaultHypervisorProvider interface { func (s *ApiService) GetCapabilities(ctx context.Context, _ oapi.GetCapabilitiesRequestObject) (oapi.GetCapabilitiesResponseObject, error) { log := logger.FromContext(ctx) - // Fall back to the compile-time default when the manager does not expose - // its configured default — the same fallback lib/instances applies when - // no default hypervisor is configured. - defaultRuntime := hypervisor.TypeCloudHypervisor + // Resolve the default runtime the way launches do. Prefer the manager's + // own effective default via the optional accessor; when the manager does + // not expose it (a wrapper embedding instances.Manager hides the concrete + // manager's extra method), fall back to the configured default the manager + // was constructed from — launches still route through the wrapped manager, + // so a hardcoded fallback would misreport a Firecracker/QEMU default as + // cloud-hypervisor. Only an empty (unconfigured) value normalizes to the + // compile-time default, mirroring lib/instances.NewManagerWithConfigE. + defaultRuntime := hypervisor.Type(s.Config.Hypervisor.Default) + if defaultRuntime == "" { + defaultRuntime = hypervisor.TypeCloudHypervisor + } if p, ok := s.InstanceManager.(defaultHypervisorProvider); ok { defaultRuntime = p.DefaultHypervisor() } diff --git a/cmd/api/api/capabilities_test.go b/cmd/api/api/capabilities_test.go index 656e2c1cd..bb3e4684a 100644 --- a/cmd/api/api/capabilities_test.go +++ b/cmd/api/api/capabilities_test.go @@ -232,17 +232,48 @@ func TestGetCapabilitiesDefaultNotAvailable(t *testing.T) { // TestGetCapabilitiesDefaultWithoutAccessor pins the fallback contract for // instance managers that do not expose the optional DefaultHypervisor -// accessor (it is deliberately not part of instances.Manager): the handler -// must fall back to the same cloud-hypervisor default lib/instances applies -// when no default is configured, not fail. +// accessor (it is deliberately not part of instances.Manager, so a wrapper +// embedding the interface hides the concrete manager's method): the handler +// must report the configured default — the value the wrapped manager was +// constructed from and launches still use — normalizing only an empty +// (unconfigured) value to the same cloud-hypervisor default lib/instances +// applies. func TestGetCapabilitiesDefaultWithoutAccessor(t *testing.T) { t.Parallel() - svc := newTestService(t) - svc.NetworkManager = &stubCapabilitiesNetworkManager{} - svc.InstanceManager = &stubOpaqueInstanceManager{} - caps := getCapabilities(t, svc) - require.Equal(t, string(hypervisor.TypeCloudHypervisor), caps.DefaultRuntime.Name) + t.Run("configured non-default runtime is reported", func(t *testing.T) { + t.Parallel() + svc := newTestService(t) + svc.Config.Hypervisor.Default = string(hypervisor.TypeFirecracker) + svc.NetworkManager = &stubCapabilitiesNetworkManager{} + svc.InstanceManager = &stubOpaqueInstanceManager{} + + caps := getCapabilities(t, svc) + require.Equal(t, string(hypervisor.TypeFirecracker), caps.DefaultRuntime.Name, + "a wrapped manager must not misreport the configured default as cloud-hypervisor") + }) + + t.Run("unconfigured default normalizes to cloud-hypervisor", func(t *testing.T) { + t.Parallel() + svc := newTestService(t) + svc.NetworkManager = &stubCapabilitiesNetworkManager{} + svc.InstanceManager = &stubOpaqueInstanceManager{} + + caps := getCapabilities(t, svc) + require.Equal(t, string(hypervisor.TypeCloudHypervisor), caps.DefaultRuntime.Name) + }) + + t.Run("accessor overrides the configured value", func(t *testing.T) { + t.Parallel() + svc := newTestService(t) + svc.Config.Hypervisor.Default = string(hypervisor.TypeFirecracker) + svc.NetworkManager = &stubCapabilitiesNetworkManager{} + svc.InstanceManager = &stubDefaultRuntimeInstanceManager{defaultRuntime: hypervisor.TypeQEMU} + + caps := getCapabilities(t, svc) + require.Equal(t, string(hypervisor.TypeQEMU), caps.DefaultRuntime.Name, + "the manager's effective default is authoritative when exposed") + }) } // TestGetCapabilitiesNoDefaultNetwork pins the gateway-absence contract: when diff --git a/lib/hypervisor/qemu/process.go b/lib/hypervisor/qemu/process.go index e4f08bd83..2be1ed17b 100644 --- a/lib/hypervisor/qemu/process.go +++ b/lib/hypervisor/qemu/process.go @@ -135,7 +135,15 @@ func (s *Starter) detectVersion(p *paths.Paths) (string, error) { if err != nil { return "", err } + return versionFromBinary(binaryPath) +} +// versionFromBinary runs the resolved QEMU binary's --version and parses the +// installed version from its output. It is the version probe behind +// GetVersion/ResolveVersion — every cold start persists its result — and the +// capability registry's launch check reuses it so "available" means the same +// binary execution that launches require actually succeeds. +func versionFromBinary(binaryPath string) (string, error) { cmd := exec.Command(binaryPath, "--version") output, err := cmd.Output() if err != nil { @@ -152,6 +160,25 @@ func (s *Starter) detectVersion(p *paths.Paths) (string, error) { return "", fmt.Errorf("could not parse QEMU version from: %s", string(output)) } +// validateExecutable rejects paths that cannot be executed as the QEMU +// binary: missing files, directories, and files without an execute bit. +// GetBinaryPath's fixed /usr/bin and /usr/local/bin candidates are accepted +// on a bare os.Stat, so launch-prerequisite checks must not assume a +// resolved path is runnable. +func validateExecutable(path string) error { + info, err := os.Stat(path) + if err != nil { + return err + } + if info.IsDir() { + return fmt.Errorf("path is a directory") + } + if info.Mode()&0111 == 0 { + return fmt.Errorf("file is not executable") + } + return nil +} + // buildQMPArgs returns the base QMP socket arguments for QEMU. func buildQMPArgs(socketPath string) []string { return []string{ diff --git a/lib/hypervisor/qemu/register_linux.go b/lib/hypervisor/qemu/register_linux.go index 2b7265391..6e8ab8672 100644 --- a/lib/hypervisor/qemu/register_linux.go +++ b/lib/hypervisor/qemu/register_linux.go @@ -2,7 +2,18 @@ package qemu -import "github.com/kernel/hypeman/lib/hypervisor" +import ( + "fmt" + "os" + + "github.com/kernel/hypeman/lib/hypervisor" +) + +// vhostVsockDevicePath is the host device QEMU's vhost-vsock backend opens. +// Every created instance is assigned a nonzero vsock CID (lib/instances), so +// buildArgs always adds a vhost-vsock device and ordinary launches fail +// without it. API startup validates KVM only, so availability must check it. +const vhostVsockDevicePath = "/dev/vhost-vsock" // QEMU guests launch with KVM acceleration and kernel AF_VSOCK, so // registration — and therefore presence in hypervisor.RegisteredRuntimes — is @@ -12,22 +23,57 @@ import "github.com/kernel/hypeman/lib/hypervisor" // // Unlike the other Linux backends, QEMU uses the host-installed system binary // and hypeman deliberately starts without it (cmd/api/main.go only warns), so -// both QEMU registrations carry a LaunchCheck probing the same binary lookup -// that launches use. Registration means "supported by this build"; the check +// both QEMU registrations carry a LaunchCheck verifying the launch +// prerequisites. Registration means "supported by this build"; the check // determines availability. func init() { - launchCheck := func() error { - _, err := (&Starter{}).GetBinaryPath(nil, "") - return err - } hypervisor.RegisterRuntime(hypervisor.TypeQEMU, hypervisor.RuntimeRegistration{ Capabilities: StandardProfile{}.capabilities, - LaunchCheck: launchCheck, + LaunchCheck: checkLaunchPrerequisites, }) if _, err := microVMMachineType(); err == nil { hypervisor.RegisterRuntime(hypervisor.TypeQEMUMicroVM, hypervisor.RuntimeRegistration{ Capabilities: MicroVMProfile{}.capabilities, - LaunchCheck: launchCheck, + LaunchCheck: checkLaunchPrerequisites, }) } } + +// checkLaunchPrerequisites is the capability registry's side-effect-free +// launch check for both QEMU boards: it resolves the system binary with the +// same lookup launches use, then verifies the prerequisites every ordinary +// launch needs. Evaluated per registry read, so installing QEMU or loading +// vhost_vsock flips availability without a restart. +func checkLaunchPrerequisites() error { + binaryPath, err := (&Starter{}).GetBinaryPath(nil, "") + if err != nil { + return err + } + return checkLaunchPrerequisitesFor(binaryPath, vhostVsockDevicePath) +} + +// checkLaunchPrerequisitesFor verifies that a resolved QEMU binary and the +// host vsock device can back an ordinary launch: +// +// - the binary must actually execute and report a parseable version +// (versionFromBinary), because every cold start persists ResolveVersion's +// result and treats failure as fatal — a non-executable or broken binary +// accepted by GetBinaryPath's bare os.Stat must not report available; +// - the vhost-vsock host device must exist, because every created instance +// receives a nonzero vsock CID and buildArgs unconditionally attaches a +// vhost-vsock device for it. +// +// Split from checkLaunchPrerequisites so unavailable cases are testable with +// fake binaries and device paths regardless of the host's QEMU install. +func checkLaunchPrerequisitesFor(binaryPath, vsockDevicePath string) error { + if err := validateExecutable(binaryPath); err != nil { + return fmt.Errorf("qemu binary %s is not executable: %w", binaryPath, err) + } + if _, err := versionFromBinary(binaryPath); err != nil { + return fmt.Errorf("qemu binary %s is not usable: %w", binaryPath, err) + } + if _, err := os.Stat(vsockDevicePath); err != nil { + return fmt.Errorf("vsock device %s is required for instance launches (load the vhost_vsock kernel module): %w", vsockDevicePath, err) + } + return nil +} diff --git a/lib/hypervisor/qemu/register_linux_test.go b/lib/hypervisor/qemu/register_linux_test.go index 9d764a383..f5f5dcf8a 100644 --- a/lib/hypervisor/qemu/register_linux_test.go +++ b/lib/hypervisor/qemu/register_linux_test.go @@ -3,6 +3,8 @@ package qemu import ( + "os" + "path/filepath" "runtime" "testing" @@ -25,23 +27,24 @@ func TestMicroVMRegistrationMatchesBoardSupport(t *testing.T) { "qemu-microvm registration must match microvm board support on %s/%s", runtime.GOOS, runtime.GOARCH) } -// TestQEMUAvailabilityTracksSystemBinary pins that registered QEMU runtimes -// report availability from the same binary lookup that launches use: hypeman -// deliberately starts without a system QEMU (cmd/api/main.go only warns), so -// registration alone must not imply launchability. -func TestQEMUAvailabilityTracksSystemBinary(t *testing.T) { +// TestQEMUAvailabilityTracksLaunchPrerequisites pins that registered QEMU +// runtimes report availability from the full launch-prerequisite check — +// binary resolution, executability/version, and the vhost-vsock host device — +// not just registration: hypeman deliberately starts without a system QEMU +// (cmd/api/main.go only warns) and validates KVM only. +func TestQEMUAvailabilityTracksLaunchPrerequisites(t *testing.T) { t.Parallel() - _, binErr := (&Starter{}).GetBinaryPath(nil, "") + prereqErr := checkLaunchPrerequisites() checked := 0 for _, rt := range hypervisor.RegisteredRuntimes() { if rt.Type != hypervisor.TypeQEMU && rt.Type != hypervisor.TypeQEMUMicroVM { continue } checked++ - require.Equal(t, binErr == nil, rt.Available(), - "%s availability must match the launch-path binary lookup", rt.Type) - if binErr != nil { + require.Equal(t, prereqErr == nil, rt.Available(), + "%s availability must match the launch-prerequisite check", rt.Type) + if prereqErr != nil { require.Error(t, rt.LaunchErr) } else { require.NoError(t, rt.LaunchErr) @@ -49,3 +52,73 @@ func TestQEMUAvailabilityTracksSystemBinary(t *testing.T) { } require.GreaterOrEqual(t, checked, 1, "standard qemu must be registered on Linux") } + +// fakeQEMUBinary writes an executable script that mimics `qemu --version` +// output, standing in for a working system QEMU. +func fakeQEMUBinary(t *testing.T, dir string) string { + t.Helper() + path := filepath.Join(dir, "qemu-system-fake") + script := "#!/bin/sh\necho 'QEMU emulator version 8.2.0 (fake)'\n" + require.NoError(t, os.WriteFile(path, []byte(script), 0o755)) + return path +} + +// fakeVsockDevice creates a stand-in for /dev/vhost-vsock: the prerequisite +// check only requires the device node to exist. +func fakeVsockDevice(t *testing.T, dir string) string { + t.Helper() + path := filepath.Join(dir, "vhost-vsock") + require.NoError(t, os.WriteFile(path, nil, 0o600)) + return path +} + +// TestCheckLaunchPrerequisitesFor covers the unavailable cases GetBinaryPath's +// bare os.Stat lookup cannot see: non-executable binaries, binaries that fail +// to run or report an unparseable version, and a missing vhost-vsock device — +// each of which makes every ordinary QEMU launch fail and must therefore +// report unavailable. +func TestCheckLaunchPrerequisitesFor(t *testing.T) { + t.Parallel() + + t.Run("working binary and device pass", func(t *testing.T) { + dir := t.TempDir() + require.NoError(t, checkLaunchPrerequisitesFor(fakeQEMUBinary(t, dir), fakeVsockDevice(t, dir))) + }) + + t.Run("missing binary fails", func(t *testing.T) { + dir := t.TempDir() + err := checkLaunchPrerequisitesFor(filepath.Join(dir, "missing"), fakeVsockDevice(t, dir)) + require.ErrorContains(t, err, "not executable") + }) + + t.Run("non-executable binary fails", func(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "qemu-system-fake") + require.NoError(t, os.WriteFile(path, []byte("#!/bin/sh\necho 'QEMU emulator version 8.2.0'\n"), 0o644)) + err := checkLaunchPrerequisitesFor(path, fakeVsockDevice(t, dir)) + require.ErrorContains(t, err, "not executable") + }) + + t.Run("broken binary fails", func(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "qemu-system-fake") + require.NoError(t, os.WriteFile(path, []byte("#!/bin/sh\nexit 1\n"), 0o755)) + err := checkLaunchPrerequisitesFor(path, fakeVsockDevice(t, dir)) + require.ErrorContains(t, err, "not usable") + }) + + t.Run("unparseable version fails", func(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "qemu-system-fake") + require.NoError(t, os.WriteFile(path, []byte("#!/bin/sh\necho 'not qemu'\n"), 0o755)) + err := checkLaunchPrerequisitesFor(path, fakeVsockDevice(t, dir)) + require.ErrorContains(t, err, "not usable") + }) + + t.Run("missing vsock device fails", func(t *testing.T) { + dir := t.TempDir() + err := checkLaunchPrerequisitesFor(fakeQEMUBinary(t, dir), filepath.Join(dir, "no-vhost-vsock")) + require.ErrorContains(t, err, "vsock device") + require.ErrorContains(t, err, "vhost_vsock") + }) +} diff --git a/lib/oapi/oapi.go b/lib/oapi/oapi.go index de427c557..64c9b95b4 100644 --- a/lib/oapi/oapi.go +++ b/lib/oapi/oapi.go @@ -586,9 +586,9 @@ type CapabilitiesRuntime struct { // Available Whether this runtime's launch prerequisites are currently met on // this host. Listed runtimes are supported by this server build on // this platform; available=false means a host prerequisite is - // missing (for example qemu requires a system-installed QEMU - // binary) and launches naming this runtime will fail until it is - // installed. + // missing (for example qemu requires a runnable system-installed + // QEMU binary and the host vhost-vsock device) and launches naming + // this runtime will fail until it is installed. Available bool `json:"available"` // Features Stable feature IDs supported by this runtime on this host: @@ -18878,317 +18878,318 @@ var swaggerSpec = []string{ "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", + "/0368ZPW6QEA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/openapi.yaml b/openapi.yaml index 2e24a667c..e7c49bbc7 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -1523,9 +1523,9 @@ components: Whether this runtime's launch prerequisites are currently met on this host. Listed runtimes are supported by this server build on this platform; available=false means a host prerequisite is - missing (for example qemu requires a system-installed QEMU - binary) and launches naming this runtime will fail until it is - installed. + missing (for example qemu requires a runnable system-installed + QEMU binary and the host vhost-vsock device) and launches naming + this runtime will fail until it is installed. features: type: array items: From f5a2d449e1dcc4aa29d7b72790a338ac6d5c1b08 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sat, 15 Aug 2026 18:05:47 -0400 Subject: [PATCH 06/11] Address round-4 review: truthful VZ fork capability, registry lock, bounded+coalesced QEMU probe - vz: advertise SupportsFork unconditionally. Stopped-source forks clone disks with no machine-state save/restore, so gating fork on the macOS 14 save/restore probe hid a valid macOS 13 operation. The fork feature now documents its contract precisely: it promises the stopped-source fork; forking a standby or running source restores/creates snapshots and additionally requires the standby feature (which vz still derives from the save/restore probe). Feature IDs are unchanged, so the OpenAPI/ Stainless surface only gains documentation. - hypervisor: guard the public runtime registry with an RWMutex. RegisterRuntime is public API and can run while capability requests enumerate the registry; the unsynchronized map crashed under concurrent register/enumerate (-race reproducer added). Readers snapshot the map under the lock and resolve Capabilities/LaunchCheck callbacks only after releasing it, so callbacks may safely re-enter the registry (pinned by a reentrancy test). The deprecated RegisterCapabilities wrapper is preserved unchanged. - qemu: bound and coalesce the availability probe. qemu --version now runs under a 5s context (WaitDelay backstops descendants holding the pipe), so a hung binary fails the check instead of wedging requests and leaking subprocesses. Both board registrations share one launchCheckCache: a short-TTL (1s) single-flight cache so a registry read and concurrent capability requests execute at most one probe per window, while installing QEMU or loading vhost_vsock still flips availability without a restart. Invalidate() is the explicit invalidation seam. Deterministic tests cover coalescing, expiry, error caching, invalidation, and the hung-binary deadline; tests use private cache instances, never the package global. --- lib/hypervisor/features.go | 10 +- lib/hypervisor/features_test.go | 9 + lib/hypervisor/hypervisor.go | 44 +- lib/hypervisor/qemu/launch_check_cache.go | 83 ++ .../qemu/launch_check_cache_test.go | 116 +++ lib/hypervisor/qemu/process.go | 20 +- lib/hypervisor/qemu/register_linux.go | 31 +- lib/hypervisor/qemu/register_linux_test.go | 46 +- lib/hypervisor/registry_concurrency_test.go | 89 ++ lib/hypervisor/vz/client.go | 14 +- .../vz/save_restore_support_darwin_test.go | 11 +- lib/oapi/oapi.go | 773 +++++++++--------- openapi.yaml | 24 +- 13 files changed, 837 insertions(+), 433 deletions(-) create mode 100644 lib/hypervisor/qemu/launch_check_cache.go create mode 100644 lib/hypervisor/qemu/launch_check_cache_test.go create mode 100644 lib/hypervisor/registry_concurrency_test.go diff --git a/lib/hypervisor/features.go b/lib/hypervisor/features.go index 8fdf2a3fb..8bfc98854 100644 --- a/lib/hypervisor/features.go +++ b/lib/hypervisor/features.go @@ -11,9 +11,13 @@ const ( FeatureSnapshots = "snapshots" // FeatureStandby: pause + memory snapshot, with later restore. FeatureStandby = "standby" - // FeatureFork: cloning an instance by restoring a snapshot of it. Derived - // from the explicit SupportsFork capability, which each backend keeps - // aligned with its VMStarter.PrepareFork implementation. + // FeatureFork: cloning an instance from a stopped source. Derived from + // the explicit SupportsFork capability, which each backend keeps aligned + // with its VMStarter.PrepareFork implementation. Fork of a standby or + // running source additionally restores/creates snapshots, so clients + // must gate those operations on FeatureStandby as well — vz on macOS 13 + // advertises fork (stopped-source clones need no machine-state + // save/restore) without snapshots or standby. FeatureFork = "fork" // FeaturePause: pause/resume of a running instance. FeaturePause = "pause" diff --git a/lib/hypervisor/features_test.go b/lib/hypervisor/features_test.go index 2eef497ca..b3703ff14 100644 --- a/lib/hypervisor/features_test.go +++ b/lib/hypervisor/features_test.go @@ -79,6 +79,15 @@ func TestFeatureIDs(t *testing.T) { ids := Capabilities{SupportsSnapshot: true, SupportsFork: true}.FeatureIDs() require.Equal(t, []string{FeatureSnapshots, FeatureFork}, ids) }) + + t.Run("fork without snapshots is valid and yields fork alone", func(t *testing.T) { + t.Parallel() + // vz on macOS 13: stopped-source forks clone disks without + // machine-state save/restore, so fork is advertised while snapshots + // and standby are not. Clients gate hot-source forks on standby. + ids := Capabilities{SupportsFork: true}.FeatureIDs() + require.Equal(t, []string{FeatureFork}, ids) + }) } // TestStandbySemantics pins that standby requires both snapshot and pause: a diff --git a/lib/hypervisor/hypervisor.go b/lib/hypervisor/hypervisor.go index 31e3981ff..4b0613ad4 100644 --- a/lib/hypervisor/hypervisor.go +++ b/lib/hypervisor/hypervisor.go @@ -8,8 +8,10 @@ import ( "context" "errors" "fmt" + "maps" "net" "slices" + "sync" "time" "github.com/kernel/hypeman/lib/paths" @@ -53,7 +55,17 @@ var vsockSocketNames = make(map[Type]string) // Registered by each hypervisor package's init() function. Registration is // platform-gated (see RegisterRuntime), so the map's keys double as the set // of runtimes supported by this build on the current host platform. -var runtimeRegistrations = make(map[Type]RuntimeRegistration) +// +// RegisterRuntime is public API, so custom backends may register after init +// — including while capability requests are being served. All access +// therefore goes through runtimeRegistrationsMu; readers snapshot the map +// under the lock and resolve registration callbacks only after releasing it, +// so a slow Capabilities resolver or LaunchCheck never blocks registration +// and callbacks can safely re-enter the registry. +var ( + runtimeRegistrationsMu sync.RWMutex + runtimeRegistrations = make(map[Type]RuntimeRegistration) +) // RegisterSocketName registers the socket filename for a hypervisor type. // Called by each hypervisor implementation's init() function. @@ -112,6 +124,8 @@ func RegisterRuntime(t Type, reg RuntimeRegistration) { if reg.Capabilities == nil { panic(fmt.Sprintf("hypervisor: RegisterRuntime(%s) requires a Capabilities resolver", t)) } + runtimeRegistrationsMu.Lock() + defer runtimeRegistrationsMu.Unlock() runtimeRegistrations[t] = reg } @@ -134,10 +148,14 @@ func RegisterCapabilities(t Type, caps Capabilities) { // type, resolved at call time. It reports ok=false for runtimes that cannot // run on the current host platform, because such runtimes never register. func CapabilitiesForType(t Type) (Capabilities, bool) { + runtimeRegistrationsMu.RLock() reg, ok := runtimeRegistrations[t] + runtimeRegistrationsMu.RUnlock() if !ok { return Capabilities{}, false } + // Resolved after releasing the lock: resolvers may be slow or re-enter + // the registry. return reg.Capabilities(), true } @@ -165,7 +183,14 @@ func (r RegisteredRuntime) Available() bool { return r.LaunchErr == nil } // host; per-entry Available/LaunchErr additionally reflect whether launch // prerequisites are met right now. func RegisteredRuntimes() []RegisteredRuntime { - return enumerateRuntimes(runtimeRegistrations) + runtimeRegistrationsMu.RLock() + snapshot := maps.Clone(runtimeRegistrations) + runtimeRegistrationsMu.RUnlock() + // Enumeration resolves capability and launch-check callbacks, so it runs + // on a snapshot taken under the lock rather than on the live map: + // concurrent RegisterRuntime calls must never race the iteration, and + // callbacks must never execute while the registry lock is held. + return enumerateRuntimes(snapshot) } // enumerateRuntimes resolves the map's entries (capabilities and launch @@ -329,11 +354,16 @@ type Capabilities struct { // SupportsSnapshot indicates if Snapshot/Restore are available SupportsSnapshot bool - // SupportsFork indicates if snapshot-based instance forking is available. - // It is explicit rather than derived from SupportsSnapshot because - // VMStarter.PrepareFork allows a snapshot-capable backend to return - // ErrNotSupported; the advertised feature must track what PrepareFork - // actually implements, not what snapshots imply. + // SupportsFork indicates VMStarter.PrepareFork implements instance + // forking, which every fork source state needs. It is explicit rather + // than derived from SupportsSnapshot because the two are independent in + // both directions: a snapshot-capable backend may still reject + // PrepareFork with ErrNotSupported, and a backend can fork a stopped + // source (a disk clone, no machine-state snapshot involved) without + // snapshot support at all — vz does exactly that on macOS 13. Forking a + // standby or running source additionally restores/creates snapshots, so + // those operations require SupportsStandby() as well; SupportsFork alone + // promises only the stopped-source fork. SupportsFork bool // SupportsHotplugMemory indicates if ResizeMemory is available diff --git a/lib/hypervisor/qemu/launch_check_cache.go b/lib/hypervisor/qemu/launch_check_cache.go new file mode 100644 index 000000000..88b52c8e1 --- /dev/null +++ b/lib/hypervisor/qemu/launch_check_cache.go @@ -0,0 +1,83 @@ +package qemu + +import ( + "sync" + "time" +) + +// launchCheckCacheTTL is how long a launch-prerequisite result stays valid. +// It is deliberately short: long enough that the standard and microvm +// registrations (and a burst of concurrent capability requests) share a +// single `qemu --version` execution, but short enough that installing QEMU or +// loading vhost_vsock flips availability promptly without a server restart. +const launchCheckCacheTTL = time.Second + +// launchCheckCache coalesces and briefly caches a launch-prerequisite probe. +// The probe executes the QEMU binary, so running it once per registry entry +// per request is wasteful and, with a hung binary, dangerous: both QEMU +// registrations share one cache instance, concurrent callers wait on a +// single in-flight probe instead of spawning duplicates, and results (success +// or failure alike) expire after ttl so repaired host prerequisites become +// visible on the next read. +type launchCheckCache struct { + probe func() error + ttl time.Duration + now func() time.Time // seam for deterministic expiry tests + + mu sync.Mutex + inflight chan struct{} // non-nil while a probe runs; closed on completion + valid bool + result error + expires time.Time +} + +func newLaunchCheckCache(probe func() error, ttl time.Duration) *launchCheckCache { + return &launchCheckCache{probe: probe, ttl: ttl, now: time.Now} +} + +// Check returns the cached probe result, refreshing it when expired. Exactly +// one caller runs the probe at a time; concurrent callers needing a fresh +// result block until the in-flight probe completes and then share it rather +// than spawning duplicates. The probe runs outside the lock, so reads that +// hit a still-valid entry are never blocked by a slow probe. +func (c *launchCheckCache) Check() error { + for { + c.mu.Lock() + if c.valid && c.now().Before(c.expires) { + err := c.result + c.mu.Unlock() + return err + } + if c.inflight != nil { + wait := c.inflight + c.mu.Unlock() + <-wait + // Re-read: the finished probe populated the cache (or another + // refresh is already underway). + continue + } + done := make(chan struct{}) + c.inflight = done + c.mu.Unlock() + + err := c.probe() + + c.mu.Lock() + c.valid = true + c.result = err + c.expires = c.now().Add(c.ttl) + c.inflight = nil + c.mu.Unlock() + close(done) + return err + } +} + +// Invalidate drops any cached result so the next Check re-probes. It is the +// explicit invalidation seam for callers (and tests) that know host state +// changed and should not wait out the TTL. +func (c *launchCheckCache) Invalidate() { + c.mu.Lock() + c.valid = false + c.mu.Unlock() +} diff --git a/lib/hypervisor/qemu/launch_check_cache_test.go b/lib/hypervisor/qemu/launch_check_cache_test.go new file mode 100644 index 000000000..1817ca3fd --- /dev/null +++ b/lib/hypervisor/qemu/launch_check_cache_test.go @@ -0,0 +1,116 @@ +package qemu + +import ( + "errors" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +// newTestCache builds a private cache instance around the given probe with a +// manually advanced clock. Tests never touch the package's shared +// launchPrereqCache, so parallel tests (and the real registrations) are +// unaffected. +func newTestCache(probe func() error, ttl time.Duration) (*launchCheckCache, *time.Time) { + c := newLaunchCheckCache(probe, ttl) + now := time.Unix(1700000000, 0) + c.now = func() time.Time { return now } + return c, &now +} + +// TestLaunchCheckCacheCoalescesConcurrentProbes pins that concurrent Check +// calls on an empty cache share a single in-flight probe: the standard and +// microvm registrations plus a burst of capability requests must not each +// execute `qemu --version`. +func TestLaunchCheckCacheCoalescesConcurrentProbes(t *testing.T) { + t.Parallel() + + const callers = 16 + var probes atomic.Int32 + release := make(chan struct{}) + entered := make(chan struct{}) + cache, _ := newTestCache(func() error { + probes.Add(1) + close(entered) // panics if a duplicate probe ever starts + <-release + return nil + }, time.Minute) + + var wg sync.WaitGroup + results := make([]error, callers) + for i := range callers { + wg.Add(1) + go func() { + defer wg.Done() + results[i] = cache.Check() + }() + } + <-entered + close(release) + wg.Wait() + + require.Equal(t, int32(1), probes.Load(), "concurrent callers must coalesce onto one probe") + for i := range callers { + require.NoError(t, results[i], "every waiter must receive the shared probe result") + } +} + +// TestLaunchCheckCacheExpiry pins TTL semantics: results (successes and +// failures alike) are served from cache within the TTL and re-probed after it +// elapses, so a repaired host prerequisite becomes visible without a restart. +func TestLaunchCheckCacheExpiry(t *testing.T) { + t.Parallel() + + probeErr := errors.New("qemu binary missing") + var result error = probeErr + var probes int + cache, now := newTestCache(func() error { + probes++ + return result + }, time.Second) + + require.ErrorIs(t, cache.Check(), probeErr) + require.Equal(t, 1, probes) + + // Within the TTL the cached failure is served without re-probing. + *now = now.Add(500 * time.Millisecond) + require.ErrorIs(t, cache.Check(), probeErr) + require.Equal(t, 1, probes, "a fresh result must be served from cache") + + // After the TTL the probe runs again and a repaired prerequisite + // (installed binary) is reported. + result = nil + *now = now.Add(time.Second) + require.NoError(t, cache.Check()) + require.Equal(t, 2, probes, "an expired result must be re-probed") + + // A subsequent failure is likewise picked up after expiry. + result = probeErr + *now = now.Add(2 * time.Second) + require.ErrorIs(t, cache.Check(), probeErr) + require.Equal(t, 3, probes) +} + +// TestLaunchCheckCacheInvalidate pins the explicit invalidation seam: callers +// that know host state changed can force the next Check to re-probe without +// waiting out the TTL. +func TestLaunchCheckCacheInvalidate(t *testing.T) { + t.Parallel() + + var probes int + cache, _ := newTestCache(func() error { + probes++ + return nil + }, time.Hour) + + require.NoError(t, cache.Check()) + require.NoError(t, cache.Check()) + require.Equal(t, 1, probes, "an unexpired result must be served from cache") + + cache.Invalidate() + require.NoError(t, cache.Check()) + require.Equal(t, 2, probes, "Invalidate must force the next Check to re-probe") +} diff --git a/lib/hypervisor/qemu/process.go b/lib/hypervisor/qemu/process.go index 2be1ed17b..153da5505 100644 --- a/lib/hypervisor/qemu/process.go +++ b/lib/hypervisor/qemu/process.go @@ -135,16 +135,28 @@ func (s *Starter) detectVersion(p *paths.Paths) (string, error) { if err != nil { return "", err } - return versionFromBinary(binaryPath) + ctx, cancel := context.WithTimeout(context.Background(), versionProbeTimeout) + defer cancel() + return versionFromBinary(ctx, binaryPath) } +// versionProbeTimeout bounds a single `qemu --version` execution. The probe +// runs on capability requests (launch checks) as well as launches, so a hung +// or broken binary must fail the caller promptly instead of leaking +// subprocesses and goroutines indefinitely. +const versionProbeTimeout = 5 * time.Second + // versionFromBinary runs the resolved QEMU binary's --version and parses the // installed version from its output. It is the version probe behind // GetVersion/ResolveVersion — every cold start persists its result — and the // capability registry's launch check reuses it so "available" means the same -// binary execution that launches require actually succeeds. -func versionFromBinary(binaryPath string) (string, error) { - cmd := exec.Command(binaryPath, "--version") +// binary execution that launches require actually succeeds. Execution is +// bounded by ctx (callers pass a versionProbeTimeout-derived context), and +// WaitDelay ensures a killed probe whose descendants still hold the output +// pipe cannot block the caller past the deadline. +func versionFromBinary(ctx context.Context, binaryPath string) (string, error) { + cmd := exec.CommandContext(ctx, binaryPath, "--version") + cmd.WaitDelay = time.Second output, err := cmd.Output() if err != nil { return "", fmt.Errorf("get qemu version: %w", err) diff --git a/lib/hypervisor/qemu/register_linux.go b/lib/hypervisor/qemu/register_linux.go index 6e8ab8672..65b07c930 100644 --- a/lib/hypervisor/qemu/register_linux.go +++ b/lib/hypervisor/qemu/register_linux.go @@ -3,6 +3,7 @@ package qemu import ( + "context" "fmt" "os" @@ -29,27 +30,39 @@ const vhostVsockDevicePath = "/dev/vhost-vsock" func init() { hypervisor.RegisterRuntime(hypervisor.TypeQEMU, hypervisor.RuntimeRegistration{ Capabilities: StandardProfile{}.capabilities, - LaunchCheck: checkLaunchPrerequisites, + LaunchCheck: launchPrereqCache.Check, }) if _, err := microVMMachineType(); err == nil { hypervisor.RegisterRuntime(hypervisor.TypeQEMUMicroVM, hypervisor.RuntimeRegistration{ Capabilities: MicroVMProfile{}.capabilities, - LaunchCheck: checkLaunchPrerequisites, + LaunchCheck: launchPrereqCache.Check, }) } } +// launchPrereqCache is the single shared launch-prerequisite result for both +// QEMU boards: the standard and microvm registrations verify the same host +// prerequisites (the same system binary and vsock device), so they share one +// briefly-cached probe instead of each executing `qemu --version` on every +// registry read — and concurrent capability requests coalesce onto one +// in-flight probe. The short TTL keeps availability live: installing QEMU or +// loading vhost_vsock flips it within launchCheckCacheTTL, no restart needed. +var launchPrereqCache = newLaunchCheckCache(checkLaunchPrerequisites, launchCheckCacheTTL) + // checkLaunchPrerequisites is the capability registry's side-effect-free -// launch check for both QEMU boards: it resolves the system binary with the -// same lookup launches use, then verifies the prerequisites every ordinary -// launch needs. Evaluated per registry read, so installing QEMU or loading -// vhost_vsock flips availability without a restart. +// launch check for both QEMU boards (via launchPrereqCache): it resolves the +// system binary with the same lookup launches use, then verifies the +// prerequisites every ordinary launch needs. The version probe it runs is +// bounded by versionProbeTimeout so a hung binary fails the check instead of +// wedging capability requests. func checkLaunchPrerequisites() error { binaryPath, err := (&Starter{}).GetBinaryPath(nil, "") if err != nil { return err } - return checkLaunchPrerequisitesFor(binaryPath, vhostVsockDevicePath) + ctx, cancel := context.WithTimeout(context.Background(), versionProbeTimeout) + defer cancel() + return checkLaunchPrerequisitesFor(ctx, binaryPath, vhostVsockDevicePath) } // checkLaunchPrerequisitesFor verifies that a resolved QEMU binary and the @@ -65,11 +78,11 @@ func checkLaunchPrerequisites() error { // // Split from checkLaunchPrerequisites so unavailable cases are testable with // fake binaries and device paths regardless of the host's QEMU install. -func checkLaunchPrerequisitesFor(binaryPath, vsockDevicePath string) error { +func checkLaunchPrerequisitesFor(ctx context.Context, binaryPath, vsockDevicePath string) error { if err := validateExecutable(binaryPath); err != nil { return fmt.Errorf("qemu binary %s is not executable: %w", binaryPath, err) } - if _, err := versionFromBinary(binaryPath); err != nil { + if _, err := versionFromBinary(ctx, binaryPath); err != nil { return fmt.Errorf("qemu binary %s is not usable: %w", binaryPath, err) } if _, err := os.Stat(vsockDevicePath); err != nil { diff --git a/lib/hypervisor/qemu/register_linux_test.go b/lib/hypervisor/qemu/register_linux_test.go index f5f5dcf8a..d81160168 100644 --- a/lib/hypervisor/qemu/register_linux_test.go +++ b/lib/hypervisor/qemu/register_linux_test.go @@ -3,10 +3,12 @@ package qemu import ( + "context" "os" "path/filepath" "runtime" "testing" + "time" "github.com/kernel/hypeman/lib/hypervisor" "github.com/stretchr/testify/require" @@ -53,6 +55,17 @@ func TestQEMUAvailabilityTracksLaunchPrerequisites(t *testing.T) { require.GreaterOrEqual(t, checked, 1, "standard qemu must be registered on Linux") } +// TestLaunchCheckCacheSharedByRegistrations pins that both QEMU registry +// entries resolve availability through the same shared cache instance with +// the short production TTL, so one registry read (which checks both boards) +// executes at most one probe per TTL window while repaired prerequisites +// still surface promptly. +func TestLaunchCheckCacheSharedByRegistrations(t *testing.T) { + t.Parallel() + require.NotNil(t, launchPrereqCache) + require.Equal(t, launchCheckCacheTTL, launchPrereqCache.ttl) +} + // fakeQEMUBinary writes an executable script that mimics `qemu --version` // output, standing in for a working system QEMU. func fakeQEMUBinary(t *testing.T, dir string) string { @@ -80,14 +93,16 @@ func fakeVsockDevice(t *testing.T, dir string) string { func TestCheckLaunchPrerequisitesFor(t *testing.T) { t.Parallel() + ctx := context.Background() + t.Run("working binary and device pass", func(t *testing.T) { dir := t.TempDir() - require.NoError(t, checkLaunchPrerequisitesFor(fakeQEMUBinary(t, dir), fakeVsockDevice(t, dir))) + require.NoError(t, checkLaunchPrerequisitesFor(ctx, fakeQEMUBinary(t, dir), fakeVsockDevice(t, dir))) }) t.Run("missing binary fails", func(t *testing.T) { dir := t.TempDir() - err := checkLaunchPrerequisitesFor(filepath.Join(dir, "missing"), fakeVsockDevice(t, dir)) + err := checkLaunchPrerequisitesFor(ctx, filepath.Join(dir, "missing"), fakeVsockDevice(t, dir)) require.ErrorContains(t, err, "not executable") }) @@ -95,7 +110,7 @@ func TestCheckLaunchPrerequisitesFor(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "qemu-system-fake") require.NoError(t, os.WriteFile(path, []byte("#!/bin/sh\necho 'QEMU emulator version 8.2.0'\n"), 0o644)) - err := checkLaunchPrerequisitesFor(path, fakeVsockDevice(t, dir)) + err := checkLaunchPrerequisitesFor(ctx, path, fakeVsockDevice(t, dir)) require.ErrorContains(t, err, "not executable") }) @@ -103,7 +118,7 @@ func TestCheckLaunchPrerequisitesFor(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "qemu-system-fake") require.NoError(t, os.WriteFile(path, []byte("#!/bin/sh\nexit 1\n"), 0o755)) - err := checkLaunchPrerequisitesFor(path, fakeVsockDevice(t, dir)) + err := checkLaunchPrerequisitesFor(ctx, path, fakeVsockDevice(t, dir)) require.ErrorContains(t, err, "not usable") }) @@ -111,14 +126,33 @@ func TestCheckLaunchPrerequisitesFor(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "qemu-system-fake") require.NoError(t, os.WriteFile(path, []byte("#!/bin/sh\necho 'not qemu'\n"), 0o755)) - err := checkLaunchPrerequisitesFor(path, fakeVsockDevice(t, dir)) + err := checkLaunchPrerequisitesFor(ctx, path, fakeVsockDevice(t, dir)) require.ErrorContains(t, err, "not usable") }) t.Run("missing vsock device fails", func(t *testing.T) { dir := t.TempDir() - err := checkLaunchPrerequisitesFor(fakeQEMUBinary(t, dir), filepath.Join(dir, "no-vhost-vsock")) + err := checkLaunchPrerequisitesFor(ctx, fakeQEMUBinary(t, dir), filepath.Join(dir, "no-vhost-vsock")) require.ErrorContains(t, err, "vsock device") require.ErrorContains(t, err, "vhost_vsock") }) + + t.Run("hung binary fails at the context deadline", func(t *testing.T) { + // A wedged QEMU binary must fail the prerequisite check when its + // bounded context expires instead of blocking the capability request + // (and leaking a subprocess) indefinitely. `exec sleep` replaces the + // shell so the kill hits the sleeping process directly, and + // versionFromBinary's WaitDelay backstops any descendant that still + // holds the output pipe. + dir := t.TempDir() + path := filepath.Join(dir, "qemu-system-fake") + require.NoError(t, os.WriteFile(path, []byte("#!/bin/sh\nexec sleep 60\n"), 0o755)) + shortCtx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) + defer cancel() + start := time.Now() + err := checkLaunchPrerequisitesFor(shortCtx, path, fakeVsockDevice(t, dir)) + require.ErrorContains(t, err, "not usable") + require.Less(t, time.Since(start), 10*time.Second, + "a hung probe must return at the deadline, not run to completion") + }) } diff --git a/lib/hypervisor/registry_concurrency_test.go b/lib/hypervisor/registry_concurrency_test.go new file mode 100644 index 000000000..bed33d0d1 --- /dev/null +++ b/lib/hypervisor/registry_concurrency_test.go @@ -0,0 +1,89 @@ +package hypervisor + +import ( + "fmt" + "sync" + "testing" + + "github.com/stretchr/testify/require" +) + +// TestRegistryConcurrentRegisterAndEnumerate exercises the public registry +// under concurrent registration and enumeration. RegisterRuntime is public +// API, so custom backends may register while capability requests iterate the +// registry; without synchronization this crashes with "concurrent map +// iteration and map write" (and the race detector reports it). Registered +// types use unique test-only names so production registrations shared with +// unrelated parallel tests are never mutated. +func TestRegistryConcurrentRegisterAndEnumerate(t *testing.T) { + t.Parallel() + + const writers = 8 + const iterations = 50 + + var wg sync.WaitGroup + for w := range writers { + wg.Add(2) + go func() { + defer wg.Done() + for i := range iterations { + typ := Type(fmt.Sprintf("registry-concurrency-test-%d-%d", w, i)) + RegisterRuntime(typ, RuntimeRegistration{ + Capabilities: func() Capabilities { return Capabilities{SupportsVsock: true} }, + LaunchCheck: func() error { return nil }, + }) + caps, ok := CapabilitiesForType(typ) + require.True(t, ok) + require.True(t, caps.SupportsVsock) + } + }() + go func() { + defer wg.Done() + for range iterations { + for _, rt := range RegisteredRuntimes() { + _ = rt.Available() + } + _, _ = CapabilitiesForType(Type("registry-concurrency-test-missing")) + } + }() + } + wg.Wait() +} + +// TestRegistryCallbacksRunOutsideLock pins that enumeration resolves +// registration callbacks after releasing the registry lock: a Capabilities +// resolver or LaunchCheck that re-enters the registry — including taking the +// write lock via RegisterRuntime — must not deadlock. If callbacks executed +// under even a read lock, the nested RegisterRuntime would block forever. +func TestRegistryCallbacksRunOutsideLock(t *testing.T) { + t.Parallel() + + reentrant := Type("registry-reentrant-callback-test") + nested := Type("registry-reentrant-callback-test-nested") + RegisterRuntime(reentrant, RuntimeRegistration{ + Capabilities: func() Capabilities { + RegisterRuntime(nested, RuntimeRegistration{ + Capabilities: func() Capabilities { return Capabilities{SupportsPause: true} }, + }) + return Capabilities{SupportsVsock: true} + }, + LaunchCheck: func() error { + _, _ = CapabilitiesForType(nested) + return nil + }, + }) + + found := false + for _, rt := range RegisteredRuntimes() { + if rt.Type == reentrant { + found = true + require.True(t, rt.Capabilities.SupportsVsock) + require.True(t, rt.Available()) + } + } + require.True(t, found, "re-entrant registration must not deadlock or drop the runtime") + + caps, ok := CapabilitiesForType(nested) + require.True(t, ok, "registration performed inside a resolver must land in the registry") + require.True(t, caps.SupportsPause) +} diff --git a/lib/hypervisor/vz/client.go b/lib/hypervisor/vz/client.go index 6fbbcee4a..53e56f4ae 100644 --- a/lib/hypervisor/vz/client.go +++ b/lib/hypervisor/vz/client.go @@ -87,10 +87,16 @@ func capabilities() hypervisor.Capabilities { // Silicon AND macOS 14+. An arm64-only check would overstate // support on macOS 13 hosts. SupportsSnapshot: saveRestoreSupported(), - // PrepareFork rewrites the snapshot manifest (fork.go), but forking - // restores a snapshot of the source, so fork is gated by the same - // save/restore probe as snapshots. - SupportsFork: saveRestoreSupported(), + // PrepareFork is implemented for every source state (fork.go): a + // stopped-source fork clones disks with no machine-state snapshot + // involved, so it works even where Virtualization.framework lacks VM + // save/restore (macOS 13). Gating fork on the save/restore probe + // would hide that valid operation. Forking a standby or running + // source does restore/create snapshots, but that is the documented + // contract of the fork feature itself: those source states + // additionally require the standby feature, which this backend + // derives from the save/restore probe (SupportsSnapshot above). + SupportsFork: true, SupportsHotplugMemory: false, SupportsBalloonControl: true, SupportsPause: true, diff --git a/lib/hypervisor/vz/save_restore_support_darwin_test.go b/lib/hypervisor/vz/save_restore_support_darwin_test.go index a057419d9..731e9112e 100644 --- a/lib/hypervisor/vz/save_restore_support_darwin_test.go +++ b/lib/hypervisor/vz/save_restore_support_darwin_test.go @@ -16,14 +16,17 @@ func TestCapabilitiesDeriveSnapshotFromHost(t *testing.T) { macOSProductVersion = func() string { return "13.6" } require.False(t, capabilities().SupportsSnapshot, "macOS 13 must not advertise snapshot/standby support") - require.False(t, capabilities().SupportsFork, "fork restores a snapshot, so macOS 13 must not advertise it") + require.True(t, capabilities().SupportsFork, + "stopped-source forks clone disks without machine-state save/restore, so macOS 13 must still advertise fork") + require.False(t, capabilities().SupportsStandby(), + "macOS 13 must not advertise standby, so hot-source forks (which require standby) are not promised") macOSProductVersion = func() string { return "14.5" } require.Equal(t, saveRestoreSupported(), capabilities().SupportsSnapshot) - require.Equal(t, capabilities().SupportsSnapshot, capabilities().SupportsFork, - "vz PrepareFork rewrites snapshot manifests, so fork must track the same save/restore probe") + require.True(t, capabilities().SupportsFork, "vz PrepareFork is implemented for every source state") macOSProductVersion = func() string { return "" } require.False(t, capabilities().SupportsSnapshot, "failed version probe must not advertise snapshot support") - require.False(t, capabilities().SupportsFork, "failed version probe must not advertise fork support") + require.True(t, capabilities().SupportsFork, + "fork does not depend on the save/restore probe: stopped-source forks work regardless") } diff --git a/lib/oapi/oapi.go b/lib/oapi/oapi.go index 64c9b95b4..52ab3b2c0 100644 --- a/lib/oapi/oapi.go +++ b/lib/oapi/oapi.go @@ -593,17 +593,19 @@ type CapabilitiesRuntime struct { // Features Stable feature IDs supported by this runtime on this host: // "snapshots" (snapshot/restore), "standby" (pause + memory - // snapshot, with later restore), "fork" (clone an instance by - // restoring a snapshot of it), "pause" (pause/resume), - // "hotplug-memory" (live memory resize), "balloon-control" (runtime - // balloon target changes), "vsock" (guest vsock communication), - // "gpu-passthrough" (GPU/PCI device passthrough), "disk-io-limit" - // (disk I/O rate limiting), "disk-resize" (live disk resize). - // Values are host- and configuration-truthful: vz omits snapshots, - // standby, and fork on macOS 13, which lacks - // Virtualization.framework VM save/restore, and cloud-hypervisor - // reports "disk-resize" only when the configured default version - // supports it. + // snapshot, with later restore), "fork" (clone an instance from a + // stopped source; forking a standby or running source + // restores/creates snapshots and additionally requires "standby"), + // "pause" (pause/resume), "hotplug-memory" (live memory resize), + // "balloon-control" (runtime balloon target changes), "vsock" + // (guest vsock communication), "gpu-passthrough" (GPU/PCI device + // passthrough), "disk-io-limit" (disk I/O rate limiting), + // "disk-resize" (live disk resize). Values are host- and + // configuration-truthful: vz omits snapshots and standby on macOS + // 13, which lacks Virtualization.framework VM save/restore, while + // still advertising fork (stopped-source clones need no + // save/restore there), and cloud-hypervisor reports "disk-resize" + // only when the configured default version supports it. Features []string `json:"features"` // Name Runtime identifier @@ -18816,380 +18818,381 @@ func (sh *strictHandler) GetVolume(w http.ResponseWriter, r *http.Request, id st // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "/0368ZPW6QEA", + "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", + "eSdugOoBAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/openapi.yaml b/openapi.yaml index e7c49bbc7..b117d2035 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -1533,17 +1533,19 @@ components: description: | Stable feature IDs supported by this runtime on this host: "snapshots" (snapshot/restore), "standby" (pause + memory - snapshot, with later restore), "fork" (clone an instance by - restoring a snapshot of it), "pause" (pause/resume), - "hotplug-memory" (live memory resize), "balloon-control" (runtime - balloon target changes), "vsock" (guest vsock communication), - "gpu-passthrough" (GPU/PCI device passthrough), "disk-io-limit" - (disk I/O rate limiting), "disk-resize" (live disk resize). - Values are host- and configuration-truthful: vz omits snapshots, - standby, and fork on macOS 13, which lacks - Virtualization.framework VM save/restore, and cloud-hypervisor - reports "disk-resize" only when the configured default version - supports it. + snapshot, with later restore), "fork" (clone an instance from a + stopped source; forking a standby or running source + restores/creates snapshots and additionally requires "standby"), + "pause" (pause/resume), "hotplug-memory" (live memory resize), + "balloon-control" (runtime balloon target changes), "vsock" + (guest vsock communication), "gpu-passthrough" (GPU/PCI device + passthrough), "disk-io-limit" (disk I/O rate limiting), + "disk-resize" (live disk resize). Values are host- and + configuration-truthful: vz omits snapshots and standby on macOS + 13, which lacks Virtualization.framework VM save/restore, while + still advertising fork (stopped-source clones need no + save/restore there), and cloud-hypervisor reports "disk-resize" + only when the configured default version supports it. CapabilitiesNetwork: type: object From 2260f64c282f879d74a741290ca63c84e36c29d1 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sat, 15 Aug 2026 18:34:49 -0400 Subject: [PATCH 07/11] Kill the QEMU version probe's whole process group on timeout The bounded probe used exec.CommandContext's default cancel, which signals only the direct child: a wrapper binary that spawned a descendant (sleep 60 & wait) returned at the deadline but left the descendant orphaned under PID 1, and WaitDelay only unblocked the pipe read. Run the probe in a dedicated process group (Setpgid) and SIGKILL the group (-pid) on cancellation so descendants die and are reaped by init; keep WaitDelay as a backstop for a descendant that setsid'd out of the group while holding the output pipe. TestVersionFromBinaryKillsProcessGroupOnTimeout pins the property with a wrapper that records both PIDs before hanging, asserting the wrapper and its descendant are both gone after the probe's context deadline (fails without the group kill). --- lib/hypervisor/qemu/process.go | 20 ++++++-- lib/hypervisor/qemu/process_test.go | 57 ++++++++++++++++++++++ lib/hypervisor/qemu/register_linux_test.go | 6 +-- 3 files changed, 77 insertions(+), 6 deletions(-) diff --git a/lib/hypervisor/qemu/process.go b/lib/hypervisor/qemu/process.go index 153da5505..4c9f0dadf 100644 --- a/lib/hypervisor/qemu/process.go +++ b/lib/hypervisor/qemu/process.go @@ -4,6 +4,7 @@ package qemu import ( "context" "encoding/json" + "errors" "fmt" "net" "os" @@ -151,11 +152,24 @@ const versionProbeTimeout = 5 * time.Second // GetVersion/ResolveVersion — every cold start persists its result — and the // capability registry's launch check reuses it so "available" means the same // binary execution that launches require actually succeeds. Execution is -// bounded by ctx (callers pass a versionProbeTimeout-derived context), and -// WaitDelay ensures a killed probe whose descendants still hold the output -// pipe cannot block the caller past the deadline. +// bounded by ctx (callers pass a versionProbeTimeout-derived context). The +// probe runs in its own process group and cancellation SIGKILLs the whole +// group: the default CommandContext cancel only signals the direct child, +// which would orphan descendants (e.g. a wrapper script's children) past the +// deadline. WaitDelay backstops the rare descendant that escaped the group +// (setsid) yet still holds the output pipe. func versionFromBinary(ctx context.Context, binaryPath string) (string, error) { cmd := exec.CommandContext(ctx, binaryPath, "--version") + cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} + cmd.Cancel = func() error { + // Setpgid makes the child's pgid its own pid; signal the group so + // descendants die (and get reaped by init) with the probe. + err := syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) + if errors.Is(err, syscall.ESRCH) { + return os.ErrProcessDone + } + return err + } cmd.WaitDelay = time.Second output, err := cmd.Output() if err != nil { diff --git a/lib/hypervisor/qemu/process_test.go b/lib/hypervisor/qemu/process_test.go index 2a9a10130..6c5024774 100644 --- a/lib/hypervisor/qemu/process_test.go +++ b/lib/hypervisor/qemu/process_test.go @@ -1,11 +1,15 @@ package qemu import ( + "context" "errors" "os" "os/exec" "path/filepath" "regexp" + "strconv" + "strings" + "syscall" "testing" "time" @@ -106,6 +110,59 @@ func TestGetVersion_ParsesVersionCorrectly(t *testing.T) { } } +// TestVersionFromBinaryKillsProcessGroupOnTimeout pins that a cancelled +// version probe terminates its entire process tree, not just the direct +// child. A QEMU wrapper script that spawns a descendant (`sleep 60 & wait`) +// must leave neither the wrapper nor the descendant behind after the probe's +// context deadline; otherwise every capability request against a wedged +// binary would orphan a subprocess under PID 1. +func TestVersionFromBinaryKillsProcessGroupOnTimeout(t *testing.T) { + t.Parallel() + + dir := t.TempDir() + parentPIDFile := filepath.Join(dir, "parent.pid") + childPIDFile := filepath.Join(dir, "child.pid") + binaryPath := filepath.Join(dir, "qemu-system-fake") + // Write both PIDs before blocking so the test can always observe them, + // then hang in `wait` with a background descendant — the shape that + // leaks when cancellation only signals the direct child. + script := "#!/bin/sh\n" + + "echo $$ > " + parentPIDFile + "\n" + + "sleep 60 &\n" + + "echo $! > " + childPIDFile + "\n" + + "wait\n" + require.NoError(t, os.WriteFile(binaryPath, []byte(script), 0o755)) + + ctx, cancel := context.WithTimeout(context.Background(), 250*time.Millisecond) + defer cancel() + start := time.Now() + _, err := versionFromBinary(ctx, binaryPath) + require.Error(t, err, "a hung probe must fail at the deadline") + require.Less(t, time.Since(start), 10*time.Second, + "a hung probe must return at the deadline, not run to completion") + + readPID := func(path string) int { + data, readErr := os.ReadFile(path) + require.NoError(t, readErr, "probe script must have recorded %s before hanging", path) + pid, convErr := strconv.Atoi(strings.TrimSpace(string(data))) + require.NoError(t, convErr) + return pid + } + parentPID := readPID(parentPIDFile) + childPID := readPID(childPIDFile) + + processGone := func(pid int) bool { + // Signal 0 probes existence; a killed-but-unreaped descendant still + // answers until init reaps it, hence Eventually below. + return errors.Is(syscall.Kill(pid, 0), syscall.ESRCH) + } + require.Eventually(t, func() bool { + return processGone(parentPID) && processGone(childPID) + }, 5*time.Second, 20*time.Millisecond, + "both the probe wrapper (pid %d) and its descendant (pid %d) must be killed with the process group", + parentPID, childPID) +} + func TestSaveAndLoadVMConfigPreservesRestoreContract(t *testing.T) { dir := t.TempDir() want := savedVMConfig{ diff --git a/lib/hypervisor/qemu/register_linux_test.go b/lib/hypervisor/qemu/register_linux_test.go index d81160168..4c04a26c3 100644 --- a/lib/hypervisor/qemu/register_linux_test.go +++ b/lib/hypervisor/qemu/register_linux_test.go @@ -141,9 +141,9 @@ func TestCheckLaunchPrerequisitesFor(t *testing.T) { // A wedged QEMU binary must fail the prerequisite check when its // bounded context expires instead of blocking the capability request // (and leaking a subprocess) indefinitely. `exec sleep` replaces the - // shell so the kill hits the sleeping process directly, and - // versionFromBinary's WaitDelay backstops any descendant that still - // holds the output pipe. + // shell so this covers the direct-child case; descendant cleanup is + // pinned by TestVersionFromBinaryKillsProcessGroupOnTimeout, which + // asserts versionFromBinary's group kill reaps the whole tree. dir := t.TempDir() path := filepath.Join(dir, "qemu-system-fake") require.NoError(t, os.WriteFile(path, []byte("#!/bin/sh\nexec sleep 60\n"), 0o755)) From 66a7fa1e83243fd73127db1011de7e00b2c5b193 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sat, 15 Aug 2026 19:17:38 -0400 Subject: [PATCH 08/11] Kill the QEMU version probe's process group on every completion path cmd.Cancel only fires on context cancellation, so a wrapper binary that exits before the deadline while leaving a background descendant holding the stdout pipe leaked that descendant on every probe: Output returned after WaitDelay with ErrWaitDelay, but nothing reaped the group. Kill the probe's process group unconditionally after Output returns (no-op ESRCH when the group is already empty) and pin the behavior with a regression test where the wrapper prints a valid version, backgrounds sleep 60, and exits immediately. --- lib/hypervisor/qemu/process.go | 21 +++++++--- lib/hypervisor/qemu/process_test.go | 49 ++++++++++++++++++++++ lib/hypervisor/qemu/register_linux_test.go | 6 ++- 3 files changed, 69 insertions(+), 7 deletions(-) diff --git a/lib/hypervisor/qemu/process.go b/lib/hypervisor/qemu/process.go index 4c9f0dadf..c02c5beef 100644 --- a/lib/hypervisor/qemu/process.go +++ b/lib/hypervisor/qemu/process.go @@ -153,11 +153,14 @@ const versionProbeTimeout = 5 * time.Second // capability registry's launch check reuses it so "available" means the same // binary execution that launches require actually succeeds. Execution is // bounded by ctx (callers pass a versionProbeTimeout-derived context). The -// probe runs in its own process group and cancellation SIGKILLs the whole -// group: the default CommandContext cancel only signals the direct child, -// which would orphan descendants (e.g. a wrapper script's children) past the -// deadline. WaitDelay backstops the rare descendant that escaped the group -// (setsid) yet still holds the output pipe. +// probe runs in its own process group, and the whole group is SIGKILLed on +// every completion path: the default CommandContext cancel only signals the +// direct child (orphaning a wrapper script's children past the deadline), +// and Cancel never fires at all when the direct child exits before the +// deadline — a wrapper that leaves a background descendant holding the +// output pipe would otherwise leak that descendant on every probe, with +// WaitDelay merely unblocking Output rather than cleaning up. ESRCH from the +// group kill means every group member already exited. func versionFromBinary(ctx context.Context, binaryPath string) (string, error) { cmd := exec.CommandContext(ctx, binaryPath, "--version") cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} @@ -172,6 +175,14 @@ func versionFromBinary(ctx context.Context, binaryPath string) (string, error) { } cmd.WaitDelay = time.Second output, err := cmd.Output() + if cmd.Process != nil { + // Kill the group on every completion path, not only via cmd.Cancel: + // Cancel never runs when the direct child exits before the context + // deadline, so descendants it left behind (still pinning the child's + // pid as their pgid) must be reaped here. A no-op ESRCH when the + // group is already empty. + _ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) + } if err != nil { return "", fmt.Errorf("get qemu version: %w", err) } diff --git a/lib/hypervisor/qemu/process_test.go b/lib/hypervisor/qemu/process_test.go index 6c5024774..2811f170f 100644 --- a/lib/hypervisor/qemu/process_test.go +++ b/lib/hypervisor/qemu/process_test.go @@ -163,6 +163,55 @@ func TestVersionFromBinaryKillsProcessGroupOnTimeout(t *testing.T) { parentPID, childPID) } +// TestVersionFromBinaryKillsDescendantsWhenWrapperExitsFirst pins the +// completion-path group kill. cmd.Cancel only fires on context cancellation, +// so when the direct wrapper prints a valid version and exits immediately +// while a background descendant keeps the inherited stdout pipe open, no +// cancellation ever runs: Output merely unblocks after WaitDelay with +// ErrWaitDelay and — before the fix — the descendant survived, so every +// capability-cache refresh against such a binary leaked one process. +func TestVersionFromBinaryKillsDescendantsWhenWrapperExitsFirst(t *testing.T) { + t.Parallel() + + dir := t.TempDir() + childPIDFile := filepath.Join(dir, "child.pid") + binaryPath := filepath.Join(dir, "qemu-system-fake") + // The wrapper answers the probe correctly, spawns a descendant that + // inherits (and holds) the stdout pipe, and exits before the context + // deadline — the shape where cmd.Cancel never runs. + script := "#!/bin/sh\n" + + "echo 'QEMU emulator version 8.2.0'\n" + + "sleep 60 &\n" + + "echo $! > " + childPIDFile + "\n" + + "exit 0\n" + require.NoError(t, os.WriteFile(binaryPath, []byte(script), 0o755)) + + ctx, cancel := context.WithTimeout(context.Background(), versionProbeTimeout) + defer cancel() + start := time.Now() + _, err := versionFromBinary(ctx, binaryPath) + // The descendant holds the output pipe past WaitDelay, so the probe + // reports ErrWaitDelay rather than success; what must never happen is + // the descendant outliving the probe. + require.ErrorIs(t, err, exec.ErrWaitDelay, + "a wrapper whose descendant holds the output pipe completes via WaitDelay") + require.Less(t, time.Since(start), versionProbeTimeout, + "the wrapper exited immediately; the probe must return at WaitDelay, not the context deadline") + + // The wrapper wrote the descendant's pid before exiting, and the probe + // cannot return before the wrapper exits, so the file is complete here. + data, readErr := os.ReadFile(childPIDFile) + require.NoError(t, readErr, "probe wrapper must have recorded its descendant's pid before exiting") + childPID, convErr := strconv.Atoi(strings.TrimSpace(string(data))) + require.NoError(t, convErr) + + require.Eventually(t, func() bool { + return errors.Is(syscall.Kill(childPID, 0), syscall.ESRCH) + }, 5*time.Second, 20*time.Millisecond, + "background descendant (pid %d) must be killed when the probe completes, even though the wrapper exited before the context deadline", + childPID) +} + func TestSaveAndLoadVMConfigPreservesRestoreContract(t *testing.T) { dir := t.TempDir() want := savedVMConfig{ diff --git a/lib/hypervisor/qemu/register_linux_test.go b/lib/hypervisor/qemu/register_linux_test.go index 4c04a26c3..c3b6099dd 100644 --- a/lib/hypervisor/qemu/register_linux_test.go +++ b/lib/hypervisor/qemu/register_linux_test.go @@ -142,8 +142,10 @@ func TestCheckLaunchPrerequisitesFor(t *testing.T) { // bounded context expires instead of blocking the capability request // (and leaking a subprocess) indefinitely. `exec sleep` replaces the // shell so this covers the direct-child case; descendant cleanup is - // pinned by TestVersionFromBinaryKillsProcessGroupOnTimeout, which - // asserts versionFromBinary's group kill reaps the whole tree. + // pinned by TestVersionFromBinaryKillsProcessGroupOnTimeout (cancelled + // probe) and TestVersionFromBinaryKillsDescendantsWhenWrapperExitsFirst + // (wrapper exits first), which assert versionFromBinary reaps the + // whole tree on both completion paths. dir := t.TempDir() path := filepath.Join(dir, "qemu-system-fake") require.NoError(t, os.WriteFile(path, []byte("#!/bin/sh\nexec sleep 60\n"), 0o755)) From 993d31e0834e818a72b550293af01531a202d302 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sat, 15 Aug 2026 19:24:23 -0400 Subject: [PATCH 09/11] Retry probe-script tests on the fork/exec ETXTBSY race Parallel tests fork subprocesses, and a concurrent fork can transiently inherit a just-written script's write descriptor between fork and exec, failing the probe with 'text file busy' before the script ever runs. Retry the probe (with a fresh timeout context per attempt) so the two process-group-kill regression tests assert probe behavior, not the well-known exec race. --- lib/hypervisor/qemu/process_test.go | 31 +++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/lib/hypervisor/qemu/process_test.go b/lib/hypervisor/qemu/process_test.go index 2811f170f..2800bd459 100644 --- a/lib/hypervisor/qemu/process_test.go +++ b/lib/hypervisor/qemu/process_test.go @@ -110,6 +110,27 @@ func TestGetVersion_ParsesVersionCorrectly(t *testing.T) { } } +// probeRetryingETXTBSY runs versionFromBinary against a just-written script +// with a fresh timeout-bounded context per attempt, retrying when exec fails +// with ETXTBSY: another parallel test's fork can transiently inherit the +// script's write descriptor between its own fork and exec (the well-known +// fork/exec text-file-busy race), and a retry must not consume the probe +// deadline the caller asserts on. Returns the final attempt's start time. +func probeRetryingETXTBSY(t *testing.T, binaryPath string, timeout time.Duration) (time.Time, error) { + t.Helper() + for attempt := 0; ; attempt++ { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + start := time.Now() + _, err := versionFromBinary(ctx, binaryPath) + cancel() + if errors.Is(err, syscall.ETXTBSY) && attempt < 100 { + time.Sleep(5 * time.Millisecond) + continue + } + return start, err + } +} + // TestVersionFromBinaryKillsProcessGroupOnTimeout pins that a cancelled // version probe terminates its entire process tree, not just the direct // child. A QEMU wrapper script that spawns a descendant (`sleep 60 & wait`) @@ -133,10 +154,7 @@ func TestVersionFromBinaryKillsProcessGroupOnTimeout(t *testing.T) { "wait\n" require.NoError(t, os.WriteFile(binaryPath, []byte(script), 0o755)) - ctx, cancel := context.WithTimeout(context.Background(), 250*time.Millisecond) - defer cancel() - start := time.Now() - _, err := versionFromBinary(ctx, binaryPath) + start, err := probeRetryingETXTBSY(t, binaryPath, 250*time.Millisecond) require.Error(t, err, "a hung probe must fail at the deadline") require.Less(t, time.Since(start), 10*time.Second, "a hung probe must return at the deadline, not run to completion") @@ -186,10 +204,7 @@ func TestVersionFromBinaryKillsDescendantsWhenWrapperExitsFirst(t *testing.T) { "exit 0\n" require.NoError(t, os.WriteFile(binaryPath, []byte(script), 0o755)) - ctx, cancel := context.WithTimeout(context.Background(), versionProbeTimeout) - defer cancel() - start := time.Now() - _, err := versionFromBinary(ctx, binaryPath) + start, err := probeRetryingETXTBSY(t, binaryPath, versionProbeTimeout) // The descendant holds the output pipe past WaitDelay, so the probe // reports ErrWaitDelay rather than success; what must never happen is // the descendant outliving the probe. From 47b6e51cc52aecaebbe17950f26c0b591331d5e5 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sat, 15 Aug 2026 19:51:15 -0400 Subject: [PATCH 10/11] Deterministic probe tests: cancel on observed pids, retry exec ETXTBSY everywhere MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The timeout regression test cancelled the probe on a fixed 250ms deadline, which raced slow script startup — macOS CI runners can take longer than that to first-exec a fresh script (XProtect scan), killing the group before the pid files existed. Cancel the context only once the script has demonstrably hung with both pids recorded, with a generous backstop that still fails fast on regression. Extend the fork/exec ETXTBSY retry (a concurrent fork transiently inheriting a just-written script's write fd) to the launch-prerequisite subtests that exec fake binaries; observed ~1/30 full-package -race runs, 0/50 after. No production code changes. --- lib/hypervisor/qemu/process_test.go | 95 +++++++++++++++++----- lib/hypervisor/qemu/register_linux_test.go | 33 ++++++-- 2 files changed, 98 insertions(+), 30 deletions(-) diff --git a/lib/hypervisor/qemu/process_test.go b/lib/hypervisor/qemu/process_test.go index 2800bd459..e8be0dda2 100644 --- a/lib/hypervisor/qemu/process_test.go +++ b/lib/hypervisor/qemu/process_test.go @@ -110,33 +110,65 @@ func TestGetVersion_ParsesVersionCorrectly(t *testing.T) { } } -// probeRetryingETXTBSY runs versionFromBinary against a just-written script -// with a fresh timeout-bounded context per attempt, retrying when exec fails -// with ETXTBSY: another parallel test's fork can transiently inherit the -// script's write descriptor between its own fork and exec (the well-known -// fork/exec text-file-busy race), and a retry must not consume the probe -// deadline the caller asserts on. Returns the final attempt's start time. -func probeRetryingETXTBSY(t *testing.T, binaryPath string, timeout time.Duration) (time.Time, error) { +// retryingETXTBSY retries run while it fails with ETXTBSY: tests exec +// just-written scripts, and another parallel test's fork can transiently +// inherit a script's write descriptor between its own fork and exec (the +// well-known fork/exec text-file-busy race), failing exec before the script +// ever runs. Only that pre-execution error is retried, so assertions about +// probe behavior are unaffected. +func retryingETXTBSY(t *testing.T, run func() error) error { t.Helper() for attempt := 0; ; attempt++ { - ctx, cancel := context.WithTimeout(context.Background(), timeout) - start := time.Now() - _, err := versionFromBinary(ctx, binaryPath) - cancel() + err := run() if errors.Is(err, syscall.ETXTBSY) && attempt < 100 { time.Sleep(5 * time.Millisecond) continue } - return start, err + return err } } +// probeRetryingETXTBSY runs versionFromBinary with a fresh caller-supplied +// context per attempt (a retry must not consume the probe deadline the +// caller asserts on), retrying the ETXTBSY exec race. Returns the final +// attempt's start time. +func probeRetryingETXTBSY(t *testing.T, binaryPath string, newCtx func() (context.Context, context.CancelFunc)) (time.Time, error) { + t.Helper() + var start time.Time + err := retryingETXTBSY(t, func() error { + ctx, cancel := newCtx() + defer cancel() + start = time.Now() + _, err := versionFromBinary(ctx, binaryPath) + return err + }) + return start, err +} + +// probeBackstopTimeout bounds the probe in tests that cancel it explicitly +// (or expect it to finish on its own): generous enough that slow CI runners +// — macOS can take hundreds of milliseconds to first-exec a fresh script — +// never hit it, but far below the scripts' 60s sleeps so a regression still +// fails fast instead of running descendants to completion. +const probeBackstopTimeout = 30 * time.Second + +// pidFileReady reports whether a probe script has fully recorded a pid: +// `echo $$ > file` creates the file before writing, so existence alone can +// race an empty read. +func pidFileReady(path string) bool { + data, err := os.ReadFile(path) + return err == nil && strings.TrimSpace(string(data)) != "" +} + // TestVersionFromBinaryKillsProcessGroupOnTimeout pins that a cancelled // version probe terminates its entire process tree, not just the direct // child. A QEMU wrapper script that spawns a descendant (`sleep 60 & wait`) // must leave neither the wrapper nor the descendant behind after the probe's -// context deadline; otherwise every capability request against a wedged -// binary would orphan a subprocess under PID 1. +// context is cancelled; otherwise every capability request against a wedged +// binary would orphan a subprocess under PID 1. The test cancels the context +// itself once the script has recorded both pids — a fixed short deadline +// races slow script startup and can kill the group before the pid files +// exist. func TestVersionFromBinaryKillsProcessGroupOnTimeout(t *testing.T) { t.Parallel() @@ -154,10 +186,26 @@ func TestVersionFromBinaryKillsProcessGroupOnTimeout(t *testing.T) { "wait\n" require.NoError(t, os.WriteFile(binaryPath, []byte(script), 0o755)) - start, err := probeRetryingETXTBSY(t, binaryPath, 250*time.Millisecond) - require.Error(t, err, "a hung probe must fail at the deadline") - require.Less(t, time.Since(start), 10*time.Second, - "a hung probe must return at the deadline, not run to completion") + // Cancel the probe as soon as the script has demonstrably hung with its + // descendant running (both pids recorded), never before. + watchCtx, stopWatch := context.WithCancel(context.Background()) + defer stopWatch() + go func() { + for watchCtx.Err() == nil { + if pidFileReady(parentPIDFile) && pidFileReady(childPIDFile) { + stopWatch() + return + } + time.Sleep(10 * time.Millisecond) + } + }() + + start, err := probeRetryingETXTBSY(t, binaryPath, func() (context.Context, context.CancelFunc) { + return context.WithTimeout(watchCtx, probeBackstopTimeout) + }) + require.Error(t, err, "a hung probe must fail when its context is cancelled") + require.Less(t, time.Since(start), probeBackstopTimeout, + "a hung probe must return at cancellation, not run to completion") readPID := func(path string) int { data, readErr := os.ReadFile(path) @@ -204,13 +252,16 @@ func TestVersionFromBinaryKillsDescendantsWhenWrapperExitsFirst(t *testing.T) { "exit 0\n" require.NoError(t, os.WriteFile(binaryPath, []byte(script), 0o755)) - start, err := probeRetryingETXTBSY(t, binaryPath, versionProbeTimeout) + start, err := probeRetryingETXTBSY(t, binaryPath, func() (context.Context, context.CancelFunc) { + return context.WithTimeout(context.Background(), probeBackstopTimeout) + }) // The descendant holds the output pipe past WaitDelay, so the probe - // reports ErrWaitDelay rather than success; what must never happen is - // the descendant outliving the probe. + // reports ErrWaitDelay rather than success — which also proves it did not + // run to the context deadline; what must never happen is the descendant + // outliving the probe. require.ErrorIs(t, err, exec.ErrWaitDelay, "a wrapper whose descendant holds the output pipe completes via WaitDelay") - require.Less(t, time.Since(start), versionProbeTimeout, + require.Less(t, time.Since(start), probeBackstopTimeout, "the wrapper exited immediately; the probe must return at WaitDelay, not the context deadline") // The wrapper wrote the descendant's pid before exiting, and the probe diff --git a/lib/hypervisor/qemu/register_linux_test.go b/lib/hypervisor/qemu/register_linux_test.go index c3b6099dd..ff42b0a36 100644 --- a/lib/hypervisor/qemu/register_linux_test.go +++ b/lib/hypervisor/qemu/register_linux_test.go @@ -97,7 +97,10 @@ func TestCheckLaunchPrerequisitesFor(t *testing.T) { t.Run("working binary and device pass", func(t *testing.T) { dir := t.TempDir() - require.NoError(t, checkLaunchPrerequisitesFor(ctx, fakeQEMUBinary(t, dir), fakeVsockDevice(t, dir))) + binary, vsock := fakeQEMUBinary(t, dir), fakeVsockDevice(t, dir) + require.NoError(t, retryingETXTBSY(t, func() error { + return checkLaunchPrerequisitesFor(ctx, binary, vsock) + })) }) t.Run("missing binary fails", func(t *testing.T) { @@ -118,7 +121,10 @@ func TestCheckLaunchPrerequisitesFor(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "qemu-system-fake") require.NoError(t, os.WriteFile(path, []byte("#!/bin/sh\nexit 1\n"), 0o755)) - err := checkLaunchPrerequisitesFor(ctx, path, fakeVsockDevice(t, dir)) + vsock := fakeVsockDevice(t, dir) + err := retryingETXTBSY(t, func() error { + return checkLaunchPrerequisitesFor(ctx, path, vsock) + }) require.ErrorContains(t, err, "not usable") }) @@ -126,13 +132,19 @@ func TestCheckLaunchPrerequisitesFor(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "qemu-system-fake") require.NoError(t, os.WriteFile(path, []byte("#!/bin/sh\necho 'not qemu'\n"), 0o755)) - err := checkLaunchPrerequisitesFor(ctx, path, fakeVsockDevice(t, dir)) + vsock := fakeVsockDevice(t, dir) + err := retryingETXTBSY(t, func() error { + return checkLaunchPrerequisitesFor(ctx, path, vsock) + }) require.ErrorContains(t, err, "not usable") }) t.Run("missing vsock device fails", func(t *testing.T) { dir := t.TempDir() - err := checkLaunchPrerequisitesFor(ctx, fakeQEMUBinary(t, dir), filepath.Join(dir, "no-vhost-vsock")) + binary := fakeQEMUBinary(t, dir) + err := retryingETXTBSY(t, func() error { + return checkLaunchPrerequisitesFor(ctx, binary, filepath.Join(dir, "no-vhost-vsock")) + }) require.ErrorContains(t, err, "vsock device") require.ErrorContains(t, err, "vhost_vsock") }) @@ -149,10 +161,15 @@ func TestCheckLaunchPrerequisitesFor(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "qemu-system-fake") require.NoError(t, os.WriteFile(path, []byte("#!/bin/sh\nexec sleep 60\n"), 0o755)) - shortCtx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) - defer cancel() - start := time.Now() - err := checkLaunchPrerequisitesFor(shortCtx, path, fakeVsockDevice(t, dir)) + vsock := fakeVsockDevice(t, dir) + var start time.Time + err := retryingETXTBSY(t, func() error { + // Fresh deadline per attempt so an ETXTBSY retry doesn't eat it. + shortCtx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) + defer cancel() + start = time.Now() + return checkLaunchPrerequisitesFor(shortCtx, path, vsock) + }) require.ErrorContains(t, err, "not usable") require.Less(t, time.Since(start), 10*time.Second, "a hung probe must return at the deadline, not run to completion") From d7fba22b79ed7f3cedfa1469b189a816813ef029 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sat, 15 Aug 2026 20:23:43 -0400 Subject: [PATCH 11/11] Tighten QEMU launch prerequisites --- .../cloudhypervisor/cloudhypervisor.go | 8 ++-- lib/hypervisor/qemu/register_linux.go | 25 ++++++++--- lib/hypervisor/qemu/register_linux_test.go | 41 ++++++++++++++++--- 3 files changed, 60 insertions(+), 14 deletions(-) diff --git a/lib/hypervisor/cloudhypervisor/cloudhypervisor.go b/lib/hypervisor/cloudhypervisor/cloudhypervisor.go index 83646e39d..6b3b4a781 100644 --- a/lib/hypervisor/cloudhypervisor/cloudhypervisor.go +++ b/lib/hypervisor/cloudhypervisor/cloudhypervisor.go @@ -48,11 +48,11 @@ func (c *CloudHypervisor) Capabilities() hypervisor.Capabilities { return capabilities() } -// capabilities resolves the effective capability set from the configured -// default version (see GetDefaultVersion), matching what the capability -// registry reports, rather than the compile-time vmm.DefaultVersion. +// capabilities preserves the legacy client behavior for clients that do not +// carry instance-version metadata. The runtime registry separately resolves +// capabilities for the configured default used by new launches. func capabilities() hypervisor.Capabilities { - return CapabilitiesForVersion(GetDefaultVersion()) + return CapabilitiesForVersion(vmm.DefaultVersion) } // CapabilitiesForVersion returns capabilities for a specific CH version. diff --git a/lib/hypervisor/qemu/register_linux.go b/lib/hypervisor/qemu/register_linux.go index 65b07c930..532c118e9 100644 --- a/lib/hypervisor/qemu/register_linux.go +++ b/lib/hypervisor/qemu/register_linux.go @@ -72,9 +72,9 @@ func checkLaunchPrerequisites() error { // (versionFromBinary), because every cold start persists ResolveVersion's // result and treats failure as fatal — a non-executable or broken binary // accepted by GetBinaryPath's bare os.Stat must not report available; -// - the vhost-vsock host device must exist, because every created instance -// receives a nonzero vsock CID and buildArgs unconditionally attaches a -// vhost-vsock device for it. +// - the vhost-vsock host device must be a character device that this process +// can open read/write, because every created instance receives a nonzero +// vsock CID and buildArgs unconditionally attaches a vhost-vsock device. // // Split from checkLaunchPrerequisites so unavailable cases are testable with // fake binaries and device paths regardless of the host's QEMU install. @@ -85,8 +85,23 @@ func checkLaunchPrerequisitesFor(ctx context.Context, binaryPath, vsockDevicePat if _, err := versionFromBinary(ctx, binaryPath); err != nil { return fmt.Errorf("qemu binary %s is not usable: %w", binaryPath, err) } - if _, err := os.Stat(vsockDevicePath); err != nil { - return fmt.Errorf("vsock device %s is required for instance launches (load the vhost_vsock kernel module): %w", vsockDevicePath, err) + return validateVsockDevice(vsockDevicePath, os.OpenFile) +} + +func validateVsockDevice(path string, openFile func(string, int, os.FileMode) (*os.File, error)) error { + info, err := os.Stat(path) + if err != nil { + return fmt.Errorf("vsock device %s is required for instance launches (load the vhost_vsock kernel module): %w", path, err) + } + if info.Mode().Type() != os.ModeDevice|os.ModeCharDevice { + return fmt.Errorf("vsock device %s must be a character device (load the vhost_vsock kernel module)", path) + } + device, err := openFile(path, os.O_RDWR, 0) + if err != nil { + return fmt.Errorf("vsock device %s is not accessible read/write for instance launches: %w", path, err) + } + if err := device.Close(); err != nil { + return fmt.Errorf("close vsock device %s after launch check: %w", path, err) } return nil } diff --git a/lib/hypervisor/qemu/register_linux_test.go b/lib/hypervisor/qemu/register_linux_test.go index ff42b0a36..fdd611a44 100644 --- a/lib/hypervisor/qemu/register_linux_test.go +++ b/lib/hypervisor/qemu/register_linux_test.go @@ -76,12 +76,15 @@ func fakeQEMUBinary(t *testing.T, dir string) string { return path } -// fakeVsockDevice creates a stand-in for /dev/vhost-vsock: the prerequisite -// check only requires the device node to exist. -func fakeVsockDevice(t *testing.T, dir string) string { +// fakeVsockDevice uses a harmless character device as a stand-in for +// /dev/vhost-vsock. The prerequisite check verifies node type and O_RDWR +// access; it deliberately does not issue vhost ioctls during diagnostics. +func fakeVsockDevice(t *testing.T, _ string) string { t.Helper() - path := filepath.Join(dir, "vhost-vsock") - require.NoError(t, os.WriteFile(path, nil, 0o600)) + const path = "/dev/null" + info, err := os.Stat(path) + require.NoError(t, err) + require.Equal(t, os.ModeDevice|os.ModeCharDevice, info.Mode().Type()) return path } @@ -149,6 +152,34 @@ func TestCheckLaunchPrerequisitesFor(t *testing.T) { require.ErrorContains(t, err, "vhost_vsock") }) + t.Run("regular file is not a vsock device", func(t *testing.T) { + dir := t.TempDir() + binary := fakeQEMUBinary(t, dir) + path := filepath.Join(dir, "vhost-vsock") + require.NoError(t, os.WriteFile(path, nil, 0o600)) + err := retryingETXTBSY(t, func() error { + return checkLaunchPrerequisitesFor(ctx, binary, path) + }) + require.ErrorContains(t, err, "must be a character device") + }) + + t.Run("directory is not a vsock device", func(t *testing.T) { + dir := t.TempDir() + binary := fakeQEMUBinary(t, dir) + err := retryingETXTBSY(t, func() error { + return checkLaunchPrerequisitesFor(ctx, binary, dir) + }) + require.ErrorContains(t, err, "must be a character device") + }) + + t.Run("inaccessible character device fails", func(t *testing.T) { + err := validateVsockDevice("/dev/null", func(string, int, os.FileMode) (*os.File, error) { + return nil, os.ErrPermission + }) + require.ErrorContains(t, err, "not accessible read/write") + require.ErrorIs(t, err, os.ErrPermission) + }) + t.Run("hung binary fails at the context deadline", func(t *testing.T) { // A wedged QEMU binary must fail the prerequisite check when its // bounded context expires instead of blocking the capability request