jemmyw-backs3 0.0.3 → 0.0.4

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.
@@ -10,8 +10,11 @@ lib/backs3/backs3.rb
10
10
  lib/backs3/backup.rb
11
11
  lib/backs3/restore.rb
12
12
  lib/backs3/version.rb
13
+ lib/backs3/backup_info.rb
13
14
  spec/spec.opts
14
15
  spec/spec_helper.rb
15
16
  spec/backs3/backup_spec.rb
16
17
  spec/backs3/restore_spec.rb
18
+ spec/backs3/backup_info_spec.rb
19
+ spec/backs3/backup_file_info_spec.rb
17
20
  tasks/rspec.rake
data/README.txt CHANGED
@@ -29,7 +29,7 @@ A simple backup and restore program for S3
29
29
 
30
30
  (The MIT License)
31
31
 
32
- Copyright (c) 2009 Jeremy Wells / Boost Limited
32
+ Copyright (c) 2009 Jeremy Wells / Boost Limited (http://www.boost.co.nz)
33
33
 
34
34
  Permission is hereby granted, free of charge, to any person obtaining
35
35
  a copy of this software and associated documentation files (the
data/bin/res3 CHANGED
@@ -81,4 +81,10 @@ rescue Exception => e
81
81
  end
82
82
 
83
83
  res3 = Backs3::Restore.new($options)
84
- res3.send($command, *args)
84
+
85
+ begin
86
+ res3.send($command, *args)
87
+ rescue Exception => e
88
+ puts e
89
+ puts e.backtrace.join("\n")
90
+ end
@@ -5,11 +5,17 @@ require 'aws/s3'
5
5
  require 'active_support'
6
6
  require 'digest/md5'
7
7
  require 'time'
8
+ require File.join(File.dirname(__FILE__), 'backup_info')
8
9
 
9
10
  $has_md5 = !(`which md5`).blank?
10
11
 
11
12
  module Backs3
12
13
  include AWS::S3
14
+
15
+ def logger
16
+ logger_output = @options['logger'] || $stdout
17
+ @logger ||= Logger.new(logger_output)
18
+ end
13
19
 
14
20
  def establish_connection
15
21
  AWS::S3::Base.establish_connection!(
@@ -20,20 +26,39 @@ module Backs3
20
26
 
21
27
  def md5(filename)
22
28
  if $has_md5
23
- `md5 #{filename}`
29
+ `md5 -q #{filename}`
24
30
  else
25
31
  Digest::MD5.hexdigest(filename)
26
32
  end
27
33
  end
34
+
35
+ def save_backup_info(info)
36
+ S3Object.store(@options['prefix'] + 's3backup', YAML.dump(info), @options['bucket'])
37
+ logger.info "Backup info has been stored"
38
+ end
28
39
 
29
- def backup_info
30
- @backup_info ||= begin
40
+ def load_backup_info
41
+ @backups ||= begin
31
42
  backup_info_file = S3Object.find(@options['prefix'] + 's3backup', @options['bucket'])
32
43
  backup_info_data = backup_info_file.value(:reload)
33
- YAML.load(backup_info_data) || {}
44
+ YAML.load(backup_info_data) || {}
34
45
  rescue Exception => e
35
46
  puts e.to_s
36
- {}
47
+ []
48
+ end
49
+
50
+ unless @backups.respond_to?(:sort) && @backups.respond_to?(:each) && @backups.respond_to?(:reject!)
51
+ @backups = []
52
+ end
53
+
54
+ @backups.reject! do |backup|
55
+ !backup.respond_to?(:date)
56
+ end
57
+
58
+ @backups.sort do |a,b|
59
+ a.date <=> b.date
37
60
  end
61
+
62
+ @backups
38
63
  end
39
64
  end
@@ -3,84 +3,19 @@ require File.dirname(__FILE__) + '/backs3'
3
3
  module Backs3
4
4
  class Backup
5
5
  include Backs3
6
- include AWS::S3
7
6
 
8
7
  def initialize(options = {})
9
8
  @options = options
10
9
  @options['prefix'] ||= ''
11
- end
12
-
13
- def first_backup?
14
- last_backup.nil?
15
- end
16
-
17
- def full_backup
18
- @options['force-full'] || first_backup? || Time.now.to_i - last_backup > (@options['full'] || 7).days
19
- end
20
10
 
21
- def last_backup
22
- backup_info['last_backup'].to_i
23
- end
24
-
25
- def backup_key
26
- @options['prefix'] + @current_backup
27
- end
28
-
29
- def last_key
30
- @options['prefix'] + last_backup.to_s
31
- end
32
-
33
- def files_to_backup
34
- @files_to_backup ||= begin
35
- Dir.glob(File.join(@options['folder'], '**', '**')).select do |file|
36
- if File.directory?(file) || File.symlink?(file)
37
- false
38
- else
39
- if @options['exclude'].blank? || file !~ /#{@options['exclude']}/
40
- if full_backup || File.mtime(file).to_i > last_backup
41
- true
42
- else
43
- false
44
- end
45
- else
46
- false
47
- end
48
- end
49
- end
50
- end
11
+ establish_connection
12
+
13
+ @backups = load_backup_info
14
+ @backup = BackupInfo.new(@backups, @options)
51
15
  end
52
16
 
53
17
  def backup
54
- @current_backup = Time.now.to_i.to_s
55
- @files_to_backup = nil
56
- @backup_info = nil
57
-
58
- establish_connection
59
-
60
- puts "Backup started at #{Time.now}"
61
- puts "Last backup happened at #{backup_info['last_backup']}"
62
- puts "Performing %s" % (full_backup ? "full backup" : "incremental backup")
63
-
64
- files_to_backup.each do |filename|
65
- puts "Backing up #{filename}"
66
- file_md5 = md5(filename)
67
- aws_filename = File.join(backup_key, filename)
68
-
69
- object = S3Object.find(aws_filename, @options['bucket']) rescue nil
70
-
71
- if object.nil? || object.metadata[:md5sum] != file_md5
72
- S3Object.store(aws_filename, open(filename), @options['bucket'])
73
- object = S3Object.find(aws_filename, @options['bucket'])
74
- object.metadata[:md5sum] = file_md5
75
- object.save
76
- end
77
- end
78
-
79
- backup_info['last_backup'] = @current_backup
80
- backup_info['backups'] ||= []
81
- backup_info['backups'] << @current_backup
82
- S3Object.store(@options['prefix'] + 's3backup', YAML.dump(backup_info), @options['bucket'])
83
- puts "Backup completed, #{files_to_backup.size} files backed up"
18
+ @backup.backup
84
19
  end
85
20
  end
86
21
  end
@@ -7,39 +7,44 @@ module Backs3
7
7
  include AWS::S3
8
8
 
9
9
  def self.commands
10
- %w(ls available restore cat)
10
+ %w(ls available restore cat info)
11
11
  end
12
12
 
13
13
  def initialize(options = {})
14
14
  @options = options
15
15
  @options['prefix'] ||= ''
16
16
  establish_connection
17
+ @backups = load_backup_info.sort{|a,b| a.date <=> b.date }
17
18
  end
18
19
 
19
- def available(file = nil)
20
- if file.nil?
21
- puts "Backups available: #{backup_info['backups'].join(", ")}"
20
+ def available(backup_key = nil)
21
+ if backup_key.nil?
22
+ puts "Backups available: #{@backups.map{|b| b.date}.join(", ")}"
22
23
  else
23
- info = backup_info['backups'].collect do |backup|
24
- backup_key = @options['prefix'] + backup.to_s
25
- object = S3Object.find(File.join(backup_key, file), @options['bucket']) rescue nil
26
-
27
- if object
28
- {
29
- :backup => backup,
30
- :md5 => object.metadata[:md5sum]
31
- }
32
- else
33
- nil
34
- end
24
+ unless backup = @backups.detect{|b| b.date.to_s == backup_key.to_s }
25
+ raise "No backup #{backup_key} available"
26
+ end
35
27
 
36
- end.compact
28
+ files = backup.all_files
37
29
 
38
- puts "Backup information for #{file}:"
39
- info.each do |i|
40
- puts "\tBackup #{i[:backup]}: #{i[:md5]}"
30
+ puts "Backup information for #{backup.date}"
31
+ files.each do |file|
32
+ puts "\tFile: #{file.path}, backed up #{Time.at(file.backup_info.date).to_s}"
41
33
  end
34
+ end
35
+ end
42
36
 
37
+ def info(file)
38
+ files = @backups.collect{|b| b.files}.flatten.select{|f| f.path == file}
39
+
40
+ if files.empty?
41
+ puts "No information found for file #{file}"
42
+ else
43
+ puts "Backup information for file #{file}"
44
+
45
+ files.each do |f|
46
+ puts "\tBacked up #{Time.at(f.backup_info.date).to_s} in #{f.backup_info.date} with md5sum #{f.md5sum}"
47
+ end
43
48
  end
44
49
  end
45
50
 
@@ -60,30 +65,10 @@ module Backs3
60
65
  end
61
66
  end
62
67
 
63
- def restore(backup, file = nil)
64
- backup_key = @options['prefix'] + backup.to_s
65
- objects = []
66
-
67
- if file.nil?
68
- objects = Bucket.objects(@options['bucket'], :prefix => @options['prefix'] + backup.to_s)
69
- else
70
- objects << S3Object.find(File.join(backup_key, file), @options['bucket']) rescue nil
71
- end
72
-
73
- objects.compact!
74
-
75
- objects.each do |object|
76
- $stdout.write "Restoring file #{object.key} to /tmp/#{object.key}"
77
- filename = "/tmp/#{object.key}"
78
- FileUtils.mkdir_p File.dirname(filename)
79
- File.open(filename, 'w') do |f|
80
- object.value do |segment|
81
- $stdout.write "."
82
- f.write segment
83
- end
84
- end
85
- $stdout.write "\n"
86
- end
68
+ def restore(date, file = nil)
69
+ backup = @backups.detect{|b| b.date.to_s == date.to_s}
70
+ raise 'Cannot find backup %s' % date if backup.nil?
71
+ backup.restore('/tmp', file)
87
72
  end
88
73
  end
89
74
  end
@@ -2,7 +2,7 @@ module Backs3 #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 0
5
- TINY = 3
5
+ TINY = 4
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -8,66 +8,4 @@ describe Backup do
8
8
  @bucket = 'test_bucket'
9
9
  @backup = Backup.new({'bucket' => @bucket})
10
10
  end
11
-
12
- describe 'backup' do
13
- it 'should backup all the files returned by files_to_backup'
14
- end
15
-
16
- describe 'first_backup?' do
17
- it 'should be true if there is no previous backup' do
18
- @backup.should_receive(:last_backup).and_return(nil)
19
- @backup.first_backup?.should be_true
20
- end
21
-
22
- it 'should be false if there is a previous backup' do
23
- @backup.should_receive(:last_backup).and_return(12345)
24
- @backup.first_backup?.should be_false
25
- end
26
- end
27
-
28
- describe 'full_backup' do
29
- it 'should be true if force-full option is set' do
30
- @backup = Backup.new('force-full' => true)
31
- @backup.should_not_receive(:last_backup)
32
- @backup.should_not_receive(:first_backup?)
33
- @backup.full_backup.should be_true
34
- end
35
- it 'should be true if this is the first backup' do
36
- @backup.should_receive(:first_backup?).and_return(true)
37
- @backup.full_backup.should be_true
38
- end
39
- it 'should be true if the last backup was more than 7 days ago' do
40
- @backup.should_receive(:first_backup?).and_return(false)
41
- @backup.should_receive(:last_backup).and_return((Time.now - 8.days).to_i)
42
- @backup.full_backup.should be_true
43
- end
44
- it 'should be false' do
45
- @backup.should_receive(:first_backup?).and_return(false)
46
- @backup.should_receive(:last_backup).and_return(Time.now.to_i)
47
- @backup.full_backup.should be_false
48
- end
49
- end
50
-
51
- describe 'last_backup' do
52
- it 'should return the integer time of the last backup' do
53
- time = Time.now
54
- time.should_receive(:to_i).and_return(12345)
55
- @backup.should_receive(:backup_info).and_return({'last_backup' => time})
56
- @backup.last_backup.should == 12345
57
- end
58
- end
59
-
60
- describe 'backup_key' do
61
- it 'should return the prefix + the backup time'
62
- end
63
-
64
- describe 'last_key' do
65
- it 'should return the prefix + the last backup time'
66
- end
67
-
68
- describe 'files_to_backup' do
69
- it 'should return all the files in the backup folder'
70
- it 'should not return excluded files'
71
- it 'should not return files that have not changed if this is an incremental backup'
72
- end
73
11
  end
@@ -1,15 +1,43 @@
1
1
  require File.dirname(__FILE__) + "/../spec_helper"
2
2
  require 'backs3/restore'
3
3
 
4
- include Backs3
5
-
6
- describe Restore do
4
+ describe Backs3::Restore do
7
5
  before(:each) do
8
6
  AWS::S3::Base.stub!(:establish_connection!)
9
7
  @bucket = 'test_bucket'
10
8
  @restore = Restore.new('bucket' => @bucket)
11
9
 
12
10
  @file_1 = mock(:s3object, :metadata => {})
11
+
12
+ @backup_mock1 = mock(:backup_info, :date => 12345, :full => true)
13
+ @backup_mock2 = mock(:backup_info, :date => 54321, :last_full_backup => @backup_mock1, :full => false)
14
+
15
+ @file_mock1 = mock(:file, :backup_info => @backup_mock1, :path => 'test/file_1')
16
+ @file_mock2 = mock(:file, :backup_info => @backup_mock1, :path => 'test/file_2')
17
+ @file_mock3 = mock(:file, :backup_info => @backup_mock1, :path => 'test/file_3')
18
+ @file_mock4 = mock(:file, :backup_info => @backup_mock2, :path => 'test/file_1')
19
+
20
+ @files_mock1 = [
21
+ @file_mock1, @file_mock2, @file_mock3
22
+ ]
23
+
24
+ @files_mock2 = [
25
+ @file_mock4
26
+ ]
27
+
28
+ @files_mock3 = [
29
+ @file_mock4, @file_mock2, @file_mock3
30
+ ]
31
+
32
+ @backup_mock1.stub!(:files).and_return(@files_mock1)
33
+ @backup_mock2.stub!(:files).and_return(@files_mock2)
34
+
35
+ @backup_mock1.stub!(:all_files).and_return(@files_mock1)
36
+ @backup_mock2.stub!(:all_files).and_return(@files_mock3)
37
+
38
+ @backup_array = [@backup_mock1, @backup_mock2]
39
+ @restore.stub!(:load_backup_info).and_return(@backup_array)
40
+ @restore.instance_variable_set('@backups', @backup_array)
13
41
  end
14
42
 
15
43
  describe 'self.commands' do
@@ -19,22 +47,27 @@ describe Restore do
19
47
  end
20
48
 
21
49
  describe 'available' do
22
- it 'should list all of the backups available if no file is specified' do
23
- @restore.should_receive(:backup_info).and_return({'backups' => [12345, 54321]})
50
+ it 'should list all of the backups available' do
24
51
  @restore.should_receive(:puts).with('Backups available: 12345, 54321')
25
52
  @restore.available
26
53
  end
27
- it 'should list all of the backups a file is in' do
28
- file = 'test/file_1'
29
- @restore.should_receive(:backup_info).and_return({'backups' => [12345, 54321]})
30
- S3Object.should_receive(:find).with('12345/test/file_1', @bucket).and_return(@file_1)
31
- S3Object.should_receive(:find).with('54321/test/file_1', @bucket).and_return(nil)
32
54
 
33
- @restore.should_receive(:puts).with('Backup information for test/file_1:')
34
- @restore.should_receive(:puts).with("\tBackup 12345: ")
35
- @restore.should_not_receive(:puts).with("\tBackup 54321: ")
55
+ it 'should list all of the available files in a full backup' do
56
+ @restore.should_receive(:puts).once.with('Backup information for 12345')
57
+ @restore.should_receive(:puts).once.with("\tFile: test/file_1, backed up #{Time.at(12345).to_s}")
58
+ @restore.should_receive(:puts).once.with("\tFile: test/file_2, backed up #{Time.at(12345).to_s}")
59
+ @restore.should_receive(:puts).once.with("\tFile: test/file_3, backed up #{Time.at(12345).to_s}")
60
+
61
+ @restore.available(12345)
62
+ end
63
+
64
+ it 'should list all the files from the last full backup for a partial backup' do
65
+ @restore.should_receive(:puts).once.with('Backup information for 54321')
66
+ @restore.should_receive(:puts).once.with("\tFile: test/file_1, backed up #{Time.at(54321).to_s}")
67
+ @restore.should_receive(:puts).once.with("\tFile: test/file_2, backed up #{Time.at(12345).to_s}")
68
+ @restore.should_receive(:puts).once.with("\tFile: test/file_3, backed up #{Time.at(12345).to_s}")
36
69
 
37
- @restore.available(file)
70
+ @restore.available(54321)
38
71
  end
39
72
  end
40
73
 
@@ -1 +1,2 @@
1
- --colour
1
+ --colour
2
+ --reverse
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jemmyw-backs3
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Wells
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-03-25 00:00:00 -07:00
12
+ date: 2009-04-27 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -97,7 +97,7 @@ requirements: []
97
97
  rubyforge_project: backs3
98
98
  rubygems_version: 1.2.0
99
99
  signing_key:
100
- specification_version: 2
100
+ specification_version: 3
101
101
  summary: S3 backup and restore program
102
102
  test_files: []
103
103