735 lines
No EOL
25 KiB
Bash
735 lines
No EOL
25 KiB
Bash
#!/bin/bash
|
|
# OSTree Atomic Package Management - Implementation for apt-layer
|
|
|
|
ostree_compose_install() {
|
|
local packages=("$@")
|
|
|
|
# Validate input
|
|
if [[ ${#packages[@]} -eq 0 ]]; then
|
|
log_error "No packages specified for installation" "apt-layer"
|
|
log_info "Usage: apt-layer ostree compose install <package1|.deb> [...]" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
log_info "[OSTree] Installing packages and creating atomic commit: ${packages[*]}" "apt-layer"
|
|
|
|
# Check for root privileges
|
|
if [[ $EUID -ne 0 ]]; then
|
|
log_error "Root privileges required for OSTree compose install" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Initialize workspace if needed
|
|
if ! init_workspace; then
|
|
log_error "Failed to initialize workspace" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Start live overlay if not active
|
|
if ! is_live_overlay_active; then
|
|
log_info "[OSTree] Starting live overlay for package installation" "apt-layer"
|
|
if ! start_live_overlay; then
|
|
log_error "Failed to start live overlay" "apt-layer"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Determine if .deb files or package names
|
|
local has_deb_files=false
|
|
for pkg in "${packages[@]}"; do
|
|
if [[ "$pkg" == *.deb ]] || [[ "$pkg" == */*.deb ]]; then
|
|
has_deb_files=true
|
|
break
|
|
fi
|
|
done
|
|
|
|
# Install packages in live overlay
|
|
log_info "[OSTree] Installing packages in live overlay" "apt-layer"
|
|
if [[ "$has_deb_files" == "true" ]]; then
|
|
log_info "[OSTree] Detected .deb files, using live_dpkg_install" "apt-layer"
|
|
if ! live_dpkg_install "${packages[@]}"; then
|
|
log_error "Failed to install .deb packages in overlay" "apt-layer"
|
|
return 1
|
|
fi
|
|
else
|
|
log_info "[OSTree] Detected package names, using live_install" "apt-layer"
|
|
if ! live_install "${packages[@]}"; then
|
|
log_error "Failed to install packages in overlay" "apt-layer"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Create OSTree-style commit
|
|
local commit_message="Install packages: ${packages[*]}"
|
|
local commit_id="ostree-$(date +%Y%m%d-%H%M%S)-$$"
|
|
|
|
log_info "[OSTree] Creating atomic commit: $commit_id" "apt-layer"
|
|
|
|
# Create simple commit metadata (avoid complex JSON escaping)
|
|
local packages_json="["
|
|
for i in "${!packages[@]}"; do
|
|
if [[ $i -gt 0 ]]; then
|
|
packages_json+=","
|
|
fi
|
|
packages_json+="\"${packages[$i]}\""
|
|
done
|
|
packages_json+="]"
|
|
|
|
local commit_data
|
|
commit_data=$(cat << EOF
|
|
{
|
|
"commit_id": "$commit_id",
|
|
"type": "ostree_compose",
|
|
"action": "install",
|
|
"packages": $packages_json,
|
|
"parent_commit": "$(get_current_deployment)",
|
|
"commit_message": "Install packages: $(IFS=' '; echo "${packages[*]}")",
|
|
"created": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
"composefs_image": "${commit_id}.composefs"
|
|
}
|
|
EOF
|
|
)
|
|
# Save commit metadata (for log/history)
|
|
local commit_log_dir="/var/lib/particle-os/ostree-commits"
|
|
mkdir -p "$commit_log_dir"
|
|
echo "$commit_data" > "$commit_log_dir/$commit_id.json"
|
|
|
|
# Commit live overlay changes as new layer
|
|
log_info "[OSTree] Committing overlay changes as OSTree layer" "apt-layer"
|
|
if ! commit_live_overlay "$commit_message"; then
|
|
log_error "Failed to commit overlay changes" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Get the created layer name (from commit_live_overlay)
|
|
local layer_name="live-overlay-commit-$(date +%Y%m%d_%H%M%S)"
|
|
|
|
# Create OSTree deployment commit
|
|
log_info "[OSTree] Creating deployment commit with layer: $layer_name" "apt-layer"
|
|
local deployment_commit_id
|
|
deployment_commit_id=$(create_deployment_commit "ostree-base" "$layer_name")
|
|
|
|
# Set as pending deployment (atomic)
|
|
set_pending_deployment "$deployment_commit_id"
|
|
|
|
log_success "[OSTree] Atomic commit created successfully: $deployment_commit_id" "apt-layer"
|
|
log_info "[OSTree] Commit includes packages: ${packages[*]}" "apt-layer"
|
|
log_info "[OSTree] Reboot to activate the new deployment" "apt-layer"
|
|
|
|
return 0
|
|
}
|
|
ostree_compose_remove() {
|
|
local packages=("$@")
|
|
|
|
# Validate input
|
|
if [[ ${#packages[@]} -eq 0 ]]; then
|
|
log_error "No packages specified for removal" "apt-layer"
|
|
log_info "Usage: apt-layer ostree compose remove <package1> [...]" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
log_info "[OSTree] Removing packages and creating atomic commit: ${packages[*]}" "apt-layer"
|
|
|
|
# Check for root privileges
|
|
if [[ $EUID -ne 0 ]]; then
|
|
log_error "Root privileges required for OSTree compose remove" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Initialize workspace if needed
|
|
if ! init_workspace; then
|
|
log_error "Failed to initialize workspace" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Start live overlay if not active
|
|
if ! is_live_overlay_active; then
|
|
log_info "[OSTree] Starting live overlay for package removal" "apt-layer"
|
|
if ! start_live_overlay; then
|
|
log_error "Failed to start live overlay" "apt-layer"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Remove packages in live overlay
|
|
log_info "[OSTree] Removing packages in live overlay" "apt-layer"
|
|
if ! live_remove "${packages[@]}"; then
|
|
log_error "Failed to remove packages in overlay" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Create OSTree-style commit
|
|
local commit_message="Remove packages: ${packages[*]}"
|
|
local commit_id="ostree-$(date +%Y%m%d-%H%M%S)-$$"
|
|
|
|
log_info "[OSTree] Creating atomic commit: $commit_id" "apt-layer"
|
|
|
|
# Create simple commit metadata
|
|
local packages_json="["
|
|
for i in "${!packages[@]}"; do
|
|
if [[ $i -gt 0 ]]; then
|
|
packages_json+=","
|
|
fi
|
|
packages_json+="\"${packages[$i]}\""
|
|
done
|
|
packages_json+="]"
|
|
|
|
local commit_data
|
|
commit_data=$(cat << EOF
|
|
{
|
|
"commit_id": "$commit_id",
|
|
"type": "ostree_compose",
|
|
"action": "remove",
|
|
"packages": $packages_json,
|
|
"parent_commit": "$(get_current_deployment)",
|
|
"commit_message": "Remove packages: $(IFS=' '; echo "${packages[*]}")",
|
|
"created": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
"composefs_image": "${commit_id}.composefs"
|
|
}
|
|
EOF
|
|
)
|
|
# Save commit metadata (for log/history)
|
|
local commit_log_dir="/var/lib/particle-os/ostree-commits"
|
|
mkdir -p "$commit_log_dir"
|
|
echo "$commit_data" > "$commit_log_dir/$commit_id.json"
|
|
|
|
# Commit live overlay changes as new layer
|
|
log_info "[OSTree] Committing overlay changes as OSTree layer" "apt-layer"
|
|
if ! commit_live_overlay "$commit_message"; then
|
|
log_error "Failed to commit overlay changes" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Get the created layer name (from commit_live_overlay)
|
|
local layer_name="live-overlay-commit-$(date +%Y%m%d_%H%M%S)"
|
|
|
|
# Create OSTree deployment commit
|
|
log_info "[OSTree] Creating deployment commit with layer: $layer_name" "apt-layer"
|
|
local deployment_commit_id
|
|
deployment_commit_id=$(create_deployment_commit "ostree-base" "$layer_name")
|
|
|
|
# Set as pending deployment (atomic)
|
|
set_pending_deployment "$deployment_commit_id"
|
|
|
|
log_success "[OSTree] Atomic commit created successfully: $deployment_commit_id" "apt-layer"
|
|
log_info "[OSTree] Commit includes removed packages: ${packages[*]}" "apt-layer"
|
|
log_info "[OSTree] Reboot to activate the new deployment" "apt-layer"
|
|
|
|
return 0
|
|
}
|
|
ostree_compose_update() {
|
|
local packages=("$@")
|
|
|
|
# Validate input
|
|
if [[ ${#packages[@]} -eq 0 ]]; then
|
|
log_error "No packages specified for update" "apt-layer"
|
|
log_info "Usage: apt-layer ostree compose update [package1] [...]" "apt-layer"
|
|
log_info "Note: If no packages specified, updates all packages" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
log_info "[OSTree] Updating packages and creating atomic commit: ${packages[*]}" "apt-layer"
|
|
|
|
# Check for root privileges
|
|
if [[ $EUID -ne 0 ]]; then
|
|
log_error "Root privileges required for OSTree compose update" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Initialize workspace if needed
|
|
if ! init_workspace; then
|
|
log_error "Failed to initialize workspace" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Start live overlay if not active
|
|
if ! is_live_overlay_active; then
|
|
log_info "[OSTree] Starting live overlay for package update" "apt-layer"
|
|
if ! start_live_overlay; then
|
|
log_error "Failed to start live overlay" "apt-layer"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Update packages in live overlay
|
|
log_info "[OSTree] Updating packages in live overlay" "apt-layer"
|
|
if ! live_update "${packages[@]}"; then
|
|
log_error "Failed to update packages in overlay" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Create OSTree-style commit
|
|
local commit_message="Update packages: ${packages[*]}"
|
|
local commit_id="ostree-$(date +%Y%m%d-%H%M%S)-$$"
|
|
|
|
log_info "[OSTree] Creating atomic commit: $commit_id" "apt-layer"
|
|
|
|
# Create simple commit metadata
|
|
local packages_json="["
|
|
for i in "${!packages[@]}"; do
|
|
if [[ $i -gt 0 ]]; then
|
|
packages_json+=","
|
|
fi
|
|
packages_json+="\"${packages[$i]}\""
|
|
done
|
|
packages_json+="]"
|
|
|
|
local commit_data
|
|
commit_data=$(cat << EOF
|
|
{
|
|
"commit_id": "$commit_id",
|
|
"type": "ostree_compose",
|
|
"action": "update",
|
|
"packages": $packages_json,
|
|
"parent_commit": "$(get_current_deployment)",
|
|
"commit_message": "Update packages: $(IFS=' '; echo "${packages[*]}")",
|
|
"created": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
"composefs_image": "${commit_id}.composefs"
|
|
}
|
|
EOF
|
|
)
|
|
# Save commit metadata (for log/history)
|
|
local commit_log_dir="/var/lib/particle-os/ostree-commits"
|
|
mkdir -p "$commit_log_dir"
|
|
echo "$commit_data" > "$commit_log_dir/$commit_id.json"
|
|
|
|
# Commit live overlay changes as new layer
|
|
log_info "[OSTree] Committing overlay changes as OSTree layer" "apt-layer"
|
|
if ! commit_live_overlay "$commit_message"; then
|
|
log_error "Failed to commit overlay changes" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Get the created layer name (from commit_live_overlay)
|
|
local layer_name="live-overlay-commit-$(date +%Y%m%d_%H%M%S)"
|
|
|
|
# Create OSTree deployment commit
|
|
log_info "[OSTree] Creating deployment commit with layer: $layer_name" "apt-layer"
|
|
local deployment_commit_id
|
|
deployment_commit_id=$(create_deployment_commit "ostree-base" "$layer_name")
|
|
|
|
# Set as pending deployment (atomic)
|
|
set_pending_deployment "$deployment_commit_id"
|
|
|
|
log_success "[OSTree] Atomic commit created successfully: $deployment_commit_id" "apt-layer"
|
|
log_info "[OSTree] Commit includes updated packages: ${packages[*]}" "apt-layer"
|
|
log_info "[OSTree] Reboot to activate the new deployment" "apt-layer"
|
|
|
|
return 0
|
|
}
|
|
ostree_log() {
|
|
local format="${1:-full}"
|
|
local limit="${2:-10}"
|
|
|
|
log_info "[OSTree] Showing commit log (format: $format, limit: $limit)" "apt-layer"
|
|
|
|
if [[ ! -f "$DEPLOYMENT_DB" ]]; then
|
|
log_error "No deployment database found" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
case "$format" in
|
|
"full"|"detailed")
|
|
echo "=== OSTree Commit Log ==="
|
|
jq -r --arg limit "$limit" '
|
|
.deployments | to_entries |
|
|
sort_by(.value.created) | reverse |
|
|
.[0:($limit | tonumber)] | .[] |
|
|
"Commit: " + .key + "\n" +
|
|
"Message: " + (.value.commit_message // "unknown") + "\n" +
|
|
"Type: " + (.value.type // "unknown") + "\n" +
|
|
"Action: " + (.value.action // "unknown") + "\n" +
|
|
"Created: " + (.value.created // "unknown") + "\n" +
|
|
"Base: " + (.value.base_image // "unknown") + "\n" +
|
|
"Layers: " + (.value.layers | join(", ") // "none") + "\n" +
|
|
"Packages: " + (.value.packages | join(", ") // "none") + "\n" +
|
|
"---"
|
|
' "$DEPLOYMENT_DB" 2>/dev/null || echo "No commits found"
|
|
;;
|
|
"short"|"compact")
|
|
echo "=== OSTree Commit Log (Compact) ==="
|
|
jq -r --arg limit "$limit" '
|
|
.deployments | to_entries |
|
|
sort_by(.value.created) | reverse |
|
|
.[0:($limit | tonumber)] | .[] |
|
|
"\(.key) - \(.value.commit_message // "unknown") (\(.value.created // "unknown"))"
|
|
' "$DEPLOYMENT_DB" 2>/dev/null || echo "No commits found"
|
|
;;
|
|
"json")
|
|
echo "=== OSTree Commit Log (JSON) ==="
|
|
jq -r --arg limit "$limit" '
|
|
.deployments | to_entries |
|
|
sort_by(.value.created) | reverse |
|
|
.[0:($limit | tonumber)] |
|
|
map({commit_id: .key, details: .value})
|
|
' "$DEPLOYMENT_DB" 2>/dev/null || echo "[]"
|
|
;;
|
|
*)
|
|
log_error "Invalid format: $format. Use: full, short, or json" "apt-layer"
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
ostree_diff() {
|
|
local commit1="${1:-}"
|
|
local commit2="${2:-}"
|
|
|
|
log_info "[OSTree] Showing diff between commits" "apt-layer"
|
|
|
|
if [[ ! -f "$DEPLOYMENT_DB" ]]; then
|
|
log_error "No deployment database found" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# If no commits specified, show diff between current and previous
|
|
if [[ -z "$commit1" ]]; then
|
|
local current_deployment
|
|
current_deployment=$(get_current_deployment)
|
|
if [[ -z "$current_deployment" ]]; then
|
|
log_error "No current deployment found" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Get the commit before current
|
|
commit1=$(jq -r --arg current "$current_deployment" '
|
|
.deployments | to_entries |
|
|
sort_by(.value.created) |
|
|
map(.key) |
|
|
index($current) as $idx |
|
|
if $idx > 0 then .[$idx - 1] else null end
|
|
' "$DEPLOYMENT_DB" 2>/dev/null)
|
|
|
|
if [[ -z "$commit1" || "$commit1" == "null" ]]; then
|
|
log_error "No previous commit found" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
commit2="$current_deployment"
|
|
log_info "[OSTree] Comparing $commit1 -> $commit2" "apt-layer"
|
|
elif [[ -z "$commit2" ]]; then
|
|
# If only one commit specified, compare with current
|
|
local current_deployment
|
|
current_deployment=$(get_current_deployment)
|
|
if [[ -z "$current_deployment" ]]; then
|
|
log_error "No current deployment found" "apt-layer"
|
|
return 1
|
|
fi
|
|
commit2="$current_deployment"
|
|
fi
|
|
|
|
# Validate commits exist
|
|
if ! jq -e ".deployments[\"$commit1\"]" "$DEPLOYMENT_DB" >/dev/null 2>&1; then
|
|
log_error "Commit not found: $commit1" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
if ! jq -e ".deployments[\"$commit2\"]" "$DEPLOYMENT_DB" >/dev/null 2>&1; then
|
|
log_error "Commit not found: $commit2" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Get commit data
|
|
local commit1_data
|
|
commit1_data=$(jq -r ".deployments[\"$commit1\"]" "$DEPLOYMENT_DB")
|
|
local commit2_data
|
|
commit2_data=$(jq -r ".deployments[\"$commit2\"]" "$DEPLOYMENT_DB")
|
|
|
|
echo "=== OSTree Diff: $commit1 -> $commit2 ==="
|
|
echo ""
|
|
|
|
# Compare commit messages
|
|
local msg1
|
|
msg1=$(echo "$commit1_data" | jq -r '.commit_message // "unknown"')
|
|
local msg2
|
|
msg2=$(echo "$commit2_data" | jq -r '.commit_message // "unknown"')
|
|
echo "Commit Messages:"
|
|
echo " $commit1: $msg1"
|
|
echo " $commit2: $msg2"
|
|
echo ""
|
|
|
|
# Compare creation times
|
|
local time1
|
|
time1=$(echo "$commit1_data" | jq -r '.created // "unknown"')
|
|
local time2
|
|
time2=$(echo "$commit2_data" | jq -r '.created // "unknown"')
|
|
echo "Creation Times:"
|
|
echo " $commit1: $time1"
|
|
echo " $commit2: $time2"
|
|
echo ""
|
|
|
|
# Compare layers
|
|
local layers1
|
|
layers1=$(echo "$commit1_data" | jq -r '.layers | join(", ") // "none"')
|
|
local layers2
|
|
layers2=$(echo "$commit2_data" | jq -r '.layers | join(", ") // "none"')
|
|
echo "Layers:"
|
|
echo " $commit1: $layers1"
|
|
echo " $commit2: $layers2"
|
|
echo ""
|
|
|
|
# Compare packages (if available)
|
|
local packages1
|
|
packages1=$(echo "$commit1_data" | jq -r '.packages | join(", ") // "none"' 2>/dev/null || echo "none")
|
|
local packages2
|
|
packages2=$(echo "$commit2_data" | jq -r '.packages | join(", ") // "none"' 2>/dev/null || echo "none")
|
|
echo "Packages:"
|
|
echo " $commit1: $packages1"
|
|
echo " $commit2: $packages2"
|
|
echo ""
|
|
|
|
# Show action type
|
|
local action1
|
|
action1=$(echo "$commit1_data" | jq -r '.action // "unknown"')
|
|
local action2
|
|
action2=$(echo "$commit2_data" | jq -r '.action // "unknown"')
|
|
echo "Actions:"
|
|
echo " $commit1: $action1"
|
|
echo " $commit2: $action2"
|
|
echo ""
|
|
|
|
# Calculate time difference
|
|
if [[ "$time1" != "unknown" && "$time2" != "unknown" ]]; then
|
|
local time_diff
|
|
time_diff=$(($(date -d "$time2" +%s) - $(date -d "$time1" +%s)))
|
|
echo "Time Difference: $time_diff seconds"
|
|
echo ""
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
ostree_rollback() {
|
|
local target_commit="${1:-}"
|
|
|
|
log_info "[OSTree] Rolling back deployment" "apt-layer"
|
|
|
|
# Check for root privileges
|
|
if [[ $EUID -ne 0 ]]; then
|
|
log_error "Root privileges required for OSTree rollback" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Get current deployment
|
|
local current_deployment
|
|
current_deployment=$(get_current_deployment)
|
|
|
|
if [[ -z "$current_deployment" ]]; then
|
|
log_error "No current deployment found" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# If no target specified, rollback to previous commit
|
|
if [[ -z "$target_commit" ]]; then
|
|
log_info "[OSTree] No target specified, rolling back to previous commit" "apt-layer"
|
|
|
|
# Get the commit before current
|
|
target_commit=$(jq -r --arg current "$current_deployment" '
|
|
.deployments | to_entries |
|
|
sort_by(.value.created) |
|
|
map(.key) |
|
|
index($current) as $idx |
|
|
if $idx > 0 then .[$idx - 1] else null end
|
|
' "$DEPLOYMENT_DB" 2>/dev/null)
|
|
|
|
if [[ -z "$target_commit" || "$target_commit" == "null" ]]; then
|
|
log_error "No previous commit found to rollback to" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
log_info "[OSTree] Rolling back to: $target_commit" "apt-layer"
|
|
fi
|
|
|
|
# Validate target commit exists
|
|
if ! jq -e ".deployments[\"$target_commit\"]" "$DEPLOYMENT_DB" >/dev/null 2>&1; then
|
|
log_error "Target commit not found: $target_commit" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Create rollback commit
|
|
local rollback_id="rollback-$(date +%Y%m%d-%H%M%S)-$$"
|
|
local rollback_message="Rollback from $current_deployment to $target_commit"
|
|
|
|
log_info "[OSTree] Creating rollback commit: $rollback_id" "apt-layer"
|
|
|
|
# Get target commit data
|
|
local target_data
|
|
target_data=$(jq -r ".deployments[\"$target_commit\"]" "$DEPLOYMENT_DB")
|
|
local base_image
|
|
base_image=$(echo "$target_data" | jq -r '.base_image')
|
|
local layers
|
|
layers=$(echo "$target_data" | jq -r '.layers | join(" ")')
|
|
|
|
# Create rollback deployment commit
|
|
local rollback_commit_id
|
|
rollback_commit_id=$(create_deployment_commit "$base_image" $layers)
|
|
|
|
# Set as pending deployment
|
|
set_pending_deployment "$rollback_commit_id"
|
|
|
|
log_success "[OSTree] Rollback prepared successfully" "apt-layer"
|
|
log_info "[OSTree] Rollback from: $current_deployment" "apt-layer"
|
|
log_info "[OSTree] Rollback to: $target_commit" "apt-layer"
|
|
log_info "[OSTree] New deployment: $rollback_commit_id" "apt-layer"
|
|
log_info "[OSTree] Reboot to activate rollback" "apt-layer"
|
|
|
|
return 0
|
|
}
|
|
ostree_status() {
|
|
log_info "[OSTree] Showing current deployment status" "apt-layer"
|
|
|
|
# Get current and pending deployments
|
|
local current_deployment
|
|
current_deployment=$(get_current_deployment)
|
|
local pending_deployment
|
|
pending_deployment=$(get_pending_deployment 2>/dev/null | tail -n1)
|
|
|
|
echo "=== OSTree Deployment Status ==="
|
|
echo "Current Deployment: ${current_deployment:-none}"
|
|
echo "Pending Deployment: ${pending_deployment:-none}"
|
|
echo ""
|
|
|
|
# Show recent commits (last 5)
|
|
echo "=== Recent Commits ==="
|
|
if [[ -f "$DEPLOYMENT_DB" ]]; then
|
|
jq -r '.deployments | to_entries | sort_by(.value.created) | reverse | .[0:5] | .[] | "\(.key) - \(.value.commit_message) (\(.value.created))"' "$DEPLOYMENT_DB" 2>/dev/null || echo "No commits found"
|
|
else
|
|
echo "No deployment database found"
|
|
fi
|
|
echo ""
|
|
|
|
# Show layer information for current deployment
|
|
if [[ -n "$current_deployment" ]]; then
|
|
echo "=== Current Deployment Details ==="
|
|
local commit_data
|
|
commit_data=$(jq -r ".deployments[\"$current_deployment\"]" "$DEPLOYMENT_DB" 2>/dev/null)
|
|
if [[ -n "$commit_data" ]]; then
|
|
echo "Base Image: $(echo "$commit_data" | jq -r '.base_image // "unknown"')"
|
|
echo "Layers: $(echo "$commit_data" | jq -r '.layers | join(", ") // "none"')"
|
|
echo "Created: $(echo "$commit_data" | jq -r '.created // "unknown"')"
|
|
fi
|
|
fi
|
|
echo ""
|
|
|
|
# Show available layers
|
|
echo "=== Available Layers ==="
|
|
if [[ -d "/var/lib/particle-os/build" ]]; then
|
|
find /var/lib/particle-os/build -name "*.squashfs" -type f | head -10 | while read -r layer; do
|
|
local size
|
|
size=$(du -h "$layer" | cut -f1)
|
|
local name
|
|
name=$(basename "$layer")
|
|
echo "$name ($size)"
|
|
done
|
|
else
|
|
echo "No layers found"
|
|
fi
|
|
}
|
|
ostree_cleanup() {
|
|
local keep_count="${1:-5}"
|
|
local dry_run="${2:-false}"
|
|
|
|
log_info "[OSTree] Cleaning up old commits (keeping $keep_count)" "apt-layer"
|
|
|
|
# Check for root privileges
|
|
if [[ $EUID -ne 0 ]]; then
|
|
log_error "Root privileges required for OSTree cleanup" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
if [[ ! -f "$DEPLOYMENT_DB" ]]; then
|
|
log_error "No deployment database found" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Get current and pending deployments (never delete these)
|
|
local current_deployment
|
|
current_deployment=$(get_current_deployment)
|
|
local pending_deployment
|
|
pending_deployment=$(get_pending_deployment)
|
|
|
|
# Get commits to keep (most recent + current + pending)
|
|
local keep_commits
|
|
keep_commits=$(jq -r --arg keep "$keep_count" --arg current "$current_deployment" --arg pending "$pending_deployment" '
|
|
.deployments | to_entries |
|
|
sort_by(.value.created) | reverse |
|
|
.[0:($keep | tonumber)] |
|
|
map(.key) +
|
|
if $current != "" then [$current] else [] end +
|
|
if $pending != "" and $pending != $current then [$pending] else [] end |
|
|
unique | join(" ")
|
|
' "$DEPLOYMENT_DB" 2>/dev/null)
|
|
|
|
# Get all commits
|
|
local all_commits
|
|
all_commits=$(jq -r '.deployments | keys | join(" ")' "$DEPLOYMENT_DB" 2>/dev/null)
|
|
|
|
# Find commits to delete
|
|
local to_delete=()
|
|
for commit in $all_commits; do
|
|
if [[ ! " $keep_commits " =~ " $commit " ]]; then
|
|
to_delete+=("$commit")
|
|
fi
|
|
done
|
|
|
|
if [[ ${#to_delete[@]} -eq 0 ]]; then
|
|
log_info "[OSTree] No commits to clean up" "apt-layer"
|
|
return 0
|
|
fi
|
|
|
|
echo "=== OSTree Cleanup Summary ==="
|
|
echo "Keeping commits: $keep_commits"
|
|
echo "Commits to delete: ${to_delete[*]}"
|
|
echo "Total to delete: ${#to_delete[@]}"
|
|
echo ""
|
|
|
|
if [[ "$dry_run" == "true" ]]; then
|
|
log_info "[OSTree] Dry run - no changes made" "apt-layer"
|
|
return 0
|
|
fi
|
|
|
|
# Confirm deletion
|
|
echo "Are you sure you want to delete these commits? (y/N)"
|
|
read -r response
|
|
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
|
log_info "[OSTree] Cleanup cancelled" "apt-layer"
|
|
return 0
|
|
fi
|
|
|
|
# Delete commits
|
|
local deleted_count=0
|
|
for commit in "${to_delete[@]}"; do
|
|
log_info "[OSTree] Deleting commit: $commit" "apt-layer"
|
|
|
|
# Remove from database
|
|
jq --arg commit "$commit" 'del(.deployments[$commit])' "$DEPLOYMENT_DB" > "${DEPLOYMENT_DB}.tmp" && mv "${DEPLOYMENT_DB}.tmp" "$DEPLOYMENT_DB"
|
|
|
|
# Remove history file
|
|
rm -f "$DEPLOYMENT_HISTORY_DIR/$commit.json"
|
|
|
|
# Remove associated layers (if not used by other commits)
|
|
local commit_data
|
|
commit_data=$(jq -r ".deployments[\"$commit\"]" "$DEPLOYMENT_DB" 2>/dev/null)
|
|
if [[ -n "$commit_data" ]]; then
|
|
local layers
|
|
layers=$(echo "$commit_data" | jq -r '.layers[]?' 2>/dev/null)
|
|
for layer in $layers; do
|
|
# Check if layer is used by other commits
|
|
local layer_used
|
|
layer_used=$(jq -r --arg layer "$layer" '
|
|
.deployments | to_entries |
|
|
map(select(.value.layers | contains([$layer]))) |
|
|
length
|
|
' "$DEPLOYMENT_DB" 2>/dev/null)
|
|
|
|
if [[ "$layer_used" == "0" ]]; then
|
|
log_info "[OSTree] Removing unused layer: $layer" "apt-layer"
|
|
rm -f "/var/lib/particle-os/build/$layer.squashfs"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
((deleted_count++))
|
|
done
|
|
|
|
log_success "[OSTree] Cleanup completed: $deleted_count commits deleted" "apt-layer"
|
|
return 0
|
|
} |