discourse/lib/backup_restore/backup_file_handler.rb

121 lines
3.5 KiB
Ruby

# frozen_string_literal: true
module BackupRestore
class BackupFileHandler
OLD_DUMP_FILENAME = "dump.sql"
delegate :log, to: :@logger, private: true
def initialize(logger, filename, current_db, root_tmp_directory = Rails.root)
@logger = logger
@filename = filename
@current_db = current_db
@root_tmp_directory = root_tmp_directory
@is_archive = !(@filename =~ /\.sql\.gz$/)
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
store = BackupRestore::BackupStore.create
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
# 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
['--transform', 's|var/www/discourse/public/uploads/|uploads/|']
when :bsd
['-s', '|var/www/discourse/public/uploads/|uploads/|']
end
log "Unzipping archive, this may take a while..."
Discourse::Utils.execute_command(
'tar', '--extract', '--gzip', '--file', @archive_path, '--directory', @tmp_directory,
*path_transformation, failure_message: "Failed to decompress archive."
)
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)
File.exists?(old_dump_path) ? old_dump_path : File.join(@tmp_directory, BackupRestore::DUMP_FILE)
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
def tar_implementation
@tar_version ||= begin
tar_version = Discourse::Utils.execute_command('tar', '--version')
if tar_version.include?("GNU tar")
:gnu
elsif tar_version.include?("bsdtar")
:bsd
else
raise "Unknown tar implementation: #{tar_version}"
end
end
end
end
end