ariossw-db2s3 0.2.5

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.
data/README ADDED
@@ -0,0 +1,37 @@
1
+ DB2S3 - A rails plugin to backup Mysql to Amazon S3
2
+ ---------------------------------------------------
3
+ You're looking at a monthly spend of four cents
4
+ So pony up you cheap bastard, and store your backups on S3
5
+
6
+ Usage:
7
+ # In config/environment.rb
8
+ config.gem "xaviershay-db2s3", :lib => "db2s3", :source => "http://gems.github.com"
9
+
10
+ # In Rakefile
11
+ require 'db2s3/tasks'
12
+
13
+ # In config/initializers/db2s3.rb
14
+ DB2S3::Config.instance_eval do
15
+ S3 = {
16
+ :access_key_id => 'yourkey',
17
+ :secret_access_key => 'yoursecretkey',
18
+ :bucket => 'yourapp-db-backup'
19
+ }
20
+ end
21
+ # DB credentials are read from your rails environment
22
+
23
+ rake gems:install
24
+
25
+ # Add to your crontab or whatever
26
+ rake db2s3:backup:full
27
+ rake db2s3:backup:incremental # Unimplemented
28
+
29
+ # Handy tasks
30
+ rake db2s3:metrics # Estimated costs
31
+ rake db2s3:backup:restore # You should be testing this regularly
32
+
33
+ Caveats:
34
+ Currently does not clean up old back ups
35
+
36
+ Kudos:
37
+ http://github.com/pauldowman/blog_code_examples/tree/master/mysql_s3_backup
@@ -0,0 +1,4 @@
1
+ desc "Create test database"
2
+ task :create_test_db do
3
+ `mysqladmin -u root create db2s3_unittest`
4
+ end
@@ -0,0 +1,47 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{db2s3}
5
+ s.version = "0.2.5"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Xavier Shay"]
9
+ s.date = %q{2009-03-08}
10
+ s.description = %q{db2s3 provides rake tasks for backing up and restoring your DB to S3}
11
+ s.email = %q{contact@rhnh.net}
12
+ s.files = %w(
13
+ README
14
+ Rakefile
15
+ db2s3.gemspec
16
+ init.rb
17
+ lib
18
+ lib/db2s3.rb
19
+ lib/db2s3/tasks.rb
20
+ rails
21
+ rails/init.rb
22
+ spec
23
+ spec/db2s3_spec.rb
24
+ spec/mysql_drop_schema.sql
25
+ spec/mysql_schema.sql
26
+ spec/s3_config.example.rb
27
+ spec/s3_config.rb
28
+ spec/spec_helper.rb
29
+ tasks
30
+ tasks/tasks.rake
31
+ )
32
+ s.has_rdoc = false
33
+ s.homepage = %q{http://github.com/ariossw/db2s3}
34
+ #s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
35
+ s.require_paths = ["lib"]
36
+ #s.rubyforge_project = %q{grit}
37
+ s.rubygems_version = %q{1.3.0}
38
+ s.summary = %q{db2s3 provides rake tasks for backing up and restoring your DB to S3}
39
+
40
+ # TODO: WTF does this do
41
+ if s.respond_to? :specification_version then
42
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
43
+ s.specification_version = 2
44
+ end
45
+
46
+ s.add_dependency(%q<aws-s3>, [">= 0.5.1"])
47
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + "/rails/init"
@@ -0,0 +1,117 @@
1
+ require 'aws/s3'
2
+ require 'tempfile'
3
+
4
+ class DB2S3
5
+ class Config
6
+ end
7
+
8
+ def initialize
9
+ end
10
+
11
+ def full_backup
12
+ file_name = "dump-#{db_credentials[:database]}-#{Time.now.utc.strftime("%Y%m%d%H%M")}.sql.gz"
13
+ store.store(file_name, open(dump_db.path))
14
+ store.store(most_recent_dump_file_name, file_name)
15
+ end
16
+
17
+ def restore
18
+ dump_file_name = store.fetch(most_recent_dump_file_name).read
19
+ file = store.fetch(dump_file_name)
20
+ run "gunzip -c #{file.path} | mysql #{mysql_options}"
21
+ end
22
+
23
+ def metrics
24
+ dump_file = dump_db
25
+
26
+ storage_dollars_per_byte_per_month = 0.15 / 1024.0 / 1024.0 / 1024.0
27
+ transfer_dollars_per_byte_per_month = 0.10 / 1024.0 / 1024.0 / 1024.0
28
+ full_dumps_per_month = 30
29
+
30
+ storage_cost = (dump_file.size * storage_dollars_per_byte_per_month * 100).ceil / 100.0
31
+ transfer_cost = (dump_file.size * full_dumps_per_month * transfer_dollars_per_byte_per_month * 100).ceil / 100.0
32
+ requests_cost = 0.02 # TODO: Actually calculate this, with incremental backups could be more
33
+
34
+ {
35
+ :db_size => dump_file.size,
36
+ :storage_cost => storage_cost,
37
+ :transfer_cost => transfer_cost,
38
+ :total_cost => storage_cost + transfer_cost + requests_cost,
39
+ :requests_cost => requests_cost,
40
+ :full_backups_per_month => full_dumps_per_month
41
+ }
42
+ end
43
+
44
+ private
45
+
46
+ def dump_db
47
+ dump_file = Tempfile.new("dump")
48
+
49
+ #cmd = "mysqldump --quick --single-transaction --create-options -u#{db_credentials[:user]} --flush-logs --master-data=2 --delete-master-logs"
50
+ cmd = "mysqldump --quick --single-transaction --create-options #{mysql_options}"
51
+ cmd += " | gzip > #{dump_file.path}"
52
+ run(cmd)
53
+
54
+ dump_file
55
+ end
56
+
57
+ def mysql_options
58
+ cmd = " -u#{db_credentials[:username]} "
59
+ cmd += " -p'#{db_credentials[:password]}'" unless db_credentials[:password].nil?
60
+ cmd += " #{db_credentials[:database]}"
61
+ end
62
+
63
+ def store
64
+ @store ||= S3Store.new
65
+ end
66
+
67
+ def most_recent_dump_file_name
68
+ "most-recent-dump-#{db_credentials[:database]}.txt"
69
+ end
70
+
71
+ def run(command)
72
+ result = system(command)
73
+ raise("error, process exited with status #{$?.exitstatus}") unless result
74
+ end
75
+
76
+ def db_credentials
77
+ ActiveRecord::Base.connection.instance_eval { @config } # Dodgy!
78
+ end
79
+
80
+ class S3Store
81
+ def initialize
82
+ @connected = false
83
+ end
84
+
85
+ def ensure_connected
86
+ return if @connected
87
+ AWS::S3::Base.establish_connection!(DB2S3::Config::S3.slice(:access_key_id, :secret_access_key).merge(:use_ssl => true))
88
+ AWS::S3::Bucket.create(bucket)
89
+ @connected = true
90
+ end
91
+
92
+ def store(file_name, file)
93
+ ensure_connected
94
+ AWS::S3::S3Object.store(file_name, file, bucket)
95
+ end
96
+
97
+ def fetch(file_name)
98
+ ensure_connected
99
+ AWS::S3::S3Object.find(file_name, bucket)
100
+
101
+ file = Tempfile.new("dump")
102
+ open(file.path, 'w') do |f|
103
+ AWS::S3::S3Object.stream(file_name, bucket) do |chunk|
104
+ f.write chunk
105
+ end
106
+ end
107
+ file
108
+ end
109
+
110
+ private
111
+
112
+ def bucket
113
+ DB2S3::Config::S3[:bucket]
114
+ end
115
+ end
116
+
117
+ end
@@ -0,0 +1 @@
1
+ Dir["#{File.dirname(__FILE__)}/../../tasks/*.rake"].each { |ext| load ext }
@@ -0,0 +1 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/db2s3')
@@ -0,0 +1,50 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe 'db2s3' do
4
+ def load_schema
5
+ `cat '#{File.dirname(__FILE__) + '/mysql_schema.sql'}' | mysql -u #{DBConfig[:user]} #{DBConfig[:database]}`
6
+ end
7
+
8
+ def drop_schema
9
+ `cat '#{File.dirname(__FILE__) + '/mysql_drop_schema.sql'}' | mysql -u #{DBConfig[:user]} #{DBConfig[:database]}`
10
+ end
11
+
12
+ class Person < ActiveRecord::Base
13
+ end
14
+
15
+ if DB2S3::Config.const_defined?('S3')
16
+ it 'can save and restore a backup to S3' do
17
+ db2s3 = DB2S3.new
18
+ load_schema
19
+ Person.create!(:name => "Baxter")
20
+ db2s3.full_backup
21
+ drop_schema
22
+ db2s3.restore
23
+ Person.find_by_name("Baxter").should_not be_nil
24
+ end
25
+ end
26
+
27
+ it 'provides estimated metrics' do
28
+ db2s3 = DB2S3.new
29
+ # 1 GB DB
30
+ db2s3.stub!(:dump_db).and_return(stub("dump file", :size => 1024 * 1024 * 1024))
31
+ metrics = db2s3.metrics
32
+ metrics.should == {
33
+ :storage_cost => 0.15, # 15c/GB-Month rounded up to nearest cent, we're only storing one backup
34
+ :transfer_cost => 3.0, # 10c/GB-Month * 30 backups
35
+ :db_size => 1024 * 1024 * 1024, # 1 GB
36
+ :total_cost => 3.17,
37
+ :requests_cost => 0.02,
38
+ :full_backups_per_month => 30 # Default 1 backup/day
39
+ }
40
+ end
41
+
42
+ it 'rounds transfer cost metric up to nearest cent' do
43
+ db2s3 = DB2S3.new
44
+ # 1 KB DB
45
+ db2s3.stub!(:dump_db).and_return(stub("dump file", :size => 1024))
46
+ metrics = db2s3.metrics
47
+ metrics[:storage_cost].should == 0.01
48
+ metrics[:transfer_cost].should == 0.01
49
+ end
50
+ end
@@ -0,0 +1 @@
1
+ DROP TABLE IF EXISTS people;
@@ -0,0 +1,4 @@
1
+ DROP TABLE IF EXISTS people;
2
+ CREATE TABLE people (
3
+ name VARCHAR(255) NULL
4
+ );
@@ -0,0 +1,5 @@
1
+ DB2S3::Config:: S3 = {
2
+ :access_key_id => 'yourkey',
3
+ :secret_access_key => 'yoursecretkey',
4
+ :bucket => 'db2s3_test'
5
+ }
@@ -0,0 +1,5 @@
1
+ DB2S3::Config:: S3 = {
2
+ :access_key_id => 'yourkey',
3
+ :secret_access_key => 'yoursecretkey',
4
+ :bucket => 'db2s3_test'
5
+ }
@@ -0,0 +1,18 @@
1
+ require 'spec'
2
+ require 'activerecord'
3
+ require File.dirname(__FILE__) + '/../lib/db2s3'
4
+ if File.exists?(File.dirname(__FILE__) + '/s3_config.rb')
5
+ require File.dirname(__FILE__) + '/s3_config.rb'
6
+ else
7
+ puts "s3_config.rb does not exist - not running live tests"
8
+ end
9
+
10
+ DBConfig = {
11
+ :adapter => "mysql",
12
+ :encoding => "utf8",
13
+ :database => 'db2s3_unittest',
14
+ :user => "root"
15
+ }
16
+
17
+ ActiveRecord::Base.configurations = { 'production' => DBConfig }
18
+ ActiveRecord::Base.establish_connection(:production)
@@ -0,0 +1,40 @@
1
+ namespace :db2s3 do
2
+ namespace :backup do
3
+ desc "Save a full back to S3"
4
+ task :full => :environment do
5
+ DB2S3.new.full_backup
6
+ end
7
+
8
+ desc "Restore your DB from S3"
9
+ task :restore => :environment do
10
+ DB2S3.new.restore
11
+ end
12
+ end
13
+
14
+ desc "Provide estimated costs for backing up your DB to S3"
15
+ task :metrics => :environment do
16
+ def format_size(size)
17
+ units = %w{B KB MB GB TB}
18
+ e = (Math.log(size)/Math.log(1024)).floor
19
+ s = "%.3f" % (size.to_f / 1024**e)
20
+ s.sub(/\.?0*$/, units[e])
21
+ end
22
+
23
+ def format_cost(cost)
24
+ "%.2f" % [cost]
25
+ end
26
+
27
+ metrics = DB2S3.new.metrics
28
+ puts <<-EOS
29
+ Estimates only, does not take into account metadata overhead
30
+ Code has recently been added that keeps old backups around - this is not taken into account in these estimates
31
+
32
+ DB Size: #{format_size(metrics[:db_size])}
33
+ Full backups/month: #{metrics[:full_backups_per_month]}
34
+ Storage Cost $US: #{format_cost(metrics[:storage_cost])}
35
+ Transfer Cost $US: #{format_cost(metrics[:transfer_cost])}
36
+ Requests Cost $US: #{format_cost(metrics[:requests_cost])}
37
+ Total Cost $US: #{format_cost(metrics[:total_cost])}
38
+ EOS
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ariossw-db2s3
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.5
5
+ platform: ruby
6
+ authors:
7
+ - Xavier Shay
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-08 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: aws-s3
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.5.1
24
+ version:
25
+ description: db2s3 provides rake tasks for backing up and restoring your DB to S3
26
+ email: contact@rhnh.net
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - README
35
+ - Rakefile
36
+ - db2s3.gemspec
37
+ - init.rb
38
+ - lib
39
+ - lib/db2s3.rb
40
+ - lib/db2s3/tasks.rb
41
+ - rails
42
+ - rails/init.rb
43
+ - spec
44
+ - spec/db2s3_spec.rb
45
+ - spec/mysql_drop_schema.sql
46
+ - spec/mysql_schema.sql
47
+ - spec/s3_config.example.rb
48
+ - spec/s3_config.rb
49
+ - spec/spec_helper.rb
50
+ - tasks
51
+ - tasks/tasks.rake
52
+ has_rdoc: false
53
+ homepage: http://github.com/ariossw/db2s3
54
+ licenses:
55
+ post_install_message:
56
+ rdoc_options: []
57
+
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: "0"
71
+ version:
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.3.5
76
+ signing_key:
77
+ specification_version: 2
78
+ summary: db2s3 provides rake tasks for backing up and restoring your DB to S3
79
+ test_files: []
80
+