dbget_client 0.1.4

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.
@@ -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: []