[CmdletBinding()] param( [Parameter(Mandatory = $true, Position = 0)] [ValidateSet('Prepare', 'Status', 'Run', 'Collect', 'Stop', 'Reset')] [string] $Action, [Parameter(Mandatory = $false)] [ValidatePattern('^[a-z0-9][a-z0-9-]{0,63}$')] [string] $RunId = '', [string] $VmName = $(if ($env:RVBOX_WINDOWS_VM) { $env:RVBOX_WINDOWS_VM } else { 'rvbox-win10-test' }), [string] $Snapshot = $(if ($env:RVBOX_WINDOWS_BASELINE_SNAPSHOT) { $env:RVBOX_WINDOWS_BASELINE_SNAPSHOT } else { 'baseline-disk-first' }) ) $ErrorActionPreference = 'Stop' $VBoxManage = if ($env:VBOXMANAGE) { $env:VBOXMANAGE } else { 'VBoxManage' } $GuestUser = $env:RVBOX_WINDOWS_GUEST_USER $GuestPassword = $env:RVBOX_WINDOWS_GUEST_PASSWORD $LeaseRoot = if ($env:RVBOX_WINDOWS_LEASE_DIR) { $env:RVBOX_WINDOWS_LEASE_DIR } else { Join-Path $PSScriptRoot '..\..\.test-runs\windows' } function Invoke-VBox([string[]] $Arguments) { $output = & $VBoxManage @Arguments 2>&1 if ($LASTEXITCODE -ne 0) { throw "VBoxManage failed ($LASTEXITCODE): $($output -join ' ')" } return $output } function Require-RunId { if ([string]::IsNullOrWhiteSpace($RunId)) { throw "$Action requires -RunId" } } function Acquire-Lease { New-Item -ItemType Directory -Force -Path $LeaseRoot | Out-Null $path = Join-Path $LeaseRoot 'lease.lock' try { $script:Lease = [System.IO.File]::Open($path, [System.IO.FileMode]::OpenOrCreate, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None) $bytes = [Text.Encoding]::UTF8.GetBytes("$VmName`n$RunId`n$([DateTime]::UtcNow.ToString('o'))`n") $Lease.SetLength(0); $Lease.Write($bytes, 0, $bytes.Length); $Lease.Flush($true) } catch { throw "Windows test VM is already leased: $path" } } function Release-Lease { if ($script:Lease) { $script:Lease.Dispose(); $script:Lease = $null } } function Guest-Args([string[]] $Arguments) { if ([string]::IsNullOrWhiteSpace($GuestUser) -or [string]::IsNullOrWhiteSpace($GuestPassword)) { throw 'Set RVBOX_WINDOWS_GUEST_USER and RVBOX_WINDOWS_GUEST_PASSWORD in the host environment; secrets are never read from repository files or printed.' } return @('guestcontrol', $VmName, '--username', $GuestUser, '--password', $GuestPassword) + $Arguments } try { if ($Action -eq 'Status') { Invoke-VBox @('showvminfo', $VmName, '--machinereadable') | Where-Object { $_ -match '^(VMState|name|SnapshotName)=' } exit 0 } Require-RunId Acquire-Lease try { $state = (Invoke-VBox @('showvminfo', $VmName, '--machinereadable') | Where-Object { $_ -like 'VMState=*' }) -replace '^VMState="?([^"\r\n]+)"?$', '$1' switch ($Action) { 'Prepare' { if ($state -eq 'running') { Invoke-VBox (Guest-Args @('run', 'C:\Windows\System32\whoami.exe', '--', '/groups')) | Out-Null } else { Invoke-VBox @('startvm', $VmName, '--type', 'headless') | Out-Null Start-Sleep -Seconds 5 Invoke-VBox (Guest-Args @('run', 'C:\Windows\System32\whoami.exe', '--', '/groups')) | Out-Null } Invoke-VBox (Guest-Args @('run', 'C:\Windows\System32\query.exe', '--', 'user')) | Out-Null Write-Output "prepared VM=$VmName run=$RunId" } 'Run' { $guestBinary = if ($env:RVBOX_WINDOWS_GUEST_BINARY) { $env:RVBOX_WINDOWS_GUEST_BINARY } else { 'C:\ProgramData\RVBox\test\rvbox.exe' } Invoke-VBox (Guest-Args @('run', $guestBinary, '--', '--check-config', '--config', 'C:\ProgramData\RVBox\client.toml')) | Out-Null Write-Output "ran native Windows smoke run=$RunId" } 'Collect' { $destination = Join-Path $LeaseRoot $RunId New-Item -ItemType Directory -Force -Path $destination | Out-Null $guestArtifacts = if ($env:RVBOX_WINDOWS_GUEST_ARTIFACTS) { $env:RVBOX_WINDOWS_GUEST_ARTIFACTS } else { 'C:\ProgramData\RVBox\test-artifacts' } Invoke-VBox (Guest-Args @('copyfrom', $guestArtifacts, $destination, '--recursive')) | Out-Null Write-Output "collected native artifacts under $destination" } 'Stop' { if ($state -eq 'running') { Invoke-VBox @('controlvm', $VmName, 'acpipowerbutton') | Out-Null } Write-Output "requested graceful stop VM=$VmName" } 'Reset' { if ($state -eq 'running') { Invoke-VBox @('controlvm', $VmName, 'acpipowerbutton') | Out-Null; Start-Sleep -Seconds 3 } Invoke-VBox @('snapshot', $VmName, 'restore', $Snapshot) | Out-Null Write-Output "restored baseline snapshot=$Snapshot VM=$VmName run=$RunId" } } } finally { Release-Lease } } catch { Release-Lease Write-Error $_ exit 1 }