backupgem 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,5 +1,9 @@
1
1
  = Backup Changelog
2
2
 
3
+ == Version 0.0.5
4
+ * Added Amazon s3 support. Thanks to Jason Perry for this patch.
5
+ * Added an option for ssh port. Thanks to Frank Oxener for this patch.
6
+
3
7
  == Version 0.0.4
4
8
 
5
9
  * Fixed a bug in 'scp' in the standard recipe that ignored the user when using
data/TODO ADDED
@@ -0,0 +1,7 @@
1
+ == 2007-01-26
2
+ * Write some better tests
3
+ * Create a "rake test" task
4
+ * Create rake tasks for generating documentation
5
+ * Move documentation maker to this project
6
+ * Add documentation for s3
7
+ * Add documentation for ssh port
data/examples/s3.rb ADDED
@@ -0,0 +1,35 @@
1
+ #------------------------------------------------------------------------------
2
+ # Example S3 Backup script
3
+ # @author: Jason L. Perry <jasper@ambethia.com>
4
+ #------------------------------------------------------------------------------
5
+
6
+ # Set the name of the s3 bucket you want to store your backups in.
7
+ # Your Access ID is prepended to this to avoid naming conflicts.
8
+ set :backup_path, "database_backup"
9
+
10
+ # You can specify your keys here, or set them as environment variables:
11
+ # AMAZON_ACCESS_KEY_ID
12
+ # AMAZON_SECRET_ACCESS_KEY
13
+ set :aws_access, '123'
14
+ set :aws_secret, 'ABC'
15
+
16
+ # S3 does not support renaming objects, so rotation data is stored in an
17
+ # index. You can specify a different key for index here, if you need to.
18
+ #
19
+ # set :rotation_object_key, 'backup_rotation_index.yml'
20
+
21
+ action(:content) do
22
+ dump = c[:tmp_dir] + "/databases.sql"
23
+ sh "mysqldump -uroot --all-databases > #{dump}"
24
+ dump
25
+ end
26
+
27
+ action :deliver, :method => :s3
28
+ action :rotate, :method => :via_s3
29
+
30
+ set :son_promoted_on, :fri
31
+ set :father_promoted_on, :last_fri_of_the_month
32
+
33
+ set :sons_to_keep, 7
34
+ set :fathers_to_keep, 5
35
+ set :grandfathers_to_keep, 12
data/lib/backup/actor.rb CHANGED
@@ -113,6 +113,7 @@ module Backup
113
113
  def via_mv; rotator.rotate_via_mv(last_result); end
114
114
  def via_ssh; rotator.rotate_via_ssh(last_result); end
115
115
  def via_ftp; rotator.rotate_via_ftp(last_result); end
116
+ def via_s3; rotator.rotate_via_s3(last_result); end
116
117
 
117
118
  # By default, +:content+ can perform one of three actions
118
119
  # * +:is_file+
@@ -79,6 +79,11 @@ action(:mv) do
79
79
  c[:backup_path] + "/" + File.basename(last_result)
80
80
  end
81
81
 
82
+ action(:s3) do
83
+ s3 = S3Actor.new(c)
84
+ s3.put last_result
85
+ end
86
+
82
87
  action(:encrypt) do
83
88
  result = last_result
84
89
  if c[:encrypt]
@@ -59,6 +59,15 @@ module Backup
59
59
  # ftp.close
60
60
  end
61
61
 
62
+ def rotate_via_s3(last_result)
63
+ s3 = Backup::S3Actor.new(c)
64
+ s3.verify_rotation_hierarchy_exists(hierarchy)
65
+ index = s3.rotation
66
+ index[todays_generation] << last_result
67
+ s3.rotation = index
68
+ s3.cleanup(todays_generation, how_many_to_keep_today)
69
+ end
70
+
62
71
  def create_sons_today?; is_today_a? :son_created_on; end
63
72
  def promote_sons_today?; is_today_a? :son_promoted_on; end
64
73
  def promote_fathers_today?; is_today_a? :father_promoted_on; end
@@ -110,18 +119,21 @@ module Backup
110
119
  promote_sons_today? ? "fathers" : "sons"
111
120
  end
112
121
 
122
+ def self.timestamped_prefix(name)
123
+ newname = Time.now.strftime("%Y-%m-%d-%H-%M-%S_") + File.basename(name)
124
+ end
125
+
126
+ # Given +name+ returns a timestamped version of name.
127
+ def timestamped_prefix(name)
128
+ Backup::Rotator.timestamped_prefix(name)
129
+ end
130
+
113
131
  private
114
132
  def place_in
115
133
  goes_in = todays_generation
116
134
  place_in = c[:backup_path] + "/" + goes_in
117
135
  end
118
136
 
119
-
120
- # Given +name+ returns a timestamped version of name.
121
- def timestamped_prefix(name)
122
- newname = Time.now.strftime("%Y-%m-%d-%H-%M-%S_") + File.basename(name)
123
- end
124
-
125
137
  # Returns the number of sons to keep. Looks for config values +:sons_to_keep+,
126
138
  # +:son_promoted_on+. Default +14+.
127
139
  def sons_to_keep
@@ -0,0 +1,95 @@
1
+ require 'YAML'
2
+
3
+ module Backup
4
+ class S3Actor
5
+ include AWS::S3
6
+
7
+ attr_accessor :rotation
8
+
9
+ attr_reader :config
10
+ alias_method :c, :config
11
+
12
+ def initialize(config)
13
+ @config = config
14
+ @rotation_key = c[:rotation_object_key] ||= 'backup_rotation_index.yml'
15
+ @access_key = c[:aws_access] ||= ENV['AMAZON_ACCESS_KEY_ID']
16
+ @secret_key = c[:aws_secret] ||= ENV['AMAZON_SECRET_ACCESS_KEY']
17
+ @bucket_key = "#{@access_key}.#{c[:backup_path]}"
18
+ Base.establish_connection!(
19
+ :access_key_id => @access_key,
20
+ :secret_access_key => @secret_key
21
+ )
22
+ begin
23
+ # Look for our bucket, if it's not there, try to create it.
24
+ @bucket = Bucket.find @bucket_key
25
+ rescue NoSuchBucket
26
+ @bucket = Bucket.create @bucket_key
27
+ @bucket = Bucket.find @bucket_key
28
+ end
29
+ end
30
+
31
+ def rotation
32
+ object = S3Object.find(@rotation_key, @bucket.name)
33
+ index = YAML::load(object.value)
34
+ end
35
+
36
+ def rotation=(index)
37
+ object = S3Object.store(@rotation_key, index.to_yaml, @bucket.name)
38
+ index
39
+ end
40
+
41
+ # Send a file to s3
42
+ def put(last_result)
43
+ puts last_result
44
+ object_key = Rotator.timestamped_prefix(last_result)
45
+ S3Object.store object_key,
46
+ open(last_result),
47
+ @bucket.name
48
+ object_key
49
+ end
50
+
51
+ # Remove a file from s3
52
+ def delete(object_key)
53
+ S3Object.delete object_key, @bucket.name
54
+ end
55
+
56
+ # Make sure our rotation index exists and contains the hierarchy we're using.
57
+ # Create it if it does not exist
58
+ def verify_rotation_hierarchy_exists(hierarchy)
59
+ begin
60
+ index = rotation
61
+ verified_index = index.merge(init_rotation_index(hierarchy)) { |m,x,y| x ||= y }
62
+ unless (verified_index == index)
63
+ self.rotation = verified_index
64
+ end
65
+ rescue NoSuchKey
66
+ self.rotation = init_rotation_index(hierarchy)
67
+ end
68
+ end
69
+
70
+ # Expire old objects
71
+ def cleanup(generation, keep)
72
+ puts "Cleaning up"
73
+
74
+ keys = self.rotation[generation]
75
+ diff = keys.size - keep
76
+
77
+ 1.upto( diff ) do
78
+ extra_key = keys.shift
79
+ delete extra_key
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ # Create a new index representing our backup hierarchy
86
+ def init_rotation_index(hierarchy)
87
+ hash = {}
88
+ hierarchy.each do |m|
89
+ hash[m] = Array.new
90
+ end
91
+ hash
92
+ end
93
+
94
+ end
95
+ end
@@ -96,8 +96,9 @@ module Backup
96
96
  def connect
97
97
  c[:servers].each do |server| # todo, make this actually work
98
98
  @session = Net::SSH.start(
99
- server,
100
- c[:ssh_user],
99
+ server,
100
+ :port => c[:port],
101
+ :username => c[:ssh_user],
101
102
  :host_key => "ssh-rsa",
102
103
  :keys => [ c[:identity_key] ],
103
104
  :auth_methods => %w{ publickey } )
data/lib/backup.rb CHANGED
@@ -5,3 +5,12 @@ require 'backup/configuration'
5
5
  require 'backup/extensions'
6
6
  require 'backup/ssh_helpers'
7
7
  require 'backup/date_parser'
8
+
9
+ begin
10
+ require 'aws/s3'
11
+ require 'backup/s3_helpers'
12
+ rescue LoadError
13
+ # If AWS::S3 is not installed, no worries, we just
14
+ # wont have access to s3 methods. It's worth noting
15
+ # at least version 1.8.4 of ruby is required for s3.
16
+ end
data/tests/s3_test.rb ADDED
@@ -0,0 +1,40 @@
1
+ require File.dirname(__FILE__) + "/tests_helper"
2
+
3
+ class S3Test < Test::Unit::TestCase
4
+
5
+ # These tests require actual S3 access keys set as environment variables
6
+
7
+ def setup
8
+ @config = Backup::Configuration.new
9
+ @config.load "standard"
10
+ @config.set :backup_path, 'test_backup'
11
+ @config.action :deliver, :method => :s3
12
+ @actor = Backup::S3Actor.new(@config)
13
+ end
14
+
15
+ def test_exists
16
+ assert @config
17
+ assert @actor
18
+ end
19
+
20
+ def test_on_s3
21
+ dir = create_tmp_files
22
+ config = <<-END
23
+ action :content, :is_folder => "#{dir}"
24
+ action :rotate, :method => :via_s3
25
+ END
26
+ @config.load :string => config
27
+ assert result = @config.actor.content
28
+ assert File.exists?(result)
29
+ @config.actor.start_process!
30
+ end
31
+
32
+ private
33
+
34
+ def create_tmp_files
35
+ newtmp = @config[:tmp_dir] + "/test_#{rand}_" + Time.now.strftime("%Y%m%d%H%M%S")
36
+ sh "mkdir #{newtmp}"
37
+ 0.upto(5) { |i| sh "touch #{newtmp}/#{i}" }
38
+ newtmp
39
+ end
40
+ end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: backupgem
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.4
7
- date: 2006-10-16 00:00:00 -07:00
6
+ version: 0.0.5
7
+ date: 2007-01-26 00:00:00 -08:00
8
8
  summary: Beginning-to-end solution for backups and rotation.
9
9
  require_paths:
10
10
  - lib
@@ -40,29 +40,35 @@ files:
40
40
  - lib/backup/extensions.rb
41
41
  - lib/backup/recipes
42
42
  - lib/backup/rotator.rb
43
+ - lib/backup/s3_helpers.rb
43
44
  - lib/backup/ssh_helpers.rb
44
45
  - lib/backup/recipes/standard.rb
45
46
  - tests/actor_test.rb
46
47
  - tests/cleanup.sh
47
48
  - tests/rotation_test.rb
49
+ - tests/s3_test.rb
48
50
  - tests/ssh_test.rb
49
51
  - tests/tests_helper.rb
50
52
  - examples/global.rb
51
53
  - examples/mediawiki.rb
54
+ - examples/s3.rb
52
55
  - doc/index.html
53
56
  - doc/LICENSE-GPL
54
57
  - doc/styles.css
55
58
  - README
56
59
  - CHANGELOG
60
+ - TODO
57
61
  test_files:
58
62
  - tests/actor_test.rb
59
63
  - tests/rotation_test.rb
64
+ - tests/s3_test.rb
60
65
  - tests/ssh_test.rb
61
66
  rdoc_options: []
62
67
 
63
68
  extra_rdoc_files:
64
69
  - README
65
70
  - CHANGELOG
71
+ - TODO
66
72
  executables: []
67
73
 
68
74
  extensions: []