jemmyw-backs3 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +0 -0
- data/Manifest.txt +17 -0
- data/README.txt +51 -0
- data/Rakefile +4 -0
- data/bin/backs3 +83 -0
- data/bin/res3 +84 -0
- data/example.conf +5 -0
- data/lib/backs3/backs3.rb +39 -0
- data/lib/backs3/backup.rb +82 -0
- data/lib/backs3/restore.rb +89 -0
- data/lib/backs3/version.rb +9 -0
- data/lib/backs3.rb +1 -0
- data/spec/backs3/backup_spec.rb +73 -0
- data/spec/backs3/restore_spec.rb +54 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +19 -0
- data/tasks/rspec.rake +21 -0
- metadata +103 -0
data/History.txt
ADDED
File without changes
|
data/Manifest.txt
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
example.conf
|
6
|
+
bin/backs3
|
7
|
+
bin/res3
|
8
|
+
lib/backs3.rb
|
9
|
+
lib/backs3/backs3.rb
|
10
|
+
lib/backs3/backup.rb
|
11
|
+
lib/backs3/restore.rb
|
12
|
+
lib/backs3/version.rb
|
13
|
+
spec/spec.opts
|
14
|
+
spec/spec_helper.rb
|
15
|
+
spec/backs3/backup_spec.rb
|
16
|
+
spec/backs3/restore_spec.rb
|
17
|
+
tasks/rspec.rake
|
data/README.txt
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
= Backs3
|
2
|
+
|
3
|
+
* http://github.com/jemmyw/backs3
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
A simple backup and restore program for S3
|
8
|
+
|
9
|
+
== FEATURES/PROBLEMS:
|
10
|
+
|
11
|
+
* FIX (list of features or problems)
|
12
|
+
|
13
|
+
== SYNOPSIS:
|
14
|
+
|
15
|
+
backs3 -c s3.conf test
|
16
|
+
res3 -c s3.conf available => [12345, 54321]
|
17
|
+
res3 -c s3.conf restore 12345
|
18
|
+
|
19
|
+
== REQUIREMENTS:
|
20
|
+
|
21
|
+
* ActiveSupport
|
22
|
+
* AWS/S3
|
23
|
+
|
24
|
+
== INSTALL:
|
25
|
+
|
26
|
+
* sudo gem install jemmyw-backs3
|
27
|
+
|
28
|
+
== LICENSE:
|
29
|
+
|
30
|
+
(The MIT License)
|
31
|
+
|
32
|
+
Copyright (c) 2009 Jeremy Wells / Boost Limited
|
33
|
+
|
34
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
35
|
+
a copy of this software and associated documentation files (the
|
36
|
+
'Software'), to deal in the Software without restriction, including
|
37
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
38
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
39
|
+
permit persons to whom the Software is furnished to do so, subject to
|
40
|
+
the following conditions:
|
41
|
+
|
42
|
+
The above copyright notice and this permission notice shall be
|
43
|
+
included in all copies or substantial portions of the Software.
|
44
|
+
|
45
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
46
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
47
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
48
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
49
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
50
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
51
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
data/bin/backs3
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rubygems'
|
5
|
+
require 'backs3/backup'
|
6
|
+
rescue LoadError => e
|
7
|
+
require 'lib/backs3/backup'
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'active_support'
|
11
|
+
require 'getoptlong'
|
12
|
+
|
13
|
+
option_parser = GetoptLong.new(
|
14
|
+
['--help', '-h', GetoptLong::NO_ARGUMENT],
|
15
|
+
['--config', '-c', GetoptLong::REQUIRED_ARGUMENT],
|
16
|
+
['--exclude', GetoptLong::REQUIRED_ARGUMENT],
|
17
|
+
['--id', '-i', GetoptLong::REQUIRED_ARGUMENT],
|
18
|
+
['--key', '-k', GetoptLong::REQUIRED_ARGUMENT],
|
19
|
+
['--bucket', '-b', GetoptLong::REQUIRED_ARGUMENT],
|
20
|
+
['--prefix', '-p', GetoptLong::REQUIRED_ARGUMENT],
|
21
|
+
['--full', '-f', GetoptLong::REQUIRED_ARGUMENT],
|
22
|
+
['--force-full', GetoptLong::NO_ARGUMENT]
|
23
|
+
)
|
24
|
+
|
25
|
+
$options = {}
|
26
|
+
|
27
|
+
def usage(message = nil)
|
28
|
+
$stderr.puts message unless message.blank?
|
29
|
+
|
30
|
+
name = $0.split('/').last
|
31
|
+
$stderr.puts <<"ENDUSAGE"
|
32
|
+
#{name} [options] <directory>
|
33
|
+
--help -h
|
34
|
+
--config -c Configuration file
|
35
|
+
--id -i AWS Access Key ID
|
36
|
+
--key -k AWS Secret Key
|
37
|
+
--bucket -b AWS Bucket name
|
38
|
+
--full=d -f d Number of days between full backups (default: 7)
|
39
|
+
--force-full Force a full backup
|
40
|
+
--prefix -p
|
41
|
+
--exclude="regex" Exclude files based on regex
|
42
|
+
|
43
|
+
ENDUSAGE
|
44
|
+
|
45
|
+
$stderr.puts "Current configuration:"
|
46
|
+
$options.each do |key, value|
|
47
|
+
$stderr.puts " #{key}: \t#{value}"
|
48
|
+
end
|
49
|
+
|
50
|
+
exit!
|
51
|
+
end #usage
|
52
|
+
|
53
|
+
begin
|
54
|
+
option_parser.each do |opt, arg|
|
55
|
+
$options[opt.gsub(/^-*/, '')] = (arg || true)
|
56
|
+
end
|
57
|
+
|
58
|
+
usage if $options['help']
|
59
|
+
$options['folder'] = ARGV[0] unless ARGV[0].blank?
|
60
|
+
|
61
|
+
raise Exception.new("Invalid configuration file #{$options['config']}") unless $options['config'].blank? || File.exists?($options['config'])
|
62
|
+
$options['config'] ||= '/etc/backs3.conf'
|
63
|
+
|
64
|
+
if File.exists?($options['config'])
|
65
|
+
begin
|
66
|
+
puts "Reading configuration from #{$options['config']}"
|
67
|
+
config = YAML::load_file($options['config'])
|
68
|
+
$options = config.merge($options)
|
69
|
+
rescue
|
70
|
+
raise Exception.new("Invalid configuration file #{$options['config']}")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
raise Exception.new("You must specify a directory to backup") if $options['folder'].blank?
|
75
|
+
raise Exception.new("You must specify a bucket") if $options['bucket'].blank?
|
76
|
+
raise Exception.new("You must specify an AWS ID") if $options['id'].blank?
|
77
|
+
raise Exception.new("You must specify an AWS Secret Key") if $options['key'].blank?
|
78
|
+
rescue Exception => e
|
79
|
+
usage(e.to_s)
|
80
|
+
end
|
81
|
+
|
82
|
+
backs3 = Backs3::Backup.new($options)
|
83
|
+
backs3.backup
|
data/bin/res3
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rubygems'
|
5
|
+
require 'backs3/restore'
|
6
|
+
rescue LoadError => e
|
7
|
+
require 'lib/backs3/restore'
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'active_support'
|
11
|
+
require 'getoptlong'
|
12
|
+
|
13
|
+
option_parser = GetoptLong.new(
|
14
|
+
['--help', '-h', GetoptLong::NO_ARGUMENT],
|
15
|
+
['--config', '-c', GetoptLong::REQUIRED_ARGUMENT],
|
16
|
+
['--id', '-i', GetoptLong::REQUIRED_ARGUMENT],
|
17
|
+
['--key', '-k', GetoptLong::REQUIRED_ARGUMENT],
|
18
|
+
['--bucket', '-b', GetoptLong::REQUIRED_ARGUMENT],
|
19
|
+
['--prefix', '-p', GetoptLong::REQUIRED_ARGUMENT]
|
20
|
+
)
|
21
|
+
|
22
|
+
$options = {}
|
23
|
+
|
24
|
+
def usage(message = nil)
|
25
|
+
$stderr.puts message unless message.blank?
|
26
|
+
|
27
|
+
name = $0.split('/').last
|
28
|
+
$stderr.puts <<"ENDUSAGE"
|
29
|
+
#{name} [options] <directory> <command> [command options]
|
30
|
+
Commands:
|
31
|
+
#{Backs3::Restore.commands.join(", ")}
|
32
|
+
|
33
|
+
--help -h
|
34
|
+
--config -c Configuration file
|
35
|
+
--id -i AWS Access Key ID
|
36
|
+
--key -k AWS Secret Key
|
37
|
+
--bucket -b AWS Bucket name
|
38
|
+
--prefix -p
|
39
|
+
|
40
|
+
ENDUSAGE
|
41
|
+
|
42
|
+
$stderr.puts "Current configuration:"
|
43
|
+
$options.each do |key, value|
|
44
|
+
$stderr.puts " #{key}: \t#{value}"
|
45
|
+
end
|
46
|
+
|
47
|
+
exit!
|
48
|
+
end #usage
|
49
|
+
|
50
|
+
begin
|
51
|
+
option_parser.each do |opt, arg|
|
52
|
+
$options[opt.gsub(/^-*/, '')] = (arg || true)
|
53
|
+
end
|
54
|
+
|
55
|
+
usage if $options['help']
|
56
|
+
|
57
|
+
raise Exception.new("Invalid configuration file #{$options['config']}") unless $options['config'].blank? || File.exists?($options['config'])
|
58
|
+
$options['config'] ||= '/etc/backs3.conf'
|
59
|
+
|
60
|
+
if File.exists?($options['config'])
|
61
|
+
begin
|
62
|
+
puts "Reading configuration from #{$options['config']}"
|
63
|
+
config = YAML::load_file($options['config'])
|
64
|
+
$options = config.merge($options)
|
65
|
+
rescue
|
66
|
+
raise Exception.new("Invalid configuration file #{$options['config']}")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
args = ARGV
|
71
|
+
$options['folder'] = args.shift if $options['folder'].blank?
|
72
|
+
$command = args.shift
|
73
|
+
|
74
|
+
raise Exception.new("You must specify a directory to restore from") if $options['folder'].blank?
|
75
|
+
raise Exception.new("You must specify a bucket") if $options['bucket'].blank?
|
76
|
+
raise Exception.new("You must specify an AWS ID") if $options['id'].blank?
|
77
|
+
raise Exception.new("You must specify an AWS Secret Key") if $options['key'].blank?
|
78
|
+
raise Exception.new("You must specify a valid command") unless Backs3::Restore.commands.include?($command)
|
79
|
+
rescue Exception => e
|
80
|
+
usage(e.to_s)
|
81
|
+
end
|
82
|
+
|
83
|
+
res3 = Backs3::Restore.new($options)
|
84
|
+
res3.send($command, *args)
|
data/example.conf
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# #!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems' rescue nil
|
4
|
+
require 'aws/s3'
|
5
|
+
require 'active_support'
|
6
|
+
require 'digest/md5'
|
7
|
+
require 'time'
|
8
|
+
|
9
|
+
$has_md5 = !(`which md5`).blank?
|
10
|
+
|
11
|
+
module Backs3
|
12
|
+
include AWS::S3
|
13
|
+
|
14
|
+
def establish_connection
|
15
|
+
AWS::S3::Base.establish_connection!(
|
16
|
+
:access_key_id => @options['id'],
|
17
|
+
:secret_access_key => @options['key']
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def md5(filename)
|
22
|
+
if $has_md5
|
23
|
+
`md5 #{filename}`
|
24
|
+
else
|
25
|
+
Digest::MD5.hexdigest(filename)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def backup_info
|
30
|
+
@backup_info ||= begin
|
31
|
+
backup_info_file = S3Object.find(@options['prefix'] + 's3backup', @options['bucket'])
|
32
|
+
backup_info_data = backup_info_file.value(:reload)
|
33
|
+
YAML.load(backup_info_data) || {}
|
34
|
+
rescue Exception => e
|
35
|
+
puts e.to_s
|
36
|
+
{}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/backs3'
|
2
|
+
|
3
|
+
module Backs3
|
4
|
+
class Backup
|
5
|
+
include Backs3
|
6
|
+
include AWS::S3
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
@options = options
|
10
|
+
@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
|
+
|
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 @options['exclude'].blank? || file !~ /#{@options['exclude']}/
|
37
|
+
if full_backup || File.mtime(file).to_i > last_backup
|
38
|
+
true
|
39
|
+
else
|
40
|
+
false
|
41
|
+
end
|
42
|
+
else
|
43
|
+
false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def backup
|
50
|
+
@current_backup = Time.now.to_i.to_s
|
51
|
+
@files_to_backup = nil
|
52
|
+
@backup_info = nil
|
53
|
+
|
54
|
+
establish_connection
|
55
|
+
|
56
|
+
puts "Backup started at #{Time.now}"
|
57
|
+
puts "Last backup happened at #{backup_info['last_backup']}"
|
58
|
+
puts "Performing %s" % (full_backup ? "full backup" : "incremental backup")
|
59
|
+
|
60
|
+
files_to_backup.each do |filename|
|
61
|
+
puts "Backing up #{filename}"
|
62
|
+
file_md5 = md5(filename)
|
63
|
+
aws_filename = File.join(backup_key, filename)
|
64
|
+
|
65
|
+
object = S3Object.find(aws_filename, @options['bucket']) rescue nil
|
66
|
+
|
67
|
+
if object.nil? || object.metadata[:md5sum] != file_md5
|
68
|
+
S3Object.store(aws_filename, open(filename), @options['bucket'])
|
69
|
+
object = S3Object.find(aws_filename, @options['bucket'])
|
70
|
+
object.metadata[:md5sum] = file_md5
|
71
|
+
object.save
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
backup_info['last_backup'] = @current_backup
|
76
|
+
backup_info['backups'] ||= []
|
77
|
+
backup_info['backups'] << @current_backup
|
78
|
+
S3Object.store(@options['prefix'] + 's3backup', YAML.dump(backup_info), @options['bucket'])
|
79
|
+
puts "Backup completed, #{files_to_backup.size} files backed up"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/backs3'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module Backs3
|
5
|
+
class Restore
|
6
|
+
include Backs3
|
7
|
+
include AWS::S3
|
8
|
+
|
9
|
+
def self.commands
|
10
|
+
%w(ls available restore cat)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
@options = options
|
15
|
+
@options['prefix'] ||= ''
|
16
|
+
establish_connection
|
17
|
+
end
|
18
|
+
|
19
|
+
def available(file = nil)
|
20
|
+
if file.nil?
|
21
|
+
puts "Backups available: #{backup_info['backups'].join(", ")}"
|
22
|
+
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
|
35
|
+
|
36
|
+
end.compact
|
37
|
+
|
38
|
+
puts "Backup information for #{file}:"
|
39
|
+
info.each do |i|
|
40
|
+
puts "\tBackup #{i[:backup]}: #{i[:md5]}"
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def ls(backup)
|
47
|
+
Bucket.objects(@options['bucket'], :prefix => @options['prefix'] + backup.to_s).each do |object|
|
48
|
+
puts object.key
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def cat(backup, file)
|
53
|
+
backup_key = @options['prefix'] + backup.to_s
|
54
|
+
object = S3Object.find(File.join(backup_key, file), @options['bucket']) rescue nil
|
55
|
+
|
56
|
+
if object.nil?
|
57
|
+
puts "Cannot find file #{file}"
|
58
|
+
else
|
59
|
+
puts object.value(:reload)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
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
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/backs3.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/backs3/backs3'
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
require 'backs3/backup'
|
3
|
+
include Backs3
|
4
|
+
|
5
|
+
describe Backup do
|
6
|
+
before(:each) do
|
7
|
+
AWS::S3::Base.stub!(:establish_connection!)
|
8
|
+
@bucket = 'test_bucket'
|
9
|
+
@backup = Backup.new({'bucket' => @bucket})
|
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
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
require 'backs3/restore'
|
3
|
+
|
4
|
+
include Backs3
|
5
|
+
|
6
|
+
describe Restore do
|
7
|
+
before(:each) do
|
8
|
+
AWS::S3::Base.stub!(:establish_connection!)
|
9
|
+
@bucket = 'test_bucket'
|
10
|
+
@restore = Restore.new('bucket' => @bucket)
|
11
|
+
|
12
|
+
@file_1 = mock(:s3object, :metadata => {})
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'self.commands' do
|
16
|
+
it 'should return an array' do
|
17
|
+
Restore.commands.should be_a(Array)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
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]})
|
24
|
+
@restore.should_receive(:puts).with('Backups available: 12345, 54321')
|
25
|
+
@restore.available
|
26
|
+
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
|
+
|
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: ")
|
36
|
+
|
37
|
+
@restore.available(file)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'ls' do
|
42
|
+
it 'should list all of the files in a directory'
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'cat' do
|
46
|
+
it 'should show an error if the file specified does not exist'
|
47
|
+
it 'should output the contents of a file'
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'restore' do
|
51
|
+
it 'should restore a whole backup if no file is specified'
|
52
|
+
it 'should restore a file'
|
53
|
+
end
|
54
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
begin
|
2
|
+
require 'spec'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems'
|
5
|
+
gem 'rspec'
|
6
|
+
require 'spec'
|
7
|
+
end
|
8
|
+
|
9
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
10
|
+
require 'backs3'
|
11
|
+
|
12
|
+
include Spec::Matchers
|
13
|
+
|
14
|
+
module Kernel
|
15
|
+
def logger
|
16
|
+
@@__log_file__ ||= StringIO.new
|
17
|
+
@@__log__ = ActiveSupport::BufferedLogger.new @@__log_file__
|
18
|
+
end
|
19
|
+
end
|
data/tasks/rspec.rake
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
require 'spec'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems'
|
5
|
+
require 'spec'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'spec/rake/spectask'
|
9
|
+
rescue LoadError
|
10
|
+
puts <<-EOS
|
11
|
+
To use rspec for testing you must install rspec gem:
|
12
|
+
gem install rspec
|
13
|
+
EOS
|
14
|
+
exit(0)
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Run the specs under spec"
|
18
|
+
Spec::Rake::SpecTask.new do |t|
|
19
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
20
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jemmyw-backs3
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jeremy Wells
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-25 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activesupport
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.0.2
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: aws-s3
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.5.1
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: hoe
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.8.0
|
44
|
+
version:
|
45
|
+
description: S3 backup and restore program
|
46
|
+
email:
|
47
|
+
- jeremy@boost.co.nz
|
48
|
+
executables:
|
49
|
+
- backs3
|
50
|
+
- res3
|
51
|
+
extensions: []
|
52
|
+
|
53
|
+
extra_rdoc_files:
|
54
|
+
- History.txt
|
55
|
+
- Manifest.txt
|
56
|
+
- README.txt
|
57
|
+
files:
|
58
|
+
- History.txt
|
59
|
+
- Manifest.txt
|
60
|
+
- README.txt
|
61
|
+
- Rakefile
|
62
|
+
- example.conf
|
63
|
+
- bin/backs3
|
64
|
+
- bin/res3
|
65
|
+
- lib/backs3.rb
|
66
|
+
- lib/backs3/backs3.rb
|
67
|
+
- lib/backs3/backup.rb
|
68
|
+
- lib/backs3/restore.rb
|
69
|
+
- lib/backs3/version.rb
|
70
|
+
- spec/spec.opts
|
71
|
+
- spec/spec_helper.rb
|
72
|
+
- spec/backs3/backup_spec.rb
|
73
|
+
- spec/backs3/restore_spec.rb
|
74
|
+
- tasks/rspec.rake
|
75
|
+
has_rdoc: true
|
76
|
+
homepage: http://backs3.rubyforge.org
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options:
|
79
|
+
- --main
|
80
|
+
- README.txt
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: "0"
|
88
|
+
version:
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: "0"
|
94
|
+
version:
|
95
|
+
requirements: []
|
96
|
+
|
97
|
+
rubyforge_project: backs3
|
98
|
+
rubygems_version: 1.2.0
|
99
|
+
signing_key:
|
100
|
+
specification_version: 2
|
101
|
+
summary: S3 backup and restore program
|
102
|
+
test_files: []
|
103
|
+
|