db_dumper 0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +1 -0
- data/lib/db_dumper.rb +19 -0
- data/lib/db_dumper/configuration.rb +66 -0
- data/lib/db_dumper/configuration/postgres.rb +31 -0
- data/lib/db_dumper/query_builder.rb +52 -0
- data/lib/db_dumper/query_builder/query.rb +31 -0
- data/lib/db_dumper/query_builder/table.rb +28 -0
- data/lib/db_dumper/remote_machine.rb +125 -0
- data/lib/db_dumper/version.rb +3 -0
- metadata +128 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a7bcc6d6d41f50f4034725ff07c8986e2e6e1f0016725f2cd0787348eb4f5a84
|
4
|
+
data.tar.gz: c7ac55eed36664fa46d3ecfc3e5f098be027207727d3748581450a1f57239f7d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1fefff8a2c3150b49b7625a1aeae6eaa2ba2a2cd786e2f166977fcf6350d09b17f4093afeac5e4a5c2e8c343b8bbd384c14e2481f99265e4249b1bd788e80ea8
|
7
|
+
data.tar.gz: 23a304879c10ffebb575300751966adbd425ae7c2405e36cf2f6eb42919972efe7b0d20d22c246c57b0663aa8e1855b956099972e50691757621ffb6ca3596be
|
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
db_dumper gem readme
|
data/lib/db_dumper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
require 'net/ssh'
|
6
|
+
require 'net/scp'
|
7
|
+
require 'active_record'
|
8
|
+
|
9
|
+
require_relative 'db_dumper/configuration'
|
10
|
+
require_relative 'db_dumper/query_builder'
|
11
|
+
require_relative 'db_dumper/remote_machine'
|
12
|
+
|
13
|
+
module DbDumper
|
14
|
+
module_function def dump(config_file_path = 'config/application.yml', &block)
|
15
|
+
config = Configuration.new(config_file_path)
|
16
|
+
sql = QueryBuilder.to_sql(config, &block)
|
17
|
+
RemoteMachine.new(config, sql).dump
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'configuration/postgres'
|
4
|
+
|
5
|
+
module DbDumper
|
6
|
+
class Configuration
|
7
|
+
SshUser = Struct.new(:name, :host, :ssh_keys, :passphrase, keyword_init: true)
|
8
|
+
RemoteDB = Struct.new(:adapter, :host, :port, :database, :username, :password,
|
9
|
+
:dump_schema_options, :dump_data_options,
|
10
|
+
keyword_init: true)
|
11
|
+
RemoteMachine = Struct.new(:dest_path, keyword_init: true) do
|
12
|
+
def data_path
|
13
|
+
"#{dest_path}/csv"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
LocalMachine = Struct.new(:dest_path, keyword_init: true)
|
17
|
+
|
18
|
+
DB_UTILS = {
|
19
|
+
'postgres' => Postgres
|
20
|
+
}
|
21
|
+
|
22
|
+
attr_reader :config_file_path
|
23
|
+
|
24
|
+
def initialize(config_file_path)
|
25
|
+
@config_file_path = config_file_path
|
26
|
+
end
|
27
|
+
|
28
|
+
def log(message)
|
29
|
+
logger.info(message)
|
30
|
+
end
|
31
|
+
|
32
|
+
def db_utils
|
33
|
+
@db_utils ||= begin
|
34
|
+
utils = DB_UTILS[remote_db.adapter]
|
35
|
+
raise 'Unknown adapter for remote_db:adapter: check application.yml' unless utils
|
36
|
+
utils.new(remote_db)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def ssh_user
|
41
|
+
@ssh_user ||= SshUser.new(loaded_file['ssh_user'])
|
42
|
+
end
|
43
|
+
|
44
|
+
def remote_machine
|
45
|
+
@remote_machine ||= RemoteMachine.new(loaded_file['remote_machine'])
|
46
|
+
end
|
47
|
+
|
48
|
+
def remote_db
|
49
|
+
@remote_db ||= RemoteDB.new(loaded_file['remote_db'])
|
50
|
+
end
|
51
|
+
|
52
|
+
def local_machine
|
53
|
+
@local_machine ||= LocalMachine.new(loaded_file['local_machine'])
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def logger
|
59
|
+
@logger ||= Logger.new(STDOUT)
|
60
|
+
end
|
61
|
+
|
62
|
+
def loaded_file
|
63
|
+
@loaded_file ||= YAML.load_file(config_file_path)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module DbDumper
|
2
|
+
class Configuration
|
3
|
+
class Postgres
|
4
|
+
attr_reader :db_config
|
5
|
+
|
6
|
+
def initialize(db_config)
|
7
|
+
@db_config = db_config
|
8
|
+
end
|
9
|
+
|
10
|
+
def dump_schema_command(dump_schema_file_path)
|
11
|
+
util_command('pg_dump', "#{db_config.dump_schema_options} -f #{dump_schema_file_path}")
|
12
|
+
end
|
13
|
+
|
14
|
+
def dump_data_command(dump_data_file_path)
|
15
|
+
util_command('psql', "#{db_config.dump_data_options} -f #{dump_data_file_path}")
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def util_command(util, command)
|
21
|
+
<<-CMD.squish
|
22
|
+
PGPASSWORD=#{db_config.password} #{util}
|
23
|
+
-h #{db_config.host}
|
24
|
+
-U #{db_config.username}
|
25
|
+
-d #{db_config.database}
|
26
|
+
#{command}
|
27
|
+
CMD
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require_relative 'query_builder/table'
|
2
|
+
require_relative 'query_builder/query'
|
3
|
+
|
4
|
+
module DbDumper
|
5
|
+
class QueryBuilder
|
6
|
+
attr_reader :config
|
7
|
+
|
8
|
+
def self.to_sql(config, &block)
|
9
|
+
establish_connection
|
10
|
+
|
11
|
+
instance = new(config)
|
12
|
+
instance.instance_eval(&block)
|
13
|
+
instance.to_sql
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(config)
|
17
|
+
@config = config
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.establish_connection
|
21
|
+
return if ActiveRecord::Base.connected?
|
22
|
+
|
23
|
+
ActiveRecord::Migration.verbose = false
|
24
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
25
|
+
end
|
26
|
+
|
27
|
+
def dump(query)
|
28
|
+
query = q(query) if query.is_a?(String)
|
29
|
+
query.tap { |q| queries << q }
|
30
|
+
end
|
31
|
+
|
32
|
+
def q(table_name)
|
33
|
+
Query.new Table.from(table_name.to_s)
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_sql
|
37
|
+
queries.map do |query|
|
38
|
+
"\\COPY (#{query.to_sql}) TO '#{copy_path}/#{query.table_name}.csv';"
|
39
|
+
end.join($RS)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def queries
|
45
|
+
@queries ||= []
|
46
|
+
end
|
47
|
+
|
48
|
+
def copy_path
|
49
|
+
config.remote_machine.dest_path
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module DbDumper
|
2
|
+
class QueryBuilder
|
3
|
+
class Query
|
4
|
+
attr_reader :table, :ar
|
5
|
+
|
6
|
+
delegate :table_name, to: :table
|
7
|
+
|
8
|
+
def initialize(table)
|
9
|
+
@table = table
|
10
|
+
@ar = @table.ar.all
|
11
|
+
end
|
12
|
+
|
13
|
+
def where(*args)
|
14
|
+
tap { @ar = @ar.where(*args) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def joins(*args)
|
18
|
+
raise 'Only simple string for joins supported' unless args.size == 1 && args[0].is_a?(String)
|
19
|
+
tap { @ar = @ar.joins(*args) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def select(*args)
|
23
|
+
tap { @ar = @ar.select(*args) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_sql
|
27
|
+
@ar.to_sql
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module DbDumper
|
2
|
+
class QueryBuilder
|
3
|
+
class Table
|
4
|
+
attr_reader :table_name
|
5
|
+
|
6
|
+
@tables = {}
|
7
|
+
|
8
|
+
def self.from(table_name)
|
9
|
+
@tables[table_name] ||= begin
|
10
|
+
ActiveRecord::Migration.create_table(table_name)
|
11
|
+
new(table_name)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def ar
|
16
|
+
@ar ||= Class.new(ActiveRecord::Base).tap do |klass|
|
17
|
+
klass.table_name = table_name
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def initialize(table_name)
|
24
|
+
@table_name = table_name
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module DbDumper
|
2
|
+
class RemoteMachine
|
3
|
+
attr_reader :config, :sql
|
4
|
+
|
5
|
+
def initialize(config, sql)
|
6
|
+
@config = config
|
7
|
+
@sql = sql
|
8
|
+
end
|
9
|
+
|
10
|
+
def dump
|
11
|
+
config.log('save sql commands to local machine')
|
12
|
+
save_commands_sql_to_tmp_file
|
13
|
+
|
14
|
+
Net::SSH.start(*ssh_credentials) do |ssh|
|
15
|
+
config.log('upload commands to remote machine')
|
16
|
+
upload_commands_sql_to_remote_machine(ssh)
|
17
|
+
|
18
|
+
config.log('dump schema on remote machine')
|
19
|
+
dump_schema(ssh)
|
20
|
+
|
21
|
+
config.log('dump data on remote machine')
|
22
|
+
dump_data(ssh)
|
23
|
+
|
24
|
+
config.log('download schema to local machine')
|
25
|
+
download_schema(ssh)
|
26
|
+
|
27
|
+
config.log('download data to local machine')
|
28
|
+
download_data(ssh)
|
29
|
+
|
30
|
+
config.log('clean remote machine')
|
31
|
+
clean_remote_machine(ssh)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Upload
|
38
|
+
def save_commands_sql_to_tmp_file
|
39
|
+
File.open(local_commands_sql_file_path, 'w') { |f| f.write(sql) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def upload_commands_sql_to_remote_machine(ssh)
|
43
|
+
ssh.scp.upload!(local_commands_sql_file_path, remote_commands_sql_file_path)
|
44
|
+
File.delete(local_commands_sql_file_path)
|
45
|
+
end
|
46
|
+
|
47
|
+
def remote_commands_sql_file_path
|
48
|
+
"#{remote_machine_dest_path}/#{commands_sql_fname}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def local_commands_sql_file_path
|
52
|
+
"#{dest_path}/#{commands_sql_fname}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def commands_sql_fname
|
56
|
+
@commands_sql_fname ||= "#{Digest::MD5.hexdigest(sql)}.sql"
|
57
|
+
end
|
58
|
+
|
59
|
+
# Schema
|
60
|
+
|
61
|
+
def dump_schema(ssh)
|
62
|
+
ssh.exec! db_utils.dump_schema_command(remote_machine_schema_file_path)
|
63
|
+
end
|
64
|
+
|
65
|
+
def download_schema(ssh)
|
66
|
+
ssh.scp.download!(remote_machine_schema_file_path, dest_path)
|
67
|
+
end
|
68
|
+
|
69
|
+
def remote_machine_schema_file_path
|
70
|
+
"#{remote_machine_dest_path}/#{dump_schema_fname}"
|
71
|
+
end
|
72
|
+
|
73
|
+
def dump_schema_fname
|
74
|
+
'schema_dump.sql'
|
75
|
+
end
|
76
|
+
|
77
|
+
# Data
|
78
|
+
|
79
|
+
def dump_data(ssh)
|
80
|
+
ssh.exec!("mkdir -p #{remote_machine_data_path}")
|
81
|
+
ssh.exec! db_utils.dump_data_command(remote_commands_sql_file_path)
|
82
|
+
end
|
83
|
+
|
84
|
+
def download_data(ssh)
|
85
|
+
ssh.scp.download!(remote_machine_data_path, dest_path, recursive: true)
|
86
|
+
end
|
87
|
+
|
88
|
+
def clean_remote_machine(ssh)
|
89
|
+
ssh.exec! "rm #{remote_commands_sql_file_path}"
|
90
|
+
ssh.exec! "rm #{remote_machine_schema_file_path}"
|
91
|
+
ssh.exec! "rm -rf #{remote_machine_data_path}"
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
|
96
|
+
def db_utils
|
97
|
+
config.db_utils
|
98
|
+
end
|
99
|
+
|
100
|
+
def remote_machine_dest_path
|
101
|
+
config.remote_machine.dest_path
|
102
|
+
end
|
103
|
+
|
104
|
+
def remote_machine_data_path
|
105
|
+
config.remote_machine.data_path
|
106
|
+
end
|
107
|
+
|
108
|
+
def ssh_credentials
|
109
|
+
[
|
110
|
+
ssh_user.host,
|
111
|
+
ssh_user.name,
|
112
|
+
keys: ssh_user.ssh_keys,
|
113
|
+
passphrase: ssh_user.passphrase
|
114
|
+
]
|
115
|
+
end
|
116
|
+
|
117
|
+
def ssh_user
|
118
|
+
config.ssh_user
|
119
|
+
end
|
120
|
+
|
121
|
+
def dest_path
|
122
|
+
config.local_machine.dest_path
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
metadata
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: db_dumper
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.5'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexey Lukyanov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-10-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.2'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 5.2.1
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '5.2'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 5.2.1
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: sqlite3
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: pg
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: net-ssh
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
type: :runtime
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: net-scp
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
type: :runtime
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
description: Dump database schema and chosen data from remote machine
|
90
|
+
email: alukyanov.dev@gmail.com
|
91
|
+
executables: []
|
92
|
+
extensions: []
|
93
|
+
extra_rdoc_files: []
|
94
|
+
files:
|
95
|
+
- README.md
|
96
|
+
- lib/db_dumper.rb
|
97
|
+
- lib/db_dumper/configuration.rb
|
98
|
+
- lib/db_dumper/configuration/postgres.rb
|
99
|
+
- lib/db_dumper/query_builder.rb
|
100
|
+
- lib/db_dumper/query_builder/query.rb
|
101
|
+
- lib/db_dumper/query_builder/table.rb
|
102
|
+
- lib/db_dumper/remote_machine.rb
|
103
|
+
- lib/db_dumper/version.rb
|
104
|
+
homepage: http://rubygems.org/gems/db_dumper
|
105
|
+
licenses:
|
106
|
+
- MIT
|
107
|
+
metadata: {}
|
108
|
+
post_install_message:
|
109
|
+
rdoc_options: []
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 1.9.3
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
requirements: []
|
123
|
+
rubyforge_project:
|
124
|
+
rubygems_version: 2.7.3
|
125
|
+
signing_key:
|
126
|
+
specification_version: 4
|
127
|
+
summary: Dump database schema and chosen data from remote machine
|
128
|
+
test_files: []
|