db_sucker 3.0.0
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/.gitignore +16 -0
- data/CHANGELOG.md +45 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +193 -0
- data/Rakefile +1 -0
- data/VERSION +1 -0
- data/bin/db_sucker +12 -0
- data/bin/db_sucker.sh +14 -0
- data/db_sucker.gemspec +29 -0
- data/doc/config_example.rb +53 -0
- data/doc/container_example.yml +150 -0
- data/lib/db_sucker/adapters/mysql2.rb +103 -0
- data/lib/db_sucker/application/colorize.rb +28 -0
- data/lib/db_sucker/application/container/accessors.rb +60 -0
- data/lib/db_sucker/application/container/ssh.rb +225 -0
- data/lib/db_sucker/application/container/validations.rb +53 -0
- data/lib/db_sucker/application/container/variation/accessors.rb +45 -0
- data/lib/db_sucker/application/container/variation/helpers.rb +21 -0
- data/lib/db_sucker/application/container/variation/worker_api.rb +65 -0
- data/lib/db_sucker/application/container/variation.rb +60 -0
- data/lib/db_sucker/application/container.rb +70 -0
- data/lib/db_sucker/application/container_collection.rb +47 -0
- data/lib/db_sucker/application/core.rb +222 -0
- data/lib/db_sucker/application/dispatch.rb +364 -0
- data/lib/db_sucker/application/evented_resultset.rb +149 -0
- data/lib/db_sucker/application/fake_channel.rb +22 -0
- data/lib/db_sucker/application/output_helper.rb +197 -0
- data/lib/db_sucker/application/sklaven_treiber/log_spool.rb +57 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/accessors.rb +105 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/core.rb +168 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/helpers.rb +144 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/io/base.rb +240 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/io/file_copy.rb +81 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/io/file_gunzip.rb +58 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/io/file_import_sql.rb +80 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/io/file_shasum.rb +49 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/io/pv_wrapper.rb +73 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/io/sftp_download.rb +57 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/io/throughput.rb +219 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/routines.rb +313 -0
- data/lib/db_sucker/application/sklaven_treiber/worker.rb +48 -0
- data/lib/db_sucker/application/sklaven_treiber.rb +281 -0
- data/lib/db_sucker/application/slot_pool.rb +137 -0
- data/lib/db_sucker/application/tie.rb +25 -0
- data/lib/db_sucker/application/window/core.rb +185 -0
- data/lib/db_sucker/application/window/dialog.rb +142 -0
- data/lib/db_sucker/application/window/keypad/core.rb +85 -0
- data/lib/db_sucker/application/window/keypad.rb +174 -0
- data/lib/db_sucker/application/window/prompt.rb +124 -0
- data/lib/db_sucker/application/window.rb +329 -0
- data/lib/db_sucker/application.rb +168 -0
- data/lib/db_sucker/patches/beta-warning.rb +374 -0
- data/lib/db_sucker/patches/developer.rb +29 -0
- data/lib/db_sucker/patches/net-sftp.rb +20 -0
- data/lib/db_sucker/patches/thread-count.rb +30 -0
- data/lib/db_sucker/version.rb +4 -0
- data/lib/db_sucker.rb +81 -0
- metadata +217 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
module DbSucker
|
2
|
+
class Application
|
3
|
+
class SklavenTreiber
|
4
|
+
class Worker
|
5
|
+
module IO
|
6
|
+
class PvWrapper < Base
|
7
|
+
NoCommandError = Class.new(::ArgumentError)
|
8
|
+
attr_accessor :cmd, :result
|
9
|
+
|
10
|
+
def init
|
11
|
+
@enabled ||= Proc.new {}
|
12
|
+
@fallback ||= Proc.new {}
|
13
|
+
@cmd ||= @local
|
14
|
+
end
|
15
|
+
|
16
|
+
def enabled &block
|
17
|
+
@enabled = block
|
18
|
+
end
|
19
|
+
|
20
|
+
def fallback &block
|
21
|
+
@fallback = block
|
22
|
+
end
|
23
|
+
|
24
|
+
def perform! opts = {}
|
25
|
+
if @ctn.pv_utility
|
26
|
+
@enabled.call(@ctn.pv_utility)
|
27
|
+
raise(NoCommandError, "no command was provided, set `pv.cmd = mycmd' in the enabled callback") if @cmd.blank?
|
28
|
+
execute(opts.slice(:tries).merge(sleep_error: 3)) do
|
29
|
+
_perform_with_wrapper
|
30
|
+
end
|
31
|
+
else
|
32
|
+
execute(opts.slice(:tries), &@fallback)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def _perform_with_wrapper
|
37
|
+
@state = :working
|
38
|
+
target_thread = Thread.current
|
39
|
+
channel, @result = @ctn.nonblocking_channel_result(cmd, channel: true, use_sh: true)
|
40
|
+
|
41
|
+
killer = @worker.app.spawn_thread(:sklaventreiber_worker_io_pv_killer) do |thr|
|
42
|
+
thr[:current_task] = target_thread[:current_task] if target_thread[:current_task]
|
43
|
+
loop do
|
44
|
+
if @worker.should_cancel && !thr[:canceled]
|
45
|
+
if channel.is_a?(Net::SSH::Connection::Channel)
|
46
|
+
if channel[:pty]
|
47
|
+
channel.send_data("\C-c") rescue false
|
48
|
+
elsif channel[:pid]
|
49
|
+
@ctn.kill_remote_process(channel[:pid])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
channel.close rescue false
|
53
|
+
thr[:canceled] = true
|
54
|
+
end
|
55
|
+
break unless channel.active?
|
56
|
+
thr.wait(0.1)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
@result.each_linex do |grp, line|
|
61
|
+
next unless grp == :stderr
|
62
|
+
@offset = line.to_i
|
63
|
+
end
|
64
|
+
killer.signal.join
|
65
|
+
ensure
|
66
|
+
killer.kill
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module DbSucker
|
2
|
+
class Application
|
3
|
+
class SklavenTreiber
|
4
|
+
class Worker
|
5
|
+
module IO
|
6
|
+
class SftpDownload < Base
|
7
|
+
UnknownEventError = Class.new(::RuntimeError)
|
8
|
+
attr_reader :downloader
|
9
|
+
|
10
|
+
def init
|
11
|
+
@label = "downloading"
|
12
|
+
@entity = "download"
|
13
|
+
@throughput.categories << :inet << :inet_down
|
14
|
+
end
|
15
|
+
|
16
|
+
def reset_state
|
17
|
+
super
|
18
|
+
@downloader = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def download! opts = {}
|
22
|
+
opts = opts.reverse_merge(tries: 3, read_size: @read_size, force_new_connection: true)
|
23
|
+
prepare_local_destination
|
24
|
+
execute(opts.slice(:tries).merge(sleep_error: 3)) do
|
25
|
+
@ctn.sftp_start(opts[:force_new_connection]) do |sftp|
|
26
|
+
@filesize = sftp.lstat!(@remote).size
|
27
|
+
sftp.download!(@remote, @local, read_size: opts[:read_size], requests: 1) do |event, downloader, *args|
|
28
|
+
if !@closing && @abort_if.call(self, event, downloader, *args)
|
29
|
+
downloader.abort!
|
30
|
+
@closing = true
|
31
|
+
end
|
32
|
+
|
33
|
+
case event
|
34
|
+
when :open
|
35
|
+
@downloader = downloader
|
36
|
+
@state = :init
|
37
|
+
when :get
|
38
|
+
@state = :downloading
|
39
|
+
@offset = args[1] + args[2].length
|
40
|
+
GC.start if @offset % GC_FORCE_RATE == 0
|
41
|
+
when :close
|
42
|
+
@state = :finishing
|
43
|
+
when :finish
|
44
|
+
@state = :done
|
45
|
+
else
|
46
|
+
raise UnknownEventError, "unknown event `#{event}'"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
module DbSucker
|
2
|
+
class Application
|
3
|
+
class SklavenTreiber
|
4
|
+
class Worker
|
5
|
+
module IO
|
6
|
+
class Throughput
|
7
|
+
InstanceAlreadyRegisteredError = Class.new(::ArgumentError)
|
8
|
+
|
9
|
+
attr_reader :sklaventreiber, :stats
|
10
|
+
|
11
|
+
def initialize sklaventreiber
|
12
|
+
@sklaventreiber = sklaventreiber
|
13
|
+
@instances = {}
|
14
|
+
@stats = {}
|
15
|
+
@monitor = Monitor.new
|
16
|
+
@polling = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def app
|
20
|
+
sklaventreiber.app
|
21
|
+
end
|
22
|
+
|
23
|
+
def sync &block
|
24
|
+
@monitor.synchronize(&block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def poll! instance
|
28
|
+
sync { @polling.push(instance) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def nopoll! instance
|
32
|
+
sync { @polling.delete(instance) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def start_loop
|
36
|
+
@poll = app.spawn_thread(:sklaventreiber_throughput) do |thr|
|
37
|
+
thr[:polling] = 0
|
38
|
+
loop do
|
39
|
+
sync {
|
40
|
+
thr[:polling] = @polling.length
|
41
|
+
@polling.each(&:ping)
|
42
|
+
}
|
43
|
+
break if thr[:stop]
|
44
|
+
thr.wait(0.1)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def stop_loop
|
50
|
+
sync do
|
51
|
+
return unless @poll
|
52
|
+
@poll[:stop] = true
|
53
|
+
end
|
54
|
+
@poll.signal.join
|
55
|
+
end
|
56
|
+
|
57
|
+
def commit! bytes, *categories
|
58
|
+
sync do
|
59
|
+
return unless bytes
|
60
|
+
categories.flatten.each do |cat|
|
61
|
+
@stats[cat] ||= 0
|
62
|
+
@stats[cat] += bytes
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def register target
|
68
|
+
sync do
|
69
|
+
if @instances[target]
|
70
|
+
raise InstanceAlreadyRegisteredError, "throughput manager cannot register more than once on the same target: `#{target}'"
|
71
|
+
else
|
72
|
+
raise NotImplementedError, "throughput manager requires the target to respond_to?(:filesize)" unless target.respond_to?(:filesize)
|
73
|
+
raise NotImplementedError, "throughput manager requires the target to respond_to?(:offset)" unless target.respond_to?(:offset)
|
74
|
+
@instances[target] = Instance.new(self, target)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def unregister instance
|
80
|
+
sync do
|
81
|
+
@instances.clone.each do |k, v|
|
82
|
+
if v == instance
|
83
|
+
@instances.delete(k)
|
84
|
+
break
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class Instance
|
91
|
+
attr_reader :ioop, :categories, :sopts
|
92
|
+
|
93
|
+
def initialize manager, ioop
|
94
|
+
@manager = manager
|
95
|
+
@ioop = ioop
|
96
|
+
@monitor = Monitor.new
|
97
|
+
@pauses = 0
|
98
|
+
@categories = [:total]
|
99
|
+
@tracking = []
|
100
|
+
@tracking_offset = 0
|
101
|
+
@sopts = { perc_modifier: 1, perc_base: 0, tracking: 5.seconds }
|
102
|
+
reset_stats
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.expose what, &how
|
106
|
+
define_method(what) do |*args, &block|
|
107
|
+
sync { instance_exec(*args, &how) }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
[:filesize, :offset].each do |m|
|
112
|
+
define_method(m) {|*a| @ioop.send(m, *a) }
|
113
|
+
end
|
114
|
+
|
115
|
+
[:human_bytes, :human_percentage, :human_seconds, :human_seconds2].each do |m|
|
116
|
+
define_method(m) {|*a| @manager.app.send(m, *a) }
|
117
|
+
end
|
118
|
+
|
119
|
+
def sync &block
|
120
|
+
@monitor.synchronize(&block)
|
121
|
+
end
|
122
|
+
|
123
|
+
def commit!
|
124
|
+
sync do
|
125
|
+
ping
|
126
|
+
return unless offset
|
127
|
+
@manager.commit!(offset, @categories)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def pause!
|
132
|
+
sync do
|
133
|
+
return if @pause_started
|
134
|
+
@pause_started = Time.current
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def unpause!
|
139
|
+
sync do
|
140
|
+
return unless @pause_started
|
141
|
+
@pauses += Time.current - @pause_started
|
142
|
+
@pause_started = false
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def ping
|
147
|
+
sync do
|
148
|
+
return unless @started_at
|
149
|
+
@stats[:bps_avg] = runtime.zero? ? 0 : (offset.to_d / runtime.to_d).to_i
|
150
|
+
@stats[:eta2] = @stats[:bps_avg].zero? ? -1 : (bytes_remain.to_d / @stats[:bps_avg].to_d).to_i
|
151
|
+
|
152
|
+
# eta tracking
|
153
|
+
od = @tracking.last ? offset - @tracking_offset - @tracking.sum{|t,o| o }.to_d : 0
|
154
|
+
@tracking << [Time.current, od] if !od.zero? || @tracking.empty?
|
155
|
+
while @tracking.any? && @tracking.first[0] < @sopts[:tracking].ago
|
156
|
+
@tracking_offset += @tracking.shift[1]
|
157
|
+
end
|
158
|
+
|
159
|
+
range = @tracking.any? ? @tracking.last[0] - @tracking.first[0] : 0
|
160
|
+
@stats[:bps] = range.zero? ? 0 : @tracking.sum{|t,o| o }.to_d / range.to_d
|
161
|
+
@stats[:eta] = @stats[:bps].zero? ? -1 : (bytes_remain.to_d / @stats[:bps].to_d).to_i
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def unregister
|
166
|
+
@manager.unregister(self)
|
167
|
+
end
|
168
|
+
|
169
|
+
def reset_stats
|
170
|
+
sync do
|
171
|
+
@tracking_offset = 0
|
172
|
+
@tracking.clear
|
173
|
+
@stats = { eta: -1, eta2: -1, bps: 0, bps_avg: 0 }
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# =======
|
178
|
+
# = API =
|
179
|
+
# =======
|
180
|
+
expose(:eta) { ping; @stats[:eta] }
|
181
|
+
expose(:eta2) { ping; @stats[:eta2] }
|
182
|
+
expose(:bps) { ping; @stats[:bps] }
|
183
|
+
expose(:bps_avg) { ping; @stats[:bps_avg] }
|
184
|
+
expose(:done_percentage) { @sopts[:perc_base].to_d + @sopts[:perc_modifier].to_d * (filesize == 0 ? 100 : offset == 0 ? 0 : (offset.to_d / filesize.to_d * 100.to_d)) }
|
185
|
+
expose(:remain_percentage) { 100.to_d - done_percentage }
|
186
|
+
expose(:bytes_remain) { filesize - offset }
|
187
|
+
expose(:pauses) { @pauses + (@pause_started ? Time.current - @pause_started : 0) }
|
188
|
+
expose(:runtime) { @started_at ? (@ended_at || Time.current) - @started_at - pauses : 0 }
|
189
|
+
expose(:f_byte_progress) { "#{f_offset}/#{f_filesize}" }
|
190
|
+
|
191
|
+
[:bps, :bps_avg, :done_percentage, :remain_percentage, :bytes_remain, :offset, :filesize].each do |m|
|
192
|
+
expose(:"f_#{m}") { human_bytes(send(m)) }
|
193
|
+
end
|
194
|
+
[:done_percentage, :remain_percentage].each do |m|
|
195
|
+
expose(:"f_#{m}") { human_percentage(send(m)) }
|
196
|
+
end
|
197
|
+
[:runtime].each do |m|
|
198
|
+
expose(:"f_#{m}") { human_seconds(send(m)) }
|
199
|
+
end
|
200
|
+
[:eta, :eta2].each do |m|
|
201
|
+
expose(:"f_#{m}") { r = send(m); r == -1 ? "?:¿?:¿?" : human_seconds2(send(m)) }
|
202
|
+
end
|
203
|
+
|
204
|
+
def measure &block
|
205
|
+
@manager.poll!(self)
|
206
|
+
@started_at = Time.current
|
207
|
+
block.call(self)
|
208
|
+
ensure
|
209
|
+
@ended_at = Time.current
|
210
|
+
@manager.nopoll!(self)
|
211
|
+
commit!
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1,313 @@
|
|
1
|
+
module DbSucker
|
2
|
+
class Application
|
3
|
+
class SklavenTreiber
|
4
|
+
class Worker
|
5
|
+
module Routines
|
6
|
+
def _r_dump_file
|
7
|
+
@status = ["dumping table to remote file...", "yellow"]
|
8
|
+
|
9
|
+
pv_wrap(@ctn, nil) do |pv|
|
10
|
+
pv.enabled do |pvbinary|
|
11
|
+
pv.filesize = -1
|
12
|
+
pv.label = "dumping table"
|
13
|
+
pv.entity = "table dump"
|
14
|
+
pv.status_format = app.opts[:status_format]
|
15
|
+
pv.mode = :nofs
|
16
|
+
@status = [pv, "yellow"]
|
17
|
+
pv.abort_if { @should_cancel }
|
18
|
+
|
19
|
+
@remote_file_raw_tmp, pv.cmd = var.dump_to_remote_command(self, pvbinary)
|
20
|
+
end
|
21
|
+
|
22
|
+
pv.fallback do
|
23
|
+
@remote_file_raw_tmp, (channel, result) = var.dump_to_remote(self, false)
|
24
|
+
second_progress(channel, "dumping table to remote file (:seconds)...").join
|
25
|
+
end
|
26
|
+
|
27
|
+
pv.on_complete do
|
28
|
+
@remote_files_to_remove << @remote_file_raw_tmp
|
29
|
+
end
|
30
|
+
|
31
|
+
pv.perform!
|
32
|
+
end
|
33
|
+
_cancelpoint
|
34
|
+
|
35
|
+
# check if response has any sort of errors and abort
|
36
|
+
# if result.any?
|
37
|
+
# r = result.join
|
38
|
+
# if m = r.match(/(Unknown column '(.+)') in .+ \(([0-9]+)\)/i)
|
39
|
+
# @status = ["[DUMP] Failed: #{m[1]} (#{m[3]})", :red]
|
40
|
+
# throw :abort_execution, true
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
|
44
|
+
@remote_file_raw = @remote_file_raw_tmp[0..-5]
|
45
|
+
ctn.sftp_start do |sftp|
|
46
|
+
# rename tmp file
|
47
|
+
sftp.rename!(@remote_file_raw_tmp, @remote_file_raw)
|
48
|
+
|
49
|
+
# save size for gzip progress
|
50
|
+
@remote_file_raw_filesize = sftp.lstat!(@remote_file_raw).size
|
51
|
+
end
|
52
|
+
|
53
|
+
@remote_files_to_remove.delete(@remote_file_raw_tmp)
|
54
|
+
@remote_files_to_remove << @remote_file_raw
|
55
|
+
end
|
56
|
+
|
57
|
+
def _r_calculate_raw_hash
|
58
|
+
@status = ["calculating integrity hash for raw file...", "yellow"]
|
59
|
+
|
60
|
+
pv_wrap(@ctn, nil) do |pv|
|
61
|
+
pv.enabled do |pvbinary|
|
62
|
+
pv.filesize = @remote_file_raw_filesize
|
63
|
+
pv.label = "hashing raw file"
|
64
|
+
pv.entity = "hashing raw file"
|
65
|
+
pv.status_format = app.opts[:status_format]
|
66
|
+
@status = [pv, "yellow"]
|
67
|
+
pv.abort_if { @should_cancel }
|
68
|
+
pv.cmd = ctn.calculate_remote_integrity_hash_command(@remote_file_raw, pvbinary)
|
69
|
+
end
|
70
|
+
|
71
|
+
pv.fallback do
|
72
|
+
cmd, (channel, pv.result) = ctn.calculate_remote_integrity_hash(@remote_file_raw, false)
|
73
|
+
second_progress(channel, "calculating integrity hash for raw file (:seconds)...").join
|
74
|
+
end
|
75
|
+
|
76
|
+
pv.on_success do
|
77
|
+
@integrity = { raw: pv.result.for_group(:stdout).join.split(" ").first.try(:strip).presence }
|
78
|
+
end
|
79
|
+
|
80
|
+
pv.perform!
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def _r_compress_file
|
85
|
+
@status = ["compressing file for transfer...", "yellow"]
|
86
|
+
|
87
|
+
pv_wrap(@ctn, nil) do |pv|
|
88
|
+
pv.enabled do |pvbinary|
|
89
|
+
pv.filesize = @remote_file_raw_filesize
|
90
|
+
pv.label = "compressing"
|
91
|
+
pv.entity = "compress"
|
92
|
+
pv.status_format = app.opts[:status_format]
|
93
|
+
@status = [pv, "yellow"]
|
94
|
+
pv.abort_if { @should_cancel }
|
95
|
+
@remote_file_compressed, pv.cmd = var.compress_file_command(@remote_file_raw, pvbinary)
|
96
|
+
@remote_files_to_remove << @remote_file_compressed
|
97
|
+
end
|
98
|
+
|
99
|
+
pv.fallback do
|
100
|
+
@remote_file_compressed, (channel, result) = var.compress_file(@remote_file_raw, false)
|
101
|
+
@remote_files_to_remove << @remote_file_compressed
|
102
|
+
second_progress(channel, "compressing file for transfer (:seconds)...").join
|
103
|
+
end
|
104
|
+
|
105
|
+
pv.on_success do
|
106
|
+
@remote_files_to_remove.delete(@remote_file_raw)
|
107
|
+
end
|
108
|
+
|
109
|
+
pv.perform!
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def _r_calculate_compressed_hash
|
114
|
+
@status = ["calculating integrity hash for compressed file...", "yellow"]
|
115
|
+
|
116
|
+
pv_wrap(@ctn, nil) do |pv|
|
117
|
+
pv.enabled do |pvbinary|
|
118
|
+
pv.filesize = @remote_file_raw_filesize
|
119
|
+
pv.label = "hashing compressed file"
|
120
|
+
pv.entity = "hashing compressed file"
|
121
|
+
pv.status_format = app.opts[:status_format]
|
122
|
+
@status = [pv, "yellow"]
|
123
|
+
pv.abort_if { @should_cancel }
|
124
|
+
pv.cmd = ctn.calculate_remote_integrity_hash_command(@remote_file_compressed, pvbinary)
|
125
|
+
end
|
126
|
+
|
127
|
+
pv.fallback do
|
128
|
+
cmd, (channel, pv.result) = ctn.calculate_remote_integrity_hash(@remote_file_compressed, false)
|
129
|
+
second_progress(channel, "calculating integrity hash for compressed file (:seconds)...").join
|
130
|
+
end
|
131
|
+
|
132
|
+
pv.on_success do
|
133
|
+
@integrity[:compressed] = pv.result.for_group(:stdout).join.split(" ").first.try(:strip).presence
|
134
|
+
end
|
135
|
+
|
136
|
+
pv.perform!
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def _l_download_file
|
141
|
+
@status = ["initiating download...", "yellow"]
|
142
|
+
@local_file_compressed = local_tmp_file(File.basename(@remote_file_compressed))
|
143
|
+
@local_files_to_remove << @local_file_compressed
|
144
|
+
|
145
|
+
sftp_download(@ctn, @remote_file_compressed => @local_file_compressed) do |dl|
|
146
|
+
dl.status_format = app.opts[:status_format]
|
147
|
+
@status = [dl, "yellow"]
|
148
|
+
dl.abort_if { @should_cancel }
|
149
|
+
dl.download!
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def _l_verify_compressed_hash
|
154
|
+
return unless @integrity[:compressed]
|
155
|
+
label = "verifying compressed file"
|
156
|
+
@status = ["#{label}...", :yellow]
|
157
|
+
|
158
|
+
file_shasum(@ctn, @local_file_compressed) do |fc|
|
159
|
+
fc.label = label
|
160
|
+
fc.sha = ctn.integrity_sha
|
161
|
+
fc.status_format = app.opts[:status_format]
|
162
|
+
@status = [fc, "yellow"]
|
163
|
+
|
164
|
+
fc.abort_if { @should_cancel }
|
165
|
+
fc.on_success do
|
166
|
+
@integrity[:compressed_local] = fc.result
|
167
|
+
end
|
168
|
+
fc.verify!
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
if !@should_cancel && @integrity[:compressed] != @integrity[:compressed_local]
|
173
|
+
@status = ["[INTEGRITY] downloaded compressed file corrupted! (remote: #{@integrity[:compressed]}, local: #{@integrity[:compressed_local]})", :red]
|
174
|
+
throw :abort_execution, true
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def _l_copy_file file = nil
|
179
|
+
label = "copying #{var.copies_file_compressed? ? "gzipped" : "raw"} file"
|
180
|
+
@status = ["#{label}...", :yellow]
|
181
|
+
|
182
|
+
@copy_file_source = var.copies_file_compressed? ? @local_file_compressed : @local_file_raw
|
183
|
+
@copy_file_target = copy_file_destination(var.data["file"])
|
184
|
+
|
185
|
+
file_copy(@ctn, @copy_file_source => @copy_file_target) do |fc|
|
186
|
+
fc.label = label
|
187
|
+
fc.status_format = app.opts[:status_format]
|
188
|
+
@status = [fc, "yellow"]
|
189
|
+
fc.abort_if { @should_cancel }
|
190
|
+
fc.copy!
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def _l_decompress_file
|
195
|
+
label = "decompressing file"
|
196
|
+
@status = ["#{label}...", :yellow]
|
197
|
+
|
198
|
+
file_gunzip(@ctn, @local_file_compressed) do |fc|
|
199
|
+
fc.filesize = @remote_file_raw_filesize
|
200
|
+
fc.label = label
|
201
|
+
fc.status_format = app.opts[:status_format]
|
202
|
+
@status = [fc, "yellow"]
|
203
|
+
|
204
|
+
fc.abort_if { @should_cancel }
|
205
|
+
fc.on_success do
|
206
|
+
@local_files_to_remove.delete(@local_file_compressed)
|
207
|
+
@local_file_raw = fc.local
|
208
|
+
@local_files_to_remove << @local_file_raw
|
209
|
+
end
|
210
|
+
fc.gunzip!
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def _l_verify_raw_hash
|
215
|
+
return unless @integrity[:raw]
|
216
|
+
label = "verifying raw file"
|
217
|
+
@status = ["#{label}...", :yellow]
|
218
|
+
|
219
|
+
file_shasum(@ctn, @local_file_raw) do |fc|
|
220
|
+
fc.label = label
|
221
|
+
fc.sha = ctn.integrity_sha
|
222
|
+
fc.status_format = app.opts[:status_format]
|
223
|
+
@status = [fc, "yellow"]
|
224
|
+
|
225
|
+
fc.abort_if { @should_cancel }
|
226
|
+
fc.on_success do
|
227
|
+
@integrity[:raw_local] = fc.result
|
228
|
+
end
|
229
|
+
fc.verify!
|
230
|
+
end
|
231
|
+
|
232
|
+
if !@should_cancel && @integrity[:raw] != @integrity[:raw_local]
|
233
|
+
@status = ["[INTEGRITY] extracted raw file corrupted! (remote: #{@integrity[:raw]}, local: #{@integrity[:raw_local]})", :red]
|
234
|
+
throw :abort_execution, true
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def _l_import_file
|
239
|
+
if File.size(@local_file_raw) > app.opts[:deferred_threshold] && app.opts[:deferred_import]
|
240
|
+
@deferred = true
|
241
|
+
@perform << "l_wait_for_workers"
|
242
|
+
else
|
243
|
+
# cancel!("importing not yet implemented", true)
|
244
|
+
_do_import_file
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def _l_wait_for_workers
|
249
|
+
@perform << "l_import_file_deferred"
|
250
|
+
wait_defer_ready
|
251
|
+
end
|
252
|
+
|
253
|
+
def _l_import_file_deferred
|
254
|
+
@status = ["importing #{human_bytes(File.size(@local_file_raw))} SQL data into local server...", :yellow]
|
255
|
+
_do_import_file(@local_file_raw)
|
256
|
+
end
|
257
|
+
|
258
|
+
def _do_import_file
|
259
|
+
@status = ["importing #{human_bytes(File.size(@local_file_raw))} SQL data into local server...", :yellow]
|
260
|
+
|
261
|
+
imp = @var.data["importer"]
|
262
|
+
impf = @var.parse_flags(var.data["importer_flags"]).merge(deferred: @deferred)
|
263
|
+
|
264
|
+
if imp == "void10"
|
265
|
+
t = app.channelfy_thread app.spawn_thread(:sklaventreiber_worker_io_import_sql) {|thr| thr.wait(10) }
|
266
|
+
second_progress(t, "importing with void10, sleeping 10 seconds (:seconds)...").join
|
267
|
+
elsif imp == "sequel" || @var.constraint(table)
|
268
|
+
raise NotImplementedError, "SequelImporter is not yet implemented/ported to new db_sucker version!"
|
269
|
+
# # imp_was_sequel = imp == "sequel"
|
270
|
+
# # imp = "sequel"
|
271
|
+
# # t = app.channelfy_thread Thread.new {
|
272
|
+
# # Thread.current[:importer] = imp = SequelImporter.new(worker, file, ignore_errors: !imp_was_sequel)
|
273
|
+
# # imp.start
|
274
|
+
# # }
|
275
|
+
# var.load_local_file(self, file) do |importer, channel|
|
276
|
+
# case importer
|
277
|
+
# when "sequel"
|
278
|
+
# sequel_progress(channel).join
|
279
|
+
# if channel[:importer].error
|
280
|
+
# @status = ["importing with Sequel", :yellow]
|
281
|
+
# raise channel[:importer].error
|
282
|
+
# end
|
283
|
+
# else second_progress(channel, "#{"(deferred) " if deferred}loading file (#{human_filesize(File.size(file))}) into local SQL server (:seconds)...").join
|
284
|
+
# end
|
285
|
+
# throw :abort_execution, channel[:error_message] if channel[:error_message]
|
286
|
+
# @return_message = channel[:return_message] if channel[:return_message]
|
287
|
+
# end
|
288
|
+
elsif imp == "binary"
|
289
|
+
t = app.channelfy_thread app.spawn_thread(:sklaventreiber_worker_io_import_sql) {|thr|
|
290
|
+
begin
|
291
|
+
file_import_sql(@ctn, :instruction) do |fi|
|
292
|
+
@status = [fi, "yellow"]
|
293
|
+
fi.instruction = @var.import_instruction_for(@local_file_raw, impf)
|
294
|
+
fi.filesize = File.size(@local_file_raw)
|
295
|
+
fi.status_format = app.opts[:status_format]
|
296
|
+
fi.abort_if { @should_cancel }
|
297
|
+
fi.import!
|
298
|
+
end
|
299
|
+
rescue Worker::IO::FileImportSql::ImportError => ex
|
300
|
+
fail! "ImportError: #{ex.message}"
|
301
|
+
sleep 3
|
302
|
+
end
|
303
|
+
}
|
304
|
+
else
|
305
|
+
raise ImporterNotFoundError, "variation `#{cfg.name}/#{name}' defines unknown importer `#{imp}' (in `#{cfg.src}')"
|
306
|
+
end
|
307
|
+
t.join
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|