1
0
forked from finn/tinyboard
Files
tinyboard/hubspoke-helper.sh
2026-04-13 13:39:16 -07:00

203 lines
5.3 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# hubspoke-helper.sh - Manage hub/spoke rclone mounts
# Assumes spoke Docker files exist in ~/autossh-tunnel/
# Assumes hub rclone service template is manually placed (or not needed)
set -euo pipefail
# ------------------------------------------------------------
# Configuration (override with env vars if needed)
# ------------------------------------------------------------
TUNNEL_DIR="${TUNNEL_DIR:-$HOME/tinyboard/spoke}"
COMPOSE_FILE="${COMPOSE_FILE:-$TUNNEL_DIR/compose.yaml}"
RCLONE_REMOTE="${RCLONE_REMOTE:-brie-remote}"
MOUNT_POINT="${MOUNT_POINT:-$HOME/mnt/$RCLONE_REMOTE}"
# ------------------------------------------------------------
# Usage
# ------------------------------------------------------------
usage() {
cat <<EOF
Usage: $0 {hub|spoke} {action}
SPOKE ACTIONS (docker-based, no systemd):
build Build the autossh image (run once)
start Start the tunnel container
stop Stop the tunnel container
restart Restart the tunnel container
status Show container status
logs Show container logs
show-cmd Show manual autossh command (non-docker)
HUB ACTIONS (systemd user service for rclone mount):
install Install the rclone user service (needs service file)
start Start the rclone mount service
stop Stop the rclone mount service
status Show service status
mount Manual foreground mount (testing)
unmount Unmount manually
EXAMPLES:
$0 spoke build
$0 spoke start
$0 hub install
$0 hub start
EOF
}
die() {
echo "ERROR: $*" >&2
exit 1
}
# ------------------------------------------------------------
# Spoke actions (docker)
# ------------------------------------------------------------
spoke_build() {
if [ ! -f "$COMPOSE_FILE" ]; then
die "docker-compose.yaml not found at $COMPOSE_FILE"
fi
cd "$TUNNEL_DIR"
docker build --build-arg UID=$(id -u armbian) --build-arg GID=$(id -g armbian) -t spoke-autossh .
echo "Image built. Use '$0 spoke start' to run."
}
spoke_start() {
cd "$TUNNEL_DIR"
docker-compose up -d
}
spoke_stop() {
cd "$TUNNEL_DIR"
docker-compose down
}
spoke_restart() {
cd "$TUNNEL_DIR"
docker-compose restart
}
spoke_status() {
docker ps --filter name=spoke-autossh --format "table {{.Names}}\t{{.Status}}"
}
spoke_logs() {
cd "$TUNNEL_DIR"
docker-compose logs --tail=50 -f
}
spoke_show_cmd() {
cat <<EOF
Manual autossh command (run on spoke):
autossh -M 0 -NT -o "ServerAliveInterval=60" -o "ServerAliveCountMax=3" \\
-R 11111:localhost:22 -i ~/.ssh/oilykey2026 armbian@oily.dad
EOF
}
# ------------------------------------------------------------
# Hub actions (rclone user service)
# ------------------------------------------------------------
hub_install() {
local SERVICE_DIR="$HOME/.config/systemd/user"
local SERVICE_FILE="$SERVICE_DIR/rclone-mount@.service"
local TEMPLATE_FILE="$(dirname "$0")/hub/rclone-mount@.service"
echo "Installing rclone mount systemd user service..."
# Create service directory if it doesn't exist
mkdir -p "$SERVICE_DIR"
# Check if template exists
if [ ! -f "$TEMPLATE_FILE" ]; then
die "Service template not found at $TEMPLATE_FILE"
fi
# Copy service template
cp "$TEMPLATE_FILE" "$SERVICE_FILE"
echo "Copied service template to $SERVICE_FILE"
# Check if user linger is enabled
if ! systemctl --user is-enabled --quiet user@$(id -u).service 2>/dev/null; then
echo "WARNING: User linger may not be enabled. Run: sudo loginctl enable-linger $USER"
echo "You may need to reboot for user services to start automatically."
fi
# Reload systemd
systemctl --user daemon-reload
echo "Systemd user daemon reloaded."
echo ""
echo "Next steps:"
echo "1. Ensure /etc/fuse.conf has 'user_allow_other' uncommented"
echo "2. Ensure you're in the 'fuse' group: sudo usermod -aG fuse $USER"
echo "3. Start the service: $0 hub start"
echo "4. Enable auto-start: systemctl --user enable rclone-mount@${RCLONE_REMOTE}.service"
}
hub_start() {
systemctl --user start "rclone-mount@${RCLONE_REMOTE}.service"
}
hub_stop() {
systemctl --user stop "rclone-mount@${RCLONE_REMOTE}.service"
}
hub_status() {
systemctl --user status "rclone-mount@${RCLONE_REMOTE}.service" --no-pager
}
hub_mount() {
mkdir -p "$MOUNT_POINT"
echo "Mounting in foreground. Press Ctrl+C to unmount."
rclone mount "${RCLONE_REMOTE}:" "$MOUNT_POINT" \
--config "${HOME}/.config/rclone/rclone.conf" \
--vfs-cache-mode writes \
--allow-other
}
hub_unmount() {
fusermount -u "$MOUNT_POINT" 2>/dev/null && echo "Unmounted." || echo "Not mounted."
}
# ------------------------------------------------------------
# Dispatch
# ------------------------------------------------------------
if [ $# -lt 2 ]; then
usage
exit 1
fi
ROLE="$1"
ACTION="$2"
case "$ROLE" in
spoke)
case "$ACTION" in
build) spoke_build ;;
start) spoke_start ;;
stop) spoke_stop ;;
restart) spoke_restart ;;
status) spoke_status ;;
logs) spoke_logs ;;
show-cmd) spoke_show_cmd ;;
*) die "Unknown action for spoke: $ACTION" ;;
esac
;;
hub)
case "$ACTION" in
install) hub_install ;;
start) hub_start ;;
stop) hub_stop ;;
status) hub_status ;;
mount) hub_mount ;;
unmount) hub_unmount ;;
*) die "Unknown action for hub: $ACTION" ;;
esac
;;
*)
usage
exit 1
;;
esac