From 4d3d218d95c2d96d6fd7153e645440e56fd1e9c6 Mon Sep 17 00:00:00 2001 From: Nick LaMuro Date: Wed, 4 Aug 2021 16:41:14 -0500 Subject: [PATCH] Remove EvmDatabaseOps and related rake tasks Well... mostly. A single (set of) method(s) is needed as the appliance_console uses this method to check the current database connection. This code could be moved into the console itself, but since it was released recently, this allows that code to still function until a new version not requiring it can be released. --- lib/evm_database_ops.rb | 243 --------------------- lib/tasks/evm_dba.rake | 102 --------- spec/lib/evm_database_ops_spec.rb | 342 ------------------------------ 3 files changed, 687 deletions(-) delete mode 100644 lib/evm_database_ops.rb delete mode 100644 spec/lib/evm_database_ops_spec.rb diff --git a/lib/evm_database_ops.rb b/lib/evm_database_ops.rb deleted file mode 100644 index 04928c35fb9..00000000000 --- a/lib/evm_database_ops.rb +++ /dev/null @@ -1,243 +0,0 @@ -$LOAD_PATH << File.expand_path(__dir__) -require 'util/postgres_admin' - -require 'mount/miq_generic_mount_session' -require 'util/miq_object_storage' - -class EvmDatabaseOps - include Vmdb::Logging - BACKUP_TMP_FILE = "/tmp/miq_backup".freeze - DUMP_TMP_FILE = "/tmp/miq_pg_dump".freeze - - DEFAULT_OPTS = {:dbname => 'vmdb_production'} - - def self.backup_destination_free_space(file_location) - require 'fileutils' - parent_directory = File.dirname(file_location) - FileUtils.mkdir_p(parent_directory) - - free_space = begin - output = AwesomeSpawn.run!("df", :params => {:P => parent_directory}, :combined_output => true).output - data_line = output.split("\n")[1] if output.kind_of?(String) - data_line.split[3].to_i * 1024 if data_line - end - - free_space || 0 - end - - def self.database_size(opts) - PostgresAdmin.database_size(opts) - end - - def self.validate_free_space(database_opts) - free_space = backup_destination_free_space(database_opts[:local_file]) - db_size = database_size(database_opts) - if free_space > db_size - _log.info("[#{database_opts[:dbname]}] with database size: [#{db_size} bytes], free space at [#{database_opts[:local_file]}]: [#{free_space} bytes]") - else - msg = "Destination location: [#{database_opts[:local_file]}], does not have enough free disk space: [#{free_space} bytes] for database of size: [#{db_size} bytes]" - _log.warn(msg) - defined?(::MiqEvent) && MiqEvent.raise_evm_event_queue(MiqServer.my_server, "evm_server_db_backup_low_space", :event_details => msg) - raise MiqException::MiqDatabaseBackupInsufficientSpace, msg - end - end - - def self.backup(db_opts, connect_opts = {}) - # db_opts: - # :dbname => 'vmdb_production', - # :username => 'root', - # :local_file => "/tmp/backup_1", - Backup locally to the file specified - - # connect_opts: - # :uri => "smb://dev005.manageiq.com/share1", - # :username => 'samba_one', - # :password => 'Zug-drep5s', - # :remote_file_name => "backup_1", - Provide a base file name for the uploaded file - - uri = with_file_storage(:backup, db_opts, connect_opts) do |database_opts| - backup_result = PostgresAdmin.backup(database_opts) - backup_result - end - _log.info("[#{merged_db_opts(db_opts)[:dbname]}] database has been backed up to file: [#{uri}]") - uri - end - - def self.dump(db_opts, connect_opts = {}) - # db_opts and connect_opts similar to .backup - - uri = with_file_storage(:dump, db_opts, connect_opts) do |database_opts| - PostgresAdmin.backup_pg_dump(database_opts) - end - _log.info("[#{merged_db_opts(db_opts)[:dbname]}] database has been dumped up to file: [#{uri}]") - uri - end - - def self.restore(db_opts, connect_opts = {}) - # db_opts: - # :local_file => "/tmp/backup_1", - Restore from this local file - # :dbname => 'vmdb_production' - # :username => 'root' - - # connect_opts: - # :uri => "smb://dev005.manageiq.com/share1/db_backup/miq_pg_backup_20100719_215444", - # :username => 'samba_one', - # :password => 'Zug-drep5s', - - uri = with_file_storage(:restore, db_opts, connect_opts) do |database_opts, backup_type| - prepare_for_restore(database_opts[:local_file], backup_type) - - # remove all the connections before we restore; AR will reconnect on the next query - ActiveRecord::Base.connection_pool.disconnect! - PostgresAdmin.restore(database_opts.merge(:backup_type => backup_type)) - end - _log.info("[#{merged_db_opts(db_opts)[:dbname]}] database has been restored from file: [#{uri}]") - uri - end - - private_class_method def self.merged_db_opts(db_opts) - DEFAULT_OPTS.merge(db_opts) - end - - STORAGE_ACTIONS_TO_METHODS = { :backup => :add, :dump => :add, :restore => :download }.freeze - private_class_method def self.with_file_storage(action, db_opts, connect_opts) - db_opts = merged_db_opts(db_opts) - - if db_opts[:local_file].nil? - if action == :restore - uri = connect_opts[:uri] - - connect_uri = URI::Generic.new(*URI.split(uri)) - connect_uri.path = File.dirname(connect_uri.path) - connect_opts[:uri] = connect_uri.to_s - else - connect_opts[:remote_file_name] ||= File.basename(backup_file_name(action)) - # - # If the passed in URI contains query parameters, ignore them - # when creating the dump file name. They'll be used in the session object. - # - uri_parts = [connect_opts[:uri].split('?')[0]] - uri_parts << (action == :dump ? "db_dump" : "db_backup") unless connect_opts[:skip_directory] - uri_parts << connect_opts[:remote_file_name] - uri = File.join(uri_parts) - end - else - uri = db_opts[:local_file] - - # HACK(ish): This just puts the bare minimum necessary for URI.parse to - # recognize the :uri option as "file" scheme, and allows MiqFileStorage - # to then instantiate MiqLocalMountSession below in the - # `.with_interface_class` method. - connect_opts[:uri] = "file://" - end - - MiqFileStorage.with_interface_class(connect_opts) do |file_storage| - send_args = [uri, db_opts[:byte_count]].compact - - if action == :restore - # `MiqFileStorage#download` requires a `nil` passed to the block form - # to accommodate streaming - send_args.unshift(nil) - magic_numbers = { - :pgdump => PostgresAdmin::PG_DUMP_MAGIC, - :basebackup => PostgresAdmin::BASE_BACKUP_MAGIC - } - backup_type = file_storage.magic_number_for(uri, :accepted => magic_numbers) - end - - # Note: `input_path` will always be a fifo stream (input coming from - # PostgresAdmin, and the output going to the `uri`), since we want to - # maintain the same interface for all backup types. - # - # This means that `uri` will always be the final destination, but - # `input_path` below will be an intermediary fifo that will take the - # input from `pg_dump`, `pg_restore`, or `pg_basebackup`, and streams the - # results from those commands (in ruby) it to whatever file storage - # endpoint `uri` is pointing to. - # - # This also makes sure that the streamed output is never written to disk - # locally, unless `uri` is targeting the local machine. This is why we - # set `db_opts` local file to that stream. - file_storage.send(STORAGE_ACTIONS_TO_METHODS[action], *send_args) do |input_path| - db_opts[:local_file] = input_path - if action == :restore - yield(db_opts, backup_type) - else - if file_storage.class <= MiqGenericMountSession - # Only check free space on "mountable" storages - # - # For database dumps, this isn't going to be as accurate (since the - # dump size will probably be larger than the calculated DB size), but - # it still won't hurt to do as a generic way to get a rough idea if - # we have enough disk space on the appliance for the task. - free_space_opts = db_opts.merge(:local_file => file_storage.uri_to_local_path(uri)) - validate_free_space(free_space_opts) - end - yield(db_opts) - end - end - end - - uri - end - - private_class_method def self.prepare_for_restore(filename, backup_type = nil) - backup_type ||= validate_backup_file_type(filename) - - if application_connections? - message = "Database restore failed. Shut down all evmserverd processes before attempting a database restore" - _log.error(message) - raise message - end - - MiqRegion.replication_type = :none - - connection_count = backup_type == :basebackup ? VmdbDatabaseConnection.unscoped.where(:backend_type => "client backend").count : VmdbDatabaseConnection.count - if connection_count > 1 - message = "Database restore failed. #{connection_count - 1} connections remain to the database." - _log.error(message) - raise message - end - end - - private_class_method def self.validate_backup_file_type(filename) - if PostgresAdmin.base_backup_file?(filename) - :basebackup - elsif PostgresAdmin.pg_dump_file?(filename) - :pgdump - else - message = "#{filename} is not in a recognized database backup format" - _log.error(message) - raise message - end - end - - private_class_method def self.application_connections? - VmdbDatabaseConnection.all.map(&:application_name).any? { |app_name| app_name.start_with?("MIQ") } - end - - def self.gc(options = {}) - PostgresAdmin.gc(options) - end - - def self.database_connections(database = nil, _type = :all) - database ||= ActiveRecord::Base.configurations[Rails.env]["database"] - conn = ActiveRecord::Base.connection - conn.client_connections.count { |c| c["database"] == database } - end - - def self.upload(connect_opts, local_file, destination_file) - MiqGenericMountSession.in_depot_session(connect_opts) { |session| session.upload(local_file, destination_file) } - destination_file - end - - def self.download(connect_opts, local_file) - MiqGenericMountSession.in_depot_session(connect_opts) { |session| session.download(local_file, connect_opts[:uri]) } - local_file - end - - def self.backup_file_name(action = :backup) - time_suffix = Time.now.utc.strftime("%Y%m%d_%H%M%S") - "#{action == :backup ? BACKUP_TMP_FILE : DUMP_TMP_FILE}_#{time_suffix}" - end - private_class_method :backup_file_name -end diff --git a/lib/tasks/evm_dba.rake b/lib/tasks/evm_dba.rake index b91fe3e2781..9892a495177 100644 --- a/lib/tasks/evm_dba.rake +++ b/lib/tasks/evm_dba.rake @@ -104,25 +104,6 @@ namespace :evm do Rake::Task['db:seed'].invoke end - desc "clean up database" - task :gc do - opts = EvmDba.with_options(:db_credentials) do - opt :aggressive, "Aggressive gc: vaccume with all options and reindexing" - opt :vacuum, "Vacuum database" - opt :reindex, "Reindex database (or table if --table specified)" - opt :analyze, "Vacuum with analyze" - opt :full, "Vacuum full" - opt :verbose, "Vacuum with verbose information printed" - - opt :table, "Tablename to reindex (if only perorm on one)", :type => :string - end - - opts = opts.delete_if { |_, v| v == false } - EvmDatabaseOps.gc(opts) - - exit # exit so that parameters to the first rake task are not run as rake tasks - end - desc "Destroys the ManageIQ EVM Database (VMDB) of all tables, views and indices" task :destroy do begin @@ -188,89 +169,6 @@ namespace :evm do exit # exit so that parameters to the first rake task are not run as rake tasks end - - # Example usage: - # bin/rake evm:db:backup:local -- --local-file /tmp/db_backup_test --dbname vmdb_production - # bin/rake evm:db:backup:remote -- --uri smb://dev005.manageiq.com/share1 --uri-username samba_one --uri-password "abc" --remote-file-name region1 - # bin/rake evm:db:restore:local -- --local-file /tmp/db_backup_test - # bin/rake evm:db:restore:remote -- --uri smb://dev005.manageiq.com/share1/db_backup/region1 --uri-username samba_one --uri-password "abc" - - namespace :backup do - require File.expand_path(File.join(Rails.root, "lib", "evm_database_ops")) - desc 'Backup the local ManageIQ EVM Database (VMDB) to a local file' - task :local do - opts = EvmDba.with_options(:local_file, :splitable, :db_credentials) - - EvmDatabaseOps.backup(opts) - - exit # exit so that parameters to the first rake task are not run as rake tasks - end - - desc 'Backup the local ManageIQ EVM Database (VMDB) to a remote file' - task :remote do - opts = EvmDba.with_options(:remote_uri, :aws, :remote_file, :splitable, :db_credentials) - - db_opts = EvmDba.collect_db_opts(opts) - connect_opts = EvmDba.collect_connect_opts(opts) - - EvmDatabaseOps.backup(db_opts, connect_opts) - - exit # exit so that parameters to the first rake task are not run as rake tasks - end - end - - namespace :dump do - require Rails.root.join("lib", "evm_database_ops").expand_path.to_s - desc 'Dump the local ManageIQ EVM Database (VMDB) to a local file' - task :local do - opts = EvmDba.with_options(:local_file, :splitable, :db_credentials, :exclude_table_data) - - EvmDatabaseOps.dump(opts) - - exit # exit so that parameters to the first rake task are not run as rake tasks - end - - desc 'Dump the local ManageIQ EVM Database (VMDB) to a remote file' - task :remote do - opts = EvmDba.with_options(:remote_uri, :aws, :remote_file, :splitable, :db_credentials, :exclude_table_data) - - db_opts = EvmDba.collect_db_opts(opts) - connect_opts = EvmDba.collect_connect_opts(opts) - - EvmDatabaseOps.dump(db_opts, connect_opts) - - exit # exit so that parameters to the first rake task are not run as rake tasks - end - end - - namespace :restore do - desc 'Restore the local ManageIQ EVM Database (VMDB) from a local backup file' - task :local => :environment do - opts = EvmDba.with_options(:local_file, :db_credentials) - - # If running through runner, disconnect any local connections - ActiveRecord::Base.clear_all_connections! if ActiveRecord && ActiveRecord::Base - - EvmDatabaseOps.restore(opts) - - exit # exit so that parameters to the first rake task are not run as rake tasks - end - - desc 'Restore the local ManageIQ EVM Database (VMDB) from a remote backup file' - task :remote => :environment do - opts = EvmDba.with_options(:remote_uri, :aws, :db_credentials) - - db_opts = EvmDba.collect_db_opts(opts) - connect_opts = EvmDba.collect_connect_opts(opts) - - # If running through runner, disconnect any local connections - ActiveRecord::Base.clear_all_connections! if ActiveRecord && ActiveRecord::Base - - EvmDatabaseOps.restore(db_opts, connect_opts) - - exit # exit so that parameters to the first rake task are not run as rake tasks - end - end end end diff --git a/spec/lib/evm_database_ops_spec.rb b/spec/lib/evm_database_ops_spec.rb deleted file mode 100644 index d43f88650b9..00000000000 --- a/spec/lib/evm_database_ops_spec.rb +++ /dev/null @@ -1,342 +0,0 @@ -RSpec.describe EvmDatabaseOps do - let(:file_storage) { double("MiqSmbSession", :disconnect => nil) } - let(:local_backup) { "/tmp/backup_1" } - let(:input_path) { "foo/bar/mkfifo" } - let(:run_db_ops) { @db_opts.dup.merge(:local_file => input_path) } - let(:tmpdir) { Rails.root.join("tmp") } - - before do - allow(MiqFileStorage).to receive(:with_interface_class).and_yield(file_storage) - end - - context "#backup" do - before do - @connect_opts = {:username => 'blah', :password => 'blahblah', :uri => "smb://myserver.com/share"} - @db_opts = {:username => 'root', :dbname => 'vmdb_production' } - allow(file_storage).to receive(:settings_mount_point).and_return(tmpdir.to_s) - allow(file_storage).to receive(:uri_to_local_path).and_return(tmpdir.join("share").to_s) - allow(file_storage).to receive(:add).and_yield(input_path) - - allow(FileUtils).to receive(:mv).and_return(true) - allow(EvmDatabaseOps).to receive(:backup_destination_free_space).and_return(200.megabytes) - allow(EvmDatabaseOps).to receive(:database_size).and_return(100.megabytes) - - MiqEvent # ensure the MiqEvent class is defined so we can verify raised events - end - - it "locally" do - @db_opts[:local_file] = local_backup - expect(PostgresAdmin).to receive(:backup).with(run_db_ops) - expect(EvmDatabaseOps.backup(@db_opts, @connect_opts)).to eq(local_backup) - end - - it "defaults" do - @db_opts[:local_file] = local_backup - expect(PostgresAdmin).to receive(:backup).with(run_db_ops) - expect(EvmDatabaseOps.backup(@db_opts, {})).to eq(local_backup) - end - - it "splits files with a local file" do - @db_opts[:local_file] = local_backup - @db_opts[:byte_count] = "200M" - - allow(file_storage).to receive(:send).with(:add, local_backup, "200M").and_yield(input_path) - expect(PostgresAdmin).to receive(:backup).with(run_db_ops) - EvmDatabaseOps.backup(@db_opts, {}) - end - - it "without enough free space" do - EvmSpecHelper.create_guid_miq_server_zone - allow(file_storage).to receive(:class).and_return(MiqSmbSession) - allow(EvmDatabaseOps).to receive(:backup_destination_free_space).and_return(100.megabytes) - allow(EvmDatabaseOps).to receive(:database_size).and_return(200.megabytes) - expect { EvmDatabaseOps.backup(@db_opts, @connect_opts) }.to raise_error(MiqException::MiqDatabaseBackupInsufficientSpace) - expect(MiqQueue.where(:class_name => "MiqEvent", :method_name => "raise_evm_event").count).to eq(1) - end - - it "remotely" do - @db_opts[:local_file] = nil - @connect_opts[:remote_file_name] = "custom_backup" - expect(PostgresAdmin).to receive(:backup).with(run_db_ops) - expect(EvmDatabaseOps.backup(@db_opts, @connect_opts)).to eq("smb://myserver.com/share/db_backup/custom_backup") - end - - it "remotely without a remote file name" do - @db_opts[:local_file] = nil - @connect_opts[:remote_file_name] = nil - expect(PostgresAdmin).to receive(:backup).with(run_db_ops) - expect(EvmDatabaseOps.backup(@db_opts, @connect_opts)).to match(/smb:\/\/myserver.com\/share\/db_backup\/miq_backup_.*/) - end - - it "properly logs the result with no :dbname provided" do - @db_opts.delete(:dbname) - @db_opts[:local_file] = nil - @connect_opts[:remote_file_name] = nil - run_db_ops[:dbname] = "vmdb_production" - allow(described_class).to receive(:backup_file_name).and_return("miq_backup") - expect(PostgresAdmin).to receive(:backup).with(run_db_ops) - - allow(file_storage).to receive(:class).and_return(MiqSmbSession) - - log_stub = instance_double("_log") - expect(described_class).to receive(:_log).twice.and_return(log_stub) - expect(log_stub).to receive(:info).with(any_args) - expect(log_stub).to receive(:info).with("[vmdb_production] database has been backed up to file: [smb://myserver.com/share/db_backup/miq_backup]") - - EvmDatabaseOps.backup(@db_opts, @connect_opts) - end - end - - context "#dump" do - let(:local_dump) { "/tmp/dump_1" } - before do - @connect_opts = {:username => 'blah', :password => 'blahblah', :uri => "smb://myserver.com/share"} - @db_opts = {:dbname => 'vmdb_production', :username => 'root'} - allow(file_storage).to receive(:settings_mount_point).and_return(tmpdir) - allow(file_storage).to receive(:uri_to_local_path).and_return(tmpdir.join("share").to_s) - allow(file_storage).to receive(:add).and_yield(input_path) - - allow(PostgresAdmin).to receive(:run_command_with_logging) - allow(FileUtils).to receive(:mv).and_return(true) - allow(EvmDatabaseOps).to receive(:backup_destination_free_space).and_return(200.megabytes) - allow(EvmDatabaseOps).to receive(:database_size).and_return(100.megabytes) - - MiqEvent # ensure the MiqEvent class is defined so we can verify raised events - end - - it "locally" do - @db_opts[:local_file] = local_dump - expect(PostgresAdmin).to receive(:backup_pg_dump).with(run_db_ops) - expect(EvmDatabaseOps.dump(@db_opts, @connect_opts)).to eq(local_dump) - end - - it "defaults" do - @db_opts[:local_file] = local_dump - expect(PostgresAdmin).to receive(:backup_pg_dump).with(run_db_ops) - expect(EvmDatabaseOps.dump(@db_opts, {})).to eq(local_dump) - end - - it "splits files with a local file" do - @db_opts[:local_file] = local_dump - @db_opts[:byte_count] = "200M" - - allow(file_storage).to receive(:send).with(:add, local_dump, "200M").and_yield(input_path) - expect(PostgresAdmin).to receive(:backup_pg_dump).with(run_db_ops) - EvmDatabaseOps.dump(@db_opts, {}) - end - - it "without enough free space" do - EvmSpecHelper.create_guid_miq_server_zone - allow(file_storage).to receive(:class).and_return(MiqSmbSession) - allow(EvmDatabaseOps).to receive(:backup_destination_free_space).and_return(100.megabytes) - allow(EvmDatabaseOps).to receive(:database_size).and_return(200.megabytes) - expect(PostgresAdmin).to receive(:backup_pg_dump).never - expect { EvmDatabaseOps.dump(@db_opts, @connect_opts) }.to raise_error(MiqException::MiqDatabaseBackupInsufficientSpace) - expect(MiqQueue.where(:class_name => "MiqEvent", :method_name => "raise_evm_event").count).to eq(1) - end - - it "remotely" do - @db_opts[:local_file] = nil - @connect_opts[:remote_file_name] = "custom_pg_dump" - expect(PostgresAdmin).to receive(:backup_pg_dump).with(run_db_ops) - expect(EvmDatabaseOps.dump(@db_opts, @connect_opts)).to eq("smb://myserver.com/share/db_dump/custom_pg_dump") - end - - it "remotely without a remote file name" do - @db_opts[:local_file] = nil - @connect_opts[:remote_file_name] = nil - expect(PostgresAdmin).to receive(:backup_pg_dump).with(run_db_ops) - expect(EvmDatabaseOps.dump(@db_opts, @connect_opts)).to match(/smb:\/\/myserver.com\/share\/db_dump\/miq_pg_dump_.*/) - end - end - - context "#restore" do - before do - @connect_opts = {:username => 'blah', :password => 'blahblah'} - @db_opts = {:dbname => 'vmdb_production', :username => 'root'} - - allow(file_storage).to receive(:settings_mount_point).and_return(tmpdir) - allow(file_storage).to receive(:magic_number_for).and_return(:pgdump) - allow(file_storage).to receive(:download).and_yield(input_path) - - allow(PostgresAdmin).to receive(:run_command_with_logging) - - allow(VmdbDatabaseConnection).to receive(:count).and_return(1) - end - - it "from local backup" do - @db_opts[:local_file] = local_backup - expect(EvmDatabaseOps.restore(@db_opts, @connect_opts)).to eq(local_backup) - end - - it "from smb backup" do - @db_opts[:local_file] = nil - remote_backup = "smb://myserver.com/share/pg_backup1.backup" - @connect_opts[:uri] = remote_backup - expect(EvmDatabaseOps.restore(@db_opts, @connect_opts)).to eq(remote_backup) - end - - it "properly logs the result with no :dbname provided" do - @db_opts.delete(:dbname) - @db_opts[:local_file] = nil - remote_backup = "smb://myserver.com/share/pg_backup1.backup" - @connect_opts[:uri] = remote_backup - - log_stub = instance_double("_log") - expect(described_class).to receive(:_log).and_return(log_stub) - expect(log_stub).to receive(:info).with("[vmdb_production] database has been restored from file: [smb://myserver.com/share/pg_backup1.backup]") - - EvmDatabaseOps.restore(@db_opts, @connect_opts) - end - end - - describe "with_file_storage (private method)" do - let(:db_opts) { {} } - let(:connect_opts) { {} } - - # convenience_wrapper for private method - def execute_with_file_storage(action = :backup) - described_class.send(:with_file_storage, action, db_opts, connect_opts) do |dbopts| - yield dbopts if block_given? - end - end - - shared_examples "default with_file_storage behaviors" do - it "sets dbopts[:local_file] to the input_path" do - execute_with_file_storage do |dbopts| - expect(dbopts[:local_file]).to eq(input_path) - end - end - - it "updates db_opts for the block to set the :dbname" do - execute_with_file_storage do |dbopts| - expect(dbopts[:dbname]).to eq("vmdb_production") - end - end - - context "db_opts[:dbname] is set" do - it "does not update :dbname if passed" do - db_opts[:dbname] = "my_db" - - execute_with_file_storage do |dbopts| - expect(dbopts[:dbname]).to eq("my_db") - end - end - end - end - - context "with a local file" do - let(:db_opts) { { :local_file => "/tmp/foo" } } - - before { expect(file_storage).to receive(:add).and_yield(input_path) } - - include_examples "default with_file_storage behaviors" - - it "always uses a file_storage interface" do - execute_with_file_storage do - expect(file_storage).to receive(:test_method) - file_storage.test_method - end - end - - it "does not try to close a session" do - expect(file_storage).to_not receive(:disconnect) - - execute_with_file_storage - end - - it "updates the db_opts[:local_file] to the file_storage fifo" do - execute_with_file_storage do |dbopts| - expect(dbopts[:local_file]).to eq(input_path) - end - end - - it "returns the result of the block" do - expect(execute_with_file_storage { "block result" }).to eq(db_opts[:local_file]) - end - end - - context "without a local file" do - let(:connect_opts) { { :uri => "smb://tmp/foo" } } - - before { allow(file_storage).to receive(:add).and_yield(input_path) } - - include_examples "default with_file_storage behaviors" - - context "for a backup-ish action" do - let(:backup_file) { "/tmp/bar/baz" } - - before { allow(described_class).to receive(:backup_file_name).and_return(backup_file) } - - it "updates db_opts[:local_file] in the method context" do - expected_uri = "smb://tmp/foo/db_backup/baz" - - expect(file_storage).to receive(:send).with(:add, expected_uri) - execute_with_file_storage - end - - it "respects user passed in connect_opts[:remote_file_name]" do - expected_uri = "smb://tmp/foo/db_backup/my_dir/my_backup" - connect_opts[:remote_file_name] = "/my_dir/my_backup" - - expect(file_storage).to receive(:send).with(:add, expected_uri) - execute_with_file_storage - end - - it "returns calculated uri" do - expect(execute_with_file_storage { "block result" }).to eq("smb://tmp/foo/db_backup/baz") - end - - it "yields `db_opt`s only" do - allow(file_storage).to receive(:download) { |&block| block.call(input_path) } - expect do |rspec_probe| - described_class.send(:with_file_storage, :backup, db_opts, connect_opts, &rspec_probe) - end.to yield_with_args(:dbname => "vmdb_production", :local_file => input_path) - end - end - - context "for a restore action" do - before { expect(file_storage).to receive(:magic_number_for).and_return(:pgdump) } - - it "updates db_opts[:local_file] in the method context" do - expect(file_storage).to receive(:send).with(:download, nil, "smb://tmp/foo") - execute_with_file_storage(:restore) - end - - it "parses the dirname of the `uri` and passes that in `connect_opts`" do - expected_connect_opts = { :uri => "smb://tmp/" } - allow(file_storage).to receive(:download) - expect(MiqFileStorage).to receive(:with_interface_class).with(expected_connect_opts) - execute_with_file_storage(:restore) - end - - it "returns calculated uri" do - allow(file_storage).to receive(:download).and_yield(input_path) - expect(execute_with_file_storage(:restore) { "block result" }).to eq("smb://tmp/foo") - end - - it "yields `backup_type` along with `db_opt`s" do - allow(file_storage).to receive(:download) { |&block| block.call(input_path) } - expected_yield_args = [ - { :dbname => "vmdb_production", :local_file => input_path }, - :pgdump - ] - expect do |rspec_probe| - described_class.send(:with_file_storage, :restore, db_opts, connect_opts, &rspec_probe) - end.to yield_with_args(*expected_yield_args) - end - - context "with query_params in the URI" do - let(:connect_opts) { { :uri => "swift://container/foo.gz?2plus2=5" } } - - it "retains query_params when parsing dirname" do - expected_connect_opts = { :uri => "swift://container/?2plus2=5" } - allow(file_storage).to receive(:download) - expect(MiqFileStorage).to receive(:with_interface_class).with(expected_connect_opts) - execute_with_file_storage(:restore) - end - end - end - end - end -end