1#!/usr/bin/env bash
2set -euo pipefail
3
4: "${STUDIO_DEPLOY_HOST:?Set STUDIO_DEPLOY_HOST to the production target}"
5source_host=${STUDIO_MIGRATION_SOURCE:-zenith}
6target_host=$STUDIO_DEPLOY_HOST
7target_port=${STUDIO_DEPLOY_PORT:-2222}
8remote=(ssh -p "$target_port" "$target_host")
9root=/srv/prod/qbittorrent
10source_copy_host=$source_host
11source_rsh=ssh
12offline=false
13if [[ -n ${STUDIO_LEGACY_HANDOFF:-} ]]; then
14 sh "$(dirname "$0")/check-legacy-handoff.sh" "$STUDIO_LEGACY_HANDOFF" "$target_host" "$target_port"
15 source_copy_host=$target_host
16 source_rsh="ssh -p $target_port"
17 offline=true
18fi
19
20if [[ $offline == false ]]; then
21 source_running=$(ssh "$source_host" 'sudo -n docker inspect -f "{{.State.Running}}" qbittorrent')
22 [[ $source_running == false ]] || { echo 'Stop Zenith qBittorrent before importing its profile' >&2; exit 1; }
23fi
24"${remote[@]}" 'test "$(findmnt -n -o FSTYPE --mountpoint /srv/clover/Media)" = zfs; test -d /srv/clover/Media/Seedbox; test -L /srv/clover/Media/torrent; test /srv/clover/Media/Seedbox -ef /srv/clover/Media/torrent' || {
25 echo 'Mount Media and point torrent at Seedbox before importing qBittorrent' >&2; exit 1;
26}
27
28response=$("${remote[@]}" 'curl -s -w "\n%{http_code}" -H "X-Nomad-Token: $(cat /var/lib/studio/nomad.token)" http://127.0.0.1:4646/v1/job/qbittorrent')
29status=${response##*$'\n'}
30if [[ $status == 200 ]]; then
31 stopped=$(python3 -c 'import json,sys; print(str(json.load(sys.stdin)["Stop"]).lower())' <<<"${response%$'\n'*}")
32 [[ $stopped == true ]] || { echo 'Stop qBittorrent on the home server before importing its profile' >&2; exit 1; }
33elif [[ $status != 404 ]]; then
34 echo "Could not verify qBittorrent job state on the home server: $status" >&2
35 exit 1
36fi
37dataset=$("${remote[@]}" "findmnt -n -o SOURCE --mountpoint $root")
38[[ $dataset == */prod/qbittorrent ]] || { echo "Expected a mounted service dataset at $root" >&2; exit 1; }
39
40scratch=$(mktemp -d)
41trap 'rm -rf "$scratch"' EXIT
42mkdir "$scratch/qBittorrent"
43rsync -a --exclude=/config/ipc-socket --exclude=/config/lockfile --exclude=/data/logs/ --exclude=/cache/ \
44 -e "$source_rsh" "$source_copy_host:/mnt/storage1/apps/qbittorrent/qBittorrent/" "$scratch/qBittorrent/"
45resume_count=$(find "$scratch/qBittorrent/data/BT_backup" -maxdepth 1 -type f -name '*.fastresume' | wc -l | tr -d ' ')
46[[ $resume_count -gt 0 ]] || { echo 'No saved torrents copied from Zenith' >&2; exit 1; }
47
48snapshot="$dataset@before-qbittorrent-import-$(date +%s)-$$"
49"${remote[@]}" "zfs snapshot $snapshot"
50rsync -a --delete -e "ssh -p $target_port" "$scratch/qBittorrent/" "$target_host:$root/config/qBittorrent/"
51changes=$(rsync -a --checksum --dry-run --itemize-changes -e "ssh -p $target_port" \
52 "$scratch/qBittorrent/" "$target_host:$root/config/qBittorrent/")
53[[ -z $changes ]] || { echo 'Copied qBittorrent profile differs from source' >&2; exit 1; }
54
55uid=$("${remote[@]}" "python3 -c 'import json; print(json.load(open(\"/var/lib/studio/identities.json\"))[\"qbittorrent\"])'")
56"${remote[@]}" "chown -R $uid:$uid $root/config/qBittorrent && chmod -R u+rwX,go-rwx $root/config/qBittorrent && printf '{\"hostRoot\":\"$root\",\"uid\":$uid}' | python3 /opt/studio/current/service/qbittorrent/prepare.py"
57copied=$("${remote[@]}" "find $root/config/qBittorrent/data/BT_backup -maxdepth 1 -type f -name '*.fastresume' | wc -l")
58[[ $copied -eq $resume_count ]] || { echo 'Saved torrent count changed during import' >&2; exit 1; }
59if [[ $offline == false ]]; then
60 [[ $(ssh "$source_host" 'sudo -n docker inspect -f "{{.State.Running}}" qbittorrent') == false ]] || {
61 echo 'Zenith qBittorrent restarted during import; leave the home server stopped' >&2
62 exit 1
63 }
64fi
65"${remote[@]}" 'python3 /opt/studio/current/tools/studio.py deploy qbittorrent'
66echo "Imported $resume_count saved torrents into $dataset; pre-import snapshot: $snapshot"