db2fog 0.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.
data/HISTORY ADDED
@@ -0,0 +1,17 @@
1
+ v0.4.0 (22nd July 2011)
2
+ - forked db2s3 into db2fog
3
+ 0.3.1 (7 Dec 2009)
4
+ - Fixed hardcoded DB name in statistics task
5
+ 0.3.0 (6 Dec 2009)
6
+ - Added db2s3:backup:clean task to delete old backups
7
+ - Added dependency on activesupport for the clean task
8
+ 0.2.6 (6 Dec 2009)
9
+ - Remove metrics task, since it was far too inaccurate
10
+ - Add statistics task to show you the size of your tables
11
+ - Only add username to mysql command line if provided in database.yml
12
+ 0.2.5
13
+ - Use host provided in database.yml
14
+ 0.2.4 (2 Sep 2009)
15
+ - Fix credentials bug
16
+ 0.2.3
17
+ - Keep old backups around
data/README.rdoc ADDED
@@ -0,0 +1,87 @@
1
+ = DB2Fog
2
+
3
+ A rails plugin to backup Mysql to a cloud storage provider. You're looking at
4
+ a monthly spend of four cents. So pony up you cheap bastard, and store your
5
+ backups offsite.
6
+
7
+ A grandfather style system to decide what backups to keep copies of:
8
+
9
+ * all backups from the past 24 hours
10
+ * one backup per day for the past week
11
+ * one backup per week forever
12
+
13
+ Depending on your tolerance for data loss you should be running a backup at
14
+ least once a day, probably more.
15
+
16
+ == Installation
17
+
18
+ Add the following to your project Gemfile
19
+
20
+ gem "db2fog"
21
+
22
+ == Configuration
23
+
24
+ Add the following to config/initializers/db2fog.rb
25
+
26
+ In general, you can use any configuration options supported by
27
+ Fog::Storage.new, plus the :directory option. If fog adds support for extra
28
+ providers they should work with just a config change to Db2Fog.
29
+
30
+ === Amazon S3
31
+
32
+ DB2Fog.config = {
33
+ :aws_access_key_id => 'yourkey',
34
+ :aws_secret_access_key => 'yoursecretkey',
35
+ :directory => 'bucket-name',
36
+ :provider => 'AWS'
37
+ }
38
+
39
+ === Rackspace Cloudfiles
40
+
41
+ DB2Fog.config = {
42
+ :rackspace_username => 'username',
43
+ :rackspace_api_key => 'api key',
44
+ :directory => 'bucket-name',
45
+ :provider => 'Rackspace'
46
+ }
47
+
48
+ === Local Storage
49
+
50
+ DB2Fog.config = {
51
+ :directory => 'bucket-name',
52
+ :local_root => Rails.root.to_s + '/db/backups',
53
+ :provider => 'Local'
54
+ }
55
+
56
+ == Usage
57
+
58
+ # Add to your crontab or whatever
59
+ rake db2fog:backup:full
60
+
61
+ # Handy tasks
62
+ rake db2fog:statistics # Shows you the size of your DB
63
+ rake db2fog:backup:restore # You should be testing this regularly
64
+ rake db2fog:backup:clean # Clean up old backups - cron this
65
+
66
+ == Compatibility
67
+
68
+ This is pure so ruby should run on most ruby VMs. I develop on MRI 1.9.2.
69
+
70
+ This will only work work with rails 3. Supporting earlier versions is more
71
+ complication than I can handle at the moment.
72
+
73
+ == Development
74
+
75
+ Specs are really weak. This code is bit hackish but is being used by quite a few people.
76
+
77
+ == Kudos
78
+
79
+ This is a fork of Xavier Shay's db2s3 gem. It worked perfectly, but only
80
+ supported Amazon S3. By switching the dependency to using fog this now
81
+ supports multiple storage providers.
82
+
83
+ Xavier's original gem is available at https://github.com/xaviershay/db2s3
84
+
85
+ Xavier quotes the following example as inspiration:
86
+
87
+ http://github.com/pauldowman/blog_code_examples/tree/master/mysql_s3_backup
data/lib/db2fog.rb ADDED
@@ -0,0 +1,162 @@
1
+ require 'active_support'
2
+ require 'active_support/core_ext/class/attribute_accessors'
3
+ require 'active_support/core_ext/hash/except'
4
+ require 'fog'
5
+ require 'tempfile'
6
+ require 'db2fog/railtie'
7
+
8
+ class DB2Fog
9
+ cattr_accessor :config
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
+ # TODO: This method really needs specs
24
+ def clean
25
+ to_keep = []
26
+ filelist = store.list
27
+ files = filelist.reject {|file| file.ends_with?(most_recent_dump_file_name) }.collect do |file|
28
+ {
29
+ :path => file,
30
+ :date => Time.parse(file.split('-').last.split('.').first)
31
+ }
32
+ end
33
+ # Keep all backups from the past day
34
+ files.select {|x| x[:date] >= 1.day.ago }.each do |backup_for_day|
35
+ to_keep << backup_for_day
36
+ end
37
+
38
+ # Keep one backup per day from the last week
39
+ files.select {|x| x[:date] >= 1.week.ago }.group_by {|x| x[:date].strftime("%Y%m%d") }.values.each do |backups_for_last_week|
40
+ to_keep << backups_for_last_week.sort_by{|x| x[:date].strftime("%Y%m%d") }.first
41
+ end
42
+
43
+ # Keep one backup per week since forever
44
+ files.group_by {|x| x[:date].strftime("%Y%W") }.values.each do |backups_for_week|
45
+ to_keep << backups_for_week.sort_by{|x| x[:date].strftime("%Y%m%d") }.first
46
+ end
47
+
48
+ to_destroy = filelist - to_keep.uniq.collect {|x| x[:path] }
49
+ to_destroy.delete_if {|x| x.ends_with?(most_recent_dump_file_name) }
50
+ to_destroy.each do |file|
51
+ store.delete(file.split('/').last)
52
+ end
53
+ end
54
+
55
+ def statistics
56
+ # From http://mysqlpreacher.com/wordpress/tag/table-size/
57
+ results = ActiveRecord::Base.connection.execute(<<-EOS)
58
+ SELECT
59
+ engine,
60
+ ROUND(data_length/1024/1024,2) total_size_mb,
61
+ ROUND(index_length/1024/1024,2) total_index_size_mb,
62
+ table_rows,
63
+ table_name article_attachment
64
+ FROM information_schema.tables
65
+ WHERE table_schema = '#{db_credentials[:database]}'
66
+ ORDER BY total_size_mb + total_index_size_mb desc;
67
+ EOS
68
+ rows = []
69
+ results.each {|x| rows << x.to_a }
70
+ rows
71
+ end
72
+
73
+ private
74
+
75
+ def dump_db
76
+ dump_file = Tempfile.new("dump")
77
+
78
+ cmd = "mysqldump --quick --single-transaction --create-options #{mysql_options}"
79
+ cmd += " | gzip > #{dump_file.path}"
80
+ run(cmd)
81
+
82
+ dump_file
83
+ end
84
+
85
+ def mysql_options
86
+ cmd = ''
87
+ cmd += " -u #{db_credentials[:username]} " unless db_credentials[:username].nil?
88
+ cmd += " -p'#{db_credentials[:password]}'" unless db_credentials[:password].nil?
89
+ cmd += " -h '#{db_credentials[:host]}'" unless db_credentials[:host].nil?
90
+ cmd += " #{db_credentials[:database]}"
91
+ end
92
+
93
+ def store
94
+ @store ||= FogStore.new
95
+ end
96
+
97
+ def most_recent_dump_file_name
98
+ "most-recent-dump-#{db_credentials[:database]}.txt"
99
+ end
100
+
101
+ def run(command)
102
+ result = system(command)
103
+ raise("error, process exited with status #{$?.exitstatus}") unless result
104
+ end
105
+
106
+ def db_credentials
107
+ ActiveRecord::Base.connection.instance_eval { @config } # Dodgy!
108
+ end
109
+
110
+ class FogStore
111
+
112
+ def store(remote_filename, io)
113
+ unless directory.files.head(remote_filename)
114
+ directory.files.create(:key => remote_filename, :body => io, :public => false)
115
+ end
116
+ end
117
+
118
+ def fetch(remote_filename)
119
+ remote_file = directory.files.get(remote_filename)
120
+
121
+ file = Tempfile.new("dump")
122
+ open(file.path, 'w') { |f| f.write(remote_file.body) }
123
+ file
124
+ end
125
+
126
+ def list
127
+ directory.files.map { |f| f.key }
128
+ end
129
+
130
+ def delete(remote_filename)
131
+ remote_file = remote_file.head(remote_filename)
132
+ remote_file.destroy if remote_file
133
+ end
134
+
135
+ private
136
+
137
+ def fog_options
138
+ if DB2Fog.config.respond_to?(:[])
139
+ DB2Fog.config.except(:directory)
140
+ else
141
+ raise "DB2Fog not configured"
142
+ end
143
+ end
144
+
145
+ def directory_name
146
+ if DB2Fog.config.respond_to?(:[])
147
+ DB2Fog.config[:directory]
148
+ else
149
+ raise "DB2Fog not configured"
150
+ end
151
+ end
152
+
153
+ def directory
154
+ @directory ||= storage.directories.get(directory_name)
155
+ end
156
+
157
+ def storage
158
+ @storage = Fog::Storage.new(fog_options)
159
+ end
160
+ end
161
+
162
+ end
@@ -0,0 +1,11 @@
1
+ require 'rails'
2
+ require 'db2fog'
3
+
4
+ class DB2Fog
5
+ class Railtie < Rails::Railtie
6
+
7
+ rake_tasks do
8
+ load File.expand_path('tasks.rb', File.dirname(__FILE__))
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+
3
+ namespace :db2fog do
4
+ namespace :backup do
5
+ desc "Save a full back to S3"
6
+ task :full => :environment do
7
+ DB2Fog.new.full_backup
8
+ end
9
+
10
+ desc "Restore your DB from S3"
11
+ task :restore => :environment do
12
+ DB2Fog.new.restore
13
+ end
14
+
15
+ desc "Keep all backups for the last day, one per day for the last week, and one per week before that. Delete the rest."
16
+ task :clean => :environment do
17
+ DB2Fog.new.clean
18
+ end
19
+ end
20
+
21
+ desc "Show table sizes for your database"
22
+ task :statistics => :environment do
23
+ rows = DB2Fog.new.statistics
24
+ rows.sort_by {|x| -x[3].to_i }
25
+ header = [["Type", "Data MB", "Index", "Rows", "Name"], []]
26
+ puts (header + rows).collect {|x| x.join("\t") }
27
+ end
28
+ end
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: db2fog
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 4
8
+ version: "0.4"
9
+ platform: ruby
10
+ authors:
11
+ - James Healy
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+
16
+ date: 2011-07-22 00:00:00 +10:00
17
+ default_executable:
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: rails
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 3
29
+ - 0
30
+ version: "3.0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: activerecord
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 3
43
+ - 0
44
+ version: "3.0"
45
+ type: :runtime
46
+ version_requirements: *id002
47
+ - !ruby/object:Gem::Dependency
48
+ name: mysql2
49
+ prerelease: false
50
+ requirement: &id003 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 0
57
+ - 2
58
+ - 0
59
+ version: 0.2.0
60
+ type: :runtime
61
+ version_requirements: *id003
62
+ - !ruby/object:Gem::Dependency
63
+ name: fog
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ~>
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 0
72
+ - 9
73
+ - 0
74
+ version: 0.9.0
75
+ type: :runtime
76
+ version_requirements: *id004
77
+ - !ruby/object:Gem::Dependency
78
+ name: rake
79
+ prerelease: false
80
+ requirement: &id005 !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ segments:
86
+ - 0
87
+ version: "0"
88
+ type: :development
89
+ version_requirements: *id005
90
+ - !ruby/object:Gem::Dependency
91
+ name: rspec
92
+ prerelease: false
93
+ requirement: &id006 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ~>
97
+ - !ruby/object:Gem::Version
98
+ segments:
99
+ - 2
100
+ - 6
101
+ version: "2.6"
102
+ type: :development
103
+ version_requirements: *id006
104
+ description: db2fig provides rake tasks for backing up and restoring your DB to cloud storage providers
105
+ email:
106
+ - james@yob.id.au
107
+ executables: []
108
+
109
+ extensions: []
110
+
111
+ extra_rdoc_files: []
112
+
113
+ files:
114
+ - lib/db2fog.rb
115
+ - lib/db2fog/railtie.rb
116
+ - lib/db2fog/tasks.rb
117
+ - README.rdoc
118
+ - HISTORY
119
+ has_rdoc: true
120
+ homepage: http://github.com/yob/db2fog
121
+ licenses: []
122
+
123
+ post_install_message:
124
+ rdoc_options:
125
+ - --title
126
+ - DB2Fog
127
+ - --line-numbers
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ segments:
136
+ - 1
137
+ - 8
138
+ - 7
139
+ version: 1.8.7
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ segments:
146
+ - 1
147
+ - 3
148
+ - 2
149
+ version: 1.3.2
150
+ requirements: []
151
+
152
+ rubyforge_project:
153
+ rubygems_version: 1.3.7
154
+ signing_key:
155
+ specification_version: 3
156
+ summary: db2fog provides rake tasks for backing up and restoring your DB to cloud storage providers
157
+ test_files: []
158
+