#!/bin/bash # Script to detect locally available OS images and update image_location URLs in main.tf files # This script updates terraform configurations to use local image paths instead of remote URLs # It also supports reverting back to original remote URLs using hardcoded values # Function to display usage usage() { echo "Usage: $0 [options]" echo " options:" echo " -h, --help Display this help message" echo " -d, --dry-run Show what would be changed without making modifications" echo " -r, --revert Revert image_location URLs back to original remote URLs" echo "" echo "Example:" echo " $0 # Convert remote URLs to local paths (default)" echo " $0 -d # Dry run - show what would be updated" echo " $0 -r # Revert to original remote URLs" echo " $0 -r -d # Dry run revert mode" exit 1 } # Parse command line arguments DRY_RUN=false REVERT_MODE=false while [[ $# -gt 0 ]]; do case $1 in -h|--help) usage ;; -d|--dry-run) DRY_RUN=true shift ;; -r|--revert) REVERT_MODE=true shift ;; *) echo "Unknown option: $1" usage ;; esac done # Define the directory where images are stored IMAGE_DIR="/var/lib/libvirt/images" # Check if we have write permissions to the target directory if [[ ! -d "$IMAGE_DIR" ]]; then echo "Error: Directory $IMAGE_DIR does not exist" exit 1 fi # Function to get all locally available image files (including Fedora Rawhide) get_local_images() { find "$IMAGE_DIR" -maxdepth 1 -type f \( -name "*.qcow2" -o -name "*.raw" -o -name "*.img" \) | \ while read -r image; do basename "$image" done | sort } # Function to check if a local file matches the pattern for a Fedora Rawhide image is_fedora_rawhide_image() { local filename=$1 # Pattern matching for Fedora Rawhide images that contain "Fedora-Cloud-Base-Generic-Rawhide" if [[ "$filename" =~ ^Fedora-Cloud-Base-Generic-Rawhide.*\.qcow2$ ]]; then return 0 fi return 1 } # Function to get the latest Fedora Rawhide image path from local directory get_latest_fedora_rawhide_path() { local latest_file latest_file=$(find "$IMAGE_DIR" -maxdepth 1 -name "Fedora-Cloud-Base-Generic-Rawhide*.qcow2" -type f \ | sort -r \ | head -1) if [[ -n "$latest_file" ]]; then echo "$latest_file" fi } # Function to provide a mapping between local files and their original URLs create_original_url_mapping() { # Create a hash-like mapping for known images cat << 'EOF' noble-server-cloudimg-amd64.img=https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img Fedora-Cloud-Base-Generic-42-1.1.x86_64.qcow2=https://download.fedoraproject.org/pub/fedora/linux/releases/42/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-42-1.1.x86_64.qcow2 openSUSE-Tumbleweed-Minimal-VM.x86_64-Cloud.qcow2=https://download.opensuse.org/tumbleweed/appliances/openSUSE-Tumbleweed-Minimal-VM.x86_64-Cloud.qcow2 Rocky-10-GenericCloud-Base.latest.x86_64.qcow2=https://dl.rockylinux.org/pub/rocky/10/images/x86_64/Rocky-10-GenericCloud-Base.latest.x86_64.qcow2 debian-13-genericcloud-amd64.raw=https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.raw CentOS-Stream-GenericCloud-x86_64-10-latest.x86_64.qcow2=https://cloud.centos.org/centos/10-stream/x86_64/images/CentOS-Stream-GenericCloud-x86_64-10-latest.x86_64.qcow2 EOF } # Find all main.tf files and process them MAIN_TF_FILES=$(find . -name "main.tf" -type f) if [ -z "$MAIN_TF_FILES" ]; then echo "No main.tf files found!" exit 1 fi echo "Found main.tf files:" echo "$MAIN_TF_FILES" echo "" # Process each file for file in $MAIN_TF_FILES; do echo "Processing $file..." # Check if the file contains image_location lines if ! grep -q "image_location" "$file"; then echo " No image_location found in $file, skipping..." continue fi if [ "$REVERT_MODE" = true ]; then # Revert operation: change file:// back to original https:// URLs temp_file=$(mktemp) while IFS= read -r line || [[ -n "$line" ]]; do # Check if the line contains a file:// URL if [[ "$line" =~ .*image_location.*=.*\"file://(.*?)\".* ]]; then # Extract local path from the file:// URL local_file_path="${BASH_REMATCH[1]}" local_filename=$(basename "$local_file_path") # Handle Fedora Rawhide images specially if [[ "$local_filename" =~ ^Fedora-Cloud-Base-Generic-Rawhide.*\.qcow2$ ]]; then echo " Reverting Fedora Rawhide image: $local_filename" # For Rawhide, we'll keep the file:// reference but note that it's a special case if [ "$DRY_RUN" = false ]; then echo "$line" >> "$temp_file" else echo " Would process Fedora Rawhide image: $local_filename (keeping file:// reference)" echo "$line" >> "$temp_file" fi else # For regular images, try to map back to original URL # Create mapping for this specific case mapping=$(create_original_url_mapping) # Find matching original URL found_match=false while IFS= read -r mapping_line; do if [[ -z "$mapping_line" ]] || [[ "$mapping_line" =~ ^#.*$ ]]; then continue fi file_pattern=$(echo "$mapping_line" | cut -d'=' -f1) original_url=$(echo "$mapping_line" | cut -d'=' -f2) if [[ "$file_pattern" == "$local_filename" ]]; then echo " Found matching original URL: $local_filename" if [ "$DRY_RUN" = false ]; then # Use precise string replacement to avoid corrupting the file new_line="${line/\"file:\/\/$local_file_path\"/\"$original_url\"}" echo "$new_line" >> "$temp_file" echo " Reverted to original URL: $original_url" else echo " Would revert to: $original_url" echo "$line" >> "$temp_file" fi found_match=true break fi done <<< "$mapping" if [ "$found_match" = false ]; then echo " Warning: No matching original URL found for $local_filename" echo "$line" >> "$temp_file" fi fi else # Not a line with image_location, just copy as is echo "$line" >> "$temp_file" fi done < "$file" if [ "$DRY_RUN" = false ]; then mv "$temp_file" "$file" else rm "$temp_file" fi else # Normal operation: convert remote URLs to local paths temp_file=$(mktemp) while IFS= read -r line || [[ -n "$line" ]]; do if [[ "$line" =~ .*image_location.*=.*\"(https://.*)\".* ]]; then remote_url="${BASH_REMATCH[1]}" filename=$(basename "$remote_url") # Check if the local file exists (including Fedora Rawhide cases) local_path="$IMAGE_DIR/$filename" # Special handling for Fedora Rawhide - check if it's the right pattern if [[ "$filename" =~ ^Fedora-Cloud-Base-Generic-Rawhide.*\.qcow2$ ]]; then # For Fedora Rawhide, we need to be more flexible with matching patterns echo " Checking Fedora Rawhide pattern for: $filename" # Find the most recent Fedora image that matches the pattern but has different timestamp latest_rawhide=$(find "$IMAGE_DIR" -maxdepth 1 -name "Fedora-Cloud-Base-Generic-Rawhide*.qcow2" -type f \ | sort -r \ | head -1) if [[ -n "$latest_rawhide" ]]; then echo " Found matching local Fedora Rawhide image: $(basename $latest_rawhide)" if [ "$DRY_RUN" = false ]; then new_line="${line/\"$remote_url\"/\"file://$latest_rawhide\"}" echo "$new_line" >> "$temp_file" echo " Updated to local file: file://$latest_rawhide" else echo " Would update Fedora Rawhide to: file://$latest_rawhide" echo "$line" >> "$temp_file" fi else # No matching locally - check if we can find a similar pattern echo " Checking for any Fedora-Cloud-Base-Generic-Rawhide*.qcow2 files..." # Look for any file with the same prefix but different timestamp local_candidates=$(find "$IMAGE_DIR" -maxdepth 1 -name "*Fedora-Cloud-Base-Generic-Rawhide*" -type f) if [[ -n "$local_candidates" ]]; then most_recent=$(echo "$local_candidates" | sort -r | head -1) echo " Found matching local Fedora Rawhide image: $(basename $most_recent)" if [ "$DRY_RUN" = false ]; then new_line="${line/\"$remote_url\"/\"file://$most_recent\"}" echo "$new_line" >> "$temp_file" echo " Updated to local file: file://$most_recent" else echo " Would update Fedora Rawhide to: file://$most_recent" echo "$line" >> "$temp_file" fi else echo " Local Fedora Rawhide image not found, using original URL" echo "$line" >> "$temp_file" fi fi elif [[ -f "$local_path" ]]; then echo " Found local image: $filename" if [ "$DRY_RUN" = false ]; then # Use precise string replacement to avoid corrupting the file new_line="${line/\"$remote_url\"/\"file://$local_path\"}" echo "$new_line" >> "$temp_file" echo " Updated to: file://$local_path" else echo " Would update to: file://$local_path" echo "$line" >> "$temp_file" fi else echo " Local image not found: $filename" echo "$line" >> "$temp_file" fi else # Not a line with image_location, just copy as is echo "$line" >> "$temp_file" fi done < "$file" if [ "$DRY_RUN" = false ]; then mv "$temp_file" "$file" else rm "$temp_file" fi fi done echo "" if [ "$DRY_RUN" = false ]; then if [ "$REVERT_MODE" = true ]; then echo "Image location URLs have been successfully reverted to original remote URLs!" else echo "Image location URLs have been successfully updated in all main.tf files!" fi else echo "Dry run completed - no changes were made." fi # Show a summary of what would be changed echo "" echo "Summary of local image availability:" find "$IMAGE_DIR" -maxdepth 1 -type f -name "*.qcow2" -o -name "*.raw" -o -name "*.img" | \ while read -r image; do filename=$(basename "$image") echo " ✓ $filename" done # If nothing was found, show what images are expected if ! find "$IMAGE_DIR" -maxdepth 1 -type f -name "*.qcow2" -o -name "*.raw" -o -name "*.img" | grep -q .; then echo " No local images found in $IMAGE_DIR" echo " Run download_images.sh to download required images." fi