Skip to content

Validation kit: Collect no longer takes another command's output from the guest agent - #5

Merged
fadwen merged 1 commit into
mainfrom
fix/validation-guest-agent-stale-replies
Oct 5, 2026
Merged

fadwen merged 1 commit into
mainfrom
fix/validation-guest-agent-stale-replies

Conversation

@fadwen

@fadwen fadwen commented Oct 5, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes the validation kit's Collect action, which #4 reported as failing on the lab device and left open ("Not changed": ConvertFrom-Json stopped at probe record 5,637 of an 11 MB payload). The payload was never the problem. The QEMU guest agent returned another command's output for one of the chunk reads, and the driver had no way to notice.

Only the validation kit changes. Nothing under Public/, Private/ or the shipped package is touched, and ModuleVersion is not bumped.

Cause

Measured on the Entra joined lab device (VM 125) on 2026-10-05:

  • The payload file on the device was intact. The JSON error sat 11 bytes after the boundary of chunk 154 of 179: that chunk had arrived with the right length and another chunk's text.
  • The guest agent keeps a result until a status call collects it, and answers a status call for a process id with the oldest result it holds under that id. Windows reuses process ids quickly. Of 245 commands started without collecting their results, 29 ids were used two or three times, and asking for one of them returned the first command's output, then the second's, then the third's.
  • A status call that times out on the host (qmp command 'guest-exec-status' failed - got timeout) has still been answered by the agent. If the command had finished, the next call says PID does not exist: the result went with the reply nobody read.

The driver ran each guest command with a synchronous qm guest exec and ran it again on a timeout. That left results behind, and a later chunk read that was given the same process id got an earlier chunk back.

Changes

  • Validation/GuestAgent.ps1 (new). Invoke-GuestPowerShell now lives here and is dot-sourced by Invoke-ValidationRound.ps1 and Invoke-LabGuestScript.ps1, which each carried their own copy.
    • A command is started without waiting (qm guest exec --synchronous 0) and its result is asked for by process id.
    • Every command prints a marker of its own first. A result without it belongs to an earlier command; asking for it has collected it, and the next result under the id is this command's.
    • When the agent holds nothing for the id after a status call timed out, the result was lost with that call and the command is run again, up to three times. A snippet sent this way has to be safe to run twice, as before.
  • Read-GuestPayload (new, same file). The chunked read moved out of Invoke-Collect. The device now reports a SHA-256 with the payload size, and the assembled text is checked against it, so a damaged payload is refused with both hashes instead of surfacing as a parse error.
  • Detached launch. A second copy of the runner batch file finds the output file held by the first, skips the script and writes the done marker at once (reproduced locally). The launch now sits behind a started marker, so a launch that runs twice starts the script once.
  • Documentation. Validation/README.md records the measurements under "Timing notes" and lists the new file. Changelog entry under Unreleased.

Verification

  • Kit unit tests: 57 pass on PowerShell 7.6.6, including the stale-result case, the lost-result case, the payload hash mismatch and the repeated launch. The test file loads cleanly under Windows PowerShell 5.1, where the driver's tests are skipped as before. PSScriptAnalyzer (Error and Warning) is clean over Validation/.
  • Live, 80 commands through the new helper against an agent seeded with 150 uncollected results: every command returned its own output; four were run again after a lost result.
  • Live, the real Collect against VM 125: completed, 79 stale results passed over, 3 commands run again, payload hash matched, result file written with 45 remediations, 11 platform scripts and 6,118 probe records. The 79 is higher than a normal run would see, because the agent still held results planted for the test above.
  • Invoke-LabGuestScript.ps1 was run once through the shared helper and returned the script's output.

Not changed

… agent for a payload chunk

The QEMU guest agent keeps a result until a status call collects it and answers a call for a process id with the oldest result it holds under that id; Windows reuses the ids. The driver's synchronous qm guest exec, retried on a timeout, left results behind, and a later chunk read given the same id got an earlier chunk back: the right length, the wrong text. That is the JSON error reported in #4.

GuestAgent.ps1 now carries the guest calls for the driver and Invoke-LabGuestScript.ps1. A command is started without waiting and its result asked for by process id; every command prints a marker of its own and a result without it is passed over; a result lost to a status call that timed out makes the command run again. The device reports a SHA-256 with the payload size and the assembled payload is checked against it. The detached launch sits behind a started marker, since a second runner skips the script and writes the done marker at once.
@fadwen
fadwen merged commit 2236fa7 into main Oct 5, 2026
4 checks passed
@fadwen
fadwen deleted the fix/validation-guest-agent-stale-replies branch October 5, 2026 19:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant