#!/usr/bin/env bash # Revision: 4 # Sync portal (file server) content from the repo to the LXC. # # All files that first-boot.sh downloads live in cloud-init/fileserver/. # This script rsyncs that folder to the LXC's portal-files directory so # the dashboard /files/ endpoint serves them. # # Expected layout on the LXC after sync: # /var/lib/cm4-provisioning/portal-files/ # ├── first-boot.sh ← cloud-init runcmd downloads this # ├── first-boot.conf ← cloud-init runcmd downloads this (required) # ├── first-boot.conf.example ← reference # └── first-boot/ ← FILE_SERVER points here # ├── steps/ # │ ├── 01-hostname.sh … 13-reboot.sh # ├── start-chromium.sh # ├── splash.png # └── ... # # Usage: ./sync-portal-files-to-lxc.sh [user@lxc_ip] # Example: ./sync-portal-files-to-lxc.sh root@10.130.60.141 set -e LXC="${1:-root@10.130.60.141}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_DIR="$(dirname "$SCRIPT_DIR")" CLOUDINIT_DIR="$REPO_DIR/cloud-init" FILESERVER_DIR="$CLOUDINIT_DIR/fileserver" REMOTE_PORTAL="/var/lib/cm4-provisioning/portal-files" REMOTE_FIRST_BOOT="${REMOTE_PORTAL}/first-boot" # Files we sync to the portal root (outside first-boot/) PORTAL_ROOT_FILES=(first-boot.sh first-boot.conf first-boot.conf.example) # ── Validate local files ──────────────────────────────────────────────── if [[ ! -d "$FILESERVER_DIR" ]]; then echo "Error: fileserver dir not found: $FILESERVER_DIR" exit 1 fi if [[ ! -f "$CLOUDINIT_DIR/first-boot.sh" ]]; then echo "Error: first-boot.sh not found in $CLOUDINIT_DIR" exit 1 fi if [[ ! -f "$CLOUDINIT_DIR/first-boot.conf" ]]; then echo "Warning: first-boot.conf not found — first-boot.sh requires it!" fi echo "╔══════════════════════════════════════════════════════════════╗" echo "║ Sync portal files → $LXC" echo "╚══════════════════════════════════════════════════════════════╝" echo "" # ── Ensure rsync on LXC ──────────────────────────────────────────────── ssh "$LXC" "command -v rsync >/dev/null 2>&1 || (apt-get update -qq && apt-get install -y rsync)" ssh "$LXC" "mkdir -p $REMOTE_FIRST_BOOT" # ── Dry-run: show what will change ────────────────────────────────────── echo "── Preview (dry-run) ──────────────────────────────────────────" echo "" echo "Portal root ($REMOTE_PORTAL/):" for f in "${PORTAL_ROOT_FILES[@]}"; do if [[ -f "$CLOUDINIT_DIR/$f" ]]; then echo " ✓ $f" else echo " ✗ $f (not found locally, will skip)" fi done echo "" echo "first-boot/ assets ($REMOTE_FIRST_BOOT/):" CHANGES=$(rsync -avzn --delete "$FILESERVER_DIR/" "$LXC:$REMOTE_FIRST_BOOT/" 2>&1 | grep -v '^\(sending\|sent\|total\|$\|building\|\.d\.\.\.\.\.\.\.\.\.\)' | head -40) if [[ -z "$CHANGES" ]]; then echo " (no changes)" else echo "$CHANGES" | sed 's/^/ /' fi echo "" read -rp "Proceed with sync? [Y/n] " CONFIRM CONFIRM="${CONFIRM:-Y}" if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then echo "Aborted." exit 0 fi # ── Sync portal root files ────────────────────────────────────────────── echo "" echo "── Syncing portal root files ──────────────────────────────────" for f in "${PORTAL_ROOT_FILES[@]}"; do if [[ -f "$CLOUDINIT_DIR/$f" ]]; then rsync -avz "$CLOUDINIT_DIR/$f" "$LXC:$REMOTE_PORTAL/" fi done # ── Sync fileserver/ → first-boot/ (with --delete) ────────────────────── echo "" echo "── Syncing fileserver/ → first-boot/ ──────────────────────────" rsync -avz --delete "$FILESERVER_DIR/" "$LXC:$REMOTE_FIRST_BOOT/" # ── Check for extra files in portal root ──────────────────────────────── echo "" echo "── Checking for extra files in portal root ────────────────────" REMOTE_FILES=$(ssh "$LXC" "find $REMOTE_PORTAL -maxdepth 1 -not -path $REMOTE_PORTAL -printf '%f\n' 2>/dev/null" | sort) EXPECTED_FILES=$(printf '%s\n' "${PORTAL_ROOT_FILES[@]}" "first-boot" | sort) EXTRA_FILES=$(comm -23 <(echo "$REMOTE_FILES") <(echo "$EXPECTED_FILES")) if [[ -z "$EXTRA_FILES" ]]; then echo " No extra files found. Portal root is clean." else echo " Extra files/folders found in $REMOTE_PORTAL/:" echo "$EXTRA_FILES" | while read -r f; do SIZE=$(ssh "$LXC" "du -sh '$REMOTE_PORTAL/$f' 2>/dev/null | cut -f1" 2>/dev/null || echo "?") TYPE="file" ssh "$LXC" "[ -d '$REMOTE_PORTAL/$f' ]" 2>/dev/null && TYPE="dir" echo " $f ($TYPE, $SIZE)" done echo "" read -rp " Remove extra files? [y/N] " REMOVE if [[ "$REMOVE" =~ ^[Yy]$ ]]; then echo "$EXTRA_FILES" | while read -r f; do echo " Removing: $f" ssh "$LXC" "rm -rf '$REMOTE_PORTAL/$f'" done echo " Done." else echo " Kept extra files." fi fi echo "" echo "══════════════════════════════════════════════════════════════" echo "Done. Portal files at http://$(echo "$LXC" | cut -d@ -f2):5000/files/" echo "══════════════════════════════════════════════════════════════"