diff options
| author | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-02-06 03:54:31 +0000 |
|---|---|---|
| committer | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-02-06 03:54:31 +0000 |
| commit | 5aab0f92f1e774305c23802566d75922f65e0862 (patch) | |
| tree | 11193654c643a39e768c267435d562e8e04e8794 /tests/test_container_cross_install.py | |
| parent | 8c31b451c6f5a9d0bb526ee77f467e5b48846bb4 (diff) | |
| download | meta-virtualization-container-cross-install.tar.gz | |
container-cross-install: add tests and documentation for custom service filescontainer-cross-install
Add pytest tests to verify CONTAINER_SERVICE_FILE varflag support:
TestCustomServiceFileSupport (unit tests, no build required):
- test_bbclass_has_service_file_support
- test_bundle_class_has_service_file_support
- test_service_file_map_syntax
- test_install_custom_service_function
TestCustomServiceFileBoot (boot tests, require built image):
- test_systemd_services_directory_exists
- test_container_services_present
- test_container_service_enabled
- test_custom_service_content
- test_podman_quadlet_directory
Documentation updates:
- docs/container-bundling.md: Add "Custom Service Files" section with
variable format, usage examples for both BUNDLED_CONTAINERS and
container-bundle packages, and example .service/.container files
- tests/README.md: Add test class entries to structure diagram and
"What the Tests Check" table
Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
Diffstat (limited to 'tests/test_container_cross_install.py')
| -rw-r--r-- | tests/test_container_cross_install.py | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/tests/test_container_cross_install.py b/tests/test_container_cross_install.py index 9a4d23a4..ceb8b874 100644 --- a/tests/test_container_cross_install.py +++ b/tests/test_container_cross_install.py | |||
| @@ -904,3 +904,197 @@ class TestBundledContainersBoot: | |||
| 904 | 904 | ||
| 905 | assert 'CONTAINER_WORKS' in output, \ | 905 | assert 'CONTAINER_WORKS' in output, \ |
| 906 | f"Container {container} failed to run.\nOutput:\n{output}" | 906 | f"Container {container} failed to run.\nOutput:\n{output}" |
| 907 | |||
| 908 | |||
| 909 | # ============================================================================ | ||
| 910 | # Custom Service File Tests | ||
| 911 | # ============================================================================ | ||
| 912 | |||
| 913 | class TestCustomServiceFileSupport: | ||
| 914 | """ | ||
| 915 | Test CONTAINER_SERVICE_FILE varflag support. | ||
| 916 | |||
| 917 | This tests the ability to provide custom systemd service files or | ||
| 918 | Podman Quadlet files instead of auto-generated ones. | ||
| 919 | """ | ||
| 920 | |||
| 921 | def test_bbclass_has_service_file_support(self, meta_virt_dir): | ||
| 922 | """Test that the bbclass includes CONTAINER_SERVICE_FILE support.""" | ||
| 923 | class_file = meta_virt_dir / "classes" / "container-cross-install.bbclass" | ||
| 924 | content = class_file.read_text() | ||
| 925 | |||
| 926 | # Check for the key implementation elements | ||
| 927 | assert "CONTAINER_SERVICE_FILE" in content, \ | ||
| 928 | "CONTAINER_SERVICE_FILE variable not found in bbclass" | ||
| 929 | assert "get_container_service_file_map" in content, \ | ||
| 930 | "get_container_service_file_map function not found" | ||
| 931 | assert "CONTAINER_SERVICE_FILE_MAP" in content, \ | ||
| 932 | "CONTAINER_SERVICE_FILE_MAP variable not found" | ||
| 933 | assert "install_custom_service" in content, \ | ||
| 934 | "install_custom_service function not found" | ||
| 935 | |||
| 936 | def test_bundle_class_has_service_file_support(self, meta_virt_dir): | ||
| 937 | """Test that container-bundle.bbclass includes CONTAINER_SERVICE_FILE support.""" | ||
| 938 | class_file = meta_virt_dir / "classes" / "container-bundle.bbclass" | ||
| 939 | content = class_file.read_text() | ||
| 940 | |||
| 941 | # Check for the key implementation elements | ||
| 942 | assert "CONTAINER_SERVICE_FILE" in content, \ | ||
| 943 | "CONTAINER_SERVICE_FILE variable not found in container-bundle.bbclass" | ||
| 944 | assert "_CONTAINER_SERVICE_FILE_MAP" in content, \ | ||
| 945 | "_CONTAINER_SERVICE_FILE_MAP variable not found" | ||
| 946 | assert "services" in content, \ | ||
| 947 | "services directory handling not found" | ||
| 948 | |||
| 949 | def test_service_file_map_syntax(self, meta_virt_dir): | ||
| 950 | """Test that the service file map function has correct syntax.""" | ||
| 951 | class_file = meta_virt_dir / "classes" / "container-cross-install.bbclass" | ||
| 952 | content = class_file.read_text() | ||
| 953 | |||
| 954 | # Check the function signature and key logic | ||
| 955 | assert "def get_container_service_file_map(d):" in content, \ | ||
| 956 | "get_container_service_file_map function signature not found" | ||
| 957 | assert "getVarFlag('CONTAINER_SERVICE_FILE'" in content, \ | ||
| 958 | "getVarFlag call for CONTAINER_SERVICE_FILE not found" | ||
| 959 | assert 'mappings.append' in content or 'mappings =' in content, \ | ||
| 960 | "Service file mapping logic not found" | ||
| 961 | |||
| 962 | def test_install_custom_service_function(self, meta_virt_dir): | ||
| 963 | """Test that install_custom_service handles both Docker and Podman.""" | ||
| 964 | class_file = meta_virt_dir / "classes" / "container-cross-install.bbclass" | ||
| 965 | content = class_file.read_text() | ||
| 966 | |||
| 967 | # Check the function handles both runtimes | ||
| 968 | assert 'install_custom_service()' in content or 'install_custom_service ' in content, \ | ||
| 969 | "install_custom_service function not found" | ||
| 970 | |||
| 971 | # Docker service installation | ||
| 972 | assert '/lib/systemd/system' in content, \ | ||
| 973 | "Docker service directory path not found" | ||
| 974 | assert 'multi-user.target.wants' in content, \ | ||
| 975 | "Systemd enable symlink path not found" | ||
| 976 | |||
| 977 | # Podman Quadlet installation | ||
| 978 | assert '/etc/containers/systemd' in content, \ | ||
| 979 | "Podman Quadlet directory path not found" | ||
| 980 | |||
| 981 | |||
| 982 | class TestCustomServiceFileBoot: | ||
| 983 | """ | ||
| 984 | Boot tests for custom service files. | ||
| 985 | |||
| 986 | These tests verify that custom service files are properly installed | ||
| 987 | and enabled in the booted system. | ||
| 988 | """ | ||
| 989 | |||
| 990 | @pytest.mark.slow | ||
| 991 | @pytest.mark.boot | ||
| 992 | def test_systemd_services_directory_exists(self, runqemu_session): | ||
| 993 | """Test that systemd service directories exist.""" | ||
| 994 | output = runqemu_session.run_command('ls -la /lib/systemd/system/ | head -5') | ||
| 995 | assert 'systemd' in output or 'total' in output, \ | ||
| 996 | "Systemd system directory not accessible" | ||
| 997 | |||
| 998 | @pytest.mark.slow | ||
| 999 | @pytest.mark.boot | ||
| 1000 | def test_container_services_present(self, runqemu_session, bundled_containers_config): | ||
| 1001 | """Test that container service files are present (custom or generated).""" | ||
| 1002 | docker_containers = bundled_containers_config.get('docker', []) | ||
| 1003 | |||
| 1004 | if not docker_containers: | ||
| 1005 | pytest.skip("No Docker containers configured") | ||
| 1006 | |||
| 1007 | # Check if docker is available | ||
| 1008 | output = runqemu_session.run_command('which docker') | ||
| 1009 | if '/docker' not in output: | ||
| 1010 | pytest.skip("docker not installed in image") | ||
| 1011 | |||
| 1012 | # Check for container service files | ||
| 1013 | output = runqemu_session.run_command('ls /lib/systemd/system/container-*.service 2>/dev/null || echo "NONE"') | ||
| 1014 | |||
| 1015 | if 'NONE' in output: | ||
| 1016 | # No autostart services - check if any containers have autostart | ||
| 1017 | pytest.skip("No container autostart services found (containers may not have autostart enabled)") | ||
| 1018 | |||
| 1019 | # Verify at least one service file exists | ||
| 1020 | assert '.service' in output, \ | ||
| 1021 | f"No container service files found. Output: {output}" | ||
| 1022 | |||
| 1023 | @pytest.mark.slow | ||
| 1024 | @pytest.mark.boot | ||
| 1025 | def test_container_service_enabled(self, runqemu_session, bundled_containers_config): | ||
| 1026 | """Test that container services are enabled (linked in wants directory).""" | ||
| 1027 | docker_containers = bundled_containers_config.get('docker', []) | ||
| 1028 | |||
| 1029 | if not docker_containers: | ||
| 1030 | pytest.skip("No Docker containers configured") | ||
| 1031 | |||
| 1032 | # Check for enabled services in multi-user.target.wants | ||
| 1033 | output = runqemu_session.run_command( | ||
| 1034 | 'ls /etc/systemd/system/multi-user.target.wants/container-*.service 2>/dev/null || echo "NONE"' | ||
| 1035 | ) | ||
| 1036 | |||
| 1037 | if 'NONE' in output: | ||
| 1038 | pytest.skip("No container autostart services enabled") | ||
| 1039 | |||
| 1040 | # Verify services are symlinked | ||
| 1041 | assert '.service' in output, \ | ||
| 1042 | f"No enabled container services found. Output: {output}" | ||
| 1043 | |||
| 1044 | @pytest.mark.slow | ||
| 1045 | @pytest.mark.boot | ||
| 1046 | def test_custom_service_content(self, runqemu_session, bundled_containers_config): | ||
| 1047 | """Test that custom service files have expected content markers.""" | ||
| 1048 | docker_containers = bundled_containers_config.get('docker', []) | ||
| 1049 | |||
| 1050 | if not docker_containers: | ||
| 1051 | pytest.skip("No Docker containers configured") | ||
| 1052 | |||
| 1053 | # Find a container service file | ||
| 1054 | output = runqemu_session.run_command( | ||
| 1055 | 'ls /lib/systemd/system/container-*.service 2>/dev/null | head -1' | ||
| 1056 | ) | ||
| 1057 | |||
| 1058 | if not output or 'container-' not in output: | ||
| 1059 | pytest.skip("No container service files found") | ||
| 1060 | |||
| 1061 | service_file = output.strip().split('\n')[0] | ||
| 1062 | |||
| 1063 | # Read the service file content | ||
| 1064 | content = runqemu_session.run_command(f'cat {service_file}') | ||
| 1065 | |||
| 1066 | # Verify it has expected systemd service structure | ||
| 1067 | assert '[Unit]' in content, f"Service file missing [Unit] section: {service_file}" | ||
| 1068 | assert '[Service]' in content, f"Service file missing [Service] section: {service_file}" | ||
| 1069 | assert '[Install]' in content, f"Service file missing [Install] section: {service_file}" | ||
| 1070 | |||
| 1071 | # Check for docker-related content | ||
| 1072 | assert 'docker' in content.lower(), \ | ||
| 1073 | f"Service file doesn't reference docker: {content}" | ||
| 1074 | |||
| 1075 | @pytest.mark.slow | ||
| 1076 | @pytest.mark.boot | ||
| 1077 | def test_podman_quadlet_directory(self, runqemu_session, bundled_containers_config): | ||
| 1078 | """Test Podman Quadlet directory exists for Podman containers.""" | ||
| 1079 | podman_containers = bundled_containers_config.get('podman', []) | ||
| 1080 | |||
| 1081 | if not podman_containers: | ||
| 1082 | pytest.skip("No Podman containers configured") | ||
| 1083 | |||
| 1084 | # Check if podman is available | ||
| 1085 | output = runqemu_session.run_command('which podman') | ||
| 1086 | if '/podman' not in output: | ||
| 1087 | pytest.skip("podman not installed in image") | ||
| 1088 | |||
| 1089 | # Check for Quadlet directory | ||
| 1090 | output = runqemu_session.run_command('ls -la /etc/containers/systemd/ 2>/dev/null || echo "NONE"') | ||
| 1091 | |||
| 1092 | if 'NONE' in output: | ||
| 1093 | pytest.skip("Quadlet directory not found (containers may not have autostart enabled)") | ||
| 1094 | |||
| 1095 | # Check for .container files | ||
| 1096 | output = runqemu_session.run_command('ls /etc/containers/systemd/*.container 2>/dev/null || echo "NONE"') | ||
| 1097 | |||
| 1098 | if 'NONE' not in output: | ||
| 1099 | assert '.container' in output, \ | ||
| 1100 | f"No Quadlet container files found. Output: {output}" | ||
