backup2s3 0.2.0

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/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
+