darkofabijan-astrails-safe 0.2.8

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.
Files changed (41) hide show
  1. data/LICENSE +20 -0
  2. data/README.markdown +237 -0
  3. data/Rakefile +61 -0
  4. data/bin/astrails-safe +53 -0
  5. data/examples/example_helper.rb +19 -0
  6. data/lib/astrails/safe.rb +61 -0
  7. data/lib/astrails/safe/archive.rb +24 -0
  8. data/lib/astrails/safe/backup.rb +20 -0
  9. data/lib/astrails/safe/cloudfiles.rb +70 -0
  10. data/lib/astrails/safe/config/builder.rb +60 -0
  11. data/lib/astrails/safe/config/node.rb +76 -0
  12. data/lib/astrails/safe/gpg.rb +46 -0
  13. data/lib/astrails/safe/gzip.rb +25 -0
  14. data/lib/astrails/safe/local.rb +70 -0
  15. data/lib/astrails/safe/mysqldump.rb +32 -0
  16. data/lib/astrails/safe/pgdump.rb +36 -0
  17. data/lib/astrails/safe/pipe.rb +17 -0
  18. data/lib/astrails/safe/s3.rb +86 -0
  19. data/lib/astrails/safe/sftp.rb +88 -0
  20. data/lib/astrails/safe/sink.rb +35 -0
  21. data/lib/astrails/safe/source.rb +47 -0
  22. data/lib/astrails/safe/stream.rb +20 -0
  23. data/lib/astrails/safe/svndump.rb +13 -0
  24. data/lib/astrails/safe/tmp_file.rb +48 -0
  25. data/lib/extensions/mktmpdir.rb +45 -0
  26. data/spec/integration/archive_integration_spec.rb +88 -0
  27. data/spec/integration/cleanup_spec.rb +61 -0
  28. data/spec/spec.opts +5 -0
  29. data/spec/spec_helper.rb +16 -0
  30. data/spec/unit/archive_spec.rb +67 -0
  31. data/spec/unit/cloudfiles_spec.rb +170 -0
  32. data/spec/unit/config_spec.rb +213 -0
  33. data/spec/unit/gpg_spec.rb +148 -0
  34. data/spec/unit/gzip_spec.rb +64 -0
  35. data/spec/unit/local_spec.rb +110 -0
  36. data/spec/unit/mysqldump_spec.rb +83 -0
  37. data/spec/unit/pgdump_spec.rb +45 -0
  38. data/spec/unit/s3_spec.rb +160 -0
  39. data/spec/unit/svndump_spec.rb +39 -0
  40. data/templates/script.rb +165 -0
  41. metadata +179 -0
@@ -0,0 +1,148 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Astrails::Safe::Gpg do
4
+ def def_backup
5
+ {
6
+ :compressed => false,
7
+ :command => "command",
8
+ :extension => ".foo",
9
+ :filename => "qweqwe"
10
+ }
11
+ end
12
+
13
+ def gpg(config = {}, backup = def_backup)
14
+ Astrails::Safe::Gpg.new(
15
+ Astrails::Safe::Config::Node.new(nil, config),
16
+ Astrails::Safe::Backup.new(backup)
17
+ )
18
+ end
19
+
20
+ after(:each) { Astrails::Safe::TmpFile.cleanup }
21
+
22
+ describe :process do
23
+
24
+ before(:each) do
25
+ @gpg = gpg()
26
+ stub(@gpg).gpg_password_file {"pwd-file"}
27
+ stub(@gpg).pipe {"|gpg -BLAH"}
28
+ end
29
+
30
+ describe "when active" do
31
+ before(:each) do
32
+ stub(@gpg).active? {true}
33
+ end
34
+
35
+ it "should add .gpg extension" do
36
+ mock(@gpg.backup.extension) << '.gpg'
37
+ @gpg.process
38
+ end
39
+
40
+ it "should add command pipe" do
41
+ mock(@gpg.backup.command) << (/\|gpg -BLAH/)
42
+ @gpg.process
43
+ end
44
+
45
+ it "should set compressed" do
46
+ mock(@gpg.backup).compressed = true
47
+ @gpg.process
48
+ end
49
+ end
50
+
51
+ describe "when inactive" do
52
+ before(:each) do
53
+ stub(@gpg).active? {false}
54
+ end
55
+
56
+ it "should not touch extension" do
57
+ dont_allow(@gpg.backup.extension) << anything
58
+ @gpg.process
59
+ end
60
+
61
+ it "should not touch command" do
62
+ dont_allow(@gpg.backup.command) << anything
63
+ @gpg.process
64
+ end
65
+
66
+ it "should not touch compressed" do
67
+ dont_allow(@gpg.backup).compressed = anything
68
+ @gpg.process
69
+ end
70
+ end
71
+ end
72
+
73
+ describe :active? do
74
+
75
+ describe "with key" do
76
+ it "should be true" do
77
+ gpg(:gpg => {:key => :foo}).should be_active
78
+ end
79
+ end
80
+
81
+ describe "with password" do
82
+ it "should be true" do
83
+ gpg(:gpg => {:password => :foo}).should be_active
84
+ end
85
+ end
86
+
87
+ describe "without key & password" do
88
+ it "should be false" do
89
+ gpg.should_not be_active
90
+ end
91
+ end
92
+
93
+ describe "with key & password" do
94
+ it "should raise RuntimeError" do
95
+ lambda {
96
+ gpg(:gpg => {:key => "foo", :password => "bar"}).send :active?
97
+ }.should raise_error(RuntimeError, "can't use both gpg password and pubkey")
98
+ end
99
+ end
100
+ end
101
+
102
+ describe :pipe do
103
+
104
+ describe "with key" do
105
+ def kgpg(extra={})
106
+ gpg({:gpg => {:key => "foo", :options => "GPG-OPT"}.merge(extra), :options => "OPT"})
107
+ end
108
+
109
+ it "should not call gpg_password_file" do
110
+ g = kgpg
111
+ dont_allow(g).gpg_password_file(anything)
112
+ g.send(:pipe)
113
+ end
114
+
115
+ it "should use '-r' and :options" do
116
+ kgpg.send(:pipe).should == "|gpg GPG-OPT -e -r foo"
117
+ end
118
+
119
+ it "should use the 'command' options" do
120
+ kgpg(:command => 'other-gpg').send(:pipe).should == "|other-gpg GPG-OPT -e -r foo"
121
+ end
122
+ end
123
+
124
+ describe "with password" do
125
+ def pgpg(extra = {})
126
+ returning(gpg({:gpg => {:password => "bar", :options => "GPG-OPT"}.merge(extra), :options => "OPT"})) do |g|
127
+ stub(g).gpg_password_file(anything) {"pass-file"}
128
+ end
129
+ end
130
+
131
+ it "should use '--passphrase-file' and :options" do
132
+ pgpg.send(:pipe).should == "|gpg GPG-OPT -c --passphrase-file pass-file"
133
+ end
134
+
135
+ it "should use the 'command' options" do
136
+ pgpg(:command => 'other-gpg').send(:pipe).should == "|other-gpg GPG-OPT -c --passphrase-file pass-file"
137
+ end
138
+ end
139
+ end
140
+
141
+ describe :gpg_password_file do
142
+ it "should create password file" do
143
+ file = gpg.send(:gpg_password_file, "foo")
144
+ File.exists?(file).should be_true
145
+ File.read(file).should == "foo"
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,64 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Astrails::Safe::Gzip do
4
+
5
+ def def_backup
6
+ {
7
+ :compressed => false,
8
+ :command => "command",
9
+ :extension => ".foo",
10
+ :filename => "qweqwe"
11
+ }
12
+ end
13
+
14
+ after(:each) { Astrails::Safe::TmpFile.cleanup }
15
+
16
+ def gzip(config = {}, backup = def_backup)
17
+ Astrails::Safe::Gzip.new(
18
+ @config = Astrails::Safe::Config::Node.new(nil, config),
19
+ @backup = Astrails::Safe::Backup.new(backup)
20
+ )
21
+ end
22
+
23
+ describe :preocess do
24
+
25
+ describe "when not yet compressed" do
26
+ before(:each) { @gzip = gzip }
27
+
28
+ it "should add .gz extension" do
29
+ mock(@backup.extension) << '.gz'
30
+ @gzip.process
31
+ end
32
+
33
+ it "should add |gzip pipe" do
34
+ mock(@backup.command) << '|gzip'
35
+ @gzip.process
36
+ end
37
+
38
+ it "should set compressed" do
39
+ mock(@backup).compressed = true
40
+ @gzip.process
41
+ end
42
+ end
43
+
44
+ describe "when already compressed" do
45
+
46
+ before(:each) { @gzip = gzip({}, :compressed => true) }
47
+
48
+ it "should not touch extension" do
49
+ dont_allow(@backup.extension).<< anything
50
+ @gzip.process
51
+ end
52
+
53
+ it "should not touch command" do
54
+ dont_allow(@backup.command).<< anything
55
+ @gzip.process
56
+ end
57
+
58
+ it "should not touch compressed" do
59
+ dont_allow(@backup).compressed = anything
60
+ @gzip.process
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,110 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Astrails::Safe::Local do
4
+ def def_config
5
+ {
6
+ :local => {
7
+ :path => "/:kind~:id~:timestamp"
8
+ },
9
+ :keep => {
10
+ :local => 2
11
+ }
12
+ }
13
+ end
14
+
15
+ def def_backup
16
+ {
17
+ :kind => "mysqldump",
18
+ :id => "blog",
19
+ :timestamp => "NoW",
20
+ :compressed => true,
21
+ :command => "command",
22
+ :extension => ".foo.gz",
23
+ :filename => "qweqwe"
24
+ }
25
+ end
26
+
27
+ def local(config = def_config, backup = def_backup)
28
+ Astrails::Safe::Local.new(
29
+ @config = Astrails::Safe::Config::Node.new(nil, config),
30
+ @backup = Astrails::Safe::Backup.new(backup)
31
+ )
32
+ end
33
+
34
+ describe :active? do
35
+ it "should be true" do
36
+ local.should be_active
37
+ end
38
+ end
39
+
40
+ describe :path do
41
+ it "should raise RuntimeError when no path" do
42
+ lambda {
43
+ local({}).send :path
44
+ }.should raise_error(RuntimeError, "missing :local/:path")
45
+ end
46
+
47
+ it "should use local/path" do
48
+ local.send(:path).should == "/mysqldump~blog~NoW"
49
+ end
50
+ end
51
+
52
+ describe :save do
53
+ before(:each) do
54
+ @local = local
55
+ stub(@local).system
56
+ stub(@local).full_path {"file-path"}
57
+ stub(FileUtils).mkdir_p
58
+ end
59
+
60
+ it "should call system to save the file" do
61
+ mock(@local).system("command>file-path")
62
+ @local.send(:save)
63
+ end
64
+
65
+ it "should create directory" do
66
+ mock(FileUtils).mkdir_p("/mysqldump~blog~NoW")
67
+ @local.send(:save)
68
+ end
69
+
70
+ it "should set backup.path" do
71
+ mock(@backup).path = "file-path"
72
+ @local.send(:save)
73
+ end
74
+
75
+ describe "dry run" do
76
+ before(:all) {$DRY_RUN = true}
77
+ after(:all) {$DRY_RUN = false}
78
+
79
+ it "should not create directory"
80
+ it "should not call system"
81
+ it "should set backup.path" do
82
+ mock(@backup).path = "file-path"
83
+ @local.send(:save)
84
+ end
85
+ end
86
+ end
87
+
88
+ describe :cleanup do
89
+ before(:each) do
90
+ @files = [4,1,3,2].to_a.map { |i| "/mysqldump~blog~NoW/qweqwe.#{i}" }
91
+ stub(File).file?(anything) {true}
92
+ stub(File).size(anything) {1}
93
+ stub(File).unlink
94
+ end
95
+
96
+ it "should check [:keep, :local]" do
97
+ @local = local(def_config.merge(:keep => {}))
98
+ dont_allow(Dir).[]
99
+ @local.send :cleanup
100
+ end
101
+
102
+ it "should delete extra files" do
103
+ @local = local
104
+ mock(Dir).[]("/mysqldump~blog~NoW/qweqwe.*") {@files}
105
+ mock(File).unlink("/mysqldump~blog~NoW/qweqwe.1")
106
+ mock(File).unlink("/mysqldump~blog~NoW/qweqwe.2")
107
+ @local.send :cleanup
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,83 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Astrails::Safe::Mysqldump do
4
+
5
+ def def_config(extra = {})
6
+ {
7
+ :options => "OPTS",
8
+ :user => "User",
9
+ :password => "pwd",
10
+ :host => "localhost",
11
+ :port => 7777,
12
+ :socket => "socket",
13
+ :skip_tables => [:bar, :baz]
14
+ }.merge(extra)
15
+ end
16
+
17
+ def mysqldump(id = :foo, config = def_config)
18
+ Astrails::Safe::Mysqldump.new(id, Astrails::Safe::Config::Node.new(nil, config))
19
+ end
20
+
21
+ before(:each) do
22
+ stub(Time).now.stub!.strftime {"NOW"}
23
+ end
24
+
25
+ after(:each) { Astrails::Safe::TmpFile.cleanup }
26
+
27
+ describe :backup do
28
+ before(:each) do
29
+ @mysql = mysqldump
30
+ stub(@mysql).mysql_password_file {"/tmp/pwd"}
31
+ end
32
+
33
+ {
34
+ :id => "foo",
35
+ :kind => "mysqldump",
36
+ :extension => ".sql",
37
+ :filename => "mysqldump-foo.NOW",
38
+ :command => "mysqldump --defaults-extra-file=/tmp/pwd OPTS --ignore-table=foo.bar --ignore-table=foo.baz foo",
39
+ }.each do |k, v|
40
+ it "should set #{k} to #{v}" do
41
+ @mysql.backup.send(k).should == v
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ describe :mysql_skip_tables do
48
+ it "should return nil if no skip_tables" do
49
+ config = def_config.dup
50
+ config.delete(:skip_tables)
51
+ m = mysqldump(:foo, Astrails::Safe::Config::Node.new(nil, config))
52
+ stub(m).timestamp {"NOW"}
53
+ m.send(:mysql_skip_tables).should be_nil
54
+ m.backup.command.should_not match(/ignore-table/)
55
+ end
56
+
57
+ it "should return '' if skip_tables empty" do
58
+ config = def_config.dup
59
+ config[:skip_tables] = []
60
+ m = mysqldump(:foo, Astrails::Safe::Config::Node.new(nil, config))
61
+ stub(m).timestamp {"NOW"}
62
+ m.send(:mysql_skip_tables).should == ""
63
+ m.backup.command.should_not match(/ignore-table/)
64
+ end
65
+
66
+ end
67
+
68
+ describe :mysql_password_file do
69
+ it "should create passwords file with quoted values" do
70
+ m = mysqldump(:foo, def_config(:password => '#qwe"asd\'zxc'))
71
+ file = m.send(:mysql_password_file)
72
+ File.exists?(file).should == true
73
+ File.read(file).should == <<-PWD
74
+ [mysqldump]
75
+ user = "User"
76
+ password = "#qwe\\"asd'zxc"
77
+ socket = "socket"
78
+ host = "localhost"
79
+ port = 7777
80
+ PWD
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,45 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Astrails::Safe::Pgdump do
4
+
5
+ def def_config
6
+ {
7
+ :options => "OPTS",
8
+ :user => "User",
9
+ :password => "pwd",
10
+ :host => "localhost",
11
+ :port => 7777,
12
+ :skip_tables => [:bar, :baz]
13
+ }
14
+ end
15
+
16
+ def pgdump(id = :foo, config = def_config)
17
+ Astrails::Safe::Pgdump.new(id, Astrails::Safe::Config::Node.new(nil, config))
18
+ end
19
+
20
+ before(:each) do
21
+ stub(Time).now.stub!.strftime {"NOW"}
22
+ end
23
+
24
+ after(:each) { Astrails::Safe::TmpFile.cleanup }
25
+
26
+ describe :backup do
27
+ before(:each) do
28
+ @pg = pgdump
29
+ end
30
+
31
+ {
32
+ :id => "foo",
33
+ :kind => "pgdump",
34
+ :extension => ".sql",
35
+ :filename => "pgdump-foo.NOW",
36
+ :command => "pg_dump OPTS --username='User' --host='localhost' --port='7777' foo",
37
+ }.each do |k, v|
38
+ it "should set #{k} to #{v}" do
39
+ @pg.backup.send(k).should == v
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ end