data_keeper 0.1.4 → 0.1.8

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: 0442b3f4ba1b60c62570ca379d4a5d6f2a2181ec9cb7561107d52a3492b62ec5
4
- data.tar.gz: e7594ccf321cd010c5cf74926ff0dd881d4aee7e6c142b204a58c9f9df137ebd
3
+ metadata.gz: 0666762022ca90ffac33878de1b342661885b9aeecfdac775a729f3de2b48cc3
4
+ data.tar.gz: 0c17de29ea41f6023579bd8e8b6bb1e5bf34e682ff934291686d3c5721af02c2
5
5
  SHA512:
6
- metadata.gz: 12a6f3303abd4528976c51e49361f183b306fc352df0d9bc8cb3c2e44845f61920f8b9bf4e70c438cd42a8a33703dd98693a71c10f0a79e90d5015c8ac2977bb
7
- data.tar.gz: a5b9ecb78feecd034287fd86e34bdbd151d028a61d0be6ea58494b35102c3080d2c7c97eb3c5816d470a66a2f52a44c773a64a96ee2e3d3a01562ad5b8fc101c
6
+ metadata.gz: 6369301be6b94b87e5a8ea26d0e56926d560c9229c3ed7dba6a12e70193324584d7eec1e10ca7f3d02a4de5f5473a2b4fb0a4109094dd7176af5e8f2a5729995
7
+ data.tar.gz: c6ef2e6e5d521def03e06e3eb8c7113f9a8f61376d6b7d3f3e3d2b26fdc4c8ebc5cfba615d84224517fb5955c15d16e89e6a7a170f21bd6eb751aac810fdc975
data/.gitignore CHANGED
@@ -6,5 +6,6 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ /lib/tmp
9
10
 
10
11
  Gemfile.lock
data/Gemfile CHANGED
@@ -5,3 +5,4 @@ gemspec
5
5
 
6
6
  gem "rake", "~> 12.0"
7
7
  gem "minitest", "~> 5.0"
8
+ gem 'pg'
@@ -1,7 +1,7 @@
1
1
  module DataKeeper
2
2
  module DatabaseConfig
3
3
  def database_connection_config
4
- Rails.configuration.database_configuration[Rails.env]
4
+ @database_connection_config ||= DataKeeper.database_config.call
5
5
  end
6
6
 
7
7
  def psql_env
@@ -33,6 +33,7 @@ module DataKeeper
33
33
  @tables = []
34
34
  @raw_sqls = {}
35
35
  @definition_block = definition_block
36
+ @on_after_load_block = nil
36
37
  end
37
38
 
38
39
  def evaluate!
@@ -48,6 +48,7 @@ module DataKeeper
48
48
  dump_schema(tar)
49
49
  dump_partial_tables(tar)
50
50
  dump_sqls(tar)
51
+ dump_sequences(tar)
51
52
  end
52
53
  end
53
54
 
@@ -136,8 +137,47 @@ module DataKeeper
136
137
  end
137
138
  end
138
139
 
140
+ def dump_sequences(tar)
141
+ Tempfile.create do |sequences_dump_file|
142
+ sequences_dump_file.binmode
143
+
144
+ sequences_args = all_sequences_to_export.map { |table| "-t #{table}" }.join(' ')
145
+ cmd = Terrapin::CommandLine.new(
146
+ 'pg_dump',
147
+ "#{connection_args} -x -Fc :database #{sequences_args} > :output_path",
148
+ environment: psql_env
149
+ )
150
+
151
+ cmd.run(database: database, host: host, port: port, output_path: sequences_dump_file.path)
152
+
153
+ tar.add_file_simple("sequences.dump", 0644, File.size(sequences_dump_file.path)) do |io|
154
+ sequences_dump_file.reopen(sequences_dump_file)
155
+
156
+ while !sequences_dump_file.eof?
157
+ io.write(sequences_dump_file.read(2048))
158
+ end
159
+ end
160
+ end
161
+ end
162
+
139
163
  def filename
140
164
  "#{@dump_name}-#{Time.now.strftime("%Y%m%d-%H%M")}"
141
165
  end
166
+
167
+ def all_sequences_to_export
168
+ cmd = Terrapin::CommandLine.new(
169
+ 'psql',
170
+ "#{connection_args} -d :database -c :sql -A -R ',' -t",
171
+ environment: psql_env
172
+ )
173
+
174
+ sequences = cmd.run(
175
+ database: database,
176
+ host: host,
177
+ port: port,
178
+ sql: "SELECT sequencename from pg_sequences;"
179
+ )
180
+ sequences.split(',').map{|x| x.strip}
181
+ end
142
182
  end
143
183
  end
@@ -7,6 +7,9 @@ module DataKeeper
7
7
  def initialize(dump, file)
8
8
  @dump = dump
9
9
  @file = file
10
+ @psql_version = Terrapin::CommandLine.new('psql', '--version').run
11
+ .match(/[0-9]{1,}\.[0-9]{1,}/)
12
+ .to_s.to_f
10
13
  end
11
14
 
12
15
  def load!
@@ -17,58 +20,68 @@ module DataKeeper
17
20
  end
18
21
 
19
22
  if @dump.on_after_load_block
23
+ ActiveRecord::Base.establish_connection
20
24
  @dump.on_after_load_block.call
21
25
  end
22
26
  end
23
27
 
24
28
  private
25
29
 
26
- def load_full_database!
30
+ def log_redirect
31
+ if Terrapin::CommandLine.logger
32
+ ""
33
+ else
34
+ " 2>/dev/null"
35
+ end
36
+ end
37
+
38
+ def set_ar_internal_metadata!
27
39
  cmd = Terrapin::CommandLine.new(
28
40
  'psql',
29
- "#{connection_args} -d :database -c :command",
41
+ "#{connection_args} -d :database -c :sql",
30
42
  environment: psql_env
31
43
  )
32
- cmd.run(database: database, host: host, port: port, command: "drop schema if exists public")
33
44
 
34
- pg_restore = Terrapin::CommandLine.new(
35
- 'pg_restore',
36
- "#{connection_args} -j 4 --no-owner --dbname #{database} #{@file.path} 2>/dev/null",
37
- environment: psql_env
45
+ cmd.run(
46
+ database: database,
47
+ host: host,
48
+ port: port,
49
+ sql: "DELETE from ar_internal_metadata"
38
50
  )
39
51
 
40
- pg_restore.run(
52
+ cmd.run(
41
53
  database: database,
42
54
  host: host,
43
- port: port
55
+ port: port,
56
+ sql: "INSERT into ar_internal_metadata (key, value, created_at, updated_at) VALUES ('environment', 'development', '2020-04-03 12:25:54.094209', '2020-04-03 12:25:54.094209')"
44
57
  )
58
+ end
45
59
 
46
- cmd = Terrapin::CommandLine.new(
47
- 'psql',
48
- "#{connection_args} -d :database -c :sql",
60
+ def load_full_database!
61
+ ensure_schema_compatibility!
62
+
63
+ pg_restore = Terrapin::CommandLine.new(
64
+ 'pg_restore',
65
+ "#{connection_args} -j 4 --no-owner --dbname #{database} #{@file.path}#{log_redirect}",
49
66
  environment: psql_env
50
67
  )
51
68
 
52
- cmd.run(
69
+ pg_restore.run(
53
70
  database: database,
54
71
  host: host,
55
- port: port,
56
- sql: "UPDATE ar_internal_metadata SET value = 'development'"
72
+ port: port
57
73
  )
74
+
75
+ set_ar_internal_metadata!
58
76
  end
59
77
 
60
78
  def load_partial_database!
61
- inflate(@file.path) do |schema_path, tables_path, sql_files|
62
- cmd = Terrapin::CommandLine.new(
63
- 'psql',
64
- "#{connection_args} -d :database -c :command",
65
- environment: psql_env
66
- )
67
- cmd.run(database: database, host: host, port: port, command: "drop schema if exists public")
79
+ ensure_schema_compatibility!
68
80
 
81
+ inflate(@file.path) do |schema_path, tables_path, sql_files, sequences_path|
69
82
  pg_restore = Terrapin::CommandLine.new(
70
83
  'pg_restore',
71
- "#{connection_args} -j 4 --no-owner --dbname :database #{schema_path} 2>/dev/null",
84
+ "#{connection_args} -j 4 --no-owner -s --dbname :database #{schema_path}#{log_redirect}",
72
85
  environment: psql_env
73
86
  )
74
87
 
@@ -80,7 +93,7 @@ module DataKeeper
80
93
 
81
94
  pg_restore = Terrapin::CommandLine.new(
82
95
  'pg_restore',
83
- "#{connection_args} --data-only -j 4 --no-owner --disable-triggers --dbname :database #{tables_path} 2>/dev/null",
96
+ "#{connection_args} --data-only -j 4 --no-owner --disable-triggers --dbname :database #{tables_path}#{log_redirect}",
84
97
  environment: psql_env
85
98
  )
86
99
 
@@ -106,7 +119,33 @@ module DataKeeper
106
119
  )
107
120
  end
108
121
 
109
- Rake::Task['db:environment:set'].invoke
122
+ pg_restore = Terrapin::CommandLine.new(
123
+ 'pg_restore',
124
+ "#{connection_args} --data-only -j 4 --no-owner --disable-triggers --dbname :database #{sequences_path}#{log_redirect}",
125
+ environment: psql_env
126
+ )
127
+
128
+ pg_restore.run(
129
+ database: database,
130
+ host: host,
131
+ port: port
132
+ )
133
+
134
+ set_ar_internal_metadata!
135
+ end
136
+ end
137
+
138
+ def ensure_schema_compatibility!
139
+ cmd = Terrapin::CommandLine.new(
140
+ 'psql',
141
+ "#{connection_args} -d :database -c :command",
142
+ environment: psql_env
143
+ )
144
+
145
+ if @psql_version >= 11.0
146
+ cmd.run(database: database, host: host, port: port, command: "drop schema if exists public")
147
+ else
148
+ cmd.run(database: database, host: host, port: port, command: "create schema if not exists public")
110
149
  end
111
150
  end
112
151
 
@@ -124,9 +163,8 @@ module DataKeeper
124
163
 
125
164
  validate("Schema file is missing") { !!schema_path } &&
126
165
  validate("Tables file is missing") { !!tables_path } &&
127
- validate("Not all sql custom dumps are present") do
128
- sql_dumps.size == @dump.sqls.keys.size
129
- end
166
+ validate("Not all sql custom dumps are present") { sql_dumps.size == @dump.sqls.keys.size } &&
167
+ validate("Sequences file is missing") { !!sequences_path }
130
168
  end
131
169
 
132
170
  def schema_path
@@ -137,6 +175,10 @@ module DataKeeper
137
175
  @tables_path ||= @paths.find { |x| File.basename(x) == "tables.dump" }
138
176
  end
139
177
 
178
+ def sequences_path
179
+ @sequences_path ||= @paths.find { |x| File.basename(x) == "sequences.dump" }
180
+ end
181
+
140
182
  def sql_dumps
141
183
  @sql_dumps ||= @dump.sqls.map do |name, (table, _proc)|
142
184
  path = @paths.find { |x| File.basename(x) == "#{name}.csv" }
@@ -166,7 +208,8 @@ module DataKeeper
166
208
  yield(
167
209
  inflated_files.schema_path,
168
210
  inflated_files.tables_path,
169
- inflated_files.sql_dumps
211
+ inflated_files.sql_dumps,
212
+ inflated_files.sequences_path
170
213
  )
171
214
  end
172
215
  end
@@ -21,14 +21,15 @@ module DataKeeper
21
21
  def retrieve(dump_name)
22
22
  tempfile = Tempfile.new
23
23
  local_store_dir = @local_store_dir
24
+ last_dump_filename = nil
24
25
 
25
26
  on complete_host do
26
- last_dump = capture :ls, "-1t #{File.join(local_store_dir, dump_name.to_s)} | head -n 1"
27
+ last_dump_filename = capture :ls, "-1t #{File.join(local_store_dir, dump_name.to_s)} | head -n 1"
27
28
 
28
- download! File.join(local_store_dir, dump_name.to_s, last_dump), tempfile.path
29
+ download! File.join(local_store_dir, dump_name.to_s, last_dump_filename), tempfile.path
29
30
  end
30
31
 
31
- yield(tempfile)
32
+ yield(tempfile, last_dump_filename)
32
33
  ensure
33
34
  tempfile.delete
34
35
  end
@@ -78,7 +78,7 @@ module DataKeeper
78
78
  end
79
79
 
80
80
  def retrieve(dump_name)
81
- prefix = "#{@store_dir}#{dump_name.to_s}"
81
+ prefix = "#{@store_dir}#{dump_name.to_s}/"
82
82
  last_dump = s3_client.list_contents(prefix).sort_by(&:last_modified).reverse.first
83
83
 
84
84
  Tempfile.create do |tmp_file|
@@ -86,7 +86,7 @@ module DataKeeper
86
86
  s3_client.stream_to_io(last_dump.key, tmp_file)
87
87
  tmp_file.flush
88
88
 
89
- yield(tmp_file)
89
+ yield(tmp_file, File.basename(last_dump.key))
90
90
  end
91
91
  end
92
92
 
@@ -35,4 +35,15 @@ namespace :data_keeper do
35
35
 
36
36
  DataKeeper.load_dump!(name, path)
37
37
  end
38
+
39
+ desc "Downloads the given dump raw file in the current directory"
40
+ task :download, [:name] => [:environment] do |_t, args|
41
+ name = args[:name]
42
+
43
+ if name.blank? || !DataKeeper.dump?(name)
44
+ raise "Please use this rake task giving a name of a configured dump. Ex: bin/rake data_keeper:pull[full]"
45
+ end
46
+
47
+ DataKeeper.download_dump!(name, ".")
48
+ end
38
49
  end
@@ -1,3 +1,3 @@
1
1
  module DataKeeper
2
- VERSION = "0.1.4"
2
+ VERSION = "0.1.8"
3
3
  end
data/lib/data_keeper.rb CHANGED
@@ -17,6 +17,7 @@ module DataKeeper
17
17
 
18
18
  @dump_definition_builders = {}
19
19
  @storage = nil
20
+ @database_config = -> { Rails.configuration.database_configuration[Rails.env] }
20
21
 
21
22
  def self.define_dump(name, type = :partial, &block)
22
23
  @dump_definition_builders[name.to_sym] = DefinitionBuilder.new(type, block)
@@ -43,6 +44,15 @@ module DataKeeper
43
44
  end
44
45
  end
45
46
 
47
+ def self.download_dump!(name, path)
48
+ raise DumpDoesNotExist unless dump?(name)
49
+ raise NoStorageDefined if @storage.nil?
50
+
51
+ @storage.retrieve(name) do |file, filename|
52
+ FileUtils.cp file.path, File.join(path, filename)
53
+ end
54
+ end
55
+
46
56
  def self.load_dump!(name, path)
47
57
  raise DumpDoesNotExist unless File.file?(path)
48
58
  raise NoStorageDefined if @storage.nil?
@@ -59,6 +69,14 @@ module DataKeeper
59
69
  @storage = value
60
70
  end
61
71
 
72
+ def self.database_config=(value)
73
+ @database_config = value
74
+ end
75
+
76
+ def self.database_config
77
+ @database_config
78
+ end
79
+
62
80
  def self.clear_dumps!
63
81
  @dump_definition_builders = {}
64
82
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: data_keeper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roger Campos
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-08-08 00:00:00.000000000 Z
11
+ date: 2021-12-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -100,7 +100,7 @@ licenses:
100
100
  - MIT
101
101
  metadata:
102
102
  homepage_uri: https://github.com/rogercampos/data_keeper
103
- post_install_message:
103
+ post_install_message:
104
104
  rdoc_options: []
105
105
  require_paths:
106
106
  - lib
@@ -115,8 +115,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
115
115
  - !ruby/object:Gem::Version
116
116
  version: '0'
117
117
  requirements: []
118
- rubygems_version: 3.0.3
119
- signing_key:
118
+ rubygems_version: 3.1.6
119
+ signing_key:
120
120
  specification_version: 4
121
121
  summary: Easy management of database dumps for dev env
122
122
  test_files: []