mongodb-migrate 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876
  1. #!/bin/bash
  2. # MongoDB Migration Script from version 3 to 7
  3. # This script handles migration with disk space checks, progress tracking, and error handling
  4. #
  5. # IMPORTANT: Migration is only triggered when MIGRATE_MONGODB=true environment variable is set
  6. # IMPORTANT: All operations are contained within SNAP_COMMON directory
  7. # IMPORTANT: No snap settings or channel changes are made during migration
  8. # This is the only writable directory in a snap environment
  9. set -e
  10. # Source settings
  11. source $SNAP/bin/wekan-read-settings
  12. # Migration configuration
  13. MIGRATION_LOG="${SNAP_COMMON}/mongodb-migration-log.txt"
  14. MIGRATION_STATUS="${SNAP_COMMON}/mongodb-migration-status.json"
  15. MIGRATION_PROGRESS="${SNAP_COMMON}/mongodb-migration-progress.html"
  16. REVERT_FILE="${SNAP_COMMON}/revert-mongodb-migration.txt"
  17. TEMP_DIR="${SNAP_COMMON}/mongodb-migration-temp"
  18. BACKUP_DIR="${SNAP_COMMON}/mongodb-backup-$(date +%Y%m%d-%H%M%S)"
  19. # MongoDB paths
  20. MONGO3_BIN="/snap/${SNAP_NAME}/current/migratemongo/bin"
  21. MONGO7_BIN="/snap/${SNAP_NAME}/current/bin"
  22. MONGO3_LIB="/snap/${SNAP_NAME}/current/migratemongo/lib"
  23. MONGO7_LIB="/snap/${SNAP_NAME}/current/usr/lib"
  24. # Set up environment for MongoDB 3 tools
  25. export LD_LIBRARY_PATH="${MONGO3_LIB}:${MONGO3_LIB}/x86_64-linux-gnu:${LD_LIBRARY_PATH}"
  26. export PATH="${MONGO3_BIN}:${MONGO7_BIN}:${PATH}"
  27. # Set MongoDB log destination to snapcommon for log file detection
  28. export MONGO_LOG_DESTINATION="snapcommon"
  29. # Validate that all operations are within SNAP_COMMON
  30. validate_snap_common_path() {
  31. local path="$1"
  32. local description="$2"
  33. if [[ "$path" != "${SNAP_COMMON}"* ]]; then
  34. log_error "Path outside SNAP_COMMON detected: $path ($description)"
  35. log_error "SNAP_COMMON: $SNAP_COMMON"
  36. return 1
  37. fi
  38. return 0
  39. }
  40. # Validate all critical paths
  41. validate_all_paths() {
  42. log_message "Validating all paths are within SNAP_COMMON"
  43. validate_snap_common_path "$MIGRATION_LOG" "Migration log" || return 1
  44. validate_snap_common_path "$MIGRATION_STATUS" "Migration status" || return 1
  45. validate_snap_common_path "$MIGRATION_PROGRESS" "Migration progress" || return 1
  46. validate_snap_common_path "$REVERT_FILE" "Revert file" || return 1
  47. validate_snap_common_path "$TEMP_DIR" "Temporary directory" || return 1
  48. validate_snap_common_path "$BACKUP_DIR" "Backup directory" || return 1
  49. log_success "All paths validated within SNAP_COMMON"
  50. return 0
  51. }
  52. # Logging functions
  53. log_message() {
  54. local message="$1"
  55. local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
  56. echo "[$timestamp] $message" | tee -a "$MIGRATION_LOG"
  57. }
  58. log_error() {
  59. local message="$1"
  60. log_message "ERROR: $message"
  61. }
  62. log_success() {
  63. local message="$1"
  64. log_message "SUCCESS: $message"
  65. }
  66. log_warning() {
  67. local message="$1"
  68. log_message "WARNING: $message"
  69. }
  70. # Disk space checking functions
  71. check_disk_space() {
  72. local required_space_gb="$1"
  73. local available_space_gb=$(df "$SNAP_COMMON" | awk 'NR==2 {print int($4/1024/1024)}')
  74. if [ "$available_space_gb" -lt "$required_space_gb" ]; then
  75. log_error "Insufficient disk space. Required: ${required_space_gb}GB, Available: ${available_space_gb}GB"
  76. return 1
  77. fi
  78. log_message "Disk space check passed. Available: ${available_space_gb}GB, Required: ${required_space_gb}GB"
  79. return 0
  80. }
  81. # Progress tracking functions
  82. update_progress() {
  83. local step="$1"
  84. local total_steps="$2"
  85. local description="$3"
  86. local percentage=$((step * 100 / total_steps))
  87. # Update JSON status file
  88. cat > "$MIGRATION_STATUS" << EOF
  89. {
  90. "step": $step,
  91. "total_steps": $total_steps,
  92. "percentage": $percentage,
  93. "description": "$description",
  94. "timestamp": "$(date -Iseconds)",
  95. "status": "running"
  96. }
  97. EOF
  98. # Update HTML progress page
  99. cat > "$MIGRATION_PROGRESS" << EOF
  100. <!DOCTYPE html>
  101. <html>
  102. <head>
  103. <title>MongoDB Migration Progress</title>
  104. <meta http-equiv="refresh" content="5">
  105. <style>
  106. body { font-family: Arial, sans-serif; margin: 40px; }
  107. .progress-bar { width: 100%; background-color: #f0f0f0; border-radius: 5px; }
  108. .progress-fill { height: 30px; background-color: #4CAF50; border-radius: 5px; width: ${percentage}%; }
  109. .status { margin: 20px 0; }
  110. .error { color: red; }
  111. .success { color: green; }
  112. </style>
  113. </head>
  114. <body>
  115. <h1>MongoDB Migration Progress</h1>
  116. <div class="progress-bar">
  117. <div class="progress-fill"></div>
  118. </div>
  119. <div class="status">
  120. <p><strong>Progress:</strong> $step of $total_steps steps ($percentage%)</p>
  121. <p><strong>Current Step:</strong> $description</p>
  122. <p><strong>Last Updated:</strong> $(date)</p>
  123. </div>
  124. <p><em>This page will refresh automatically every 5 seconds.</em></p>
  125. </body>
  126. </html>
  127. EOF
  128. log_message "Progress: $step/$total_steps ($percentage%) - $description"
  129. }
  130. # Estimate completion time
  131. estimate_completion_time() {
  132. local start_time="$1"
  133. local current_step="$2"
  134. local total_steps="$3"
  135. if [ "$current_step" -gt 0 ]; then
  136. local elapsed=$(($(date +%s) - start_time))
  137. local avg_time_per_step=$((elapsed / current_step))
  138. local remaining_steps=$((total_steps - current_step))
  139. local estimated_remaining=$((remaining_steps * avg_time_per_step))
  140. local hours=$((estimated_remaining / 3600))
  141. local minutes=$(((estimated_remaining % 3600) / 60))
  142. local seconds=$((estimated_remaining % 60))
  143. echo "${hours}h ${minutes}m ${seconds}s"
  144. else
  145. echo "Calculating..."
  146. fi
  147. }
  148. # Create backup before migration
  149. create_backup() {
  150. log_message "Creating backup of MongoDB 3 database"
  151. # Check disk space for backup (estimate 2x current database size)
  152. local db_size=$(du -s "${SNAP_COMMON}/wekan" 2>/dev/null | awk '{print $1}' || echo "0")
  153. local required_space=$((db_size * 2 / 1024 / 1024 + 1)) # Convert to GB and add 1GB buffer
  154. if ! check_disk_space "$required_space"; then
  155. log_error "Insufficient disk space for backup"
  156. return 1
  157. fi
  158. # Create backup directory
  159. mkdir -p "$BACKUP_DIR"
  160. # Copy database files
  161. if [ -d "${SNAP_COMMON}/wekan" ]; then
  162. cp -r "${SNAP_COMMON}/wekan" "$BACKUP_DIR/"
  163. log_success "Database backup created at $BACKUP_DIR"
  164. return 0
  165. else
  166. log_error "No database found to backup"
  167. return 1
  168. fi
  169. }
  170. # Check if migration is needed
  171. check_migration_needed() {
  172. if [ -f "$MIGRATION_STATUS" ]; then
  173. local status=$(jq -r '.status' "$MIGRATION_STATUS" 2>/dev/null || echo "unknown")
  174. if [ "$status" = "completed" ]; then
  175. log_message "Migration already completed"
  176. return 1
  177. elif [ "$status" = "running" ]; then
  178. log_message "Migration already in progress"
  179. return 0
  180. fi
  181. fi
  182. # Check if we have MongoDB 3 data (either in wekan directory or raw database files)
  183. if [ -d "${SNAP_COMMON}/wekan" ] && [ ! -f "${SNAP_COMMON}/mongodb-version-7" ]; then
  184. log_message "MongoDB 3 data detected in wekan directory"
  185. return 0
  186. fi
  187. # Check for MongoDB upgrade needed by examining log file
  188. if detect_mongodb_upgrade_needed; then
  189. log_message "MongoDB upgrade needed detected from log file"
  190. return 0
  191. fi
  192. log_message "No migration needed"
  193. return 1
  194. }
  195. # Display MongoDB log content for debugging
  196. display_mongodb_log_content() {
  197. local mongodb_log="${SNAP_COMMON}/mongodb.log"
  198. if [ ! -f "$mongodb_log" ]; then
  199. log_message "MongoDB log file not found: $mongodb_log"
  200. return 1
  201. fi
  202. log_message "MongoDB log file content (last 50 lines):"
  203. if [ -r "$mongodb_log" ]; then
  204. tail -50 "$mongodb_log" | while read -r line; do
  205. log_message "LOG: $line"
  206. done
  207. else
  208. log_message "MongoDB log file not readable, trying with sudo"
  209. sudo tail -50 "$mongodb_log" 2>/dev/null | while read -r line; do
  210. log_message "LOG: $line"
  211. done
  212. fi
  213. }
  214. # Detect if MongoDB upgrade is needed by checking log file
  215. detect_mongodb_upgrade_needed() {
  216. local mongodb_log="${SNAP_COMMON}/mongodb.log"
  217. # Check if MongoDB log file exists
  218. if [ ! -f "$mongodb_log" ]; then
  219. log_message "MongoDB log file not found: $mongodb_log"
  220. return 1
  221. fi
  222. # Display log content for debugging
  223. display_mongodb_log_content
  224. # Check file permissions and try to read with appropriate method
  225. if [ ! -r "$mongodb_log" ]; then
  226. log_message "MongoDB log file not readable, trying with sudo"
  227. # Try to read with sudo if not readable
  228. if ! sudo grep -q "too recent to start up on the existing data files" "$mongodb_log" 2>/dev/null; then
  229. log_message "No MongoDB upgrade needed detected in log file (via sudo)"
  230. return 1
  231. fi
  232. else
  233. # Check for various error messages that indicate upgrade is needed
  234. # The exact message may vary between MongoDB versions
  235. local upgrade_patterns=(
  236. "This version of MongoDB is too recent to start up on the existing data files"
  237. "too recent to start up on the existing data files"
  238. "Try MongoDB 4.2 or earlier"
  239. "unsupported format version"
  240. "data files are incompatible"
  241. "database files are incompatible"
  242. "version too new"
  243. "version too recent"
  244. )
  245. local found_upgrade_needed=false
  246. for pattern in "${upgrade_patterns[@]}"; do
  247. if grep -q "$pattern" "$mongodb_log" 2>/dev/null; then
  248. log_message "Found upgrade pattern in log: '$pattern'"
  249. found_upgrade_needed=true
  250. break
  251. fi
  252. done
  253. if [ "$found_upgrade_needed" = false ]; then
  254. log_message "No MongoDB upgrade needed detected in log file"
  255. return 1
  256. fi
  257. fi
  258. log_message "MongoDB upgrade needed detected in log file"
  259. return 0
  260. }
  261. # Log rotation function for MongoDB logs
  262. rotate_mongodb_logs() {
  263. local mongodb_log="${SNAP_COMMON}/mongodb.log"
  264. local max_size_mb=100
  265. local keep_copies=10
  266. # Check if log file exists and is large enough to rotate
  267. if [ ! -f "$mongodb_log" ]; then
  268. log_message "MongoDB log file not found, skipping rotation"
  269. return 0
  270. fi
  271. # Get log file size in MB
  272. local log_size_mb=$(du -m "$mongodb_log" | cut -f1)
  273. if [ "$log_size_mb" -lt "$max_size_mb" ]; then
  274. log_message "MongoDB log size (${log_size_mb}MB) is below rotation threshold (${max_size_mb}MB)"
  275. return 0
  276. fi
  277. log_message "Rotating MongoDB log file (size: ${log_size_mb}MB)"
  278. # Create rotated log file with timestamp
  279. local timestamp=$(date +%Y%m%d-%H%M%S)
  280. local rotated_log="${mongodb_log}.${timestamp}"
  281. # Copy current log to rotated file
  282. if cp "$mongodb_log" "$rotated_log"; then
  283. log_message "Created rotated log file: $rotated_log"
  284. # Truncate original log file
  285. if > "$mongodb_log"; then
  286. log_message "Truncated original log file"
  287. else
  288. log_error "Failed to truncate original log file"
  289. return 1
  290. fi
  291. # Compress rotated log file
  292. if gzip "$rotated_log"; then
  293. log_message "Compressed rotated log file: ${rotated_log}.gz"
  294. else
  295. log_warning "Failed to compress rotated log file"
  296. fi
  297. # Clean up old rotated logs (keep only specified number)
  298. local old_logs=$(ls -t "${mongodb_log}".* 2>/dev/null | tail -n +$((keep_copies + 1)))
  299. if [ -n "$old_logs" ]; then
  300. echo "$old_logs" | xargs rm -f
  301. log_message "Cleaned up old rotated log files"
  302. fi
  303. log_success "MongoDB log rotation completed successfully"
  304. return 0
  305. else
  306. log_error "Failed to create rotated log file"
  307. return 1
  308. fi
  309. }
  310. # Enhanced log rotation function for migration logs
  311. rotate_migration_logs() {
  312. local migration_log="${SNAP_COMMON}/mongodb-migration-log.txt"
  313. local max_size_mb=50
  314. local keep_copies=5
  315. # Check if migration log file exists and is large enough to rotate
  316. if [ ! -f "$migration_log" ]; then
  317. log_message "Migration log file not found, skipping rotation"
  318. return 0
  319. fi
  320. # Get log file size in MB
  321. local log_size_mb=$(du -m "$migration_log" | cut -f1)
  322. if [ "$log_size_mb" -lt "$max_size_mb" ]; then
  323. log_message "Migration log size (${log_size_mb}MB) is below rotation threshold (${max_size_mb}MB)"
  324. return 0
  325. fi
  326. log_message "Rotating migration log file (size: ${log_size_mb}MB)"
  327. # Create rotated log file with timestamp
  328. local timestamp=$(date +%Y%m%d-%H%M%S)
  329. local rotated_log="${migration_log}.${timestamp}"
  330. # Copy current log to rotated file
  331. if cp "$migration_log" "$rotated_log"; then
  332. log_message "Created rotated migration log file: $rotated_log"
  333. # Truncate original log file
  334. if > "$migration_log"; then
  335. log_message "Truncated original migration log file"
  336. else
  337. log_error "Failed to truncate original migration log file"
  338. return 1
  339. fi
  340. # Compress rotated log file
  341. if gzip "$rotated_log"; then
  342. log_message "Compressed rotated migration log file: ${rotated_log}.gz"
  343. else
  344. log_warning "Failed to compress rotated migration log file"
  345. fi
  346. # Clean up old rotated logs (keep only specified number)
  347. local old_logs=$(ls -t "${migration_log}".* 2>/dev/null | tail -n +$((keep_copies + 1)))
  348. if [ -n "$old_logs" ]; then
  349. echo "$old_logs" | xargs rm -f
  350. log_message "Cleaned up old rotated migration log files"
  351. fi
  352. log_success "Migration log rotation completed successfully"
  353. return 0
  354. else
  355. log_error "Failed to create rotated migration log file"
  356. return 1
  357. fi
  358. }
  359. # Reset MONGO_LOG_DESTINATION to devnull after successful migration - DISABLED
  360. # Snap settings changes are permanently disabled during migration
  361. reset_mongo_log_destination() {
  362. log_message "MONGO_LOG_DESTINATION reset disabled - snap settings changes not allowed during migration"
  363. # Snap settings changes permanently disabled during migration
  364. # This ensures no snap configuration is modified during the migration process
  365. # if snap set wekan mongo-log-destination="devnull" 2>/dev/null; then
  366. # log_success "MONGO_LOG_DESTINATION reset to devnull successfully"
  367. # else
  368. # log_error "Failed to reset MONGO_LOG_DESTINATION to devnull"
  369. # # Don't fail the migration for this setting issue
  370. # fi
  371. }
  372. # Migrate raw MongoDB 3 database files
  373. migrate_raw_database_files() {
  374. log_message "Starting raw MongoDB 3 database files migration"
  375. # Validate paths are within SNAP_COMMON
  376. if ! validate_snap_common_path "${SNAP_COMMON}" "Database path"; then
  377. log_error "Database path validation failed"
  378. return 1
  379. fi
  380. # Stop any running MongoDB processes
  381. log_message "Stopping any running MongoDB processes"
  382. pkill -f mongod || true
  383. sleep 3
  384. # Start MongoDB 3 with raw files
  385. log_message "Starting MongoDB 3 with raw database files"
  386. local mongo3_pid
  387. mongod --dbpath "${SNAP_COMMON}" --port "${MONGODB_PORT:-27019}" --quiet &
  388. mongo3_pid=$!
  389. # Wait for MongoDB 3 to start
  390. local retry_count=0
  391. while [ $retry_count -lt 30 ]; do
  392. if mongosh --quiet --eval "db.adminCommand('ping')" "mongodb://localhost:${MONGODB_PORT:-27019}/admin" >/dev/null 2>&1; then
  393. log_message "MongoDB 3 started successfully"
  394. break
  395. fi
  396. sleep 1
  397. retry_count=$((retry_count + 1))
  398. done
  399. if [ $retry_count -eq 30 ]; then
  400. log_error "MongoDB 3 failed to start"
  401. kill $mongo3_pid 2>/dev/null || true
  402. return 1
  403. fi
  404. # Dump all databases from MongoDB 3
  405. log_message "Dumping databases from MongoDB 3"
  406. if ! mongodump --port "${MONGODB_PORT:-27019}" --out "$TEMP_DIR" --dbpath "${SNAP_COMMON}"; then
  407. log_error "Failed to dump databases from MongoDB 3"
  408. kill $mongo3_pid 2>/dev/null || true
  409. return 1
  410. fi
  411. # Stop MongoDB 3
  412. log_message "Stopping MongoDB 3"
  413. kill $mongo3_pid 2>/dev/null || true
  414. sleep 3
  415. # Start MongoDB 7
  416. log_message "Starting MongoDB 7"
  417. local mongo7_pid
  418. mongod --dbpath "${SNAP_COMMON}" --port "${MONGODB_PORT:-27019}" --quiet &
  419. mongo7_pid=$!
  420. # Wait for MongoDB 7 to start
  421. retry_count=0
  422. while [ $retry_count -lt 30 ]; do
  423. if mongosh --quiet --eval "db.adminCommand('ping')" "mongodb://localhost:${MONGODB_PORT:-27019}/admin" >/dev/null 2>&1; then
  424. log_message "MongoDB 7 started successfully"
  425. break
  426. fi
  427. sleep 1
  428. retry_count=$((retry_count + 1))
  429. done
  430. if [ $retry_count -eq 30 ]; then
  431. log_error "MongoDB 7 failed to start"
  432. kill $mongo7_pid 2>/dev/null || true
  433. return 1
  434. fi
  435. # Restore databases to MongoDB 7
  436. log_message "Restoring databases to MongoDB 7"
  437. if ! mongorestore --port "${MONGODB_PORT:-27019}" --dbpath "${SNAP_COMMON}" "$TEMP_DIR"; then
  438. log_error "Failed to restore databases to MongoDB 7"
  439. kill $mongo7_pid 2>/dev/null || true
  440. return 1
  441. fi
  442. # Stop MongoDB 7
  443. log_message "Stopping MongoDB 7"
  444. kill $mongo7_pid 2>/dev/null || true
  445. sleep 3
  446. # Clean up old MongoDB 3 files
  447. log_message "Cleaning up old MongoDB 3 files"
  448. find "${SNAP_COMMON}" -maxdepth 1 -name "*.0" -o -name "*.1" -o -name "*.ns" -o -name "j._*" -o -name "mongod.lock" | while read -r file; do
  449. if [ -f "$file" ]; then
  450. rm -f "$file"
  451. log_message "Removed old file: $file"
  452. fi
  453. done
  454. # Remove journal directory if it exists
  455. if [ -d "${SNAP_COMMON}/journal" ]; then
  456. rm -rf "${SNAP_COMMON}/journal"
  457. log_message "Removed journal directory"
  458. fi
  459. log_success "Raw database files migration completed successfully"
  460. # Reset MONGO_LOG_DESTINATION to devnull after successful migration
  461. reset_mongo_log_destination
  462. return 0
  463. }
  464. # Snap channel detection and switching functions
  465. get_current_snap_channel() {
  466. local snap_name="$1"
  467. snap list "$snap_name" 2>/dev/null | awk 'NR==2 {print $4}' || echo "unknown"
  468. }
  469. is_wekan_snap() {
  470. local snap_name="$1"
  471. case "$snap_name" in
  472. wekan|wekan-gantt-gpl|wekan-ondra)
  473. return 0
  474. ;;
  475. *)
  476. return 1
  477. ;;
  478. esac
  479. }
  480. switch_to_stable_channel() {
  481. local snap_name="$1"
  482. local current_channel=$(get_current_snap_channel "$snap_name")
  483. log_message "Snap channel switching disabled - not changing $snap_name channel (currently: $current_channel)"
  484. log_message "Snap channel changes are permanently disabled during migration"
  485. return 0
  486. # Snap channel switching permanently disabled during migration
  487. # This ensures no snap channels are modified during the migration process
  488. # if [ "$current_channel" != "stable" ] && [ "$current_channel" != "unknown" ]; then
  489. # log_message "Switching $snap_name from $current_channel to stable channel"
  490. # if snap refresh "$snap_name" --channel=stable; then
  491. # log_success "Successfully switched $snap_name to stable channel"
  492. # return 0
  493. # else
  494. # log_error "Failed to switch $snap_name to stable channel"
  495. # return 1
  496. # fi
  497. # else
  498. # log_message "$snap_name is already on stable channel or not installed"
  499. # return 0
  500. # fi
  501. }
  502. switch_all_wekan_snaps_to_stable() {
  503. log_message "Checking for Wekan-related snaps to switch to stable channel"
  504. local wekan_snaps=("wekan" "wekan-gantt-gpl" "wekan-ondra")
  505. local switched_count=0
  506. local failed_count=0
  507. for snap_name in "${wekan_snaps[@]}"; do
  508. if snap list "$snap_name" >/dev/null 2>&1; then
  509. if switch_to_stable_channel "$snap_name"; then
  510. switched_count=$((switched_count + 1))
  511. else
  512. failed_count=$((failed_count + 1))
  513. fi
  514. fi
  515. done
  516. log_message "Channel switching completed: $switched_count successful, $failed_count failed"
  517. if [ "$failed_count" -gt 0 ]; then
  518. return 1
  519. else
  520. return 0
  521. fi
  522. }
  523. # Get database collections
  524. get_collections() {
  525. local mongo_url="$1"
  526. local collections=$(mongosh --quiet --eval "db.getCollectionNames().join('\n')" "$mongo_url" 2>/dev/null | grep -v "^$" || echo "")
  527. echo "$collections"
  528. }
  529. # Migrate a single collection
  530. migrate_collection() {
  531. local collection="$1"
  532. local mongo3_url="$2"
  533. local mongo7_url="$3"
  534. local step="$4"
  535. local total_steps="$5"
  536. log_message "Migrating collection: $collection"
  537. # Check disk space before each collection (estimate 2x collection size)
  538. local collection_size=$(mongosh --quiet --eval "db.$collection.stats().size" "$mongo3_url" 2>/dev/null || echo "0")
  539. local required_space=$((collection_size * 2 / 1024 / 1024 / 1024 + 1)) # Convert to GB and add 1GB buffer
  540. if ! check_disk_space "$required_space"; then
  541. log_error "Insufficient disk space for collection $collection"
  542. return 1
  543. fi
  544. # Dump collection
  545. local dump_file="${TEMP_DIR}/${collection}.bson"
  546. log_message "Dumping collection $collection to $dump_file"
  547. if ! mongodump --db wekan --collection "$collection" --out "$TEMP_DIR" --port "${MONGODB_PORT:-27019}" --dbpath "${SNAP_COMMON}"; then
  548. log_error "Failed to dump collection $collection"
  549. return 1
  550. fi
  551. # Restore collection
  552. log_message "Restoring collection $collection to MongoDB 7"
  553. if ! mongorestore --db wekan --collection "$collection" "$dump_file" --port "${MONGODB_PORT:-27019}" --dbpath "${SNAP_COMMON}"; then
  554. log_error "Failed to restore collection $collection"
  555. return 1
  556. fi
  557. # Update progress
  558. update_progress "$step" "$total_steps" "Migrated collection: $collection"
  559. # Clean up dump file
  560. rm -f "$dump_file"
  561. log_success "Collection $collection migrated successfully"
  562. return 0
  563. }
  564. # Main migration function
  565. perform_migration() {
  566. local start_time=$(date +%s)
  567. log_message "Starting MongoDB migration from version 3 to 7"
  568. # Rotate MongoDB logs before migration if needed
  569. log_message "Checking if MongoDB log rotation is needed"
  570. if ! rotate_mongodb_logs; then
  571. log_warning "MongoDB log rotation failed, continuing with migration"
  572. fi
  573. # Rotate migration logs before migration if needed
  574. log_message "Checking if migration log rotation is needed"
  575. if ! rotate_migration_logs; then
  576. log_warning "Migration log rotation failed, continuing with migration"
  577. fi
  578. # Create backup before migration
  579. log_message "Creating backup before migration"
  580. if ! create_backup; then
  581. log_error "Failed to create backup, aborting migration"
  582. return 1
  583. fi
  584. # Create temporary directory
  585. mkdir -p "$TEMP_DIR"
  586. # Check if we need to migrate raw database files
  587. if detect_mongodb_upgrade_needed; then
  588. log_message "MongoDB upgrade needed detected, starting raw file migration"
  589. if ! migrate_raw_database_files; then
  590. log_error "Failed to migrate raw database files"
  591. return 1
  592. fi
  593. fi
  594. # Get MongoDB connection details
  595. local mongo3_url="mongodb://localhost:${MONGODB_PORT:-27019}/wekan"
  596. local mongo7_url="mongodb://localhost:${MONGODB_PORT:-27019}/wekan"
  597. # Get collections to migrate
  598. log_message "Getting list of collections to migrate"
  599. local collections=$(get_collections "$mongo3_url")
  600. if [ -z "$collections" ]; then
  601. log_error "No collections found to migrate"
  602. return 1
  603. fi
  604. local collection_count=$(echo "$collections" | wc -l)
  605. log_message "Found $collection_count collections to migrate"
  606. # Migrate each collection
  607. local current_step=0
  608. for collection in $collections; do
  609. current_step=$((current_step + 1))
  610. if ! migrate_collection "$collection" "$mongo3_url" "$mongo7_url" "$current_step" "$collection_count"; then
  611. log_error "Migration failed at collection $collection"
  612. return 1
  613. fi
  614. # Update completion time estimate
  615. local estimated_time=$(estimate_completion_time "$start_time" "$current_step" "$collection_count")
  616. log_message "Estimated completion time: $estimated_time"
  617. done
  618. # Mark migration as completed
  619. cat > "$MIGRATION_STATUS" << EOF
  620. {
  621. "step": $collection_count,
  622. "total_steps": $collection_count,
  623. "percentage": 100,
  624. "description": "Migration completed successfully",
  625. "timestamp": "$(date -Iseconds)",
  626. "status": "completed"
  627. }
  628. EOF
  629. # Create MongoDB 7 version marker
  630. touch "${SNAP_COMMON}/mongodb-version-7"
  631. # Clean up temporary files
  632. rm -rf "$TEMP_DIR"
  633. # Switch Wekan snaps to stable channel after successful migration
  634. log_message "Switching Wekan snaps to stable channel after successful migration"
  635. if switch_all_wekan_snaps_to_stable; then
  636. log_success "All Wekan snaps switched to stable channel successfully"
  637. else
  638. log_error "Some Wekan snaps failed to switch to stable channel"
  639. # Don't fail the migration for channel switching issues
  640. fi
  641. log_success "MongoDB migration completed successfully"
  642. # Rotate MongoDB logs after successful migration
  643. log_message "Rotating MongoDB logs after successful migration"
  644. if ! rotate_mongodb_logs; then
  645. log_warning "MongoDB log rotation after migration failed"
  646. fi
  647. # Rotate migration logs after successful migration
  648. log_message "Rotating migration logs after successful migration"
  649. if ! rotate_migration_logs; then
  650. log_warning "Migration log rotation after migration failed"
  651. fi
  652. # Reset MONGO_LOG_DESTINATION to devnull after successful migration
  653. reset_mongo_log_destination
  654. return 0
  655. }
  656. # Revert migration
  657. revert_migration() {
  658. log_message "Reverting MongoDB migration"
  659. if [ ! -f "$REVERT_FILE" ]; then
  660. log_error "Revert file not found: $REVERT_FILE"
  661. return 1
  662. fi
  663. # Stop MongoDB 7 - DISABLED
  664. log_message "MongoDB stopping disabled - not using snapctl stop"
  665. # snapctl stop --disable "${SNAP_NAME}.mongodb"
  666. # Remove MongoDB 7 version marker
  667. rm -f "${SNAP_COMMON}/mongodb-version-7"
  668. # Find the most recent backup directory
  669. local latest_backup=$(ls -td "${SNAP_COMMON}/mongodb-backup-"* 2>/dev/null | head -1)
  670. if [ -n "$latest_backup" ] && [ -d "$latest_backup" ]; then
  671. log_message "Restoring from backup: $latest_backup"
  672. # Stop any running MongoDB processes
  673. pkill -f mongod || true
  674. sleep 2
  675. # Remove current database directory
  676. rm -rf "${SNAP_COMMON}/wekan"
  677. # Restore from backup
  678. cp -r "$latest_backup"/* "${SNAP_COMMON}/"
  679. # Clean up backup directory
  680. rm -rf "$latest_backup"
  681. log_success "Database restored from backup"
  682. else
  683. log_error "No backup found for revert"
  684. return 1
  685. fi
  686. # Remove revert file
  687. rm -f "$REVERT_FILE"
  688. # Clear migration status
  689. rm -f "$MIGRATION_STATUS"
  690. # Start MongoDB 3 - DISABLED
  691. log_message "MongoDB starting disabled - not using snapctl start"
  692. # snapctl start --enable "${SNAP_NAME}.mongodb"
  693. log_success "Migration reverted successfully"
  694. return 0
  695. }
  696. # Main execution
  697. main() {
  698. log_message "MongoDB Migration Script started"
  699. # Validate all paths are within SNAP_COMMON
  700. if ! validate_all_paths; then
  701. log_error "Path validation failed - aborting migration"
  702. exit 1
  703. fi
  704. # Check if revert is requested
  705. if [ -f "$REVERT_FILE" ]; then
  706. revert_migration
  707. exit $?
  708. fi
  709. # Check if migration is needed
  710. if ! check_migration_needed; then
  711. exit 0
  712. fi
  713. # Perform migration
  714. if perform_migration; then
  715. log_success "Migration completed successfully"
  716. exit 0
  717. else
  718. log_error "Migration failed"
  719. exit 1
  720. fi
  721. }
  722. # Run main function
  723. main "$@"