reserve production runtime state during restore
reserve production runtime state during restore
This commit is contained in:
@@ -14,6 +14,14 @@ usage() {
|
||||
cat <<USAGE
|
||||
Usage:
|
||||
sudo ./restore-appfactory.sh /path/to/appfactory-backup.tar.gz [target_user]
|
||||
|
||||
Notes:
|
||||
- Existing production appfactory.env is preserved.
|
||||
- Missing env keys from backup are appended.
|
||||
- Existing non-empty env values are never overwritten.
|
||||
- Existing Caddy TLS state is preserved.
|
||||
- Existing app runtime .env files are preserved and merged.
|
||||
- Existing /opt/appfactory/data/secrets is preserved.
|
||||
USAGE
|
||||
}
|
||||
|
||||
@@ -77,7 +85,11 @@ merge_env_missing_keys() {
|
||||
local target_file="$2"
|
||||
|
||||
[ -f "$source_file" ] || return 0
|
||||
[ -f "$target_file" ] || cp "$source_file" "$target_file"
|
||||
|
||||
if [ ! -f "$target_file" ]; then
|
||||
cp "$source_file" "$target_file"
|
||||
return 0
|
||||
fi
|
||||
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
@@ -117,6 +129,63 @@ resolve_runtime_uid_gid() {
|
||||
echo "$uid:$gid"
|
||||
}
|
||||
|
||||
copy_dir_if_exists() {
|
||||
local src="$1"
|
||||
local dst="$2"
|
||||
|
||||
if [ -d "$src" ]; then
|
||||
mkdir -p "$(dirname "$dst")"
|
||||
rm -rf "$dst"
|
||||
cp -a "$src" "$dst"
|
||||
fi
|
||||
}
|
||||
|
||||
preserve_app_envs() {
|
||||
local source_apps_dir="$1"
|
||||
local target_dir="$2"
|
||||
|
||||
[ -d "$source_apps_dir" ] || return 0
|
||||
mkdir -p "$target_dir"
|
||||
|
||||
while IFS= read -r env_file; do
|
||||
[ -f "$env_file" ] || continue
|
||||
app_id="$(basename "$(dirname "$env_file")")"
|
||||
mkdir -p "$target_dir/$app_id"
|
||||
cp "$env_file" "$target_dir/$app_id/.env"
|
||||
ok "Preserved app runtime env: $app_id"
|
||||
done < <(find "$source_apps_dir" -mindepth 2 -maxdepth 2 -type f -name ".env" 2>/dev/null || true)
|
||||
}
|
||||
|
||||
restore_preserved_app_envs() {
|
||||
local preserved_dir="$1"
|
||||
local target_apps_dir="$2"
|
||||
|
||||
[ -d "$preserved_dir" ] || return 0
|
||||
|
||||
while IFS= read -r preserved_env; do
|
||||
[ -f "$preserved_env" ] || continue
|
||||
|
||||
app_id="$(basename "$(dirname "$preserved_env")")"
|
||||
target_env="$target_apps_dir/$app_id/.env"
|
||||
|
||||
mkdir -p "$(dirname "$target_env")"
|
||||
|
||||
if [ -f "$target_env" ]; then
|
||||
backup_env_copy="$(mktemp)"
|
||||
cp "$target_env" "$backup_env_copy"
|
||||
|
||||
cp "$preserved_env" "$target_env"
|
||||
merge_env_missing_keys "$backup_env_copy" "$target_env"
|
||||
|
||||
rm -f "$backup_env_copy"
|
||||
ok "Restored and merged app runtime env: $app_id"
|
||||
else
|
||||
cp "$preserved_env" "$target_env"
|
||||
ok "Restored app runtime env: $app_id"
|
||||
fi
|
||||
done < <(find "$preserved_dir" -mindepth 2 -maxdepth 2 -type f -name ".env" 2>/dev/null || true)
|
||||
}
|
||||
|
||||
[ -n "$BACKUP_FILE_INPUT" ] || { usage; fail "Backup file argument is required"; }
|
||||
[ "$(id -u)" -eq 0 ] || fail "Run as root via sudo"
|
||||
[ -f "$BACKUP_FILE_INPUT" ] || fail "Backup file not found: $BACKUP_FILE_INPUT"
|
||||
@@ -147,15 +216,38 @@ log "Calculated runtime APPFACTORY_UID=$APPFACTORY_UID"
|
||||
log "Calculated runtime APPFACTORY_GID=$APPFACTORY_GID"
|
||||
log "Calculated runtime APPFACTORY_DOCKER_GID=$APPFACTORY_DOCKER_GID"
|
||||
|
||||
PRESERVE_ROOT="$(mktemp -d)"
|
||||
PRESERVED_ENV=""
|
||||
PRESERVED_CADDY="$PRESERVE_ROOT/caddy"
|
||||
PRESERVED_CADDY_CONFIG="$PRESERVE_ROOT/caddy-config"
|
||||
PRESERVED_SECRETS="$PRESERVE_ROOT/secrets"
|
||||
PRESERVED_APP_ENVS="$PRESERVE_ROOT/app-envs"
|
||||
|
||||
EXISTING_ENV="$APPFACTORY_ROOT/config/appfactory.env"
|
||||
|
||||
if [ -f "$EXISTING_ENV" ]; then
|
||||
PRESERVED_ENV="$(mktemp)"
|
||||
PRESERVED_ENV="$PRESERVE_ROOT/appfactory.env"
|
||||
cp "$EXISTING_ENV" "$PRESERVED_ENV"
|
||||
ok "Preserved existing env before restore: $EXISTING_ENV"
|
||||
fi
|
||||
|
||||
copy_dir_if_exists "$APPFACTORY_ROOT/data/caddy" "$PRESERVED_CADDY"
|
||||
copy_dir_if_exists "$APPFACTORY_ROOT/data/caddy-config" "$PRESERVED_CADDY_CONFIG"
|
||||
copy_dir_if_exists "$APPFACTORY_ROOT/data/secrets" "$PRESERVED_SECRETS"
|
||||
preserve_app_envs "$APPFACTORY_ROOT/data/apps" "$PRESERVED_APP_ENVS"
|
||||
|
||||
if [ -d "$PRESERVED_CADDY" ]; then
|
||||
ok "Preserved Caddy TLS state: $APPFACTORY_ROOT/data/caddy"
|
||||
fi
|
||||
|
||||
if [ -d "$PRESERVED_CADDY_CONFIG" ]; then
|
||||
ok "Preserved Caddy config state: $APPFACTORY_ROOT/data/caddy-config"
|
||||
fi
|
||||
|
||||
if [ -d "$PRESERVED_SECRETS" ]; then
|
||||
ok "Preserved secrets directory: $APPFACTORY_ROOT/data/secrets"
|
||||
fi
|
||||
|
||||
log "Removing existing AppFactory containers if present"
|
||||
mapfile -t APPFACTORY_CONTAINERS < <(
|
||||
docker ps -a --format '{{.Names}}' \
|
||||
@@ -221,6 +313,29 @@ else
|
||||
set_env_value "$ENV_FILE" "APPFACTORY_DOCKER_GID" "$APPFACTORY_DOCKER_GID"
|
||||
fi
|
||||
|
||||
if [ -d "$PRESERVED_CADDY" ]; then
|
||||
log "Restoring preserved Caddy TLS state"
|
||||
rm -rf "$APPFACTORY_ROOT/data/caddy"
|
||||
cp -a "$PRESERVED_CADDY" "$APPFACTORY_ROOT/data/caddy"
|
||||
ok "Restored Caddy TLS state"
|
||||
fi
|
||||
|
||||
if [ -d "$PRESERVED_CADDY_CONFIG" ]; then
|
||||
log "Restoring preserved Caddy config state"
|
||||
rm -rf "$APPFACTORY_ROOT/data/caddy-config"
|
||||
cp -a "$PRESERVED_CADDY_CONFIG" "$APPFACTORY_ROOT/data/caddy-config"
|
||||
ok "Restored Caddy config state"
|
||||
fi
|
||||
|
||||
if [ -d "$PRESERVED_SECRETS" ]; then
|
||||
log "Restoring preserved secrets directory"
|
||||
rm -rf "$APPFACTORY_ROOT/data/secrets"
|
||||
cp -a "$PRESERVED_SECRETS" "$APPFACTORY_ROOT/data/secrets"
|
||||
ok "Restored secrets directory"
|
||||
fi
|
||||
|
||||
restore_preserved_app_envs "$PRESERVED_APP_ENVS" "$APPFACTORY_ROOT/data/apps"
|
||||
|
||||
log "Ensuring mandatory env keys without overwriting existing non-empty values"
|
||||
set_env_if_missing_or_empty "$ENV_FILE" "APPFACTORY_DIR" "$APPFACTORY_ROOT"
|
||||
set_env_if_missing_or_empty "$ENV_FILE" "WORKSPACE_DIR" "$APPFACTORY_ROOT/workspace"
|
||||
@@ -244,14 +359,20 @@ set_env_if_missing_or_empty "$ENV_FILE" "WEBHOOK_URL" ""
|
||||
set_env_if_missing_or_empty "$ENV_FILE" "GITEA_OAUTH_REDIRECT_URI" ""
|
||||
set_env_if_missing_or_empty "$ENV_FILE" "GITEA_SSH_PORT" "2222"
|
||||
|
||||
set_env_if_missing_or_empty "$ENV_FILE" "GITEA_ADMIN_TOKEN" ""
|
||||
set_env_if_missing_or_empty "$ENV_FILE" "GITEA_API_TOKEN" ""
|
||||
set_env_if_missing_or_empty "$ENV_FILE" "GITEA_TOKEN" ""
|
||||
|
||||
set_env_if_missing_or_empty "$ENV_FILE" "GOOGLE_OAUTH_ENABLED" "false"
|
||||
set_env_if_missing_or_empty "$ENV_FILE" "GOOGLE_CLIENT_ID" ""
|
||||
set_env_if_missing_or_empty "$ENV_FILE" "GOOGLE_CLIENT_SECRET" ""
|
||||
set_env_if_missing_or_empty "$ENV_FILE" "GOOGLE_REDIRECT_URI" ""
|
||||
set_env_if_missing_or_empty "$ENV_FILE" "GOOGLE_ALLOWED_DOMAIN" ""
|
||||
|
||||
set_env_if_missing_or_empty "$ENV_FILE" "AUTH_MODE" "google"
|
||||
|
||||
log "Effective runtime env:"
|
||||
grep -E '^(APPFACTORY_UID|APPFACTORY_GID|APPFACTORY_DOCKER_GID|APPFACTORY_ENABLE_HTTPS|APPFACTORY_DOMAIN|APPFACTORY_PORTAL_DOMAIN|APPFACTORY_GITEA_DOMAIN|APPFACTORY_REGISTRY_DOMAIN|APPFACTORY_PORTAL_PUBLIC_URL|GITEA_DOMAIN|GITEA_ROOT_URL|GITEA_URL|WEBHOOK_URL|GITEA_OAUTH_REDIRECT_URI)=' "$ENV_FILE" || true
|
||||
grep -E '^(APPFACTORY_UID|APPFACTORY_GID|APPFACTORY_DOCKER_GID|APPFACTORY_ENABLE_HTTPS|APPFACTORY_DOMAIN|APPFACTORY_PORTAL_DOMAIN|APPFACTORY_GITEA_DOMAIN|APPFACTORY_REGISTRY_DOMAIN|APPFACTORY_PORTAL_PUBLIC_URL|GITEA_DOMAIN|GITEA_ROOT_URL|GITEA_URL|WEBHOOK_URL|GITEA_OAUTH_REDIRECT_URI|AUTH_MODE)=' "$ENV_FILE" || true
|
||||
|
||||
DB_FILE="$APPFACTORY_ROOT/data/appfactory/appfactory.db"
|
||||
[ -f "$DB_FILE" ] || fail "Missing AppFactory DB after restore: $DB_FILE"
|
||||
@@ -352,6 +473,15 @@ if [ -f "$GENERATE_CADDY" ]; then
|
||||
"$GENERATE_CADDY"
|
||||
fi
|
||||
|
||||
REDEPLOY_APPS="$APPFACTORY_ROOT/workspace/appfactory-tools/scripts/redeploy-enabled-apps.sh"
|
||||
if [ -f "$REDEPLOY_APPS" ]; then
|
||||
chmod +x "$REDEPLOY_APPS"
|
||||
log "Redeploying enabled user applications"
|
||||
"$REDEPLOY_APPS" || warn "redeploy-enabled-apps.sh failed; check app logs"
|
||||
else
|
||||
warn "redeploy-enabled-apps.sh not found, skipping user app redeploy"
|
||||
fi
|
||||
|
||||
PREFLIGHT="$APPFACTORY_ROOT/workspace/appfactory-tools/maintenance/preflight-check.sh"
|
||||
[ -f "$PREFLIGHT" ] || fail "Missing preflight script: $PREFLIGHT"
|
||||
chmod +x "$PREFLIGHT"
|
||||
@@ -359,5 +489,8 @@ chmod +x "$PREFLIGHT"
|
||||
log "Running final preflight"
|
||||
"$PREFLIGHT"
|
||||
|
||||
rm -rf "$PRESERVE_ROOT"
|
||||
|
||||
echo
|
||||
echo "APPFACTORY RESTORE: READY"
|
||||
EOF
|
||||
|
||||
Reference in New Issue
Block a user