From 518e5c838aa8349afcd0f92c5ea6e4a9c04c4b45 Mon Sep 17 00:00:00 2001 From: Clint Branham Date: Thu, 19 Mar 2026 14:08:52 -0500 Subject: [PATCH] feat(test): use new cmdlets in test VM setup and add guest agent tests prepare-test-vm.sh now uses module cmdlets for 7 of 8 steps: - Set-PveVmConfig -AdditionalConfig replaces SSH qm set (8 config keys) - Test-PveVmGuestAgent replaces SSH qm agent ping - Only SSH remaining: pvesm set, SCP snippet, qm importdisk New integration tests for guest agent cmdlets: - Test-PveVmGuestAgent: verify agent responds - Get-PveVmGuestNetwork: verify interfaces with IPv4 addresses - Invoke-PveVmGuestExec: run hostname command, verify exit code + output Also force Node.js 24 for delete-package-versions (no v6 available yet). Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/integration-tests.yml | 2 + .../Integration/Integration.Tests.ps1 | 32 +++++ .../infrastructure/scripts/prepare-test-vm.sh | 134 ++++++++---------- 3 files changed, 97 insertions(+), 71 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 9ade554..404d4b9 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -95,6 +95,8 @@ jobs: if: always() runs-on: ubuntu-latest timeout-minutes: 5 + env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true steps: - name: Delete old container image versions uses: actions/delete-package-versions@v5 diff --git a/tests/PSProxmoxVE.Tests/Integration/Integration.Tests.ps1 b/tests/PSProxmoxVE.Tests/Integration/Integration.Tests.ps1 index 5234dd5..d23f575 100644 --- a/tests/PSProxmoxVE.Tests/Integration/Integration.Tests.ps1 +++ b/tests/PSProxmoxVE.Tests/Integration/Integration.Tests.ps1 @@ -560,6 +560,38 @@ Describe 'Integration Tests' -Tag 'Integration' { } } + # ----------------------------------------------------------------------- + Context 'Guest Agent — Cmdlets' { + It 'Should ping guest agent (Test-PveVmGuestAgent)' { + if (Skip-IfNoLinuxVm) { return } + + $result = Test-PveVmGuestAgent -Node $script:Node -VmId $script:LinuxVmId + $result | Should -BeTrue + } + + It 'Should discover guest network interfaces (Get-PveVmGuestNetwork)' { + if (Skip-IfNoLinuxVm) { return } + + $interfaces = Get-PveVmGuestNetwork -Node $script:Node -VmId $script:LinuxVmId + $interfaces | Should -Not -BeNullOrEmpty + + # Should have at least one interface with an IPv4 address + $withIp = $interfaces | Where-Object { + $_.IpAddresses | Where-Object { $_.Type -eq 'ipv4' -and $_.Address -ne '127.0.0.1' } + } + $withIp | Should -Not -BeNullOrEmpty + } + + It 'Should execute a command in guest (Invoke-PveVmGuestExec)' { + if (Skip-IfNoLinuxVm) { return } + + $result = Invoke-PveVmGuestExec -Node $script:Node -VmId $script:LinuxVmId -Command 'hostname' + $result | Should -Not -BeNullOrEmpty + $result.ExitCode | Should -Be 0 + $result.Stdout | Should -Not -BeNullOrEmpty + } + } + # ----------------------------------------------------------------------- Context 'Guest Agent VM — Lifecycle' { It 'Should have a running Linux VM with guest agent' { diff --git a/tests/infrastructure/scripts/prepare-test-vm.sh b/tests/infrastructure/scripts/prepare-test-vm.sh index 7a063dd..eed54e9 100755 --- a/tests/infrastructure/scripts/prepare-test-vm.sh +++ b/tests/infrastructure/scripts/prepare-test-vm.sh @@ -1,12 +1,18 @@ #!/usr/bin/env bash -# Prepares a Debian cloud image VM with qemu-guest-agent on the nested PVE. +# Prepares an Ubuntu cloud image VM with qemu-guest-agent on the nested PVE. # -# Uses PSProxmoxVE cmdlets where possible (we're integration testing the module -# after all), SSH/SCP only for operations the API doesn't expose: -# - SCP the cloud-init snippet (no snippet upload API) -# - pvesm set to enable snippets content type -# - qm importdisk (no API equivalent) -# - qm set for --scsi0, --cicustom, --boot, --agent (Set-PveVmConfig doesn't expose these) +# Uses PSProxmoxVE cmdlets for all supported operations: +# - Invoke-PveStorageDownload (cloud image download) +# - New-PveVm (VM creation) +# - Set-PveVmConfig -AdditionalConfig (disk/boot/agent/cloud-init config) +# - Set-PveCloudInitConfig (user/password/IP) +# - Start-PveVm (boot) +# - Test-PveVmGuestAgent (agent ping) +# +# SSH/SCP only for operations without API support: +# - pvesm set (enable snippets content type) +# - SCP snippet upload (no snippet API) +# - qm importdisk (no disk import API) # # Usage: prepare-test-vm.sh # @@ -28,6 +34,8 @@ SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLeve SSH_CMD="sshpass -p ${ROOT_PASS} ssh ${SSH_OPTS} root@${NESTED_IP}" SCP_CMD="sshpass -p ${ROOT_PASS} scp ${SSH_OPTS}" +CONNECT_CMD="Connect-PveServer -Server '${NESTED_IP}' -ApiToken '${API_TOKEN}' -SkipCertificateCheck" + echo "=== Preparing test Linux VM (VMID ${VMID}) on ${NESTED_IP} ===" # ── Step 1: Upload cloud-init snippet (SSH — no API for snippets) ──── @@ -46,92 +54,76 @@ ${SSH_CMD} "mkdir -p /var/lib/vz/snippets && pvesm set local --content iso,vztmp ${SCP_CMD} "${USERDATA}" "root@${NESTED_IP}:/var/lib/vz/snippets/test-vm-userdata.yml" rm -f "${USERDATA}" -# ── Step 2: Download cloud image (PSProxmoxVE cmdlet) ──────────────── +# ── Step 2: Download cloud image (module cmdlet) ───────────────────── echo "Downloading Ubuntu cloud image via Invoke-PveStorageDownload..." pwsh -NoProfile -Command " - Import-Module PSProxmoxVE - Connect-PveServer -Server '${NESTED_IP}' -ApiToken '${API_TOKEN}' -SkipCertificateCheck + Import-Module PSProxmoxVE; ${CONNECT_CMD} Invoke-PveStorageDownload \ - -Node '${NODE}' \ - -Storage 'local' \ - -Url '${CLOUD_IMAGE_URL}' \ - -Filename '${CLOUD_IMAGE_FILENAME}' \ - -ContentType 'iso' \ - -Wait + -Node '${NODE}' -Storage 'local' \ + -Url '${CLOUD_IMAGE_URL}' -Filename '${CLOUD_IMAGE_FILENAME}' \ + -ContentType 'iso' -Wait " -# ── Step 3: Create VM (PSProxmoxVE cmdlet) ─────────────────────────── +# ── Step 3: Create VM (module cmdlet) ──────────────────────────────── echo "Creating VM ${VMID} via New-PveVm..." pwsh -NoProfile -Command " - Import-Module PSProxmoxVE - Connect-PveServer -Server '${NESTED_IP}' -ApiToken '${API_TOKEN}' -SkipCertificateCheck - New-PveVm \ - -Node '${NODE}' \ - -VmId ${VMID} \ - -Name 'debian-test' \ - -Memory 512 \ - -Cores 1 \ - -OsType 'l26' \ - -Wait + Import-Module PSProxmoxVE; ${CONNECT_CMD} + New-PveVm -Node '${NODE}' -VmId ${VMID} -Name 'ubuntu-test' \ + -Memory 512 -Cores 1 -OsType 'l26' -Wait " -# ── Step 4: Import disk and configure (SSH — no importdisk API) ────── -echo "Importing disk and configuring VM..." -${SSH_CMD} bash <&1 | tail -1" -# Import the downloaded image as a disk (stored in local ISO dir by download-url API) -qm importdisk ${VMID} /var/lib/vz/template/iso/${CLOUD_IMAGE_FILENAME} local-lvm 2>&1 | tail -1 +# ── Step 5: Configure VM (module cmdlet — AdditionalConfig) ────────── +echo "Configuring VM via Set-PveVmConfig -AdditionalConfig..." +pwsh -NoProfile -Command " + Import-Module PSProxmoxVE; ${CONNECT_CMD} + Set-PveVmConfig -Node '${NODE}' -VmId ${VMID} -AdditionalConfig @{ + scsihw = 'virtio-scsi-single' + scsi0 = 'local-lvm:vm-${VMID}-disk-0' + boot = 'order=scsi0' + serial0 = 'socket' + agent = '1' + net0 = 'virtio,bridge=vmbr0' + ide2 = 'local-lvm:cloudinit' + cicustom = 'user=local:snippets/test-vm-userdata.yml' + } +" -# Attach disk, enable agent, configure boot, add cloud-init -qm set ${VMID} \ - --scsihw virtio-scsi-single \ - --scsi0 local-lvm:vm-${VMID}-disk-0 \ - --boot order=scsi0 \ - --serial0 socket \ - --agent 1 \ - --net0 virtio,bridge=vmbr0 \ - --ide2 local-lvm:cloudinit \ - --cicustom "user=local:snippets/test-vm-userdata.yml" -REMOTE - -# ── Step 5: Set cloud-init config (PSProxmoxVE cmdlet) ─────────────── +# ── Step 6: Set cloud-init config (module cmdlet) ──────────────────── echo "Setting cloud-init config via Set-PveCloudInitConfig..." pwsh -NoProfile -Command " - Import-Module PSProxmoxVE - Connect-PveServer -Server '${NESTED_IP}' -ApiToken '${API_TOKEN}' -SkipCertificateCheck - Set-PveCloudInitConfig \ - -Node '${NODE}' \ - -VmId ${VMID} \ + Import-Module PSProxmoxVE; ${CONNECT_CMD} + Set-PveCloudInitConfig -Node '${NODE}' -VmId ${VMID} \ -CiUser 'root' \ -Password (ConvertTo-SecureString '${ROOT_PASS}' -AsPlainText -Force) \ -IpConfig0 'ip=dhcp' - # Note: Invoke-PveCloudInitRegenerate has a bug (returns cloud-config - # content as UPID). Skipping — PVE regenerates cloud-init on VM start. " -# ── Step 6: Start VM (PSProxmoxVE cmdlet) ──────────────────────────── +# ── Step 7: Start VM (module cmdlet) ───────────────────────────────── echo "Starting VM ${VMID} via Start-PveVm..." pwsh -NoProfile -Command " - Import-Module PSProxmoxVE - Connect-PveServer -Server '${NESTED_IP}' -ApiToken '${API_TOKEN}' -SkipCertificateCheck + Import-Module PSProxmoxVE; ${CONNECT_CMD} Start-PveVm -Node '${NODE}' -VmId ${VMID} -Wait " -# ── Step 7: Wait for guest agent ───────────────────────────────────── +# ── Step 8: Wait for guest agent (module cmdlet) ───────────────────── echo "Waiting for guest agent on VM ${VMID} (cloud-init installing packages)..." -TIMEOUT=300 -ELAPSED=0 -while [ $ELAPSED -lt $TIMEOUT ]; do - if ${SSH_CMD} "qm agent ${VMID} ping" 2>/dev/null; then - echo "Guest agent responding on VM ${VMID}" - echo "LINUX_VMID=${VMID}" - exit 0 - fi - sleep 10 - ELAPSED=$((ELAPSED + 10)) - echo " Waiting... (${ELAPSED}s / ${TIMEOUT}s)" -done +pwsh -NoProfile -Command " + Import-Module PSProxmoxVE; ${CONNECT_CMD} + \$timeout = 300; \$elapsed = 0 + while (\$elapsed -lt \$timeout) { + if (Test-PveVmGuestAgent -Node '${NODE}' -VmId ${VMID}) { + Write-Host 'Guest agent responding on VM ${VMID}' + exit 0 + } + Start-Sleep -Seconds 10 + \$elapsed += 10 + Write-Host \" Waiting... (\${elapsed}s / \${timeout}s)\" + } + throw 'Timeout waiting for guest agent on VM ${VMID}' +" -echo "ERROR: Timeout waiting for guest agent on VM ${VMID}" >&2 -exit 1 +echo "LINUX_VMID=${VMID}"