rayback 0.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,228 @@
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 *args
88
+ options = args.extract_options!
89
+
90
+ dump_file = Tempfile.new("dump")
91
+ flags = "--quick --single-transaction --create-options"
92
+ cmd = "mysqldump #{flags} #{mysql_options} #{options[:database]} " + options[:tables].join(" ")
93
+ cmd += " | gzip -9 > #{dump_file.path}"
94
+ run(cmd)
95
+
96
+ dump_file.path
97
+ end
98
+
99
+ def restore(path)
100
+ run "gunzip -c #{path} | mysql #{mysql_options}"
101
+ end
102
+
103
+ private
104
+
105
+ def mysql_options
106
+ cmd = ''
107
+ cmd += " -u #{@credentials[:username]} " unless @credentials[:username].nil?
108
+ cmd += " -p'#{@credentials[:password]}'" unless @credentials[:password].nil?
109
+ cmd += " -h '#{@credentials[:host]}'" unless @credentials[:host].nil?
110
+ cmd += " --default-character-set=#{@credentials[:encoding]}" unless @credentials[:encoding].nil?
111
+ end
112
+
113
+ def run(command)
114
+ result = system(command)
115
+ raise("error, process exited with status #{$?.exitstatus}") unless result
116
+ end
117
+
118
+ end
119
+
120
+ class PsqlAdaptor
121
+
122
+ def initialize(credentials)
123
+ @credentials = credentials
124
+ end
125
+
126
+ def dump
127
+ dump_file = Tempfile.new("dump")
128
+
129
+ cmd = "pg_dump --clean --format=p #{pg_dump_options}"
130
+ cmd += " | gzip -9 > #{dump_file.path}"
131
+ run(cmd)
132
+
133
+ dump_file.path
134
+ end
135
+
136
+ def restore(path)
137
+ run "gunzip -c #{path} | psql #{psql_options}"
138
+ end
139
+
140
+ private
141
+
142
+ def pg_dump_options
143
+ cmd = ''
144
+ cmd += " -U #{@credentials[:username]} " unless @credentials[:username].nil?
145
+ cmd += " -h '#{@credentials[:host]}'" unless @credentials[:host].nil?
146
+ cmd += " -w" if pg_version >= 9
147
+ cmd += " #{@credentials[:database]}"
148
+ end
149
+
150
+ def psql_options
151
+ cmd = ''
152
+ cmd += " -U #{@credentials[:username]} " unless @credentials[:username].nil?
153
+ cmd += " -h '#{@credentials[:host]}'" unless @credentials[:host].nil?
154
+ cmd += " -w" if pg_version >= 9
155
+ cmd += " -d #{@credentials[:database]}"
156
+ end
157
+
158
+ def pg_version
159
+ opts = database_options || {}
160
+ opts[:pg_version] || 9
161
+ end
162
+
163
+ def run(command)
164
+ result = system(command)
165
+ raise("error, process exited with status #{$?.exitstatus}") unless result
166
+ end
167
+
168
+ def database_options
169
+ if DB2Fog.config.respond_to?(:[])
170
+ DB2Fog.config[:database_options]
171
+ else
172
+ raise "DB2Fog not configured"
173
+ end
174
+ end
175
+
176
+ end
177
+
178
+ class FogStore
179
+
180
+ def store(remote_filename, io)
181
+ directory.files.create(:key => remote_filename, :body => io, :public => false)
182
+ end
183
+
184
+ def fetch(remote_filename)
185
+ remote_file = directory.files.get(remote_filename)
186
+
187
+ file = Tempfile.new("dump")
188
+ open(file.path, 'wb') { |f| f.write(remote_file.body) }
189
+ file
190
+ end
191
+
192
+ def list
193
+ directory.files.map { |f| f.key }
194
+ end
195
+
196
+ def delete(remote_filename)
197
+ remote_file = directory.files.head(remote_filename)
198
+ remote_file.destroy if remote_file
199
+ end
200
+
201
+ private
202
+
203
+ def fog_options
204
+ if DB2Fog.config.respond_to?(:[])
205
+ DB2Fog.config.except(:directory, :database_options)
206
+ else
207
+ raise "DB2Fog not configured"
208
+ end
209
+ end
210
+
211
+ def directory_name
212
+ if DB2Fog.config.respond_to?(:[])
213
+ DB2Fog.config[:directory]
214
+ else
215
+ raise "DB2Fog not configured"
216
+ end
217
+ end
218
+
219
+ def directory
220
+ @directory ||= storage.directories.get(directory_name)
221
+ end
222
+
223
+ def storage
224
+ @storage = Fog::Storage.new(fog_options)
225
+ end
226
+ end
227
+
228
+ 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,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rayback
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ version: "0.1"
10
+ platform: ruby
11
+ authors:
12
+ - Sasan Padidar
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2012-01-05 00:00:00 Z
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
+ hash: 7
28
+ segments:
29
+ - 3
30
+ - 0
31
+ version: "3.0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: activerecord
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ~>
41
+ - !ruby/object:Gem::Version
42
+ hash: 7
43
+ segments:
44
+ - 3
45
+ - 0
46
+ version: "3.0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: fog
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ hash: 15
58
+ segments:
59
+ - 1
60
+ - 0
61
+ version: "1.0"
62
+ type: :runtime
63
+ version_requirements: *id003
64
+ - !ruby/object:Gem::Dependency
65
+ name: rake
66
+ prerelease: false
67
+ requirement: &id004 !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ hash: 3
73
+ segments:
74
+ - 0
75
+ version: "0"
76
+ type: :development
77
+ version_requirements: *id004
78
+ - !ruby/object:Gem::Dependency
79
+ name: mysql2
80
+ prerelease: false
81
+ requirement: &id005 !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ hash: 3
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ type: :development
91
+ version_requirements: *id005
92
+ - !ruby/object:Gem::Dependency
93
+ name: rspec
94
+ prerelease: false
95
+ requirement: &id006 !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ~>
99
+ - !ruby/object:Gem::Version
100
+ hash: 15
101
+ segments:
102
+ - 2
103
+ - 6
104
+ version: "2.6"
105
+ type: :development
106
+ version_requirements: *id006
107
+ - !ruby/object:Gem::Dependency
108
+ name: timecop
109
+ prerelease: false
110
+ requirement: &id007 !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ~>
114
+ - !ruby/object:Gem::Version
115
+ hash: 25
116
+ segments:
117
+ - 0
118
+ - 3
119
+ - 5
120
+ version: 0.3.5
121
+ type: :development
122
+ version_requirements: *id007
123
+ description: rayback was forked from https://github.com/yob/db2fog.git.
124
+ email:
125
+ - spadidar@raybeam.com
126
+ executables: []
127
+
128
+ extensions: []
129
+
130
+ extra_rdoc_files: []
131
+
132
+ files:
133
+ - lib/db2fog.rb
134
+ - lib/db2fog/tasks.rb
135
+ - lib/db2fog/railtie.rb
136
+ - README.rdoc
137
+ - HISTORY
138
+ homepage: http://github.com/spadidar/db2fog
139
+ licenses: []
140
+
141
+ post_install_message:
142
+ rdoc_options:
143
+ - --title
144
+ - DB2Fog
145
+ - --line-numbers
146
+ require_paths:
147
+ - lib
148
+ required_ruby_version: !ruby/object:Gem::Requirement
149
+ none: false
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ hash: 57
154
+ segments:
155
+ - 1
156
+ - 8
157
+ - 7
158
+ version: 1.8.7
159
+ required_rubygems_version: !ruby/object:Gem::Requirement
160
+ none: false
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ hash: 31
165
+ segments:
166
+ - 1
167
+ - 3
168
+ - 2
169
+ version: 1.3.2
170
+ requirements: []
171
+
172
+ rubyforge_project:
173
+ rubygems_version: 1.8.10
174
+ signing_key:
175
+ specification_version: 3
176
+ summary: Rayback provides rake tasks for backing up and restoring your DB to cloud storage providers
177
+ test_files: []
178
+