yore 0.0.1 → 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/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
|