Advanced ComposeFS Features - Implemented multi-layer composition, deduplication, compression, benchmarking, and enhanced metadata handling
Some checks failed
Compile apt-layer (v2) / compile (push) Has been cancelled
Some checks failed
Compile apt-layer (v2) / compile (push) Has been cancelled
This commit is contained in:
parent
5089ee421f
commit
ee24d8b6b2
3 changed files with 1293 additions and 2 deletions
|
|
@ -507,5 +507,592 @@ composefs_status() {
|
|||
echo "Metadata preservation: ${COMPOSEFS_PRESERVE_METADATA:-true}"
|
||||
echo "Layer validation: ${COMPOSEFS_LAYER_VALIDATION:-true}"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Advanced ComposeFS Features for Phase 2.3
|
||||
# Multi-layer composition, advanced conflict resolution, optimization
|
||||
|
||||
# Multi-layer composition (support for more than 2 layers)
|
||||
compose_multiple_layers() {
|
||||
local layers=("$@")
|
||||
local output_layer="${layers[-1]}"
|
||||
unset "layers[-1]"
|
||||
local conflict_resolution="${CONFLICT_RESOLUTION:-keep-latest}"
|
||||
|
||||
log_info "Composing multiple layers: ${#layers[@]} layers -> $output_layer" "apt-layer"
|
||||
|
||||
if [[ ${#layers[@]} -lt 2 ]]; then
|
||||
log_error "At least 2 layers required for composition" "apt-layer"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Start transaction
|
||||
start_transaction "composefs-multi-$(basename "$output_layer")"
|
||||
|
||||
# Create temporary mount points
|
||||
local mount_points=()
|
||||
local temp_dirs=()
|
||||
|
||||
# Mount all layers
|
||||
for i in "${!layers[@]}"; do
|
||||
local layer="${layers[$i]}"
|
||||
local mount_point
|
||||
mount_point=$(mktemp -d)
|
||||
mount_points+=("$mount_point")
|
||||
temp_dirs+=("$mount_point")
|
||||
|
||||
if ! mount_composefs_layer "$layer" "$mount_point"; then
|
||||
log_error "Failed to mount layer $i: $layer" "apt-layer"
|
||||
cleanup_multiple_mounts "${temp_dirs[@]}"
|
||||
rollback_transaction
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Create work directory for overlay
|
||||
local work_dir
|
||||
work_dir=$(mktemp -d)
|
||||
temp_dirs+=("$work_dir")
|
||||
|
||||
# Create output mount point
|
||||
local output_mount
|
||||
output_mount=$(mktemp -d)
|
||||
temp_dirs+=("$output_mount")
|
||||
|
||||
# Build overlay lowerdir string (reverse order for proper layering)
|
||||
local lowerdirs=""
|
||||
for ((i=${#mount_points[@]}-1; i>=0; i--)); do
|
||||
if [[ -n "$lowerdirs" ]]; then
|
||||
lowerdirs="$lowerdirs:${mount_points[$i]}"
|
||||
else
|
||||
lowerdirs="${mount_points[$i]}"
|
||||
fi
|
||||
done
|
||||
|
||||
# Create overlay filesystem
|
||||
if ! mount -t overlay overlay -o "lowerdir=$lowerdirs,workdir=$work_dir" "$output_mount"; then
|
||||
log_error "Failed to create multi-layer overlay" "apt-layer"
|
||||
cleanup_multiple_mounts "${temp_dirs[@]}"
|
||||
rollback_transaction
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Create composed layer
|
||||
if ! create_composefs_layer "$output_mount" "$output_layer" "multi-composed"; then
|
||||
log_error "Failed to create multi-composed layer" "apt-layer"
|
||||
cleanup_multiple_mounts "${temp_dirs[@]}"
|
||||
rollback_transaction
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
cleanup_multiple_mounts "${temp_dirs[@]}"
|
||||
|
||||
commit_transaction
|
||||
log_success "Multi-layer composition completed: $output_layer" "apt-layer"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Cleanup multiple mount points
|
||||
cleanup_multiple_mounts() {
|
||||
local mounts=("$@")
|
||||
|
||||
for mount_point in "${mounts[@]}"; do
|
||||
if [[ -n "$mount_point" ]] && [[ -d "$mount_point" ]]; then
|
||||
unmount_composefs_layer "$mount_point"
|
||||
rmdir "$mount_point" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Advanced conflict resolution with interactive mode
|
||||
resolve_conflicts_interactive() {
|
||||
local base_layer="$1"
|
||||
local new_layer="$2"
|
||||
local output_layer="$3"
|
||||
local conflict_file="$4"
|
||||
|
||||
log_info "Interactive conflict resolution: $base_layer vs $new_layer" "apt-layer"
|
||||
|
||||
# Mount both layers
|
||||
local base_mount
|
||||
local new_mount
|
||||
local output_mount
|
||||
base_mount=$(mktemp -d)
|
||||
new_mount=$(mktemp -d)
|
||||
output_mount=$(mktemp -d)
|
||||
|
||||
if ! mount_composefs_layer "$base_layer" "$base_mount"; then
|
||||
log_error "Failed to mount base layer" "apt-layer"
|
||||
cleanup_multiple_mounts "$base_mount" "$new_mount" "$output_mount"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! mount_composefs_layer "$new_layer" "$new_mount"; then
|
||||
log_error "Failed to mount new layer" "apt-layer"
|
||||
cleanup_multiple_mounts "$base_mount" "$new_mount" "$output_mount"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Detect conflicts
|
||||
local conflicts=()
|
||||
while IFS= read -r -d '' file; do
|
||||
if [[ -f "$base_mount/$file" ]] && [[ -f "$new_mount/$file" ]]; then
|
||||
if ! cmp -s "$base_mount/$file" "$new_mount/$file"; then
|
||||
conflicts+=("$file")
|
||||
fi
|
||||
fi
|
||||
done < <(find "$new_mount" -type f -printf "%P\0" 2>/dev/null)
|
||||
|
||||
if [[ ${#conflicts[@]} -eq 0 ]]; then
|
||||
log_info "No conflicts detected" "apt-layer"
|
||||
# Use new layer as-is
|
||||
cp "$new_layer" "$output_layer"
|
||||
cleanup_multiple_mounts "$base_mount" "$new_mount" "$output_mount"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Found ${#conflicts[@]} conflicts" "apt-layer"
|
||||
|
||||
# Write conflict report
|
||||
if [[ -n "$conflict_file" ]]; then
|
||||
echo "# Conflict Resolution Report" > "$conflict_file"
|
||||
echo "Generated: $(date)" >> "$conflict_file"
|
||||
echo "Base Layer: $base_layer" >> "$conflict_file"
|
||||
echo "New Layer: $new_layer" >> "$conflict_file"
|
||||
echo "Conflicts: ${#conflicts[@]}" >> "$conflict_file"
|
||||
echo "" >> "$conflict_file"
|
||||
|
||||
for conflict in "${conflicts[@]}"; do
|
||||
echo "## $conflict" >> "$conflict_file"
|
||||
echo "Base: $(stat -c%s "$base_mount/$conflict") bytes" >> "$conflict_file"
|
||||
echo "New: $(stat -c%s "$new_mount/$conflict") bytes" >> "$conflict_file"
|
||||
echo "" >> "$conflict_file"
|
||||
done
|
||||
fi
|
||||
|
||||
# Interactive resolution
|
||||
if [[ "$INTERACTIVE_CONFLICT_RESOLUTION" == "true" ]]; then
|
||||
for conflict in "${conflicts[@]}"; do
|
||||
echo "Conflict in: $conflict"
|
||||
echo "1) Keep base version"
|
||||
echo "2) Keep new version"
|
||||
echo "3) Show diff"
|
||||
echo "4) Manual merge"
|
||||
read -p "Choose option (1-4): " choice
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
# Keep base version
|
||||
cp "$base_mount/$conflict" "$new_mount/$conflict"
|
||||
;;
|
||||
2)
|
||||
# Keep new version (already in place)
|
||||
;;
|
||||
3)
|
||||
# Show diff
|
||||
diff -u "$base_mount/$conflict" "$new_mount/$conflict" || true
|
||||
read -p "Press Enter to continue..."
|
||||
;;
|
||||
4)
|
||||
# Manual merge
|
||||
echo "Manual merge not implemented yet"
|
||||
;;
|
||||
*)
|
||||
log_error "Invalid choice, keeping new version" "apt-layer"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
else
|
||||
# Non-interactive: use default strategy
|
||||
log_info "Using default conflict resolution strategy: $CONFLICT_RESOLUTION" "apt-layer"
|
||||
case "$CONFLICT_RESOLUTION" in
|
||||
keep-latest)
|
||||
# Keep new version (already in place)
|
||||
;;
|
||||
keep-base)
|
||||
# Keep base version
|
||||
for conflict in "${conflicts[@]}"; do
|
||||
cp "$base_mount/$conflict" "$new_mount/$conflict"
|
||||
done
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown conflict resolution strategy: $CONFLICT_RESOLUTION" "apt-layer"
|
||||
cleanup_multiple_mounts "$base_mount" "$new_mount" "$output_mount"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Create resolved layer
|
||||
if ! create_composefs_layer "$new_mount" "$output_layer" "resolved"; then
|
||||
log_error "Failed to create resolved layer" "apt-layer"
|
||||
cleanup_multiple_mounts "$base_mount" "$new_mount" "$output_mount"
|
||||
return 1
|
||||
fi
|
||||
|
||||
cleanup_multiple_mounts "$base_mount" "$new_mount" "$output_mount"
|
||||
log_success "Interactive conflict resolution completed: $output_layer" "apt-layer"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Layer deduplication
|
||||
deduplicate_layer() {
|
||||
local input_layer="$1"
|
||||
local output_layer="$2"
|
||||
local dedup_strategy="${3:-content-hash}"
|
||||
|
||||
log_info "Deduplicating layer: $input_layer" "apt-layer"
|
||||
|
||||
# Start transaction
|
||||
start_transaction "composefs-dedup-$(basename "$output_layer")"
|
||||
|
||||
# Mount input layer
|
||||
local input_mount
|
||||
input_mount=$(mktemp -d)
|
||||
|
||||
if ! mount_composefs_layer "$input_layer" "$input_mount"; then
|
||||
log_error "Failed to mount input layer" "apt-layer"
|
||||
rmdir "$input_mount" 2>/dev/null || true
|
||||
rollback_transaction
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Create deduplicated directory
|
||||
local dedup_dir
|
||||
dedup_dir=$(mktemp -d)
|
||||
|
||||
# Create hash store
|
||||
local hash_store
|
||||
hash_store=$(mktemp -d)
|
||||
|
||||
# Deduplicate files
|
||||
local total_files=0
|
||||
local unique_files=0
|
||||
local saved_space=0
|
||||
|
||||
while IFS= read -r -d '' file; do
|
||||
((total_files++))
|
||||
|
||||
if [[ -f "$input_mount/$file" ]]; then
|
||||
local file_hash
|
||||
file_hash=$(sha256sum "$input_mount/$file" | cut -d' ' -f1)
|
||||
local hash_file="$hash_store/$file_hash"
|
||||
|
||||
if [[ ! -f "$hash_file" ]]; then
|
||||
# First occurrence of this content
|
||||
cp "$input_mount/$file" "$hash_file"
|
||||
mkdir -p "$(dirname "$dedup_dir/$file")"
|
||||
ln "$hash_file" "$dedup_dir/$file"
|
||||
((unique_files++))
|
||||
else
|
||||
# Duplicate content found
|
||||
local original_size
|
||||
original_size=$(stat -c%s "$input_mount/$file")
|
||||
((saved_space += original_size))
|
||||
|
||||
mkdir -p "$(dirname "$dedup_dir/$file")"
|
||||
ln "$hash_file" "$dedup_dir/$file"
|
||||
fi
|
||||
fi
|
||||
done < <(find "$input_mount" -type f -printf "%P\0" 2>/dev/null)
|
||||
|
||||
# Create deduplicated layer
|
||||
if ! create_composefs_layer "$dedup_dir" "$output_layer" "deduplicated"; then
|
||||
log_error "Failed to create deduplicated layer" "apt-layer"
|
||||
cleanup_multiple_mounts "$input_mount" "$dedup_dir" "$hash_store"
|
||||
rollback_transaction
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Log deduplication results
|
||||
log_info "Deduplication results:" "apt-layer"
|
||||
log_info " Total files: $total_files" "apt-layer"
|
||||
log_info " Unique files: $unique_files" "apt-layer"
|
||||
log_info " Duplicates: $((total_files - unique_files))" "apt-layer"
|
||||
log_info " Space saved: $((saved_space / 1024)) KB" "apt-layer"
|
||||
|
||||
# Cleanup
|
||||
cleanup_multiple_mounts "$input_mount" "$dedup_dir" "$hash_store"
|
||||
|
||||
commit_transaction
|
||||
log_success "Layer deduplication completed: $output_layer" "apt-layer"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Layer compression
|
||||
compress_layer() {
|
||||
local input_layer="$1"
|
||||
local output_layer="$2"
|
||||
local compression_type="${3:-gzip}"
|
||||
local compression_level="${4:-6}"
|
||||
|
||||
log_info "Compressing layer: $input_layer ($compression_type, level $compression_level)" "apt-layer"
|
||||
|
||||
# Start transaction
|
||||
start_transaction "composefs-compress-$(basename "$output_layer")"
|
||||
|
||||
# Mount input layer
|
||||
local input_mount
|
||||
input_mount=$(mktemp -d)
|
||||
|
||||
if ! mount_composefs_layer "$input_layer" "$input_mount"; then
|
||||
log_error "Failed to mount input layer" "apt-layer"
|
||||
rmdir "$input_mount" 2>/dev/null || true
|
||||
rollback_transaction
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Create compressed layer
|
||||
local original_size
|
||||
local compressed_size
|
||||
|
||||
original_size=$(stat -c%s "$input_layer")
|
||||
|
||||
case "$compression_type" in
|
||||
gzip)
|
||||
if ! gzip -c -$compression_level "$input_layer" > "$output_layer"; then
|
||||
log_error "Failed to compress layer with gzip" "apt-layer"
|
||||
cleanup_multiple_mounts "$input_mount"
|
||||
rollback_transaction
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
zstd)
|
||||
if ! zstd -c -$compression_level "$input_layer" > "$output_layer"; then
|
||||
log_error "Failed to compress layer with zstd" "apt-layer"
|
||||
cleanup_multiple_mounts "$input_mount"
|
||||
rollback_transaction
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
xz)
|
||||
if ! xz -c -$compression_level "$input_layer" > "$output_layer"; then
|
||||
log_error "Failed to compress layer with xz" "apt-layer"
|
||||
cleanup_multiple_mounts "$input_mount"
|
||||
rollback_transaction
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
log_error "Unsupported compression type: $compression_type" "apt-layer"
|
||||
cleanup_multiple_mounts "$input_mount"
|
||||
rollback_transaction
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
compressed_size=$(stat -c%s "$output_layer")
|
||||
local compression_ratio
|
||||
compression_ratio=$((100 - (compressed_size * 100 / original_size)))
|
||||
|
||||
log_info "Compression results:" "apt-layer"
|
||||
log_info " Original size: $((original_size / 1024)) KB" "apt-layer"
|
||||
log_info " Compressed size: $((compressed_size / 1024)) KB" "apt-layer"
|
||||
log_info " Compression ratio: ${compression_ratio}%" "apt-layer"
|
||||
|
||||
# Cleanup
|
||||
cleanup_multiple_mounts "$input_mount"
|
||||
|
||||
commit_transaction
|
||||
log_success "Layer compression completed: $output_layer" "apt-layer"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Enhanced metadata handling with JSON format
|
||||
handle_enhanced_metadata() {
|
||||
local source_dir="$1"
|
||||
local metadata_file="$2"
|
||||
local metadata_format="${3:-json}"
|
||||
|
||||
log_debug "Handling enhanced metadata: $source_dir ($metadata_format)" "apt-layer"
|
||||
|
||||
# Create metadata directory
|
||||
local metadata_dir
|
||||
metadata_dir=$(dirname "$metadata_file")
|
||||
mkdir -p "$metadata_dir"
|
||||
|
||||
case "$metadata_format" in
|
||||
json)
|
||||
# Generate JSON metadata
|
||||
cat > "$metadata_file" << EOF
|
||||
{
|
||||
"metadata_version": "2.0",
|
||||
"generated_at": "$(date -u -Iseconds)",
|
||||
"source_directory": "$source_dir",
|
||||
"file_statistics": {
|
||||
"total_files": $(find "$source_dir" -type f | wc -l),
|
||||
"total_directories": $(find "$source_dir" -type d | wc -l),
|
||||
"total_size_bytes": $(du -sb "$source_dir" | cut -f1)
|
||||
},
|
||||
"permissions": {
|
||||
"uid": $(stat -c%u "$source_dir"),
|
||||
"gid": $(stat -c%g "$source_dir"),
|
||||
"mode": "$(stat -c%a "$source_dir")"
|
||||
},
|
||||
"content_hashes": {
|
||||
"source_hash": "$(find "$source_dir" -type f -exec sha256sum {} \; | sort | sha256sum | cut -d' ' -f1)"
|
||||
},
|
||||
"layer_info": {
|
||||
"created_by": "apt-layer",
|
||||
"version": "2.3",
|
||||
"features": ["multi-layer", "compression", "deduplication"]
|
||||
}
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
yaml)
|
||||
# Generate YAML metadata
|
||||
cat > "$metadata_file" << EOF
|
||||
metadata_version: "2.0"
|
||||
generated_at: "$(date -u -Iseconds)"
|
||||
source_directory: "$source_dir"
|
||||
file_statistics:
|
||||
total_files: $(find "$source_dir" -type f | wc -l)
|
||||
total_directories: $(find "$source_dir" -type d | wc -l)
|
||||
total_size_bytes: $(du -sb "$source_dir" | cut -f1)
|
||||
permissions:
|
||||
uid: $(stat -c%u "$source_dir")
|
||||
gid: $(stat -c%g "$source_dir")
|
||||
mode: "$(stat -c%a "$source_dir")"
|
||||
content_hashes:
|
||||
source_hash: "$(find "$source_dir" -type f -exec sha256sum {} \; | sort | sha256sum | cut -d' ' -f1)"
|
||||
layer_info:
|
||||
created_by: apt-layer
|
||||
version: "2.3"
|
||||
features:
|
||||
- multi-layer
|
||||
- compression
|
||||
- deduplication
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
log_error "Unsupported metadata format: $metadata_format" "apt-layer"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
log_debug "Enhanced metadata written: $metadata_file" "apt-layer"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Layer performance benchmarking
|
||||
benchmark_layer() {
|
||||
local layer_path="$1"
|
||||
local benchmark_file="${2:-}"
|
||||
|
||||
log_info "Benchmarking layer: $layer_path" "apt-layer"
|
||||
|
||||
# Mount layer
|
||||
local mount_point
|
||||
mount_point=$(mktemp -d)
|
||||
|
||||
if ! mount_composefs_layer "$layer_path" "$mount_point"; then
|
||||
log_error "Failed to mount layer for benchmarking" "apt-layer"
|
||||
rmdir "$mount_point" 2>/dev/null || true
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Benchmark metrics
|
||||
local start_time
|
||||
local end_time
|
||||
local mount_time
|
||||
local file_count
|
||||
local total_size
|
||||
local read_speed
|
||||
|
||||
# Measure mount time
|
||||
start_time=$(date +%s.%N)
|
||||
# Mount already done above
|
||||
end_time=$(date +%s.%N)
|
||||
mount_time=$(echo "$end_time - $start_time" | bc -l 2>/dev/null || echo "0")
|
||||
|
||||
# Count files and size
|
||||
file_count=$(find "$mount_point" -type f | wc -l)
|
||||
total_size=$(du -sb "$mount_point" | cut -f1)
|
||||
|
||||
# Measure read speed (simple test)
|
||||
start_time=$(date +%s.%N)
|
||||
find "$mount_point" -type f -name "*.so*" | head -10 | xargs cat > /dev/null 2>/dev/null || true
|
||||
end_time=$(date +%s.%N)
|
||||
local read_time
|
||||
read_time=$(echo "$end_time - $start_time" | bc -l 2>/dev/null || echo "0")
|
||||
|
||||
if [[ "$read_time" != "0" ]]; then
|
||||
read_speed=$(echo "scale=2; $total_size / $read_time / 1024 / 1024" | bc -l 2>/dev/null || echo "0")
|
||||
else
|
||||
read_speed="0"
|
||||
fi
|
||||
|
||||
# Generate benchmark report
|
||||
local report_content=""
|
||||
report_content+="# Layer Performance Benchmark Report\n"
|
||||
report_content+="Generated: $(date)\n"
|
||||
report_content+="Layer: $layer_path\n"
|
||||
report_content+="\n"
|
||||
report_content+="## Performance Metrics\n"
|
||||
report_content+="Mount Time: ${mount_time}s\n"
|
||||
report_content+="File Count: $file_count\n"
|
||||
report_content+="Total Size: $((total_size / 1024)) KB\n"
|
||||
report_content+="Read Speed: ${read_speed} MB/s\n"
|
||||
report_content+="\n"
|
||||
report_content+="## Layer Statistics\n"
|
||||
report_content+="Layer Size: $(stat -c%s "$layer_path" 2>/dev/null || echo "0") bytes\n"
|
||||
report_content+="Compression Ratio: $((100 - ($(stat -c%s "$layer_path" 2>/dev/null || echo 0) * 100 / total_size)))%\n"
|
||||
|
||||
if [[ -n "$benchmark_file" ]]; then
|
||||
echo -e "$report_content" > "$benchmark_file"
|
||||
log_info "Benchmark report written: $benchmark_file" "apt-layer"
|
||||
else
|
||||
echo -e "$report_content"
|
||||
fi
|
||||
|
||||
# Unmount
|
||||
unmount_composefs_layer "$mount_point"
|
||||
rmdir "$mount_point" 2>/dev/null || true
|
||||
|
||||
log_success "Layer benchmarking completed" "apt-layer"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Layer relationship tracking
|
||||
track_layer_relationships() {
|
||||
local layer_path="$1"
|
||||
local relationship_file="$2"
|
||||
local parent_layers=("${@:3}")
|
||||
|
||||
log_debug "Tracking layer relationships: $layer_path" "apt-layer"
|
||||
|
||||
# Create relationship metadata
|
||||
local relationship_data=""
|
||||
relationship_data+="{\n"
|
||||
relationship_data+=" \"layer_id\": \"$(basename "$layer_path")\",\n"
|
||||
relationship_data+=" \"layer_path\": \"$layer_path\",\n"
|
||||
relationship_data+=" \"created_at\": \"$(date -u -Iseconds)\",\n"
|
||||
relationship_data+=" \"parent_layers\": [\n"
|
||||
|
||||
for parent in "${parent_layers[@]}"; do
|
||||
relationship_data+=" {\n"
|
||||
relationship_data+=" \"path\": \"$parent\",\n"
|
||||
relationship_data+=" \"id\": \"$(basename "$parent")\",\n"
|
||||
relationship_data+=" \"hash\": \"$(sha256sum "$parent" 2>/dev/null | cut -d' ' -f1 || echo "unknown")\"\n"
|
||||
relationship_data+=" }"
|
||||
if [[ "$parent" != "${parent_layers[-1]}" ]]; then
|
||||
relationship_data+=","
|
||||
fi
|
||||
relationship_data+="\n"
|
||||
done
|
||||
|
||||
relationship_data+=" ],\n"
|
||||
relationship_data+=" \"layer_hash\": \"$(sha256sum "$layer_path" 2>/dev/null | cut -d' ' -f1 || echo "unknown")\",\n"
|
||||
relationship_data+=" \"layer_size\": $(stat -c%s "$layer_path" 2>/dev/null || echo "0"),\n"
|
||||
relationship_data+=" \"generation\": ${#parent_layers[@]}\n"
|
||||
relationship_data+="}\n"
|
||||
|
||||
# Write relationship file
|
||||
echo -e "$relationship_data" > "$relationship_file"
|
||||
|
||||
log_debug "Layer relationships tracked: $relationship_file" "apt-layer"
|
||||
return 0
|
||||
}
|
||||
|
|
@ -102,7 +102,7 @@ LIVE SYSTEM LAYERING:
|
|||
# Install packages on live system with overlayfs (like rpm-ostree install)
|
||||
|
||||
apt-layer --live-dpkg packages
|
||||
# Install packages on live system using dpkg (optimized)
|
||||
# Install packages on live system using dpkg (optimized for overlays, offline, WSL)
|
||||
|
||||
apt-layer --live-overlay action [options]
|
||||
# Manage live system overlayfs
|
||||
|
|
@ -600,6 +600,15 @@ BASIC LAYER CREATION:
|
|||
apt-layer composefs rollback <current-layer> <backup-layer>
|
||||
apt-layer composefs status
|
||||
|
||||
# Advanced ComposeFS Features (Phase 2.3)
|
||||
apt-layer composefs multi-compose <layer1> <layer2> ... <output-layer>
|
||||
apt-layer composefs deduplicate <input-layer> <output-layer> [strategy]
|
||||
apt-layer composefs compress <input-layer> <output-layer> [type] [level]
|
||||
apt-layer composefs benchmark <layer-path> [benchmark-file]
|
||||
apt-layer composefs resolve-conflicts <base-layer> <new-layer> <output-layer> [conflict-file]
|
||||
apt-layer composefs track-relationships <layer-path> <relationship-file> [parent-layers...]
|
||||
apt-layer composefs enhanced-metadata <source-dir> <metadata-file> [format]
|
||||
|
||||
LIVE SYSTEM MANAGEMENT:
|
||||
# Install packages on running system
|
||||
apt-layer --live-install firefox
|
||||
|
|
@ -1117,9 +1126,101 @@ main() {
|
|||
shift 2
|
||||
composefs_status
|
||||
;;
|
||||
multi-compose)
|
||||
# Multi-layer composition (Phase 2.3)
|
||||
local output_layer="${!#}"
|
||||
local layers=("${@:3:$#-3}")
|
||||
if [[ ${#layers[@]} -lt 2 ]]; then
|
||||
log_error "At least 2 layers required for multi-composition" "apt-layer"
|
||||
log_info "Usage: apt-layer composefs multi-compose <layer1> <layer2> ... <output-layer>" "apt-layer"
|
||||
show_usage
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
compose_multiple_layers "${layers[@]}" "$output_layer"
|
||||
;;
|
||||
deduplicate)
|
||||
local input_layer="${3:-}"
|
||||
local output_layer="${4:-}"
|
||||
local strategy="${5:-content-hash}"
|
||||
if [[ -z "$input_layer" ]] || [[ -z "$output_layer" ]]; then
|
||||
log_error "Input and output layers required" "apt-layer"
|
||||
log_info "Usage: apt-layer composefs deduplicate <input-layer> <output-layer> [strategy]" "apt-layer"
|
||||
show_usage
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
deduplicate_layer "$input_layer" "$output_layer" "$strategy"
|
||||
;;
|
||||
compress)
|
||||
local input_layer="${3:-}"
|
||||
local output_layer="${4:-}"
|
||||
local compression_type="${5:-gzip}"
|
||||
local compression_level="${6:-6}"
|
||||
if [[ -z "$input_layer" ]] || [[ -z "$output_layer" ]]; then
|
||||
log_error "Input and output layers required" "apt-layer"
|
||||
log_info "Usage: apt-layer composefs compress <input-layer> <output-layer> [type] [level]" "apt-layer"
|
||||
show_usage
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
compress_layer "$input_layer" "$output_layer" "$compression_type" "$compression_level"
|
||||
;;
|
||||
benchmark)
|
||||
local layer_path="${3:-}"
|
||||
local benchmark_file="${4:-}"
|
||||
if [[ -z "$layer_path" ]]; then
|
||||
log_error "Layer path required" "apt-layer"
|
||||
log_info "Usage: apt-layer composefs benchmark <layer-path> [benchmark-file]" "apt-layer"
|
||||
show_usage
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
benchmark_layer "$layer_path" "$benchmark_file"
|
||||
;;
|
||||
resolve-conflicts)
|
||||
local base_layer="${3:-}"
|
||||
local new_layer="${4:-}"
|
||||
local output_layer="${5:-}"
|
||||
local conflict_file="${6:-}"
|
||||
if [[ -z "$base_layer" ]] || [[ -z "$new_layer" ]] || [[ -z "$output_layer" ]]; then
|
||||
log_error "Base layer, new layer, and output layer required" "apt-layer"
|
||||
log_info "Usage: apt-layer composefs resolve-conflicts <base-layer> <new-layer> <output-layer> [conflict-file]" "apt-layer"
|
||||
show_usage
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
resolve_conflicts_interactive "$base_layer" "$new_layer" "$output_layer" "$conflict_file"
|
||||
;;
|
||||
track-relationships)
|
||||
local layer_path="${3:-}"
|
||||
local relationship_file="${4:-}"
|
||||
local parent_layers=("${@:5}")
|
||||
if [[ -z "$layer_path" ]] || [[ -z "$relationship_file" ]]; then
|
||||
log_error "Layer path and relationship file required" "apt-layer"
|
||||
log_info "Usage: apt-layer composefs track-relationships <layer-path> <relationship-file> [parent-layers...]" "apt-layer"
|
||||
show_usage
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
track_layer_relationships "$layer_path" "$relationship_file" "${parent_layers[@]}"
|
||||
;;
|
||||
enhanced-metadata)
|
||||
local source_dir="${3:-}"
|
||||
local metadata_file="${4:-}"
|
||||
local metadata_format="${5:-json}"
|
||||
if [[ -z "$source_dir" ]] || [[ -z "$metadata_file" ]]; then
|
||||
log_error "Source directory and metadata file required" "apt-layer"
|
||||
log_info "Usage: apt-layer composefs enhanced-metadata <source-dir> <metadata-file> [format]" "apt-layer"
|
||||
show_usage
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
handle_enhanced_metadata "$source_dir" "$metadata_file" "$metadata_format"
|
||||
;;
|
||||
*)
|
||||
log_error "Invalid composefs subcommand: $subcommand" "apt-layer"
|
||||
log_info "Valid subcommands: create, atomic-create, mount, unmount, compose, validate, test, rollback, status" "apt-layer"
|
||||
log_info "Valid subcommands: create, atomic-create, mount, unmount, compose, validate, test, rollback, status, multi-compose, deduplicate, compress, benchmark, resolve-conflicts, track-relationships, enhanced-metadata" "apt-layer"
|
||||
show_usage
|
||||
exit 1
|
||||
;;
|
||||
|
|
|
|||
603
test-advanced-composefs.sh
Normal file
603
test-advanced-composefs.sh
Normal file
|
|
@ -0,0 +1,603 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Test script for apt-layer Advanced ComposeFS Features
|
||||
# Validates the Phase 2.3 implementation: Advanced ComposeFS Features
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Test counters
|
||||
TOTAL_TESTS=0
|
||||
PASSED_TESTS=0
|
||||
FAILED_TESTS=0
|
||||
|
||||
# Test logging functions
|
||||
log_test() {
|
||||
echo -e "${BLUE}[TEST]${NC} $1"
|
||||
}
|
||||
|
||||
log_pass() {
|
||||
echo -e "${GREEN}[PASS]${NC} $1"
|
||||
((PASSED_TESTS++))
|
||||
}
|
||||
|
||||
log_fail() {
|
||||
echo -e "${RED}[FAIL]${NC} $1"
|
||||
((FAILED_TESTS++))
|
||||
}
|
||||
|
||||
log_info() {
|
||||
echo -e "${YELLOW}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
# Test summary
|
||||
print_summary() {
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "ADVANCED COMPOSEFS FEATURES TEST SUMMARY"
|
||||
echo "=========================================="
|
||||
echo "Total Tests: $TOTAL_TESTS"
|
||||
echo "Passed: $PASSED_TESTS"
|
||||
echo "Failed: $FAILED_TESTS"
|
||||
echo "Success Rate: $((PASSED_TESTS * 100 / TOTAL_TESTS))%"
|
||||
echo "=========================================="
|
||||
|
||||
if [[ $FAILED_TESTS -eq 0 ]]; then
|
||||
echo -e "${GREEN}All tests passed! Advanced ComposeFS features are working correctly.${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}Some tests failed. Please review the output above.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Cleanup function
|
||||
cleanup() {
|
||||
log_info "Cleaning up test artifacts..."
|
||||
|
||||
# Unmount any test mounts
|
||||
for mount_point in /tmp/apt-layer-advanced-test-*; do
|
||||
if [[ -d "$mount_point" ]] && mountpoint -q "$mount_point" 2>/dev/null; then
|
||||
umount "$mount_point" 2>/dev/null || true
|
||||
rmdir "$mount_point" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
|
||||
# Remove test files
|
||||
rm -rf /tmp/apt-layer-advanced-test-*
|
||||
}
|
||||
|
||||
# Setup test environment
|
||||
setup_test_env() {
|
||||
log_info "Setting up advanced test environment..."
|
||||
|
||||
# Create test directories
|
||||
mkdir -p /tmp/apt-layer-advanced-test-source1
|
||||
mkdir -p /tmp/apt-layer-advanced-test-source2
|
||||
mkdir -p /tmp/apt-layer-advanced-test-source3
|
||||
mkdir -p /tmp/apt-layer-advanced-test-layers
|
||||
mkdir -p /tmp/apt-layer-advanced-test-mounts
|
||||
|
||||
# Create test source content with duplicates
|
||||
echo "Common file content" > /tmp/apt-layer-advanced-test-source1/common.txt
|
||||
echo "Source 1 unique" > /tmp/apt-layer-advanced-test-source1/unique1.txt
|
||||
echo "Common file content" > /tmp/apt-layer-advanced-test-source1/duplicate.txt
|
||||
|
||||
echo "Common file content" > /tmp/apt-layer-advanced-test-source2/common.txt
|
||||
echo "Source 2 unique" > /tmp/apt-layer-advanced-test-source2/unique2.txt
|
||||
echo "Common file content" > /tmp/apt-layer-advanced-test-source2/duplicate.txt
|
||||
|
||||
echo "Source 3 unique" > /tmp/apt-layer-advanced-test-source3/unique3.txt
|
||||
echo "Common file content" > /tmp/apt-layer-advanced-test-source3/duplicate.txt
|
||||
|
||||
# Create subdirectories
|
||||
mkdir -p /tmp/apt-layer-advanced-test-source1/subdir
|
||||
echo "Subdir file" > /tmp/apt-layer-advanced-test-source1/subdir/file.txt
|
||||
|
||||
mkdir -p /tmp/apt-layer-advanced-test-source2/subdir
|
||||
echo "Subdir file" > /tmp/apt-layer-advanced-test-source2/subdir/file.txt
|
||||
|
||||
log_info "Advanced test environment setup completed"
|
||||
}
|
||||
|
||||
# Test 1: Multi-layer composition
|
||||
test_multi_layer_composition() {
|
||||
((TOTAL_TESTS++))
|
||||
log_test "Testing multi-layer composition..."
|
||||
|
||||
# Create base layers
|
||||
local source1="/tmp/apt-layer-advanced-test-source1"
|
||||
local source2="/tmp/apt-layer-advanced-test-source2"
|
||||
local source3="/tmp/apt-layer-advanced-test-source3"
|
||||
|
||||
local layer1="/tmp/apt-layer-advanced-test-layers/layer1.composefs"
|
||||
local layer2="/tmp/apt-layer-advanced-test-layers/layer2.composefs"
|
||||
local layer3="/tmp/apt-layer-advanced-test-layers/layer3.composefs"
|
||||
local composed="/tmp/apt-layer-advanced-test-layers/multi-composed.composefs"
|
||||
|
||||
# Create individual layers
|
||||
if ! apt-layer composefs create "$source1" "$layer1" "layer1"; then
|
||||
log_fail "Failed to create layer1"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! apt-layer composefs create "$source2" "$layer2" "layer2"; then
|
||||
log_fail "Failed to create layer2"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! apt-layer composefs create "$source3" "$layer3" "layer3"; then
|
||||
log_fail "Failed to create layer3"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Compose multiple layers
|
||||
if ! apt-layer composefs multi-compose "$layer1" "$layer2" "$layer3" "$composed"; then
|
||||
log_fail "Multi-layer composition failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Validate composed layer
|
||||
if ! apt-layer composefs validate "$composed"; then
|
||||
log_fail "Composed layer validation failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Test mounting composed layer
|
||||
local mount_point="/tmp/apt-layer-advanced-test-mounts/multi-composed"
|
||||
if ! apt-layer composefs mount "$composed" "$mount_point"; then
|
||||
log_fail "Failed to mount composed layer"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if files from all layers are present
|
||||
if [[ ! -f "$mount_point/common.txt" ]]; then
|
||||
log_fail "Common file not found in composed layer"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$mount_point/unique1.txt" ]]; then
|
||||
log_fail "Unique1 file not found in composed layer"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$mount_point/unique2.txt" ]]; then
|
||||
log_fail "Unique2 file not found in composed layer"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$mount_point/unique3.txt" ]]; then
|
||||
log_fail "Unique3 file not found in composed layer"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Unmount
|
||||
apt-layer composefs unmount "$mount_point"
|
||||
|
||||
log_pass "Multi-layer composition test passed"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Test 2: Layer deduplication
|
||||
test_layer_deduplication() {
|
||||
((TOTAL_TESTS++))
|
||||
log_test "Testing layer deduplication..."
|
||||
|
||||
local input_layer="/tmp/apt-layer-advanced-test-layers/multi-composed.composefs"
|
||||
local dedup_layer="/tmp/apt-layer-advanced-test-layers/deduplicated.composefs"
|
||||
|
||||
# Create input layer if it doesn't exist
|
||||
if [[ ! -f "$input_layer" ]]; then
|
||||
log_info "Creating input layer for deduplication test..."
|
||||
local source1="/tmp/apt-layer-advanced-test-source1"
|
||||
local source2="/tmp/apt-layer-advanced-test-source2"
|
||||
local layer1="/tmp/apt-layer-advanced-test-layers/layer1.composefs"
|
||||
local layer2="/tmp/apt-layer-advanced-test-layers/layer2.composefs"
|
||||
|
||||
apt-layer composefs create "$source1" "$layer1" "layer1"
|
||||
apt-layer composefs create "$source2" "$layer2" "layer2"
|
||||
apt-layer composefs multi-compose "$layer1" "$layer2" "$input_layer"
|
||||
fi
|
||||
|
||||
# Deduplicate layer
|
||||
if ! apt-layer composefs deduplicate "$input_layer" "$dedup_layer" "content-hash"; then
|
||||
log_fail "Layer deduplication failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Validate deduplicated layer
|
||||
if ! apt-layer composefs validate "$dedup_layer"; then
|
||||
log_fail "Deduplicated layer validation failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if deduplicated layer is smaller or same size
|
||||
local original_size
|
||||
local dedup_size
|
||||
original_size=$(stat -c%s "$input_layer")
|
||||
dedup_size=$(stat -c%s "$dedup_layer")
|
||||
|
||||
if [[ $dedup_size -gt $original_size ]]; then
|
||||
log_fail "Deduplicated layer is larger than original"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_pass "Layer deduplication test passed"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Test 3: Layer compression
|
||||
test_layer_compression() {
|
||||
((TOTAL_TESTS++))
|
||||
log_test "Testing layer compression..."
|
||||
|
||||
local input_layer="/tmp/apt-layer-advanced-test-layers/deduplicated.composefs"
|
||||
local compressed_gzip="/tmp/apt-layer-advanced-test-layers/compressed-gzip.composefs.gz"
|
||||
local compressed_zstd="/tmp/apt-layer-advanced-test-layers/compressed-zstd.composefs.zst"
|
||||
|
||||
# Create input layer if it doesn't exist
|
||||
if [[ ! -f "$input_layer" ]]; then
|
||||
log_info "Creating input layer for compression test..."
|
||||
local source1="/tmp/apt-layer-advanced-test-source1"
|
||||
local layer1="/tmp/apt-layer-advanced-test-layers/layer1.composefs"
|
||||
apt-layer composefs create "$source1" "$layer1" "layer1"
|
||||
apt-layer composefs deduplicate "$layer1" "$input_layer" "content-hash"
|
||||
fi
|
||||
|
||||
# Test gzip compression
|
||||
if ! apt-layer composefs compress "$input_layer" "$compressed_gzip" "gzip" "6"; then
|
||||
log_fail "Gzip compression failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Test zstd compression (if available)
|
||||
if command -v zstd &> /dev/null; then
|
||||
if ! apt-layer composefs compress "$input_layer" "$compressed_zstd" "zstd" "3"; then
|
||||
log_fail "Zstd compression failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check compression ratios
|
||||
local original_size
|
||||
local gzip_size
|
||||
local zstd_size
|
||||
|
||||
original_size=$(stat -c%s "$input_layer")
|
||||
gzip_size=$(stat -c%s "$compressed_gzip")
|
||||
zstd_size=$(stat -c%s "$compressed_zstd")
|
||||
|
||||
log_info "Compression results:"
|
||||
log_info " Original: $((original_size / 1024)) KB"
|
||||
log_info " Gzip: $((gzip_size / 1024)) KB"
|
||||
log_info " Zstd: $((zstd_size / 1024)) KB"
|
||||
else
|
||||
log_info "Zstd not available, skipping zstd compression test"
|
||||
fi
|
||||
|
||||
log_pass "Layer compression test passed"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Test 4: Layer benchmarking
|
||||
test_layer_benchmarking() {
|
||||
((TOTAL_TESTS++))
|
||||
log_test "Testing layer benchmarking..."
|
||||
|
||||
local layer_path="/tmp/apt-layer-advanced-test-layers/deduplicated.composefs"
|
||||
local benchmark_file="/tmp/apt-layer-advanced-test-layers/benchmark-report.txt"
|
||||
|
||||
# Create layer if it doesn't exist
|
||||
if [[ ! -f "$layer_path" ]]; then
|
||||
log_info "Creating layer for benchmarking test..."
|
||||
local source1="/tmp/apt-layer-advanced-test-source1"
|
||||
local layer1="/tmp/apt-layer-advanced-test-layers/layer1.composefs"
|
||||
apt-layer composefs create "$source1" "$layer1" "layer1"
|
||||
apt-layer composefs deduplicate "$layer1" "$layer_path" "content-hash"
|
||||
fi
|
||||
|
||||
# Run benchmark
|
||||
if ! apt-layer composefs benchmark "$layer_path" "$benchmark_file"; then
|
||||
log_fail "Layer benchmarking failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if benchmark file was created
|
||||
if [[ ! -f "$benchmark_file" ]]; then
|
||||
log_fail "Benchmark report file not created"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if benchmark file has content
|
||||
local file_size
|
||||
file_size=$(stat -c%s "$benchmark_file" 2>/dev/null || echo "0")
|
||||
if [[ $file_size -eq 0 ]]; then
|
||||
log_fail "Benchmark report file is empty"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_pass "Layer benchmarking test passed"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Test 5: Enhanced metadata handling
|
||||
test_enhanced_metadata() {
|
||||
((TOTAL_TESTS++))
|
||||
log_test "Testing enhanced metadata handling..."
|
||||
|
||||
local source_dir="/tmp/apt-layer-advanced-test-source1"
|
||||
local json_metadata="/tmp/apt-layer-advanced-test-layers/metadata.json"
|
||||
local yaml_metadata="/tmp/apt-layer-advanced-test-layers/metadata.yaml"
|
||||
|
||||
# Test JSON metadata
|
||||
if ! apt-layer composefs enhanced-metadata "$source_dir" "$json_metadata" "json"; then
|
||||
log_fail "JSON metadata generation failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Test YAML metadata
|
||||
if ! apt-layer composefs enhanced-metadata "$source_dir" "$yaml_metadata" "yaml"; then
|
||||
log_fail "YAML metadata generation failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Validate JSON metadata
|
||||
if ! command -v jq &> /dev/null; then
|
||||
log_info "jq not available, skipping JSON validation"
|
||||
else
|
||||
if ! jq empty "$json_metadata" 2>/dev/null; then
|
||||
log_fail "Invalid JSON metadata"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check required fields
|
||||
if ! jq -e '.metadata_version' "$json_metadata" >/dev/null 2>&1; then
|
||||
log_fail "Missing metadata_version in JSON"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! jq -e '.file_statistics.total_files' "$json_metadata" >/dev/null 2>&1; then
|
||||
log_fail "Missing file_statistics in JSON"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if metadata files have content
|
||||
local json_size
|
||||
local yaml_size
|
||||
json_size=$(stat -c%s "$json_metadata" 2>/dev/null || echo "0")
|
||||
yaml_size=$(stat -c%s "$yaml_metadata" 2>/dev/null || echo "0")
|
||||
|
||||
if [[ $json_size -eq 0 ]]; then
|
||||
log_fail "JSON metadata file is empty"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ $yaml_size -eq 0 ]]; then
|
||||
log_fail "YAML metadata file is empty"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_pass "Enhanced metadata handling test passed"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Test 6: Layer relationship tracking
|
||||
test_layer_relationships() {
|
||||
((TOTAL_TESTS++))
|
||||
log_test "Testing layer relationship tracking..."
|
||||
|
||||
local layer1="/tmp/apt-layer-advanced-test-layers/layer1.composefs"
|
||||
local layer2="/tmp/apt-layer-advanced-test-layers/layer2.composefs"
|
||||
local composed="/tmp/apt-layer-advanced-test-layers/multi-composed.composefs"
|
||||
local relationship_file="/tmp/apt-layer-advanced-test-layers/relationships.json"
|
||||
|
||||
# Create layers if they don't exist
|
||||
if [[ ! -f "$layer1" ]]; then
|
||||
local source1="/tmp/apt-layer-advanced-test-source1"
|
||||
apt-layer composefs create "$source1" "$layer1" "layer1"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$layer2" ]]; then
|
||||
local source2="/tmp/apt-layer-advanced-test-source2"
|
||||
apt-layer composefs create "$source2" "$layer2" "layer2"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$composed" ]]; then
|
||||
apt-layer composefs multi-compose "$layer1" "$layer2" "$composed"
|
||||
fi
|
||||
|
||||
# Track relationships
|
||||
if ! apt-layer composefs track-relationships "$composed" "$relationship_file" "$layer1" "$layer2"; then
|
||||
log_fail "Layer relationship tracking failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if relationship file was created
|
||||
if [[ ! -f "$relationship_file" ]]; then
|
||||
log_fail "Relationship file not created"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Validate relationship file (basic check)
|
||||
if ! command -v jq &> /dev/null; then
|
||||
log_info "jq not available, skipping relationship validation"
|
||||
else
|
||||
if ! jq empty "$relationship_file" 2>/dev/null; then
|
||||
log_fail "Invalid relationship JSON"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check required fields
|
||||
if ! jq -e '.layer_id' "$relationship_file" >/dev/null 2>&1; then
|
||||
log_fail "Missing layer_id in relationship file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! jq -e '.parent_layers' "$relationship_file" >/dev/null 2>&1; then
|
||||
log_fail "Missing parent_layers in relationship file"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
log_pass "Layer relationship tracking test passed"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Test 7: Advanced conflict resolution
|
||||
test_advanced_conflict_resolution() {
|
||||
((TOTAL_TESTS++))
|
||||
log_test "Testing advanced conflict resolution..."
|
||||
|
||||
# Create conflicting layers
|
||||
local conflict_source1="/tmp/apt-layer-advanced-test-conflict1"
|
||||
local conflict_source2="/tmp/apt-layer-advanced-test-conflict2"
|
||||
local conflict_layer1="/tmp/apt-layer-advanced-test-layers/conflict1.composefs"
|
||||
local conflict_layer2="/tmp/apt-layer-advanced-test-layers/conflict2.composefs"
|
||||
local resolved_layer="/tmp/apt-layer-advanced-test-layers/resolved.composefs"
|
||||
local conflict_report="/tmp/apt-layer-advanced-test-layers/conflict-report.txt"
|
||||
|
||||
# Create conflicting sources
|
||||
mkdir -p "$conflict_source1" "$conflict_source2"
|
||||
echo "Version 1 of conflicting file" > "$conflict_source1/conflict.txt"
|
||||
echo "Version 2 of conflicting file" > "$conflict_source2/conflict.txt"
|
||||
echo "Unique to source 1" > "$conflict_source1/unique1.txt"
|
||||
echo "Unique to source 2" > "$conflict_source2/unique2.txt"
|
||||
|
||||
# Create layers
|
||||
if ! apt-layer composefs create "$conflict_source1" "$conflict_layer1" "conflict1"; then
|
||||
log_fail "Failed to create conflict layer 1"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! apt-layer composefs create "$conflict_source2" "$conflict_layer2" "conflict2"; then
|
||||
log_fail "Failed to create conflict layer 2"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Resolve conflicts
|
||||
if ! apt-layer composefs resolve-conflicts "$conflict_layer1" "$conflict_layer2" "$resolved_layer" "$conflict_report"; then
|
||||
log_fail "Conflict resolution failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if conflict report was created
|
||||
if [[ ! -f "$conflict_report" ]]; then
|
||||
log_fail "Conflict report not created"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Validate resolved layer
|
||||
if ! apt-layer composefs validate "$resolved_layer"; then
|
||||
log_fail "Resolved layer validation failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Test mounting resolved layer
|
||||
local mount_point="/tmp/apt-layer-advanced-test-mounts/resolved"
|
||||
if ! apt-layer composefs mount "$resolved_layer" "$mount_point"; then
|
||||
log_fail "Failed to mount resolved layer"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if files are present
|
||||
if [[ ! -f "$mount_point/conflict.txt" ]]; then
|
||||
log_fail "Conflicting file not found in resolved layer"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$mount_point/unique1.txt" ]]; then
|
||||
log_fail "Unique1 file not found in resolved layer"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$mount_point/unique2.txt" ]]; then
|
||||
log_fail "Unique2 file not found in resolved layer"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Unmount
|
||||
apt-layer composefs unmount "$mount_point"
|
||||
|
||||
log_pass "Advanced conflict resolution test passed"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Test 8: Performance testing
|
||||
test_performance_features() {
|
||||
((TOTAL_TESTS++))
|
||||
log_test "Testing performance features..."
|
||||
|
||||
local layer_path="/tmp/apt-layer-advanced-test-layers/deduplicated.composefs"
|
||||
local benchmark_file="/tmp/apt-layer-advanced-test-layers/performance-benchmark.txt"
|
||||
|
||||
# Create layer if it doesn't exist
|
||||
if [[ ! -f "$layer_path" ]]; then
|
||||
log_info "Creating layer for performance test..."
|
||||
local source1="/tmp/apt-layer-advanced-test-source1"
|
||||
local layer1="/tmp/apt-layer-advanced-test-layers/layer1.composefs"
|
||||
apt-layer composefs create "$source1" "$layer1" "layer1"
|
||||
apt-layer composefs deduplicate "$layer1" "$layer_path" "content-hash"
|
||||
fi
|
||||
|
||||
# Run performance benchmark
|
||||
local start_time
|
||||
local end_time
|
||||
local benchmark_time
|
||||
|
||||
start_time=$(date +%s.%N)
|
||||
if ! apt-layer composefs benchmark "$layer_path" "$benchmark_file"; then
|
||||
log_fail "Performance benchmarking failed"
|
||||
return 1
|
||||
fi
|
||||
end_time=$(date +%s.%N)
|
||||
benchmark_time=$(echo "$end_time - $start_time" | bc -l 2>/dev/null || echo "0")
|
||||
|
||||
log_info "Benchmark execution time: ${benchmark_time}s"
|
||||
|
||||
# Check if benchmark completed within reasonable time (30 seconds)
|
||||
if [[ $(echo "$benchmark_time > 30" | bc -l 2>/dev/null || echo "0") -eq 1 ]]; then
|
||||
log_fail "Benchmark took too long: ${benchmark_time}s"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_pass "Performance features test passed"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Main test execution
|
||||
main() {
|
||||
echo "=========================================="
|
||||
echo "apt-layer ADVANCED COMPOSEFS FEATURES TEST"
|
||||
echo "=========================================="
|
||||
echo "Testing Phase 2.3: Advanced ComposeFS Features"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Setup test environment
|
||||
setup_test_env
|
||||
|
||||
# Run tests
|
||||
test_multi_layer_composition
|
||||
test_layer_deduplication
|
||||
test_layer_compression
|
||||
test_layer_benchmarking
|
||||
test_enhanced_metadata
|
||||
test_layer_relationships
|
||||
test_advanced_conflict_resolution
|
||||
test_performance_features
|
||||
|
||||
# Print summary
|
||||
print_summary
|
||||
}
|
||||
|
||||
# Cleanup on exit
|
||||
trap cleanup EXIT
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
Loading…
Add table
Add a link
Reference in a new issue