sweety_backy 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.rvmrc_example +1 -0
- data/Gemfile +4 -0
- data/README.md +121 -0
- data/Rakefile +31 -0
- data/bin/sweety_backy +28 -0
- data/lib/sweety_backy.rb +9 -0
- data/lib/sweety_backy/commander.rb +82 -0
- data/lib/sweety_backy/opts_reader.rb +47 -0
- data/lib/sweety_backy/runner.rb +88 -0
- data/lib/sweety_backy/s3.rb +56 -0
- data/lib/sweety_backy/utils.rb +42 -0
- data/lib/sweety_backy/version.rb +3 -0
- data/server1/backups/databases/test.20100104.daily.sql.tar.gz +0 -0
- data/server1/backups/files/Users.fguillen.Develop.Ruby.SweetyBacky.test.fixtures.path.20100104.daily.tar.gz +0 -0
- data/sweety_backy.gemspec +26 -0
- data/test/commander_test.rb +134 -0
- data/test/fixtures/config_s3.yml +13 -0
- data/test/fixtures/file.txt +1 -0
- data/test/fixtures/path/a/file2.txt +1 -0
- data/test/fixtures/path/b/file3.txt +1 -0
- data/test/fixtures/path/file1.txt +1 -0
- data/test/fixtures/s3.passwd +2 -0
- data/test/opts_reader_test.rb +25 -0
- data/test/runner_test.rb +102 -0
- data/test/s3/commander_s3_test.rb +100 -0
- data/test/s3/runner_s3_test.rb +59 -0
- data/test/s3/s3_test.rb +61 -0
- data/test/test_helper.rb +9 -0
- data/test/utils_test.rb +24 -0
- metadata +169 -0
data/.rvmrc_example
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm 1.8.7@sweety_backy
|
data/Gemfile
ADDED
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"
|
data/lib/sweety_backy.rb
ADDED
@@ -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
|
Binary file
|
Binary file
|
@@ -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,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
|
data/test/runner_test.rb
ADDED
@@ -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
|
+
|
data/test/s3/s3_test.rb
ADDED
@@ -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
|
data/test/test_helper.rb
ADDED
data/test/utils_test.rb
ADDED
@@ -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
|