@@ -13,9 +13,190 @@ log_step() {
13
13
echo " [$( date ' +%H:%M:%S' ) ] [$1 /$2 ] $3 "
14
14
}
15
15
16
+ # Show help information
17
+ show_help () {
18
+ cat << EOF
19
+ Usage: $0 --stage STAGE [YYYYMMDD]
20
+
21
+ Restore Databend system data from backup archive in stage.
22
+
23
+ RECOMMENDED USAGE:
24
+ # Interactive file selection (recommended)
25
+ $0 --stage backup_stage
26
+
27
+ # Restore specific date
28
+ $0 --stage backup_stage 20250109
29
+
30
+ OPTIONS:
31
+ --dsn DSN Database connection string (overrides BENDSQL_DSN env var)
32
+ --stage STAGE Source stage name (required)
33
+ --file FILENAME Specific file name in stage
34
+ --interactive Force interactive mode
35
+ -h, --help Show this help message
36
+
37
+ ENVIRONMENT VARIABLES:
38
+ BENDSQL_DSN Default database connection string
39
+
40
+ INTERACTIVE CONTROLS:
41
+ โ/โ or k/j Navigate files
42
+ Enter Select file
43
+ q Quit
44
+
45
+ EXAMPLES:
46
+ export BENDSQL_DSN="http://username:password@localhost:8000/database"
47
+
48
+ # Best: Browse and select interactively
49
+ $0 --stage my_backup_stage
50
+
51
+ # Quick: Restore specific date
52
+ $0 --stage my_backup_stage 20250109
53
+ EOF
54
+ }
55
+
56
+ # Interactive file selector
57
+ interactive_file_selector () {
58
+ local stage=" $1 "
59
+ local dsn=" $2 "
60
+
61
+ echo " Fetching file list from stage @${stage} ..."
62
+
63
+ # Get file list
64
+ local file_list
65
+ file_list=$( bendsql --dsn " ${dsn} " --query=" list @${stage} ;" 2> /dev/null | awk ' {print $1}' | grep -E ' \.(tar\.gz|tgz)$' | sort)
66
+
67
+ if [[ -z " $file_list " ]]; then
68
+ log_error " No .tar.gz files found in stage @${stage} "
69
+ return 1
70
+ fi
71
+
72
+ # Convert to array
73
+ local files=()
74
+ while IFS= read -r line; do
75
+ [[ -n " $line " ]] && files+=(" $line " )
76
+ done <<< " $file_list"
77
+
78
+ if [[ ${# files[@]} -eq 0 ]]; then
79
+ log_error " No .tar.gz files found in stage @${stage} "
80
+ return 1
81
+ fi
82
+
83
+ # Default to last file
84
+ local selected=$(( ${# files[@]} - 1 ))
85
+ local total=${# files[@]}
86
+
87
+ echo " "
88
+ echo " Found $total backup files in stage @${stage} :"
89
+ echo " Use โ/โ or k/j to navigate, Enter to select, q to quit"
90
+ echo " "
91
+
92
+ # Display function
93
+ display_files () {
94
+ # Clear screen and move to top
95
+ echo -ne " \033[2J\033[H"
96
+ echo " Stage: @${stage} ($total files)"
97
+ echo " Use โ/โ or k/j to navigate, Enter to select, q to quit"
98
+ echo " "
99
+
100
+ for i in " ${! files[@]} " ; do
101
+ if [[ $i -eq $selected ]]; then
102
+ echo -e " \033[7m> ${files[$i]} \033[0m" # Highlighted
103
+ else
104
+ echo " ${files[$i]} "
105
+ fi
106
+ done
107
+
108
+ echo " "
109
+ echo " Selected: ${files[$selected]} "
110
+ }
111
+
112
+ # Initial display
113
+ display_files
114
+
115
+ # Input loop
116
+ while true ; do
117
+ # Read single character
118
+ read -rsn1 key
119
+
120
+ case " $key " in
121
+ $' \033 ' ) # Escape sequence
122
+ read -rsn2 key
123
+ case " $key " in
124
+ ' [A' ) # Up arrow
125
+ (( selected > 0 )) && (( selected-- ))
126
+ display_files
127
+ ;;
128
+ ' [B' ) # Down arrow
129
+ (( selected < total - 1 )) && (( selected++ ))
130
+ display_files
131
+ ;;
132
+ esac
133
+ ;;
134
+ ' k' |' K' ) # Up (vim-style)
135
+ (( selected > 0 )) && (( selected-- ))
136
+ display_files
137
+ ;;
138
+ ' j' |' J' ) # Down (vim-style)
139
+ (( selected < total - 1 )) && (( selected++ ))
140
+ display_files
141
+ ;;
142
+ ' ' ) # Enter
143
+ echo " "
144
+ echo " Selected: ${files[$selected]} "
145
+ SELECTED_FILE=" ${files[$selected]} "
146
+ return 0
147
+ ;;
148
+ ' q' |' Q' ) # Quit
149
+ echo " "
150
+ echo " Selection cancelled."
151
+ return 1
152
+ ;;
153
+ esac
154
+ done
155
+ }
156
+
157
+ # Parse date from filename
158
+ parse_date_from_filename () {
159
+ local filename=" $1 "
160
+ local basename=$( basename " $filename " .tar.gz)
161
+
162
+ # Try different patterns
163
+ if [[ " $basename " =~ data_([0-9]{4})-([0-9]{2})-([0-9]{2}) ]]; then
164
+ # Pattern: data_YYYY-MM-DD
165
+ YEAR=" ${BASH_REMATCH[1]} "
166
+ MONTH=" ${BASH_REMATCH[2]} "
167
+ DAY=" ${BASH_REMATCH[3]} "
168
+ elif [[ " $basename " =~ ([0-9]{4})([0-9]{2})([0-9]{2}) ]]; then
169
+ # Pattern: YYYYMMDD anywhere in filename
170
+ YEAR=" ${BASH_REMATCH[1]} "
171
+ MONTH=" ${BASH_REMATCH[2]} "
172
+ DAY=" ${BASH_REMATCH[3]} "
173
+ else
174
+ # Fallback to current date
175
+ log " Could not extract date from filename: $filename , using current date"
176
+ YEAR=$( date ' +%Y' )
177
+ MONTH=$( date ' +%m' )
178
+ DAY=$( date ' +%d' )
179
+ fi
180
+
181
+ FORMATTED_DATE=" ${YEAR} -${MONTH} -${DAY} "
182
+ DATE_ARG=" ${YEAR}${MONTH}${DAY} "
183
+ }
184
+
16
185
# Parse arguments
186
+ FILE_MODE=false
187
+ INTERACTIVE_MODE=false
188
+ STAGE=" "
189
+ DSN=" "
190
+ DATE_ARG=" "
191
+ TAR_FILE=" "
192
+ STAGE_FILE=" "
193
+
17
194
while [[ $# -gt 0 ]]; do
18
195
case " $1 " in
196
+ -h | --help)
197
+ show_help
198
+ exit 0
199
+ ;;
19
200
--dsn)
20
201
DSN=" $2 "
21
202
shift 2
@@ -24,41 +205,98 @@ while [[ $# -gt 0 ]]; do
24
205
STAGE=" $2 "
25
206
shift 2
26
207
;;
208
+ --file)
209
+ STAGE_FILE=" $2 "
210
+ FILE_MODE=true
211
+ shift 2
212
+ ;;
213
+ --interactive)
214
+ INTERACTIVE_MODE=true
215
+ shift
216
+ ;;
27
217
* )
28
218
if [[ " $1 " =~ ^[0-9]{8}$ ]]; then
219
+ if [[ " $FILE_MODE " = true || " $INTERACTIVE_MODE " = true ]]; then
220
+ log_error " Cannot specify date with --file or --interactive"
221
+ echo " Use -h or --help for usage information." >&2
222
+ exit 1
223
+ fi
29
224
DATE_ARG=" $1 "
30
225
shift
31
226
else
32
227
log_error " Unknown parameter: $1 "
228
+ echo " Use -h or --help for usage information." >&2
33
229
exit 1
34
230
fi
35
231
;;
36
232
esac
37
233
done
38
234
39
235
# Validate parameters
40
- if [[ -z " $STAGE " || -z " $DATE_ARG " ]]; then
41
- log_error " Missing required parameters: --stage or yyyymmdd date"
236
+ if [[ -z " $STAGE " ]]; then
237
+ log_error " Missing required parameter: --stage"
238
+ echo " Use -h or --help for usage information." >&2
42
239
exit 1
43
240
fi
44
241
242
+ # Validate DSN early for interactive mode
45
243
if [[ -z " $DSN " ]]; then
46
244
DSN=" $BENDSQL_DSN "
47
245
if [[ -z " $DSN " ]]; then
48
246
log_error " DSN not provided and BENDSQL_DSN not set"
247
+ echo " Use -h or --help for usage information." >&2
49
248
exit 1
50
249
fi
51
250
fi
52
251
53
- # Format date
54
- YEAR=${DATE_ARG: 0: 4}
55
- MONTH=${DATE_ARG: 4: 2}
56
- DAY=${DATE_ARG: 6: 2}
57
- FORMATTED_DATE=" ${YEAR} -${MONTH} -${DAY} "
58
- TAR_FILE=" data_${FORMATTED_DATE} .tar.gz"
252
+ # Determine mode
253
+ if [[ " $INTERACTIVE_MODE " = true ]] || [[ -z " $DATE_ARG " && -z " $STAGE_FILE " ]]; then
254
+ # Interactive mode
255
+ if ! interactive_file_selector " $STAGE " " $DSN " ; then
256
+ exit 1
257
+ fi
258
+
259
+ STAGE_FILE=" $SELECTED_FILE "
260
+ FILE_MODE=true
261
+ parse_date_from_filename " $SELECTED_FILE "
262
+ TAR_FILE=" $SELECTED_FILE "
263
+
264
+ elif [[ " $FILE_MODE " = true ]]; then
265
+ # File mode validation
266
+ if [[ -z " $STAGE_FILE " ]]; then
267
+ log_error " Missing required parameter: --file"
268
+ echo " Use -h or --help for usage information." >&2
269
+ exit 1
270
+ fi
271
+
272
+ # Parse date from filename
273
+ parse_date_from_filename " $STAGE_FILE "
274
+ TAR_FILE=" $STAGE_FILE "
275
+
276
+ else
277
+ # Date mode validation
278
+ if [[ -z " $DATE_ARG " ]]; then
279
+ log_error " Missing required parameter: yyyymmdd date"
280
+ echo " Use -h or --help for usage information." >&2
281
+ exit 1
282
+ fi
283
+
284
+ # Format date
285
+ YEAR=${DATE_ARG: 0: 4}
286
+ MONTH=${DATE_ARG: 4: 2}
287
+ DAY=${DATE_ARG: 6: 2}
288
+ FORMATTED_DATE=" ${YEAR} -${MONTH} -${DAY} "
289
+ TAR_FILE=" data_${FORMATTED_DATE} .tar.gz"
290
+ fi
59
291
60
- log " Starting log restoration for date: ${FORMATTED_DATE} "
61
- log " Source stage: @${STAGE} , Target file: ${TAR_FILE} "
292
+ # Show operation mode
293
+ if [[ " $FILE_MODE " = true ]]; then
294
+ log " Starting log restoration from stage file: ${TAR_FILE} "
295
+ log " Source stage: @${STAGE} , Extracted date: ${FORMATTED_DATE} "
296
+ else
297
+ log " Starting log restoration for date: ${FORMATTED_DATE} "
298
+ log " Source stage: @${STAGE} , Target file: ${TAR_FILE} "
299
+ fi
62
300
63
301
# Step 1: Generate download URL
64
302
log_step " 1" " 6" " Generating presigned download URL for @${STAGE} /${TAR_FILE} "
@@ -73,24 +311,25 @@ log "Download URL generated successfully"
73
311
74
312
# Step 2: Download backup
75
313
log_step " 2" " 6" " Downloading ${TAR_FILE} from stage @${STAGE} "
76
- curl -s -o " ${TAR_FILE} " " ${DOWNLOAD_URL} "
314
+ LOCAL_TAR_FILE=$( basename " $TAR_FILE " )
315
+ curl -s -o " ${LOCAL_TAR_FILE} " " ${DOWNLOAD_URL} "
77
316
78
- if [[ ! -f " ${TAR_FILE } " ]]; then
317
+ if [[ ! -f " ${LOCAL_TAR_FILE } " ]]; then
79
318
log_error " Failed to download ${TAR_FILE} "
80
319
exit 1
81
320
fi
82
321
83
- FILE_SIZE=$( du -h " ${TAR_FILE } " | cut -f1)
322
+ FILE_SIZE=$( du -h " ${LOCAL_TAR_FILE } " | cut -f1)
84
323
log " Downloaded ${TAR_FILE} successfully (${FILE_SIZE} )"
85
324
86
325
# Step 3: Extract archive
87
- log_step " 3" " 6" " Extracting ${TAR_FILE } to temporary directory"
326
+ log_step " 3" " 6" " Extracting ${LOCAL_TAR_FILE } to temporary directory"
88
327
TEMP_DIR=" temp_extracted_${DATE_ARG} "
89
328
mkdir -p " ${TEMP_DIR} "
90
- tar -xzf " ${TAR_FILE } " -C " ${TEMP_DIR} "
329
+ tar -xzf " ${LOCAL_TAR_FILE } " -C " ${TEMP_DIR} "
91
330
92
331
EXTRACTED_FILES=$( find " ${TEMP_DIR} " -type f | wc -l)
93
- log " Extracted ${EXTRACTED_FILES} files from ${TAR_FILE } "
332
+ log " Extracted ${EXTRACTED_FILES} files from ${LOCAL_TAR_FILE } "
94
333
95
334
# Step 4: Detect path prefix
96
335
log_step " 4" " 6" " Analyzing directory structure for path prefix"
@@ -158,8 +397,8 @@ echo # New line after progress
158
397
log " Upload completed: ${UPLOAD_SUCCESS} successful, ${UPLOAD_FAILED} failed"
159
398
160
399
# Cleanup
161
- log " Cleaning up: removing ${TEMP_DIR} and ${TAR_FILE } "
162
- rm -rf " ${TEMP_DIR} " " ${TAR_FILE } "
400
+ log " Cleaning up: removing ${TEMP_DIR} and ${LOCAL_TAR_FILE } "
401
+ rm -rf " ${TEMP_DIR} " " ${LOCAL_TAR_FILE } "
163
402
164
403
# Step 6: Restore database
165
404
RESTORE_DATABASE=" ${STAGE} _${YEAR} _${MONTH} _${DAY} "
0 commit comments