From 0dff14f4ba1369ce9d7478b4371500040226508b Mon Sep 17 00:00:00 2001 From: Clint Branham Date: Wed, 18 Mar 2026 14:19:24 -0500 Subject: [PATCH] fix(ci): use Terraform provider for ISO upload, fix port/lockfile issues - Use proxmox_virtual_environment_file resource to upload the auto-install ISO via the API. terraform destroy now cleans up both the VM and ISO. - Remove manual upload-to-pve.sh and cleanup-pve-storage.sh scripts. - Commit .terraform.lock.hcl for reproducible CI builds. - Replace iso_file_id variable with iso_local_path and iso_storage. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/integration-tests.yml | 33 +-------- tests/infrastructure/.gitignore | 2 +- tests/infrastructure/.terraform.lock.hcl | 63 +++++++++++++++++ tests/infrastructure/main.tf | 12 +++- .../scripts/cleanup-pve-storage.sh | 25 ------- tests/infrastructure/scripts/upload-to-pve.sh | 69 ------------------- tests/infrastructure/terraform.tfvars.example | 7 +- tests/infrastructure/variables.tf | 10 ++- 8 files changed, 89 insertions(+), 132 deletions(-) create mode 100644 tests/infrastructure/.terraform.lock.hcl delete mode 100755 tests/infrastructure/scripts/cleanup-pve-storage.sh delete mode 100755 tests/infrastructure/scripts/upload-to-pve.sh diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 4450a2a..114839c 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -85,23 +85,6 @@ jobs: ${SCRIPTS_DIR}/first-boot.sh \ "/tmp/${{ steps.iso.outputs.filename }}" - - name: Upload ISO to PVE storage - if: inputs.skip_provision != true - id: upload - shell: bash - env: - PVE_API_TOKEN: ${{ secrets.PVE_API_TOKEN }} - run: | - bash ${SCRIPTS_DIR}/upload-to-pve.sh \ - "$(echo '${{ secrets.PVE_ENDPOINT }}' | sed -E 's|https?://||;s|:.*||')" \ - "${PVE_API_TOKEN}" \ - "${{ secrets.PVE_TARGET_NODE }}" \ - "local" \ - "/tmp/${{ steps.iso.outputs.filename }}" \ - "iso" | tee /tmp/upload-output.txt - ISO_VOLID=$(tail -1 /tmp/upload-output.txt) - echo "iso_volid=${ISO_VOLID}" >> "$GITHUB_OUTPUT" - - name: Terraform init if: inputs.skip_provision != true shell: bash @@ -117,7 +100,7 @@ jobs: 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_file_id: ${{ steps.upload.outputs.iso_volid }} + TF_VAR_iso_local_path: /tmp/${{ steps.iso.outputs.filename }} TF_VAR_test_vm_password: ${{ env.PVE_PASSWORD }} run: | terraform apply -auto-approve -input=false @@ -236,18 +219,6 @@ jobs: 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_file_id: ${{ steps.upload.outputs.iso_volid }} + TF_VAR_iso_local_path: /tmp/${{ steps.iso.outputs.filename }} TF_VAR_test_vm_password: ${{ env.PVE_PASSWORD }} run: terraform destroy -auto-approve -input=false - - - name: Clean up uploaded ISO - if: always() && inputs.skip_provision != true - shell: bash - env: - PVE_API_TOKEN: ${{ secrets.PVE_API_TOKEN }} - run: | - PARENT_HOST=$(echo '${{ secrets.PVE_ENDPOINT }}' | sed -E 's|https?://||;s|:.*||') - bash ${SCRIPTS_DIR}/cleanup-pve-storage.sh \ - "${PARENT_HOST}" \ - "${PVE_API_TOKEN}" \ - "${{ steps.upload.outputs.iso_volid }}" || true diff --git a/tests/infrastructure/.gitignore b/tests/infrastructure/.gitignore index 53922fa..8ea4e9f 100644 --- a/tests/infrastructure/.gitignore +++ b/tests/infrastructure/.gitignore @@ -2,6 +2,6 @@ .terraform/ *.tfvars !terraform.tfvars.example -.terraform.lock.hcl +# Keep .terraform.lock.hcl tracked for reproducible CI builds .api-token .build/ diff --git a/tests/infrastructure/.terraform.lock.hcl b/tests/infrastructure/.terraform.lock.hcl new file mode 100644 index 0000000..afb264c --- /dev/null +++ b/tests/infrastructure/.terraform.lock.hcl @@ -0,0 +1,63 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/bpg/proxmox" { + version = "0.98.1" + constraints = ">= 0.70.0" + hashes = [ + "h1:oegYetLP9VqFSnRfHeUQnm6v58dH/VT4ivJSXg4RLnI=", + "zh:165705d0a0db92ccc5b1d23b0033cc87011f1151f4f178ad9df61473c3ca30b5", + "zh:3ecab62be80d89cf8f019ab23809254f854eb5a443682a1a22a52e0d80b1273f", + "zh:48b520a3f5b8cccca66e0dc7b1e264e01d2f35ef8a9bbfc6b02964cd7235914b", + "zh:7fa7e166b924b5192ff963f47a56772c7b34f0004d825d830c3122eab970534f", + "zh:804bdf5353d3a3ad9d06e6faada1b8600af8ecfd1e5b23280a81e62b0cb7037d", + "zh:87ebe8b56a895870ebc0d353fba5ebbb4615401dc3f53a2e157f158cb9025d2d", + "zh:9b0c8e02c036152bcb2f54c354da4647d10d016396e5ca2b70084a8bd5546970", + "zh:a0039465282dc75ac9c53750b61ba4038d56bed545cb39df669b6216bdff82da", + "zh:bbc2c187c8283b6a5f44cbd65f4b6e5d534a3be1eeb1a7c8caaaa6f1a7e48e76", + "zh:d04aed773582e95c8d7610833e85b204fe8c01c09c94e7128914c8e22b9218c7", + "zh:d7a1433a7d6f1b158f499a93d3fb1825eec73eac4c61b00a39f2f5e95ae0965a", + "zh:df46cb59fa1ddda335934720ad22d8205bb8902ce780ca32205f1622f1fa1649", + "zh:e100ad439313db667a5c4ca53ae4c54d8b1c72299a2240a1493823cb89cb0781", + "zh:f26e0763dbe6a6b2195c94b44696f2110f7f55433dc142839be16b9697fa5597", + ] +} + +provider "registry.terraform.io/hashicorp/local" { + version = "2.7.0" + constraints = ">= 2.0.0" + hashes = [ + "h1:sSwlfp2etjCaE9hIF7bJBDjRIhDCVFglEOVyiCI7vgs=", + "zh:261fec71bca13e0a7812dc0d8ae9af2b4326b24d9b2e9beab3d2400fab5c5f9a", + "zh:308da3b5376a9ede815042deec5af1050ec96a5a5410a2206ae847d82070a23e", + "zh:3d056924c420464dc8aba10e1915956b2e5c4d55b11ffff79aa8be563fbfe298", + "zh:643256547b155459c45e0a3e8aab0570db59923c68daf2086be63c444c8c445b", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:7aa4d0b853f84205e8cf79f30c9b2c562afbfa63592f7231b6637e5d7a6b5b27", + "zh:7dc251bbc487d58a6ab7f5b07ec9edc630edb45d89b761dba28e0e2ba6b1c11f", + "zh:7ee0ca546cd065030039168d780a15cbbf1765a4c70cd56d394734ab112c93da", + "zh:b1d5d80abb1906e6c6b3685a52a0192b4ca6525fe090881c64ec6f67794b1300", + "zh:d81ea9856d61db3148a4fc6c375bf387a721d78fc1fea7a8823a027272a47a78", + "zh:df0a1f0afc947b8bfc88617c1ad07a689ce3bd1a29fd97318392e6bdd32b230b", + "zh:dfbcad800240e0c68c43e0866f2a751cff09777375ec701918881acf67a268da", + ] +} + +provider "registry.terraform.io/hashicorp/null" { + version = "3.2.4" + hashes = [ + "h1:L5V05xwp/Gto1leRryuesxjMfgZwjb7oool4WS1UEFQ=", + "zh:59f6b52ab4ff35739647f9509ee6d93d7c032985d9f8c6237d1f8a59471bbbe2", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:795c897119ff082133150121d39ff26cb5f89a730a2c8c26f3a9c1abf81a9c43", + "zh:7b9c7b16f118fbc2b05a983817b8ce2f86df125857966ad356353baf4bff5c0a", + "zh:85e33ab43e0e1726e5f97a874b8e24820b6565ff8076523cc2922ba671492991", + "zh:9d32ac3619cfc93eb3c4f423492a8e0f79db05fec58e449dee9b2d5873d5f69f", + "zh:9e15c3c9dd8e0d1e3731841d44c34571b6c97f5b95e8296a45318b94e5287a6e", + "zh:b4c2ab35d1b7696c30b64bf2c0f3a62329107bd1a9121ce70683dec58af19615", + "zh:c43723e8cc65bcdf5e0c92581dcbbdcbdcf18b8d2037406a5f2033b1e22de442", + "zh:ceb5495d9c31bfb299d246ab333f08c7fb0d67a4f82681fbf47f2a21c3e11ab5", + "zh:e171026b3659305c558d9804062762d168f50ba02b88b231d20ec99578a6233f", + "zh:ed0fe2acdb61330b01841fa790be00ec6beaac91d41f311fb8254f74eb6a711f", + ] +} diff --git a/tests/infrastructure/main.tf b/tests/infrastructure/main.tf index 1dce7f8..96b3715 100644 --- a/tests/infrastructure/main.tf +++ b/tests/infrastructure/main.tf @@ -14,6 +14,16 @@ provider "proxmox" { insecure = var.proxmox_insecure } +resource "proxmox_virtual_environment_file" "auto_iso" { + content_type = "iso" + datastore_id = var.iso_storage + node_name = var.target_node + + source_file { + path = var.iso_local_path + } +} + resource "proxmox_virtual_environment_vm" "nested_pve" { name = var.vm_name node_name = var.target_node @@ -45,7 +55,7 @@ resource "proxmox_virtual_environment_vm" "nested_pve" { } cdrom { - file_id = var.iso_file_id + file_id = proxmox_virtual_environment_file.auto_iso.id interface = "ide2" } diff --git a/tests/infrastructure/scripts/cleanup-pve-storage.sh b/tests/infrastructure/scripts/cleanup-pve-storage.sh deleted file mode 100755 index 6766bff..0000000 --- a/tests/infrastructure/scripts/cleanup-pve-storage.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash -# Remove an uploaded ISO from PVE storage. -# -# Usage: cleanup-pve-storage.sh -# e.g.: cleanup-pve-storage.sh 172.16.100.150 "root@pam!tok=secret" "local:iso/proxmox-ve_9.1-1-auto.iso" -set -euo pipefail - -PVE_HOST="$1" -API_TOKEN="$2" -VOLUME_ID="$3" - -# Extract node name from API -NODE=$(curl -sk -H "Authorization: PVEAPIToken=${API_TOKEN}" \ - "https://${PVE_HOST}/api2/json/nodes" \ - | python3 -c "import json,sys; print(json.load(sys.stdin)['data'][0]['node'])") - -echo "Deleting ${VOLUME_ID} from node ${NODE}..." -ENCODED_VOLID=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${VOLUME_ID}', safe=''))") - -RESPONSE=$(curl -sk -X DELETE \ - -H "Authorization: PVEAPIToken=${API_TOKEN}" \ - "https://${PVE_HOST}/api2/json/nodes/${NODE}/storage/local/content/${ENCODED_VOLID}") - -echo "Response: $RESPONSE" -echo "Cleanup complete." diff --git a/tests/infrastructure/scripts/upload-to-pve.sh b/tests/infrastructure/scripts/upload-to-pve.sh deleted file mode 100755 index 82aa4b6..0000000 --- a/tests/infrastructure/scripts/upload-to-pve.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env bash -# Upload a file to PVE storage via the REST API. -# -# Usage: upload-to-pve.sh -# content-type: "iso" or "vztmpl" -set -euo pipefail - -PVE_HOST="$1" -API_TOKEN="$2" -NODE="$3" -STORAGE="$4" -FILE_PATH="$5" -CONTENT_TYPE="${6:-iso}" - -FILENAME=$(basename "$FILE_PATH") -API_URL="https://${PVE_HOST}/api2/json/nodes/${NODE}/storage/${STORAGE}/upload" - -# Check if file already exists on storage -echo "Checking if ${FILENAME} already exists on ${STORAGE}..." -EXISTING=$(curl -sk \ - -H "Authorization: PVEAPIToken=${API_TOKEN}" \ - "https://${PVE_HOST}/api2/json/nodes/${NODE}/storage/${STORAGE}/content" \ - | python3 -c " -import json, sys -data = json.load(sys.stdin).get('data', []) -for item in data: - if item.get('volid', '').endswith('/${FILENAME}'): - print(item['volid']) - break -" 2>/dev/null || true) - -if [ -n "$EXISTING" ]; then - echo "Already exists: $EXISTING (skipping upload)" - echo "$EXISTING" - exit 0 -fi - -echo "Uploading ${FILENAME} ($(du -h "$FILE_PATH" | cut -f1)) to ${STORAGE}:${CONTENT_TYPE}/..." -RESPONSE=$(curl -sk --progress-bar \ - -H "Authorization: PVEAPIToken=${API_TOKEN}" \ - -F "content=${CONTENT_TYPE}" \ - -F "filename=@${FILE_PATH}" \ - "$API_URL") - -UPID=$(echo "$RESPONSE" | python3 -c "import json,sys; print(json.load(sys.stdin)['data'])" 2>/dev/null || true) -if [ -z "$UPID" ]; then - echo "ERROR: Upload failed. Response: $RESPONSE" >&2 - exit 1 -fi - -echo "Upload started: $UPID" - -# Wait for upload task to complete -echo "Waiting for upload task..." -for i in $(seq 1 60); do - STATUS=$(curl -sk \ - -H "Authorization: PVEAPIToken=${API_TOKEN}" \ - "https://${PVE_HOST}/api2/json/nodes/${NODE}/tasks/${UPID}/status" \ - | python3 -c "import json,sys; d=json.load(sys.stdin)['data']; print(d.get('status','unknown'))" 2>/dev/null || echo "unknown") - if [ "$STATUS" = "stopped" ]; then - echo "Upload complete: ${STORAGE}:${CONTENT_TYPE}/${FILENAME}" - echo "${STORAGE}:${CONTENT_TYPE}/${FILENAME}" - exit 0 - fi - sleep 2 -done - -echo "ERROR: Upload task did not complete within timeout" >&2 -exit 1 diff --git a/tests/infrastructure/terraform.tfvars.example b/tests/infrastructure/terraform.tfvars.example index 7c0e6ae..e56fee4 100644 --- a/tests/infrastructure/terraform.tfvars.example +++ b/tests/infrastructure/terraform.tfvars.example @@ -3,12 +3,13 @@ proxmox_endpoint = "https://pve.example.com:8006" proxmox_api_token = "root@pam!terraform=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" target_node = "pve" -# Auto-install ISO (prepared by scripts/prepare-auto-iso.sh, then uploaded) -iso_file_id = "local:iso/proxmox-ve_9.1-1-auto.iso" -disk_storage = "local-lvm" +# Local path to the prepared auto-install ISO (output of prepare-auto-iso.sh) +iso_local_path = "/tmp/proxmox-ve_9.1-1-auto.iso" +disk_storage = "local-lvm" # Optional overrides # cores = 4 # memory = 8192 # disk_size = 64 # network_bridge = "vmbr0" +# iso_storage = "local" diff --git a/tests/infrastructure/variables.tf b/tests/infrastructure/variables.tf index 6a95b44..ae77152 100644 --- a/tests/infrastructure/variables.tf +++ b/tests/infrastructure/variables.tf @@ -56,11 +56,17 @@ variable "disk_storage" { default = "nas-iscsi-lvm" } -variable "iso_file_id" { - description = "Proxmox file ID of the auto-install PVE ISO (e.g. local:iso/proxmox-ve_9.1-1-auto.iso)" +variable "iso_local_path" { + description = "Local path to the prepared auto-install PVE ISO on the runner" type = string } +variable "iso_storage" { + description = "Proxmox storage pool for uploading the ISO (must accept ISO content type)" + type = string + default = "local" +} + variable "network_bridge" { description = "Network bridge on the host to attach the nested PVE VM to" type = string