backup2s3 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
File without changes
data/Manifest ADDED
@@ -0,0 +1,14 @@
1
+ CHANGELOG
2
+ Manifest
3
+ README
4
+ Rakefile
5
+ generators/backup2s3/USAGE
6
+ generators/backup2s3/backup2s3_generator.rb
7
+ generators/backup2s3/templates/backup2s3.rake
8
+ generators/backup2s3/templates/backup2s3.yml
9
+ init.rb
10
+ lib/adapters/s3_adapter.rb
11
+ lib/backup2s3.rb
12
+ lib/backup_management/backup.rb
13
+ lib/backup_management/backup_manager.rb
14
+ lib/system.rb
data/README ADDED
@@ -0,0 +1,29 @@
1
+ SETUP:
2
+
3
+ 1. Install the gem
4
+ gem install backup2s3
5
+
6
+
7
+ 2. Add the below code to config/environment.rb
8
+ config.gem "backup2s3", :version => ">= 0.1.0"
9
+
10
+
11
+ 3. Run the generator in your application root directory
12
+ script/generate backup2s3
13
+
14
+
15
+ 4. Change your settings in config/backup2s3.yml
16
+
17
+
18
+ USAGE:
19
+
20
+ Backup tasks
21
+
22
+ rake backup2s3:backup:create - Creates a backup and moves it to S3
23
+ rake backup2s3:backup:delete - Deletes the specific backup
24
+ rake backup2s3:backup:list - Lists all backups that are currently on S3
25
+ rake backup2s3:backup:restore - Restores a specific backup
26
+
27
+ Some handy tasks
28
+
29
+ rake backup2s3:statistics - Shows you the size of your DB
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('backup2s3', '0.2.0') do |p|
6
+ p.description = "Backup application and database to S3"
7
+ p.url = "http://github.com/aricwalker/backup2s3"
8
+ p.author = "Aric Walker"
9
+ p.email = "aric@truespire.com"
10
+ p.ignore_pattern = ["nbproject/*/*", "nbproject/*"]
11
+ p.development_dependencies = []
12
+ end
data/backup2s3.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{backup2s3}
5
+ s.version = "0.2.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Aric Walker"]
9
+ s.date = %q{2010-04-18}
10
+ s.description = %q{Backup application and database to S3}
11
+ s.email = %q{aric@truespire.com}
12
+ s.extra_rdoc_files = ["CHANGELOG", "README", "lib/adapters/s3_adapter.rb", "lib/backup2s3.rb", "lib/backup_management/backup.rb", "lib/backup_management/backup_manager.rb", "lib/system.rb"]
13
+ s.files = ["CHANGELOG", "Manifest", "README", "Rakefile", "generators/backup2s3/USAGE", "generators/backup2s3/backup2s3_generator.rb", "generators/backup2s3/templates/backup2s3.rake", "generators/backup2s3/templates/backup2s3.yml", "init.rb", "lib/adapters/s3_adapter.rb", "lib/backup2s3.rb", "lib/backup_management/backup.rb", "lib/backup_management/backup_manager.rb", "lib/system.rb", "backup2s3.gemspec"]
14
+ s.homepage = %q{http://github.com/aricwalker/backup2s3}
15
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Backup2s3", "--main", "README"]
16
+ s.require_paths = ["lib"]
17
+ s.rubyforge_project = %q{backup2s3}
18
+ s.rubygems_version = %q{1.3.6}
19
+ s.summary = %q{Backup application and database to S3}
20
+
21
+ if s.respond_to? :specification_version then
22
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
23
+ s.specification_version = 3
24
+
25
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
26
+ else
27
+ end
28
+ else
29
+ end
30
+ end
@@ -0,0 +1 @@
1
+ backup2s3 generator
@@ -0,0 +1,45 @@
1
+ class Backup2s3Generator < Rails::Generator::Base
2
+
3
+ def manifest
4
+ record do |m|
5
+
6
+ m.directory("lib/tasks")
7
+ m.file("backup2s3.rake", "lib/tasks/backup2s3.rake")
8
+
9
+ m.directory("config")
10
+ m.file("backup2s3.yml", "config/backup2s3.yml")
11
+
12
+ puts message
13
+ end
14
+ end
15
+
16
+ def message
17
+ <<-MESSAGE
18
+
19
+ -------------------------------------------------------------------
20
+
21
+ You have successfully installed backup2s3!
22
+
23
+ 1. Modify your configuration file:
24
+
25
+ config/backup2s3.yml
26
+
27
+ 2. Get started.
28
+
29
+ Backup tasks
30
+
31
+ rake backup2s3:backup:create - Creates a backup and moves it to S3
32
+ rake backup2s3:backup:delete - Deletes the specific backup
33
+ rake backup2s3:backup:list - Lists all backups that are currently on S3
34
+ rake backup2s3:backup:restore - Restores a specific backup
35
+
36
+ Some handy tasks
37
+
38
+ rake backup2s3:statistics - Shows you the size of your DB
39
+
40
+ -------------------------------------------------------------------
41
+
42
+ MESSAGE
43
+ end
44
+
45
+ end
@@ -0,0 +1,36 @@
1
+ namespace :backup2s3 do
2
+ namespace :backup do
3
+ desc "Save a full back to S3"
4
+ task :create => :environment do
5
+ Backup2s3.new.create
6
+ end
7
+
8
+ desc "Save a full back to S3"
9
+ task :delete => :environment do
10
+ Backup2s3.new.delete
11
+ end
12
+
13
+ desc "Save a full back to S3"
14
+ task :list => :environment do
15
+ Backup2s3.new.list
16
+ end
17
+
18
+ desc "Restore your DB from S3"
19
+ task :restore => :environment do
20
+ Backup2s3.new.restore
21
+ end
22
+
23
+ # desc "Keep all backups for the last day, one per day for the last week, and one per week before that. Delete the rest."
24
+ # task :clean => :environment do
25
+ # Backup2s3.new.clean
26
+ # end
27
+ end
28
+
29
+ desc "Show table sizes for your database"
30
+ task :statistics => :environment do
31
+ rows = Backup2s3.new.statistics
32
+ rows.sort_by {|x| -x[3].to_i }
33
+ header = [["Type", "Data MB", "Index", "Rows", "Name"], []]
34
+ puts (header + rows).collect {|x| x.join("\t") }
35
+ end
36
+ end
@@ -0,0 +1,15 @@
1
+ #Sample backup2s3 configuration file
2
+ :backups:
3
+ :max_number_of_backups: 5
4
+ :backup_database: true
5
+ :backup_application_folders:
6
+ - public
7
+ - lib
8
+
9
+ :adapter:
10
+ :access_key_id: XXXXXXXXXXXXXXXXXXXX
11
+ :secret_access_key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
12
+ :use_ssl: true
13
+
14
+
15
+
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'backup2s3'
@@ -0,0 +1,69 @@
1
+ require 'aws/s3'
2
+ require 'tidy'
3
+
4
+ class Adapters::S3Adapter
5
+
6
+ def initialize(config)
7
+ @config = config
8
+ @connected = false
9
+ end
10
+
11
+ def ensure_connected
12
+ return if @connected
13
+ AWS::S3::Base.establish_connection!(@config)
14
+ AWS::S3::Bucket.create(bucket)
15
+ @connected = true
16
+ end
17
+
18
+ def store(file_name, file)
19
+ ensure_connected
20
+ AWS::S3::S3Object.store(file_name, file, bucket)
21
+ end
22
+
23
+ def fetch(file_name)
24
+ ensure_connected
25
+ AWS::S3::S3Object.find(file_name, bucket)
26
+
27
+ file = Tempfile.new("temp")
28
+ open(file.path, 'w') do |f|
29
+ AWS::S3::S3Object.stream(file_name, bucket) do |chunk|
30
+ f.write chunk
31
+ end
32
+ end
33
+ file
34
+ end
35
+
36
+ def read(file_name)
37
+ puts "trying..."
38
+ ensure_connected
39
+ puts "trying..."
40
+ return AWS::S3::S3Object.find(file_name, bucket)
41
+ end
42
+
43
+ def list
44
+ ensure_connected
45
+ AWS::S3::Bucket.find(bucket).objects.collect {|x| x.path }
46
+ end
47
+
48
+ def delete(file_name)
49
+ if object = AWS::S3::S3Object.find(file_name, bucket)
50
+ object.delete
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def access_key_id
57
+
58
+ end
59
+
60
+ def bucket
61
+ @bucket ||= clean("#{ActiveRecord::Base.connection.current_database.to_str.downcase}-ON-#{`hostname`.to_str.downcase}")
62
+ end
63
+
64
+ def clean(str)
65
+ str.gsub!(".", "-dot-")
66
+ str.gsub!("_", "-")
67
+ return str.tidy
68
+ end
69
+ end
data/lib/backup2s3.rb ADDED
@@ -0,0 +1,137 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+ require 'tempfile'
4
+ require 'yaml'
5
+
6
+ class Backup2s3
7
+ include System
8
+
9
+ def initialize
10
+ STDOUT.sync = true #used so that print will not buffer output
11
+ #ActiveResource::Base.logger = true
12
+ load_configuration
13
+ load_adapter
14
+ load_backup_manager
15
+ @database_file = ""
16
+ @application_file = ""
17
+ @time = Time.now.utc.strftime("%Y%m%d%H%M%S")
18
+ end
19
+
20
+
21
+ #CREATE creates a backup
22
+ def create(comment = ENV['comment'])
23
+ create_backup(comment)
24
+ save_backup_manager
25
+ end
26
+
27
+ #DELETE deletes a backup
28
+ def delete(backup_id = ENV['id'])
29
+ raise "ID to delete is blank!" and return if backup_id == nil
30
+ delete_backup(backup_id)
31
+ save_backup_manager
32
+ end
33
+
34
+ #RESTORE restores a backup
35
+ def restore(backup_id = ENV['id'])
36
+ raise "ID to restore is blank!" and return if backup_id == nil
37
+ restore_backup(backup_id)
38
+ save_backup_manager
39
+ end
40
+
41
+ #LIST
42
+ def list
43
+ @backup_manager.list_backups
44
+ end
45
+
46
+
47
+ private
48
+
49
+ # Creates both the application file backup and database backup and moves them
50
+ # to S3. This method will also update the BackupManager and store it's updated
51
+ # information.
52
+ def create_backup(comment)
53
+ if @conf[:backups][:backup_database]
54
+ @database_file = "#{@time}-#{System.db_credentials['database']}-database.sql"
55
+ database_temp = System.db_dump
56
+ puts "\n- System dump size: " << database_temp.size.to_s << " B"; print "--- Backing up database..."
57
+ @adapter.store(@database_file, open(database_temp.path))
58
+ puts "done"
59
+ end
60
+
61
+ if @conf[:backups][:backup_application_folders].is_a?(Array)
62
+ @application_file = "#{@time}-#{System.db_credentials['database']}-application.tar.gz"
63
+ application_temp = System.tarzip_folders(@conf[:backups][:backup_application_folders])
64
+ puts "\n- Application tarball size: " << application_temp.size.to_s << " B"; print "--- Backing up application folders..."
65
+ @adapter.store(@application_file, open(application_temp.path))
66
+ puts "done"
67
+ end
68
+
69
+ if @conf[:backups][:max_number_of_backups] == @backup_manager.number_of_backups then
70
+ puts "\nReached max_number_of_backups, removing oldest backup..."
71
+ backup_to_delete = @backup_manager.get_oldest_backup
72
+ delete_backup(backup_to_delete.time)
73
+ end
74
+ backup = BackupManagement::Backup.new(@time, @application_file, @database_file, comment)
75
+ @backup_manager.add_backup(backup)
76
+ puts ""
77
+ end
78
+
79
+ # Deletes the Backup, application backup files and database files associated
80
+ # with the Backup identified by backup_id.
81
+ def delete_backup(backup_id)
82
+ backup = @backup_manager.get_backup(backup_id)
83
+ if backup.nil? then
84
+ puts "Backup with ID #{backup_id} does not exist."
85
+ return
86
+ end
87
+ begin @adapter.delete(backup.application_file) rescue puts "Could not delete #{backup.application_file}!" end
88
+ begin @adapter.delete(backup.database_file) rescue puts "Could not delete #{backup.database_file}!" end
89
+ puts (@backup_manager.delete_backup(backup) ?
90
+ "Backup with ID #{backup.time} was successfully deleted." :
91
+ "Warning: Backup with ID #{backup.time} was not found and therefore not deleted.")
92
+ end
93
+
94
+ # def restore_backup(backup_id)
95
+ # backup = backup_manager.get_backup(backup_id)
96
+ # if backup.nil? then
97
+ # puts "Backup with ID #{backup_id} does not exist."
98
+ # return
99
+ # end
100
+ #
101
+ # end
102
+
103
+ # Loads the config/backup2s3.yml configuration file
104
+ def load_configuration
105
+ @conf = YAML.load_file("#{RAILS_ROOT}/config/backup2s3.yml")
106
+ end
107
+
108
+ # Creates instance of class used to interface with S3
109
+ def load_adapter
110
+ @adapter = Adapters::S3Adapter.new(@conf[:adapter])
111
+ end
112
+
113
+ def load_backup_manager
114
+ BackupManagement::BackupManager.new()
115
+ BackupManagement::Backup.new(nil, nil, nil)
116
+ begin
117
+ @backup_manager = YAML.load_file(@adapter.fetch(BackupManagement::BackupManager.filename).path)
118
+ @backup_manager ||= YAML.load_file(BackupManagement::BackupManager.local_filename)
119
+ rescue
120
+ @backup_manager ||= BackupManagement::BackupManager.new
121
+ end
122
+ end
123
+
124
+ def save_backup_manager
125
+ begin
126
+ File.open(BackupManagement::BackupManager.local_filename, "w") { |f| YAML.dump(@backup_manager, f) }
127
+ rescue
128
+ puts "Unable to save local file: " << BackupManagement::BackupManager.local_filename
129
+ end
130
+ begin
131
+ @adapter.store(BackupManagement::BackupManager.filename, open(BackupManagement::BackupManager.local_filename))
132
+ rescue
133
+ puts "Unable to save BackupManager to S3"
134
+ end
135
+ end
136
+
137
+ end
@@ -0,0 +1,19 @@
1
+ # To change this template, choose Tools | Templates
2
+ # and open the template in the editor.
3
+ require 'yaml'
4
+
5
+ class BackupManagement::Backup
6
+
7
+ attr_accessor :time, :application_file, :database_file, :comment
8
+
9
+ def initialize(time, application_file, database_file, comment = nil)
10
+ self.time = time
11
+ self.application_file = application_file
12
+ self.database_file = database_file
13
+ self.comment = comment
14
+ end
15
+
16
+ def human_readable_time
17
+ DateTime.parse(self.time).strftime("%m-%d-%Y %H:%M:%S")
18
+ end
19
+ end
@@ -0,0 +1,70 @@
1
+ require 'yaml'
2
+
3
+ class BackupManagement::BackupManager
4
+ include System
5
+
6
+ attr_accessor :backup_list_filename, :backups
7
+
8
+
9
+ def initialize
10
+ self.backups = Array.new
11
+ end
12
+
13
+ def self.filename
14
+ "#{System.db_credentials['database']}_ON_#{`hostname`.tidy}_backups.yaml".tidy
15
+ end
16
+
17
+ def self.local_filename
18
+ "#{RAILS_ROOT}/lib/#{filename}"
19
+ end
20
+
21
+ def add_backup(backup)
22
+ self.backups << backup
23
+ end
24
+
25
+ def delete_backup(backup)
26
+ self.backups.delete(backup)
27
+ end
28
+
29
+ def delete_backup_by_id(backup_id)
30
+ self.backups.each { |backup|
31
+ if backup.time == backup_id then
32
+ self.backups.delete(backup)
33
+ end
34
+ }
35
+ nil
36
+ end
37
+
38
+ def get_oldest_backup
39
+ self.backups.sort{|a,b| b.time <=> a.time}.last
40
+ end
41
+
42
+ def get_backup(backup_id)
43
+ self.backups.each { |backup|
44
+ if backup.time == backup_id then
45
+ return backup
46
+ end
47
+ }
48
+ nil
49
+ end
50
+
51
+ def list_backups(details = ENV['details'])
52
+ puts "\n--- Backups by Date ---"
53
+ count = 1
54
+ self.backups.sort{|a,b| b.time <=> a.time}.each do |backup|
55
+ puts "#{count}. #{backup.human_readable_time}, ID - #{backup.time}"
56
+ if details then
57
+ puts " --- App -> #{backup.application_file}"
58
+ puts " --- DB -> #{backup.database_file}"
59
+ puts " --- Comment -> #{backup.comment}"
60
+ end
61
+ count = count.next
62
+ end
63
+ puts "-----------------------\n\n"
64
+ end
65
+
66
+ def number_of_backups
67
+ backups.size
68
+ end
69
+
70
+ end
data/lib/system.rb ADDED
@@ -0,0 +1,46 @@
1
+ require 'tempfile'
2
+
3
+ module System
4
+
5
+ def self.db_credentials
6
+ ActiveRecord::Base.configurations[RAILS_ENV]
7
+ end
8
+
9
+ # Run system commands
10
+ def self.run(command)
11
+ result = system(command)
12
+ raise("error, process exited with status #{$?.exitstatus}") unless result
13
+ end
14
+
15
+ # Creates app tar file
16
+ def self.tarzip_folders(folders)
17
+ application_tar = Tempfile.new("app")
18
+ if folders.is_a?(Array)
19
+ cmd = "tar --dereference -czf #{application_tar.path} #{folders.join(" ")}"
20
+ elsif folders.is_a?(String)
21
+ cmd = "tar --dereference -czf #{application_tar.path} #{folders}"
22
+ end
23
+ run(cmd)
24
+ return application_tar
25
+ end
26
+
27
+ # Creates and runs mysqldump and throws into .tar.gz file.
28
+ # Returns .tar.gz file
29
+ def self.db_dump
30
+ dump_file = Tempfile.new("dump")
31
+ cmd = "mysqldump --quick --single-transaction --create-options #{mysql_options}"
32
+ cmd += " > #{dump_file.path}"
33
+ run(cmd)
34
+ return dump_file
35
+ end
36
+
37
+ def self.mysql_options
38
+ cmd = ''
39
+ cmd += " -u #{db_credentials['username']} " unless db_credentials['username'].nil?
40
+ cmd += " -p'#{db_credentials['password']}'" unless db_credentials['password'].nil?
41
+ cmd += " -h '#{db_credentials['host']}'" unless db_credentials['host'].nil?
42
+ cmd += " #{db_credentials['database']}"
43
+ end
44
+
45
+ end
46
+
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: backup2s3
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ - 0
9
+ version: 0.2.0
10
+ platform: ruby
11
+ authors:
12
+ - Aric Walker
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-18 00:00:00 -06:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Backup application and database to S3
22
+ email: aric@truespire.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - CHANGELOG
29
+ - README
30
+ - lib/adapters/s3_adapter.rb
31
+ - lib/backup2s3.rb
32
+ - lib/backup_management/backup.rb
33
+ - lib/backup_management/backup_manager.rb
34
+ - lib/system.rb
35
+ files:
36
+ - CHANGELOG
37
+ - Manifest
38
+ - README
39
+ - Rakefile
40
+ - generators/backup2s3/USAGE
41
+ - generators/backup2s3/backup2s3_generator.rb
42
+ - generators/backup2s3/templates/backup2s3.rake
43
+ - generators/backup2s3/templates/backup2s3.yml
44
+ - init.rb
45
+ - lib/adapters/s3_adapter.rb
46
+ - lib/backup2s3.rb
47
+ - lib/backup_management/backup.rb
48
+ - lib/backup_management/backup_manager.rb
49
+ - lib/system.rb
50
+ - backup2s3.gemspec
51
+ has_rdoc: true
52
+ homepage: http://github.com/aricwalker/backup2s3
53
+ licenses: []
54
+
55
+ post_install_message:
56
+ rdoc_options:
57
+ - --line-numbers
58
+ - --inline-source
59
+ - --title
60
+ - Backup2s3
61
+ - --main
62
+ - README
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ segments:
77
+ - 1
78
+ - 2
79
+ version: "1.2"
80
+ requirements: []
81
+
82
+ rubyforge_project: backup2s3
83
+ rubygems_version: 1.3.6
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: Backup application and database to S3
87
+ test_files: []
88
+