#!/usr/bin/env python3 """ Test Debian Atomic Blueprint System This script validates the blueprint system for Debian atomic images, testing blueprint structure, validation, and OSBuild pipeline integration. """ import json import os import sys import tempfile from pathlib import Path def test_blueprint_structure(): """Test basic blueprint structure validation""" print("Testing blueprint structure validation...") # Test basic blueprint basic_blueprint = { "name": "debian-atomic-base", "description": "Debian Atomic Base System", "version": "0.0.1", "packages": [ {"name": "systemd"}, {"name": "systemd-sysv"}, {"name": "dbus"}, {"name": "udev"}, {"name": "ostree"}, {"name": "linux-image-amd64"} ], "modules": [], "groups": [], "customizations": { "user": [ { "name": "debian", "description": "Debian user", "password": "$6$rounds=656000$YQvKxqQKqQKqQKqQ$...", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...", "home": "/home/debian", "shell": "/bin/bash", "groups": ["wheel"], "uid": 1000, "gid": 1000 } ], "services": { "enabled": ["sshd", "systemd-networkd"] } } } # Validate required fields required_fields = ["name", "description", "version", "packages"] for field in required_fields: if field not in basic_blueprint: print(f" ❌ Missing required field: {field}") return False # Validate packages structure if not isinstance(basic_blueprint["packages"], list): print(" ❌ Packages must be a list") return False for package in basic_blueprint["packages"]: if "name" not in package: print(" ❌ Package missing name") return False print(" ✅ Basic blueprint structure is valid") return True def test_blueprint_variants(): """Test different blueprint variants""" print("\nTesting blueprint variants...") variants = [ "debian-atomic-base", "debian-atomic-workstation", "debian-atomic-server" ] for variant in variants: blueprint = create_variant_blueprint(variant) # Validate variant-specific requirements if variant == "debian-atomic-workstation": if "desktop" not in [g["name"] for g in blueprint.get("groups", [])]: print(f" ❌ {variant} missing desktop group") return False elif variant == "debian-atomic-server": if "server" not in [g["name"] for g in blueprint.get("groups", [])]: print(f" ❌ {variant} missing server group") return False print(f" ✅ {variant} blueprint is valid") return True def create_variant_blueprint(variant): """Create a blueprint for a specific variant""" base_packages = ["systemd", "systemd-sysv", "dbus", "udev", "ostree", "linux-image-amd64"] if variant == "debian-atomic-workstation": packages = base_packages + ["gnome-shell", "gnome-session", "gdm3", "network-manager", "firefox-esr"] groups = [{"name": "desktop"}] services = ["sshd", "systemd-networkd", "gdm3", "NetworkManager"] elif variant == "debian-atomic-server": packages = base_packages + ["nginx", "postgresql", "redis-server", "fail2ban"] groups = [{"name": "server"}] services = ["sshd", "systemd-networkd", "nginx", "postgresql", "redis-server", "fail2ban"] else: # base packages = base_packages groups = [] services = ["sshd", "systemd-networkd"] return { "name": variant, "description": f"Debian Atomic {variant.replace('debian-atomic-', '').title()}", "version": "0.0.1", "packages": [{"name": pkg} for pkg in packages], "modules": [], "groups": groups, "customizations": { "user": [ { "name": "debian", "description": "Debian user", "password": "$6$rounds=656000$YQvKxqQKqQKqQKqQ$...", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...", "home": "/home/debian", "shell": "/bin/bash", "groups": ["wheel"] + [g["name"] for g in groups], "uid": 1000, "gid": 1000 } ], "services": { "enabled": services } } } def test_blueprint_variables(): """Test blueprint variables and templating""" print("\nTesting blueprint variables...") variables = { "architecture": "amd64", "suite": "bookworm", "variant": "minbase", "mirror": "http://deb.debian.org/debian", "apt_proxy": "http://192.168.1.101:3142" } # Validate variable types expected_types = { "architecture": str, "suite": str, "variant": str, "mirror": str, "apt_proxy": str } for var, expected_type in expected_types.items(): if var in variables and not isinstance(variables[var], expected_type): print(f" ❌ Variable {var} has wrong type") return False # Test package groups package_groups = { "base": ["systemd", "systemd-sysv", "dbus", "udev", "ostree"], "desktop": ["gnome-shell", "gnome-session", "gdm3"], "server": ["nginx", "postgresql", "redis-server"], "development": ["build-essential", "git", "python3", "nodejs"], "security": ["fail2ban", "unattended-upgrades", "rkhunter"] } for group, packages in package_groups.items(): if not isinstance(packages, list): print(f" ❌ Package group {group} must be a list") return False print(" ✅ Blueprint variables are valid") return True def test_osbuild_pipeline_integration(): """Test OSBuild pipeline integration""" print("\nTesting OSBuild pipeline integration...") # Test debootstrap stage debootstrap_stage = { "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" } } if "type" not in debootstrap_stage: print(" ❌ Stage missing type") return False if "options" not in debootstrap_stage: print(" ❌ Stage missing options") return False # Test apt stage apt_stage = { "type": "org.osbuild.apt", "options": { "packages": ["systemd", "systemd-sysv", "dbus", "udev"], "recommends": False, "update": True, "apt_proxy": "http://192.168.1.101:3142" } } if "type" not in apt_stage: print(" ❌ Stage missing type") return False # Test ostree commit stage ostree_stage = { "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" } } if "type" not in ostree_stage: print(" ❌ Stage missing type") return False print(" ✅ OSBuild pipeline integration is valid") return True def test_blueprint_validation(): """Test blueprint validation rules""" print("\nTesting blueprint validation rules...") # Test invalid blueprint (missing required fields) invalid_blueprint = { "name": "invalid-blueprint" # Missing description, version, packages } required_fields = ["description", "version", "packages"] missing_fields = [] for field in required_fields: if field not in invalid_blueprint: missing_fields.append(field) if missing_fields: print(f" ✅ Correctly identified missing fields: {missing_fields}") else: print(" ❌ Failed to identify missing fields") return False # Test package validation invalid_package = { "name": "debian-atomic-invalid", "description": "Invalid blueprint", "version": "0.0.1", "packages": [ {"wrong_field": "systemd"} # Missing 'name' field ] } invalid_packages = [] for package in invalid_package["packages"]: if "name" not in package: invalid_packages.append(package) if invalid_packages: print(" ✅ Correctly identified invalid packages") else: print(" ❌ Failed to identify invalid packages") return False print(" ✅ Blueprint validation rules work correctly") return True def test_composer_integration(): """Test composer integration patterns""" print("\nTesting composer integration patterns...") # Test composer API structure composer_api = { "endpoints": { "blueprints": "/api/v1/blueprints", "compose": "/api/v1/compose", "status": "/api/v1/compose/status", "logs": "/api/v1/compose/logs" }, "methods": { "submit_blueprint": "POST", "get_blueprint": "GET", "start_compose": "POST", "get_compose_status": "GET" } } # Validate API structure if "endpoints" not in composer_api or "methods" not in composer_api: print(" ❌ Composer API missing required sections") return False # Test blueprint submission workflow workflow = [ "submit_blueprint", "get_blueprint", "start_compose", "get_compose_status" ] for step in workflow: if step not in composer_api["methods"]: print(f" ❌ Missing workflow step: {step}") return False print(" ✅ Composer integration patterns are valid") return True def main(): """Main test function""" print("Debian Atomic Blueprint System Test") print("=" * 50) tests = [ ("Blueprint Structure", test_blueprint_structure), ("Blueprint Variants", test_blueprint_variants), ("Blueprint Variables", test_blueprint_variables), ("OSBuild Pipeline Integration", test_osbuild_pipeline_integration), ("Blueprint Validation", test_blueprint_validation), ("Composer Integration", test_composer_integration) ] 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" + "=" * 50) print("TEST SUMMARY") print("=" * 50) 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! Blueprint system is ready for composer integration.") return 0 else: print("⚠️ Some tests failed. Please review the issues above.") return 1 if __name__ == '__main__': sys.exit(main())