Add comprehensive tests for Parks API and models

- Implemented extensive test cases for the Parks API, covering endpoints for listing, retrieving, creating, updating, and deleting parks.
- Added tests for filtering, searching, and ordering parks in the API.
- Created tests for error handling in the API, including malformed JSON and unsupported methods.
- Developed model tests for Park, ParkArea, Company, and ParkReview models, ensuring validation and constraints are enforced.
- Introduced utility mixins for API and model testing to streamline assertions and enhance test readability.
- Included integration tests to validate complete workflows involving park creation, retrieval, updating, and deletion.
This commit is contained in:
pacnpal
2025-08-17 19:36:20 -04:00
parent 17228e9935
commit c26414ff74
210 changed files with 24155 additions and 833 deletions

275
scripts/unraid/main.py Normal file
View File

@@ -0,0 +1,275 @@
#!/usr/bin/env python3
"""
Unraid VM Manager for ThrillWiki - Main Orchestrator
Follows the Ubuntu autoinstall guide exactly:
1. Creates modified Ubuntu ISO with autoinstall configuration
2. Manages VM lifecycle on Unraid server
3. Handles ThrillWiki deployment automation
"""
import os
import sys
import time
import logging
import tempfile
from pathlib import Path
from typing import Optional
# Import our modular components
from iso_builder import UbuntuISOBuilder
from vm_manager import UnraidVMManager
# Configuration
UNRAID_HOST = os***REMOVED***iron.get("UNRAID_HOST", "localhost")
UNRAID_USER = os***REMOVED***iron.get("UNRAID_USER", "root")
VM_NAME = os***REMOVED***iron.get("VM_NAME", "thrillwiki-vm")
VM_MEMORY = int(os***REMOVED***iron.get("VM_MEMORY", 4096)) # MB
VM_VCPUS = int(os***REMOVED***iron.get("VM_VCPUS", 2))
VM_DISK_SIZE = int(os***REMOVED***iron.get("VM_DISK_SIZE", 50)) # GB
SSH_PUBLIC_KEY = os***REMOVED***iron.get("SSH_PUBLIC_KEY", "")
# Network Configuration
VM_IP = os***REMOVED***iron.get("VM_IP", "dhcp")
VM_GATEWAY = os***REMOVED***iron.get("VM_GATEWAY", "192.168.20.1")
VM_NETMASK = os***REMOVED***iron.get("VM_NETMASK", "255.255.255.0")
VM_NETWORK = os***REMOVED***iron.get("VM_NETWORK", "192.168.20.0/24")
# GitHub Configuration
REPO_URL = os***REMOVED***iron.get("REPO_URL", "")
GITHUB_USERNAME = os***REMOVED***iron.get("GITHUB_USERNAME", "")
GITHUB_TOKEN = os***REMOVED***iron.get("GITHUB_TOKEN", "")
# Ubuntu version preference
UBUNTU_VERSION = os***REMOVED***iron.get("UBUNTU_VERSION", "24.04")
# Setup logging
os.makedirs("logs", exist_ok=True)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[logging.FileHandler("logs/unraid-vm.log"), logging.StreamHandler()],
)
logger = logging.getLogger(__name__)
class ThrillWikiVMOrchestrator:
"""Main orchestrator for ThrillWiki VM deployment."""
def __init__(self):
self.vm_manager = UnraidVMManager(VM_NAME, UNRAID_HOST, UNRAID_USER)
self.iso_builder = None
def create_autoinstall_user_data(self) -> str:
"""Create autoinstall user-data configuration."""
# Read autoinstall template
template_path = Path(__file__).parent / "autoinstall-user-data.yaml"
if not template_path.exists():
raise FileNotFoundError(f"Autoinstall template not found: {template_path}")
with open(template_path, 'r', encoding='utf-8') as f:
template = f.read()
# Replace placeholders using string replacement (avoiding .format() due to curly braces in YAML)
user_data = template.replace(
"{SSH_PUBLIC_KEY}", SSH_PUBLIC_KEY if SSH_PUBLIC_KEY else "# No SSH key provided"
).replace(
"{GITHUB_REPO}", REPO_URL if REPO_URL else ""
)
# Update network configuration based on VM_IP setting
if VM_IP.lower() == "dhcp":
# Keep DHCP configuration as-is
pass
else:
# Replace with static IP configuration
network_config = f"""dhcp4: false
addresses:
- {VM_IP}/24
gateway4: {VM_GATEWAY}
nameservers:
addresses:
- 8.8.8.8
- 8.8.4.4"""
user_data = user_data.replace("dhcp4: true", network_config)
return user_data
def build_autoinstall_iso(self) -> Path:
"""Build Ubuntu autoinstall ISO following the guide."""
logger.info("🔨 Building Ubuntu autoinstall ISO...")
# Create ISO builder
self.iso_builder = UbuntuISOBuilder(VM_NAME)
# Create user-data configuration
user_data = self.create_autoinstall_user_data()
# Build autoinstall ISO
iso_output_path = Path(f"/tmp/{VM_NAME}-ubuntu-autoinstall.iso")
success = self.iso_builder.build_autoinstall_iso(
user_data=user_data,
output_path=iso_output_path,
ubuntu_version=UBUNTU_VERSION
)
if not success:
raise RuntimeError("Failed to build autoinstall ISO")
logger.info(f"✅ Autoinstall ISO built successfully: {iso_output_path}")
return iso_output_path
def deploy_vm(self) -> bool:
"""Complete VM deployment process."""
try:
logger.info("🚀 Starting ThrillWiki VM deployment...")
# Step 1: Check SSH connectivity
logger.info("📡 Testing Unraid connectivity...")
if not self.vm_manager.authenticate():
logger.error("❌ Cannot connect to Unraid server")
return False
# Step 2: Build autoinstall ISO
logger.info("🔨 Building Ubuntu autoinstall ISO...")
iso_path = self.build_autoinstall_iso()
# Step 3: Upload ISO to Unraid
logger.info("📤 Uploading autoinstall ISO to Unraid...")
remote_iso_path = self.vm_manager.upload_iso_to_unraid(iso_path)
# Step 4: Create/update VM configuration
logger.info("⚙️ Creating VM configuration...")
success = self.vm_manager.create_vm(
vm_memory=VM_MEMORY,
vm_vcpus=VM_VCPUS,
vm_disk_size=VM_DISK_SIZE,
vm_ip=VM_IP
)
if not success:
logger.error("❌ Failed to create VM configuration")
return False
# Step 5: Start VM
logger.info("🟢 Starting VM...")
success = self.vm_manager.start_vm()
if not success:
logger.error("❌ Failed to start VM")
return False
logger.info("🎉 VM deployment completed successfully!")
logger.info("")
logger.info("📋 Next Steps:")
logger.info("1. VM is now booting with Ubuntu autoinstall")
logger.info("2. Installation will take 15-30 minutes")
logger.info("3. Use 'python main.py ip' to get VM IP when ready")
logger.info("4. SSH to VM and run /home/thrillwiki/deploy-thrillwiki.sh")
logger.info("")
return True
except Exception as e:
logger.error(f"❌ VM deployment failed: {e}")
return False
finally:
# Cleanup ISO builder temp files
if self.iso_builder:
self.iso_builder.cleanup()
def get_vm_info(self) -> dict:
"""Get VM information."""
return {
"name": VM_NAME,
"status": self.vm_manager.vm_status(),
"ip": self.vm_manager.get_vm_ip(),
"memory": VM_MEMORY,
"vcpus": VM_VCPUS,
"disk_size": VM_DISK_SIZE
}
def main():
"""Main entry point."""
import argparse
parser = argparse.ArgumentParser(
description="ThrillWiki VM Manager - Ubuntu Autoinstall on Unraid",
epilog="""
Examples:
python main.py setup # Complete VM setup with autoinstall
python main.py start # Start existing VM
python main.py ip # Get VM IP address
python main.py status # Get VM status
python main.py delete # Remove VM completely
""",
formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument(
"action",
choices=["setup", "create", "start", "stop", "status", "ip", "delete", "info"],
help="Action to perform"
)
args = parser.parse_args()
# Create orchestrator
orchestrator = ThrillWikiVMOrchestrator()
if args.action == "setup":
logger.info("🚀 Setting up complete ThrillWiki VM environment...")
success = orchestrator.deploy_vm()
sys.exit(0 if success else 1)
elif args.action == "create":
logger.info("⚙️ Creating VM configuration...")
success = orchestrator.vm_manager.create_vm(VM_MEMORY, VM_VCPUS, VM_DISK_SIZE, VM_IP)
sys.exit(0 if success else 1)
elif args.action == "start":
logger.info("🟢 Starting VM...")
success = orchestrator.vm_manager.start_vm()
sys.exit(0 if success else 1)
elif args.action == "stop":
logger.info("🛑 Stopping VM...")
success = orchestrator.vm_manager.stop_vm()
sys.exit(0 if success else 1)
elif args.action == "status":
status = orchestrator.vm_manager.vm_status()
print(f"VM Status: {status}")
sys.exit(0)
elif args.action == "ip":
ip = orchestrator.vm_manager.get_vm_ip()
if ip:
print(f"VM IP: {ip}")
print(f"SSH: ssh thrillwiki@{ip}")
print(f"Deploy: ssh thrillwiki@{ip} '/home/thrillwiki/deploy-thrillwiki.sh'")
sys.exit(0)
else:
print("❌ Failed to get VM IP (VM may not be ready yet)")
sys.exit(1)
elif args.action == "info":
info = orchestrator.get_vm_info()
print("🖥️ VM Information:")
print(f" Name: {info['name']}")
print(f" Status: {info['status']}")
print(f" IP: {info['ip'] or 'Not available'}")
print(f" Memory: {info['memory']} MB")
print(f" vCPUs: {info['vcpus']}")
print(f" Disk: {info['disk_size']} GB")
sys.exit(0)
elif args.action == "delete":
logger.info("🗑️ Deleting VM and all files...")
success = orchestrator.vm_manager.delete_vm()
sys.exit(0 if success else 1)
if __name__ == "__main__":
main()