new_backup 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,95 @@
1
+ =begin
2
+
3
+ = mysqlcmds.rb
4
+
5
+ *Copyright*:: (C) 2013 by Novu, LLC
6
+ *Author(s)*:: Tamara Temple <tamara.temple@novu.com>
7
+ *Since*:: 2013-05-01
8
+ *License*:: MIT
9
+ *Version*:: 0.0.1
10
+
11
+ == Description
12
+
13
+ Class to wrap mysql command line applications mysqldump, mysqladmin, and mysql.
14
+
15
+ =end
16
+
17
+ require 'methadone'
18
+ require 'RunIt'
19
+
20
+ module NewBackup
21
+
22
+ class MySqlCmds
23
+
24
+ include Methadone::CLILogging
25
+
26
+ attr_accessor :hostname, :username, :password, :database, :obfuscate_script
27
+
28
+
29
+ def initialize(hostname, username, password, database, obfuscate)
30
+ self.hostname=hostname.dup.to_s
31
+ self.username=username.dup.to_s
32
+ self.password=password.dup.to_s
33
+ self.database=database.dup.to_s
34
+ self.obfuscate_script=obfuscate.dup.to_s
35
+
36
+ @commands =
37
+ {:mysqldump => get_command('mysqldump'),
38
+ :mysql => get_command('mysql'),
39
+ :gzip => get_command('gzip')
40
+ }
41
+
42
+ debug "#{self.class}##{__method__}:#{__LINE__}: @commands: #{@commands}"
43
+
44
+ end
45
+
46
+ def dump(save_file)
47
+ cmd = []
48
+ cmd << @commands[:mysqldump]
49
+ cmd << "--host #{self.hostname}"
50
+ cmd << "--user=#{self.username}"
51
+ cmd << "--password=#{self.password}" unless self.password.nil? or self.password.empty?
52
+ cmd << self.database
53
+ cmd << "|"
54
+ cmd << @commands[:gzip]
55
+ cmd << ">"
56
+ cmd << save_file
57
+
58
+ debug "#{self.class}##{__method__}:#{__LINE__}: cmd = #{cmd.join(" ")}"
59
+
60
+ saver = RunIt.new cmd.join(" ")
61
+ saver.run
62
+ raise "#{self.class}#save error: #{saver.result.exitstatus}: #{saver.error.inspect}" unless saver.result.success?
63
+ debug "#{self.class}##{__method__}:#{__LINE__}: saver.output: #{saver.output}"
64
+ end
65
+
66
+ def obfuscate
67
+
68
+ cmd = []
69
+ cmd << @commands[:mysql]
70
+ cmd << "--host #{self.hostname}"
71
+ cmd << "--user #{self.username}"
72
+ cmd << "--password=#{self.password}" unless self.password.nil? or self.password.empty?
73
+ cmd << self.database
74
+ cmd << "<"
75
+ cmd << self.obfuscate_script
76
+
77
+ debug "#{self.class}##{__method__}:#{__LINE__}: cmd= #{cmd.join(" ")}"
78
+ obfuscator = RunIt.new cmd.join(" ")
79
+ obfuscator.run
80
+ raise "#{self.class}##{__method__}:#{__LINE__}: error: #{obfuscator.result.exitstatus}: #{obfuscator.error.inspect}" unless obfuscator.result.success?
81
+ debug "#{self.class}##{__method__}:#{__LINE__}: obfuscator.output: #{obfuscator.output}"
82
+ end
83
+
84
+
85
+ private
86
+
87
+ def get_command(command)
88
+ command_path = `which #{command}`.chomp
89
+ raise "#{command} not found!" if command_path.empty? || command_path.nil?
90
+ command_path
91
+ end
92
+
93
+ end
94
+
95
+ end
@@ -0,0 +1,17 @@
1
+ =begin
2
+
3
+ = new_backup/version.rb
4
+
5
+ *Copyright*:: (C) 2013 by Novu, LLC
6
+ *Author(s)*:: Tamara Temple <tamara.temple@novu.com>
7
+
8
+ =end
9
+
10
+
11
+
12
+
13
+ module NewBackup
14
+ VERSION = "1.0.1"
15
+ DESCRIPTION = "Backup an RDS database instance to S3, and create and obfuscated version"
16
+ SUMMARY = DESCRIPTION
17
+ end
@@ -0,0 +1,27 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'new_backup/version'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "new_backup"
7
+ gem.version = NewBackup::VERSION
8
+ gem.authors = ["Tamara Temple"]
9
+ gem.email = ["tamouse@gmail.com"]
10
+ gem.description = NewBackup::DESCRIPTION
11
+ gem.summary = NewBackup::SUMMARY
12
+ gem.homepage = ""
13
+
14
+ gem.files = `git ls-files`.split($/)
15
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
+ gem.require_paths = ["lib"]
18
+ gem.add_development_dependency('rdoc')
19
+ gem.add_development_dependency('aruba')
20
+ gem.add_development_dependency('rake', '~> 0.9.2')
21
+ gem.add_dependency('methadone', '~> 1.2.6')
22
+ gem.add_dependency('RunIt')
23
+ gem.add_dependency('fog')
24
+ gem.add_dependency('dogapi')
25
+ gem.add_development_dependency('rspec')
26
+ gem.add_development_dependency('debugger')
27
+ end
data/sample-config.yml ADDED
@@ -0,0 +1,20 @@
1
+ # Sample Config file for the RDS->S3 backup script
2
+ fog_timeout: 3600
3
+ dump_directory: /tmp
4
+ dump_ttl: 20
5
+ aws_region: us-west-1
6
+ db_instance_type: db.t1.micro
7
+ timestamp_format: "%FT%T-%X"
8
+ rds_instance_id: myrds
9
+ db_subnet_group_name: yolo
10
+ backup_bucket: raw_backups
11
+ s3_bucket: clean_backups
12
+ s3_prefix: felix
13
+ aws_s3_region: outermars
14
+ mysql_username: despereaux
15
+ mysql_database: gollywogs
16
+ mysql_password: nutz
17
+ obfuscate_script: nolocontendre
18
+ datadog_apikey: youdthink
19
+ aws_access_key_id: bogus aws key
20
+ aws_secret_access_key: bogus aws secret
@@ -0,0 +1,31 @@
1
+ =begin
2
+
3
+ = datadog_spec.rb
4
+
5
+ *Copyright*:: (C) 2013 by Novu, LLC
6
+ *Author(s)*:: Tamara Temple <tamara.temple@novu.com>
7
+ *Since*:: 2013-05-02
8
+ *License*:: GPLv3
9
+ *Version*:: 0.0.1
10
+
11
+ =end
12
+
13
+ require 'spec_helper'
14
+ require 'new_backup/datadog'
15
+
16
+
17
+ module NewBackup
18
+
19
+ describe DataDog do
20
+
21
+ it "#api_key=" do
22
+ NewBackup::DataDog.api_key= '12345'
23
+ NewBackup::DataDog.api_key.should == '12345'
24
+ end
25
+
26
+
27
+
28
+ end
29
+
30
+
31
+ end
data/spec/main_spec.rb ADDED
@@ -0,0 +1,77 @@
1
+ =begin
2
+
3
+ = new_backup_spec.rb
4
+
5
+ *Copyright*:: (C) 2013 by Novu, LLC
6
+ *Author(s)*:: Tamara Temple <tamara.temple@novu.com>
7
+ *Since*:: 2013-05-01
8
+ *License*:: GPLv3
9
+ *Version*:: 0.0.1
10
+
11
+ == Description
12
+
13
+ Test base library module.
14
+
15
+ =end
16
+
17
+ require 'spec_helper'
18
+ require 'new_backup/main'
19
+
20
+ def check_keys(h,klist)
21
+ (h.keys <=> klist) == 0
22
+ end
23
+
24
+ module NewBackup
25
+
26
+ describe Main do
27
+
28
+ describe "processing options and initializing" do
29
+ let(:dogger) {double('dogger')}
30
+
31
+ it "no options specified" do
32
+ expect {NewBackup::Main.new()}.to raise_error(RuntimeError)
33
+ end
34
+
35
+ describe "full options" do
36
+ let(:options) {{
37
+ 'fog_timeout' => 3600,
38
+ 'dump_directory' => '/tmp',
39
+ 'dump_ttl' => 20,
40
+ 'aws_region' => 'us-west-1',
41
+ 'db_instance_type' => 'db.t1.micro',
42
+ 'timestamp_format' => "%FT%T-%Z",
43
+ 'rds_instance_id' => 'myrds',
44
+ 'db_subnet_group_name' => 'yolo',
45
+ 'backup_bucket' => 'raw_backups',
46
+ 's3_bucket' => 'clean_backups',
47
+ 's3_prefix' => 'felix',
48
+ 'aws_s3_region' => 'outermars',
49
+ 'mysql_username' => 'despereaux',
50
+ 'mysql_database' => 'gollywogs',
51
+ 'mysql_password' => 'nutz',
52
+ 'obfuscate_script' => 'nolocontendre',
53
+ 'datadog_apikey' => 'youdthink',
54
+ 'aws_access_key_id' => 'bogus aws key',
55
+ 'aws_secret_access_key' => 'bogus aws secret'
56
+ }}
57
+ let(:main) { NewBackup::Main.new(options) }
58
+ let(:actual_options) { main.options }
59
+
60
+ it {main.should be_a(NewBackup::Main)}
61
+ it {check_keys(actual_options, %w{fog aws rds s3 mysql datadog dump_directory timestamp debug nords nos3}.map(&:to_sym)).should be_true}
62
+ it {check_keys(actual_options[:fog], %w{timeout}.map(&:to_sym)).should be_true}
63
+ it {check_keys(actual_options[:aws], %w{access_key secret_key region}.map(&:to_sym)).should be_true}
64
+ it {check_keys(actual_options[:rds], %w{instance_id subnet_group instance_type}.map(&:to_sym)).should be_true}
65
+ it {check_keys(actual_options[:s3], %w{raw_bucket clean_bucket prefix region dump_ttl}.map(&:to_sym)).should be_true}
66
+ it {check_keys(actual_options[:mysql], %w{username password database obfuscate_script}.map(&:to_sym)).should be_true}
67
+ it {check_keys(actual_options[:datadog], %w{api_key}.map(&:to_sym)).should be_true}
68
+
69
+ it { main.should respond_to(:run) }
70
+
71
+ end
72
+
73
+ end
74
+
75
+ end
76
+
77
+ end
@@ -0,0 +1,83 @@
1
+ =begin
2
+
3
+ = mockbucket.rb
4
+
5
+ *Copyright*:: (C) 2013 by Novu, LLC
6
+ *Author(s)*:: Tamara Temple <tamara.temple@novu.com>
7
+ *Since*:: 2013-05-14
8
+ *License*:: GPLv3
9
+ *Version*:: 0.0.1
10
+
11
+ == Description
12
+
13
+ =end
14
+
15
+ class MockFile
16
+ attr_accessor :name, :last_modified, :key, :directory
17
+ def initialize(n,d,dir)
18
+ self.name = n.dup
19
+ self.key = self.name
20
+ self.last_modified = d.dup
21
+ self.directory = dir
22
+ end
23
+
24
+ def destroy
25
+ self.name = nil
26
+ self.key = nil
27
+ self.last_modified = nil
28
+ end
29
+ end
30
+
31
+ class MockBucket
32
+ attr_accessor :bucket_contents, :name, :key
33
+ def initialize(n=1, name)
34
+ self.bucket_contents = (1..n).to_a.map{|i| MockFile.new("file-#{i}", Time.new(2012, 6, (i+1), 0, 0, 0), self) }
35
+ self.name = name.dup
36
+ self.key = self.name
37
+ end
38
+
39
+ def files
40
+ self
41
+ end
42
+
43
+ def create(options={})
44
+ self.bucket_contents << MockFile.new(options[:key], Time.now, self)
45
+ end
46
+
47
+ def all(options={})
48
+ @bucket_contents.reject {|f| f.name.nil?}
49
+ end
50
+
51
+ end
52
+
53
+
54
+ class MockConnection
55
+ attr_accessor :bucket_list, :name, :key
56
+ def initialize(n=1, name)
57
+ self.name = name.dup
58
+ self.key = self.name
59
+ if n > 0
60
+ self.bucket_list = (1..n).to_a.map{|i| MockBucket.new(10, "bucket-#{i}")}
61
+ else
62
+ self.bucket_list = []
63
+ end
64
+
65
+ end
66
+
67
+ def directories
68
+ @bucket_list
69
+ end
70
+
71
+ def get(name)
72
+ @bucket_list.select {|bucket| bucket.name == name}.first
73
+ end
74
+
75
+ def create(name)
76
+ @bucket_list << MockBucket.new(10, name)
77
+ end
78
+
79
+ def destroy(name)
80
+ @bucket_list.reject! {|bucket| bucket.name == name}
81
+ end
82
+
83
+ end
@@ -0,0 +1,131 @@
1
+ =begin
2
+
3
+ = myrds_spec.rb
4
+
5
+ *Copyright*:: (C) 2013 by Novu, LLC
6
+ *Author(s)*:: Tamara Temple <tamara.temple@novu.com>
7
+ *Since*:: 2013-05-01
8
+ *License*:: GPLv3
9
+ *Version*:: 0.0.1
10
+
11
+ == Description
12
+
13
+ Test myrds.rb
14
+
15
+ =end
16
+
17
+ require 'spec_helper'
18
+ require 'new_backup/myrds.rb'
19
+ require 'fog'
20
+ require 'date'
21
+
22
+ module NewBackup
23
+
24
+ describe MyRds do
25
+
26
+ it {NewBackup::MyRds.should respond_to(:new)}
27
+
28
+ describe "Operations" do
29
+
30
+
31
+ it "#connect" do
32
+ Fog.mock!
33
+ options = {
34
+ :aws => {
35
+ :access_key => 'bogus access key',
36
+ :secret_key => 'bogus secret key',
37
+ :rds_region => 'us-east-1'},
38
+ :fog => {
39
+ :timeout => 10
40
+ },
41
+ :timestamp => DateTime.now.iso8601
42
+ }
43
+
44
+ NewBackup::MyRds.new(options).connect do |connection|
45
+ connection.should_not be_nil
46
+ end
47
+ end
48
+
49
+ it "#retrieve_snapshot" do
50
+
51
+ Fog.mock!
52
+ options = {
53
+ :rds => {
54
+ :instance_id => 'tamtest'
55
+ },
56
+ :aws => {
57
+ :access_key => 'bogus access key',
58
+ :secret_key => 'bogus secret key',
59
+ :rds_region => 'us-east-1'},
60
+ :fog => {
61
+ :timeout => 10},
62
+ :timestamp => DateTime.now.iso8601
63
+ }
64
+
65
+ rds_server = double('rds_server')
66
+ snapshot = double('snapshot')
67
+ rds_server.stub_chain(:snapshots, :new, :save)
68
+ rds_server.stub_chain(:snapshots, :get) {snapshot}
69
+ snapshot.stub(:wait_for)
70
+ snapshot.stub(:destroy)
71
+
72
+ NewBackup::MyRds.new(options).retrieve_snapshot(rds_server) do |snapshot|
73
+ snapshot.should_not be_nil
74
+ end
75
+
76
+ end
77
+
78
+ it "#restore_db" do
79
+
80
+ Fog.mock!
81
+ options = {
82
+ :rds => {
83
+ :instance_id => 'tamtest',
84
+ :subnet_group => '',
85
+ :instance_type => 'db.t1.micro'
86
+ },
87
+ :aws => {
88
+ :access_key => 'bogus access key',
89
+ :secret_key => 'bogus secret key',
90
+ :rds_region => 'us-east-1'},
91
+ :mysql => {
92
+ :database => 'qadb',
93
+ :username => 'root',
94
+ :password => 'aaaa',
95
+ :obfuscate_script => 'zzzz'
96
+ },
97
+ :fog => {
98
+ :timeout => 10
99
+ },
100
+ :timestamp => DateTime.now.iso8601
101
+ }
102
+ connection = double('connection')
103
+ snapshot = double('snapshot')
104
+ backup_server = double('backup_server')
105
+ connection.stub(:restore_db_instance_from_db_snapshot)
106
+ connection.stub_chain(:servers, :get) {backup_server}
107
+ snapshot.stub(:id)
108
+ backup_server.stub_chain(:endpoint, :[]).with("Address").and_return("localhost")
109
+ backup_server.stub(:wait_for)
110
+ backup_server.stub(:destroy)
111
+
112
+ NewBackup::MyRds.new(options).restore_db(connection, snapshot) do |db|
113
+ db.should_not be_nil
114
+ end
115
+
116
+ end
117
+
118
+ end
119
+
120
+
121
+ describe "#snap_name" do
122
+ let(:timestamp) {DateTime.now.iso8601}
123
+ it {NewBackup::MyRds.new(:timestamp => timestamp).snap_name.should == "s3-dump-snap-#{timestamp}" }
124
+ end
125
+
126
+
127
+
128
+
129
+ end
130
+
131
+ end