diff options
Diffstat (limited to 'classes/container-cross-install.bbclass')
| -rw-r--r-- | classes/container-cross-install.bbclass | 157 |
1 files changed, 156 insertions, 1 deletions
diff --git a/classes/container-cross-install.bbclass b/classes/container-cross-install.bbclass index d18c6436..bb2547e0 100644 --- a/classes/container-cross-install.bbclass +++ b/classes/container-cross-install.bbclass | |||
| @@ -98,6 +98,27 @@ | |||
| 98 | # The runtime is determined by the subdirectory (docker/ vs podman/), | 98 | # The runtime is determined by the subdirectory (docker/ vs podman/), |
| 99 | # which is set by container-bundle.bbclass based on CONTAINER_BUNDLE_RUNTIME. | 99 | # which is set by container-bundle.bbclass based on CONTAINER_BUNDLE_RUNTIME. |
| 100 | # | 100 | # |
| 101 | # =========================================================================== | ||
| 102 | # Custom Service Files (CONTAINER_SERVICE_FILE) | ||
| 103 | # =========================================================================== | ||
| 104 | # | ||
| 105 | # For containers requiring specific startup configuration (ports, volumes, | ||
| 106 | # capabilities, dependencies), provide custom service files instead of | ||
| 107 | # auto-generated ones using the CONTAINER_SERVICE_FILE varflag: | ||
| 108 | # | ||
| 109 | # CONTAINER_SERVICE_FILE[container-name] = "${UNPACKDIR}/myservice.service" | ||
| 110 | # CONTAINER_SERVICE_FILE[other-container] = "${UNPACKDIR}/other.container" | ||
| 111 | # | ||
| 112 | # Usage in image recipe: | ||
| 113 | # SRC_URI += "file://myapp.service" | ||
| 114 | # BUNDLED_CONTAINERS = "myapp-container:docker:autostart" | ||
| 115 | # CONTAINER_SERVICE_FILE[myapp-container] = "${UNPACKDIR}/myapp.service" | ||
| 116 | # | ||
| 117 | # The custom file replaces the auto-generated service. For Docker, provide | ||
| 118 | # a .service file; for Podman, provide a .container Quadlet file. | ||
| 119 | # | ||
| 120 | # See docs/container-bundling.md for detailed examples. | ||
| 121 | # | ||
| 101 | # See also: container-bundle.bbclass | 122 | # See also: container-bundle.bbclass |
| 102 | 123 | ||
| 103 | # Inherit shared functions for multiconfig/machine/arch mapping | 124 | # Inherit shared functions for multiconfig/machine/arch mapping |
| @@ -164,6 +185,26 @@ python __anonymous() { | |||
| 164 | d.appendVarFlag('do_rootfs', 'depends', deps) | 185 | d.appendVarFlag('do_rootfs', 'depends', deps) |
| 165 | } | 186 | } |
| 166 | 187 | ||
| 188 | # Build CONTAINER_SERVICE_FILE_MAP from varflags for shell access | ||
| 189 | # Format: container1=/path/to/file1;container2=/path/to/file2 | ||
| 190 | def get_container_service_file_map(d): | ||
| 191 | """Build a semicolon-separated map of container names to custom service files""" | ||
| 192 | bundled = (d.getVar('BUNDLED_CONTAINERS') or "").split() | ||
| 193 | if not bundled: | ||
| 194 | return "" | ||
| 195 | |||
| 196 | mappings = [] | ||
| 197 | for entry in bundled: | ||
| 198 | parts = entry.split(':') | ||
| 199 | container_name = parts[0] | ||
| 200 | custom_file = d.getVarFlag('CONTAINER_SERVICE_FILE', container_name) | ||
| 201 | if custom_file: | ||
| 202 | mappings.append(f"{container_name}={custom_file}") | ||
| 203 | |||
| 204 | return ";".join(mappings) | ||
| 205 | |||
| 206 | CONTAINER_SERVICE_FILE_MAP = "${@get_container_service_file_map(d)}" | ||
| 207 | |||
| 167 | # Path to vrunner.sh from vcontainer-native | 208 | # Path to vrunner.sh from vcontainer-native |
| 168 | VRUNNER_PATH = "${STAGING_BINDIR_NATIVE}/vrunner.sh" | 209 | VRUNNER_PATH = "${STAGING_BINDIR_NATIVE}/vrunner.sh" |
| 169 | 210 | ||
| @@ -419,7 +460,21 @@ merge_installed_bundles() { | |||
| 419 | 460 | ||
| 420 | bbnote "Creating autostart service for $source ($runtime_type, restart=$restart_policy)" | 461 | bbnote "Creating autostart service for $source ($runtime_type, restart=$restart_policy)" |
| 421 | 462 | ||
| 463 | # Check for custom service file in bundle's services directory | ||
| 464 | # Custom files are stored as: services/<source-sanitized>.(service|container) | ||
| 465 | local source_sanitized=$(echo "$source" | sed 's|[/:]|_|g') | ||
| 466 | local custom_service_file="" | ||
| 467 | |||
| 422 | if [ "$runtime_type" = "docker" ]; then | 468 | if [ "$runtime_type" = "docker" ]; then |
| 469 | custom_service_file="${BUNDLES_DIR}/${runtime_type}/services/${source_sanitized}.service" | ||
| 470 | elif [ "$runtime_type" = "podman" ]; then | ||
| 471 | custom_service_file="${BUNDLES_DIR}/${runtime_type}/services/${source_sanitized}.container" | ||
| 472 | fi | ||
| 473 | |||
| 474 | if [ -n "$custom_service_file" ] && [ -f "$custom_service_file" ]; then | ||
| 475 | bbnote "Using custom service file from bundle: $custom_service_file" | ||
| 476 | install_custom_service_from_bundle "$source" "$service_name" "$runtime_type" "$custom_service_file" | ||
| 477 | elif [ "$runtime_type" = "docker" ]; then | ||
| 423 | generate_docker_service_from_bundle "$service_name" "$image_name" "$image_tag" "$restart_policy" | 478 | generate_docker_service_from_bundle "$service_name" "$image_name" "$image_tag" "$restart_policy" |
| 424 | elif [ "$runtime_type" = "podman" ]; then | 479 | elif [ "$runtime_type" = "podman" ]; then |
| 425 | generate_podman_service_from_bundle "$service_name" "$image_name" "$image_tag" "$restart_policy" | 480 | generate_podman_service_from_bundle "$service_name" "$image_name" "$image_tag" "$restart_policy" |
| @@ -434,6 +489,51 @@ merge_installed_bundles() { | |||
| 434 | return 0 | 489 | return 0 |
| 435 | } | 490 | } |
| 436 | 491 | ||
| 492 | # Install a custom service file from a bundle package | ||
| 493 | # Args: source service_name runtime_type custom_file | ||
| 494 | install_custom_service_from_bundle() { | ||
| 495 | local source="$1" | ||
| 496 | local service_name="$2" | ||
| 497 | local runtime_type="$3" | ||
| 498 | local custom_file="$4" | ||
| 499 | |||
| 500 | if [ ! -f "$custom_file" ]; then | ||
| 501 | bbwarn "Custom service file not found: $custom_file (for container $source)" | ||
| 502 | return 1 | ||
| 503 | fi | ||
| 504 | |||
| 505 | if [ "$runtime_type" = "docker" ]; then | ||
| 506 | # Docker: Install as systemd service | ||
| 507 | local service_dir="${IMAGE_ROOTFS}/lib/systemd/system" | ||
| 508 | local service_file="${service_dir}/${service_name}.service" | ||
| 509 | |||
| 510 | mkdir -p "$service_dir" | ||
| 511 | install -m 0644 "$custom_file" "$service_file" | ||
| 512 | |||
| 513 | # Enable the service via symlink | ||
| 514 | local wants_dir="${IMAGE_ROOTFS}/etc/systemd/system/multi-user.target.wants" | ||
| 515 | mkdir -p "$wants_dir" | ||
| 516 | ln -sf "/lib/systemd/system/${service_name}.service" "${wants_dir}/${service_name}.service" | ||
| 517 | |||
| 518 | bbnote "Installed custom service from bundle: $custom_file -> ${service_name}.service" | ||
| 519 | |||
| 520 | elif [ "$runtime_type" = "podman" ]; then | ||
| 521 | # Podman: Install as Quadlet container file | ||
| 522 | local quadlet_dir="${IMAGE_ROOTFS}/etc/containers/systemd" | ||
| 523 | local container_file="${quadlet_dir}/${service_name}.container" | ||
| 524 | |||
| 525 | mkdir -p "$quadlet_dir" | ||
| 526 | install -m 0644 "$custom_file" "$container_file" | ||
| 527 | |||
| 528 | bbnote "Installed custom Quadlet file from bundle: $custom_file -> ${service_name}.container" | ||
| 529 | else | ||
| 530 | bbwarn "Unknown runtime '$runtime_type' for custom service file from bundle, skipping" | ||
| 531 | return 1 | ||
| 532 | fi | ||
| 533 | |||
| 534 | return 0 | ||
| 535 | } | ||
| 536 | |||
| 437 | # Generate Docker systemd service (for bundle packages) | 537 | # Generate Docker systemd service (for bundle packages) |
| 438 | generate_docker_service_from_bundle() { | 538 | generate_docker_service_from_bundle() { |
| 439 | local service_name="$1" | 539 | local service_name="$1" |
| @@ -648,6 +748,47 @@ EOF | |||
| 648 | bbnote "Created Quadlet file ${service_name}.container for Podman container ${image_name}:${image_tag}" | 748 | bbnote "Created Quadlet file ${service_name}.container for Podman container ${image_name}:${image_tag}" |
| 649 | } | 749 | } |
| 650 | 750 | ||
| 751 | # Install a custom service file provided by the user | ||
| 752 | # Args: container_name service_name runtime_type custom_file | ||
| 753 | install_custom_service() { | ||
| 754 | local container_name="$1" | ||
| 755 | local service_name="$2" | ||
| 756 | local runtime_type="$3" | ||
| 757 | local custom_file="$4" | ||
| 758 | |||
| 759 | if [ ! -f "$custom_file" ]; then | ||
| 760 | bbfatal "Custom service file not found: $custom_file (for container $container_name)" | ||
| 761 | fi | ||
| 762 | |||
| 763 | if [ "$runtime_type" = "docker" ]; then | ||
| 764 | # Docker: Install as systemd service | ||
| 765 | local service_dir="${IMAGE_ROOTFS}/lib/systemd/system" | ||
| 766 | local service_file="${service_dir}/${service_name}.service" | ||
| 767 | |||
| 768 | mkdir -p "$service_dir" | ||
| 769 | install -m 0644 "$custom_file" "$service_file" | ||
| 770 | |||
| 771 | # Enable the service via symlink | ||
| 772 | local wants_dir="${IMAGE_ROOTFS}/etc/systemd/system/multi-user.target.wants" | ||
| 773 | mkdir -p "$wants_dir" | ||
| 774 | ln -sf "/lib/systemd/system/${service_name}.service" "${wants_dir}/${service_name}.service" | ||
| 775 | |||
| 776 | bbnote "Installed custom service: $custom_file -> ${service_name}.service" | ||
| 777 | |||
| 778 | elif [ "$runtime_type" = "podman" ]; then | ||
| 779 | # Podman: Install as Quadlet container file | ||
| 780 | local quadlet_dir="${IMAGE_ROOTFS}/etc/containers/systemd" | ||
| 781 | local container_file="${quadlet_dir}/${service_name}.container" | ||
| 782 | |||
| 783 | mkdir -p "$quadlet_dir" | ||
| 784 | install -m 0644 "$custom_file" "$container_file" | ||
| 785 | |||
| 786 | bbnote "Installed custom Quadlet file: $custom_file -> ${service_name}.container" | ||
| 787 | else | ||
| 788 | bbwarn "Unknown runtime '$runtime_type' for custom service file, skipping" | ||
| 789 | fi | ||
| 790 | } | ||
| 791 | |||
| 651 | # Install autostart services for containers with autostart policy | 792 | # Install autostart services for containers with autostart policy |
| 652 | install_autostart_services() { | 793 | install_autostart_services() { |
| 653 | bbnote "Processing container autostart services..." | 794 | bbnote "Processing container autostart services..." |
| @@ -697,7 +838,21 @@ EOF | |||
| 697 | 838 | ||
| 698 | bbnote "Creating autostart service for $container_name ($runtime_type, restart=$restart_policy)" | 839 | bbnote "Creating autostart service for $container_name ($runtime_type, restart=$restart_policy)" |
| 699 | 840 | ||
| 700 | if [ "$runtime_type" = "docker" ]; then | 841 | # Check for custom service file via CONTAINER_SERVICE_FILE varflag |
| 842 | # This is evaluated at rootfs time, so we use a Python helper | ||
| 843 | local custom_service_file="${CONTAINER_SERVICE_FILE_MAP}" | ||
| 844 | local custom_file="" | ||
| 845 | |||
| 846 | # Parse the map to find this container's custom file | ||
| 847 | # Format: container1=/path/to/file1;container2=/path/to/file2 | ||
| 848 | if [ -n "$custom_service_file" ]; then | ||
| 849 | custom_file=$(echo "$custom_service_file" | tr ';' '\n' | grep "^${container_name}=" | cut -d= -f2-) | ||
| 850 | fi | ||
| 851 | |||
| 852 | if [ -n "$custom_file" ] && [ -f "$custom_file" ]; then | ||
| 853 | bbnote "Using custom service file for $container_name: $custom_file" | ||
| 854 | install_custom_service "$container_name" "$service_name" "$runtime_type" "$custom_file" | ||
| 855 | elif [ "$runtime_type" = "docker" ]; then | ||
| 701 | generate_docker_service "$service_name" "${CONTAINER_IMAGE_NAME}" "${CONTAINER_IMAGE_TAG}" "$restart_policy" | 856 | generate_docker_service "$service_name" "${CONTAINER_IMAGE_NAME}" "${CONTAINER_IMAGE_TAG}" "$restart_policy" |
| 702 | elif [ "$runtime_type" = "podman" ]; then | 857 | elif [ "$runtime_type" = "podman" ]; then |
| 703 | generate_podman_service "$service_name" "${CONTAINER_IMAGE_NAME}" "${CONTAINER_IMAGE_TAG}" "$restart_policy" | 858 | generate_podman_service "$service_name" "${CONTAINER_IMAGE_NAME}" "${CONTAINER_IMAGE_TAG}" "$restart_policy" |
