-
Notifications
You must be signed in to change notification settings - Fork 5.6k
[INFRA-804] fix(airgapped): harden community restore-airgapped.sh script #9727
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: preview
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,49 @@ | ||
| #!/bin/bash | ||
| +set -euo pipefail | ||
| set -euo pipefail | ||
|
|
||
| function print_header() { | ||
| clear | ||
|
|
||
| cat <<"EOF" | ||
| -------------------------------------------- | ||
| ____ _ ///////// | ||
| | _ \| | __ _ _ __ ___ ///////// | ||
| | |_) | |/ _` | '_ \ / _ \ ///// ///// | ||
| | __/| | (_| | | | | __/ ///// ///// | ||
| |_| |_|\__,_|_| |_|\___| //// | ||
| //// | ||
| -------------------------------------------- | ||
| Project management tool from the future | ||
| -------------------------------------------- | ||
| ##+. ##+ .##- | ||
| ######+.######-.######. | ||
| #######. -### +#####+. | ||
| #######. + +######. | ||
| #######. .####### | ||
| #######. .####### | ||
| ####### + .####### | ||
| .+#####+ ###- .####### | ||
| .######.-#####+.+###### | ||
| -##. -## .+## | ||
| EOF | ||
| } | ||
|
|
||
| # Replace $2 (dest) with $1 (src), keeping the old dest recoverable until the | ||
| # swap succeeds. Rolls the old data back if the move fails. | ||
| function replaceDir() { | ||
| local src="$1" dest="$2" label="$3" | ||
| local old="${dest}.old.$$" | ||
|
|
||
| if [ -d "$dest" ]; then | ||
| mv "$dest" "$old" | ||
| # Restore the old data if we are interrupted mid-swap | ||
| trap "mv \"$old\" \"$dest\" 2>/dev/null || true; exit 1" INT TERM HUP | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Keep rollback active until the directory state is stable. If the second Make the handler remove an installed Proposed fix- trap "mv \"$old\" \"$dest\" 2>/dev/null || true; exit 1" INT TERM HUP
+ trap 'if [ -d "$old" ]; then
+ if [ -e "$dest" ]; then rm -rf -- "$dest" || exit 1; fi
+ mv -- "$old" "$dest" || exit 1
+ fi
+ exit 1' INT TERM HUP
...
- trap - INT TERM HUP
echo "Error: Failed to install $label; restoring previous data"
if [ -d "$old" ]; then
mv "$old" "$dest"
fi
+ trap - INT TERM HUPAlso applies to: 34-38 🧰 Tools🪛 Shellcheck (0.11.0)[warning] 30-30: Use single quotes, otherwise this expands now rather than when signalled. (SC2064) [warning] 30-30: Use single quotes, otherwise this expands now rather than when signalled. (SC2064) 🤖 Prompt for AI Agents |
||
| fi | ||
|
|
||
| if mv "$src" "$dest"; then | ||
| trap - INT TERM HUP | ||
| rm -rf "$old" | ||
| echo "Renamed $label" | ||
| else | ||
| trap - INT TERM HUP | ||
| echo "Error: Failed to install $label; restoring previous data" | ||
| if [ -d "$old" ]; then | ||
| mv "$old" "$dest" | ||
| fi | ||
| exit 1 | ||
| fi | ||
| } | ||
|
|
||
| function restoreData() { | ||
|
|
||
| echo "" | ||
|
|
@@ -27,7 +53,7 @@ function restoreData() { | |
| echo "" | ||
|
|
||
| # set the backup folder path | ||
| BACKUP_FOLDER=${1} | ||
| BACKUP_FOLDER="${1:-}" | ||
|
|
||
| if [ -z "$BACKUP_FOLDER" ]; then | ||
| BACKUP_FOLDER="$PWD/backup" | ||
|
|
@@ -46,6 +72,8 @@ function restoreData() { | |
| # check if there are any .tar.gz files in the backup folder | ||
| if ! ls "$BACKUP_FOLDER"/*.tar.gz 1> /dev/null 2>&1; then | ||
| echo "Error: Backup folder does not contain .tar.gz files" | ||
| echo "" | ||
| echo "Usage: $0 /path/to/backup" | ||
| exit 1 | ||
| fi | ||
|
|
||
|
|
@@ -76,57 +104,75 @@ function restoreData() { | |
| exit 1 | ||
| fi | ||
|
|
||
| local dockerServiceStatus | ||
| local dockerServiceStatus compose_output | ||
| if command -v jq &> /dev/null; then | ||
| dockerServiceStatus=$($COMPOSE_CMD ls --filter name=plane-airgapped --format=json | jq -r .[0].Status) | ||
| if ! compose_output=$($COMPOSE_CMD ls --filter name=plane-airgapped --format=json); then | ||
| echo "Error: Failed to query Docker Compose services" | ||
| exit 1 | ||
| fi | ||
| dockerServiceStatus=$(printf '%s\n' "$compose_output" | jq -r '.[0].Status // empty') | ||
| else | ||
|
akshat5302 marked this conversation as resolved.
|
||
| dockerServiceStatus=$($COMPOSE_CMD ls --filter name=plane-airgapped | grep -o "running" | head -n 1) | ||
| if ! compose_output=$($COMPOSE_CMD ls --filter name=plane-airgapped); then | ||
| echo "Error: Failed to query Docker Compose services" | ||
| exit 1 | ||
| fi | ||
| dockerServiceStatus=$(printf '%s\n' "$compose_output" | grep -o "running" | head -n 1 || true) | ||
| fi | ||
|
|
||
| if [[ $dockerServiceStatus == "running" ]]; then | ||
| if [[ "$dockerServiceStatus" == running* ]]; then | ||
| echo "Plane Airgapped is running. Please STOP the Plane Airgapped before restoring data." | ||
| exit 1 | ||
| fi | ||
|
|
||
| CURRENT_USER_ID=$(id -u) | ||
| CURRENT_GROUP_ID=$(id -g) | ||
|
|
||
| DATA_DIR="$AIRGAPPED_INSTALL_PATH/data" | ||
|
|
||
| # if the data folder not exists, create it | ||
| if [ ! -d "$AIRGAPPED_INSTALL_PATH/data" ]; then | ||
| mkdir -p "$AIRGAPPED_INSTALL_PATH/data" | ||
| chown -R $CURRENT_USER_ID:$CURRENT_GROUP_ID "$AIRGAPPED_INSTALL_PATH/data" | ||
| if [ ! -d "$DATA_DIR" ]; then | ||
| mkdir -p "$DATA_DIR" | ||
| chown -R $CURRENT_USER_ID:$CURRENT_GROUP_ID "$DATA_DIR" | ||
| fi | ||
|
|
||
| for BACKUP_FILE in "$BACKUP_FOLDER/*.tar.gz"; do | ||
| if [ -e "$BACKUP_FILE" ]; then | ||
|
|
||
| # get the basefilename without the extension | ||
| BASE_FILE_NAME=$(basename "$BACKUP_FILE" ".tar.gz") | ||
|
|
||
| # extract the restoreFile to the airgapped instance install path | ||
| echo "Restoring $BASE_FILE_NAME" | ||
| rm -rf "$AIRGAPPED_INSTALL_PATH/data/$BASE_FILE_NAME" || true | ||
|
|
||
| tar -xvzf "$BACKUP_FILE" -C "$AIRGAPPED_INSTALL_PATH/data/" | ||
| if [ $? -ne 0 ]; then | ||
| echo "Error: Failed to extract $BACKUP_FILE" | ||
| exit 1 | ||
| fi | ||
| chown -R $CURRENT_USER_ID:$CURRENT_GROUP_ID "$AIRGAPPED_INSTALL_PATH/data/$BASE_FILE_NAME" | ||
| if [ $? -ne 0 ]; then | ||
| echo "Error: Failed to change ownership of $AIRGAPPED_INSTALL_PATH/data/$BASE_FILE_NAME" | ||
| exit 1 | ||
| fi | ||
| else | ||
| echo "No .tar.gz files found in the current directory." | ||
| echo "" | ||
| echo "Please provide the path to the backup file." | ||
| echo "" | ||
| echo "Usage: $0 /path/to/backup" | ||
| # Remove stale extracted source directories from a previous run so tar | ||
| # does not merge new files into old data | ||
| rm -rf "$DATA_DIR/pgdata" "$DATA_DIR/redisdata" "$DATA_DIR/uploads" "$DATA_DIR/rabbitmq_data" | ||
|
|
||
| # Extract all backup tar files | ||
| for BACKUP_FILE in "$BACKUP_FOLDER"/*.tar.gz; do | ||
|
akshat5302 marked this conversation as resolved.
|
||
| BASE_FILE_NAME=$(basename "$BACKUP_FILE" ".tar.gz") | ||
| echo "Extracting $BASE_FILE_NAME" | ||
| if ! tar -xzvf "$BACKUP_FILE" -C "$DATA_DIR/"; then | ||
| echo "Error: Failed to extract $BACKUP_FILE" | ||
| exit 1 | ||
| fi | ||
| done | ||
|
|
||
| # Rename extracted directories to match docker-compose volume paths | ||
| # Backup tars: pgdata, redisdata, uploads, rabbitmq_data | ||
| # Docker-compose expects: db, redis, minio/uploads, mq | ||
|
|
||
| if [ -d "$DATA_DIR/pgdata" ]; then | ||
| replaceDir "$DATA_DIR/pgdata" "$DATA_DIR/db" "pgdata -> db" | ||
| fi | ||
|
|
||
| if [ -d "$DATA_DIR/redisdata" ]; then | ||
| replaceDir "$DATA_DIR/redisdata" "$DATA_DIR/redis" "redisdata -> redis" | ||
| fi | ||
|
|
||
| if [ -d "$DATA_DIR/uploads" ]; then | ||
| mkdir -p "$DATA_DIR/minio" | ||
| replaceDir "$DATA_DIR/uploads" "$DATA_DIR/minio/uploads" "uploads -> minio/uploads" | ||
| fi | ||
|
|
||
| if [ -d "$DATA_DIR/rabbitmq_data" ]; then | ||
| replaceDir "$DATA_DIR/rabbitmq_data" "$DATA_DIR/mq" "rabbitmq_data -> mq" | ||
| fi | ||
|
|
||
| # Fix ownership on all restored data | ||
| chown -R $CURRENT_USER_ID:$CURRENT_GROUP_ID "$DATA_DIR" | ||
|
|
||
| echo "" | ||
| echo "Restore completed successfully." | ||
| echo "" | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.