ray-db2fog 0.7.1

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,36 @@
1
+ v0.6.0 (24th October 2011)
2
+ - Support for postgresql 8
3
+ v0.5.4 (17th October 2011)
4
+ - only clean files that were created by db2fog
5
+ v0.5.3 (12th October 2011)
6
+ - improve robustness of clean task
7
+ v0.5.2 (15th September 2011)
8
+ - fix restoring of postgresql databases (thanks Dan Neighman)
9
+ v0.5.1 (29th August 2011)
10
+ - relax fog dependency to allow higher versions
11
+ v0.5.0 (6th August 2011)
12
+ - initial postgresql support
13
+ - removed the statistics rake task
14
+ - fix an encoding issue under 1.9
15
+ v0.4.2 (24th July 2011)
16
+ - fixed recording of most recent backup
17
+ - remove runtime dependency on mysql2
18
+ v0.4.1 (23rd July 2011)
19
+ - fixed cleaning of old backups
20
+ v0.4.0 (22nd July 2011)
21
+ - forked db2s3 into db2fog
22
+ 0.3.1 (7 Dec 2009)
23
+ - Fixed hardcoded DB name in statistics task
24
+ 0.3.0 (6 Dec 2009)
25
+ - Added db2s3:backup:clean task to delete old backups
26
+ - Added dependency on activesupport for the clean task
27
+ 0.2.6 (6 Dec 2009)
28
+ - Remove metrics task, since it was far too inaccurate
29
+ - Add statistics task to show you the size of your tables
30
+ - Only add username to mysql command line if provided in database.yml
31
+ 0.2.5
32
+ - Use host provided in database.yml
33
+ 0.2.4 (2 Sep 2009)
34
+ - Fix credentials bug
35
+ 0.2.3
36
+ - Keep old backups around
@@ -0,0 +1,113 @@
1
+ = DB2Fog
2
+
3
+ A rails plugin to backup Mysql or PostgreSQL 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 is used 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
+ == Passing options to the database driver
57
+
58
+ Database adaptors may support further configuration via the :database_options hash.
59
+
60
+ DB2Fog.config = {
61
+ :directory => 'bucket-name',
62
+ :local_root => Rails.root.to_s + '/db/backups',
63
+ :provider => 'Local',
64
+ :database_options => {
65
+ :pg_version => 8
66
+ }
67
+ }
68
+
69
+ Supported database options:
70
+
71
+ pg_version 8 or 9
72
+ The major version of PostgreSQL installed on the server. Defaults to 9.
73
+
74
+ == Usage
75
+
76
+ # Add to your crontab or whatever
77
+ rake db2fog:full
78
+
79
+ # Handy tasks
80
+ rake db2fog:restore # You should be testing this regularly
81
+ rake db2fog:clean # Clean up old backups - cron this
82
+
83
+ === Alternative
84
+
85
+ If you want to trigger backups from ruby (say, from a delayed job) you can
86
+ do this:
87
+
88
+ DB2Fog.new.backup
89
+ DB2Fog.new.clean
90
+
91
+ == Compatibility
92
+
93
+ This is pure so ruby should run on most ruby VMs. I develop on MRI 1.9.2.
94
+
95
+ This will only work with rails 3. Supporting earlier versions is more
96
+ complication than I feel like handling at the moment.
97
+
98
+ == Development
99
+
100
+ Specs are a little weak and mysql based. This code is bit hackish but is being
101
+ used by quite a few people.
102
+
103
+ == Kudos
104
+
105
+ This is a fork of Xavier Shay's db2s3 gem. It worked perfectly, but only
106
+ supported Amazon S3 within US-east. By switching the dependency to using fog
107
+ this now supports all S3 regions and multiple storage providers
108
+
109
+ Xavier's original gem is available at https://github.com/xaviershay/db2s3
110
+
111
+ Xavier quotes the following example as inspiration:
112
+
113
+ http://github.com/pauldowman/blog_code_examples/tree/master/mysql_s3_backup
@@ -0,0 +1,227 @@
1
+ # coding: utf-8
2
+
3
+ require 'active_support'
4
+ require 'active_support/core_ext/class/attribute_accessors'
5
+ require 'active_support/core_ext/hash/except'
6
+ require 'fog'
7
+ require 'tempfile'
8
+ require 'db2fog/railtie'
9
+
10
+ class DB2Fog
11
+ cattr_accessor :config
12
+
13
+ def backup
14
+ file_name = "dump-#{db_credentials[:database]}-#{Time.now.utc.strftime("%Y%m%d%H%M")}.sql.gz"
15
+ store.store(file_name, open(database.dump))
16
+ store.store(most_recent_dump_file_name, file_name)
17
+ end
18
+
19
+ def restore
20
+ dump_file_name = store.fetch(most_recent_dump_file_name).read
21
+ file = store.fetch(dump_file_name)
22
+ database.restore(file.path)
23
+ end
24
+
25
+ def clean
26
+ to_keep = []
27
+ # only consider files that belong to db2fog. Other files are ignored
28
+ filelist = store.list.select {|file|
29
+ file.include?(db_credentials[:database]) && file.match(/\d{12}.sql.gz\Z/)
30
+ }
31
+ files = filelist.map { |file|
32
+ {
33
+ :path => file,
34
+ :date => Time.parse(file.split('-').last.split('.').first)
35
+ }
36
+ }
37
+ # Keep all backups from the past day
38
+ files.select {|x| x[:date] >= 1.day.ago }.each do |backup_for_day|
39
+ to_keep << backup_for_day
40
+ end
41
+
42
+ # Keep one backup per day from the last week
43
+ files.select {|x| x[:date] >= 1.week.ago }.group_by {|x| x[:date].strftime("%Y%m%d") }.values.each do |backups_for_last_week|
44
+ to_keep << backups_for_last_week.sort_by{|x| x[:date].strftime("%Y%m%d") }.first
45
+ end
46
+
47
+ # Keep one backup per week since forever
48
+ files.group_by {|x| x[:date].strftime("%Y%W") }.values.each do |backups_for_week|
49
+ to_keep << backups_for_week.sort_by{|x| x[:date].strftime("%Y%m%d") }.first
50
+ end
51
+
52
+ to_destroy = filelist - to_keep.uniq.collect {|x| x[:path] }
53
+ to_destroy.each do |file|
54
+ store.delete(file.split('/').last)
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def store
61
+ @store ||= FogStore.new
62
+ end
63
+
64
+ def most_recent_dump_file_name
65
+ "most-recent-dump-#{db_credentials[:database]}.txt"
66
+ end
67
+
68
+ def db_credentials
69
+ ActiveRecord::Base.connection.instance_eval { @config } # Dodgy!
70
+ end
71
+
72
+ def database
73
+ @database ||= case db_credentials[:adapter]
74
+ when /mysql/ then MysqlAdaptor.new(db_credentials)
75
+ when /postgres/ then PsqlAdaptor.new(db_credentials)
76
+ else
77
+ raise "database adaptor '#{db_credentials[:adapter]}' not supported"
78
+ end
79
+ end
80
+
81
+ class MysqlAdaptor
82
+
83
+ def initialize(credentials)
84
+ @credentials = credentials
85
+ end
86
+
87
+ def dump
88
+ dump_file = Tempfile.new("dump")
89
+
90
+ cmd = "mysqldump --quick --single-transaction --create-options #{mysql_options}"
91
+ cmd += " | gzip -9 > #{dump_file.path}"
92
+ run(cmd)
93
+
94
+ dump_file.path
95
+ end
96
+
97
+ def restore(path)
98
+ run "gunzip -c #{path} | mysql #{mysql_options}"
99
+ end
100
+
101
+ private
102
+
103
+ def mysql_options
104
+ cmd = ''
105
+ cmd += " -u #{@credentials[:username]} " unless @credentials[:username].nil?
106
+ cmd += " -p'#{@credentials[:password]}'" unless @credentials[:password].nil?
107
+ cmd += " -h '#{@credentials[:host]}'" unless @credentials[:host].nil?
108
+ cmd += " --default-character-set=#{@credentials[:encoding]}" unless @credentials[:encoding].nil?
109
+ cmd += " #{@credentials[:database]}"
110
+ end
111
+
112
+ def run(command)
113
+ result = system(command)
114
+ raise("error, process exited with status #{$?.exitstatus}") unless result
115
+ end
116
+
117
+ end
118
+
119
+ class PsqlAdaptor
120
+
121
+ def initialize(credentials)
122
+ @credentials = credentials
123
+ end
124
+
125
+ def dump
126
+ dump_file = Tempfile.new("dump")
127
+
128
+ cmd = "pg_dump --clean --format=p #{pg_dump_options}"
129
+ cmd += " | gzip -9 > #{dump_file.path}"
130
+ run(cmd)
131
+
132
+ dump_file.path
133
+ end
134
+
135
+ def restore(path)
136
+ run "gunzip -c #{path} | psql #{psql_options}"
137
+ end
138
+
139
+ private
140
+
141
+ def pg_dump_options
142
+ cmd = ''
143
+ cmd += " -U #{@credentials[:username]} " unless @credentials[:username].nil?
144
+ cmd += " -h '#{@credentials[:host]}'" unless @credentials[:host].nil?
145
+ cmd += " -w" if pg_version >= 9
146
+ cmd += " #{@credentials[:database]}"
147
+ end
148
+
149
+ def psql_options
150
+ cmd = ''
151
+ cmd += " -U #{@credentials[:username]} " unless @credentials[:username].nil?
152
+ cmd += " -h '#{@credentials[:host]}'" unless @credentials[:host].nil?
153
+ cmd += " -w" if pg_version >= 9
154
+ cmd += " -d #{@credentials[:database]}"
155
+ end
156
+
157
+ def pg_version
158
+ opts = database_options || {}
159
+ opts[:pg_version] || 9
160
+ end
161
+
162
+ def run(command)
163
+ result = system(command)
164
+ raise("error, process exited with status #{$?.exitstatus}") unless result
165
+ end
166
+
167
+ def database_options
168
+ if DB2Fog.config.respond_to?(:[])
169
+ DB2Fog.config[:database_options]
170
+ else
171
+ raise "DB2Fog not configured"
172
+ end
173
+ end
174
+
175
+ end
176
+
177
+ class FogStore
178
+
179
+ def store(remote_filename, io)
180
+ directory.files.create(:key => remote_filename, :body => io, :public => false)
181
+ end
182
+
183
+ def fetch(remote_filename)
184
+ remote_file = directory.files.get(remote_filename)
185
+
186
+ file = Tempfile.new("dump")
187
+ open(file.path, 'wb') { |f| f.write(remote_file.body) }
188
+ file
189
+ end
190
+
191
+ def list
192
+ directory.files.map { |f| f.key }
193
+ end
194
+
195
+ def delete(remote_filename)
196
+ remote_file = directory.files.head(remote_filename)
197
+ remote_file.destroy if remote_file
198
+ end
199
+
200
+ private
201
+
202
+ def fog_options
203
+ if DB2Fog.config.respond_to?(:[])
204
+ DB2Fog.config.except(:directory, :database_options)
205
+ else
206
+ raise "DB2Fog not configured"
207
+ end
208
+ end
209
+
210
+ def directory_name
211
+ if DB2Fog.config.respond_to?(:[])
212
+ DB2Fog.config[:directory]
213
+ else
214
+ raise "DB2Fog not configured"
215
+ end
216
+ end
217
+
218
+ def directory
219
+ @directory ||= storage.directories.get(directory_name)
220
+ end
221
+
222
+ def storage
223
+ @storage = Fog::Storage.new(fog_options)
224
+ end
225
+ end
226
+
227
+ 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,35 @@
1
+ # coding: utf-8
2
+
3
+ namespace :db2fog do
4
+ desc "Save a full back to S3"
5
+ task :backup => :environment do
6
+ DB2Fog.new.backup
7
+ end
8
+
9
+ desc "Restore your DB from S3"
10
+ task :restore => :environment do
11
+ DB2Fog.new.restore
12
+ end
13
+
14
+ desc "Keep all backups for the last day, one per day for the last week, and one per week before that. Delete the rest."
15
+ task :clean => :environment do
16
+ DB2Fog.new.clean
17
+ end
18
+
19
+ namespace :backup do
20
+ task :full => :environment do
21
+ $stderr.puts "the db2fog:backup:full rake task is deprecated, use db2fog:backup instead"
22
+ DB2Fog.new.backup
23
+ end
24
+
25
+ task :restore => :environment do
26
+ $stderr.puts "the db2fog:backup:restore rake task is deprecated, use db2fog:restore instead"
27
+ DB2Fog.new.restore
28
+ end
29
+
30
+ task :clean => :environment do
31
+ $stderr.puts "the db2fog:backup:clean rake task is deprecated, use db2fog:clean instead"
32
+ DB2Fog.new.clean
33
+ end
34
+ end
35
+ end
metadata ADDED
@@ -0,0 +1,179 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ray-db2fog
3
+ version: !ruby/object:Gem::Version
4
+ hash: 1
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 7
9
+ - 1
10
+ version: 0.7.1
11
+ platform: ruby
12
+ authors:
13
+ - James Healy, Sasan Padidar
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-01-04 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rails
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 7
29
+ segments:
30
+ - 3
31
+ - 0
32
+ version: "3.0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: activerecord
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ hash: 7
44
+ segments:
45
+ - 3
46
+ - 0
47
+ version: "3.0"
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: fog
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ~>
57
+ - !ruby/object:Gem::Version
58
+ hash: 15
59
+ segments:
60
+ - 1
61
+ - 0
62
+ version: "1.0"
63
+ type: :runtime
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: rake
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ type: :development
78
+ version_requirements: *id004
79
+ - !ruby/object:Gem::Dependency
80
+ name: mysql2
81
+ prerelease: false
82
+ requirement: &id005 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ hash: 3
88
+ segments:
89
+ - 0
90
+ version: "0"
91
+ type: :development
92
+ version_requirements: *id005
93
+ - !ruby/object:Gem::Dependency
94
+ name: rspec
95
+ prerelease: false
96
+ requirement: &id006 !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ hash: 15
102
+ segments:
103
+ - 2
104
+ - 6
105
+ version: "2.6"
106
+ type: :development
107
+ version_requirements: *id006
108
+ - !ruby/object:Gem::Dependency
109
+ name: timecop
110
+ prerelease: false
111
+ requirement: &id007 !ruby/object:Gem::Requirement
112
+ none: false
113
+ requirements:
114
+ - - ~>
115
+ - !ruby/object:Gem::Version
116
+ hash: 25
117
+ segments:
118
+ - 0
119
+ - 3
120
+ - 5
121
+ version: 0.3.5
122
+ type: :development
123
+ version_requirements: *id007
124
+ description: ray-db2fog was forked from https://github.com/yob/db2fog.git.
125
+ email:
126
+ - spadidar@raybeam.com
127
+ executables: []
128
+
129
+ extensions: []
130
+
131
+ extra_rdoc_files: []
132
+
133
+ files:
134
+ - lib/db2fog.rb
135
+ - lib/db2fog/tasks.rb
136
+ - lib/db2fog/railtie.rb
137
+ - README.rdoc
138
+ - HISTORY
139
+ homepage: http://github.com/spadidar/db2fog
140
+ licenses: []
141
+
142
+ post_install_message:
143
+ rdoc_options:
144
+ - --title
145
+ - DB2Fog
146
+ - --line-numbers
147
+ require_paths:
148
+ - lib
149
+ required_ruby_version: !ruby/object:Gem::Requirement
150
+ none: false
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ hash: 57
155
+ segments:
156
+ - 1
157
+ - 8
158
+ - 7
159
+ version: 1.8.7
160
+ required_rubygems_version: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ hash: 31
166
+ segments:
167
+ - 1
168
+ - 3
169
+ - 2
170
+ version: 1.3.2
171
+ requirements: []
172
+
173
+ rubyforge_project:
174
+ rubygems_version: 1.8.10
175
+ signing_key:
176
+ specification_version: 3
177
+ summary: db2fog provides rake tasks for backing up and restoring your DB to cloud storage providers
178
+ test_files: []
179
+