db_dumper 0.5 → 0.5.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7bcc6d6d41f50f4034725ff07c8986e2e6e1f0016725f2cd0787348eb4f5a84
4
- data.tar.gz: c7ac55eed36664fa46d3ecfc3e5f098be027207727d3748581450a1f57239f7d
3
+ metadata.gz: ffa5f19bbf8e2ae8ff7df6413b88ad23eb1ab4aeb702af65150eed75aaa3394c
4
+ data.tar.gz: ae7a26c5be8a10c80af089b02f38af86e3fdaf6907a6060e4badf2cffbf9a50b
5
5
  SHA512:
6
- metadata.gz: 1fefff8a2c3150b49b7625a1aeae6eaa2ba2a2cd786e2f166977fcf6350d09b17f4093afeac5e4a5c2e8c343b8bbd384c14e2481f99265e4249b1bd788e80ea8
7
- data.tar.gz: 23a304879c10ffebb575300751966adbd425ae7c2405e36cf2f6eb42919972efe7b0d20d22c246c57b0663aa8e1855b956099972e50691757621ffb6ca3596be
6
+ metadata.gz: 2335003bbacde8b9c6a9784d753863dee4ab7f800e10ac68cc4df7633af8b5cabca0cb1b3ef149735fabba8361a0bf507f9512e5d389d0d99658e61dcb86b5dc
7
+ data.tar.gz: eff420c0c74e6e665f24b2a886abe547095c04f3446cd7f3ea81d73d56b0dd46d9161c179c47268888444dddd715fa09ef2071e21d52ff2a4d8ef5e17ded6c65
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # CHANGELOG
2
+
3
+ ## unreleased
4
+
5
+ ## 0.5.1
6
+
7
+ * Fix gemspec
8
+ * More logs
9
+ * Add Changelog
10
+ * Update README
data/README.md CHANGED
@@ -1 +1,125 @@
1
- db_dumper gem readme
1
+ # DB dumper - configurable SQL database data copying util from remote to local machine.
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/db_dumper.svg)](https://badge.fury.io/rb/db_dumper)
4
+
5
+ ## Index
6
+ - [Usage](#usage)
7
+ - [Config](#config)
8
+ - [Installation](#installation)
9
+ - [Manually from RubyGems.org](#manually-from-rubygemsorg)
10
+ - [Bundler](#or-if-you-are-using-bundler)
11
+
12
+ ## Usage
13
+
14
+ Example for simple Ruby file:
15
+
16
+ ```ruby
17
+
18
+ require 'rubygems/package'
19
+ require 'bundler/inline'
20
+
21
+ gemfile do
22
+ source 'https://rubygems.org'
23
+ gem 'db_dumper'
24
+ end
25
+
26
+ DbDumper.dump do
27
+ user_id = 1
28
+ dump('users').where(id: user_id)
29
+ campaigns_q = q('campaigns').where('user_id = ? OR for_all IS TRUE', user_id)
30
+ dump(campaigns_q)
31
+ dump('offices').where(campaign_id: campaigns_q.ar)
32
+ end
33
+ ```
34
+
35
+ After executing you will see:
36
+ - schema sql at local_machine/dest_path/schema_dump.sql.
37
+ - csv files at local_machine/dest_path/csv/*.csv
38
+
39
+
40
+ ### Config
41
+
42
+ Config file example at config/application.yml
43
+
44
+ ```
45
+ remote_db:
46
+ adapter: postgres
47
+ host: host
48
+ port: 5432
49
+ database: dockerdb
50
+ username: docker
51
+ password: pass
52
+
53
+ remote_user:
54
+ name: docker
55
+ host: beta.staging.com
56
+ ssh_keys: ['/.ssh/docker']
57
+ passphrase: pass
58
+
59
+ remote_machine:
60
+ dest_path: /tmp
61
+
62
+ local_machine:
63
+ dest_path: /tmp
64
+ ```
65
+
66
+ ### Testing
67
+
68
+ No tests fo now
69
+
70
+ ## <a id="installation">Installation ##
71
+
72
+ ### Manually from RubyGems.org ###
73
+
74
+ ```sh
75
+ % gem install db_dumper
76
+ ```
77
+
78
+ ### Or if you are using Bundler ###
79
+
80
+ ```ruby
81
+ # Gemfile
82
+ gem 'db_dumper'
83
+ ```
84
+
85
+ ## Latest changes ##
86
+
87
+ Take a look at the [CHANGELOG](https://github.com/alukyanov/db_dumper/blob/master/CHANGELOG.md) for details about recent changes to the current version.
88
+
89
+ ## Questions? ##
90
+
91
+ Feel free to
92
+
93
+ * [create an issue on GitHub](https://github.com/alukyanov/db_dumper/issues)
94
+
95
+ ## Maintainers ##
96
+
97
+ * [Alexey Lukyanov](https://github.com/alukyanov)
98
+
99
+ ## Warranty ##
100
+
101
+ This software is provided "as is" and without any express or
102
+ implied warranties, including, without limitation, the implied
103
+ warranties of merchantibility and fitness for a particular
104
+ purpose.
105
+
106
+ ## License ##
107
+
108
+ Permission is hereby granted, free of charge, to any person obtaining
109
+ a copy of this software and associated documentation files (the
110
+ "Software"), to deal in the Software without restriction, including
111
+ without limitation the rights to use, copy, modify, merge, publish,
112
+ distribute, sublicense, and/or sell copies of the Software, and to
113
+ permit persons to whom the Software is furnished to do so, subject to
114
+ the following conditions:
115
+
116
+ The above copyright notice and this permission notice shall be
117
+ included in all copies or substantial portions of the Software.
118
+
119
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
120
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
121
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
122
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
123
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
124
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
125
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'configuration/base'
3
4
  require_relative 'configuration/postgres'
4
5
 
5
6
  module DbDumper
7
+
8
+ # Configuration class, by default loads from config/application.yml file
9
+ # see config/application.sample.yml for format details
6
10
  class Configuration
7
11
  SshUser = Struct.new(:name, :host, :ssh_keys, :passphrase, keyword_init: true)
8
12
  RemoteDB = Struct.new(:adapter, :host, :port, :database, :username, :password,
@@ -32,7 +36,7 @@ module DbDumper
32
36
  def db_utils
33
37
  @db_utils ||= begin
34
38
  utils = DB_UTILS[remote_db.adapter]
35
- raise 'Unknown adapter for remote_db:adapter: check application.yml' unless utils
39
+ raise 'Unknown adapter for remote_db:adapter check application.yml' unless utils
36
40
  utils.new(remote_db)
37
41
  end
38
42
  end
@@ -53,12 +57,12 @@ module DbDumper
53
57
  @local_machine ||= LocalMachine.new(loaded_file['local_machine'])
54
58
  end
55
59
 
56
- private
57
-
58
60
  def logger
59
61
  @logger ||= Logger.new(STDOUT)
60
62
  end
61
63
 
64
+ private
65
+
62
66
  def loaded_file
63
67
  @loaded_file ||= YAML.load_file(config_file_path)
64
68
  end
@@ -0,0 +1,23 @@
1
+ module DbDumper
2
+ class Configuration
3
+ class Base
4
+ attr_reader :db_config
5
+
6
+ def initialize(db_config)
7
+ @db_config = db_config
8
+ end
9
+
10
+ def copy_data_command(sql, file_path)
11
+ raise NotImplementedError
12
+ end
13
+
14
+ def dump_schema_command(dump_schema_file_path)
15
+ raise NotImplementedError
16
+ end
17
+
18
+ def dump_data_command(dump_data_file_path)
19
+ raise NotImplementedError
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,10 +1,8 @@
1
1
  module DbDumper
2
2
  class Configuration
3
- class Postgres
4
- attr_reader :db_config
5
-
6
- def initialize(db_config)
7
- @db_config = db_config
3
+ class Postgres < Base
4
+ def copy_data_command(sql, file_path)
5
+ "\\COPY (#{sql}) TO '#{file_path}';"
8
6
  end
9
7
 
10
8
  def dump_schema_command(dump_schema_file_path)
@@ -2,51 +2,62 @@ require_relative 'query_builder/table'
2
2
  require_relative 'query_builder/query'
3
3
 
4
4
  module DbDumper
5
+
6
+ # Generates queries for copying data
5
7
  class QueryBuilder
6
8
  attr_reader :config
7
9
 
8
10
  def self.to_sql(config, &block)
9
11
  establish_connection
10
12
 
11
- instance = new(config)
12
- instance.instance_eval(&block)
13
- instance.to_sql
13
+ new(config).yield_self do |instance|
14
+ instance.instance_eval(&block)
15
+ instance.send(:to_sql)
16
+ end
14
17
  end
15
18
 
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
19
+ # DSL start
26
20
 
21
+ # creates new query and add it to resulting queries array
27
22
  def dump(query)
28
23
  query = q(query) if query.is_a?(String)
29
24
  query.tap { |q| queries << q }
30
25
  end
31
26
 
27
+ # creates new query
32
28
  def q(table_name)
33
29
  Query.new Table.from(table_name.to_s)
34
30
  end
35
31
 
32
+ # DSL end
33
+
34
+ private
35
+
36
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)
37
+ queries.map(&method(:build_copy_command)).join("\n")
40
38
  end
41
39
 
42
- private
40
+ def initialize(config)
41
+ @config = config
42
+ end
43
+
44
+ def self.establish_connection
45
+ return if ActiveRecord::Base.connected?
46
+
47
+ ActiveRecord::Migration.verbose = false
48
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
49
+ end
43
50
 
44
51
  def queries
45
52
  @queries ||= []
46
53
  end
47
54
 
55
+ def build_copy_command(query)
56
+ config.db_utils.copy_data_command(query.to_sql, "#{copy_path}/#{query.table_name}.csv")
57
+ end
58
+
48
59
  def copy_path
49
- config.remote_machine.dest_path
60
+ config.remote_machine.data_path
50
61
  end
51
62
  end
52
63
  end
@@ -1,5 +1,7 @@
1
1
  module DbDumper
2
2
  class QueryBuilder
3
+
4
+ # Wrapper under ActiveRecord::Relation
3
5
  class Query
4
6
  attr_reader :table, :ar
5
7
 
@@ -1,5 +1,7 @@
1
1
  module DbDumper
2
2
  class QueryBuilder
3
+
4
+ # Storage for generated classes inherited from ActiveRecord::Base
3
5
  class Table
4
6
  attr_reader :table_name
5
7
 
@@ -1,96 +1,80 @@
1
+ require_relative 'remote_machine/ssh_agent'
2
+
1
3
  module DbDumper
2
4
  class RemoteMachine
3
- attr_reader :config, :sql
5
+ attr_reader :config, :copy_commands_sql
4
6
 
5
- def initialize(config, sql)
7
+ def initialize(config, copy_commands_sql)
6
8
  @config = config
7
- @sql = sql
9
+ @copy_commands_sql = copy_commands_sql
8
10
  end
9
11
 
10
12
  def dump
11
13
  config.log('save sql commands to local machine')
12
14
  save_commands_sql_to_tmp_file
13
15
 
14
- Net::SSH.start(*ssh_credentials) do |ssh|
15
- config.log('upload commands to remote machine')
16
+ with_ssh do |ssh|
16
17
  upload_commands_sql_to_remote_machine(ssh)
17
-
18
- config.log('dump schema on remote machine')
19
18
  dump_schema(ssh)
20
-
21
- config.log('dump data on remote machine')
22
19
  dump_data(ssh)
23
-
24
- config.log('download schema to local machine')
25
20
  download_schema(ssh)
26
-
27
- config.log('download data to local machine')
28
21
  download_data(ssh)
29
-
30
- config.log('clean remote machine')
31
22
  clean_remote_machine(ssh)
32
23
  end
33
24
  end
34
25
 
35
26
  private
36
27
 
28
+ def with_ssh
29
+ ssh_agent = SshAgent.new(ssh_credentials, config)
30
+ yield(ssh_agent)
31
+ end
32
+
37
33
  # Upload
38
34
  def save_commands_sql_to_tmp_file
39
- File.open(local_commands_sql_file_path, 'w') { |f| f.write(sql) }
35
+ File.open(local_commands_sql_file_path, 'w') { |f| f.write(copy_commands_sql) }
40
36
  end
41
37
 
42
38
  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}"
39
+ ssh.upload!(local_commands_sql_file_path, remote_commands_sql_file_path)
53
40
  end
54
41
 
55
42
  def commands_sql_fname
56
- @commands_sql_fname ||= "#{Digest::MD5.hexdigest(sql)}.sql"
43
+ @commands_sql_fname ||= "#{Digest::MD5.hexdigest(copy_commands_sql)}.sql"
57
44
  end
58
45
 
59
46
  # Schema
60
47
 
61
48
  def dump_schema(ssh)
62
- ssh.exec! db_utils.dump_schema_command(remote_machine_schema_file_path)
49
+ ssh.exec!(db_utils.dump_schema_command(remote_machine_schema_file_path))
63
50
  end
64
51
 
65
52
  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'
53
+ ssh.download!(remote_machine_schema_file_path, dest_path)
75
54
  end
76
55
 
77
56
  # Data
78
57
 
79
58
  def dump_data(ssh)
80
59
  ssh.exec!("mkdir -p #{remote_machine_data_path}")
81
- ssh.exec! db_utils.dump_data_command(remote_commands_sql_file_path)
60
+ ssh.exec!(db_utils.dump_data_command(remote_commands_sql_file_path))
82
61
  end
83
62
 
84
63
  def download_data(ssh)
85
- ssh.scp.download!(remote_machine_data_path, dest_path, recursive: true)
64
+ ssh.download!(remote_machine_data_path, dest_path, recursive: true)
86
65
  end
87
66
 
67
+ # Clean
88
68
  def clean_remote_machine(ssh)
89
69
  ssh.exec! "rm #{remote_commands_sql_file_path}"
90
70
  ssh.exec! "rm #{remote_machine_schema_file_path}"
91
71
  ssh.exec! "rm -rf #{remote_machine_data_path}"
92
72
  end
93
73
 
74
+ def clean_local_machine
75
+ File.delete(local_commands_sql_file_path)
76
+ end
77
+
94
78
  #
95
79
 
96
80
  def db_utils
@@ -105,6 +89,22 @@ module DbDumper
105
89
  config.remote_machine.data_path
106
90
  end
107
91
 
92
+ def remote_machine_schema_file_path
93
+ "#{remote_machine_dest_path}/#{dump_schema_fname}"
94
+ end
95
+
96
+ def remote_commands_sql_file_path
97
+ "#{remote_machine_dest_path}/#{commands_sql_fname}"
98
+ end
99
+
100
+ def local_commands_sql_file_path
101
+ "#{dest_path}/#{commands_sql_fname}"
102
+ end
103
+
104
+ def dump_schema_fname
105
+ 'schema_dump.sql'
106
+ end
107
+
108
108
  def ssh_credentials
109
109
  [
110
110
  ssh_user.host,
@@ -0,0 +1,46 @@
1
+ module DbDumper
2
+ class RemoteMachine
3
+
4
+ # Wrapper around Net::SSH, Net:SCP
5
+ class SshAgent
6
+ attr_reader :credentials, :config
7
+ attr_reader :block, :ssh
8
+ attr_reader :ssh_user_name, :ssh_host_name
9
+
10
+ def initialize(credentials, config, &block)
11
+ @credentials = credentials
12
+ @config = config
13
+ @block = block
14
+ @ssh = Net::SSH.start(*credentials)
15
+
16
+ @ssh_host_name = credentials[0]
17
+ @ssh_user_name = credentials[1]
18
+ end
19
+
20
+ def exec!(command)
21
+ log("EXECUTING on #{ssh_machine_name}: #{command}")
22
+ log ssh.exec!(command)
23
+ end
24
+
25
+ def download!(remote_source, local_dest, options = {})
26
+ log("DOWNLOADING from #{ssh_machine_name}:#{remote_source} to #{local_dest}")
27
+ log ssh.scp.download!(remote_source, local_dest, options)
28
+ end
29
+
30
+ def upload!(local_source, remote_dest, options = {})
31
+ log("uploading from #{ssh_machine_name}:#{local_source} to #{remote_dest}")
32
+ log ssh.scp.upload!(local_source, remote_dest, options)
33
+ end
34
+
35
+ private
36
+
37
+ def ssh_machine_name
38
+ "#{ssh_user_name}@#{ssh_host_name}"
39
+ end
40
+
41
+ def log(message)
42
+ config.log(message)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,3 +1,3 @@
1
1
  module DbDumper
2
- VERSION = '0.5'
2
+ VERSION = '0.5.1'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: db_dumper
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.5'
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexey Lukyanov
@@ -31,21 +31,7 @@ dependencies:
31
31
  - !ruby/object:Gem::Version
32
32
  version: 5.2.1
33
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
34
+ name: net-scp
49
35
  requirement: !ruby/object:Gem::Requirement
50
36
  requirements:
51
37
  - - ">="
@@ -73,7 +59,7 @@ dependencies:
73
59
  - !ruby/object:Gem::Version
74
60
  version: '0'
75
61
  - !ruby/object:Gem::Dependency
76
- name: net-scp
62
+ name: sqlite3
77
63
  requirement: !ruby/object:Gem::Requirement
78
64
  requirements:
79
65
  - - ">="
@@ -92,19 +78,28 @@ executables: []
92
78
  extensions: []
93
79
  extra_rdoc_files: []
94
80
  files:
81
+ - CHANGELOG.md
95
82
  - README.md
96
83
  - lib/db_dumper.rb
97
84
  - lib/db_dumper/configuration.rb
85
+ - lib/db_dumper/configuration/base.rb
98
86
  - lib/db_dumper/configuration/postgres.rb
99
87
  - lib/db_dumper/query_builder.rb
100
88
  - lib/db_dumper/query_builder/query.rb
101
89
  - lib/db_dumper/query_builder/table.rb
102
90
  - lib/db_dumper/remote_machine.rb
91
+ - lib/db_dumper/remote_machine/ssh_agent.rb
103
92
  - lib/db_dumper/version.rb
104
93
  homepage: http://rubygems.org/gems/db_dumper
105
94
  licenses:
106
95
  - MIT
107
- metadata: {}
96
+ metadata:
97
+ bug_tracker_uri: https://github.com/alukyanov/db_dumper/issues
98
+ changelog_uri: https://github.com/alukyanov/db_dumper/CHANGELOG.md
99
+ documentation_uri: http://www.rubydoc.info/gems/db_dumper/0.5.1
100
+ homepage_uri: https://github.com/alukyanov/db_dumper
101
+ source_code_uri: https://github.com/alukyanov/db_dumper
102
+ wiki_uri: https://github.com/alukyanov/db_dumper/wiki
108
103
  post_install_message:
109
104
  rdoc_options: []
110
105
  require_paths: