- Complete Particle-OS rebranding from uBlue-OS - Professional installation system with standardized paths - Self-initialization system with --init and --reset commands - Enhanced error messages and dependency checking - Comprehensive testing infrastructure - All source scriptlets updated with runtime improvements - Clean codebase with redundant files moved to archive - Complete documentation suite
407 lines
No EOL
12 KiB
Bash
407 lines
No EOL
12 KiB
Bash
#!/bin/bash
|
|
|
|
# Particle-OS OCI Integration
|
|
# Provides OCI export/import functionality for ComposeFS images
|
|
|
|
set -euo pipefail
|
|
|
|
# Source unified configuration
|
|
if [[ -f "${PARTICLE_CONFIG_FILE:-/usr/local/etc/particle-config.sh}" ]]; then
|
|
source "${PARTICLE_CONFIG_FILE:-/usr/local/etc/particle-config.sh}"
|
|
else
|
|
# Fallback configuration
|
|
PARTICLE_WORKSPACE="${PARTICLE_WORKSPACE:-/var/lib/particle-os}"
|
|
PARTICLE_CONFIG_DIR="${PARTICLE_CONFIG_DIR:-/usr/local/etc/particle-os}"
|
|
PARTICLE_LOG_DIR="${PARTICLE_LOG_DIR:-/var/log/particle-os}"
|
|
PARTICLE_CACHE_DIR="${PARTICLE_CACHE_DIR:-/var/cache/particle-os}"
|
|
COMPOSEFS_SCRIPT="${PARTICLE_COMPOSEFS_SCRIPT:-/usr/local/bin/composefs-alternative.sh}"
|
|
PARTICLE_CONTAINER_RUNTIME="${PARTICLE_CONTAINER_RUNTIME:-podman}"
|
|
PARTICLE_TEMP_DIR="${PARTICLE_TEMP_DIR:-/tmp/particle-oci-$$}"
|
|
fi
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
# Logging functions
|
|
log_info() {
|
|
echo -e "${BLUE}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_success() {
|
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
log_warning() {
|
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
|
}
|
|
|
|
# Cleanup function
|
|
cleanup() {
|
|
if [[ -d "$PARTICLE_TEMP_DIR" ]]; then
|
|
log_info "Cleaning up temporary directory: $PARTICLE_TEMP_DIR"
|
|
rm -rf "$PARTICLE_TEMP_DIR" 2>/dev/null || true
|
|
fi
|
|
}
|
|
|
|
# Set up trap for cleanup
|
|
trap cleanup EXIT
|
|
|
|
# OCI export functions
|
|
export_composefs_to_oci() {
|
|
local composefs_image="$1"
|
|
local oci_image_name="$2"
|
|
local oci_tag="${3:-latest}"
|
|
local full_oci_name="$oci_image_name:$oci_tag"
|
|
|
|
log_info "Exporting ComposeFS image to OCI: $composefs_image -> $full_oci_name"
|
|
|
|
# Create temporary directory
|
|
mkdir -p "$PARTICLE_TEMP_DIR"
|
|
|
|
# Mount ComposeFS image
|
|
local mount_point="$PARTICLE_TEMP_DIR/mount"
|
|
mkdir -p "$mount_point"
|
|
|
|
log_info "Mounting ComposeFS image..."
|
|
if ! "$COMPOSEFS_SCRIPT" mount "$composefs_image" "$mount_point"; then
|
|
log_error "Failed to mount ComposeFS image: $composefs_image"
|
|
return 1
|
|
fi
|
|
|
|
# Create Containerfile
|
|
local containerfile="$PARTICLE_TEMP_DIR/Containerfile"
|
|
cat > "$containerfile" << EOF
|
|
FROM scratch
|
|
COPY . /
|
|
LABEL org.ubuntu.ublue.image="$composefs_image"
|
|
LABEL org.ubuntu.ublue.type="composefs-export"
|
|
LABEL org.ubuntu.ublue.created="$(date -Iseconds)"
|
|
CMD ["/bin/bash"]
|
|
EOF
|
|
|
|
# Build OCI image
|
|
log_info "Building OCI image..."
|
|
if ! "$PARTICLE_CONTAINER_RUNTIME" build -f "$containerfile" -t "$full_oci_name" "$mount_point"; then
|
|
log_error "Failed to build OCI image: $full_oci_name"
|
|
"$COMPOSEFS_SCRIPT" unmount "$mount_point"
|
|
return 1
|
|
fi
|
|
|
|
# Unmount ComposeFS image
|
|
"$COMPOSEFS_SCRIPT" unmount "$mount_point"
|
|
|
|
log_success "ComposeFS image exported to OCI: $full_oci_name"
|
|
return 0
|
|
}
|
|
|
|
# OCI import functions
|
|
import_oci_to_composefs() {
|
|
local oci_image_name="$1"
|
|
local composefs_image="$2"
|
|
local oci_tag="${3:-latest}"
|
|
local full_oci_name="$oci_image_name:$oci_tag"
|
|
|
|
log_info "Importing OCI image to ComposeFS: $full_oci_name -> $composefs_image"
|
|
|
|
# Create temporary directory
|
|
mkdir -p "$PARTICLE_TEMP_DIR"
|
|
|
|
# Create temporary container and export filesystem
|
|
local container_name="particle-import-$$"
|
|
|
|
log_info "Creating temporary container..."
|
|
if ! "$PARTICLE_CONTAINER_RUNTIME" create --name "$container_name" "$full_oci_name"; then
|
|
log_error "Failed to create temporary container from: $full_oci_name"
|
|
return 1
|
|
fi
|
|
|
|
# Export container filesystem
|
|
local export_dir="$PARTICLE_TEMP_DIR/export"
|
|
mkdir -p "$export_dir"
|
|
|
|
log_info "Exporting container filesystem..."
|
|
if ! "$PARTICLE_CONTAINER_RUNTIME" export "$container_name" | tar -xf - -C "$export_dir"; then
|
|
log_error "Failed to export container filesystem"
|
|
"$PARTICLE_CONTAINER_RUNTIME" rm "$container_name" >/dev/null 2>&1 || true
|
|
return 1
|
|
fi
|
|
|
|
# Remove temporary container
|
|
"$PARTICLE_CONTAINER_RUNTIME" rm "$container_name" >/dev/null 2>&1 || true
|
|
|
|
# Clean up device files and ephemeral directories that can't be in ComposeFS
|
|
log_info "Cleaning up device files and ephemeral directories..."
|
|
rm -rf "$export_dir"/{dev,proc,sys}/* 2>/dev/null || true
|
|
|
|
# Remove temporary and ephemeral directories
|
|
rm -rf "$export_dir"/tmp/* 2>/dev/null || true
|
|
rm -rf "$export_dir"/var/tmp/* 2>/dev/null || true
|
|
rm -rf "$export_dir"/run/* 2>/dev/null || true
|
|
rm -rf "$export_dir"/mnt/* 2>/dev/null || true
|
|
rm -rf "$export_dir"/media/* 2>/dev/null || true
|
|
|
|
# Create ComposeFS image
|
|
log_info "Creating ComposeFS image..."
|
|
if ! "$COMPOSEFS_SCRIPT" create "$composefs_image" "$export_dir"; then
|
|
log_error "Failed to create ComposeFS image: $composefs_image"
|
|
return 1
|
|
fi
|
|
|
|
log_success "OCI image imported to ComposeFS: $composefs_image"
|
|
return 0
|
|
}
|
|
|
|
# OCI push/pull functions
|
|
push_oci_image() {
|
|
local oci_image_name="$1"
|
|
local registry_url="${2:-}"
|
|
local oci_tag="${3:-latest}"
|
|
|
|
log_info "Pushing OCI image: $oci_image_name:$oci_tag"
|
|
|
|
# Add registry prefix if provided
|
|
local full_image_name="$oci_image_name"
|
|
if [[ -n "$registry_url" ]]; then
|
|
full_image_name="$registry_url/$oci_image_name"
|
|
fi
|
|
|
|
# Tag image with full name
|
|
if ! "$PARTICLE_CONTAINER_RUNTIME" tag "$oci_image_name:$oci_tag" "$full_image_name:$oci_tag"; then
|
|
log_error "Failed to tag image: $full_image_name:$oci_tag"
|
|
return 1
|
|
fi
|
|
|
|
# Push to registry
|
|
if ! "$PARTICLE_CONTAINER_RUNTIME" push "$full_image_name:$oci_tag"; then
|
|
log_error "Failed to push image: $full_image_name:$oci_tag"
|
|
return 1
|
|
fi
|
|
|
|
log_success "OCI image pushed: $full_image_name:$oci_tag"
|
|
return 0
|
|
}
|
|
|
|
pull_oci_image() {
|
|
local oci_image_name="$1"
|
|
local registry_url="${2:-}"
|
|
local oci_tag="${3:-latest}"
|
|
|
|
log_info "Pulling OCI image: $oci_image_name:$oci_tag"
|
|
|
|
# Add registry prefix if provided
|
|
local full_image_name="$oci_image_name"
|
|
if [[ -n "$registry_url" ]]; then
|
|
full_image_name="$registry_url/$oci_image_name"
|
|
fi
|
|
|
|
# Pull from registry
|
|
if ! "$PARTICLE_CONTAINER_RUNTIME" pull "$full_image_name:$oci_tag"; then
|
|
log_error "Failed to pull image: $full_image_name:$oci_tag"
|
|
return 1
|
|
fi
|
|
|
|
# Tag with local name
|
|
if ! "$PARTICLE_CONTAINER_RUNTIME" tag "$full_image_name:$oci_tag" "$oci_image_name:$oci_tag"; then
|
|
log_error "Failed to tag image: $oci_image_name:$oci_tag"
|
|
return 1
|
|
fi
|
|
|
|
log_success "OCI image pulled: $oci_image_name:$oci_tag"
|
|
return 0
|
|
}
|
|
|
|
# OCI image inspection
|
|
inspect_oci_image() {
|
|
local oci_image_name="$1"
|
|
local oci_tag="${2:-latest}"
|
|
local full_image_name="$oci_image_name:$oci_tag"
|
|
|
|
log_info "Inspecting OCI image: $full_image_name"
|
|
|
|
if ! "$PARTICLE_CONTAINER_RUNTIME" inspect "$full_image_name"; then
|
|
log_error "Failed to inspect image: $full_image_name"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# List OCI images
|
|
list_oci_images() {
|
|
log_info "Listing OCI images:"
|
|
echo
|
|
"$PARTICLE_CONTAINER_RUNTIME" images
|
|
}
|
|
|
|
# Remove OCI image
|
|
remove_oci_image() {
|
|
local oci_image_name="$1"
|
|
local oci_tag="${2:-latest}"
|
|
local full_image_name="$oci_image_name:$oci_tag"
|
|
|
|
log_info "Removing OCI image: $full_image_name"
|
|
|
|
if ! "$PARTICLE_CONTAINER_RUNTIME" rmi "$full_image_name"; then
|
|
log_error "Failed to remove image: $full_image_name"
|
|
return 1
|
|
fi
|
|
|
|
log_success "OCI image removed: $full_image_name"
|
|
return 0
|
|
}
|
|
|
|
# Integration with apt-layer.sh
|
|
integrate_with_apt_layer() {
|
|
local operation="$1"
|
|
local composefs_image="$2"
|
|
local oci_image_name="$3"
|
|
local oci_tag="${4:-latest}"
|
|
|
|
case "$operation" in
|
|
"export")
|
|
log_info "Integrating OCI export with apt-layer: $composefs_image"
|
|
export_composefs_to_oci "$composefs_image" "$oci_image_name" "$oci_tag"
|
|
;;
|
|
|
|
"import")
|
|
log_info "Integrating OCI import with apt-layer: $oci_image_name"
|
|
import_oci_to_composefs "$oci_image_name" "$composefs_image" "$oci_tag"
|
|
;;
|
|
|
|
*)
|
|
log_error "Unknown operation: $operation"
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
return 0
|
|
}
|
|
|
|
# Main function
|
|
main() {
|
|
# Check dependencies
|
|
if [[ ! -f "$COMPOSEFS_SCRIPT" ]]; then
|
|
log_error "composefs-alternative.sh not found at: $COMPOSEFS_SCRIPT"
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v "$PARTICLE_CONTAINER_RUNTIME" >/dev/null 2>&1; then
|
|
log_error "Container runtime not found: $PARTICLE_CONTAINER_RUNTIME"
|
|
exit 1
|
|
fi
|
|
|
|
# Parse command line arguments
|
|
case "${1:-}" in
|
|
"export")
|
|
if [[ -z "${2:-}" ]] || [[ -z "${3:-}" ]]; then
|
|
log_error "ComposeFS image and OCI image name required for export"
|
|
exit 1
|
|
fi
|
|
export_composefs_to_oci "$2" "$3" "${4:-}"
|
|
;;
|
|
|
|
"import")
|
|
if [[ -z "${2:-}" ]] || [[ -z "${3:-}" ]]; then
|
|
log_error "OCI image name and ComposeFS image required for import"
|
|
exit 1
|
|
fi
|
|
import_oci_to_composefs "$2" "$3" "${4:-}"
|
|
;;
|
|
|
|
"push")
|
|
if [[ -z "${2:-}" ]]; then
|
|
log_error "OCI image name required for push"
|
|
exit 1
|
|
fi
|
|
push_oci_image "$2" "${3:-}" "${4:-}"
|
|
;;
|
|
|
|
"pull")
|
|
if [[ -z "${2:-}" ]]; then
|
|
log_error "OCI image name required for pull"
|
|
exit 1
|
|
fi
|
|
pull_oci_image "$2" "${3:-}" "${4:-}"
|
|
;;
|
|
|
|
"inspect")
|
|
if [[ -z "${2:-}" ]]; then
|
|
log_error "OCI image name required for inspect"
|
|
exit 1
|
|
fi
|
|
inspect_oci_image "$2" "${3:-}"
|
|
;;
|
|
|
|
"list")
|
|
list_oci_images
|
|
;;
|
|
|
|
"remove")
|
|
if [[ -z "${2:-}" ]]; then
|
|
log_error "OCI image name required for remove"
|
|
exit 1
|
|
fi
|
|
remove_oci_image "$2" "${3:-}"
|
|
;;
|
|
|
|
"integrate")
|
|
if [[ -z "${2:-}" ]] || [[ -z "${3:-}" ]] || [[ -z "${4:-}" ]]; then
|
|
log_error "Operation, ComposeFS image, and OCI image name required for integrate"
|
|
exit 1
|
|
fi
|
|
integrate_with_apt_layer "$2" "$3" "$4" "${5:-}"
|
|
;;
|
|
|
|
"help"|"-h"|"--help")
|
|
cat << EOF
|
|
Particle-OS OCI Integration
|
|
|
|
Usage: $0 <command> [options]
|
|
|
|
Commands:
|
|
export <composefs> <oci> [tag] Export ComposeFS image to OCI
|
|
import <oci> <composefs> [tag] Import OCI image to ComposeFS
|
|
push <oci> [registry] [tag] Push OCI image to registry
|
|
pull <oci> [registry] [tag] Pull OCI image from registry
|
|
inspect <oci> [tag] Inspect OCI image
|
|
list List OCI images
|
|
remove <oci> [tag] Remove OCI image
|
|
integrate <op> <composefs> <oci> [tag] Integrate with apt-layer.sh
|
|
|
|
Operations for integrate:
|
|
export Export ComposeFS to OCI
|
|
import Import OCI to ComposeFS
|
|
|
|
Examples:
|
|
$0 export particle-os/gaming/24.04 particle-os/gaming:latest
|
|
$0 import ubuntu:24.04 particle-os/base/24.04
|
|
$0 push particle-os/gaming registry.example.com latest
|
|
$0 integrate export particle-os/gaming/24.04 particle-os/gaming
|
|
|
|
Environment Variables:
|
|
PARTICLE_CONTAINER_RUNTIME=podman Container runtime to use
|
|
PARTICLE_REGISTRY_URL=registry.example.com Default registry URL
|
|
PARTICLE_CONFIG_FILE=/path/to/config.sh Configuration file path
|
|
PARTICLE_WORKSPACE=/var/lib/particle-os Workspace directory
|
|
PARTICLE_COMPOSEFS_SCRIPT=/path/to/composefs ComposeFS script path
|
|
EOF
|
|
;;
|
|
|
|
*)
|
|
log_error "Unknown command: ${1:-}"
|
|
echo "Use '$0 help' for usage information"
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Run main function
|
|
main "$@" |