#!/usr/bin/env python3 """ Test Composer Orchestration with Debian Forge This script tests the integration between OSBuild Composer and our Debian Forge build orchestration system. """ import json import os import sys from pathlib import Path def test_blueprint_loading(): """Test loading and validation of blueprint files""" print("Testing blueprint loading...") blueprint_dir = Path("blueprints") if not blueprint_dir.exists(): print(" ❌ Blueprint directory not found") return False blueprints = ["debian-atomic-base.json", "debian-atomic-workstation.json", "debian-atomic-server.json"] for blueprint_file in blueprints: blueprint_path = blueprint_dir / blueprint_file if not blueprint_path.exists(): print(f" ❌ Blueprint file not found: {blueprint_file}") return False try: with open(blueprint_path, 'r') as f: blueprint = json.load(f) # Validate basic structure required_fields = ["name", "description", "version", "packages"] for field in required_fields: if field not in blueprint: print(f" ❌ {blueprint_file} missing field: {field}") return False print(f" ✅ {blueprint_file} loaded and validated") except json.JSONDecodeError as e: print(f" ❌ {blueprint_file} invalid JSON: {e}") return False except Exception as e: print(f" ❌ {blueprint_file} error: {e}") return False return True def test_pipeline_generation(): """Test OSBuild pipeline generation from blueprints""" print("\nTesting pipeline generation...") # Test pipeline generation for base blueprint base_pipeline = { "version": "2", "pipelines": [ { "name": "build", "runner": "org.osbuild.linux", "stages": [ { "type": "org.osbuild.debootstrap", "options": { "suite": "bookworm", "mirror": "http://deb.debian.org/debian", "arch": "amd64", "variant": "minbase", "apt_proxy": "http://192.168.1.101:3142" } }, { "type": "org.osbuild.apt", "options": { "packages": ["systemd", "systemd-sysv", "dbus", "udev", "ostree", "linux-image-amd64"], "recommends": False, "update": True, "apt_proxy": "http://192.168.1.101:3142" } }, { "type": "org.osbuild.ostree.commit", "options": { "repo": "debian-atomic", "branch": "debian/bookworm", "subject": "Debian Bookworm atomic system", "body": "Debian Bookworm minbase system with systemd and OSTree" } } ] } ] } # Validate pipeline structure if "version" not in base_pipeline: print(" ❌ Pipeline missing version") return False if "pipelines" not in base_pipeline: print(" ❌ Pipeline missing pipelines array") return False if len(base_pipeline["pipelines"]) == 0: print(" ❌ Pipeline array is empty") return False build_pipeline = base_pipeline["pipelines"][0] if "stages" not in build_pipeline: print(" ❌ Build pipeline missing stages") return False # Validate stages expected_stages = ["org.osbuild.debootstrap", "org.osbuild.apt", "org.osbuild.ostree.commit"] actual_stages = [stage["type"] for stage in build_pipeline["stages"]] for expected_stage in expected_stages: if expected_stage not in actual_stages: print(f" ❌ Missing expected stage: {expected_stage}") return False print(" ✅ Pipeline generation is valid") return True def test_build_orchestration_integration(): """Test integration with our build orchestration system""" print("\nTesting build orchestration integration...") # Check if build orchestration components exist orchestration_files = [ "build_orchestrator.py", "artifact_manager.py", "build_environment.py", "osbuild_integration.py" ] for file in orchestration_files: if not os.path.exists(file): print(f" ❌ Build orchestration file not found: {file}") return False # Test build request structure build_request = { "blueprint": "debian-atomic-base", "target": "qcow2", "architecture": "amd64", "compose_type": "debian-atomic", "priority": "normal" } required_fields = ["blueprint", "target", "architecture"] for field in required_fields: if field not in build_request: print(f" ❌ Build request missing field: {field}") return False print(" ✅ Build orchestration integration is valid") return True def test_composer_api_integration(): """Test composer API integration patterns""" print("\nTesting composer API integration...") # Test API endpoints api_endpoints = { "blueprints": "/api/v1/blueprints", "compose": "/api/v1/compose", "status": "/api/v1/compose/status", "logs": "/api/v1/compose/logs", "upload": "/api/v1/upload" } for endpoint, path in api_endpoints.items(): if not path.startswith("/api/v1/"): print(f" ❌ Invalid API path for {endpoint}: {path}") return False # Test HTTP methods http_methods = { "submit_blueprint": "POST", "get_blueprint": "GET", "update_blueprint": "PUT", "delete_blueprint": "DELETE", "start_compose": "POST", "get_compose_status": "GET", "cancel_compose": "DELETE" } valid_methods = ["GET", "POST", "PUT", "DELETE"] for operation, method in http_methods.items(): if method not in valid_methods: print(f" ❌ Invalid HTTP method for {operation}: {method}") return False print(" ✅ Composer API integration is valid") return True def test_debian_specific_features(): """Test Debian-specific composer features""" print("\nTesting Debian-specific features...") # Test Debian package management debian_packages = { "base_system": ["systemd", "systemd-sysv", "dbus", "udev"], "desktop_environment": ["gnome-shell", "gnome-session", "gdm3"], "server_services": ["nginx", "postgresql", "redis-server"], "development_tools": ["build-essential", "git", "python3"], "security_tools": ["fail2ban", "unattended-upgrades"] } for category, packages in debian_packages.items(): if not isinstance(packages, list): print(f" ❌ {category} packages must be a list") return False for package in packages: if not isinstance(package, str): print(f" ❌ Package name must be string: {package}") return False # Test Debian repository configuration debian_repos = { "main": "http://deb.debian.org/debian", "security": "http://security.debian.org/debian-security", "updates": "http://deb.debian.org/debian" } for repo_name, repo_url in debian_repos.items(): if not repo_url.startswith("http"): print(f" ❌ Invalid repository URL for {repo_name}: {repo_url}") return False print(" ✅ Debian-specific features are valid") return True def test_end_to_end_workflow(): """Test end-to-end Debian atomic build workflow""" print("\nTesting end-to-end workflow...") # Define the complete workflow workflow = [ "blueprint_submission", "pipeline_generation", "build_execution", "ostree_composition", "image_generation", "deployment_preparation" ] # Test workflow dependencies workflow_deps = { "blueprint_submission": [], "pipeline_generation": ["blueprint_submission"], "build_execution": ["pipeline_generation"], "ostree_composition": ["build_execution"], "image_generation": ["ostree_composition"], "deployment_preparation": ["image_generation"] } for step, dependencies in workflow_deps.items(): if step not in workflow: print(f" ❌ Workflow step not found: {step}") return False for dep in dependencies: if dep not in workflow: print(f" ❌ Workflow dependency not found: {dep}") return False # Test workflow validation workflow_validation = { "blueprint_submission": "User submits blueprint via composer API", "pipeline_generation": "Composer generates OSBuild pipeline from blueprint", "build_execution": "Our build orchestrator executes the pipeline", "ostree_composition": "Debian stages create atomic filesystem", "image_generation": "Output formats (ISO, QCOW2, RAW) generated", "deployment_preparation": "OSTree commits available for deployment" } for step, description in workflow_validation.items(): if not description or len(description) < 10: print(f" ❌ Workflow step {step} missing description") return False print(" ✅ End-to-end workflow is valid") return True def main(): """Main test function""" print("Composer Orchestration Test for Debian Forge") print("=" * 60) tests = [ ("Blueprint Loading", test_blueprint_loading), ("Pipeline Generation", test_pipeline_generation), ("Build Orchestration Integration", test_build_orchestration_integration), ("Composer API Integration", test_composer_api_integration), ("Debian-Specific Features", test_debian_specific_features), ("End-to-End Workflow", test_end_to_end_workflow) ] results = [] for test_name, test_func in tests: try: result = test_func() results.append((test_name, result)) except Exception as e: print(f" ❌ {test_name} test failed with exception: {e}") results.append((test_name, False)) # Summary print("\n" + "=" * 60) print("TEST SUMMARY") print("=" * 60) passed = 0 total = len(results) for test_name, result in results: status = "✅ PASS" if result else "❌ FAIL" print(f"{test_name}: {status}") if result: passed += 1 print(f"\nOverall: {passed}/{total} tests passed") if passed == total: print("🎉 All tests passed! Composer orchestration is ready for production.") return 0 else: print("⚠️ Some tests failed. Please review the issues above.") return 1 if __name__ == '__main__': sys.exit(main())