feat: Tailscale resolution hardening, partnership offboard completion, Emby provisioning

common.sh:
- Add resolve_tailscale_ip() helper — tries `tailscale ip -4` first, falls back to
  parsing `tailscale status` output; handles hosts where MagicDNS short-name resolution
  is not active
- Add PARTNERSHIP_OWN_CONTAINERS alias in detect_hosts()
- Add aliasing for 4 Emby provisioning vars (PARTNERSHIP_PROVISION_EMBY_ADMIN,
  PARTNERSHIP_EMBY_ADMIN_USER, PARTNERSHIP_EMBY_ADMIN_PASS, PARTNERSHIP_EMBY_PORT)

Partnership/partnership_manager.sh:
- Replace 9 bare `tailscale ip -4` calls with resolve_tailscale_ip()
- Add read_remote_conf_var() and read_remote_conf_array() — SSH to mirror, source its
  own load_config.sh + detect_hosts(), return aliased variable; solves sparse-checkout
  problem where HOST1 cannot read master_host2.conf directly
- Add derive_short_name() — strips unraid- prefix, capitalises first char
- Add cleanup_partner_containers() — removes partner containers via FolderView3 folder
  if enabled, else falls back to FALLBACK_*_COVERS_*_TIER* arrays
- Add cleanup_owner_containers_on_mirror() — SSH to mirror, stops and removes containers
  matching *-${OWNER_SHORT} naming convention
- Add start_own_stack() and start_mirror_own_stack() — restart own containers locally
  or on mirror via SSH using PARTNERSHIP_OWN_CONTAINERS
- Add provision_emby_admin() — reads mirror credentials via read_remote_conf_var, checks
  for username collision, creates user + sets password + grants admin policy via Emby API
- Add revoke_emby_admin() — looks up mirror username on local Emby, deletes via REST API
- Wire offboard paths (both mirror-initiated and owner-initiated) to call container
  cleanup and stack restart; update --check finalisation paths accordingly
- Fix write_state_file in --onboard not gated on DRY_RUN (was writing ACTIVE state on
  dry runs)

master_host1.conf:
- Add HOST1_PARTNERSHIP_OWN_CONTAINERS array
- Add partnership Emby provisioning config (toggle + port + per-host credentials)

master_host2.conf:
- Add HOST2_PARTNERSHIP_OWN_CONTAINERS array
- Add HOST2_PARTNERSHIP_EMBY_ADMIN_USER and HOST2_PARTNERSHIP_EMBY_ADMIN_PASS

Tailscale fix applied to:
- Initial_run/ssh_setup.sh (2 callsites)
- unRAID_Essentials/rsync_stop.sh (1 callsite)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gmer4Lfe
2026-05-10 20:09:13 -04:00
co-authored by Claude Sonnet 4.6
parent 6948755c86
commit 0ae31b5fa6
33 changed files with 1241 additions and 155 deletions
+23 -25
View File
@@ -21,7 +21,7 @@
#
# ── HOST AWARENESS ────────────────────────────────────────────────────────────────────────────
# detect_hosts() sets MY_ID and aliases all HOST*_WATCHDOG_* arrays.
# Required containers, tier delays, and Tailscale checks use MY_ID/REMOTE_ID correctly.
# Required containers and tier delays use MY_ID/REMOTE_ID correctly.
#
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
# continuous_scripts_status.sh — show dashboard
@@ -121,7 +121,6 @@ echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " 🛡️ WATCHDOG STATUS — $(date '+%A, %B %-d at %-I:%M%p')"
echo " $ICON_HOST $MY_ID$LOCAL_SERVER_NAME"
echo " $ICON_HOST Remote: $REMOTE_ID$REMOTE_SERVER_NAME"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# ==============================================================================================
@@ -303,28 +302,43 @@ if command -v docker >/dev/null 2>&1; then
UNHEALTHY=$(timeout "$DOCKER_TIMEOUT" docker ps \
--filter health=unhealthy -q 2>/dev/null | wc -l)
# Stopped containers — filter intentionally ignored ones
STOPPED_FILTERED=()
# Stopped containers — bucket into clean vs unexpected, skip SCAN_IGNORE entirely
CLEAN_STOPPED=()
UNEXPECTED_STOPPED=()
while IFS= read -r name; do
[[ -z "$name" ]] && continue
SKIP=false
for ignore in "${WATCHDOG_SCAN_IGNORE[@]}"; do
[[ "$name" == "$ignore" ]] && SKIP=true && break
done
[[ "$SKIP" == false ]] && STOPPED_FILTERED+=("$name")
[[ "$SKIP" == true ]] && continue
exit_code=$(docker inspect --format '{{.State.ExitCode}}' "$name" 2>/dev/null)
if [[ "$exit_code" == "0" || "$exit_code" == "143" ]]; then
CLEAN_STOPPED+=("$name")
else
UNEXPECTED_STOPPED+=("$name")
fi
done < <(timeout "$DOCKER_TIMEOUT" docker ps -af "status=exited" \
--format "{{.Names}}" 2>/dev/null)
STOPPED_COUNT="${#STOPPED_FILTERED[@]}"
echo " Running: $RUNNING / $TOTAL total"
[[ "$UNHEALTHY" -gt 0 ]] && echo " ⚠️ Unhealthy: $UNHEALTHY"
if [[ "$STOPPED_COUNT" -gt 0 ]]; then
if [[ "${#UNEXPECTED_STOPPED[@]}" -gt 0 ]]; then
echo " ⚠️ Stopped (unexpected):"
for name in "${STOPPED_FILTERED[@]}"; do
for name in "${UNEXPECTED_STOPPED[@]}"; do
echo "$name"
done
else
fi
if [[ "${#CLEAN_STOPPED[@]}" -gt 0 ]]; then
echo " ⏸️ Stopped (clean):"
for name in "${CLEAN_STOPPED[@]}"; do
echo "$name"
done
fi
if [[ "${#UNEXPECTED_STOPPED[@]}" -eq 0 && "${#CLEAN_STOPPED[@]}" -eq 0 ]]; then
echo " ✅ All containers running"
fi
@@ -458,22 +472,6 @@ case "$FALLBACK_STATE" in
;;
esac
# Tailscale remote visibility — uses REMOTE_SERVER_NAME from detect_hosts()
echo ""
if command -v tailscale >/dev/null 2>&1; then
REMOTE_IP=$(tailscale ip -4 "${REMOTE_SERVER_NAME,,}" 2>/dev/null)
if [[ -n "$REMOTE_IP" ]]; then
if ping -c 1 -W 2 "$REMOTE_IP" >/dev/null 2>&1; then
echo " 🌐 $REMOTE_ID ($REMOTE_SERVER_NAME): $REMOTE_IP — reachable ✅"
else
echo " 🌐 $REMOTE_ID ($REMOTE_SERVER_NAME): $REMOTE_IP — not responding ⚠️"
fi
else
echo " 🌐 $REMOTE_ID ($REMOTE_SERVER_NAME) not visible on Tailscale ❌"
fi
else
echo " 🌐 Tailscale: not available"
fi
echo " 📡 Check interval: ${FALLBACK_CHECK_INTERVAL}s │ Handback strikes: ${FALLBACK_HANDBACK_STRIKES}"
+2 -2
View File
@@ -174,7 +174,7 @@ fi
# ── Container Watchdog Strikes ────────────────────────────────────────────────────────────────
if [[ -f "$WATCHDOG_STATE_FILE" ]]; then
ACTIVE_STRIKES=$(grep -v ":0$" "$WATCHDOG_STATE_FILE" 2>/dev/null | grep -c "." || echo 0)
ACTIVE_STRIKES=$(grep -v ":0$" "$WATCHDOG_STATE_FILE" 2>/dev/null | grep -c ".")
if [[ "$ACTIVE_STRIKES" -gt 0 ]]; then
STRIKE_LIST=$(grep -v ":0$" "$WATCHDOG_STATE_FILE" 2>/dev/null | tr '\n' ' ')
DIGEST_LINES+=("$ICON_WATCHDOG Container strikes: $ACTIVE_STRIKES active — $STRIKE_LIST")
@@ -187,7 +187,7 @@ fi
# ── System Watchdog Strikes ───────────────────────────────────────────────────────────────────
if [[ -f "$SYS_WATCHDOG_STATE_FILE" ]]; then
SYS_STRIKES=$(grep -v ":0$" "$SYS_WATCHDOG_STATE_FILE" 2>/dev/null | grep -c "." || echo 0)
SYS_STRIKES=$(grep -v ":0$" "$SYS_WATCHDOG_STATE_FILE" 2>/dev/null | grep -c ".")
if [[ "$SYS_STRIKES" -gt 0 ]]; then
SYS_STRIKE_LIST=$(grep -v ":0$" "$SYS_WATCHDOG_STATE_FILE" 2>/dev/null | tr '\n' ' ')
DIGEST_LINES+=("$ICON_REBOOT_SMART System strikes: $SYS_STRIKES active — $SYS_STRIKE_LIST")