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,222 @@
|
|
1
|
+
module DbSucker
|
2
|
+
class Application
|
3
|
+
module Core
|
4
|
+
def sandboxed &block
|
5
|
+
block.call
|
6
|
+
rescue StandardError => ex
|
7
|
+
e = ["#{ex.class}: #{ex.message}"]
|
8
|
+
ex.backtrace.each{|l| e << "\t#{l}" }
|
9
|
+
error(e.join("\n"))
|
10
|
+
return false
|
11
|
+
end
|
12
|
+
|
13
|
+
def notify_exception label_or_ex, ex = nil
|
14
|
+
error [].tap{|e|
|
15
|
+
e << label_or_ex if ex
|
16
|
+
e << "#{"\t" if ex}#{ex ? ex.class : label_or_ex.class}: #{ex ? ex.message : label_or_ex.message}"
|
17
|
+
(ex ? ex : label_or_ex).backtrace.each{|l| e << "\t#{" " if ex}#{l}" }
|
18
|
+
}.join("\n")
|
19
|
+
end
|
20
|
+
|
21
|
+
def dump_file ctx, open = false, &block
|
22
|
+
"#{core_tmp_path}/#{ctx}-#{Time.current.to_i}-#{uniqid}.log".tap do |df|
|
23
|
+
FileUtils.mkdir_p(File.dirname(df))
|
24
|
+
File.open(df, "wb", &block) if block
|
25
|
+
if block && open && sdf = Shellwords.shellescape(df)
|
26
|
+
fork { exec("#{opts[:core_dump_editor]} #{sdf} ; rm #{sdf}") }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def dump_core
|
32
|
+
dump_file "coredump", true do |f|
|
33
|
+
# thread info
|
34
|
+
f.puts "#{Thread.list.length} threads:\n"
|
35
|
+
Thread.list.each do |thr|
|
36
|
+
f.puts "#{thr.inspect}"
|
37
|
+
f.puts " iType: #{thr == Thread.main ? :main_thread : thr[:itype] || :uncategorized}"
|
38
|
+
f.puts " Aborts?: #{thr.abort_on_exception}"
|
39
|
+
f.puts " Priority: #{thr.priority}"
|
40
|
+
f.puts " T-Vars: #{thr.thread_variables.inspect}"
|
41
|
+
thr.thread_variables.each {|k| f.puts " #{k} => #{thr.thread_variable(k)}" }
|
42
|
+
f.puts " F-Vars: #{thr.keys.inspect}"
|
43
|
+
thr.keys.each {|k| f.puts " #{k} => #{thr[k]}" }
|
44
|
+
f.puts " Backtrace: #{thr.backtrace.length}"
|
45
|
+
thr.backtrace.each {|l| f.puts " #{l}" }
|
46
|
+
#f.puts " Caller: #{thr.backtrace_locations.try(:length) || "unknown"}"
|
47
|
+
#thr.backtrace_locations.each {|l| f.puts " #{l}" } if thr.backtrace_locations
|
48
|
+
f.puts
|
49
|
+
end
|
50
|
+
|
51
|
+
# worker info
|
52
|
+
f.puts "\n\n#{sklaventreiber.workers.length} workers:\n"
|
53
|
+
sklaventreiber.workers.each do |w|
|
54
|
+
f.puts "#{"[SSH] " if w.sshing} #{w.descriptive} #{w.state}".strip
|
55
|
+
end
|
56
|
+
|
57
|
+
# slot pool
|
58
|
+
f.puts "\n\n#{sklaventreiber.slot_pools.length} slot pools:\n"
|
59
|
+
sklaventreiber.slot_pools.each do |name, pool|
|
60
|
+
f.puts "#{name}: #{pool.slots}"
|
61
|
+
pool.active.each do |thr|
|
62
|
+
f.puts "\tactive\t #{thr} [#{thr[:itype]}]"
|
63
|
+
end
|
64
|
+
pool.waiting.each do |wthr, tthr|
|
65
|
+
f.puts "\twaiting\t #{tthr} [#{tthr[:itype]}]"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def sync &block
|
72
|
+
@monitor.synchronize(&block)
|
73
|
+
end
|
74
|
+
|
75
|
+
def outsync &block
|
76
|
+
@output_monitor.synchronize(&block)
|
77
|
+
end
|
78
|
+
|
79
|
+
def uniqid
|
80
|
+
Digest::SHA1.hexdigest(SecureRandom.urlsafe_base64(128))
|
81
|
+
end
|
82
|
+
|
83
|
+
def spawn_thread type, &block
|
84
|
+
waitlock = Queue.new
|
85
|
+
sync do
|
86
|
+
if !@opts[:"tp_#{type}"]
|
87
|
+
warning "Thread type `#{type}' has no priority setting, defaulting to 0..."
|
88
|
+
end
|
89
|
+
|
90
|
+
# spawn thread
|
91
|
+
Thread.new do
|
92
|
+
waitlock.pop
|
93
|
+
block.call(Thread.current)
|
94
|
+
end.tap do |thr|
|
95
|
+
# set type and priority
|
96
|
+
thr[:itype] = type
|
97
|
+
thr.priority = @opts[:"tp_#{type}"]
|
98
|
+
thr.abort_on_exception = true
|
99
|
+
|
100
|
+
# add signal methods
|
101
|
+
signalify_thread(thr)
|
102
|
+
|
103
|
+
# start thread
|
104
|
+
waitlock << true
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def signalify_thread thr
|
110
|
+
# set lock and signal
|
111
|
+
thr[:monitor] = Monitor.new
|
112
|
+
thr[:signal] = thr[:monitor].new_cond
|
113
|
+
|
114
|
+
# define helper methods
|
115
|
+
def thr.signal
|
116
|
+
self[:monitor].synchronize{ self[:signal].broadcast } ; self
|
117
|
+
end
|
118
|
+
def thr.wait(*a)
|
119
|
+
self[:monitor].synchronize{ self[:signal].wait(*a) }
|
120
|
+
end
|
121
|
+
def thr.sync(&b)
|
122
|
+
self[:monitor].synchronize(&b)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def channelfy_thread thr
|
127
|
+
def thr.active?
|
128
|
+
alive?
|
129
|
+
end
|
130
|
+
|
131
|
+
def thr.closed?
|
132
|
+
!alive?
|
133
|
+
end
|
134
|
+
|
135
|
+
def thr.closing?
|
136
|
+
false
|
137
|
+
end
|
138
|
+
|
139
|
+
thr
|
140
|
+
end
|
141
|
+
|
142
|
+
def fake_channel &block
|
143
|
+
FakeChannel.new(&block).tap do |ch|
|
144
|
+
channelfy_thread(ch)
|
145
|
+
signalify_thread(ch)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def wakeup_handlers
|
150
|
+
Thread.list.each{|thr| thr.try(:signal) }
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
# =================
|
155
|
+
# = Configuration =
|
156
|
+
# =================
|
157
|
+
def core_cfg_path
|
158
|
+
File.expand_path(ENV["DBS_CFGDIR"].presence || "~/.db_sucker")
|
159
|
+
end
|
160
|
+
|
161
|
+
def core_tmp_path
|
162
|
+
"#{File.expand_path(ENV["DBS_TMPDIR"] || ENV["TMPDIR"] || "/tmp")}/db_sucker_temp"
|
163
|
+
end
|
164
|
+
|
165
|
+
def core_cfg_configfile
|
166
|
+
"#{core_cfg_path}/config.rb"
|
167
|
+
end
|
168
|
+
|
169
|
+
def load_appconfig
|
170
|
+
return unless File.exist?(core_cfg_configfile)
|
171
|
+
instance_eval File.read(core_cfg_configfile, encoding: "utf-8"), core_cfg_configfile
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
# ===================
|
176
|
+
# = Signal trapping =
|
177
|
+
# ===================
|
178
|
+
def trap_signals
|
179
|
+
debug "Trapping signals..."
|
180
|
+
Signal.trap("INT") do
|
181
|
+
$core_runtime_exiting = 1
|
182
|
+
Kernel.puts "Interrupting..."
|
183
|
+
end
|
184
|
+
Signal.trap("TERM") do
|
185
|
+
$core_runtime_exiting = 2
|
186
|
+
Kernel.puts "Terminating..."
|
187
|
+
end
|
188
|
+
Signal.trap("USR1") do
|
189
|
+
dump_core
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def release_signals
|
194
|
+
debug "Releasing signal traps..."
|
195
|
+
Signal.trap("INT", "DEFAULT")
|
196
|
+
Signal.trap("TERM", "DEFAULT")
|
197
|
+
Signal.trap("USR1", "DEFAULT")
|
198
|
+
end
|
199
|
+
|
200
|
+
def haltpoint
|
201
|
+
raise Interrupt if $core_runtime_exiting && !$core_runtime_graceful
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
# ==========
|
206
|
+
# = Events =
|
207
|
+
# ==========
|
208
|
+
def hook *which, &hook_block
|
209
|
+
which.each do |w|
|
210
|
+
@hooks[w.to_sym] ||= []
|
211
|
+
@hooks[w.to_sym] << hook_block
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def fire which, *args
|
216
|
+
return if @disable_event_firing
|
217
|
+
sync { debug "[Event] Firing #{which} (#{@hooks[which].try(:length) || 0} handlers) #{args.map(&:to_s)}", 99 }
|
218
|
+
@hooks[which] && @hooks[which].each{|h| h.call(self, *args) }
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
@@ -0,0 +1,364 @@
|
|
1
|
+
module DbSucker
|
2
|
+
class Application
|
3
|
+
module Dispatch
|
4
|
+
def dispatch action = (@opts[:dispatch] || :help)
|
5
|
+
if respond_to?("dispatch_#{action}")
|
6
|
+
has_action = true
|
7
|
+
fire(:dispatch_before, action)
|
8
|
+
send("dispatch_#{action}")
|
9
|
+
else
|
10
|
+
fire(:dispatch_before, false)
|
11
|
+
abort("unknown action `#{action}'\nRegistered actions: #{available_actions.join(", ")}")
|
12
|
+
end
|
13
|
+
rescue StandardError => ex
|
14
|
+
fire(:dispatch_after, has_action ? action : false, ex)
|
15
|
+
raise ex
|
16
|
+
else
|
17
|
+
fire(:dispatch_after, has_action ? action : false)
|
18
|
+
end
|
19
|
+
|
20
|
+
def available_actions
|
21
|
+
methods.select{|m| m.to_s.start_with?("dispatch_") }.map{|s| s.to_s.gsub(/\Adispatch_/, "").to_sym }
|
22
|
+
end
|
23
|
+
|
24
|
+
def configful_dispatch identifier, variation, &block
|
25
|
+
log "Using config directory #{c core_cfg_path.to_s, :magenta}"
|
26
|
+
|
27
|
+
all_cfgs = cfg.yml_configs(true)
|
28
|
+
enabled = cfg.yml_configs(false)
|
29
|
+
disabled = all_cfgs - enabled
|
30
|
+
r = c("Found #{c all_cfgs.count, :blue} #{c "config files"}")
|
31
|
+
r << c(" (#{c "#{disabled.count} disabled", :red}#{c ")"}") if disabled.any?
|
32
|
+
log r << c("...")
|
33
|
+
|
34
|
+
cfg.load_all_configs
|
35
|
+
ctn = cfg.get(identifier)
|
36
|
+
|
37
|
+
if identifier.present? && !ctn
|
38
|
+
abort "Identifier `#{identifier}' couldn't be found!", 1
|
39
|
+
end
|
40
|
+
|
41
|
+
if ctn && variation && !(var = ctn.variation(variation))
|
42
|
+
abort "Variation `#{variation}' for identifier `#{identifier}' couldn't be found!", 1
|
43
|
+
end
|
44
|
+
|
45
|
+
begin
|
46
|
+
ctn.try(:ssh_begin)
|
47
|
+
block.call(identifier, ctn, variation, var)
|
48
|
+
ensure
|
49
|
+
ctn.try(:ssh_end)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# ----------------------------------------------------------------------
|
54
|
+
|
55
|
+
# ====================
|
56
|
+
# = Dev/Test actions =
|
57
|
+
# ====================
|
58
|
+
|
59
|
+
# via -a/--action cloop
|
60
|
+
def dispatch_cloop
|
61
|
+
begin
|
62
|
+
trap_signals
|
63
|
+
@sklaventreiber = SklavenTreiber.new(self, uniqid)
|
64
|
+
@sklaventreiber.spooled do
|
65
|
+
@opts[:stdout].disable
|
66
|
+
hook(:prompt_start) { @opts[:stdout].enable }
|
67
|
+
hook(:prompt_stop) { @opts[:stdout].disable(true) }
|
68
|
+
begin
|
69
|
+
@sklaventreiber._init_window
|
70
|
+
loop do
|
71
|
+
break if $core_runtime_exiting
|
72
|
+
sleep 0.1
|
73
|
+
end
|
74
|
+
ensure
|
75
|
+
sandboxed { @sklaventreiber.window.try(:stop) }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
ensure
|
79
|
+
release_signals
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# via -a/--action console
|
84
|
+
def dispatch_console
|
85
|
+
configful_dispatch(ARGV.shift, ARGV.shift) do |identifier, ctn, variation, var|
|
86
|
+
begin ; require "pry" ; rescue LoadError ; end
|
87
|
+
::Kernel.binding.pry
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# via -a/--action threadtest
|
92
|
+
def dispatch_threadtest
|
93
|
+
t = Thread.new { Thread.stop }
|
94
|
+
i, m = 1, [0, nil, nil]
|
95
|
+
while i < 3
|
96
|
+
m[0] += i == 1 ? -1 : 1
|
97
|
+
t.priority = m[0]
|
98
|
+
if t.priority != m[0]
|
99
|
+
m[i] = t.priority
|
100
|
+
m[0] = 0
|
101
|
+
i += 1
|
102
|
+
end
|
103
|
+
end
|
104
|
+
t.kill
|
105
|
+
puts "Thread priority: -#{m[1].abs}..+#{m[2].abs}"
|
106
|
+
end
|
107
|
+
|
108
|
+
# via -a/--action sshdiag
|
109
|
+
def dispatch_sshdiag
|
110
|
+
log c("\nPlease wait while we run some tests...\n", :blue)
|
111
|
+
_identifier, _ctn = false, false, false
|
112
|
+
idstr = ARGV.shift
|
113
|
+
varstr = ARGV.shift
|
114
|
+
|
115
|
+
configful_dispatch(idstr, varstr) do |identifier, ctn, variation, var|
|
116
|
+
_identifier = identifier
|
117
|
+
_ctn = ctn
|
118
|
+
channels = []
|
119
|
+
stop = false
|
120
|
+
maxsessions = :unknown
|
121
|
+
begin
|
122
|
+
t = Thread.new {
|
123
|
+
begin
|
124
|
+
loop do
|
125
|
+
ctn.loop_ssh(0.1)
|
126
|
+
end
|
127
|
+
rescue DbSucker::Application::Container::SSH::ChannelOpenFailedError
|
128
|
+
maxsessions = channels.length - channels.select{|c| c[:open_failed] }.length
|
129
|
+
stop = true
|
130
|
+
retry
|
131
|
+
end
|
132
|
+
}
|
133
|
+
250.times do
|
134
|
+
break if stop
|
135
|
+
c, r = ctn.blocking_channel_result("sleep 60", blocking: false, channel: true, use_sh: true)
|
136
|
+
channels << c
|
137
|
+
sleep 0.1
|
138
|
+
end
|
139
|
+
ensure
|
140
|
+
debug "Stopping sessions (#{channels.length})..."
|
141
|
+
channels.each_with_index do |c, i|
|
142
|
+
debug "Channel ##{i+1} #{c[:pid] ? "with PID #{c[:pid]}" : "has no PID"}"
|
143
|
+
ctn.kill_remote_process(c[:pid]) if c[:pid]
|
144
|
+
end
|
145
|
+
log c("\nSSH MaxSessions: #{c maxsessions, :magenta}", :cyan)
|
146
|
+
log "This value determines how many sessions we can multiplex over a single TCP connection."
|
147
|
+
log "Currently, DbSucker can only utilize one connection, thus this value defines the maxmium concurrency."
|
148
|
+
log "If you get errors you can either"
|
149
|
+
log " * increase the SSHd `MaxSessions' setting on the remote (if you can)"
|
150
|
+
log " * reduce the amount of workers and/or remote slots"
|
151
|
+
log " * fix the mess that is this tool, visit #{c "https://github.com/2called-chaos/db_sucker", :blue}"
|
152
|
+
t.kill
|
153
|
+
end
|
154
|
+
end
|
155
|
+
rescue Net::SSH::AuthenticationFailed => ex
|
156
|
+
notify_exception(ex)
|
157
|
+
log "\nDbSucker can't authenticate with the remote host, see exception."
|
158
|
+
log " * username correct?"
|
159
|
+
log " * password/keyfile correct?"
|
160
|
+
log " * check remote /var/log/auth.log?"
|
161
|
+
rescue SocketError => ex
|
162
|
+
notify_exception(ex)
|
163
|
+
log "\nDbSucker can't establish a connection to the remote host " << c(cfg.get(idstr).source["ssh"]["hostname"], :magenta)
|
164
|
+
log " * typo in hostname / IP?"
|
165
|
+
log " * firewall?"
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
# ================
|
170
|
+
# = Main actions =
|
171
|
+
# ================
|
172
|
+
|
173
|
+
# via -h/--help
|
174
|
+
def dispatch_help
|
175
|
+
colorized_help = @optparse.to_s.split("\n").map do |l|
|
176
|
+
if l.start_with?("Usage:")
|
177
|
+
lc = l.split(" ")
|
178
|
+
"#{c lc[0]} #{c lc[1], :blue} #{c lc[2..-1].join(" "), :cyan}"
|
179
|
+
elsif l.start_with?("#")
|
180
|
+
c(l, :blue)
|
181
|
+
elsif l.strip.start_with?("-")
|
182
|
+
"#{c l.to_s[0...33], :cyan}#{c l[33..-1]}"
|
183
|
+
else
|
184
|
+
c(l)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
puts nil, colorized_help, nil
|
188
|
+
puts c("The current config directory is #{c core_cfg_path.to_s, :magenta}"), nil
|
189
|
+
end
|
190
|
+
|
191
|
+
# via -v/--version
|
192
|
+
def dispatch_info
|
193
|
+
your_version = Gem::Version.new(DbSucker::VERSION)
|
194
|
+
puts c ""
|
195
|
+
puts c(" Your version: ", :blue) << c("#{your_version}", :magenta)
|
196
|
+
|
197
|
+
print c(" Current version: ", :blue)
|
198
|
+
if @opts[:check_for_updates]
|
199
|
+
require "net/http"
|
200
|
+
print c("checking...", :blue)
|
201
|
+
|
202
|
+
begin
|
203
|
+
current_version = Gem::Version.new Net::HTTP.get_response(URI.parse(DbSucker::UPDATE_URL)).body.strip
|
204
|
+
|
205
|
+
if current_version > your_version
|
206
|
+
status = c("#{current_version} (consider update)", :red)
|
207
|
+
elsif current_version < your_version
|
208
|
+
status = c("#{current_version} (ahead, beta)", :green)
|
209
|
+
else
|
210
|
+
status = c("#{current_version} (up2date)", :green)
|
211
|
+
end
|
212
|
+
rescue
|
213
|
+
status = c("failed (#{$!.message})", :red)
|
214
|
+
end
|
215
|
+
|
216
|
+
print "#{"\b" * 11}#{" " * 11}#{"\b" * 11}" # reset line
|
217
|
+
puts status
|
218
|
+
else
|
219
|
+
puts c("check disabled", :red)
|
220
|
+
end
|
221
|
+
|
222
|
+
# more info
|
223
|
+
puts c ""
|
224
|
+
puts c " DbSucker is brought to you by #{c "bmonkeys.net", :green}"
|
225
|
+
puts c " Contribute @ #{c "github.com/2called-chaos/db_sucker", :cyan}"
|
226
|
+
puts c " Eat bananas every day!"
|
227
|
+
puts c ""
|
228
|
+
end
|
229
|
+
|
230
|
+
# via --generate
|
231
|
+
def dispatch_generate_config
|
232
|
+
cfg_name = @opts[:config_name] || "default"
|
233
|
+
cfg_file = "#{core_cfg_path}/#{cfg_name}.yml"
|
234
|
+
puts c("Generating container configuration file `#{cfg_name}'")
|
235
|
+
if File.exist?(cfg_file)
|
236
|
+
abort "Conflict, file already exists: #{cfg_file}", 1
|
237
|
+
else
|
238
|
+
puts c("Writing #{cfg_file}", :green)
|
239
|
+
FileUtils.mkdir_p(core_cfg_path)
|
240
|
+
FileUtils.cp("#{ROOT}/doc/container_example.yml", cfg_file)
|
241
|
+
editor = ENV["EDITOR"].presence
|
242
|
+
exec("#{editor.chomp} #{cfg_file}") if editor
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# helper for #dispatch_stat_tmp
|
247
|
+
def _dispatch_stat_tmp_display files, directories, managed, cleanup = false, sftp = false
|
248
|
+
log "Directories: #{c directories.count, :blue}"
|
249
|
+
log " Files: #{c files.count, :blue} #{c "("}#{c managed.count, :blue}#{c " managed)"}"
|
250
|
+
log " Size: #{c human_bytes(files.map(&:second).sum), :blue} #{c "("}#{c human_bytes(managed.map(&:second).sum), :blue} #{c "managed)"}"
|
251
|
+
|
252
|
+
if cleanup
|
253
|
+
if managed.any?
|
254
|
+
log c("WE ONLY SIMULATE! Nothing will be deleted!", :green) if opts[:simulate]
|
255
|
+
warning "----------- Removing #{managed.count} managed files! Press Ctrl-C to abort -----------"
|
256
|
+
managed.each{|f, s| warning " REMOVE #{c "#{f}", :magenta} #{c human_bytes(s), :cyan}" }
|
257
|
+
warning "----------- Removing #{managed.count} managed files! Press Ctrl-C to abort -----------"
|
258
|
+
sleep 3
|
259
|
+
4.times {|n| log "Cleaning up in #{3 - n}..." ; sleep 1 ; rll }
|
260
|
+
|
261
|
+
managed.each do |f, s|
|
262
|
+
if opts[:simulate]
|
263
|
+
warning "(simulate) Removing #{f}..."
|
264
|
+
else
|
265
|
+
warning "Removing #{f}..."
|
266
|
+
sftp ? sftp.remove!(f) : File.unlink(f)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
else
|
270
|
+
log c("No managed files found, nothing to cleanup.", :green)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
# via --stat-tmp
|
276
|
+
def dispatch_stat_tmp cleanup = false
|
277
|
+
configful_dispatch(ARGV.shift, ARGV.shift) do |identifier, ctn, variation, var|
|
278
|
+
if ctn
|
279
|
+
ctn.sftp_begin do |sftp|
|
280
|
+
log c("Analyzing ") << c("remote", :cyan) << c(" temp directory #{c ctn.tmp_path, :magenta}")
|
281
|
+
begin
|
282
|
+
files = sftp.dir.glob("#{ctn.tmp_path}", "**/*")
|
283
|
+
rescue Net::SFTP::StatusException => ex
|
284
|
+
if ex.message["no such file"]
|
285
|
+
abort "Destination directory `#{ctn.tmp_path}' does not exist on the remote side!"
|
286
|
+
else
|
287
|
+
raise
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
d_files = files.select(&:file?).map{|f| ["#{ctn.tmp_path}/#{f.name}", f.attributes.size] }
|
292
|
+
d_directories = files.select(&:directory?).map{|f| ["#{ctn.tmp_path}/#{f.name}"] }
|
293
|
+
d_managed = files.select(&:file?).select{|f| f.name.end_with?(".dbsc", ".dbsc.tmp", ".dbsc.gz") }.map{|f| ["#{ctn.tmp_path}/#{f.name}", f.attributes.size] }
|
294
|
+
_dispatch_stat_tmp_display(d_files, d_directories, d_managed, cleanup, sftp)
|
295
|
+
end
|
296
|
+
else
|
297
|
+
log c("Analyzing ") << c("local", :cyan) << c(" temp directory #{c core_tmp_path, :magenta}")
|
298
|
+
files = Dir.glob("#{core_tmp_path}/**/*")
|
299
|
+
|
300
|
+
managed_files = files
|
301
|
+
d_files = files.select{|f| File.file?(f) }.map{|f| [f, File.size(f)] }
|
302
|
+
d_directories = files.select{|f| File.directory?(f) }
|
303
|
+
d_managed = files.select{|f| File.file?(f) && File.basename(f).end_with?(".dbsc", ".dbsc.tmp", ".dbsc.gz") }.map{|f| [f, File.size(f)] }
|
304
|
+
_dispatch_stat_tmp_display(d_files, d_directories, d_managed, cleanup)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
# via --cleanup-tmp
|
310
|
+
def dispatch_cleanup_tmp
|
311
|
+
dispatch_stat_tmp(true)
|
312
|
+
end
|
313
|
+
|
314
|
+
# via -l/--list-databases
|
315
|
+
def _list_databases identifier, ctn, variation, var
|
316
|
+
return unless opts[:list_databases]
|
317
|
+
log "Listing databases for identifier #{c identifier, :magenta}#{c "..."}"
|
318
|
+
dbs = ctn.database_list(opts[:list_tables])
|
319
|
+
print_db_table_list ctn.hostname, dbs
|
320
|
+
throw :dispatch_handled
|
321
|
+
end
|
322
|
+
|
323
|
+
# via -t/--list-tables
|
324
|
+
def _list_tables identifier, ctn, variation, var
|
325
|
+
return if !(opts[:list_tables].present? && opts[:list_tables] != :all)
|
326
|
+
print_db_table_list ctn.hostname, [[opts[:list_tables], ctn.table_list(opts[:list_tables])]]
|
327
|
+
throw :dispatch_handled
|
328
|
+
end
|
329
|
+
|
330
|
+
# default action if variation given
|
331
|
+
def _suck_variation identifier, ctn, variation, var
|
332
|
+
if ctn && (var || (@opts[:suck_only].any? || @opts[:suck_except].any?))
|
333
|
+
var ||= ctn.variation("default")
|
334
|
+
begin
|
335
|
+
trap_signals
|
336
|
+
@sklaventreiber = SklavenTreiber.new(self, uniqid)
|
337
|
+
@sklaventreiber.spooled do
|
338
|
+
@sklaventreiber.whip_it!(ctn, var)
|
339
|
+
end
|
340
|
+
ensure
|
341
|
+
release_signals
|
342
|
+
end
|
343
|
+
throw :dispatch_handled
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
# default action if no variation is given
|
348
|
+
def _default_listing identifier, ctn, variation, var
|
349
|
+
db_table_listing(ctn ? [[identifier, ctn]] : cfg)
|
350
|
+
end
|
351
|
+
|
352
|
+
# default actions in order
|
353
|
+
def dispatch_index
|
354
|
+
configful_dispatch(ARGV.shift, ARGV.shift) do |identifier, ctn, variation, var|
|
355
|
+
catch :dispatch_handled do
|
356
|
+
[:_list_databases, :_list_tables, :_suck_variation, :_default_listing].each do |meth|
|
357
|
+
__send__(meth, identifier, ctn, variation, var)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|