| 1 | #!/usr/bin/env bash |
| 2 | set -euo pipefail |
| 3 | |
| 4 | : "${STUDIO_DEPLOY_HOST:?Set STUDIO_DEPLOY_HOST to the production target}" |
| 5 | source_host=${STUDIO_MIGRATION_SOURCE:-zenith} |
| 6 | target_host=$STUDIO_DEPLOY_HOST |
| 7 | target_port=${STUDIO_DEPLOY_PORT:-2222} |
| 8 | remote=(ssh -p "$target_port" "$target_host") |
| 9 | root=/srv/prod/qbittorrent |
| 10 | source_copy_host=$source_host |
| 11 | source_rsh=ssh |
| 12 | offline=false |
| 13 | if [[ -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 |
| 18 | fi |
| 19 | |
| 20 | if [[ $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; } |
| 23 | fi |
| 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 | |
| 28 | response=$("${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') |
| 29 | status=${response##*$'\n'} |
| 30 | if [[ $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; } |
| 33 | elif [[ $status != 404 ]]; then |
| 34 | echo "Could not verify qBittorrent job state on the home server: $status" >&2 |
| 35 | exit 1 |
| 36 | fi |
| 37 | dataset=$("${remote[@]}" "findmnt -n -o SOURCE --mountpoint $root") |
| 38 | [[ $dataset == */prod/qbittorrent ]] || { echo "Expected a mounted service dataset at $root" >&2; exit 1; } |
| 39 | |
| 40 | scratch=$(mktemp -d) |
| 41 | trap 'rm -rf "$scratch"' EXIT |
| 42 | mkdir "$scratch/qBittorrent" |
| 43 | rsync -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/" |
| 45 | resume_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 | |
| 48 | snapshot="$dataset@before-qbittorrent-import-$(date +%s)-$$" |
| 49 | "${remote[@]}" "zfs snapshot $snapshot" |
| 50 | rsync -a --delete -e "ssh -p $target_port" "$scratch/qBittorrent/" "$target_host:$root/config/qBittorrent/" |
| 51 | changes=$(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 | |
| 55 | uid=$("${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" |
| 57 | copied=$("${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; } |
| 59 | if [[ $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 | } |
| 64 | fi |
| 65 | "${remote[@]}" 'python3 /opt/studio/current/tools/studio.py deploy qbittorrent' |
| 66 | echo "Imported $resume_count saved torrents into $dataset; pre-import snapshot: $snapshot" |