snatchdb 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/snatch +66 -0
- data/lib/snatch/handler.rb +15 -0
- data/lib/snatch/hash.rb +8 -0
- data/lib/snatch/session.rb +90 -0
- data/lib/snatch/version.rb +3 -0
- data/lib/snatch.rb +14 -0
- metadata +103 -0
data/bin/snatch
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'yaml'
|
8
|
+
require 'optparse'
|
9
|
+
require 'snatch'
|
10
|
+
|
11
|
+
SAMPLE_SCHEMA = <<END
|
12
|
+
---
|
13
|
+
host: YOUR_HOSTNAME
|
14
|
+
user: YOUR_SSH_USER
|
15
|
+
password: YOUR_SSH_PASSWORD
|
16
|
+
db_user: MYSQL_USER
|
17
|
+
db_password: MYSQL_PASSWORD
|
18
|
+
db_list:
|
19
|
+
- database1
|
20
|
+
- database2
|
21
|
+
END
|
22
|
+
|
23
|
+
optparse = OptionParser.new do |opts|
|
24
|
+
opts.banner = "Usage: snatch CONFIG.yml"
|
25
|
+
opts.on('-i', '--info', 'Display this information.') { puts opts ; exit }
|
26
|
+
opts.on('-v', '--version', 'Show version') { puts "Snatch v#{Snatch::VERSION}" ; exit }
|
27
|
+
opts.on('-n', '--new NAME', 'Create a new config file') do |name|
|
28
|
+
unless name =~ /^[a-z\d\_\-]{2,64}$/i
|
29
|
+
puts "Name should be in format: [A-Za-z0-9\-\_]{2,64}"
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
|
33
|
+
file = "#{Dir.pwd}/#{name}.yml"
|
34
|
+
if File.exists?(file)
|
35
|
+
puts "File already exists!" ; exit
|
36
|
+
end
|
37
|
+
|
38
|
+
File.open(file, 'w') { |f| f.write(SAMPLE_SCHEMA) }
|
39
|
+
puts "File #{file} has been created. Edit and snatch!" ; exit
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
begin
|
44
|
+
optparse.parse!
|
45
|
+
|
46
|
+
unless ARGV.empty?
|
47
|
+
file = File.expand_path(ARGV.shift)
|
48
|
+
unless File.exists?(file)
|
49
|
+
puts "File #{file} was not found!"
|
50
|
+
exit
|
51
|
+
end
|
52
|
+
|
53
|
+
config = YAML.load_file(file)
|
54
|
+
config.symbolize!
|
55
|
+
Snatch.run(config)
|
56
|
+
else
|
57
|
+
puts optparse
|
58
|
+
end
|
59
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument
|
60
|
+
puts optparse
|
61
|
+
exit
|
62
|
+
rescue Interrupt
|
63
|
+
puts "Interrupted."
|
64
|
+
rescue StandardError, Snatch::CredentialsError, Snatch::NoDatabaseError => ex
|
65
|
+
puts "ERROR: #{ex.message}"
|
66
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Snatch
|
2
|
+
class TransferHandler
|
3
|
+
def on_open(downloader, file)
|
4
|
+
puts "ftp: downloading: #{file.remote} -> #{file.local}"
|
5
|
+
end
|
6
|
+
|
7
|
+
def on_close(downloader, file)
|
8
|
+
puts "ftp: finished downloading #{File.basename(file.remote)}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def on_finish(downloader)
|
12
|
+
puts "ftp: file saved."
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/snatch/hash.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
module Snatch
|
2
|
+
class CredentialsError < Exception ; end
|
3
|
+
class NoDatabaseError < Exception ; end
|
4
|
+
|
5
|
+
class Session
|
6
|
+
attr_reader :config, :filename
|
7
|
+
|
8
|
+
def initialize(config)
|
9
|
+
@config = config
|
10
|
+
@ssh = nil
|
11
|
+
@filename = "#{@config[:host]}_#{Time.now.strftime("%Y%m%d%H%M%S")}.sql.gz"
|
12
|
+
end
|
13
|
+
|
14
|
+
def run!
|
15
|
+
connect
|
16
|
+
test
|
17
|
+
dump
|
18
|
+
download
|
19
|
+
cleanup
|
20
|
+
disconnect
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# Connect to remote server
|
26
|
+
def connect
|
27
|
+
puts "ssh: connecting..."
|
28
|
+
begin
|
29
|
+
@ssh = Net::SSH.start(@config[:host], @config[:user],:password => @config[:password])
|
30
|
+
rescue Net::SSH::AuthenticationFailed
|
31
|
+
raise CredentialsError, 'Invalid SSH user or password'
|
32
|
+
end
|
33
|
+
puts "ssh: connected."
|
34
|
+
end
|
35
|
+
|
36
|
+
# Close ssh connection
|
37
|
+
def disconnect
|
38
|
+
@ssh.close
|
39
|
+
puts "ssh: disconnected."
|
40
|
+
end
|
41
|
+
|
42
|
+
# Check configuration options
|
43
|
+
def test
|
44
|
+
resp = @ssh.exec!("mysql --execute='SHOW DATABASES;' --user=#{@config[:db_user]} --password=#{@config[:db_password]}")
|
45
|
+
if resp =~ /ERROR 1045/
|
46
|
+
raise CredentialsError, 'Invalid MySQL user or password'
|
47
|
+
end
|
48
|
+
|
49
|
+
list = resp.split("\n").map { |s| s.strip }.select { |s| !s.empty? }.drop(1)
|
50
|
+
diff = @config[:db_list] - list
|
51
|
+
|
52
|
+
unless diff.empty?
|
53
|
+
raise NoDatabaseError, "Database(s) not found: #{diff.join(', ')}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Generate MySQL database dump
|
58
|
+
def dump
|
59
|
+
cmd = []
|
60
|
+
cmd << "--user=#{@config[:db_user]}"
|
61
|
+
cmd << "--password=#{@config[:db_password]}" unless @config[:db_password].strip.empty?
|
62
|
+
cmd << "--databases #{@config[:db_list].join(' ')}"
|
63
|
+
cmd << "--add-drop-database"
|
64
|
+
cmd << "--add-drop-table"
|
65
|
+
cmd << "--compact"
|
66
|
+
cmd = "mysqldump #{cmd.join(' ')} | gzip --best > /tmp/#{filename}"
|
67
|
+
|
68
|
+
puts "mysql: creating a dump..."
|
69
|
+
@ssh.exec!(cmd)
|
70
|
+
puts "mysql: done."
|
71
|
+
end
|
72
|
+
|
73
|
+
# Cleanup dump
|
74
|
+
def cleanup
|
75
|
+
@ssh.exec!("rm -f /tmp/#{filename}")
|
76
|
+
end
|
77
|
+
|
78
|
+
# Fetch remote dump to local filesystem
|
79
|
+
def download
|
80
|
+
Net::SFTP.start(@config[:host], @config[:user], :password => @config[:password]) do |sftp|
|
81
|
+
t = sftp.download!(
|
82
|
+
"/tmp/#{filename}", # Remote file
|
83
|
+
"#{Dir.pwd}/#{filename}", # Local file
|
84
|
+
:progress => TransferHandler.new # Handler
|
85
|
+
)
|
86
|
+
t.wait
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/snatch.rb
ADDED
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: snatchdb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Dan Sosedoff
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-02-09 00:00:00 -06:00
|
19
|
+
default_executable: snatch
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: net-ssh
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - "="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 63
|
30
|
+
segments:
|
31
|
+
- 2
|
32
|
+
- 0
|
33
|
+
- 24
|
34
|
+
version: 2.0.24
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: net-sftp
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - "="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 5
|
46
|
+
segments:
|
47
|
+
- 2
|
48
|
+
- 0
|
49
|
+
- 5
|
50
|
+
version: 2.0.5
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
description: Remote database downloader
|
54
|
+
email: dan.sosedoff@gmail.com
|
55
|
+
executables:
|
56
|
+
- snatch
|
57
|
+
extensions: []
|
58
|
+
|
59
|
+
extra_rdoc_files: []
|
60
|
+
|
61
|
+
files:
|
62
|
+
- bin/snatch
|
63
|
+
- lib/snatch.rb
|
64
|
+
- lib/snatch/version.rb
|
65
|
+
- lib/snatch/hash.rb
|
66
|
+
- lib/snatch/handler.rb
|
67
|
+
- lib/snatch/session.rb
|
68
|
+
has_rdoc: true
|
69
|
+
homepage: http://github.com/sosedoff/snatch
|
70
|
+
licenses: []
|
71
|
+
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
hash: 3
|
83
|
+
segments:
|
84
|
+
- 0
|
85
|
+
version: "0"
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
hash: 3
|
92
|
+
segments:
|
93
|
+
- 0
|
94
|
+
version: "0"
|
95
|
+
requirements: []
|
96
|
+
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 1.4.1
|
99
|
+
signing_key:
|
100
|
+
specification_version: 3
|
101
|
+
summary: Snatch is a remote database downloader via SSH protocol
|
102
|
+
test_files: []
|
103
|
+
|