Files
PSProxmoxVE/tests/infrastructure
goodolclint-claude[bot] 08ee3ae249 chore: repair the local dev path and delete its dead scaffolding
The local dev environment had drifted badly from CI. Remove the parts that no
longer describe anything real, and make the rest match how CI actually runs.

Delete tests/dev.ps1. It wrapped run-integration.sh, which CI calls directly,
and duplicated the module build that script already performs internally. As a
second entry point it drifted: it still offered the PVE 8 leg retired in #88,
mounted the Docker socket for storage containers replaced by the storage VM in
#87, and pointed its remote-host examples at a runner decommissioned in the ARC
migration. All four documents describing it used a positional syntax that bound
the bare word to -Tests and then fell through to -Shell, so every documented
command silently opened a container shell. Recorded as D019.

Delete tests/infrastructure/runner/, a self-hosted-runner-in-Docker superseded
by Actions Runner Controller.

Make disk_storage and iso_storage required. Their defaults named a NAS that the
lab replaced with Ceph, and CI overrides both from repository variables, so the
defaults only ever misled local runs. require_env now fails at the top of a run
rather than at terraform apply, and the descriptions point at tests/.env.test
because cmd_provision deletes terraform.tfvars before applying.

preflight-cleanup.sh no longer falls back to the literal "local" storage. An
unset TF_VAR_iso_storage now skips only the ISO branch, leaving VM destroy and
state cleanup intact, and emits a workflow annotation: force-cleanup is the
only cleanup CI runs and it wipes Terraform state, so a silent skip strands the
uploaded ISO with nothing left to reclaim it.

Drop docker-ce-cli and the /var/run/docker.sock mount. Nothing in the container
has called docker since #87 moved storage into a VM; the remaining docker calls
run inside that VM over SSH. The CI job image is built from the same target, so
this also removes a third-party apt repository from its supply chain.

Rewrite tests/.env.test.example against what the code now requires, and fix the
documented commands in CLAUDE.md, README.md, copilot-instructions.md and the
integration README.
2026-09-01 16:27:10 -05:00
..

Nested Proxmox VE Test Infrastructure

This Terraform configuration provisions a throwaway nested Proxmox VE virtual machine on an existing Proxmox host. The nested instance is used as a target for PSProxmoxVE integration tests, providing a real PVE API to test against without risking production infrastructure.

Prerequisites

  • Terraform >= 1.5.0
  • Proxmox VE ISO downloaded from proxmox.com/en/downloads
  • API token on the existing Proxmox host with full administrator privileges (Datastore.Allocate, VM.Allocate, VM.Config.*, Sys.Modify, etc.)
  • curl and jq installed on the machine running Terraform (used by provisioner scripts)
  • SSH agent running with a key that can access the Proxmox host (used by the bpg/proxmox provider for file uploads)
  • A routable IP address available for the nested PVE instance (see Network section below)
  • Nested virtualization enabled on the Proxmox host (see Intel vs AMD notes below)

Quick Start

  1. Copy the example variables file and fill in your values:

    cp terraform.tfvars.example terraform.tfvars
    # Edit terraform.tfvars with your Proxmox host details, ISO path, and network config
    
  2. Initialize Terraform and download the provider:

    terraform init
    
  3. Review the plan:

    terraform plan
    
  4. Apply to create the nested PVE VM:

    terraform apply
    

    This will:

    • Upload the PVE ISO to the target node
    • Generate an answer file for unattended installation
    • Create and start the nested VM
    • Wait for the PVE API to become responsive (up to 10 minutes)
    • Create an API token (root@pam!integration) for integration tests
  5. After apply completes, retrieve the test connection details:

    terraform output pve_test_url
    terraform output -raw pve_test_api_token
    

Preparing the Installer ISO (one-time per PVE version)

The standard PVE installer ISO has no automated install mode. Before first use — and again whenever you upgrade to a new PVE release — run the following on the Proxmox host to produce a modified ISO that tells the installer to fetch its answer file from an attached FAT partition labeled PROXMOX-AIS:

ssh root@<proxmox-host>

# Paths are examples — adjust to match your NAS mount points
ORIGINAL_ISO=/mnt/pve/nas-nfs/template/iso/proxmox-ve_9.1-1.iso
MODIFIED_ISO=/mnt/pve/nas-nfs/template/iso/proxmox-ve_9.1-1-auto.iso

proxmox-auto-install-assistant prepare-iso "$ORIGINAL_ISO" \
  --fetch-from partition \
  --partition-label proxmox-ais \
  --output "$MODIFIED_ISO"

Update iso_file_id in terraform.tfvars to point to the new *-auto.iso file. The original ISO is left untouched.

How It Works

Answer File

The answer.toml.tftpl template generates a TOML answer file that automates the Proxmox VE installer. It configures the root password, network settings (static IP), disk layout, and other installation parameters so that no manual interaction is required during installation.

Wait Script

After the VM is created and booted from the ISO, the scripts/wait-for-api.sh script polls the nested PVE API endpoint every 10 seconds for up to 10 minutes. Installation typically takes 3-7 minutes depending on disk and CPU performance. The script exits successfully once the API returns a valid version response.

API Token Creation

Once the API is responsive, a provisioner script authenticates to the nested PVE using the root password and creates an API token (root@pam!integration) with full privileges (privsep=0). The token value is saved to .api-token and exposed via the pve_test_api_token output for use in integration tests.

Cleanup

To destroy the nested PVE VM and all associated resources:

terraform destroy

This removes the VM, uploaded ISO, and answer file snippet from the Proxmox host. The .api-token file is also cleaned up locally.

Network Configuration

The nested PVE instance requires a static IP address that is:

  • Routable from the machine running integration tests (CI runner or developer workstation)
  • Not in use by any other device on the network
  • On the same subnet as the network bridge (vmbr0 by default) on the host

The nested PVE will configure its own vmbr0 bridge internally, but from the host's perspective it appears as a single VM with the assigned IP address.

If your test environment uses VLANs or an isolated test network, adjust the network_bridge variable accordingly and ensure the CI runner has connectivity to that network.

Resource Requirements

The nested PVE VM requires sufficient resources to run Proxmox VE and potentially host lightweight test VMs inside it:

Resource Minimum Default Recommended
CPU cores 2 4 4+
Memory 4096 MB 8192 MB 8192+ MB
Disk 32 GB 64 GB 64+ GB

Ensure the Proxmox host has enough free resources to accommodate these allocations.

Intel vs AMD Nested Virtualization

Nested virtualization must be enabled on the host for the nested PVE to function as a hypervisor itself. The CPU type is set to host to pass through virtualization extensions.

Intel

Nested virtualization is typically enabled by default on modern Intel CPUs. Verify with:

cat /sys/module/kvm_intel/parameters/nested

If it shows N, enable it:

echo "options kvm_intel nested=1" > /etc/modprobe.d/kvm-intel.conf
modprobe -r kvm_intel && modprobe kvm_intel

AMD

AMD nested virtualization support varies. Check with:

cat /sys/module/kvm_amd/parameters/nested

If it shows 0, enable it:

echo "options kvm_amd nested=1" > /etc/modprobe.d/kvm-amd.conf
modprobe -r kvm_amd && modprobe kvm_amd

Note that AMD nested virtualization can be less stable than Intel in some configurations. If you encounter issues with nested VMs inside the nested PVE, the integration tests themselves (which test the PVE API, not nested VM creation) will still work -- only tests that attempt to create VMs inside the nested PVE would be affected.

Files

File Purpose
main.tf Provider config, VM resource, provisioners
variables.tf Input variable definitions with defaults
outputs.tf Test connection details for integration tests
storage-vm.tf Shared NFS/iSCSI/answer-server VM on the CI VLAN
answer.toml.tftpl Unattended PVE installer answer file template
scripts/setup-storage-server.sh Configures services on the storage VM over SSH
scripts/wait-for-api.sh Polls PVE API until responsive
terraform.tfvars.example Example variable values
.gitignore Excludes state, provider cache, secrets