s3-backup 0.6.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/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +63 -0
- data/Rakefile +61 -0
- data/VERSION +1 -0
- data/lib/s3-backup.rb +16 -0
- data/lib/s3-backup/base.rb +118 -0
- data/s3-backup.gemspec +64 -0
- data/test/base_test.rb +164 -0
- data/test/helper.rb +17 -0
- metadata +138 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Ben Koski
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
= s3-backup
|
2
|
+
|
3
|
+
Easy tar-based backups to S3, with backup rotation, cleanup helpers, and pre-/post-backup hooks.
|
4
|
+
|
5
|
+
== Install
|
6
|
+
|
7
|
+
sudo gem install s3-backup
|
8
|
+
|
9
|
+
== Easy Example
|
10
|
+
|
11
|
+
b = S3Backup.new('vps-backups') # Init with the name of bucket to push to; keys handled by AWSCredentials
|
12
|
+
|
13
|
+
b.files << '/usr/local/important.data'
|
14
|
+
b.files << '/usr/local/more/important.data'
|
15
|
+
b.files << '/usr/local/secrets'
|
16
|
+
|
17
|
+
b.run
|
18
|
+
|
19
|
+
This will:
|
20
|
+
|
21
|
+
1. Create a .tar.gz file including everything in <tt>files</tt>
|
22
|
+
2. Push tar to S3
|
23
|
+
3. Delete old backups (keeps 5 by default, but you can change <tt>copies_to_keep</tt>)
|
24
|
+
4. Remove scratch files
|
25
|
+
|
26
|
+
== Hooks
|
27
|
+
|
28
|
+
S3Backup also includes a <tt>before_backup</tt> and <tt>after_backup</tt> hook to take care of preparing dumpfiles
|
29
|
+
and restarting services. These are just blocks of code.
|
30
|
+
|
31
|
+
Also useful, <tt>files_to_cleanup</tt> contains a list of files to cleanup post-backup. You
|
32
|
+
can add your scratch files to this list and they'll be deleted after each run, even if an error is encountered.
|
33
|
+
|
34
|
+
<tt>tar_excludes</tt> is an array of patterns for tar to exclude -- for example, "*.log"
|
35
|
+
|
36
|
+
For example:
|
37
|
+
|
38
|
+
b = S3Backup.new('vps-backups')
|
39
|
+
|
40
|
+
b.before_backup do
|
41
|
+
`mysqldump -h locahost important_data > /tmp/important_data.sql`
|
42
|
+
raise "mysqdump failed" if !$?.success?
|
43
|
+
|
44
|
+
b.files_to_cleanup << '/tmp/important_data.sql'
|
45
|
+
b.files << '/tmp/important_data.sql'
|
46
|
+
end
|
47
|
+
|
48
|
+
b.run
|
49
|
+
|
50
|
+
You'll find additional opts in the S3Backup docs.
|
51
|
+
|
52
|
+
== Suggested deployment
|
53
|
+
|
54
|
+
Create a ruby script, and add to your crontab. Don't forget to 2>&1 the output and set a <tt>MAILTO</tt> to get error notices.
|
55
|
+
|
56
|
+
== Notes
|
57
|
+
|
58
|
+
1. This uses AWSCredentials[http://github.com/bkoski/aws_credentials] to manage AWS keys.
|
59
|
+
2. This doesn't work on Windows.
|
60
|
+
|
61
|
+
== Copyright
|
62
|
+
|
63
|
+
Copyright (c) 2010 Ben Koski. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "s3-backup"
|
8
|
+
gem.summary = %Q{Easy tar-based backups to S3, with backup rotation, cleanup helpers, and pre-/post-backup hooks.}
|
9
|
+
gem.description = %Q{Easy tar-based backups to S3, with backup rotation, cleanup helpers, and pre-/post-backup hooks.}
|
10
|
+
gem.email = "gems@benkoski.com"
|
11
|
+
gem.homepage = "http://github.com/bkoski/s3-backup"
|
12
|
+
gem.authors = ["Ben Koski"]
|
13
|
+
|
14
|
+
gem.files += ["lib/s3-backup/base.rb"]
|
15
|
+
|
16
|
+
gem.add_dependency "aws_credentials", ">= 0.6.0"
|
17
|
+
gem.add_dependency "right_aws", "~> 2.0.0"
|
18
|
+
|
19
|
+
gem.add_development_dependency "shoulda", ">= 0"
|
20
|
+
gem.add_development_dependency "mocha", ">= 0"
|
21
|
+
|
22
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
23
|
+
end
|
24
|
+
Jeweler::GemcutterTasks.new
|
25
|
+
rescue LoadError
|
26
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
27
|
+
end
|
28
|
+
|
29
|
+
require 'rake/testtask'
|
30
|
+
Rake::TestTask.new(:test) do |test|
|
31
|
+
test.libs << 'lib' << 'test'
|
32
|
+
test.pattern = 'test/**/test_*.rb'
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
|
36
|
+
begin
|
37
|
+
require 'rcov/rcovtask'
|
38
|
+
Rcov::RcovTask.new do |test|
|
39
|
+
test.libs << 'test'
|
40
|
+
test.pattern = 'test/**/test_*.rb'
|
41
|
+
test.verbose = true
|
42
|
+
end
|
43
|
+
rescue LoadError
|
44
|
+
task :rcov do
|
45
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
task :test => :check_dependencies
|
50
|
+
|
51
|
+
task :default => :test
|
52
|
+
|
53
|
+
require 'hanna/rdoctask'
|
54
|
+
Rake::RDocTask.new do |rdoc|
|
55
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
56
|
+
|
57
|
+
rdoc.rdoc_dir = 'rdoc'
|
58
|
+
rdoc.title = "s3-backup #{version}"
|
59
|
+
rdoc.rdoc_files.include('README*')
|
60
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
61
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.6.0
|
data/lib/s3-backup.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'right_aws'
|
2
|
+
require 'aws_credentials'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
require File.join(File.dirname(__FILE__), 's3-backup', 'base')
|
6
|
+
|
7
|
+
# To suppress "warning: peer certificate won't be verified in this SSL session" errors,
|
8
|
+
# borrowed from http://www.5dollarwhitebox.org/drupal/node/64
|
9
|
+
class Net::HTTP
|
10
|
+
alias_method :old_initialize, :initialize
|
11
|
+
def initialize(*args)
|
12
|
+
old_initialize(*args)
|
13
|
+
@ssl_context = OpenSSL::SSL::SSLContext.new
|
14
|
+
@ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
class S3Backup
|
2
|
+
|
3
|
+
# Array of files or paths to back up
|
4
|
+
attr_accessor :files
|
5
|
+
|
6
|
+
# Array of files that will be deleted post-backup, regardless of success
|
7
|
+
attr_accessor :files_to_cleanup
|
8
|
+
|
9
|
+
# Array of exclude patterns, these are passed to tar as <tt>--exclude</tt> flags
|
10
|
+
attr_accessor :tar_excludes
|
11
|
+
|
12
|
+
# Number of backups to keep on S3. Defaults to 5.
|
13
|
+
attr_accessor :copies_to_keep
|
14
|
+
|
15
|
+
# Prefix for tarball, defaults to "backup".
|
16
|
+
attr_accessor :backup_name
|
17
|
+
|
18
|
+
# Name of bucket to push to, set on initialize.
|
19
|
+
attr_reader :bucket_name
|
20
|
+
|
21
|
+
# Initialize with the name of S3 bucket to push to
|
22
|
+
def initialize(bucket_name)
|
23
|
+
@files = []
|
24
|
+
@files_to_cleanup = []
|
25
|
+
@tar_excludes = []
|
26
|
+
@copies_to_keep = 5
|
27
|
+
@bucket_name = bucket_name
|
28
|
+
@backup_name = 'backup'
|
29
|
+
|
30
|
+
@s3 = RightAws::S3.new(AWSCredentials.access_key, AWSCredentials.secret_access_key, :logger => Logger.new(nil))
|
31
|
+
end
|
32
|
+
|
33
|
+
# Called before backup runs. Useful for dumping a database, or creating files
|
34
|
+
# prior to tarball create. As you create tmpfiles, you can push onto files_to_cleanup
|
35
|
+
# to ensure post-backup cleanup.
|
36
|
+
def before_backup &block
|
37
|
+
@before_backup = block
|
38
|
+
end
|
39
|
+
|
40
|
+
# Called after backup runs. Useful for restarting services.
|
41
|
+
def after_backup &block
|
42
|
+
@after_backup = block
|
43
|
+
end
|
44
|
+
|
45
|
+
# Runs the backup: creates tarball, pushes to s3, and rotates old backups.
|
46
|
+
def run
|
47
|
+
begin
|
48
|
+
@before_backup.call unless @before_backup.nil?
|
49
|
+
|
50
|
+
create_tarball
|
51
|
+
push_to_s3
|
52
|
+
rotate_remote_backups
|
53
|
+
|
54
|
+
@after_backup.call unless @after_backup.nil?
|
55
|
+
ensure
|
56
|
+
cleanup_files
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
# Name of the tarball created locally
|
62
|
+
def tarball_name
|
63
|
+
@tarbarll_name ||= "/tmp/#{backup_name}-#{Time.now.to_i}.tar.gz"
|
64
|
+
@tarbarll_name
|
65
|
+
end
|
66
|
+
|
67
|
+
# Name of the file passed to tar -I containing files to back up
|
68
|
+
def include_file_name
|
69
|
+
tarball_name.gsub('.tar.gz','-includes.txt')
|
70
|
+
end
|
71
|
+
|
72
|
+
def create_tarball
|
73
|
+
raise ArgumentError, "files to backup is empty!" if files.empty?
|
74
|
+
|
75
|
+
write_include_file
|
76
|
+
files_to_cleanup << include_file_name
|
77
|
+
|
78
|
+
excludes = tar_excludes.collect { |e| "--exclude #{e}" }
|
79
|
+
run_tar(%{#{excludes.join(" ")} --preserve -I #{include_file_name} -czf #{tarball_name}})
|
80
|
+
|
81
|
+
files_to_cleanup << tarball_name
|
82
|
+
end
|
83
|
+
|
84
|
+
def write_include_file
|
85
|
+
File.open(include_file_name, 'w') { |f| f.write(files.join("\n")) }
|
86
|
+
end
|
87
|
+
|
88
|
+
def run_tar params
|
89
|
+
output = `tar #{params} 2>&1`
|
90
|
+
raise "tar create failed with #{output}" if !$?.success?
|
91
|
+
end
|
92
|
+
|
93
|
+
def s3_bucket
|
94
|
+
raise ArgumentError, "bucket_name must be set to run a backup!" if @bucket_name.nil?
|
95
|
+
if @s3_bucket.nil?
|
96
|
+
@s3_bucket = @s3.bucket(bucket_name)
|
97
|
+
raise ArgumentError, "bucket #{bucket_name} not found!" if @s3_bucket.nil?
|
98
|
+
end
|
99
|
+
|
100
|
+
@s3_bucket
|
101
|
+
end
|
102
|
+
|
103
|
+
def push_to_s3
|
104
|
+
s3_bucket.put(File.basename(tarball_name), File.open(tarball_name))
|
105
|
+
end
|
106
|
+
|
107
|
+
def rotate_remote_backups
|
108
|
+
all_backups = s3_bucket.keys(:prefix => backup_name).sort_by { |k| k.name }.reverse
|
109
|
+
if all_backups.length > copies_to_keep
|
110
|
+
all_backups[copies_to_keep, all_backups.length - copies_to_keep].each { |k| k.delete }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def cleanup_files
|
115
|
+
files_to_cleanup.each { |f| FileUtils.rm_rf(f) }
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
data/s3-backup.gemspec
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{s3-backup}
|
8
|
+
s.version = "0.6.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Ben Koski"]
|
12
|
+
s.date = %q{2010-10-17}
|
13
|
+
s.description = %q{Easy tar-based backups to S3, with backup rotation, cleanup helpers, and pre-/post-backup hooks.}
|
14
|
+
s.email = %q{gems@benkoski.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"lib/s3-backup.rb",
|
27
|
+
"lib/s3-backup/base.rb",
|
28
|
+
"s3-backup.gemspec",
|
29
|
+
"test/base_test.rb",
|
30
|
+
"test/helper.rb"
|
31
|
+
]
|
32
|
+
s.homepage = %q{http://github.com/bkoski/s3-backup}
|
33
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
34
|
+
s.require_paths = ["lib"]
|
35
|
+
s.rubygems_version = %q{1.3.7}
|
36
|
+
s.summary = %q{Easy tar-based backups to S3, with backup rotation, cleanup helpers, and pre-/post-backup hooks.}
|
37
|
+
s.test_files = [
|
38
|
+
"test/base_test.rb",
|
39
|
+
"test/helper.rb"
|
40
|
+
]
|
41
|
+
|
42
|
+
if s.respond_to? :specification_version then
|
43
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
44
|
+
s.specification_version = 3
|
45
|
+
|
46
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
47
|
+
s.add_runtime_dependency(%q<aws_credentials>, [">= 0.6.0"])
|
48
|
+
s.add_runtime_dependency(%q<right_aws>, ["~> 2.0.0"])
|
49
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
50
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
51
|
+
else
|
52
|
+
s.add_dependency(%q<aws_credentials>, [">= 0.6.0"])
|
53
|
+
s.add_dependency(%q<right_aws>, ["~> 2.0.0"])
|
54
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
55
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
56
|
+
end
|
57
|
+
else
|
58
|
+
s.add_dependency(%q<aws_credentials>, [">= 0.6.0"])
|
59
|
+
s.add_dependency(%q<right_aws>, ["~> 2.0.0"])
|
60
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
61
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
data/test/base_test.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class BaseTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@b = S3Backup.new('test')
|
7
|
+
@b.files = ['/tmp/test1.txt']
|
8
|
+
end
|
9
|
+
|
10
|
+
context "callbacks" do
|
11
|
+
setup do
|
12
|
+
stub_io_methods
|
13
|
+
end
|
14
|
+
|
15
|
+
should "call before_backup block before backup is run" do
|
16
|
+
s = sequence('backup')
|
17
|
+
|
18
|
+
before_mock = mock()
|
19
|
+
before_mock.expects(:test_cmd).in_sequence(s)
|
20
|
+
@b.before_backup { before_mock.test_cmd }
|
21
|
+
|
22
|
+
@b.expects(:create_tarball).in_sequence(s)
|
23
|
+
|
24
|
+
@b.run
|
25
|
+
end
|
26
|
+
|
27
|
+
should "call after_backup block after backup is run" do
|
28
|
+
s = sequence('backup')
|
29
|
+
|
30
|
+
@b.expects(:push_to_s3).in_sequence(s)
|
31
|
+
|
32
|
+
after_mock = mock()
|
33
|
+
after_mock.expects(:test_cmd).in_sequence(s)
|
34
|
+
@b.after_backup { after_mock.test_cmd }
|
35
|
+
|
36
|
+
@b.run
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "cleanup" do
|
41
|
+
setup do
|
42
|
+
stub_io_methods
|
43
|
+
end
|
44
|
+
|
45
|
+
should "remove everything specified in files_to_delete on success" do
|
46
|
+
@b.files_to_cleanup << '/tmp/testtmpfile.txt'
|
47
|
+
FileUtils.expects(:rm_rf).with('/tmp/testtmpfile.txt')
|
48
|
+
@b.run
|
49
|
+
end
|
50
|
+
|
51
|
+
should "remove everything specified in files_to_delete even if an exception was raised" do
|
52
|
+
@b.files_to_cleanup << '/tmp/testtmpfile.txt'
|
53
|
+
FileUtils.expects(:rm_rf).with('/tmp/testtmpfile.txt')
|
54
|
+
@b.stubs(:create_tarball).raises('test error')
|
55
|
+
@b.run rescue nil
|
56
|
+
end
|
57
|
+
|
58
|
+
should "remove local tarfile" do
|
59
|
+
FileUtils.expects(:rm_rf).with(@b.tarball_name)
|
60
|
+
@b.run
|
61
|
+
end
|
62
|
+
|
63
|
+
should "remove local includes file" do
|
64
|
+
FileUtils.expects(:rm_rf).with(@b.include_file_name)
|
65
|
+
@b.run
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "tar file" do
|
70
|
+
|
71
|
+
setup do
|
72
|
+
stub_io_methods(:run_tar, :rotate_remote_backups)
|
73
|
+
|
74
|
+
@tarball_mock = mock()
|
75
|
+
File.stubs(:open).with(@b.tarball_name).returns(@tarball_mock)
|
76
|
+
|
77
|
+
@include_file_mock = mock()
|
78
|
+
@include_file_mock.stubs(:write)
|
79
|
+
File.stubs(:open).with(@b.include_file_name, 'w').yields(@include_file_mock)
|
80
|
+
end
|
81
|
+
|
82
|
+
context "naming" do
|
83
|
+
should "default to backup" do
|
84
|
+
@b.expects(:run_tar).with(regexp_matches(/backup-\d+.tar.gz$/))
|
85
|
+
@b.run
|
86
|
+
end
|
87
|
+
|
88
|
+
should "include backup_name if specified" do
|
89
|
+
@b = S3Backup.new('test')
|
90
|
+
|
91
|
+
@b.files = ['/tmp/test1.txt']
|
92
|
+
@b.backup_name = 'app_data'
|
93
|
+
|
94
|
+
File.stubs(:open).with(@b.include_file_name, 'w').yields(@include_file_mock)
|
95
|
+
File.stubs(:open).with(anything).returns(mock())
|
96
|
+
|
97
|
+
@b.expects(:run_tar).with(regexp_matches(/app_data-\d+.tar.gz$/))
|
98
|
+
@b.run
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
should "include all files named in files" do
|
103
|
+
@include_file_mock.expects(:write).with(@b.files.join("\n"))
|
104
|
+
@b.expects(:run_tar).with(regexp_matches(/-I #{@b.include_file_name}/))
|
105
|
+
@b.run
|
106
|
+
end
|
107
|
+
|
108
|
+
should "exclude tar_excludes" do
|
109
|
+
@b.tar_excludes = ['*.log','*.txt']
|
110
|
+
@b.expects(:run_tar).with(regexp_matches(/--exclude \*.log --exclude \*.txt/))
|
111
|
+
@b.run
|
112
|
+
end
|
113
|
+
|
114
|
+
should "be run with --perserve" do
|
115
|
+
@b.expects(:run_tar).with(regexp_matches(/--preserve /))
|
116
|
+
@b.run
|
117
|
+
end
|
118
|
+
|
119
|
+
should "be pushed to s3" do
|
120
|
+
@s3_bucket.expects(:put).with(File.basename(@b.tarball_name), @tarball_mock)
|
121
|
+
@b.run
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context "rotation" do
|
126
|
+
setup do
|
127
|
+
stub_io_methods(:run_tar, :write_include_file, :push_to_s3)
|
128
|
+
end
|
129
|
+
|
130
|
+
should "not remove anything if total count is < copies_to_keep" do
|
131
|
+
mock_keys = [mock(),mock()]
|
132
|
+
mock_keys.each_with_index do |k,i|
|
133
|
+
k.stubs(:name).returns(i.to_s)
|
134
|
+
k.expects(:delete).never()
|
135
|
+
end
|
136
|
+
|
137
|
+
@s3_bucket.stubs(:keys).with(:prefix => @b.backup_name).returns(mock_keys)
|
138
|
+
@b.run
|
139
|
+
end
|
140
|
+
|
141
|
+
should "remove oldest files if total count is > copies_to_keep" do
|
142
|
+
mock_keys = [mock(:name => 'file-299'),mock(:name => 'file-300'),mock(:name => 'file-301'),mock(:name => 'file-302'),mock(:name => 'file-303')]
|
143
|
+
mock_keys[0,@b.copies_to_keep].each { |k| k.expects(:delete).never() }
|
144
|
+
|
145
|
+
extra_keys = [mock(:name => 'file-100'),mock(:name => 'file-102')]
|
146
|
+
extra_keys.each { |k| k.expects(:delete) }
|
147
|
+
mock_keys += extra_keys
|
148
|
+
|
149
|
+
@s3_bucket.stubs(:keys).with(:prefix => @b.backup_name).returns(mock_keys)
|
150
|
+
@b.run
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
def stub_io_methods *methods
|
156
|
+
methods = [:run_tar, :write_include_file, :push_to_s3, :rotate_remote_backups] if methods.empty?
|
157
|
+
methods.each { |io| S3Backup.any_instance.stubs(io) }
|
158
|
+
@s3_bucket = mock()
|
159
|
+
@s3_bucket.stubs(:put)
|
160
|
+
S3Backup.any_instance.stubs(:s3_bucket).returns(@s3_bucket)
|
161
|
+
FileUtils.stubs(:rm_rf)
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
require 'mocha'
|
5
|
+
require 'ruby-debug'
|
6
|
+
|
7
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
8
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
9
|
+
require 's3-backup'
|
10
|
+
|
11
|
+
class S3Backup
|
12
|
+
# These methods become relevant in tests
|
13
|
+
public :tarball_name, :include_file_name
|
14
|
+
end
|
15
|
+
|
16
|
+
class Test::Unit::TestCase
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: s3-backup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 7
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 6
|
9
|
+
- 0
|
10
|
+
version: 0.6.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Ben Koski
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-10-17 00:00:00 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: aws_credentials
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 7
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 6
|
33
|
+
- 0
|
34
|
+
version: 0.6.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: right_aws
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 15
|
46
|
+
segments:
|
47
|
+
- 2
|
48
|
+
- 0
|
49
|
+
- 0
|
50
|
+
version: 2.0.0
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: shoulda
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 3
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
version: "0"
|
65
|
+
type: :development
|
66
|
+
version_requirements: *id003
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: mocha
|
69
|
+
prerelease: false
|
70
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
hash: 3
|
76
|
+
segments:
|
77
|
+
- 0
|
78
|
+
version: "0"
|
79
|
+
type: :development
|
80
|
+
version_requirements: *id004
|
81
|
+
description: Easy tar-based backups to S3, with backup rotation, cleanup helpers, and pre-/post-backup hooks.
|
82
|
+
email: gems@benkoski.com
|
83
|
+
executables: []
|
84
|
+
|
85
|
+
extensions: []
|
86
|
+
|
87
|
+
extra_rdoc_files:
|
88
|
+
- LICENSE
|
89
|
+
- README.rdoc
|
90
|
+
files:
|
91
|
+
- .document
|
92
|
+
- .gitignore
|
93
|
+
- LICENSE
|
94
|
+
- README.rdoc
|
95
|
+
- Rakefile
|
96
|
+
- VERSION
|
97
|
+
- lib/s3-backup.rb
|
98
|
+
- lib/s3-backup/base.rb
|
99
|
+
- s3-backup.gemspec
|
100
|
+
- test/base_test.rb
|
101
|
+
- test/helper.rb
|
102
|
+
has_rdoc: true
|
103
|
+
homepage: http://github.com/bkoski/s3-backup
|
104
|
+
licenses: []
|
105
|
+
|
106
|
+
post_install_message:
|
107
|
+
rdoc_options:
|
108
|
+
- --charset=UTF-8
|
109
|
+
require_paths:
|
110
|
+
- lib
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
none: false
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
hash: 3
|
117
|
+
segments:
|
118
|
+
- 0
|
119
|
+
version: "0"
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
hash: 3
|
126
|
+
segments:
|
127
|
+
- 0
|
128
|
+
version: "0"
|
129
|
+
requirements: []
|
130
|
+
|
131
|
+
rubyforge_project:
|
132
|
+
rubygems_version: 1.3.7
|
133
|
+
signing_key:
|
134
|
+
specification_version: 3
|
135
|
+
summary: Easy tar-based backups to S3, with backup rotation, cleanup helpers, and pre-/post-backup hooks.
|
136
|
+
test_files:
|
137
|
+
- test/base_test.rb
|
138
|
+
- test/helper.rb
|