mirror of
https://github.com/GoodOlClint/PSProxmoxVE.git
synced 2026-08-11 14:56:53 +00:00
8d268aee61
The deps.json file causes .NET's AssemblyLoadContext to conflict with PowerShell's own runtime assembly resolution, resulting in FileNotFoundException for System.Runtime 9.0.0.0 on all PS 7.x platforms. Binary PS modules should not include deps.json. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
257 lines
10 KiB
YAML
257 lines
10 KiB
YAML
name: Integration Tests
|
|
|
|
# Real integration tests against a live Proxmox VE instance.
|
|
# By default, provisions a throwaway nested PVE VM via Terraform, runs tests,
|
|
# then destroys it. Can also run against a pre-existing PVE with skip_provision.
|
|
#
|
|
# Required repository secrets (for provisioning):
|
|
# PVE_ENDPOINT - Parent PVE API URL (e.g. https://pve.example.com:8006)
|
|
# PVE_API_TOKEN - Parent PVE API token (for Terraform + uploads)
|
|
# PVE_TARGET_NODE - Parent PVE node name
|
|
#
|
|
# Optional secrets (for skip_provision mode):
|
|
# PVETEST_HOST - Hostname or IP of a pre-existing nested PVE
|
|
# PVETEST_APITOKEN - API token for the pre-existing nested PVE
|
|
#
|
|
# WARNING: Integration tests create and destroy real VMs, upload files,
|
|
# and modify network configuration. Never run against production.
|
|
|
|
on:
|
|
push:
|
|
branches: [ main ]
|
|
pull_request:
|
|
branches: [ main ]
|
|
workflow_dispatch:
|
|
inputs:
|
|
pve_version:
|
|
description: 'PVE version to test (8 or 9)'
|
|
required: false
|
|
type: choice
|
|
options:
|
|
- '9'
|
|
- '8'
|
|
default: '9'
|
|
skip_provision:
|
|
description: 'Skip provisioning (use existing PVE from PVETEST_HOST secret)'
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
|
|
env:
|
|
INFRA_DIR: tests/infrastructure
|
|
SCRIPTS_DIR: tests/infrastructure/scripts
|
|
PVE_PASSWORD: "Testpass123!"
|
|
|
|
jobs:
|
|
integration:
|
|
runs-on: [self-hosted, proxmox, integration]
|
|
timeout-minutes: 45
|
|
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
|
|
# ── Pre-flight cleanup ─────────────────────────────────────────────
|
|
|
|
- name: Pre-flight cleanup
|
|
if: inputs.skip_provision != true
|
|
shell: bash
|
|
id: iso
|
|
env:
|
|
PVE_API_TOKEN: ${{ secrets.PVE_API_TOKEN }}
|
|
run: |
|
|
VERSION="${{ inputs.pve_version || '9' }}"
|
|
if [ "$VERSION" = "9" ]; then
|
|
echo "base=/opt/pve-isos/proxmox-ve_9.1-1.iso" >> "$GITHUB_OUTPUT"
|
|
echo "filename=proxmox-ve_9.1-1-auto.iso" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "base=/opt/pve-isos/proxmox-ve_8.4-1.iso" >> "$GITHUB_OUTPUT"
|
|
echo "filename=proxmox-ve_8.4-1-auto.iso" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
bash ${SCRIPTS_DIR}/preflight-cleanup.sh \
|
|
"${{ secrets.PVE_ENDPOINT }}" \
|
|
"${PVE_API_TOKEN}" \
|
|
99900 \
|
|
"auto.iso" \
|
|
"${INFRA_DIR}"
|
|
|
|
# ── Provision nested PVE ───────────────────────────────────────────
|
|
|
|
- name: Generate answer file
|
|
if: inputs.skip_provision != true
|
|
shell: bash
|
|
run: |
|
|
sed "s/\${root_password}/${PVE_PASSWORD}/" \
|
|
${INFRA_DIR}/answer.toml.tftpl > ${RUNNER_TEMP}/answer.toml
|
|
echo "Generated answer file:"
|
|
cat ${RUNNER_TEMP}/answer.toml
|
|
|
|
- name: Prepare auto-install ISO
|
|
if: inputs.skip_provision != true
|
|
shell: bash
|
|
run: |
|
|
bash ${SCRIPTS_DIR}/prepare-auto-iso.sh \
|
|
"${{ steps.iso.outputs.base }}" \
|
|
${RUNNER_TEMP}/answer.toml \
|
|
${SCRIPTS_DIR}/first-boot.sh \
|
|
"${{ runner.temp }}/${{ steps.iso.outputs.filename }}"
|
|
|
|
- name: Terraform init
|
|
if: inputs.skip_provision != true
|
|
shell: bash
|
|
working-directory: ${{ env.INFRA_DIR }}
|
|
run: terraform init -input=false
|
|
|
|
- name: Terraform apply
|
|
if: inputs.skip_provision != true
|
|
id: terraform
|
|
shell: bash
|
|
working-directory: ${{ env.INFRA_DIR }}
|
|
env:
|
|
TF_VAR_proxmox_endpoint: ${{ secrets.PVE_ENDPOINT }}
|
|
TF_VAR_proxmox_api_token: ${{ secrets.PVE_API_TOKEN }}
|
|
TF_VAR_target_node: ${{ secrets.PVE_TARGET_NODE }}
|
|
TF_VAR_iso_local_path: ${{ runner.temp }}/${{ steps.iso.outputs.filename }}
|
|
TF_VAR_test_vm_password: ${{ env.PVE_PASSWORD }}
|
|
run: |
|
|
terraform apply -auto-approve -input=false
|
|
echo "vm_id=$(terraform output -raw pve_test_vm_id)" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Wait for install and create API token
|
|
if: inputs.skip_provision != true
|
|
id: provision
|
|
shell: bash
|
|
env:
|
|
PVE_API_TOKEN: ${{ secrets.PVE_API_TOKEN }}
|
|
run: |
|
|
OUTPUT=$(bash ${SCRIPTS_DIR}/create-api-token.sh \
|
|
"${{ secrets.PVE_ENDPOINT }}" \
|
|
"${PVE_API_TOKEN}" \
|
|
"${{ steps.terraform.outputs.vm_id }}" \
|
|
"${PVE_PASSWORD}" \
|
|
900)
|
|
VM_IP=$(echo "$OUTPUT" | grep "^IP=" | cut -d= -f2)
|
|
VM_TOKEN=$(echo "$OUTPUT" | grep "^TOKEN=" | cut -d= -f2-)
|
|
# Mask sensitive values before they appear in logs
|
|
echo "::add-mask::${VM_IP}"
|
|
echo "::add-mask::${VM_TOKEN}"
|
|
echo "Nested PVE ready at ${VM_IP}"
|
|
echo "host=${VM_IP}" >> "$GITHUB_OUTPUT"
|
|
echo "token=${VM_TOKEN}" >> "$GITHUB_OUTPUT"
|
|
|
|
# ── Resolve test target ────────────────────────────────────────────
|
|
|
|
- name: Set test target
|
|
id: target
|
|
shell: bash
|
|
run: |
|
|
if [ "${{ inputs.skip_provision }}" = "true" ]; then
|
|
echo "host=${{ secrets.PVETEST_HOST }}" >> "$GITHUB_OUTPUT"
|
|
echo "port=${{ secrets.PVETEST_PORT || '8006' }}" >> "$GITHUB_OUTPUT"
|
|
echo "token=${{ secrets.PVETEST_APITOKEN }}" >> "$GITHUB_OUTPUT"
|
|
echo "node=${{ secrets.PVETEST_NODE || 'pve' }}" >> "$GITHUB_OUTPUT"
|
|
echo "storage=${{ secrets.PVETEST_STORAGE || 'local' }}" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "host=${{ steps.provision.outputs.host }}" >> "$GITHUB_OUTPUT"
|
|
echo "port=8006" >> "$GITHUB_OUTPUT"
|
|
echo "token=${{ steps.provision.outputs.token }}" >> "$GITHUB_OUTPUT"
|
|
echo "node=pve" >> "$GITHUB_OUTPUT"
|
|
echo "storage=local" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
- name: Verify PVE API reachable
|
|
shell: bash
|
|
run: |
|
|
HOST="${{ steps.target.outputs.host }}"
|
|
PORT="${{ steps.target.outputs.port }}"
|
|
TOKEN="${{ steps.target.outputs.token }}"
|
|
echo "Testing connectivity to PVE API at ${HOST}:${PORT}..."
|
|
if curl -sk --connect-timeout 10 \
|
|
-H "Authorization: PVEAPIToken=${TOKEN}" \
|
|
"https://${HOST}:${PORT}/api2/json/nodes" | grep -q '"node"'; then
|
|
echo "PVE API is responsive"
|
|
else
|
|
echo "::error::Cannot reach PVE API at ${HOST}:${PORT}"
|
|
exit 1
|
|
fi
|
|
|
|
# ── Build and test ─────────────────────────────────────────────────
|
|
|
|
- name: Build module
|
|
run: dotnet publish src/PSProxmoxVE/PSProxmoxVE.csproj --configuration Release --framework net9.0 --output ./publish/net9.0
|
|
|
|
- name: Remove deps.json from publish output
|
|
run: rm -f ./publish/net9.0/PSProxmoxVE.deps.json
|
|
|
|
- name: Install Pester 5
|
|
shell: pwsh
|
|
run: Install-Module -Name Pester -MinimumVersion 5.0 -Force -Scope CurrentUser
|
|
|
|
- name: Copy module to module path
|
|
shell: pwsh
|
|
run: |
|
|
$modulePath = "$HOME/.local/share/powershell/Modules/PSProxmoxVE"
|
|
New-Item -ItemType Directory -Path $modulePath -Force | Out-Null
|
|
Copy-Item -Path ./publish/net9.0/* -Destination $modulePath -Recurse -Force
|
|
|
|
- name: Create test ISO
|
|
shell: bash
|
|
run: dd if=/dev/urandom of=${RUNNER_TEMP}/pvetest.iso bs=1M count=1 2>/dev/null
|
|
|
|
- name: Run integration tests
|
|
shell: pwsh
|
|
env:
|
|
PVETEST_HOST: ${{ steps.target.outputs.host }}
|
|
PVETEST_PORT: ${{ steps.target.outputs.port }}
|
|
PVETEST_APITOKEN: ${{ steps.target.outputs.token }}
|
|
PVETEST_NODE: ${{ steps.target.outputs.node }}
|
|
PVETEST_STORAGE: ${{ steps.target.outputs.storage }}
|
|
PVETEST_ISO_PATH: ${{ runner.temp }}/pvetest.iso
|
|
run: |
|
|
Import-Module Pester -MinimumVersion 5.0
|
|
$config = New-PesterConfiguration
|
|
$config.Run.Path = "tests/PSProxmoxVE.Tests/Integration"
|
|
$config.Filter.Tag = "Integration"
|
|
$config.Output.Verbosity = "Detailed"
|
|
$config.TestResult.Enabled = $true
|
|
$config.TestResult.OutputFormat = "NUnitXml"
|
|
$config.TestResult.OutputPath = "TestResults/integration-results.xml"
|
|
Invoke-Pester -Configuration $config
|
|
|
|
- name: Upload test results
|
|
if: always()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: integration-test-results
|
|
path: TestResults/
|
|
|
|
# ── Teardown ───────────────────────────────────────────────────────
|
|
|
|
- name: Terraform destroy
|
|
if: always() && inputs.skip_provision != true
|
|
shell: bash
|
|
working-directory: ${{ env.INFRA_DIR }}
|
|
env:
|
|
TF_VAR_proxmox_endpoint: ${{ secrets.PVE_ENDPOINT }}
|
|
TF_VAR_proxmox_api_token: ${{ secrets.PVE_API_TOKEN }}
|
|
TF_VAR_target_node: ${{ secrets.PVE_TARGET_NODE }}
|
|
TF_VAR_iso_local_path: ${{ runner.temp }}/${{ steps.iso.outputs.filename }}
|
|
TF_VAR_test_vm_password: ${{ env.PVE_PASSWORD }}
|
|
run: |
|
|
terraform init -input=false
|
|
terraform destroy -auto-approve -input=false || true
|
|
rm -f terraform.tfstate terraform.tfstate.backup .terraform.tfstate.lock.info
|
|
|
|
- name: Final cleanup
|
|
if: always() && inputs.skip_provision != true
|
|
shell: bash
|
|
env:
|
|
PVE_API_TOKEN: ${{ secrets.PVE_API_TOKEN }}
|
|
run: |
|
|
bash ${SCRIPTS_DIR}/preflight-cleanup.sh \
|
|
"${{ secrets.PVE_ENDPOINT }}" \
|
|
"${PVE_API_TOKEN}" \
|
|
99900 \
|
|
"${{ steps.iso.outputs.filename }}" \
|
|
"${INFRA_DIR}" || true
|