2020-01-12 18:12:27 -05:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
module BackupRestore
|
|
|
|
class BackupFileHandler
|
|
|
|
OLD_DUMP_FILENAME = "dump.sql"
|
|
|
|
|
|
|
|
delegate :log, to: :@logger, private: true
|
|
|
|
|
2021-02-09 10:20:11 -05:00
|
|
|
def initialize(logger, filename, current_db, root_tmp_directory: Rails.root, location: nil)
|
2020-01-12 18:12:27 -05:00
|
|
|
@logger = logger
|
|
|
|
@filename = filename
|
|
|
|
@current_db = current_db
|
|
|
|
@root_tmp_directory = root_tmp_directory
|
|
|
|
@is_archive = !(@filename =~ /\.sql\.gz$/)
|
2021-02-09 10:02:44 -05:00
|
|
|
@store_location = location
|
2020-01-12 18:12:27 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def decompress
|
|
|
|
create_tmp_directory
|
|
|
|
@archive_path = File.join(@tmp_directory, @filename)
|
|
|
|
|
|
|
|
copy_archive_to_tmp_directory
|
|
|
|
decompress_archive
|
|
|
|
extract_db_dump
|
|
|
|
|
|
|
|
[@tmp_directory, @db_dump_path]
|
|
|
|
end
|
|
|
|
|
|
|
|
def clean_up
|
|
|
|
return if @tmp_directory.blank?
|
|
|
|
|
|
|
|
log "Removing tmp '#{@tmp_directory}' directory..."
|
|
|
|
FileUtils.rm_rf(@tmp_directory) if Dir[@tmp_directory].present?
|
|
|
|
rescue => ex
|
|
|
|
log "Something went wrong while removing the following tmp directory: #{@tmp_directory}", ex
|
|
|
|
end
|
|
|
|
|
|
|
|
protected
|
|
|
|
|
|
|
|
def create_tmp_directory
|
|
|
|
timestamp = Time.zone.now.strftime("%Y-%m-%d-%H%M%S")
|
|
|
|
@tmp_directory = File.join(@root_tmp_directory, "tmp", "restores", @current_db, timestamp)
|
|
|
|
ensure_directory_exists(@tmp_directory)
|
|
|
|
end
|
|
|
|
|
|
|
|
def ensure_directory_exists(directory)
|
|
|
|
log "Making sure #{directory} exists..."
|
|
|
|
FileUtils.mkdir_p(directory)
|
|
|
|
end
|
|
|
|
|
|
|
|
def copy_archive_to_tmp_directory
|
2021-02-09 10:02:44 -05:00
|
|
|
store = BackupRestore::BackupStore.create(location: @store_location)
|
2020-01-12 18:12:27 -05:00
|
|
|
|
|
|
|
if store.remote?
|
|
|
|
log "Downloading archive to tmp directory..."
|
|
|
|
failure_message = "Failed to download archive to tmp directory."
|
|
|
|
else
|
|
|
|
log "Copying archive to tmp directory..."
|
|
|
|
failure_message = "Failed to copy archive to tmp directory."
|
|
|
|
end
|
|
|
|
|
|
|
|
store.download_file(@filename, @archive_path, failure_message)
|
|
|
|
end
|
|
|
|
|
|
|
|
def decompress_archive
|
|
|
|
return if !@is_archive
|
|
|
|
|
2020-08-27 13:03:23 -04:00
|
|
|
# the transformation is a workaround for a bug which existed between v2.6.0.beta1 and v2.6.0.beta2
|
|
|
|
path_transformation =
|
|
|
|
case tar_implementation
|
|
|
|
when :gnu
|
|
|
|
%w[--transform s|var/www/discourse/public/uploads/|uploads/|]
|
|
|
|
when :bsd
|
|
|
|
%w[-s |var/www/discourse/public/uploads/|uploads/|]
|
|
|
|
end
|
|
|
|
|
2020-01-12 18:12:27 -05:00
|
|
|
log "Unzipping archive, this may take a while..."
|
2020-05-23 00:56:13 -04:00
|
|
|
Discourse::Utils.execute_command(
|
|
|
|
"tar",
|
|
|
|
"--extract",
|
|
|
|
"--gzip",
|
|
|
|
"--file",
|
|
|
|
@archive_path,
|
|
|
|
"--directory",
|
|
|
|
@tmp_directory,
|
2020-08-27 13:03:23 -04:00
|
|
|
*path_transformation,
|
|
|
|
failure_message: "Failed to decompress archive.",
|
2020-05-23 00:56:13 -04:00
|
|
|
)
|
2020-01-12 18:12:27 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def extract_db_dump
|
|
|
|
@db_dump_path =
|
|
|
|
if @is_archive
|
|
|
|
# for compatibility with backups from Discourse v1.5 and below
|
|
|
|
old_dump_path = File.join(@tmp_directory, OLD_DUMP_FILENAME)
|
2022-01-05 12:45:08 -05:00
|
|
|
if File.exist?(old_dump_path)
|
|
|
|
old_dump_path
|
2023-01-09 07:10:19 -05:00
|
|
|
else
|
2022-01-05 12:45:08 -05:00
|
|
|
File.join(@tmp_directory, BackupRestore::DUMP_FILE)
|
2023-01-09 07:10:19 -05:00
|
|
|
end
|
2020-01-12 18:12:27 -05:00
|
|
|
else
|
|
|
|
File.join(@tmp_directory, @filename)
|
|
|
|
end
|
|
|
|
|
|
|
|
if File.extname(@db_dump_path) == ".gz"
|
|
|
|
log "Extracting dump file..."
|
|
|
|
Compression::Gzip.new.decompress(@tmp_directory, @db_dump_path, available_size)
|
|
|
|
@db_dump_path.delete_suffix!(".gz")
|
|
|
|
end
|
|
|
|
|
|
|
|
@db_dump_path
|
|
|
|
end
|
|
|
|
|
|
|
|
def available_size
|
|
|
|
SiteSetting.decompressed_backup_max_file_size_mb
|
|
|
|
end
|
2020-08-27 13:03:23 -04:00
|
|
|
|
|
|
|
def tar_implementation
|
|
|
|
@tar_version ||=
|
|
|
|
begin
|
|
|
|
tar_version = Discourse::Utils.execute_command("tar", "--version")
|
2023-01-09 07:10:19 -05:00
|
|
|
|
2020-08-27 13:03:23 -04:00
|
|
|
if tar_version.include?("GNU tar")
|
|
|
|
:gnu
|
|
|
|
elsif tar_version.include?("bsdtar")
|
|
|
|
:bsd
|
|
|
|
else
|
|
|
|
raise "Unknown tar implementation: #{tar_version}"
|
2023-01-09 07:10:19 -05:00
|
|
|
end
|
2020-08-27 13:03:23 -04:00
|
|
|
end
|
|
|
|
end
|
2020-01-12 18:12:27 -05:00
|
|
|
end
|
|
|
|
end
|