sweety_backy 0.0.2

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/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .rvmrc
2
+ *.gem
3
+ .bundle
4
+ Gemfile.lock
5
+ pkg/*
data/.rvmrc_example ADDED
@@ -0,0 +1 @@
1
+ rvm 1.8.7@sweety_backy
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in sweety_backy.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # Sweety Backy
2
+
3
+ Simple mechanism to **configure and execute backups** of folders and MySQL DBs and store them in **local folder** or **S3 bucket**.
4
+
5
+ ## State
6
+
7
+ This is a **really beta** version which is working in my servers actually without problems but you have to use it under your own risk.
8
+
9
+ ## Other possibilities
10
+
11
+ Please take a look of other **Ruby backups gems**:
12
+
13
+ * http://ruby-toolbox.com/categories/backups.html
14
+
15
+ ## How install
16
+
17
+ gem install 'sweety_backy'
18
+
19
+ ## How to use it
20
+
21
+ sweety_backy <config_file>
22
+
23
+ ### Config file
24
+
25
+ It is a _yaml_ file with all this attributes
26
+
27
+ paths: <array of folder paths>
28
+ databases: <array of database names>
29
+ yearly: <quantity of yearly backups>
30
+ monthly: <quantity of monthly backups>
31
+ weekly: <quantity of weekly backups>
32
+ daily: <quantity of daily backups>
33
+ database_user: <database user with read privileges of all datases>
34
+ database_pass: <database user password>
35
+ storage_system: { 's3' | 'local' }
36
+ local_opts: (only if the storage_system is 'local')
37
+ path: <absoulte path to the root folder of the backups>
38
+ s3_opts: (only if the storage_system is 's3')
39
+ bucket: <bucket name>
40
+ path: <bucket path where the backups will be stored><
41
+ passwd_file: <path to the S3 credentials>
42
+
43
+ ### S3 credentials file
44
+
45
+ It is a _yaml_ file with two keys with the S3 credentials:
46
+
47
+ access_key_id: "XXX"
48
+ secret_access_key: "YYY"
49
+
50
+ ### Example
51
+
52
+ #### S3 config example
53
+
54
+ # ~/.s3.passwd
55
+ access_key_id: "XXX"
56
+ secret_access_key: "YYY"
57
+
58
+
59
+ #### SweetyBacky config example
60
+
61
+ # ~/.sweety_backy.conf
62
+ paths: [ "/Users/fguillen/Develop/Brico", "/Users/fguillen/Develop/Arduino" ]
63
+ databases: [ "test", "mysql" ]
64
+ yearly: 1
65
+ monthly: 2
66
+ weekly: 3
67
+ daily: 4
68
+ database_user: 'root'
69
+ database_pass: ''
70
+ storage_system: 's3'
71
+ s3_opts:
72
+ bucket: 'sweety_backy'
73
+ path: 'fguillen'
74
+ passwd_file: '~/.s3.passwd'
75
+
76
+ #### Execute
77
+
78
+ sweety_backy ~/.sweety_backy.conf
79
+
80
+ #### Result
81
+
82
+ This will generate a bunch of backups in the _sweety_backy_ bucket like these ones:
83
+
84
+ https://s3.amazonaws.com/sweety_backy/fguillen/files/Users.fguillen.Develop.Arduino.20110626.weekly.tar.gz
85
+ https://s3.amazonaws.com/sweety_backy/fguillen/files/Users.fguillen.Develop.Arduino.20110703.weekly.tar.gz
86
+ https://s3.amazonaws.com/sweety_backy/fguillen/files/Users.fguillen.Develop.Arduino.20110704.daily.tar.gz
87
+ https://s3.amazonaws.com/sweety_backy/fguillen/files/Users.fguillen.Develop.Arduino.20110705.daily.tar.gz
88
+ https://s3.amazonaws.com/sweety_backy/fguillen/files/Users.fguillen.Develop.Arduino.20110706.daily.tar.gz
89
+ https://s3.amazonaws.com/sweety_backy/fguillen/files/Users.fguillen.Develop.Arduino.20110707.daily.tar.gz
90
+
91
+ https://s3.amazonaws.com/sweety_backy/fguillen/files/Users.fguillen.Develop.Brico.20110626.weekly.tar.gz
92
+ https://s3.amazonaws.com/sweety_backy/fguillen/files/Users.fguillen.Develop.Brico.20110703.weekly.tar.gz
93
+ https://s3.amazonaws.com/sweety_backy/fguillen/files/Users.fguillen.Develop.Brico.20110704.daily.tar.gz
94
+ https://s3.amazonaws.com/sweety_backy/fguillen/files/Users.fguillen.Develop.Brico.20110705.daily.tar.gz
95
+ https://s3.amazonaws.com/sweety_backy/fguillen/files/Users.fguillen.Develop.Brico.20110706.daily.tar.gz
96
+ https://s3.amazonaws.com/sweety_backy/fguillen/files/Users.fguillen.Develop.Brico.20110707.daily.tar.gz
97
+
98
+ https://s3.amazonaws.com/sweety_backy/fguillen/databases/test.20110626.weekly.sql.tar.gz
99
+ https://s3.amazonaws.com/sweety_backy/fguillen/databases/test.20110703.weekly.sql.tar.gz
100
+ https://s3.amazonaws.com/sweety_backy/fguillen/databases/test.20110704.daily.sql.tar.gz
101
+ https://s3.amazonaws.com/sweety_backy/fguillen/databases/test.20110705.daily.sql.tar.gz
102
+ https://s3.amazonaws.com/sweety_backy/fguillen/databases/test.20110706.daily.sql.tar.gz
103
+ https://s3.amazonaws.com/sweety_backy/fguillen/databases/test.20110707.daily.sql.tar.gz
104
+
105
+ https://s3.amazonaws.com/sweety_backy/fguillen/databases/mysql.20110626.weekly.sql.tar.gz
106
+ https://s3.amazonaws.com/sweety_backy/fguillen/databases/mysql.20110703.weekly.sql.tar.gz
107
+ https://s3.amazonaws.com/sweety_backy/fguillen/databases/mysql.20110704.daily.sql.tar.gz
108
+ https://s3.amazonaws.com/sweety_backy/fguillen/databases/mysql.20110705.daily.sql.tar.gz
109
+ https://s3.amazonaws.com/sweety_backy/fguillen/databases/mysql.20110706.daily.sql.tar.gz
110
+ https://s3.amazonaws.com/sweety_backy/fguillen/databases/mysql.20110707.daily.sql.tar.gz
111
+
112
+ ... and so on.
113
+
114
+ ### Cron execution example
115
+
116
+ # every day at 02:00 am
117
+ 00 02 * * * sweety_backy /home/fguillen/.sweety_backy.conf >> /var/log/sweety_backy.log 2>&1
118
+
119
+ ## License
120
+
121
+ MIT License. (c) 2011 Fernando Guillen (http://fernandoguillen.info).
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ # require 'bundler/gem_tasks'
2
+
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+ require 'bundler'
6
+
7
+ Bundler::GemHelper.install_tasks
8
+
9
+ task :default => :test
10
+
11
+ Rake::TestTask.new do |t|
12
+ t.libs << '.'
13
+ t.test_files = FileList['test/*_test.rb']
14
+ t.verbose = true
15
+ end
16
+
17
+ namespace :test do
18
+
19
+ desc "run s3 test in real"
20
+ task :s3 do
21
+ test_task =
22
+ Rake::TestTask.new("s3_tests") do |t|
23
+ t.libs << '.'
24
+ t.test_files = FileList['test/s3/*_test.rb']
25
+ t.verbose = true
26
+ end
27
+
28
+ task("s3_tests").execute
29
+ end
30
+
31
+ end
data/bin/sweety_backy ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Use:
4
+ # sweety_backy /path/to/sweety_backy.conf
5
+
6
+ begin
7
+ require 'sweety_backy'
8
+ rescue LoadError
9
+ require 'rubygems'
10
+ require 'sweety_backy'
11
+ end
12
+
13
+ require 'benchmark'
14
+
15
+ if( ARGV[0].nil? )
16
+ SweetyBacky::Utils.log "use: $ sweety_backy <config_file_path>"
17
+ exit 1
18
+ end
19
+
20
+ lapsus_time =
21
+ Benchmark.realtime do
22
+ SweetyBacky::Utils.log "--------------------"
23
+ SweetyBacky::Utils.log "Starting SweetyBacky"
24
+ sb = SweetyBacky::Runner.new( ARGV[0] )
25
+ sb.run
26
+ end
27
+
28
+ SweetyBacky::Utils.log "SweetyBacky on #{lapsus_time} seconds"
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require "s3"
3
+
4
+ require "#{File.dirname(__FILE__)}/sweety_backy/version"
5
+ require "#{File.dirname(__FILE__)}/sweety_backy/runner"
6
+ require "#{File.dirname(__FILE__)}/sweety_backy/utils"
7
+ require "#{File.dirname(__FILE__)}/sweety_backy/commander"
8
+ require "#{File.dirname(__FILE__)}/sweety_backy/s3"
9
+ require "#{File.dirname(__FILE__)}/sweety_backy/opts_reader"
@@ -0,0 +1,82 @@
1
+ module SweetyBacky
2
+ module Commander
3
+ def self.do_files_backup( path, backup_path, opts )
4
+ SweetyBacky::Utils.log "doing files backup of #{path} to #{backup_path}"
5
+
6
+ FileUtils.mkdir_p( File.dirname( backup_path ) )
7
+ SweetyBacky::Utils::command( "tar -cz --directory #{path} --same-permissions --file #{backup_path} ." )
8
+ end
9
+
10
+ def self.do_database_backup( database_name, backup_path, opts )
11
+ SweetyBacky::Utils.log "doing database backup #{database_name} on #{backup_path}"
12
+
13
+ FileUtils.mkdir_p( File.dirname( backup_path ) )
14
+ tmp_sql_file_path = File.join( Dir::tmpdir, "#{File.basename( backup_path, '.tar.gz' )}" )
15
+
16
+ database_pass = opts[:database_pass].empty? ? '' : "-p#{opts[:database_pass]}"
17
+
18
+ SweetyBacky::Utils::command( "mysqldump -u#{opts[:database_user]} #{opts[:database_pass]} #{database_name} > #{tmp_sql_file_path}" )
19
+ SweetyBacky::Utils::command( "tar -cz --same-permissions --file #{backup_path} --directory #{File.dirname(tmp_sql_file_path)} #{File.basename(tmp_sql_file_path)}" )
20
+
21
+ File.delete( tmp_sql_file_path )
22
+ end
23
+
24
+ def self.clear( opts )
25
+ clear_files( opts )
26
+ clear_databases( opts )
27
+ end
28
+
29
+ def self.clear_files( opts )
30
+ SweetyBacky::Utils.log "cleaning files on #{opts[:working_path]}/files/"
31
+
32
+ opts[:paths].each do |path|
33
+ SweetyBacky::Utils.log "cleaning file #{path}"
34
+
35
+ [:yearly, :monthly, :weekly, :daily].each do |period|
36
+ paths_in(
37
+ "#{opts[:working_path]}/files/#{SweetyBacky::Utils.namerize( path )}.*.#{period.to_s}.*",
38
+ opts
39
+ ).sort[0..(-1*(opts[period]+1))].each do |file_path|
40
+ SweetyBacky::Utils.log "removing: #{file_path}"
41
+ remove_path( file_path, opts )
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ def self.clear_databases( opts )
48
+ SweetyBacky::Utils.log "cleaning databases on #{opts[:working_path]}/databases/"
49
+
50
+ opts[:databases].each do |database_name|
51
+ SweetyBacky::Utils.log "cleaning database #{database_name}"
52
+
53
+ [:yearly, :monthly, :weekly, :daily].each do |period|
54
+ paths_in(
55
+ "#{opts[:working_path]}/databases/#{database_name}.*.#{period.to_s}.*",
56
+ opts
57
+ ).sort[0..(-1*(opts[period]+1))].each do |file_path|
58
+ SweetyBacky::Utils.log "removing: #{file_path}"
59
+ remove_path( file_path, opts )
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ def self.paths_in( path, opts )
66
+ if( opts[:storage_system].to_sym == :s3 )
67
+ return SweetyBacky::S3.paths_in( path, opts[:s3_opts] )
68
+ else
69
+ return Dir.glob( path )
70
+ end
71
+ end
72
+
73
+ def self.remove_path( path, opts )
74
+ if( opts[:storage_system].to_sym == :s3 )
75
+ SweetyBacky::S3.delete( path, opts[:s3_opts] )
76
+ else
77
+ File.delete( path )
78
+ end
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,47 @@
1
+ module SweetyBacky
2
+ module OptsReader
3
+ def self.read_opts( conf_path )
4
+ SweetyBacky::Utils::log "conf_path: #{conf_path}"
5
+
6
+ opts = YAML.load( File.read( conf_path ) )
7
+ new_opts = {}
8
+
9
+ # symbolize keys
10
+ opts.keys.each do |key|
11
+
12
+ if( opts[key].is_a? Hash )
13
+ new_opts[key.to_sym] = {}
14
+ opts[key].keys.each do |key2|
15
+ new_opts[key.to_sym][key2.to_sym] = opts[key][key2]
16
+ end
17
+ else
18
+ new_opts[key.to_sym] = opts[key]
19
+ end
20
+
21
+ end
22
+
23
+ log_configuration( new_opts )
24
+
25
+ # TODO: test all options are ok
26
+
27
+ return new_opts
28
+ end
29
+
30
+ def self.log_configuration( opts )
31
+ SweetyBacky::Utils::log "configuration:"
32
+ SweetyBacky::Utils::log "------------"
33
+ opts.each_pair do |key, value|
34
+ if( value.is_a? Array )
35
+ SweetyBacky::Utils::log "#{key}: #{value.join(' | ')}"
36
+ elsif( value.is_a? Hash )
37
+ value.each_pair do |key2, value2|
38
+ SweetyBacky::Utils::log "#{key} => #{key2}: #{value2}"
39
+ end
40
+ else
41
+ SweetyBacky::Utils::log "#{key}: #{value}"
42
+ end
43
+ end
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,88 @@
1
+ require 'rubygems'
2
+ require 'fileutils'
3
+ require 'tmpdir'
4
+ require 'yaml'
5
+ require File.dirname(__FILE__) + "/utils.rb"
6
+
7
+ module SweetyBacky
8
+ class Runner
9
+ attr_reader :opts
10
+
11
+ def initialize( path = nil )
12
+ if( !path.nil? )
13
+ config( SweetyBacky::OptsReader.read_opts( path ) )
14
+ end
15
+ end
16
+
17
+ def config( opts )
18
+ @opts = {
19
+ :paths => [],
20
+ :databases => [],
21
+ :yearly => 1,
22
+ :monthly => 1,
23
+ :weekly => 2,
24
+ :daily => 4,
25
+ :storage_system => :local
26
+ }.merge( opts )
27
+
28
+ if( @opts[:storage_system].to_sym == :s3 )
29
+ @opts[:working_path] = File.join( Dir::tmpdir, "sweety_backy_#{Time.now.to_i}" )
30
+ else
31
+ @opts[:working_path] = @opts[:local_opts][:path]
32
+ end
33
+ end
34
+
35
+ def do_backup
36
+ do_files_backup
37
+ do_databases_backup
38
+ end
39
+
40
+ def do_files_backup
41
+ @opts[:paths].each do |path|
42
+ backup_path = "#{@opts[:working_path]}/files/#{SweetyBacky::Utils.namerize( path )}.#{Date.today.strftime('%Y%m%d')}.#{SweetyBacky::Utils.period}.tar.gz"
43
+ SweetyBacky::Commander.do_files_backup( path, backup_path, @opts )
44
+
45
+ if( @opts[:storage_system].to_sym == :s3 )
46
+ SweetyBacky::S3.upload(
47
+ backup_path,
48
+ "#{@opts[:s3_opts][:path]}/files/#{File.basename( backup_path )}",
49
+ @opts[:s3_opts]
50
+ )
51
+
52
+ FileUtils.rm_rf backup_path
53
+ end
54
+ end
55
+ end
56
+
57
+ def do_databases_backup
58
+ @opts[:databases].each do |database_name|
59
+ backup_path = "#{@opts[:working_path]}/databases/#{database_name}.#{Date.today.strftime('%Y%m%d')}.#{SweetyBacky::Utils.period}.sql.tar.gz"
60
+ SweetyBacky::Commander.do_database_backup( database_name, backup_path, @opts)
61
+
62
+ if( @opts[:storage_system].to_sym == :s3 )
63
+ SweetyBacky::S3.upload(
64
+ backup_path,
65
+ "#{@opts[:s3_opts][:path]}/databases/#{File.basename( backup_path )}",
66
+ @opts[:s3_opts]
67
+ )
68
+
69
+ FileUtils.rm_rf backup_path
70
+ end
71
+ end
72
+ end
73
+
74
+ def run
75
+ begin
76
+ do_backup
77
+ SweetyBacky::Commander.clear( @opts )
78
+ rescue => e
79
+ SweetyBacky::Utils.log "ERROR: #{e}"
80
+ SweetyBacky::Utils.log "BACKTRACE: #{e.backtrace.join("\n")}"
81
+ SweetyBacky::Utils.log "I should send and email at this moment"
82
+ end
83
+ end
84
+
85
+
86
+ end
87
+ end
88
+
@@ -0,0 +1,56 @@
1
+ module SweetyBacky
2
+ class S3
3
+ def self.upload( path, backup_path, opts )
4
+ SweetyBacky::Utils.log( "S3 uploading: #{path} to #{opts[:bucket]}/#{backup_path}" )
5
+
6
+ s3 = ::S3::Service.new( read_s3_password( opts[:passwd_file] ) )
7
+ bucket = s3.bucket( opts[:bucket] )
8
+
9
+ if !bucket.exists?
10
+ bucket = s3.buckets.build( opts[:bucket] )
11
+ bucket.save
12
+ end
13
+
14
+ object = bucket.objects.build( backup_path )
15
+ object.content = File.open( path )
16
+ object.save
17
+ end
18
+
19
+ def self.object( path, opts )
20
+ s3 = ::S3::Service.new( read_s3_password( opts[:passwd_file] ) )
21
+ bucket = s3.buckets.find( opts[:bucket] )
22
+ object = bucket.objects.find( path )
23
+
24
+ return object
25
+ end
26
+
27
+ def self.paths_in( path, opts )
28
+ s3 = ::S3::Service.new( read_s3_password( opts[:passwd_file] ) )
29
+ bucket = s3.buckets.find( opts[:bucket] )
30
+
31
+ regex = Regexp.escape( path ).gsub('\*', '.*').gsub('\?', '.')
32
+
33
+ objects = bucket.objects.select { |e| e.key =~ /#{regex}/ }
34
+ paths = objects.map(&:key)
35
+
36
+ return paths
37
+ end
38
+
39
+ def self.read_s3_password( path )
40
+ opts = YAML.load( File.read( File.expand_path path ) )
41
+ new_opts = {}
42
+
43
+ # symbolize keys
44
+ opts.keys.each do |key|
45
+ new_opts[key.to_sym] = opts[key]
46
+ end
47
+
48
+ return new_opts
49
+ end
50
+
51
+ def self.delete( path, opts )
52
+ SweetyBacky::S3.object( path, opts ).destroy
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,42 @@
1
+ module SweetyBacky
2
+ class Utils
3
+
4
+ def self.period
5
+ return 'yearly' if( is_last_day_of_year? )
6
+ return 'monthly' if( is_last_day_of_month? )
7
+ return 'weekly' if( is_last_day_of_week? )
8
+ return 'daily'
9
+ end
10
+
11
+ def self.is_last_day_of_year?
12
+ return Date.new(Date.today.year, 12, -1) == Date.today
13
+ end
14
+
15
+ def self.is_last_day_of_month?
16
+ return Date.new(Date.today.year, Date.today.month, -1) == Date.today
17
+ end
18
+
19
+ def self.is_last_day_of_week?
20
+ return Date.today.cwday == 7
21
+ end
22
+
23
+ def self.command( _command )
24
+ Utils.log "command: #{_command}"
25
+
26
+ result = %x( #{_command} 2>&1 )
27
+
28
+ raise "ERROR: on command: '#{_command}', result: '#{result}'" if $?.exitstatus != 0
29
+
30
+ return result
31
+ end
32
+
33
+ def self.log( msg )
34
+ puts "#{Time.now.strftime("%Y-%m-%d %H:%M")}: #{msg}"
35
+ end
36
+
37
+ def self.namerize( path )
38
+ path.gsub('/', '.').gsub(/^\./, '')
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,3 @@
1
+ module SweetyBacky
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "sweety_backy"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "sweety_backy"
7
+ s.version = SweetyBacky::VERSION
8
+ s.authors = ["Fernando Guillen"]
9
+ s.email = ["fguillen.mail@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = "Ruby backup mechanism"
12
+ s.description = "Simple mechanism to configure and execute backups of folders and MySQL DBs and store them in local folder or S3 bucket"
13
+
14
+ s.rubyforge_project = "SweetyBacky"
15
+
16
+ s.add_development_dependency "bundler", ">= 1.0.0.rc.6"
17
+ s.add_development_dependency "mocha"
18
+ s.add_development_dependency "delorean"
19
+
20
+ s.add_dependency "s3"
21
+
22
+ s.files = `git ls-files`.split("\n")
23
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
+ s.require_paths = ["lib"]
26
+ end
@@ -0,0 +1,134 @@
1
+ require "#{File.dirname(__FILE__)}/test_helper"
2
+
3
+ class CommanderTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ SweetyBacky::Utils.stubs(:log)
7
+
8
+ # tmp dir
9
+ @tmp_dir = File.join( Dir::tmpdir, "sweety_backy_#{Time.now.to_i}" )
10
+ Dir.mkdir( @tmp_dir ) unless File.exists?(@tmp_dir)
11
+ end
12
+
13
+ def teardown
14
+ FileUtils.rm_rf @tmp_dir if File.exists?(@tmp_dir)
15
+ end
16
+
17
+ def test_do_files_backup
18
+ SweetyBacky::Commander.do_files_backup(
19
+ "#{FIXTURES_PATH}/path",
20
+ "#{@tmp_dir}/back.tar.gz",
21
+ {}
22
+ )
23
+
24
+ result = %x(tar -tzvf #{@tmp_dir}/back.tar.gz)
25
+
26
+ assert_match( "./", result )
27
+ assert_match( "./file1.txt", result )
28
+ assert_match( "./a/", result )
29
+ assert_match( "./b/file3.txt", result )
30
+ end
31
+
32
+ def test_do_databases_backup
33
+ SweetyBacky::Commander.do_database_backup(
34
+ "test",
35
+ "#{@tmp_dir}/back.sql.tar.gz",
36
+ {
37
+ :database_user => "test",
38
+ :database_pass => ""
39
+ }
40
+ )
41
+
42
+ result = %x(tar -tzvf #{@tmp_dir}/back.sql.tar.gz)
43
+
44
+ assert_match( /\sback.sql$/, result )
45
+ end
46
+
47
+
48
+ def test_clear
49
+ opts = {
50
+ :paths => [ 'name1', 'name2' ],
51
+ :databases => [ 'name1', 'name2' ],
52
+ :yearly => 1,
53
+ :monthly => 2,
54
+ :weekly => 3,
55
+ :daily => 4,
56
+ :storage_system => :local,
57
+ :local_opts => {
58
+ :path => @tmp_dir
59
+ },
60
+ :working_path => @tmp_dir
61
+ }
62
+
63
+ Dir.mkdir( "#{@tmp_dir}/files" ) unless File.exists?( "#{@tmp_dir}/files" )
64
+ Dir.mkdir( "#{@tmp_dir}/databases" ) unless File.exists?( "#{@tmp_dir}/databases" )
65
+
66
+ [
67
+ 'name1.20081231.yearly',
68
+ 'name1.20081232.yearly',
69
+ 'name2.20091231.yearly',
70
+ 'name1.20100131.monthly',
71
+ 'name1.20100228.monthly',
72
+ 'name1.20100331.monthly',
73
+ 'name2.20100430.monthly',
74
+ 'name2.20100531.monthly',
75
+ 'name2.20100630.monthly',
76
+ 'name1.20100704.weekly',
77
+ 'name1.20100711.weekly',
78
+ 'name1.20100718.weekly',
79
+ 'name1.20100725.weekly',
80
+ 'name1.20100720.daily',
81
+ 'name2.20100721.daily',
82
+ 'name2.20100722.daily',
83
+ 'name2.20100723.daily',
84
+ 'name2.20100724.daily',
85
+ 'name2.20100726.daily'
86
+ ].each do |file_part|
87
+ File.open( "#{@tmp_dir}/files/#{file_part}.tar.gz", 'w' ) { |f| f.write 'wadus' }
88
+ File.open( "#{@tmp_dir}/databases/#{file_part}.sql.tar.gz", 'w' ) { |f| f.write 'wadus' }
89
+ end
90
+
91
+ # puts @tmp_dir
92
+ # exit 1
93
+
94
+ SweetyBacky::Commander.clear( opts )
95
+
96
+ files_keeped = Dir.glob( "#{@tmp_dir}/files/*" ).join( "\n" )
97
+ databases_keeped = Dir.glob( "#{@tmp_dir}/databases/*" ).join( "\n" )
98
+
99
+ # files to keep
100
+ [
101
+ 'name1.20081232.yearly',
102
+ 'name2.20091231.yearly',
103
+ 'name1.20100228.monthly',
104
+ 'name1.20100331.monthly',
105
+ 'name2.20100531.monthly',
106
+ 'name2.20100630.monthly',
107
+ 'name1.20100718.weekly',
108
+ 'name1.20100725.weekly',
109
+ 'name1.20100720.daily',
110
+ 'name2.20100722.daily',
111
+ 'name2.20100723.daily',
112
+ 'name2.20100724.daily',
113
+ 'name2.20100726.daily'
114
+ ].each do |file_part|
115
+ assert_match( "#{file_part}.tar.gz", files_keeped )
116
+ assert_match( "#{file_part}.sql.tar.gz", databases_keeped )
117
+ end
118
+
119
+ # files to deleted
120
+ [
121
+ 'name1.20081231.yearly',
122
+ 'name1.20100131.monthly',
123
+ 'name2.20100430.monthly',
124
+ 'name1.20100704.weekly',
125
+ 'name2.20100721.daily'
126
+ ].each do |file_part|
127
+ assert_no_match( /#{file_part}.tar.gz/, files_keeped )
128
+ assert_no_match( /#{file_part}.sql.tar.gz/, databases_keeped )
129
+ end
130
+ end
131
+
132
+
133
+ end
134
+
@@ -0,0 +1,13 @@
1
+ paths: [ "path1", "path2" ]
2
+ databases: [ "db1", "db2" ]
3
+ yearly: 1
4
+ monthly: 2
5
+ weekly: 3
6
+ daily: 4
7
+ database_user: 'database_user'
8
+ database_pass: 'database_pass'
9
+ storage_system: 's3'
10
+ s3_opts:
11
+ bucket: 'bucket_name'
12
+ path: 's3/path/path'
13
+ passwd_file: '/path/.s3.passwd'
@@ -0,0 +1 @@
1
+ file
@@ -0,0 +1 @@
1
+ file2
@@ -0,0 +1 @@
1
+ file3
@@ -0,0 +1 @@
1
+ file1
@@ -0,0 +1,2 @@
1
+ access_key_id: "access_key"
2
+ secret_access_key: "secret_access"
@@ -0,0 +1,25 @@
1
+ require "#{File.dirname(__FILE__)}/test_helper"
2
+
3
+ class OptsReaderTest < Test::Unit::TestCase
4
+ def setup
5
+ SweetyBacky::Utils.stubs(:log)
6
+ end
7
+
8
+ def test_read_opts
9
+ opts = SweetyBacky::OptsReader.read_opts( "#{FIXTURES_PATH}/config_s3.yml" )
10
+
11
+ assert_equal( [ "path1", "path2" ], opts[:paths] )
12
+ assert_equal( [ "db1", "db2" ], opts[:databases] )
13
+ assert_equal( 1, opts[:yearly] )
14
+ assert_equal( 2, opts[:monthly] )
15
+ assert_equal( 3, opts[:weekly] )
16
+ assert_equal( 4, opts[:daily] )
17
+ assert_equal( 'database_user', opts[:database_user] )
18
+ assert_equal( 'database_pass', opts[:database_pass] )
19
+ assert_equal( 'bucket_name', opts[:s3_opts][:bucket] )
20
+ assert_equal( 's3/path/path', opts[:s3_opts][:path] )
21
+ assert_equal( '/path/.s3.passwd', opts[:s3_opts][:passwd_file] )
22
+ end
23
+
24
+
25
+ end
@@ -0,0 +1,102 @@
1
+ require "#{File.dirname(__FILE__)}/test_helper"
2
+
3
+ class RunnerTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ SweetyBacky::Utils.stubs(:log)
7
+
8
+ # tmp dir
9
+ @tmp_dir = File.join( Dir::tmpdir, "sweety_backy_#{Time.now.to_i}" )
10
+ Dir.mkdir( @tmp_dir ) unless File.exists?(@tmp_dir)
11
+
12
+ # runner
13
+ @opts = {
14
+ :paths => [ "#{FIXTURES_PATH}/path" ],
15
+ :databases => [ "test" ],
16
+ :yearly => 1,
17
+ :monthly => 1,
18
+ :weekly => 2,
19
+ :daily => 4,
20
+ :database_user => 'test',
21
+ :database_pass => '',
22
+ :storage_system => :local,
23
+ :local_opts => {
24
+ :path => @tmp_dir
25
+ }
26
+ }
27
+
28
+ @runner = SweetyBacky::Runner.new
29
+ @runner.config( @opts )
30
+ end
31
+
32
+ def teardown
33
+ FileUtils.rm_rf @tmp_dir if File.exists?(@tmp_dir)
34
+ end
35
+
36
+ def test_do_backup_yearly
37
+ Delorean.time_travel_to( '2009-12-31' ) do
38
+ @runner.do_backup
39
+ end
40
+
41
+ assert( File.exists?( "#{@tmp_dir}/files/#{SweetyBacky::Utils.namerize( @opts[:paths][0] )}.20091231.yearly.tar.gz") )
42
+ assert( File.exists?( "#{@tmp_dir}/databases/test.20091231.yearly.sql.tar.gz") )
43
+ end
44
+
45
+ def test_do_backup_monthly
46
+ Delorean.time_travel_to( '2010-01-31' ) do
47
+ @runner.do_backup
48
+ end
49
+
50
+ assert( File.exists?( "#{@tmp_dir}/files/#{SweetyBacky::Utils.namerize( @opts[:paths][0] )}.20100131.monthly.tar.gz") )
51
+ assert( File.exists?( "#{@tmp_dir}/databases/test.20100131.monthly.sql.tar.gz") )
52
+ end
53
+
54
+ def test_do_backup_weekly
55
+ Delorean.time_travel_to( '2010-01-03' ) do
56
+ @runner.do_backup
57
+ end
58
+
59
+ assert( File.exists?( "#{@tmp_dir}/files/#{SweetyBacky::Utils.namerize( @opts[:paths][0] )}.20100103.weekly.tar.gz") )
60
+ assert( File.exists?( "#{@tmp_dir}/databases/test.20100103.weekly.sql.tar.gz") )
61
+ end
62
+
63
+ def test_do_backup_daily
64
+ Delorean.time_travel_to( '2010-01-04' ) do
65
+ @runner.do_backup
66
+ end
67
+
68
+ assert( File.exists?( "#{@tmp_dir}/files/#{SweetyBacky::Utils.namerize( @opts[:paths][0] )}.20100104.daily.tar.gz") )
69
+ assert( File.exists?( "#{@tmp_dir}/databases/test.20100104.daily.sql.tar.gz") )
70
+ end
71
+
72
+ def test_run
73
+ @runner.expects(:do_backup)
74
+ SweetyBacky::Commander.expects(:clear)
75
+
76
+ @runner.run
77
+ end
78
+
79
+ def test_initialize_with_config_file
80
+ SweetyBacky::OptsReader.expects( :read_opts ).with( '/path/config.yml' ).returns(
81
+ {
82
+ :paths => [ 'pepe', 'juan' ],
83
+ :local_opts => {
84
+ :path => '/local/path'
85
+ }
86
+ }
87
+ )
88
+
89
+ runner = SweetyBacky::Runner.new( "/path/config.yml" )
90
+
91
+ assert_equal( [ "pepe", "juan" ], runner.opts[:paths] )
92
+ assert_equal( [], runner.opts[:databases] )
93
+ assert_equal( 1, runner.opts[:yearly] )
94
+ assert_equal( 1, runner.opts[:monthly] )
95
+ assert_equal( 2, runner.opts[:weekly] )
96
+ assert_equal( 4, runner.opts[:daily] )
97
+ assert_equal( :local, runner.opts[:storage_system] )
98
+ assert_equal( '/local/path', runner.opts[:local_opts][:path] )
99
+ end
100
+
101
+ end
102
+
@@ -0,0 +1,100 @@
1
+ require "#{File.dirname(__FILE__)}/../test_helper"
2
+
3
+ class CommanderS3Test < Test::Unit::TestCase
4
+
5
+ def setup
6
+ SweetyBacky::Utils.stubs(:log)
7
+
8
+ @opts = {
9
+ :paths => [ 'name1', 'name2' ],
10
+ :databases => [ 'name1', 'name2' ],
11
+ :yearly => 1,
12
+ :monthly => 2,
13
+ :weekly => 3,
14
+ :daily => 4,
15
+ :storage_system => :s3,
16
+ :s3_opts => {
17
+ :bucket => 'sweety_backy_test',
18
+ :path => 'test/path',
19
+ :passwd_file => '~/.s3.passwd'
20
+ },
21
+ :working_path => @tmp_dir
22
+ }
23
+
24
+ s3 = ::S3::Service.new( SweetyBacky::S3.read_s3_password( @opts[:s3_opts][:passwd_file] ) )
25
+
26
+ @bucket = s3.buckets.build( @opts[:s3_opts][:bucket] )
27
+ @bucket.save
28
+ end
29
+
30
+ def teardown
31
+ @bucket.destroy( true )
32
+ end
33
+
34
+ def test_clear
35
+ [
36
+ 'name1.20081231.yearly',
37
+ 'name1.20081232.yearly',
38
+ 'name2.20091231.yearly',
39
+ 'name1.20100131.monthly',
40
+ 'name1.20100228.monthly',
41
+ 'name1.20100331.monthly',
42
+ 'name2.20100430.monthly',
43
+ 'name2.20100531.monthly',
44
+ 'name2.20100630.monthly',
45
+ 'name1.20100704.weekly',
46
+ 'name1.20100711.weekly',
47
+ 'name1.20100718.weekly',
48
+ 'name1.20100725.weekly',
49
+ 'name1.20100720.daily',
50
+ 'name2.20100721.daily',
51
+ 'name2.20100722.daily',
52
+ 'name2.20100723.daily',
53
+ 'name2.20100724.daily',
54
+ 'name2.20100726.daily'
55
+ ].each do |file_part|
56
+ SweetyBacky::S3.upload( "#{FIXTURES_PATH}/file.txt", "#{@opts[:s3_opts][:path]}/files/#{file_part}.tar.gz", @opts[:s3_opts] )
57
+ SweetyBacky::S3.upload( "#{FIXTURES_PATH}/file.txt", "#{@opts[:s3_opts][:path]}/databases/#{file_part}.sql.tar.gz", @opts[:s3_opts] )
58
+ end
59
+
60
+ SweetyBacky::Commander.clear( @opts )
61
+
62
+ files_keeped = SweetyBacky::S3.paths_in( "#{@opts[:s3_opts][:path]}/files/*", @opts[:s3_opts] ).join( "\n" )
63
+ databases_keeped = SweetyBacky::S3.paths_in( "#{@opts[:s3_opts][:path]}/databases/*", @opts[:s3_opts] ).join( "\n" )
64
+
65
+ # files to keep
66
+ [
67
+ 'name1.20081232.yearly',
68
+ 'name2.20091231.yearly',
69
+ 'name1.20100228.monthly',
70
+ 'name1.20100331.monthly',
71
+ 'name2.20100531.monthly',
72
+ 'name2.20100630.monthly',
73
+ 'name1.20100718.weekly',
74
+ 'name1.20100725.weekly',
75
+ 'name1.20100720.daily',
76
+ 'name2.20100722.daily',
77
+ 'name2.20100723.daily',
78
+ 'name2.20100724.daily',
79
+ 'name2.20100726.daily'
80
+ ].each do |file_part|
81
+ assert_match( "#{file_part}.tar.gz", files_keeped )
82
+ assert_match( "#{file_part}.sql.tar.gz", databases_keeped )
83
+ end
84
+
85
+ # files to deleted
86
+ [
87
+ 'name1.20081231.yearly',
88
+ 'name1.20100131.monthly',
89
+ 'name2.20100430.monthly',
90
+ 'name1.20100704.weekly',
91
+ 'name2.20100721.daily'
92
+ ].each do |file_part|
93
+ assert_no_match( /#{file_part}.tar.gz/, files_keeped )
94
+ assert_no_match( /#{file_part}.sql.tar.gz/, databases_keeped )
95
+ end
96
+ end
97
+
98
+
99
+ end
100
+
@@ -0,0 +1,59 @@
1
+ require "#{File.dirname(__FILE__)}/../test_helper"
2
+
3
+ class RunnerS3Test < Test::Unit::TestCase
4
+
5
+ def setup
6
+ SweetyBacky::Utils.stubs(:log)
7
+
8
+ # runner
9
+ @opts = {
10
+ :paths => [ "#{FIXTURES_PATH}/path" ],
11
+ :databases => [ "test" ],
12
+ :yearly => 1,
13
+ :monthly => 1,
14
+ :weekly => 2,
15
+ :daily => 4,
16
+ :database_user => 'test',
17
+ :database_pass => '',
18
+ :storage_system => :s3,
19
+ :s3_opts => {
20
+ :bucket => 'sweety_backy_test',
21
+ :passwd_file => '~/.s3.passwd',
22
+ :path => 'test/path'
23
+ }
24
+ }
25
+
26
+ @runner = SweetyBacky::Runner.new
27
+ @runner.config( @opts )
28
+
29
+ s3 = ::S3::Service.new( SweetyBacky::S3.read_s3_password( @opts[:s3_opts][:passwd_file] ) )
30
+ @bucket = s3.buckets.build( @opts[:s3_opts][:bucket] )
31
+ @bucket.save
32
+ end
33
+
34
+ def teardown
35
+ @bucket.destroy( true )
36
+ end
37
+
38
+ def test_do_backup_daily
39
+ SweetyBacky::Utils.stubs( :period ).returns( 'daily' )
40
+
41
+ @runner.do_backup
42
+
43
+ assert(
44
+ @bucket.
45
+ object(
46
+ "test/path/files/#{SweetyBacky::Utils.namerize( @opts[:paths][0] )}.#{Date.today.strftime('%Y%m%d')}.daily.tar.gz"
47
+ ).exists?
48
+ )
49
+
50
+ assert(
51
+ @bucket.
52
+ object(
53
+ "test/path/databases/test.#{Date.today.strftime('%Y%m%d')}.daily.sql.tar.gz"
54
+ ).exists?
55
+ )
56
+ end
57
+
58
+ end
59
+
@@ -0,0 +1,61 @@
1
+ require "#{File.dirname(__FILE__)}/../test_helper"
2
+
3
+
4
+ class S3Test < Test::Unit::TestCase
5
+ def setup
6
+ SweetyBacky::Utils.stubs(:log)
7
+
8
+ @opts = {
9
+ :bucket => 'sweety_backy_test',
10
+ :path => 'test_path',
11
+ :passwd_file => '~/.s3.passwd'
12
+ }
13
+
14
+ s3 = ::S3::Service.new( SweetyBacky::S3.read_s3_password( @opts[:passwd_file] ) )
15
+ @bucket = s3.buckets.build( @opts[:bucket] )
16
+ @bucket.save
17
+ end
18
+
19
+ def teardown
20
+ @bucket.destroy( true )
21
+ end
22
+
23
+ def test_upload
24
+ SweetyBacky::S3.upload(
25
+ "#{FIXTURES_PATH}/file.txt",
26
+ "test/path/file.txt",
27
+ @opts
28
+ )
29
+
30
+ assert_equal(
31
+ File.read( "#{FIXTURES_PATH}/file.txt" ),
32
+ SweetyBacky::S3.object( "test/path/file.txt", @opts ).content
33
+ )
34
+ end
35
+
36
+ def test_paths_in
37
+ SweetyBacky::S3.upload( "#{FIXTURES_PATH}/file.txt", "test/path/file1.txt", @opts )
38
+ SweetyBacky::S3.upload( "#{FIXTURES_PATH}/file.txt", "test/path/file2.txt", @opts )
39
+ SweetyBacky::S3.upload( "#{FIXTURES_PATH}/file.txt", "test/path/file3.txt", @opts )
40
+ SweetyBacky::S3.upload( "#{FIXTURES_PATH}/file.txt", "test/path/other_file.txt", @opts )
41
+
42
+ paths = SweetyBacky::S3.paths_in( "test/path/file*.txt", @opts )
43
+
44
+ assert_equal(3, paths.size)
45
+ assert( ( paths.include? "test/path/file1.txt" ) )
46
+ assert( ( paths.include? "test/path/file2.txt" ) )
47
+ assert( ( paths.include? "test/path/file3.txt" ) )
48
+ assert( ( !paths.include? "test/path/other_file.txt" ) )
49
+ end
50
+
51
+ def test_delete
52
+ SweetyBacky::S3.upload( "#{FIXTURES_PATH}/file.txt", "test/path/file1.txt", @opts )
53
+ SweetyBacky::S3.upload( "#{FIXTURES_PATH}/file.txt", "test/path/file2.txt", @opts )
54
+
55
+ SweetyBacky::S3.delete( "test/path/file2.txt", @opts )
56
+
57
+ assert( @bucket.object( "test/path/file1.txt" ).exists? )
58
+ assert( !@bucket.object( "test/path/file2.txt" ).exists? )
59
+ end
60
+
61
+ end
@@ -0,0 +1,9 @@
1
+ require "#{File.dirname(__FILE__)}/../lib/sweety_backy"
2
+
3
+ require 'rubygems'
4
+ require 'test/unit'
5
+ require 'mocha'
6
+ require 'delorean'
7
+
8
+
9
+ FIXTURES_PATH = File.expand_path "#{File.dirname(__FILE__)}/fixtures"
@@ -0,0 +1,24 @@
1
+ require "#{File.dirname(__FILE__)}/test_helper"
2
+
3
+ class UtilsTest < Test::Unit::TestCase
4
+ def setup
5
+ SweetyBacky::Utils.stubs(:log)
6
+ end
7
+
8
+ def test_error_on_command
9
+ assert_raise RuntimeError do
10
+ SweetyBacky::Utils::command( 'command_not_exists' )
11
+ end
12
+ end
13
+
14
+ def test_namerize
15
+ assert_equal(
16
+ 'Users.fguillen.Develop.Ruby.SweetyBacky.test.fixtures.path',
17
+ SweetyBacky::Utils.namerize( '/Users/fguillen/Develop/Ruby/SweetyBacky/test/fixtures/path' )
18
+ )
19
+
20
+ assert_equal( 'path', SweetyBacky::Utils.namerize( '/path' ) )
21
+ assert_equal( 'path', SweetyBacky::Utils.namerize( 'path' ) )
22
+ end
23
+
24
+ end
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sweety_backy
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 2
10
+ version: 0.0.2
11
+ platform: ruby
12
+ authors:
13
+ - Fernando Guillen
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-07-05 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: bundler
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 15424057
30
+ segments:
31
+ - 1
32
+ - 0
33
+ - 0
34
+ - rc
35
+ - 6
36
+ version: 1.0.0.rc.6
37
+ type: :development
38
+ version_requirements: *id001
39
+ - !ruby/object:Gem::Dependency
40
+ name: mocha
41
+ prerelease: false
42
+ requirement: &id002 !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ hash: 3
48
+ segments:
49
+ - 0
50
+ version: "0"
51
+ type: :development
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: delorean
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: s3
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: :runtime
80
+ version_requirements: *id004
81
+ description: Simple mechanism to configure and execute backups of folders and MySQL DBs and store them in local folder or S3 bucket
82
+ email:
83
+ - fguillen.mail@gmail.com
84
+ executables:
85
+ - sweety_backy
86
+ extensions: []
87
+
88
+ extra_rdoc_files: []
89
+
90
+ files:
91
+ - .gitignore
92
+ - .rvmrc_example
93
+ - Gemfile
94
+ - README.md
95
+ - Rakefile
96
+ - bin/sweety_backy
97
+ - lib/sweety_backy.rb
98
+ - lib/sweety_backy/commander.rb
99
+ - lib/sweety_backy/opts_reader.rb
100
+ - lib/sweety_backy/runner.rb
101
+ - lib/sweety_backy/s3.rb
102
+ - lib/sweety_backy/utils.rb
103
+ - lib/sweety_backy/version.rb
104
+ - server1/backups/databases/test.20100104.daily.sql.tar.gz
105
+ - server1/backups/files/Users.fguillen.Develop.Ruby.SweetyBacky.test.fixtures.path.20100104.daily.tar.gz
106
+ - sweety_backy.gemspec
107
+ - test/commander_test.rb
108
+ - test/fixtures/config_s3.yml
109
+ - test/fixtures/file.txt
110
+ - test/fixtures/path/a/file2.txt
111
+ - test/fixtures/path/b/file3.txt
112
+ - test/fixtures/path/file1.txt
113
+ - test/fixtures/s3.passwd
114
+ - test/opts_reader_test.rb
115
+ - test/runner_test.rb
116
+ - test/s3/commander_s3_test.rb
117
+ - test/s3/runner_s3_test.rb
118
+ - test/s3/s3_test.rb
119
+ - test/test_helper.rb
120
+ - test/utils_test.rb
121
+ has_rdoc: true
122
+ homepage: ""
123
+ licenses: []
124
+
125
+ post_install_message:
126
+ rdoc_options: []
127
+
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ hash: 3
136
+ segments:
137
+ - 0
138
+ version: "0"
139
+ required_rubygems_version: !ruby/object:Gem::Requirement
140
+ none: false
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ hash: 3
145
+ segments:
146
+ - 0
147
+ version: "0"
148
+ requirements: []
149
+
150
+ rubyforge_project: SweetyBacky
151
+ rubygems_version: 1.3.7
152
+ signing_key:
153
+ specification_version: 3
154
+ summary: Ruby backup mechanism
155
+ test_files:
156
+ - test/commander_test.rb
157
+ - test/fixtures/config_s3.yml
158
+ - test/fixtures/file.txt
159
+ - test/fixtures/path/a/file2.txt
160
+ - test/fixtures/path/b/file3.txt
161
+ - test/fixtures/path/file1.txt
162
+ - test/fixtures/s3.passwd
163
+ - test/opts_reader_test.rb
164
+ - test/runner_test.rb
165
+ - test/s3/commander_s3_test.rb
166
+ - test/s3/runner_s3_test.rb
167
+ - test/s3/s3_test.rb
168
+ - test/test_helper.rb
169
+ - test/utils_test.rb