From 32383ae517039b2ab0813a4f9592713797504771 Mon Sep 17 00:00:00 2001 From: alvir Date: Sun, 10 May 2020 00:16:36 +0300 Subject: [PATCH 1/8] Add support more than one DB. --- .ruby-version | 1 + .travis.yml | 7 +++++-- dump-hook.gemspec | 6 +++--- lib/dump_hook.rb | 30 ++++++++++++++++++++++++---- spec/dump_hook_spec.rb | 45 +++++++++++++++++++++++++++++++++++++++--- 5 files changed, 77 insertions(+), 12 deletions(-) create mode 100644 .ruby-version diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..860487c --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.7.1 diff --git a/.travis.yml b/.travis.yml index 772ca77..eebff45 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,8 @@ sudo: false language: ruby rvm: - - 2.2.3 -before_install: gem install bundler -v 1.12.2 + - 2.4.4 + - 2.5.5 + - 2.6.2 + - 2.7.1 +script: bundle exec rspec diff --git a/dump-hook.gemspec b/dump-hook.gemspec index 8f4cff4..d301eee 100644 --- a/dump-hook.gemspec +++ b/dump-hook.gemspec @@ -29,9 +29,9 @@ Gem::Specification.new do |spec| spec.add_dependency 'timecop' - spec.add_development_dependency "bundler", "~> 1.12" - spec.add_development_dependency "rake", "~> 10.0" - spec.add_development_dependency "rspec", "~> 3.0" + spec.add_development_dependency "bundler" + spec.add_development_dependency "rake", ">= 12.3.3" + spec.add_development_dependency "rspec" spec.add_development_dependency "sequel" spec.add_development_dependency "pg" spec.add_development_dependency "mysql2" diff --git a/lib/dump_hook.rb b/lib/dump_hook.rb index fd109c1..6db8a82 100644 --- a/lib/dump_hook.rb +++ b/lib/dump_hook.rb @@ -11,13 +11,15 @@ class Settings :username, :password, :host, - :port + :port, + :sources def initialize @database = 'please set database' @database_type = 'postgres' @dumps_location = 'tmp/dump_hook' @remove_old_dumps = true + @sources = {} end end @@ -35,7 +37,7 @@ def execute_with_dump(name, opts={}, &block) actual = opts[:actual] || settings.actual create_dirs_if_not_exists filename = full_filename(name, created_on, actual) - if File.exists?(filename) + if File.exist?(filename) restore_dump(filename) else if created_on @@ -54,7 +56,8 @@ def settings end def store_dump(filename) - case settings.database_type + if settings.sources.empty? + case settings.database_type when 'postgres' args = ['-a', '-x', '-O', '-f', filename, '-Fc', '-T', 'schema_migrations'] args.concat(pg_connection_args) @@ -65,6 +68,24 @@ def store_dump(filename) args.concat ["--result-file", filename] args.concat ["--ignore-table", "#{settings.database}.schema_migrations"] Kernel.system("mysqldump", *args) + end + else + FileUtils.mkdir_p(filename) + settings.sources.each do |name, parameters| + filename_with_namespace = File.join(filename, "#{name}.dump") + case parameters[:type] + when :postgres + args = ['-a', '-x', '-O', '-f', filename_with_namespace, '-Fc', '-T', 'schema_migrations'] + args.concat(['-d', parameters[:database]]) + Kernel.system("pg_dump", *args) + when :mysql + args = [parameters[:database], "--user", parameters[:username]] + args << "--compress" + args.concat(["--result-file", filename_with_namespace]) + args.concat(["--ignore-table", "#{parameters[:database]}.schema_migrations"]) + Kernel.system("mysqldump", *args) + end + end end end @@ -88,7 +109,8 @@ def full_filename(name, created_on, actual) elsif actual name_with_created_on = "#{name_with_created_on}_actual#{actual}" end - "#{settings.dumps_location}/#{name_with_created_on}.dump" + full_path = "#{settings.dumps_location}/#{name_with_created_on}" + settings.sources.empty? ? "#{full_path}.dump" : full_path end def create_dirs_if_not_exists diff --git a/spec/dump_hook_spec.rb b/spec/dump_hook_spec.rb index 73731de..e4676f5 100644 --- a/spec/dump_hook_spec.rb +++ b/spec/dump_hook_spec.rb @@ -77,7 +77,7 @@ end describe '.execute_with_dump' do - let(:object) { object = Object.new } + let(:object) { Object.new } before(:each) do object.extend(DumpHook) @@ -98,7 +98,7 @@ it 'creates folders' do object.execute_with_dump("some_dump") { } - expect(Dir.exists?(dumps_location)).to be(true) + expect(Dir.exist?(dumps_location)).to be(true) end end @@ -122,7 +122,7 @@ it 'creates dump file' do object.execute_with_dump("some_dump") { } - expect(File.exists?("tmp/dump_hook/some_dump.dump")).to be(true) + expect(File.exist?("tmp/dump_hook/some_dump.dump")).to be(true) end context 'dump content' do @@ -191,5 +191,44 @@ end end end + + context "multi DBs" do + let(:mysql_db_name) { "mysql_dump_hook_test"} + let(:postgres_db_name) { "postgres_dump_hook_test"} + let(:mysql_username) { "root" } + + let(:mysql_db) { Sequel.connect(adapter: 'mysql2', user: mysql_username) } + let(:postgres_db) { Sequel.connect(adapter: 'postgres', database: postgres_db_name) } + + before(:each) do + mysql_db.run("CREATE DATABASE #{mysql_db_name}") + mysql_db.run("USE #{mysql_db_name}") + + Kernel.system("createdb", postgres_db_name) + FileUtils.mkdir_p("tmp") + + DumpHook.setup do |c| + c.sources = { primary: { type: :postgres, database: postgres_db_name }, + secondary: { type: :mysql, username: mysql_username, database: mysql_db_name } } + end + end + + after(:each) do + postgres_db.disconnect + Kernel.system("dropdb", postgres_db_name) + + mysql_db.run("DROP DATABASE #{mysql_db_name}") + mysql_db.disconnect + + FileUtils.rm_r('tmp') + end + + it "creates dump files" do + object.execute_with_dump("some_dump") { } + expect(File.exist?("tmp/dump_hook/some_dump/primary.dump")).to be(true) + expect(File.exist?("tmp/dump_hook/some_dump/secondary.dump")).to be(true) + end + end + end end From c90186e3ff82e6b4ab4abe6fa6b63a3016f559a5 Mon Sep 17 00:00:00 2001 From: alvir Date: Thu, 21 May 2020 19:13:33 +0300 Subject: [PATCH 2/8] Cleanup specs. --- spec/dump_hook_spec.rb | 138 +++++++++++++++++++++-------------------- 1 file changed, 70 insertions(+), 68 deletions(-) diff --git a/spec/dump_hook_spec.rb b/spec/dump_hook_spec.rb index e4676f5..35bad3a 100644 --- a/spec/dump_hook_spec.rb +++ b/spec/dump_hook_spec.rb @@ -102,21 +102,67 @@ end end - context 'postgres' do - let(:database) { 'dump_hook_test' } - let(:db) { Sequel.connect(adapter: 'postgres', database: database) } + shared_context "mysql db init" do + let(:mysql_db_name) { 'dump_hook_test' } + let(:mysql_username) { 'root' } + let(:mysql_db) { Sequel.connect(adapter: 'mysql2', user: mysql_username) } + + before(:each) do + mysql_db.run("CREATE DATABASE #{mysql_db_name}") + mysql_db.run("USE #{mysql_db_name}") + end + + after(:each) do + mysql_db.run("DROP DATABASE #{mysql_db_name}") + + mysql_db.disconnect + end + end + + shared_context "postgres db init" do + let(:postgres_db_name) { 'dump_hook_test' } + let(:postgres_db) { Sequel.connect(adapter: 'postgres', database: postgres_db_name) } before(:each) do - Kernel.system('createdb', database) + Kernel.system('createdb', postgres_db_name) + end + + after(:each) do + postgres_db.disconnect + Kernel.system("dropdb", postgres_db_name) + end + end + + shared_examples_for 'data insertion and restoring' do + before(:each) do + object.execute_with_dump("some_dump") do + db.run("create table t (a text, b text)") + db.run("insert into t values ('a', 'b')") + end + end + + it 'inserts some info' do + expect(db[:t].map([:a, :b])).to eq([['a', 'b']]) + end + + it 'uses dump content if dump exists' do + db.run("delete from t") + object.execute_with_dump("some_dump") { } + expect(db[:t].map([:a, :b])).to eq([['a', 'b']]) + end + end + + context 'postgres' do + include_context "postgres db init" + let(:db) { postgres_db } + before(:each) do DumpHook.setup do |c| - c.database = database + c.database = postgres_db_name end end after(:each) do - db.disconnect - Kernel.system('dropdb', database) FileUtils.rm_r('tmp') end @@ -125,45 +171,23 @@ expect(File.exist?("tmp/dump_hook/some_dump.dump")).to be(true) end - context 'dump content' do - before(:each) do - object.execute_with_dump("some_dump") do - db.run("create table t (a text, b text)") - db.run("insert into t values ('a', 'b')") - end - end - - it 'inserts some info' do - expect(db[:t].map([:a, :b])).to eq([['a', 'b']]) - end - - it 'uses dump content if dump exists' do - db.run("delete from t") - object.execute_with_dump("some_dump") { } - expect(db[:t].map([:a, :b])).to eq([['a', 'b']]) - end + it_behaves_like "data insertion and restoring" do + let(:db) { postgres_db } end end context 'mysql' do - let(:database) { 'dump_hook_test' } - let(:username) { 'root' } - let(:db) { Sequel.connect(adapter: 'mysql2', user: username) } + include_context "mysql db init" before(:each) do - db.run("CREATE DATABASE #{database}") - db.run("USE #{database}") DumpHook.setup do |c| - c.database = database + c.database = mysql_db_name c.database_type = 'mysql' - c.username = username + c.username = mysql_username end end after(:each) do - db.run("DROP DATABASE #{database}") - - db.disconnect FileUtils.rm_r('tmp') end @@ -172,39 +196,16 @@ expect(File.exists?("tmp/dump_hook/some_dump.dump")).to be(true) end - context 'dump content' do - before(:each) do - object.execute_with_dump("some_dump") do - db.run("create table t (a text, b text)") - db.run("insert into t values ('a', 'b')") - end - end - - it 'inserts some info' do - expect(db[:t].map([:a, :b])).to eq([['a', 'b']]) - end - - it 'uses dump content if dump exists' do - db.run("delete from t") - object.execute_with_dump("some_dump") { } - expect(db[:t].map([:a, :b])).to eq([['a', 'b']]) - end + it_behaves_like "data insertion and restoring" do + let(:db) { mysql_db } end end context "multi DBs" do - let(:mysql_db_name) { "mysql_dump_hook_test"} - let(:postgres_db_name) { "postgres_dump_hook_test"} - let(:mysql_username) { "root" } - - let(:mysql_db) { Sequel.connect(adapter: 'mysql2', user: mysql_username) } - let(:postgres_db) { Sequel.connect(adapter: 'postgres', database: postgres_db_name) } + include_context "postgres db init" + include_context "mysql db init" before(:each) do - mysql_db.run("CREATE DATABASE #{mysql_db_name}") - mysql_db.run("USE #{mysql_db_name}") - - Kernel.system("createdb", postgres_db_name) FileUtils.mkdir_p("tmp") DumpHook.setup do |c| @@ -214,12 +215,6 @@ end after(:each) do - postgres_db.disconnect - Kernel.system("dropdb", postgres_db_name) - - mysql_db.run("DROP DATABASE #{mysql_db_name}") - mysql_db.disconnect - FileUtils.rm_r('tmp') end @@ -228,7 +223,14 @@ expect(File.exist?("tmp/dump_hook/some_dump/primary.dump")).to be(true) expect(File.exist?("tmp/dump_hook/some_dump/secondary.dump")).to be(true) end - end + it_behaves_like "data insertion and restoring" do + let(:db) { mysql_db } + end + + it_behaves_like "data insertion and restoring" do + let(:db) { postgres_db } + end + end end end From ee80ecf744203f21b04a1c95f1111e39a976c0b4 Mon Sep 17 00:00:00 2001 From: alvir Date: Thu, 21 May 2020 19:13:59 +0300 Subject: [PATCH 3/8] Extract dumpers. --- lib/dump_hook.rb | 101 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 29 deletions(-) diff --git a/lib/dump_hook.rb b/lib/dump_hook.rb index 6db8a82..450601e 100644 --- a/lib/dump_hook.rb +++ b/lib/dump_hook.rb @@ -23,13 +23,48 @@ def initialize end end + module Dumpers + class Base + def initialize(connection_settings) + @connection_settings = connection_settings + end + end + + class Postgres < Base + def dump(filename) + args = ['-a', '-x', '-O', '-f', filename, '-Fc', '-T', 'schema_migrations'] + args.concat(['-d', @connection_settings.database]) + Kernel.system("pg_dump", *args) + end + + def restore + + end + end + + class MySql < Base + def dump(filename) + args = [@connection_settings.database, "--user", @connection_settings.username] + args << "--compress" + args.concat(["--result-file", filename]) + args.concat(["--ignore-table", "#{@connection_settings.database}.schema_migrations"]) + args << "--password=#{@connection_settings.password}" if @connection_settings.password + Kernel.system("mysqldump", *args) + end + + def restore + + end + end + end + class << self attr_accessor :settings - end - def self.setup - self.settings = Settings.new - yield(settings) + def setup + self.settings = Settings.new + yield(settings) + end end def execute_with_dump(name, opts={}, &block) @@ -57,40 +92,32 @@ def settings def store_dump(filename) if settings.sources.empty? - case settings.database_type - when 'postgres' - args = ['-a', '-x', '-O', '-f', filename, '-Fc', '-T', 'schema_migrations'] - args.concat(pg_connection_args) - Kernel.system("pg_dump", *args) - when 'mysql' - args = mysql_connection_args - args << "--compress" - args.concat ["--result-file", filename] - args.concat ["--ignore-table", "#{settings.database}.schema_migrations"] - Kernel.system("mysqldump", *args) - end + dumper = case settings.database_type + when "postgres" + Dumpers::Postgres.new(settings) + when "mysql" + Dumpers::MySql.new(settings) + end + dumper.dump(filename) else FileUtils.mkdir_p(filename) settings.sources.each do |name, parameters| filename_with_namespace = File.join(filename, "#{name}.dump") - case parameters[:type] - when :postgres - args = ['-a', '-x', '-O', '-f', filename_with_namespace, '-Fc', '-T', 'schema_migrations'] - args.concat(['-d', parameters[:database]]) - Kernel.system("pg_dump", *args) - when :mysql - args = [parameters[:database], "--user", parameters[:username]] - args << "--compress" - args.concat(["--result-file", filename_with_namespace]) - args.concat(["--ignore-table", "#{parameters[:database]}.schema_migrations"]) - Kernel.system("mysqldump", *args) - end + connection_settings = OpenStruct.new(parameters.slice(:database, :username)) + dumper = case parameters[:type] + when :postgres + Dumpers::Postgres.new(connection_settings) + when :mysql + Dumpers::MySql.new(connection_settings) + end + dumper.dump(filename_with_namespace) end end end def restore_dump(filename) - case settings.database_type + if settings.sources.empty? + case settings.database_type when 'postgres' args = pg_connection_args args << filename @@ -99,6 +126,22 @@ def restore_dump(filename) args = mysql_connection_args args.concat ["-e", "source #{filename}"] Kernel.system("mysql", *args) + end + else + FileUtils.mkdir_p(filename) + settings.sources.each do |name, parameters| + filename_with_namespace = File.join(filename, "#{name}.dump") + case parameters[:type] + when :postgres + args = ['-d', parameters[:database]] + args << filename_with_namespace + Kernel.system("pg_restore", *args) + when :mysql + args = [parameters[:database], "--user", parameters[:username]] + args.concat ["-e", "source #{filename_with_namespace}"] + Kernel.system("mysql", *args) + end + end end end From 52ff8e119f250ff4728ffab739831d0f6ec610f7 Mon Sep 17 00:00:00 2001 From: alvir Date: Thu, 21 May 2020 19:45:51 +0300 Subject: [PATCH 4/8] Cleanup restoring. --- lib/dump_hook.rb | 73 +++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 41 deletions(-) diff --git a/lib/dump_hook.rb b/lib/dump_hook.rb index 450601e..32718d1 100644 --- a/lib/dump_hook.rb +++ b/lib/dump_hook.rb @@ -34,26 +34,37 @@ class Postgres < Base def dump(filename) args = ['-a', '-x', '-O', '-f', filename, '-Fc', '-T', 'schema_migrations'] args.concat(['-d', @connection_settings.database]) + args.concat(['-h', @connection_settings.host]) if @connection_settings.host + args.concat(['-p', @connection_settings.port]) if @connection_settings.port Kernel.system("pg_dump", *args) end - def restore - + def restore(filename) + args = ['-d', @connection_settings.database] + args.concat(['-h', @connection_settings.host]) if @connection_settings.host + args.concat(['-p', @connection_settings.port]) if @connection_settings.port + args << filename + Kernel.system("pg_restore", *args) end end class MySql < Base def dump(filename) - args = [@connection_settings.database, "--user", @connection_settings.username] + args = [@connection_settings.database] args << "--compress" args.concat(["--result-file", filename]) args.concat(["--ignore-table", "#{@connection_settings.database}.schema_migrations"]) + args.concat ['--user', @connection_settings.username] if @connection_settings.username args << "--password=#{@connection_settings.password}" if @connection_settings.password Kernel.system("mysqldump", *args) end - def restore - + def restore(filename) + args = [@connection_settings.database] + args.concat ["-e", "source #{filename}"] + args.concat ['--user', @connection_settings.username] if @connection_settings.username + args << "--password=#{@connection_settings.password}" if @connection_settings.password + Kernel.system("mysql", *args) end end end @@ -103,7 +114,7 @@ def store_dump(filename) FileUtils.mkdir_p(filename) settings.sources.each do |name, parameters| filename_with_namespace = File.join(filename, "#{name}.dump") - connection_settings = OpenStruct.new(parameters.slice(:database, :username)) + connection_settings = OpenStruct.new(parameters.slice(:database, :username, :password)) dumper = case parameters[:type] when :postgres Dumpers::Postgres.new(connection_settings) @@ -117,30 +128,25 @@ def store_dump(filename) def restore_dump(filename) if settings.sources.empty? - case settings.database_type - when 'postgres' - args = pg_connection_args - args << filename - Kernel.system("pg_restore", *args) - when 'mysql' - args = mysql_connection_args - args.concat ["-e", "source #{filename}"] - Kernel.system("mysql", *args) - end + dumper = case settings.database_type + when 'postgres' + Dumpers::Postgres.new(settings) + when 'mysql' + Dumpers::MySql.new(settings) + end + dumper.restore(filename) else FileUtils.mkdir_p(filename) settings.sources.each do |name, parameters| filename_with_namespace = File.join(filename, "#{name}.dump") - case parameters[:type] - when :postgres - args = ['-d', parameters[:database]] - args << filename_with_namespace - Kernel.system("pg_restore", *args) - when :mysql - args = [parameters[:database], "--user", parameters[:username]] - args.concat ["-e", "source #{filename_with_namespace}"] - Kernel.system("mysql", *args) - end + connection_settings = OpenStruct.new(parameters.slice(:database, :username, :password)) + dumper = case parameters[:type] + when :postgres + Dumpers::Postgres.new(connection_settings) + when :mysql + Dumpers::MySql.new(connection_settings) + end + dumper.restore(filename_with_namespace) end end end @@ -159,19 +165,4 @@ def full_filename(name, created_on, actual) def create_dirs_if_not_exists FileUtils.mkdir_p(settings.dumps_location) end - - def mysql_connection_args - args = [settings.database] - args.concat ['--user', settings.username] if settings.username - args << "--password=#{settings.password}" if settings.password - args - end - - def pg_connection_args - args = ['-d', settings.database] - args.concat(['-U', settings.username]) if settings.username - args.concat(['-h', settings.host]) if settings.host - args.concat(['-p', settings.port]) if settings.port - args - end end From fbfe23a1163dc16000ffaa06b14a1082a08a26f8 Mon Sep 17 00:00:00 2001 From: alvir Date: Thu, 4 Jun 2020 13:06:57 +0300 Subject: [PATCH 5/8] Extract Hooks module. --- lib/dump_hook.rb | 130 +++++++++-------------------- lib/dump_hook/hooks/connectable.rb | 9 ++ lib/dump_hook/hooks/mysql.rb | 27 ++++++ lib/dump_hook/hooks/postgres.rb | 25 ++++++ spec/dump_hook_spec.rb | 7 +- 5 files changed, 103 insertions(+), 95 deletions(-) create mode 100644 lib/dump_hook/hooks/connectable.rb create mode 100644 lib/dump_hook/hooks/mysql.rb create mode 100644 lib/dump_hook/hooks/postgres.rb diff --git a/lib/dump_hook.rb b/lib/dump_hook.rb index 32718d1..45e7789 100644 --- a/lib/dump_hook.rb +++ b/lib/dump_hook.rb @@ -1,5 +1,8 @@ require "dump_hook/version" +require "dump_hook/hooks/mysql" +require "dump_hook/hooks/postgres" require "timecop" +require "ostruct" module DumpHook class Settings @@ -12,69 +15,37 @@ class Settings :password, :host, :port, - :sources + :sources, + :with_sources def initialize - @database = 'please set database' @database_type = 'postgres' @dumps_location = 'tmp/dump_hook' @remove_old_dumps = true + @with_sources = false @sources = {} end end - module Dumpers - class Base - def initialize(connection_settings) - @connection_settings = connection_settings - end - end - - class Postgres < Base - def dump(filename) - args = ['-a', '-x', '-O', '-f', filename, '-Fc', '-T', 'schema_migrations'] - args.concat(['-d', @connection_settings.database]) - args.concat(['-h', @connection_settings.host]) if @connection_settings.host - args.concat(['-p', @connection_settings.port]) if @connection_settings.port - Kernel.system("pg_dump", *args) - end - - def restore(filename) - args = ['-d', @connection_settings.database] - args.concat(['-h', @connection_settings.host]) if @connection_settings.host - args.concat(['-p', @connection_settings.port]) if @connection_settings.port - args << filename - Kernel.system("pg_restore", *args) - end - end - - class MySql < Base - def dump(filename) - args = [@connection_settings.database] - args << "--compress" - args.concat(["--result-file", filename]) - args.concat(["--ignore-table", "#{@connection_settings.database}.schema_migrations"]) - args.concat ['--user', @connection_settings.username] if @connection_settings.username - args << "--password=#{@connection_settings.password}" if @connection_settings.password - Kernel.system("mysqldump", *args) - end - - def restore(filename) - args = [@connection_settings.database] - args.concat ["-e", "source #{filename}"] - args.concat ['--user', @connection_settings.username] if @connection_settings.username - args << "--password=#{@connection_settings.password}" if @connection_settings.password - Kernel.system("mysql", *args) - end - end - end - class << self attr_accessor :settings + attr_accessor :hooks def setup self.settings = Settings.new yield(settings) + unless settings.sources.empty? + settings.with_sources = true + end + unless settings.database.nil? + single_source = { type: settings.database_type.to_sym, + database: settings.database, + username: settings.username, + password: settings.password, + host: settings.host, + port: settings.port } + self.settings.sources[settings.database_type.to_sym] = single_source + end end end @@ -102,52 +73,32 @@ def settings end def store_dump(filename) - if settings.sources.empty? - dumper = case settings.database_type - when "postgres" - Dumpers::Postgres.new(settings) - when "mysql" - Dumpers::MySql.new(settings) + FileUtils.mkdir_p(filename) + settings.sources.each do |name, parameters| + filename_with_namespace = File.join(filename, "#{name}.dump") + connection_settings = OpenStruct.new(parameters.slice(:database, :username, :password)) + dumper = case parameters[:type] + when :postgres + Hooks::Postgres.new(connection_settings) + when :mysql + Hooks::MySql.new(connection_settings) end - dumper.dump(filename) - else - FileUtils.mkdir_p(filename) - settings.sources.each do |name, parameters| - filename_with_namespace = File.join(filename, "#{name}.dump") - connection_settings = OpenStruct.new(parameters.slice(:database, :username, :password)) - dumper = case parameters[:type] - when :postgres - Dumpers::Postgres.new(connection_settings) - when :mysql - Dumpers::MySql.new(connection_settings) - end - dumper.dump(filename_with_namespace) - end + dumper.dump(filename_with_namespace) end end def restore_dump(filename) - if settings.sources.empty? - dumper = case settings.database_type - when 'postgres' - Dumpers::Postgres.new(settings) - when 'mysql' - Dumpers::MySql.new(settings) + FileUtils.mkdir_p(filename) + settings.sources.each do |name, parameters| + filename_with_namespace = File.join(filename, "#{name}.dump") + connection_settings = OpenStruct.new(parameters.slice(:database, :username, :password)) + dumper = case parameters[:type] + when :postgres + Hooks::Postgres.new(connection_settings) + when :mysql + Hooks::MySql.new(connection_settings) end - dumper.restore(filename) - else - FileUtils.mkdir_p(filename) - settings.sources.each do |name, parameters| - filename_with_namespace = File.join(filename, "#{name}.dump") - connection_settings = OpenStruct.new(parameters.slice(:database, :username, :password)) - dumper = case parameters[:type] - when :postgres - Dumpers::Postgres.new(connection_settings) - when :mysql - Dumpers::MySql.new(connection_settings) - end - dumper.restore(filename_with_namespace) - end + dumper.restore(filename_with_namespace) end end @@ -159,7 +110,8 @@ def full_filename(name, created_on, actual) name_with_created_on = "#{name_with_created_on}_actual#{actual}" end full_path = "#{settings.dumps_location}/#{name_with_created_on}" - settings.sources.empty? ? "#{full_path}.dump" : full_path + + settings.with_sources ? full_path : "#{full_path}.dump" end def create_dirs_if_not_exists diff --git a/lib/dump_hook/hooks/connectable.rb b/lib/dump_hook/hooks/connectable.rb new file mode 100644 index 0000000..329f1dc --- /dev/null +++ b/lib/dump_hook/hooks/connectable.rb @@ -0,0 +1,9 @@ +module DumpHook + module Hooks + module Connectable + def initialize(connection_settings) + @connection_settings = connection_settings + end + end + end +end diff --git a/lib/dump_hook/hooks/mysql.rb b/lib/dump_hook/hooks/mysql.rb new file mode 100644 index 0000000..28eb62b --- /dev/null +++ b/lib/dump_hook/hooks/mysql.rb @@ -0,0 +1,27 @@ +require "dump_hook/hooks/connectable" + +module DumpHook + module Hooks + class MySql + include ::DumpHook::Hooks::Connectable + + def dump(filename) + args = [@connection_settings.database] + args << "--compress" + args.concat(["--result-file", filename]) + args.concat(["--ignore-table", "#{@connection_settings.database}.schema_migrations"]) + args.concat ['--user', @connection_settings.username] if @connection_settings.username + args << "--password=#{@connection_settings.password}" if @connection_settings.password + Kernel.system("mysqldump", *args) + end + + def restore(filename) + args = [@connection_settings.database] + args.concat ["-e", "source #{filename}"] + args.concat ['--user', @connection_settings.username] if @connection_settings.username + args << "--password=#{@connection_settings.password}" if @connection_settings.password + Kernel.system("mysql", *args) + end + end + end +end diff --git a/lib/dump_hook/hooks/postgres.rb b/lib/dump_hook/hooks/postgres.rb new file mode 100644 index 0000000..912ba68 --- /dev/null +++ b/lib/dump_hook/hooks/postgres.rb @@ -0,0 +1,25 @@ +require "dump_hook/hooks/connectable" + +module DumpHook + module Hooks + class Postgres + include ::DumpHook::Hooks::Connectable + + def dump(filename) + args = ['-a', '-x', '-O', '-f', filename, '-Fc', '-T', 'schema_migrations'] + args.concat(['-d', @connection_settings.database]) + args.concat(['-h', @connection_settings.host]) if @connection_settings.host + args.concat(['-p', @connection_settings.port]) if @connection_settings.port + Kernel.system("pg_dump", *args) + end + + def restore(filename) + args = ['-d', @connection_settings.database] + args.concat(['-h', @connection_settings.host]) if @connection_settings.host + args.concat(['-p', @connection_settings.port]) if @connection_settings.port + args << filename + Kernel.system("pg_restore", *args) + end + end + end +end diff --git a/spec/dump_hook_spec.rb b/spec/dump_hook_spec.rb index 35bad3a..51251fb 100644 --- a/spec/dump_hook_spec.rb +++ b/spec/dump_hook_spec.rb @@ -21,10 +21,6 @@ expect(DumpHook.settings.remove_old_dumps).to eq(true) end - it 'sets database' do - expect(DumpHook.settings.database).to eq('please set database') - end - it 'does not set actual' do expect(DumpHook.settings.actual).to be nil end @@ -147,8 +143,7 @@ it 'uses dump content if dump exists' do db.run("delete from t") - object.execute_with_dump("some_dump") { } - expect(db[:t].map([:a, :b])).to eq([['a', 'b']]) + expect { object.execute_with_dump("some_dump") { } }.to change { db[:t].map([:a, :b]) }.to([['a', 'b']]) end end From d91918b7a4d5649b1a11393618c020f18e40cb29 Mon Sep 17 00:00:00 2001 From: alvir Date: Thu, 4 Jun 2020 13:35:17 +0300 Subject: [PATCH 6/8] Add username processing of postgres connection settings --- lib/dump_hook.rb | 4 ++-- lib/dump_hook/hooks/postgres.rb | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/dump_hook.rb b/lib/dump_hook.rb index 45e7789..69869f9 100644 --- a/lib/dump_hook.rb +++ b/lib/dump_hook.rb @@ -76,7 +76,7 @@ def store_dump(filename) FileUtils.mkdir_p(filename) settings.sources.each do |name, parameters| filename_with_namespace = File.join(filename, "#{name}.dump") - connection_settings = OpenStruct.new(parameters.slice(:database, :username, :password)) + connection_settings = OpenStruct.new(parameters.slice(:database, :username, :password, :port, :host)) dumper = case parameters[:type] when :postgres Hooks::Postgres.new(connection_settings) @@ -91,7 +91,7 @@ def restore_dump(filename) FileUtils.mkdir_p(filename) settings.sources.each do |name, parameters| filename_with_namespace = File.join(filename, "#{name}.dump") - connection_settings = OpenStruct.new(parameters.slice(:database, :username, :password)) + connection_settings = OpenStruct.new(parameters.slice(:database, :username, :password, :port, :host)) dumper = case parameters[:type] when :postgres Hooks::Postgres.new(connection_settings) diff --git a/lib/dump_hook/hooks/postgres.rb b/lib/dump_hook/hooks/postgres.rb index 912ba68..34bf7e7 100644 --- a/lib/dump_hook/hooks/postgres.rb +++ b/lib/dump_hook/hooks/postgres.rb @@ -8,6 +8,7 @@ class Postgres def dump(filename) args = ['-a', '-x', '-O', '-f', filename, '-Fc', '-T', 'schema_migrations'] args.concat(['-d', @connection_settings.database]) + args.concat(['-U', @connection_settings.username]) if @connection_settings.username args.concat(['-h', @connection_settings.host]) if @connection_settings.host args.concat(['-p', @connection_settings.port]) if @connection_settings.port Kernel.system("pg_dump", *args) @@ -15,6 +16,7 @@ def dump(filename) def restore(filename) args = ['-d', @connection_settings.database] + args.concat(['-U', @connection_settings.username]) if @connection_settings.username args.concat(['-h', @connection_settings.host]) if @connection_settings.host args.concat(['-p', @connection_settings.port]) if @connection_settings.port args << filename From 42acb49ba8c31e5b85a0f0087697132bc162d5fb Mon Sep 17 00:00:00 2001 From: alvir Date: Thu, 4 Jun 2020 17:20:31 +0300 Subject: [PATCH 7/8] Convert port settings to String. --- lib/dump_hook.rb | 4 ++++ lib/dump_hook/hooks/mysql.rb | 6 +++--- lib/dump_hook/hooks/postgres.rb | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/dump_hook.rb b/lib/dump_hook.rb index 69869f9..506eb98 100644 --- a/lib/dump_hook.rb +++ b/lib/dump_hook.rb @@ -82,6 +82,8 @@ def store_dump(filename) Hooks::Postgres.new(connection_settings) when :mysql Hooks::MySql.new(connection_settings) + else + raise "Unsupported type of source" end dumper.dump(filename_with_namespace) end @@ -97,6 +99,8 @@ def restore_dump(filename) Hooks::Postgres.new(connection_settings) when :mysql Hooks::MySql.new(connection_settings) + else + raise "Unsupported type of source" end dumper.restore(filename_with_namespace) end diff --git a/lib/dump_hook/hooks/mysql.rb b/lib/dump_hook/hooks/mysql.rb index 28eb62b..dacc7d4 100644 --- a/lib/dump_hook/hooks/mysql.rb +++ b/lib/dump_hook/hooks/mysql.rb @@ -10,15 +10,15 @@ def dump(filename) args << "--compress" args.concat(["--result-file", filename]) args.concat(["--ignore-table", "#{@connection_settings.database}.schema_migrations"]) - args.concat ['--user', @connection_settings.username] if @connection_settings.username + args.concat(['--user', @connection_settings.username]) if @connection_settings.username args << "--password=#{@connection_settings.password}" if @connection_settings.password Kernel.system("mysqldump", *args) end def restore(filename) args = [@connection_settings.database] - args.concat ["-e", "source #{filename}"] - args.concat ['--user', @connection_settings.username] if @connection_settings.username + args.concat(["-e", "source #{filename}"]) + args.concat(['--user', @connection_settings.username]) if @connection_settings.username args << "--password=#{@connection_settings.password}" if @connection_settings.password Kernel.system("mysql", *args) end diff --git a/lib/dump_hook/hooks/postgres.rb b/lib/dump_hook/hooks/postgres.rb index 34bf7e7..1768f7d 100644 --- a/lib/dump_hook/hooks/postgres.rb +++ b/lib/dump_hook/hooks/postgres.rb @@ -10,7 +10,7 @@ def dump(filename) args.concat(['-d', @connection_settings.database]) args.concat(['-U', @connection_settings.username]) if @connection_settings.username args.concat(['-h', @connection_settings.host]) if @connection_settings.host - args.concat(['-p', @connection_settings.port]) if @connection_settings.port + args.concat(['-p', @connection_settings.port.to_s]) if @connection_settings.port Kernel.system("pg_dump", *args) end @@ -18,7 +18,7 @@ def restore(filename) args = ['-d', @connection_settings.database] args.concat(['-U', @connection_settings.username]) if @connection_settings.username args.concat(['-h', @connection_settings.host]) if @connection_settings.host - args.concat(['-p', @connection_settings.port]) if @connection_settings.port + args.concat(['-p', @connection_settings.port.to_s]) if @connection_settings.port args << filename Kernel.system("pg_restore", *args) end From 0a3b92cbb36c4e62a183bc82bfcc7115fa9f4ab1 Mon Sep 17 00:00:00 2001 From: alvir Date: Sun, 21 Nov 2021 18:22:19 +0300 Subject: [PATCH 8/8] Bump version to 1.0.0.rc --- dump-hook.gemspec | 2 +- lib/dump_hook/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dump-hook.gemspec b/dump-hook.gemspec index d301eee..636cb5a 100644 --- a/dump-hook.gemspec +++ b/dump-hook.gemspec @@ -29,7 +29,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'timecop' - spec.add_development_dependency "bundler" + spec.add_development_dependency "bundler", ">= 2.2.10" spec.add_development_dependency "rake", ">= 12.3.3" spec.add_development_dependency "rspec" spec.add_development_dependency "sequel" diff --git a/lib/dump_hook/version.rb b/lib/dump_hook/version.rb index 0e0c7d0..5e3f2a6 100644 --- a/lib/dump_hook/version.rb +++ b/lib/dump_hook/version.rb @@ -1,3 +1,3 @@ module DumpHook - VERSION = "0.0.1" + VERSION = "1.0.0.rc" end