yore 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +3 -6
- data/README.rdoc +18 -7
- data/Rakefile +6 -6
- data/lib/yore.orig.rb +1 -1
- data/lib/yore/yore_core.rb +87 -11
- data/{spec → test}/test_job_a.xml +0 -0
- data/test/test_job_b.xml +41 -0
- data/{spec/yore_spec.rb → test/yore_test.rb} +42 -45
- metadata +18 -11
- data/spec/rewind_spec.rb +0 -187
- data/spec/spec.opts +0 -1
- data/spec/spec_helper.rb +0 -10
- data/tasks/rspec.rake +0 -21
data/Manifest.txt
CHANGED
@@ -14,9 +14,6 @@ README.rdoc
|
|
14
14
|
script/console
|
15
15
|
script/destroy
|
16
16
|
script/generate
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
spec/test_job_a.xml
|
21
|
-
spec/yore_spec.rb
|
22
|
-
tasks/rspec.rake
|
17
|
+
test/test_job_a.xml
|
18
|
+
test/test_job_b.xml
|
19
|
+
test/yore_test.rb
|
data/README.rdoc
CHANGED
@@ -1,32 +1,43 @@
|
|
1
1
|
= yore
|
2
2
|
|
3
|
-
*
|
3
|
+
* RubyForge Project : http://rubyforge.org/projects/buzzware
|
4
|
+
* Author : http://www.buzzware.com.au
|
4
5
|
|
5
6
|
== DESCRIPTION:
|
6
7
|
|
7
|
-
|
8
|
+
yore (as in "days of yore") is hands-off scheduled backup utility designed for web servers.
|
9
|
+
It does backups of files and databases (MySQL only at present) to Amazon S3.
|
8
10
|
|
9
11
|
== FEATURES/PROBLEMS:
|
10
12
|
|
11
|
-
*
|
13
|
+
* Compressed, encrypted, single file backups of folders and mysql databases
|
14
|
+
* Designed to be called regularly
|
15
|
+
* Backups are uploaded to Amazon S3
|
16
|
+
* will later remove old files that don't match the configurable scheme for backup history
|
12
17
|
|
13
18
|
== SYNOPSIS:
|
14
19
|
|
15
|
-
|
20
|
+
yore backup yore_config.xml
|
16
21
|
|
17
22
|
== REQUIREMENTS:
|
18
23
|
|
19
|
-
*
|
24
|
+
* s3sync (http://rubyforge.org/projects/s3sync/)
|
25
|
+
* RequirePaths (http://rubyforge.org/projects/blackwhitelogin)
|
26
|
+
* cmdparse (http://cmdparse.rubyforge.org)
|
27
|
+
* shairontoledo-popen4 (http://github.com/shairontoledo/popen4)
|
28
|
+
* tar, bzip2, openssl, mysql
|
29
|
+
* tested on MacOS and Linux
|
20
30
|
|
21
31
|
== INSTALL:
|
22
32
|
|
23
|
-
|
33
|
+
sudo gem sources -a http://gems.github.com
|
34
|
+
sudo gem install yore
|
24
35
|
|
25
36
|
== LICENSE:
|
26
37
|
|
27
38
|
(The MIT License)
|
28
39
|
|
29
|
-
Copyright (c) 2009
|
40
|
+
Copyright (c) 2009 Gary McGhee, Buzzware Solutions
|
30
41
|
|
31
42
|
Permission is hereby granted, free of charge, to any person obtaining
|
32
43
|
a copy of this software and associated documentation files (the
|
data/Rakefile
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake/rdoctask'
|
3
3
|
require 'rake/testtask'
|
4
|
-
require 'spec/rake/spectask'
|
5
4
|
%w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
|
6
5
|
require File.dirname(__FILE__) + '/lib/yore.orig'
|
7
6
|
|
8
7
|
# built following http://newgem.rubyforge.org/
|
9
|
-
# using RSpec option
|
8
|
+
# using RSpec option (later converted to shoulda)
|
10
9
|
# and had to add processor_ids from dr nic :
|
11
10
|
# http://groups.google.com/group/new-gem-generator/browse_thread/thread/648f70da782e607a/928ea0fe8319d886?lnk=raot
|
12
11
|
|
@@ -20,7 +19,8 @@ $hoe = Hoe.new('yore', Yore::VERSION) do |p|
|
|
20
19
|
p.extra_deps = [
|
21
20
|
['RequirePaths','>= 1.0.1'],
|
22
21
|
['cmdparse', '>= 2.0.2'],
|
23
|
-
['s3sync', '>= 1.2.5']
|
22
|
+
['s3sync', '>= 1.2.5'],
|
23
|
+
['shairontoledo-popen4', '>= 0.1.2']
|
24
24
|
]
|
25
25
|
p.extra_dev_deps = [
|
26
26
|
['newgem', ">= #{::Newgem::VERSION}"]
|
@@ -51,7 +51,7 @@ Dir['tasks/**/*.rake'].each { |t| load t }
|
|
51
51
|
# t.test_files = FileList['test/**/*.rb']
|
52
52
|
# end
|
53
53
|
|
54
|
-
Spec::Rake::SpecTask.new do |t|
|
55
|
-
t.spec_files = FileList['spec/**/*.rb']
|
56
|
-
end
|
54
|
+
#Spec::Rake::SpecTask.new do |t|
|
55
|
+
# t.spec_files = FileList['spec/**/*.rb']
|
56
|
+
#end
|
57
57
|
|
data/lib/yore.orig.rb
CHANGED
data/lib/yore/yore_core.rb
CHANGED
@@ -2,6 +2,9 @@ require 'rubygems'
|
|
2
2
|
gem 'RequirePaths'; require 'require_paths'
|
3
3
|
require_paths '.','..'
|
4
4
|
|
5
|
+
gem 'Platform'; require 'platform'
|
6
|
+
gem 'shairontoledo-popen4'; require 'popen4'
|
7
|
+
|
5
8
|
require 'fileutils'
|
6
9
|
require 'net/smtp'
|
7
10
|
|
@@ -13,6 +16,66 @@ require 'ihl_ruby/extend_base_classes'
|
|
13
16
|
THIS_FILE = __FILE__
|
14
17
|
THIS_DIR = File.dirname(THIS_FILE)
|
15
18
|
|
19
|
+
module POpen4
|
20
|
+
|
21
|
+
class ExecuteError < StandardError
|
22
|
+
|
23
|
+
attr_reader :result #,:stderr,:stdout,:exitcode,:pid
|
24
|
+
|
25
|
+
def initialize(aArg)
|
26
|
+
if aArg.is_a? Hash
|
27
|
+
super(aArg[:stdout] || '')
|
28
|
+
@result = aArg
|
29
|
+
# @stderr = aArg[:stderr]
|
30
|
+
# @stdout = aArg[:stdout]
|
31
|
+
# @exitcode = aArg[:exitcode]
|
32
|
+
# @pid = aArg[:pid]
|
33
|
+
else
|
34
|
+
super(aArg)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
# Usage :
|
41
|
+
# result = POpen4::shell('somebinary') do |r| # block gives opportunity to adjust result, and avoid exception raised from non-zero exit codes
|
42
|
+
# if r[:exitcode]==254 # eg. say this binary returns 254 to mean something special but not an error
|
43
|
+
# r[:stdout] = 'some correct output'
|
44
|
+
# r[:stderr] = ''
|
45
|
+
# r[:exitcode] = 0
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# OR
|
50
|
+
#
|
51
|
+
# result = POpen4::shell('somebinary');
|
52
|
+
# puts result[:stdout]
|
53
|
+
def self.shell(aCommand,aWorkingDir=nil,aTimeout=nil)
|
54
|
+
raise ExecuteError.new('aWorkingDir doesnt exist') unless !aWorkingDir || File.exists?(aWorkingDir)
|
55
|
+
orig_wd = Dir.getwd
|
56
|
+
result = {:command => aCommand, :dir => (aWorkingDir || orig_wd)}
|
57
|
+
status = nil
|
58
|
+
begin
|
59
|
+
Dir.chdir(aWorkingDir) if aWorkingDir
|
60
|
+
Timeout.timeout(aTimeout,ExecuteError) do # nil aTimeout will not time out
|
61
|
+
status = POpen4::popen4(aCommand) do |stdout, stderr, stdin, pid|
|
62
|
+
result[:stdout] = stdout.read
|
63
|
+
result[:stderr] = stderr.read
|
64
|
+
result[:pid] = pid
|
65
|
+
end
|
66
|
+
end
|
67
|
+
ensure
|
68
|
+
Dir.chdir(orig_wd)
|
69
|
+
end
|
70
|
+
result[:exitcode] = (status && status.exitstatus) || 1
|
71
|
+
yield(result) if block_given?
|
72
|
+
raise ExecuteError.new(result) if result[:exitcode] != 0
|
73
|
+
return result
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
|
16
79
|
module YoreCore
|
17
80
|
|
18
81
|
class KeepDaily
|
@@ -163,8 +226,8 @@ module YoreCore
|
|
163
226
|
:keep_daily => 14,
|
164
227
|
:keep_weekly => 12,
|
165
228
|
:keep_monthly => 12,
|
166
|
-
:crypto_iv => "3A63775C1E3F291B0925578165EB917E",
|
167
|
-
:crypto_key => "07692FC8656F04AE5518B80D38681E038A3C12050DF6CC97CEEC33D800D5E2FE",
|
229
|
+
:crypto_iv => "3A63775C1E3F291B0925578165EB917E", # apparently a string of up to 32 random hex digits
|
230
|
+
:crypto_key => "07692FC8656F04AE5518B80D38681E038A3C12050DF6CC97CEEC33D800D5E2FE", # apparently a string of up to 64 random hex digits
|
168
231
|
:first_hour => 4,
|
169
232
|
:prefix => 'backup',
|
170
233
|
:log_level => 'DEBUG',
|
@@ -190,7 +253,7 @@ module YoreCore
|
|
190
253
|
attr_reader :basepath
|
191
254
|
|
192
255
|
def initialize(aConfig=nil)
|
193
|
-
DEFAULT_CONFIG[:email_report] =
|
256
|
+
DEFAULT_CONFIG[:email_report] = false # fixes some bug where this was nil
|
194
257
|
configure(aConfig || DEFAULT_CONFIG)
|
195
258
|
end
|
196
259
|
|
@@ -242,10 +305,11 @@ module YoreCore
|
|
242
305
|
#@filelist =
|
243
306
|
end
|
244
307
|
|
245
|
-
def shell(aCommandline)
|
246
|
-
reporter.debug aCommandline
|
247
|
-
|
248
|
-
|
308
|
+
def shell(aCommandline,&aBlock)
|
309
|
+
reporter.debug "To shell: " + aCommandline
|
310
|
+
result = block_given? ? POpen4::shell(aCommandline,nil,nil,&aBlock) : POpen4::shell(aCommandline)
|
311
|
+
reporter.debug "From shell: '#{result[:stdout]}'"
|
312
|
+
return result[:stdout]
|
249
313
|
end
|
250
314
|
|
251
315
|
def get_log
|
@@ -303,8 +367,14 @@ module YoreCore
|
|
303
367
|
shell "openssl enc -d -aes-256-cbc -K #{config[:crypto_key]} -iv #{config[:crypto_iv]} -in #{aFileIn} -out #{aFileOut}"
|
304
368
|
end
|
305
369
|
|
370
|
+
def ensure_bucket(aBucket=nil)
|
371
|
+
aBucket ||= config[:bucket]
|
372
|
+
shell "s3cmd createbucket #{aBucket}"
|
373
|
+
end
|
374
|
+
|
306
375
|
# uploads the given file to the current bucket as its basename
|
307
376
|
def upload(aFile)
|
377
|
+
ensure_bucket()
|
308
378
|
shell "s3cmd put #{config[:bucket]}:#{File.basename(aFile)} #{aFile}"
|
309
379
|
end
|
310
380
|
|
@@ -323,7 +393,7 @@ module YoreCore
|
|
323
393
|
# :first_hour
|
324
394
|
# :
|
325
395
|
def encode_file_name(aTimeNow=Time.now)
|
326
|
-
"#{config[:prefix]}-#{backup_date(aTimeNow).date_numeric}.
|
396
|
+
"#{config[:prefix]}-#{backup_date(aTimeNow).date_numeric}.yor"
|
327
397
|
end
|
328
398
|
|
329
399
|
# return date based on filename
|
@@ -337,6 +407,7 @@ module YoreCore
|
|
337
407
|
#end
|
338
408
|
|
339
409
|
def backup_process(aSourceFiles,aTimeNow=Time.now,aTempDir=nil)
|
410
|
+
#require 'ruby-debug'; debugger
|
340
411
|
aTempDir ||= MiscUtils.make_temp_dir('yore_')
|
341
412
|
temp_file = File.expand_path('backup.tar',aTempDir)
|
342
413
|
collect(aSourceFiles,temp_file)
|
@@ -411,12 +482,14 @@ module YoreCore
|
|
411
482
|
xmlRoot = XmlUtils.get_file_root(job)
|
412
483
|
|
413
484
|
filelist = []
|
485
|
+
sourceFound = false
|
414
486
|
|
415
487
|
REXML::XPath.each(xmlRoot, '/Yore/Sources/Source') do |xmlSource|
|
416
|
-
|
488
|
+
case xmlSource.attributes['Type']
|
417
489
|
when 'File' then
|
418
490
|
REXML::XPath.each(xmlSource, 'IncludePath') do |xmlPath|
|
419
|
-
filelist += MiscUtils::recursive_file_list(
|
491
|
+
filelist += MiscUtils::recursive_file_list(MiscUtils::path_combine(config[:basepath],xmlPath.text))
|
492
|
+
sourceFound = true
|
420
493
|
end
|
421
494
|
when 'MySql' then
|
422
495
|
#<Source Type="MySql" >
|
@@ -431,11 +504,14 @@ module YoreCore
|
|
431
504
|
raise Error.new("Invalid or missing parameter")
|
432
505
|
end
|
433
506
|
db_to_file(args,file)
|
434
|
-
filelist += file
|
507
|
+
filelist += file
|
508
|
+
sourceFound = true
|
435
509
|
end
|
436
510
|
end
|
437
511
|
end
|
438
512
|
|
513
|
+
raise Error.new("Backup source found but file list empty") if sourceFound && filelist.empty?
|
514
|
+
|
439
515
|
filelist.uniq!
|
440
516
|
filelist.sort!
|
441
517
|
|
File without changes
|
data/test/test_job_b.xml
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<Yore>
|
3
|
+
<SimpleItems>
|
4
|
+
<Item Name="keep_daily">14</Item>
|
5
|
+
<Item Name="keep_weekly">12</Item>
|
6
|
+
<Item Name="keep_monthly">12</Item>
|
7
|
+
<Item Name="crypto_iv">56FD29B5CE3F291B0925577435EBEE7E</Item>
|
8
|
+
<Item Name="crypto_key">07692FC3456A345055B45A37560A85390A3565A0436E035E35636A603A5A2FE</Item>
|
9
|
+
<Item Name="bucket">yoretest</Item>
|
10
|
+
<Item Name="first_hour">4</Item>
|
11
|
+
<Item Name="prefix">yoretest</Item>
|
12
|
+
<Item Name="log_level">DEBUG</Item>
|
13
|
+
<Item Name="email_report">false</Item>
|
14
|
+
|
15
|
+
<Item Name="mail_host">mail.authsmtp.com</Item>
|
16
|
+
<Item Name="mail_port">25</Item>
|
17
|
+
<Item Name="mail_helodomain">finwa.org.au</Item>
|
18
|
+
<Item Name="mail_user">ac37391</Item>
|
19
|
+
<Item Name="mail_password">mbju7wwzv</Item>
|
20
|
+
<Item Name="mail_from">noreply@buzzware.com.au</Item>
|
21
|
+
<Item Name="mail_from_alias">FINWA Web Server</Item>
|
22
|
+
<Item Name="mail_to">familyincnet@gmail.com</Item>
|
23
|
+
<Item Name="mail_to_alias">FINWA</Item>
|
24
|
+
<Item Name="mail_auth">login</Item>
|
25
|
+
|
26
|
+
<Item Name="mysqldump">/usr/local/mysql/bin/mysqldump</Item>
|
27
|
+
|
28
|
+
</SimpleItems>
|
29
|
+
<Sources>
|
30
|
+
<Source Type="File">
|
31
|
+
<IncludePath>/tmp/yoretest</IncludePath>
|
32
|
+
<!-- <ExcludePath></ExcludePath> -->
|
33
|
+
</Source>
|
34
|
+
<!--<Source Type="MySql" > -->
|
35
|
+
<!-- <Database Name="joomla_db" Host="localhost" User="root" Password="prot123ection">-->
|
36
|
+
<!-- <ToFile>/Users/gary/temp/test.sql</ToFile> -->
|
37
|
+
<!-- </Database> -->
|
38
|
+
<!--</Source> -->
|
39
|
+
</Sources>
|
40
|
+
</Yore>
|
41
|
+
|
@@ -1,18 +1,10 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require File.dirname(__FILE__) + '/spec_helper.rb'
|
3
|
-
|
4
2
|
gem 'RequirePaths'; require 'require_paths'
|
5
3
|
require_paths '../lib','../../..'
|
6
4
|
|
5
|
+
require 'test/unit'
|
6
|
+
gem 'Shoulda'; require 'shoulda'
|
7
7
|
|
8
|
-
## Time to add your specs!
|
9
|
-
# http://rspec.info/
|
10
|
-
#describe "Place your specs here" do
|
11
|
-
#
|
12
|
-
# it "find this spec in spec directory" do
|
13
|
-
# violated "Be sure to write your specs"
|
14
|
-
# end
|
15
|
-
#end
|
16
8
|
|
17
9
|
require 'ihl_ruby/misc_utils'
|
18
10
|
|
@@ -25,14 +17,15 @@ def ensure_clean_bucket(aName)
|
|
25
17
|
`s3cmd deleteall #{aName}`
|
26
18
|
end
|
27
19
|
|
28
|
-
|
29
|
-
|
20
|
+
class YoreTest < Test::Unit::TestCase
|
21
|
+
|
22
|
+
def setup
|
30
23
|
@yore = YoreCore::Yore.new
|
31
24
|
end
|
32
25
|
|
33
|
-
|
26
|
+
should "collect a list of files into a single file with a .contents file" do
|
34
27
|
# create a temp dir
|
35
|
-
temp_dir = MiscUtils::make_temp_dir('
|
28
|
+
temp_dir = MiscUtils::make_temp_dir('yore_test')
|
36
29
|
# create source and dest subfolders
|
37
30
|
source_dir = File.expand_path('source',temp_dir)
|
38
31
|
dest_dir = File.expand_path('dest',temp_dir)
|
@@ -55,14 +48,14 @@ describe YoreCore::Yore do
|
|
55
48
|
i = 0
|
56
49
|
filelist_out.each_line {|line|
|
57
50
|
next if line==".contents\n"
|
58
|
-
i
|
59
|
-
line.chomp("\n")
|
51
|
+
assert i < filelist.length
|
52
|
+
assert_equal line.chomp("\n"), MiscUtils.path_debase(filelist[i],source_dir) # .bite('/')
|
60
53
|
i += 1
|
61
54
|
}
|
62
55
|
# could also check .contents file against actual contents
|
63
56
|
end
|
64
57
|
|
65
|
-
|
58
|
+
should "encrypt and unencrypt a file with a standard iv but a supplied key" do
|
66
59
|
orig_content = 'abcdef123456'
|
67
60
|
temp_file1 = MiscUtils.make_temp_file(nil,nil,orig_content)
|
68
61
|
temp_file2 = temp_file1+'.enc'
|
@@ -72,23 +65,23 @@ describe YoreCore::Yore do
|
|
72
65
|
@yore.unpack(temp_file2,temp_file3)
|
73
66
|
|
74
67
|
file3_content = MiscUtils.string_from_file(temp_file3)
|
75
|
-
file3_content
|
68
|
+
assert_equal file3_content, orig_content
|
76
69
|
end
|
77
70
|
|
78
|
-
|
71
|
+
should "create a backup filename combining a prefix, date, day and standard extension, and be able to decode the filename back to a date" do
|
79
72
|
@yore.configure({
|
80
|
-
:prefix => '
|
73
|
+
:prefix => 'test',
|
81
74
|
:first_hour => 4
|
82
75
|
})
|
83
76
|
now = Time.local(2009, 1, 1, 0, 0, 0, 0)
|
84
|
-
exp_filename = "
|
77
|
+
exp_filename = "test-20081231.yor"
|
85
78
|
calc_filename = @yore.encode_file_name(now)
|
86
|
-
calc_filename
|
87
|
-
|
79
|
+
assert_equal calc_filename, exp_filename
|
80
|
+
assert_equal Time.local(2008, 12, 31), @yore.decode_file_name(calc_filename)
|
88
81
|
end
|
89
82
|
|
90
|
-
|
91
|
-
bucket = '
|
83
|
+
should "upload and download files intact" do
|
84
|
+
bucket = 'yore_test'
|
92
85
|
ensure_clean_bucket(bucket)
|
93
86
|
@yore.configure({
|
94
87
|
:bucket => bucket
|
@@ -101,19 +94,19 @@ describe YoreCore::Yore do
|
|
101
94
|
File.rename(temp_file, orig_file)
|
102
95
|
puts @yore.download(temp_file)
|
103
96
|
down_file_content = MiscUtils.string_from_file(temp_file)
|
104
|
-
|
97
|
+
assert_equal content, down_file_content
|
105
98
|
end
|
106
99
|
|
107
|
-
|
100
|
+
should "backup and restore multiple directories of file" do
|
108
101
|
# create a temp dir
|
109
|
-
temp_dir = MiscUtils::make_temp_dir('
|
102
|
+
temp_dir = MiscUtils::make_temp_dir('yore_test')
|
110
103
|
# create source and dest subfolders
|
111
104
|
source_dir = File.expand_path('source',temp_dir)
|
112
105
|
source1 = File.join(source_dir,'source1')
|
113
106
|
source2 = File.join(source_dir,'source2')
|
114
107
|
dest_dir = File.expand_path('dest',temp_dir)
|
115
|
-
bucket = '
|
116
|
-
|
108
|
+
bucket = 'yore_test'
|
109
|
+
|
117
110
|
FileUtils.mkdir_p([source1,source2,dest_dir])
|
118
111
|
# create some dirs and files in source
|
119
112
|
['a','a/1','b','c'].each {|p| FileUtils.mkdir_p(File.expand_path(p,source1))}
|
@@ -137,7 +130,7 @@ job_template = <<EOF
|
|
137
130
|
<Item Name="bucket">${BUCKET}</Item>
|
138
131
|
</SimpleItems>
|
139
132
|
<Sources>
|
140
|
-
<Source>
|
133
|
+
<Source Type="File">
|
141
134
|
<IncludePath>${SOURCE1}</IncludePath>
|
142
135
|
<IncludePath>${SOURCE2}</IncludePath>
|
143
136
|
</Source>
|
@@ -158,8 +151,13 @@ EOF
|
|
158
151
|
})
|
159
152
|
|
160
153
|
# call yore script with ruby from the command line, then download result and check contents
|
161
|
-
|
162
|
-
|
154
|
+
begin
|
155
|
+
result = @yore.shell("ruby #{File.expand_path('../../bin/yore',THIS_DIR)} backup #{job_file}")
|
156
|
+
rescue ::StandardError => e
|
157
|
+
flunk e.inspect
|
158
|
+
end
|
159
|
+
|
160
|
+
puts result
|
163
161
|
|
164
162
|
retrieved_fname = File.expand_path(@yore.encode_file_name(), dest_dir)
|
165
163
|
collection_fname = MiscUtils::temp_file
|
@@ -171,9 +169,9 @@ EOF
|
|
171
169
|
|
172
170
|
# check contains filelist files ignoring .contents
|
173
171
|
cmd = "tar --list --file=#{collection_fname}"
|
174
|
-
filelist_out =
|
172
|
+
filelist_out = @yore.shell(cmd)
|
175
173
|
filelist_out = filelist_out.split("\n").sort
|
176
|
-
|
174
|
+
assert_equal filelist, filelist_out
|
177
175
|
# i = 0
|
178
176
|
# filelist_out.each {|line|
|
179
177
|
# next if line==".contents\n"
|
@@ -184,16 +182,15 @@ EOF
|
|
184
182
|
# could also check .contents file against actual contents
|
185
183
|
end
|
186
184
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
185
|
+
should "handle test_file_b" do
|
186
|
+
aController = YoreCore::Yore.new # main program object
|
187
|
+
job = File.expand_path('../../test/test_job_b.xml',THIS_DIR)
|
188
|
+
xmlRoot = XmlUtils.get_file_root(job)
|
189
|
+
aController.configure(xmlRoot,nil,{:basepath => File.dirname(File.expand_path(job))})
|
190
|
+
aController.backup(job)
|
191
|
+
end
|
195
192
|
|
196
|
-
|
193
|
+
should "provide configurable criteria for keeping old files"
|
197
194
|
|
198
|
-
|
195
|
+
should "clean a folder full of files, removing files that don't match configurable criteria for keeping old files"
|
199
196
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yore
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gary McGhee
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-02-04 00:00:00 +09:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -42,6 +42,16 @@ dependencies:
|
|
42
42
|
- !ruby/object:Gem::Version
|
43
43
|
version: 1.2.5
|
44
44
|
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: shairontoledo-popen4
|
47
|
+
type: :runtime
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.1.2
|
54
|
+
version:
|
45
55
|
- !ruby/object:Gem::Dependency
|
46
56
|
name: newgem
|
47
57
|
type: :development
|
@@ -62,7 +72,7 @@ dependencies:
|
|
62
72
|
- !ruby/object:Gem::Version
|
63
73
|
version: 1.8.0
|
64
74
|
version:
|
65
|
-
description:
|
75
|
+
description: yore (as in "days of yore") is hands-off scheduled backup utility designed for web servers. It does backups of files and databases (MySQL only at present) to Amazon S3.
|
66
76
|
email:
|
67
77
|
- contact@buzz@ware@com@au
|
68
78
|
executables:
|
@@ -91,14 +101,11 @@ files:
|
|
91
101
|
- script/console
|
92
102
|
- script/destroy
|
93
103
|
- script/generate
|
94
|
-
-
|
95
|
-
-
|
96
|
-
-
|
97
|
-
- spec/test_job_a.xml
|
98
|
-
- spec/yore_spec.rb
|
99
|
-
- tasks/rspec.rake
|
104
|
+
- test/test_job_a.xml
|
105
|
+
- test/test_job_b.xml
|
106
|
+
- test/yore_test.rb
|
100
107
|
has_rdoc: true
|
101
|
-
homepage:
|
108
|
+
homepage: "RubyForge Project : http://rubyforge.org/projects/buzzware"
|
102
109
|
post_install_message:
|
103
110
|
rdoc_options:
|
104
111
|
- --main
|
@@ -123,6 +130,6 @@ rubyforge_project: buzzware
|
|
123
130
|
rubygems_version: 1.3.1
|
124
131
|
signing_key:
|
125
132
|
specification_version: 2
|
126
|
-
summary:
|
133
|
+
summary: yore (as in "days of yore") is hands-off scheduled backup utility designed for web servers
|
127
134
|
test_files: []
|
128
135
|
|
data/spec/rewind_spec.rb
DELETED
@@ -1,187 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
gem 'RequirePaths'; require 'require_paths'
|
3
|
-
require_paths '../../..'
|
4
|
-
|
5
|
-
require 'ihl_ruby/misc_utils'
|
6
|
-
|
7
|
-
require 'fileutils'
|
8
|
-
|
9
|
-
require 'yore_core'
|
10
|
-
|
11
|
-
def ensure_clean_bucket(aName)
|
12
|
-
`s3cmd createbucket #{aName}`
|
13
|
-
`s3cmd deleteall #{aName}`
|
14
|
-
end
|
15
|
-
|
16
|
-
describe YoreCore::Yore do
|
17
|
-
before(:each) do
|
18
|
-
@yore = YoreCore::Yore.new
|
19
|
-
end
|
20
|
-
|
21
|
-
it "should collect a list of files into a single file with a .contents file" do
|
22
|
-
# create a temp dir
|
23
|
-
temp_dir = MiscUtils::make_temp_dir('yore_spec')
|
24
|
-
# create source and dest subfolders
|
25
|
-
source_dir = File.expand_path('source',temp_dir)
|
26
|
-
dest_dir = File.expand_path('dest',temp_dir)
|
27
|
-
FileUtils.mkdir_p([source_dir,dest_dir])
|
28
|
-
# create some dirs and files in source
|
29
|
-
['a','a/1','b','c'].each {|p| FileUtils.mkdir_p(File.expand_path(p,source_dir))}
|
30
|
-
%w(a/blah.txt a/1/blahblah.txt b/apples.txt c/carrots.txt).each do |f|
|
31
|
-
MiscUtils::make_temp_file(f,source_dir)
|
32
|
-
end
|
33
|
-
# collect into dest file
|
34
|
-
# get recursive file list, without carrots
|
35
|
-
filelist = MiscUtils::recursive_file_list(source_dir) {|p| not p =~ /carrots.txt/}
|
36
|
-
dest_file = File.join(dest_dir,'destfile.bzb')
|
37
|
-
|
38
|
-
@yore.collect(filelist,dest_file)
|
39
|
-
|
40
|
-
# check contains filelist files ignoring .contents
|
41
|
-
cmd = "tar --list --file=#{dest_file}"
|
42
|
-
filelist_out = `#{cmd}`
|
43
|
-
i = 0
|
44
|
-
filelist_out.each_line {|line|
|
45
|
-
next if line==".contents\n"
|
46
|
-
i.should < filelist.length
|
47
|
-
line.chomp("\n").should == MiscUtils.path_debase(filelist[i],source_dir) # .bite('/')
|
48
|
-
i += 1
|
49
|
-
}
|
50
|
-
# could also check .contents file against actual contents
|
51
|
-
end
|
52
|
-
|
53
|
-
it "should encrypt and unencrypt a file with a standard iv but a supplied key" do
|
54
|
-
orig_content = 'abcdef123456'
|
55
|
-
temp_file1 = MiscUtils.make_temp_file(nil,nil,orig_content)
|
56
|
-
temp_file2 = temp_file1+'.enc'
|
57
|
-
temp_file3 = temp_file1+'.dec'
|
58
|
-
|
59
|
-
@yore.pack(temp_file1,temp_file2)
|
60
|
-
@yore.unpack(temp_file2,temp_file3)
|
61
|
-
|
62
|
-
file3_content = MiscUtils.string_from_file(temp_file3)
|
63
|
-
file3_content.should == orig_content
|
64
|
-
end
|
65
|
-
|
66
|
-
it "should create a backup filename combining a prefix, date, day and standard extension, and be able to decode the filename back to a date" do
|
67
|
-
@yore.configure({
|
68
|
-
:prefix => 'spec',
|
69
|
-
:first_hour => 4
|
70
|
-
})
|
71
|
-
now = Time.local(2009, 1, 1, 0, 0, 0, 0)
|
72
|
-
exp_filename = "spec-20081231.rew"
|
73
|
-
calc_filename = @yore.encode_file_name(now)
|
74
|
-
calc_filename.should == exp_filename
|
75
|
-
@yore.decode_file_name(calc_filename).should == Time.local(2008, 12, 31)
|
76
|
-
end
|
77
|
-
|
78
|
-
it "should upload and download files intact" do
|
79
|
-
bucket = 'yore_spec'
|
80
|
-
ensure_clean_bucket(bucket)
|
81
|
-
@yore.configure({
|
82
|
-
:bucket => bucket
|
83
|
-
})
|
84
|
-
content = 'this is my test content'
|
85
|
-
temp_dir = MiscUtils.make_temp_dir()
|
86
|
-
temp_file = MiscUtils.make_temp_file(nil, temp_dir, content)
|
87
|
-
puts @yore.upload(temp_file)
|
88
|
-
orig_file = temp_file+'.orig'
|
89
|
-
File.rename(temp_file, orig_file)
|
90
|
-
puts @yore.download(temp_file)
|
91
|
-
down_file_content = MiscUtils.string_from_file(temp_file)
|
92
|
-
down_file_content.should == content
|
93
|
-
end
|
94
|
-
|
95
|
-
it "should backup and restore multiple directories of file" do
|
96
|
-
# create a temp dir
|
97
|
-
temp_dir = MiscUtils::make_temp_dir('yore_spec')
|
98
|
-
# create source and dest subfolders
|
99
|
-
source_dir = File.expand_path('source',temp_dir)
|
100
|
-
source1 = File.join(source_dir,'source1')
|
101
|
-
source2 = File.join(source_dir,'source2')
|
102
|
-
dest_dir = File.expand_path('dest',temp_dir)
|
103
|
-
bucket = 'yore_spec'
|
104
|
-
|
105
|
-
FileUtils.mkdir_p([source1,source2,dest_dir])
|
106
|
-
# create some dirs and files in source
|
107
|
-
['a','a/1','b','c'].each {|p| FileUtils.mkdir_p(File.expand_path(p,source1))}
|
108
|
-
%w(a/blah.txt a/1/blahblah.txt b/apples.txt c/carrots.txt).each do |f|
|
109
|
-
MiscUtils::make_temp_file(f,source1)
|
110
|
-
end
|
111
|
-
['w','x/1','y/2','z'].each {|p| FileUtils.mkdir_p(File.expand_path(p,source2))}
|
112
|
-
%w(w/zonk.txt x/1/eggs.txt y/2/bloop.txt z/zax.txt).each do |f|
|
113
|
-
MiscUtils::make_temp_file(f,source2)
|
114
|
-
end
|
115
|
-
|
116
|
-
# create job file
|
117
|
-
job_template = <<EOF
|
118
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
119
|
-
<Yore>
|
120
|
-
<SimpleItems>
|
121
|
-
<Item Name="crypto_iv">3A63775C1E3F291B0925578165EB917E</Item>
|
122
|
-
<Item Name="crypto_key">07692FC8656F04AE5518B80D38681E038A3C12050DF6CC97CEEC33D800D5E2FE</Item>
|
123
|
-
<Item Name="prefix">backup</Item>
|
124
|
-
<Item Name="log_level">DEBUG</Item>
|
125
|
-
<Item Name="bucket">${BUCKET}</Item>
|
126
|
-
</SimpleItems>
|
127
|
-
<Sources>
|
128
|
-
<Source>
|
129
|
-
<IncludePath>${SOURCE1}</IncludePath>
|
130
|
-
<IncludePath>${SOURCE2}</IncludePath>
|
131
|
-
</Source>
|
132
|
-
</Sources>
|
133
|
-
</Yore>
|
134
|
-
EOF
|
135
|
-
|
136
|
-
job_content = StringUtils::render_template(job_template,{
|
137
|
-
'SOURCE1' => source1,
|
138
|
-
'SOURCE2' => source2,
|
139
|
-
'BUCKET' => bucket
|
140
|
-
})
|
141
|
-
MiscUtils::string_to_file(job_content,job_file = MiscUtils::temp_file)
|
142
|
-
|
143
|
-
ensure_clean_bucket(bucket)
|
144
|
-
@yore.configure({
|
145
|
-
:bucket => bucket
|
146
|
-
})
|
147
|
-
|
148
|
-
# call yore script with ruby from the command line, then download result and check contents
|
149
|
-
puts cmd = "#{File.expand_path('../lib/yore',THIS_DIR)} backup #{job_file}"
|
150
|
-
puts result = `#{cmd}`
|
151
|
-
|
152
|
-
retrieved_fname = File.expand_path(@yore.encode_file_name(), dest_dir)
|
153
|
-
collection_fname = MiscUtils::temp_file
|
154
|
-
|
155
|
-
puts @yore.download(retrieved_fname)
|
156
|
-
puts @yore.unpack(retrieved_fname,collection_fname)
|
157
|
-
|
158
|
-
filelist = MiscUtils::recursive_file_list(source_dir,false)
|
159
|
-
|
160
|
-
# check contains filelist files ignoring .contents
|
161
|
-
cmd = "tar --list --file=#{collection_fname}"
|
162
|
-
filelist_out = `#{cmd}`
|
163
|
-
filelist_out = filelist_out.split("\n").sort
|
164
|
-
filelist_out.should == filelist
|
165
|
-
# i = 0
|
166
|
-
# filelist_out.each {|line|
|
167
|
-
# next if line==".contents\n"
|
168
|
-
# i.should < filelist.length
|
169
|
-
# line.chomp("\n").should == MiscUtils.path_debase(filelist[i],source_dir) # .bite('/')
|
170
|
-
# i += 1
|
171
|
-
# }
|
172
|
-
# could also check .contents file against actual contents
|
173
|
-
end
|
174
|
-
|
175
|
-
it "should assign proper boolean values" do
|
176
|
-
class Blah
|
177
|
-
SOMECONSTANT = {:a => 'AAA', :b => true, :c => false}
|
178
|
-
end
|
179
|
-
|
180
|
-
puts Blah::SOMECONSTANT.inspect
|
181
|
-
end
|
182
|
-
|
183
|
-
it "should provide configurable criteria for keeping old files"
|
184
|
-
|
185
|
-
it "should clean a folder full of files, removing files that don't match configurable criteria for keeping old files"
|
186
|
-
end
|
187
|
-
|
data/spec/spec.opts
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
--colour
|
data/spec/spec_helper.rb
DELETED
data/tasks/rspec.rake
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
begin
|
2
|
-
require 'spec'
|
3
|
-
rescue LoadError
|
4
|
-
require 'rubygems'
|
5
|
-
require 'spec'
|
6
|
-
end
|
7
|
-
begin
|
8
|
-
require 'spec/rake/spectask'
|
9
|
-
rescue LoadError
|
10
|
-
puts <<-EOS
|
11
|
-
To use rspec for testing you must install rspec gem:
|
12
|
-
gem install rspec
|
13
|
-
EOS
|
14
|
-
exit(0)
|
15
|
-
end
|
16
|
-
|
17
|
-
desc "Run the specs under spec/models"
|
18
|
-
Spec::Rake::SpecTask.new do |t|
|
19
|
-
t.spec_opts = ['--options', "spec/spec.opts"]
|
20
|
-
t.spec_files = FileList['spec/**/*_spec.rb']
|
21
|
-
end
|