Timothy S. Phan commited on
Commit
72cb211
Β·
1 Parent(s): 34d74a6

Label Studio with S3 backup and signup disabled

Browse files
Files changed (1) hide show
  1. entrypoint.sh +134 -0
entrypoint.sh ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # =============================================================================
3
+ # entrypoint.sh – Label Studio wrapper for Hugging Face Spaces
4
+ #
5
+ # What this script does:
6
+ # 1. On startup : restores SQLite DB + exports from S3
7
+ # 2. After restore: verifies DB integrity before starting Label Studio
8
+ # 3. Starts Label Studio in the background
9
+ # 4. Every 60 s : checkpoints WAL then syncs /data β†’ S3
10
+ # 5. On SIGTERM : does one final sync before the pod dies
11
+ #
12
+ # Required HF Space secrets:
13
+ # BACKUP_S3_BUCKET – bucket name (no s3:// prefix)
14
+ # LABEL_STUDIO_USERNAME – admin username / email
15
+ # LABEL_STUDIO_PASSWORD – admin password
16
+ #
17
+ # Optional HF Space secrets (with defaults shown):
18
+ # BACKUP_S3_PREFIX – labelstudio
19
+ # AWS_DEFAULT_REGION – us-east-1
20
+ # AWS_ENDPOINT_URL – https://s3.amazonaws.com (change for R2/MinIO)
21
+ # SYNC_INTERVAL_SECONDS – 60
22
+ # =============================================================================
23
+
24
+ set -euo pipefail
25
+
26
+ # ------------- configuration (all from HF secrets / env) -------------------
27
+ BUCKET="${BACKUP_S3_BUCKET:?'BACKUP_S3_BUCKET secret is required'}"
28
+ PREFIX="${BACKUP_S3_PREFIX:-labelstudio}"
29
+ REGION="${AWS_DEFAULT_REGION:-us-east-1}"
30
+ ENDPOINT="${AWS_ENDPOINT_URL:-https://s3.amazonaws.com}"
31
+ SYNC_INTERVAL="${SYNC_INTERVAL_SECONDS:-60}"
32
+
33
+ DATA_DIR=/data
34
+ # Label Studio uses label_studio.sqlite3 (not .db) β€” confirmed from live logs
35
+ DB_FILE="$DATA_DIR/label_studio.sqlite3"
36
+ S3_PATH="s3://${BUCKET}/${PREFIX}"
37
+
38
+ export AWS_DEFAULT_REGION="$REGION"
39
+
40
+ # ------------- helper: WAL checkpoint + S3 upload --------------------------
41
+ sync_to_s3() {
42
+ local reason="${1:-periodic}"
43
+ echo "[sync] Starting $reason sync to $S3_PATH/ ..."
44
+
45
+ # Checkpoint the WAL so S3 always receives a single self-consistent file.
46
+ # TRUNCATE: folds all WAL frames into the main DB file and zeros the WAL.
47
+ # This briefly blocks writers (not readers) β€” safe for ≀10 concurrent users.
48
+ if [ -f "$DB_FILE" ]; then
49
+ sqlite3 "$DB_FILE" "PRAGMA wal_checkpoint(TRUNCATE);" 2>/dev/null || \
50
+ echo "[sync] WAL checkpoint skipped (DB may be locked) – proceeding anyway"
51
+ fi
52
+
53
+ aws s3 sync "$DATA_DIR/" "$S3_PATH/" \
54
+ --endpoint-url "$ENDPOINT" \
55
+ --no-progress \
56
+ && echo "[sync] $reason sync complete" \
57
+ || echo "[sync] WARNING: $reason sync failed – will retry next cycle"
58
+ }
59
+
60
+ # ------------- graceful shutdown -------------------------------------------
61
+ shutdown_handler() {
62
+ echo ""
63
+ echo "[shutdown] SIGTERM received – uploading final snapshot before exit ..."
64
+ sync_to_s3 "shutdown"
65
+ echo "[shutdown] Stopping Label Studio (PID $LS_PID) ..."
66
+ kill "$LS_PID" 2>/dev/null || true
67
+ wait "$LS_PID" 2>/dev/null || true
68
+ echo "[shutdown] Done. Goodbye."
69
+ }
70
+
71
+ # ------------- 1. restore from S3 -----------------------------------------
72
+ echo "============================================================"
73
+ echo " Label Studio | HF Spaces + S3 backup | $(date -u)"
74
+ echo " Bucket : $S3_PATH/"
75
+ echo " Data dir: $DATA_DIR"
76
+ echo " Sync : every ${SYNC_INTERVAL}s"
77
+ echo "============================================================"
78
+
79
+ mkdir -p "$DATA_DIR"
80
+
81
+ echo "[restore] Syncing data DOWN from $S3_PATH/ ..."
82
+ # Use || true so a fresh bucket (nothing to restore) doesn't abort the script
83
+ aws s3 sync "$S3_PATH/" "$DATA_DIR/" \
84
+ --endpoint-url "$ENDPOINT" \
85
+ --no-progress \
86
+ && echo "[restore] Restore complete" \
87
+ || echo "[restore] Restore returned non-zero (may be first run) – continuing"
88
+
89
+ # ------------- 2. DB integrity check before starting LS -------------------
90
+ if [ -f "$DB_FILE" ]; then
91
+ echo "[integrity] Checking DB integrity ..."
92
+ INTEGRITY=$(sqlite3 "$DB_FILE" "PRAGMA integrity_check;" 2>&1 | head -1)
93
+ if [ "$INTEGRITY" != "ok" ]; then
94
+ echo "[integrity] ERROR: DB integrity check failed: $INTEGRITY"
95
+ echo "[integrity] The restored database is corrupt."
96
+ echo "[integrity] β†’ Check your S3 bucket for a healthy backup."
97
+ echo "[integrity] β†’ If you have bucket versioning enabled, restore an earlier version."
98
+ exit 1
99
+ fi
100
+ echo "[integrity] DB is healthy: $INTEGRITY"
101
+ else
102
+ echo "[integrity] No existing DB found – Label Studio will create a fresh one."
103
+ fi
104
+
105
+ # ------------- 3. ensure admin user exists (idempotent) -------------------
106
+ echo "[user] Ensuring admin user exists ..."
107
+ label-studio user create \
108
+ --username "${LABEL_STUDIO_USERNAME:?'LABEL_STUDIO_USERNAME secret is required'}" \
109
+ --password "${LABEL_STUDIO_PASSWORD:?'LABEL_STUDIO_PASSWORD secret is required'}" \
110
+ --email "${LABEL_STUDIO_USERNAME}@localhost" \
111
+ --preserve-case 2>/dev/null || true
112
+
113
+ # ------------- 4. start Label Studio in the background --------------------
114
+ # Use $SPACE_HOST exactly as the official HF Dockerfile does β€” this is what
115
+ # makes HF's reverse proxy route traffic correctly to the container.
116
+ echo "[start] Starting Label Studio ..."
117
+ label-studio --host="$SPACE_HOST" &
118
+ LS_PID=$!
119
+ echo "[start] Label Studio PID: $LS_PID"
120
+
121
+ # Register shutdown handler NOW (after we have LS_PID)
122
+ trap shutdown_handler EXIT INT TERM
123
+
124
+ # ------------- 5. periodic sync loop (runs forever) -----------------------
125
+ echo "[loop] Entering sync loop (every ${SYNC_INTERVAL}s) ..."
126
+ while true; do
127
+ sleep "$SYNC_INTERVAL"
128
+ # Verify LS is still alive before syncing
129
+ if ! kill -0 "$LS_PID" 2>/dev/null; then
130
+ echo "[loop] Label Studio process has exited unexpectedly – stopping container"
131
+ exit 1
132
+ fi
133
+ sync_to_s3 "periodic"
134
+ done