From 4d719725c88a0a3e8e50c5a1ea9e634c1a39b1e3 Mon Sep 17 00:00:00 2001 From: Gerhard Schlager Date: Tue, 9 Feb 2021 16:02:44 +0100 Subject: [PATCH] FEATURE: Allow overriding the backup location when restoring via CLI (#12015) You can use `discourse restore --location=local FILENAME` if you want to restore a backup that is stored locally even though the `backup_location` has the value `s3`. --- lib/backup_restore/backup_file_handler.rb | 5 +++-- lib/backup_restore/backup_store.rb | 2 +- lib/backup_restore/factory.rb | 4 ++-- lib/backup_restore/restorer.rb | 4 ++-- script/discourse | 4 +++- .../backup_file_handler_spec.rb | 19 +++++++++++++++++-- 6 files changed, 28 insertions(+), 10 deletions(-) diff --git a/lib/backup_restore/backup_file_handler.rb b/lib/backup_restore/backup_file_handler.rb index 86103663e20..5b4fd82cd5c 100644 --- a/lib/backup_restore/backup_file_handler.rb +++ b/lib/backup_restore/backup_file_handler.rb @@ -6,12 +6,13 @@ module BackupRestore delegate :log, to: :@logger, private: true - def initialize(logger, filename, current_db, root_tmp_directory = Rails.root) + def initialize(logger, filename, current_db, root_tmp_directory = Rails.root, location = nil) @logger = logger @filename = filename @current_db = current_db @root_tmp_directory = root_tmp_directory @is_archive = !(@filename =~ /\.sql\.gz$/) + @store_location = location end def decompress @@ -48,7 +49,7 @@ module BackupRestore end def copy_archive_to_tmp_directory - store = BackupRestore::BackupStore.create + store = BackupRestore::BackupStore.create(location: @store_location) if store.remote? log "Downloading archive to tmp directory..." diff --git a/lib/backup_restore/backup_store.rb b/lib/backup_restore/backup_store.rb index 2f95551dd55..2243837fd0c 100644 --- a/lib/backup_restore/backup_store.rb +++ b/lib/backup_restore/backup_store.rb @@ -8,7 +8,7 @@ module BackupRestore # @return [BackupStore] def self.create(opts = {}) - case SiteSetting.backup_location + case opts[:location] || SiteSetting.backup_location when BackupLocationSiteSetting::LOCAL require_dependency "backup_restore/local_backup_store" BackupRestore::LocalBackupStore.new(opts) diff --git a/lib/backup_restore/factory.rb b/lib/backup_restore/factory.rb index 5bc044b9242..1e7d35f0be7 100644 --- a/lib/backup_restore/factory.rb +++ b/lib/backup_restore/factory.rb @@ -27,8 +27,8 @@ module BackupRestore MetaDataHandler.new(logger, filename, tmp_directory) end - def create_backup_file_handler(filename, current_db) - BackupFileHandler.new(logger, filename, current_db) + def create_backup_file_handler(filename, current_db, location) + BackupFileHandler.new(logger, filename, current_db, location) end end end diff --git a/lib/backup_restore/restorer.rb b/lib/backup_restore/restorer.rb index ce2248862c2..bbcc225f51f 100644 --- a/lib/backup_restore/restorer.rb +++ b/lib/backup_restore/restorer.rb @@ -9,7 +9,7 @@ module BackupRestore attr_reader :success - def initialize(user_id:, filename:, factory:, disable_emails: true) + def initialize(user_id:, filename:, factory:, disable_emails: true, location:) @user_id = user_id @filename = filename @factory = factory @@ -24,7 +24,7 @@ module BackupRestore @current_db = RailsMultisite::ConnectionManagement.current_db @system = factory.create_system_interface - @backup_file_handler = factory.create_backup_file_handler(@filename, @current_db) + @backup_file_handler = factory.create_backup_file_handler(@filename, @current_db, location) @database_restorer = factory.create_database_restorer(@current_db) @uploads_restorer = factory.create_uploads_restorer end diff --git a/script/discourse b/script/discourse index 7b4990215bb..52eb3440f1e 100755 --- a/script/discourse +++ b/script/discourse @@ -108,6 +108,7 @@ class DiscourseCLI < Thor desc "restore", "Restore a Discourse backup" option :disable_emails, type: :boolean, default: true + option :location, type: :string, enum: ["local", "s3"], desc: "Override the backup location" def restore(filename = nil) if File.exist?('/usr/local/bin/discourse') @@ -124,7 +125,7 @@ class DiscourseCLI < Thor if !filename puts "You must provide a filename to restore. Did you mean one of the following?\n\n" - store = BackupRestore::BackupStore.create + store = BackupRestore::BackupStore.create(location: options[:location]) store.files.each do |file| puts "#{discourse} restore #{file.filename}" end @@ -138,6 +139,7 @@ class DiscourseCLI < Thor user_id: Discourse.system_user.id, filename: filename, disable_emails: options[:disable_emails], + location: options[:location], factory: BackupRestore::Factory.new(user_id: Discourse.system_user.id) ) restorer.run diff --git a/spec/lib/backup_restore/backup_file_handler_spec.rb b/spec/lib/backup_restore/backup_file_handler_spec.rb index c9a12aac2d8..46bd36815d7 100644 --- a/spec/lib/backup_restore/backup_file_handler_spec.rb +++ b/spec/lib/backup_restore/backup_file_handler_spec.rb @@ -7,7 +7,8 @@ describe BackupRestore::BackupFileHandler do include_context "shared stuff" def expect_decompress_and_clean_up_to_work(backup_filename:, expected_dump_filename: "dump.sql", - require_metadata_file:, require_uploads:, expected_upload_paths: nil) + require_metadata_file:, require_uploads:, expected_upload_paths: nil, + location: nil) freeze_time(DateTime.parse('2019-12-24 14:31:48')) @@ -18,7 +19,7 @@ describe BackupRestore::BackupFileHandler do Dir.mktmpdir do |root_directory| current_db = RailsMultisite::ConnectionManagement.current_db - file_handler = BackupRestore::BackupFileHandler.new(logger, backup_filename, current_db, root_directory) + file_handler = BackupRestore::BackupFileHandler.new(logger, backup_filename, current_db, root_directory, location) tmp_directory, db_dump_path = file_handler.decompress expected_tmp_path = File.join(root_directory, "tmp/restores", current_db, "2019-12-24-143148") @@ -101,4 +102,18 @@ describe BackupRestore::BackupFileHandler do end end end + + it "allows overriding the backup store" do + SiteSetting.s3_backup_bucket = "s3-backup-bucket" + SiteSetting.s3_access_key_id = "s3-access-key-id" + SiteSetting.s3_secret_access_key = "s3-secret-access-key" + SiteSetting.backup_location = BackupLocationSiteSetting::S3 + + expect_decompress_and_clean_up_to_work( + backup_filename: "backup_since_v1.6.tar.gz", + require_metadata_file: false, + require_uploads: true, + location: BackupLocationSiteSetting::LOCAL + ) + end end