snatchdb 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,8 @@
1
+ class Hash
2
+ def symbolize!
3
+ keys.each do |key|
4
+ self[(key.to_sym rescue key) || key] = delete(key)
5
+ end
6
+ self
7
+ end
8
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module Snatch
2
+ VERSION = '1.0.0'
3
+ end
data/lib/snatch.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'net/ssh'
2
+ require 'net/sftp'
3
+
4
+ require 'snatch/version'
5
+ require 'snatch/hash'
6
+ require 'snatch/handler'
7
+ require 'snatch/session'
8
+
9
+ module Snatch
10
+ def self.run(config)
11
+ s = Snatch::Session.new(config)
12
+ s.run!
13
+ end
14
+ end
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
+