dbget_client 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in dbget_client.gemspec
4
+ gemspec
data/README ADDED
@@ -0,0 +1 @@
1
+ dbget client
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ DBGET_LIB_PATH = File.join(File.dirname(__FILE__), '../lib')
4
+ require File.join(DBGET_LIB_PATH, 'dbget')
5
+
6
+ DBGet::Runner.boot(ARGV, 'mysql')
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ DBGET_LIB_PATH = File.join(File.dirname(__FILE__), '../lib')
4
+ require File.join(DBGET_LIB_PATH, 'dbget')
5
+
6
+ DBGet::Runner.boot(ARGV, 'mongo')
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require 'dbget/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "dbget_client"
7
+ s.version = DBGet::VERSION
8
+ s.authors = ["Jan Mendoza"]
9
+ s.email = ["poymode@gmail.com"]
10
+ s.homepage = "https://github.com/poymode/dbget_client"
11
+ s.summary = %q{Client for dbget server}
12
+ s.description = %q{Decrypts and uncompress MySQL and MongoDB backups from dbget server}
13
+
14
+ s.rubyforge_project = "dbget_client"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ # s.add_runtime_dependency "rest-client"
24
+ end
@@ -0,0 +1,17 @@
1
+ require 'fileutils'
2
+ require 'yaml'
3
+ require 'zlib'
4
+ require 'optparse'
5
+
6
+ DBGET_LIB_ROOT = File.expand_path('../dbget', __FILE__)
7
+ require File.join(DBGET_LIB_ROOT, 'version')
8
+
9
+ module DBGet
10
+ autoload :Initializer, File.join(DBGET_LIB_ROOT, 'initializer')
11
+ autoload :DBDump, File.join(DBGET_LIB_ROOT, 'db_dump')
12
+ autoload :Loaders, File.join(DBGET_LIB_ROOT, 'loaders')
13
+ autoload :Constants, File.join(DBGET_LIB_ROOT, 'constants')
14
+ autoload :ConfigLoader, File.join(DBGET_LIB_ROOT, 'config_loader')
15
+ autoload :Runner, File.join(DBGET_LIB_ROOT, 'runner')
16
+ autoload :Utils, File.join(DBGET_LIB_ROOT, 'utils')
17
+ end
@@ -0,0 +1,19 @@
1
+ module DBGet
2
+ class ConfigLoader
3
+
4
+ def initialize(dbget_config, options = {})
5
+ @dbget_config = dbget_config
6
+ @options = options
7
+ end
8
+
9
+ def get_config(db_dump)
10
+ @db_dump = db_dump
11
+
12
+ if @dbget_config['mapping'].include?(db_dump.db)
13
+ @dbget_config
14
+ else
15
+ raise "#{db_dump.db} is not found in your dbget.yml!"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,6 @@
1
+ module DBGet
2
+ module Constants
3
+ DBGET_CONFIG_FILE = 'dbget.yml'
4
+ MONGO_FILE_EXT = '.bson'
5
+ end
6
+ end
@@ -0,0 +1,124 @@
1
+ module DBGet
2
+ class DBDump
3
+ include Utils
4
+ include Constants
5
+
6
+ attr_accessor :dump_path, :collections
7
+ attr_reader :db, :database, :db_path
8
+ attr_reader :host, :port, :username, :password
9
+ attr_reader :clean, :verbose
10
+
11
+ def initialize(options)
12
+ @db = options[:db]
13
+ @date = options[:date]
14
+ @key = options[:key]
15
+ @dbtype = options[:dbtype]
16
+ @server = options[:server]
17
+ @opt_db_name = options[:opt_db_name]
18
+ @verbose = options[:verbose]
19
+ @append_date = options[:append_date]
20
+ @clean = options[:clean]
21
+ @collections = options[:collections] || []
22
+ @header = {}
23
+ end
24
+
25
+ def load!(db_config)
26
+ @server ||= db_config['source']['server']
27
+ @key ||= db_config['key']
28
+ @database = @opt_db_name || db_config['mapping'][@db][@dbtype]
29
+ @host = db_config['target'][@dbtype]['host']
30
+ @port = db_config['target'][@dbtype]['port']
31
+ @username = db_config['target'][@dbtype]['username']
32
+ @password = db_config['target'][@dbtype]['password']
33
+ @dump_path = db_config['source']['dump_path']
34
+ @db_path = File.join(@dump_path, "#{@db}")
35
+
36
+ if @append_date
37
+ @database = @database.concat("_#{@date.delete('-')}")
38
+ end
39
+
40
+ get_dump(db_config)
41
+
42
+ if @header[:status] == 'SUCCESS'
43
+ if !@database.nil?
44
+ Utils.say "Dump info of #{@db} to #{@dbtype} using connection: \n" +
45
+ " host: #{@host}\n" +
46
+ " port: #{@port}\n" +
47
+ " user: #{@username}\n" +
48
+ " database: #{@database}\n"
49
+
50
+ if @dbtype == 'mysql'
51
+ Loaders::MySQL.new(self).load!
52
+ else
53
+ Loaders::MongoDB.new(self).load!
54
+ end
55
+ else
56
+ raise "Database #{@db} for #{@dbtype} is not found on #{DBGET_CONFIG_FILE}!"
57
+ end
58
+ else
59
+ raise "There was a problem fetching the database from the server!"
60
+ end
61
+ end
62
+
63
+ protected
64
+
65
+ def get_dump(db_config)
66
+ user = db_config['source']['user']
67
+ host = db_config['source']['host']
68
+
69
+ unless File.exist? @dump_path
70
+ FileUtils.mkdir_p(@dump_path)
71
+ end
72
+
73
+ ssh_params = "#{user}@#{host} db=#{@db} date=#{@date} " +
74
+ "dbtype=#{@dbtype} server=#{@server}"
75
+
76
+ if !@key.nil?
77
+ ssh_cmd = "#{SSH_CMD} -i #{@key} #{ssh_params}"
78
+ else
79
+ ssh_cmd = "#{SSH_CMD} #{ssh_params}"
80
+ end
81
+
82
+ Utils.say "Fetching..."
83
+
84
+ io_handle = IO.popen(ssh_cmd)
85
+
86
+ parse_header(io_handle)
87
+
88
+ if !io_handle.eof?
89
+ File.open(@db_path, "w+") do |f|
90
+ gz = Zlib::GzipReader.new(io_handle)
91
+
92
+ while !io_handle.eof?
93
+ g = gz.read(16*1024)
94
+ f.write(g)
95
+ end
96
+
97
+ f.write(gz.read)
98
+
99
+ gz.close
100
+ f.close
101
+ end
102
+ end
103
+ end
104
+
105
+ def parse_header(io_handle)
106
+ while (s = io_handle.readline) != "\r\n"
107
+ s = s.split(': ')
108
+ @header[s.first.to_sym] = s.last.chomp
109
+ end
110
+
111
+ print_header if @verbose
112
+
113
+ if @header[:status] != "SUCCESS"
114
+ raise "Server returned #{@header[:status]}!"
115
+ end
116
+ end
117
+
118
+ def print_header
119
+ @header.each do |k, v|
120
+ puts ">> #{k.capitalize}: #{v}"
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,53 @@
1
+ module DBGet
2
+ class Initializer
3
+ include Constants
4
+
5
+ def self.boot(options)
6
+ self.new(options).init
7
+ end
8
+
9
+ def initialize(options)
10
+ @options = options
11
+ end
12
+
13
+ def init
14
+ load_dbget_config
15
+
16
+ @options[:databases].each do |d|
17
+ @options[:db] = d
18
+
19
+ db_dump = DBGet::DBDump.new(@options)
20
+
21
+ load_db_dumps(db_dump)
22
+ end
23
+ end
24
+
25
+ protected
26
+
27
+ def load_dbget_config
28
+ config_path = File.join(@options[:dbget_path], DBGET_CONFIG_FILE)
29
+
30
+ if File.exists?(config_path)
31
+ @dbget_config = YAML.load_file(config_path)
32
+ else
33
+ raise "Cannot find #{config_path}!"
34
+ end
35
+ end
36
+
37
+ def load_db_dumps(db_dump)
38
+ if !@dbget_config.empty?
39
+ config_loader = DBGet::ConfigLoader.new(@dbget_config, @options)
40
+ else
41
+ raise "Your dbget.yml is empty!"
42
+ end
43
+
44
+ db_dump.load!(config_loader.get_config(db_dump))
45
+ end
46
+
47
+ def cleanup_db_dumps(db_dumps)
48
+ db_dumps.each do |db_dump|
49
+ db_dump.clean_up!
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,6 @@
1
+ module DBGet
2
+ module Loaders
3
+ autoload :MongoDB, File.join(DBGET_LIB_ROOT, 'loaders/mongodb')
4
+ autoload :MySQL, File.join(DBGET_LIB_ROOT, 'loaders/mysql')
5
+ end
6
+ end
@@ -0,0 +1,69 @@
1
+ module DBGet
2
+ module Loaders
3
+ class MongoDB
4
+ include Utils
5
+ include Constants
6
+
7
+ def self.boot(dump)
8
+ # some boot here
9
+
10
+ self.new(dump)
11
+ end
12
+
13
+ def initialize(dump)
14
+ @dump = dump
15
+ end
16
+
17
+ def load!
18
+ temp_path = File.join(@dump.dump_path, "#{@dump.db}_#{Utils.randomize(16)}")
19
+
20
+ if !File.exists?(temp_path)
21
+ FileUtils.mkdir(temp_path)
22
+
23
+ Utils.say_with_time "Extracting archive..." do
24
+ `#{TAR_CMD} -C #{temp_path} -xf #{File.join(@dump.dump_path, @dump.db)} 2> /dev/null`
25
+ end
26
+
27
+ Utils.say_with_time "Moving mongo files..." do
28
+ `#{FIND_CMD} #{temp_path} -name '*.#{MONGO_FILE_EXT}'`.each_line do |l|
29
+ FileUtils.mv(l.chomp!, File.join(temp_path, File.basename(l)))
30
+ end
31
+ end
32
+ end
33
+
34
+ dump_files = Dir["#{temp_path}/*#{MONGO_FILE_EXT}"]
35
+
36
+ if !@dump.collections.empty?
37
+ @dump.collections = @dump.collections.collect do |c|
38
+ File.join(temp_path, c.concat(MONGO_FILE_EXT))
39
+ end
40
+
41
+ dump_files &= @dump.collections
42
+ end
43
+
44
+ dump_files.each do |d|
45
+ # do not include indexes
46
+ if File.basename(d) != "system.indexes#{MONGO_FILE_EXT}"
47
+ Utils.say_with_time "Dumping #{d}..." do
48
+ if !@dump.verbose
49
+ `#{MONGORESTORE_CMD} -d #{@dump.database} #{d} --drop`
50
+ else
51
+ system "#{MONGORESTORE_CMD} -d #{@dump.database} #{d} --drop"
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ Utils.say "Hooray! Dump for #{@dump.db} done!"
58
+
59
+ if FileUtils.rm_rf(File.join(@dump.dump_path, @dump.db))
60
+ Utils.say "Dump file removed!"
61
+ end
62
+
63
+ if FileUtils.rm_rf(temp_path)
64
+ Utils.say "Temp directory removed!"
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,50 @@
1
+ module DBGet
2
+ module Loaders
3
+ class MySQL
4
+ include Utils
5
+ include Constants
6
+
7
+ def self.boot(dump)
8
+ # some boot here
9
+
10
+ self.new(dump)
11
+ end
12
+
13
+ def initialize(dump)
14
+ @dump = dump
15
+ end
16
+
17
+ def load!
18
+ command = "#{MYSQL_CMD} "
19
+ command += "-h#{@dump.host} "
20
+ command += "-P#{@dump.port} "
21
+ command += "-u#{@dump.username} "
22
+ command += "-p#{@dump.password} " if @dump.password
23
+
24
+ if @dump.clean
25
+ Utils.say_with_time "Dropping database..." do
26
+ system "echo \"DROP DATABASE IF EXISTS #{@dump.database}\" | #{command}"
27
+ end
28
+ end
29
+
30
+ system "echo \"CREATE DATABASE IF NOT EXISTS #{@dump.database}\" | #{command}"
31
+
32
+ if File.exist?(@dump.db_path) and !File.size?(@dump.db_path).nil?
33
+ command += " #{@dump.database} "
34
+
35
+ Utils.say_with_time "Dumping #{@dump.db}..." do
36
+ system "#{command}< #{File.join(@dump.dump_path, @dump.db)}"
37
+ end
38
+
39
+ if FileUtils.rm_rf(File.join(@dump.dump_path, @dump.db))
40
+ Utils.say "Cleaned temporary file!"
41
+ end
42
+ else
43
+ raise "Dump for #{@dump.db} not found!"
44
+ end
45
+
46
+ Utils.say "Hooray! Dump for #{@dump.db} done!"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,102 @@
1
+ module DBGet
2
+ class Runner
3
+ def self.boot(args, dbtype)
4
+ DBGet::Constants.const_set("MYSQL_CMD", `which mysql 2>/dev/null`.strip)
5
+ DBGet::Constants.const_set("SSH_CMD", `which ssh 2>/dev/null`.strip)
6
+ DBGet::Constants.const_set("MONGORESTORE_CMD", `which mongorestore 2>/dev/null`.strip)
7
+ DBGet::Constants.const_set("FIND_CMD", `which find 2>/dev/null`.strip)
8
+ DBGet::Constants.const_set("TAR_CMD", `which tar 2>/dev/null`.strip)
9
+
10
+ self.new(args, dbtype)
11
+ end
12
+
13
+ def initialize(args, dbtype)
14
+ options = {}
15
+ options[:append_date] = false
16
+ options[:verbose] = false
17
+
18
+ optparse = OptionParser.new do |opts|
19
+ opts.banner = "Usage: dbget [options] db1 db2 ...\n"
20
+ opts.separator "Options:"
21
+
22
+ opts.on('-d', '--date DATE', 'Date of database dump (yyyy-mm-dd).') do |date|
23
+ options[:date] = date
24
+ end
25
+
26
+ opts.on('-c', '--clean', 'Drops the database before dumping') do
27
+ options[:clean] = true
28
+ end
29
+
30
+ opts.on('-f', '--force', 'Force the use of dbget in production.') do
31
+ options[:force] = true
32
+ end
33
+
34
+ opts.on('-n', '--name NAME', 'Explicitly declare a database name for dumping.') do |name|
35
+ options[:opt_db_name] = name
36
+ end
37
+
38
+ opts.on('-i', '--key KEY', 'Specify ssh connection key') do |key|
39
+ options[:key] = key
40
+ end
41
+
42
+ opts.on('-s', '--server SERVER', 'Specify the server that contained the database.') do |server|
43
+ options[:server] = server
44
+ end
45
+
46
+ opts.on('-a', '--append-date', 'Append the given date as suffix') do
47
+ options[:append_date] = true
48
+ end
49
+
50
+ opts.on('--collections COLLECTION', Array, 'Only dump specific mongodb collections separated by comma') do |collection|
51
+ options[:collections] = collection
52
+ end
53
+
54
+ opts.on('-v', '--verbose', 'Execute NERD mode!') do
55
+ options[:verbose] = true
56
+ end
57
+
58
+ opts.on('-V', '--version', 'Version') do
59
+ puts DBGet::VERSION
60
+ exit
61
+ end
62
+
63
+ opts.on('-h', '--help', 'Display this screen') do
64
+ puts opts
65
+ exit
66
+ end
67
+ end
68
+
69
+ optparse.parse!
70
+
71
+ if args.length == 0
72
+ puts optparse.help
73
+ else
74
+ options[:dbtype] = dbtype
75
+ options[:databases] = args.dup
76
+
77
+ run(options)
78
+ end
79
+ end
80
+
81
+ def run(opts)
82
+ if ENV.include?('DBGET_PATH')
83
+ opts[:dbget_path] = ENV['DBGET_PATH']
84
+ else
85
+ raise "Cannot find DBGET_PATH!"
86
+ end
87
+
88
+ # lets turn off opt_db_name if we have more arguments
89
+ # this feature is not supported at the moment
90
+ if opts[:databases].count > 1 and opts.include?(:opt_db_name)
91
+ raise "You cannot use -n with multiple databases!"
92
+ end
93
+
94
+ # check if -a switch is give but no -d
95
+ if opts[:append_date] and !opts.include?(:date)
96
+ raise "You cannnot use -a without -d!"
97
+ end
98
+
99
+ DBGet::Initializer.boot(opts)
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,23 @@
1
+ require 'benchmark'
2
+
3
+ module DBGet
4
+ module Utils
5
+ def self.say(message, subitem = false)
6
+ puts "#{subitem ? " ->" : "--"} #{message}"
7
+ end
8
+
9
+ def self.say_with_time(message)
10
+ say(message)
11
+ result = nil
12
+ time = Benchmark.measure { result = yield }
13
+ say "%.4fs" % time.real, :subitem
14
+ say("#{result} rows", :subitem) if result.is_a?(Integer)
15
+ result
16
+ end
17
+
18
+ def self.randomize(size)
19
+ chars = ('a'..'z').to_a + ('A'..'Z').to_a
20
+ (0...size).collect { chars[Kernel.rand(chars.length)] }.join
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module DBGet
2
+ VERSION = "0.1.4"
3
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dbget_client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.4
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jan Mendoza
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-20 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: Decrypts and uncompress MySQL and MongoDB backups from dbget server
15
+ email:
16
+ - poymode@gmail.com
17
+ executables:
18
+ - dbget
19
+ - mongoget
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - .gitignore
24
+ - Gemfile
25
+ - README
26
+ - Rakefile
27
+ - bin/dbget
28
+ - bin/mongoget
29
+ - dbget_client.gemspec
30
+ - lib/dbget.rb
31
+ - lib/dbget/config_loader.rb
32
+ - lib/dbget/constants.rb
33
+ - lib/dbget/db_dump.rb
34
+ - lib/dbget/initializer.rb
35
+ - lib/dbget/loaders.rb
36
+ - lib/dbget/loaders/mongodb.rb
37
+ - lib/dbget/loaders/mysql.rb
38
+ - lib/dbget/runner.rb
39
+ - lib/dbget/utils.rb
40
+ - lib/dbget/version.rb
41
+ homepage: https://github.com/poymode/dbget_client
42
+ licenses: []
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ requirements: []
60
+ rubyforge_project: dbget_client
61
+ rubygems_version: 1.8.10
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: Client for dbget server
65
+ test_files: []