Skip to content

fix(e2e): safely generalize Windows PIS images - #9042

Open
r2k1 wants to merge 4 commits into
mainfrom
r2k1-safe-windows-generalization
Open

fix(e2e): safely generalize Windows PIS images#9042
r2k1 wants to merge 4 commits into
mainfrom
r2k1-safe-windows-generalization

Conversation

@r2k1

@r2k1 r2k1 commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

What this PR does / why we need it:

Mirrors AKS RP's Windows Prepared Image Specification generalization sequence in AgentBaker E2E:

  • runs Sysprep with /oobe /generalize /mode:vm /quiet /shutdown
  • starts managed RunCommand asynchronously and externally waits for PowerState/stopped before deallocation and SIG capture
  • preserves native nonzero Sysprep failures while tolerating an unset $LASTEXITCODE as shutdown begins
  • uses AKS RP's separate 15-minute RunCommand timeout and 20-minute host-side shutdown wait
  • treats canceled/failed RunCommand states without a nonzero exit as provisional guest-agent interruption while shutdown is in progress
  • rejects premature deallocating or deallocated VM states before Sysprep reaches stopped
  • performs no guest-agent/script cleanup after generalization
  • centralizes VM instance-view power-state parsing for running and stopped waits

This intentionally differs from the Windows Packer VHD flow, where Packer owns the lifecycle and uses an in-guest ImageState poll.

Tests:

cd e2e && go test . -run 'TestWindowsSysprepScriptShutsDownAfterGeneralization|TestRunCommandTerminalError|TestIsRunCommandShutdownInterruption|TestVMPowerState' -count=1

Which issue(s) this PR fixes:

N/A

r2k1 added 3 commits July 25, 2026 22:58
Run Sysprep asynchronously with /shutdown, wait for the VM to stop before deallocation, and preserve terminal RunCommand failures.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3f4c66a0-992f-414b-bd60-647d28365281
Apply the existing Windows PIS ceiling to both the managed command and host-side shutdown observation.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3f4c66a0-992f-414b-bd60-647d28365281
Document parity with AKS RP Windows PIS and reuse one VM instance-view power-state parser for running and stopped waits.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3f4c66a0-992f-414b-bd60-647d28365281
@github-actions

github-actions Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Windows Unit Test Results

  3 files   11 suites   48s ⏱️
381 tests 381 ✅ 0 💤 0 ❌
384 runs  384 ✅ 0 💤 0 ❌

Results for commit b466777.

♻️ This comment has been updated with latest results.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Windows image-capture path in AgentBaker e2e to more closely mirror AKS RP’s Prepared Image Specification (PIS) generalization flow: Sysprep shuts the VM down, the harness waits for PowerState/stopped, then deallocates and captures the disk, while also centralizing and hardening power-state parsing and RunCommand terminal failure detection.

Changes:

  • Centralize VMSS VM instance-view power state parsing via a shared helper and add unit coverage for it.
  • Introduce runWindowsSysprep() that starts managed RunCommand asynchronously, waits for VM shutdown, and checks terminal RunCommand failures during the wait.
  • Add unit tests pinning the Sysprep script semantics and validating RunCommand terminal-error classification.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
e2e/vmss.go Adds vmPowerState() helper and refactors running-state wait to use it.
e2e/vmss_test.go Adds unit test coverage for vmPowerState().
e2e/test_helpers.go Adds runCommandTerminalError, pins Sysprep timeout, and introduces runWindowsSysprep() flow used by CreateImage().
e2e/test_helpers_test.go Adds tests to pin Sysprep shutdown behavior and validate RunCommand terminal-error logic.

Comment thread e2e/vmss.go
Comment on lines +784 to +788
for _, status := range vm.Properties.InstanceView.Statuses {
if status.Code != nil && strings.HasPrefix(*status.Code, "PowerState/") {
return strings.TrimPrefix(*status.Code, "PowerState/")
}
}
Comment thread e2e/test_helpers.go Outdated
Comment on lines +863 to +882
powerState := vmPowerState(vm.VirtualMachineScaleSetVM)
if powerState == "stopped" {
return view, nil
}
if powerState != "" {
toolkit.Logf(ctx, "VM is in power state: %s, waiting for stopped state...", powerState)
}

getResp, err := config.Azure.VMSSVMRunCommands.Get(ctx, rg, s.Runtime.VMSSName, instanceID, runCommandName, &armcompute.VirtualMachineScaleSetVMRunCommandsClientGetOptions{
Expand: to.Ptr("instanceView"),
})
if err != nil {
return view, fmt.Errorf("failed to verify sysprep result while waiting for VM shutdown: %w", err)
}
if getResp.Properties != nil && getResp.Properties.InstanceView != nil {
view = *getResp.Properties.InstanceView
if err := runCommandTerminalError(view); err != nil {
return view, err
}
}
Comment thread e2e/test_helpers_test.go
}
}

func TestRunCommandTerminalError(t *testing.T) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feels like adding a test for e2e test is a bit overkill

Mirror AKS RP's separate RunCommand and shutdown wait timeouts, tolerate guest-agent interruption during shutdown, and reject premature VM deallocation.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3f4c66a0-992f-414b-bd60-647d28365281
Copilot AI review requested due to automatic review settings July 30, 2026 20:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

e2e/test_helpers.go:883

  • runWindowsSysprep returns success immediately when the VM reaches PowerState/stopped, without checking the RunCommand instance view one last time. If Sysprep returns a non-zero exit code but still shuts down the VM, this path would incorrectly treat the run as successful (contradicting the stated goal of preserving native Sysprep failures). Consider validating the RunCommand instance view before returning on "stopped".
		lastPowerState = vmPowerState(vm.VirtualMachineScaleSetVM)
		if lastPowerState == "stopped" {
			return view, nil
		}

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.

3 participants