From a2608c9fadf76c242527a636ee08968fa16ddde8 Mon Sep 17 00:00:00 2001 From: "pulse-triage[bot]" <249995291+pulse-triage[bot]@users.noreply.github.com> Date: Sun, 4 Oct 2026 22:49:53 +0100 Subject: [PATCH] Match guest Discovery integration proof to native platform capabilities Native Windows and macOS correctly advertise no PVE guest guard, so their real-server fixture must assert refusal before any config or QGA handoff. Keep the existing Linux admission, outcome and resumption assertions and exercise foreign platform labels without changing runtime capability policy. Contract-Neutral: Test-only platform evidence repair; guest execution and Discovery runtime contracts, capability advertisement and safety gates are unchanged. Change-source: pulse-maintainer --- .../guest_exec_discovery_integration_test.go | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/internal/hostagent/guest_exec_discovery_integration_test.go b/internal/hostagent/guest_exec_discovery_integration_test.go index 292bc68855..b3e85e3c0d 100644 --- a/internal/hostagent/guest_exec_discovery_integration_test.go +++ b/internal/hostagent/guest_exec_discovery_integration_test.go @@ -6,6 +6,7 @@ import ( "net/http" "net/http/httptest" "os/exec" + "runtime" "strings" "sync/atomic" "testing" @@ -39,13 +40,27 @@ func (e guestSafetyDiscoveryExecutor) IsAgentConnected(id string) bool { // Real server/session/registration/agent/Discovery paths, with a fake non-QGA // config reader and fake provider process. This is NOT native PVE acceptance. func testGuestExecRealServerAgentDiscoveryAdmission(t *testing.T) { + platforms := []string{runtime.GOOS, "linux", "darwin", "windows"} + seen := make(map[string]bool) + for _, platform := range platforms { + if seen[platform] { + continue + } + seen[platform] = true + t.Run(platform, func(t *testing.T) { + testGuestExecRealServerAgentDiscoveryPlatform(t, platform) + }) + } +} + +func testGuestExecRealServerAgentDiscoveryPlatform(t *testing.T, platform string) { s := agentexec.NewServer(func(token, agent, host string) bool { return token == "fixture" && agent == "node-agent" && host == "node" }) ts := httptest.NewServer(http.HandlerFunc(s.HandleWebSocket)) defer ts.Close() logger := zerolog.Nop() - c := NewCommandClient(Config{PulseURL: ts.URL, APIToken: "fixture", StateDir: t.TempDir(), Logger: &logger}, "node-agent", "node", "linux", "6") + c := NewCommandClient(Config{PulseURL: ts.URL, APIToken: "fixture", StateDir: t.TempDir(), Logger: &logger}, "node-agent", "node", platform, "6") var locked, lockDuringHandoff, failedGuest atomic.Bool var qgaCalls, configReads atomic.Int32 locked.Store(true) @@ -109,6 +124,27 @@ func testGuestExecRealServerAgentDiscoveryAdmission(t *testing.T) { } } locked.Store(false) + if runtime.GOOS != "linux" || platform != "linux" { + // PVE admission belongs to a Linux host agent. A platform label must + // not make a non-Linux executable advertise that capability, and a + // Linux executable labelled as another platform must still be refused. + // Exercise the real registration and server gate, not a skipped test + // or an artificial capability override just to reach the happy path. + for _, rt := range []servicediscovery.ResourceType{servicediscovery.ResourceTypeVM, servicediscovery.ResourceTypeDockerVM} { + id := "105" + if rt == servicediscovery.ResourceTypeDockerVM { + id = "105:app" + } + got, err := scanner.Scan(context.Background(), servicediscovery.DiscoveryRequest{ResourceType: rt, ResourceID: id, TargetID: "node-agent"}) + if err == nil || err.Error() != agentexec.GuestExecDeferred(agentexec.GuestExecGuardUnavailable).Error() || got == nil || len(got.CommandOutputs) != 0 || qgaCalls.Load() != 0 || configReads.Load() != 0 { + t.Fatalf("unsupported platform reached guest admission: %+v %v calls=%d reads=%d", got, err, qgaCalls.Load(), configReads.Load()) + } + } + if !s.IsAgentConnected("node-agent") { + t.Fatal("platform deferral dropped session liveness") + } + return + } got, err := scanner.ScanVM(context.Background(), "node-agent", "node", "105") want := int32(len(servicediscovery.GetCommandsForResource(servicediscovery.ResourceTypeVM))) if err != nil || got == nil || int32(len(got.CommandOutputs)) != want || qgaCalls.Load() != want {