jemmyw-backs3 0.0.1
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/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
|
+
|