bostonlogic-safe 0.3.0

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 +227 -0
  3. data/Rakefile +54 -0
  4. data/VERSION.yml +4 -0
  5. data/bin/astrails-safe +53 -0
  6. data/examples/example_helper.rb +19 -0
  7. data/examples/integration/archive_integration_example.rb +86 -0
  8. data/examples/integration/cleanup_example.rb +62 -0
  9. data/examples/unit/archive_example.rb +67 -0
  10. data/examples/unit/config_example.rb +184 -0
  11. data/examples/unit/gpg_example.rb +138 -0
  12. data/examples/unit/gzip_example.rb +64 -0
  13. data/examples/unit/local_example.rb +110 -0
  14. data/examples/unit/mysqldump_example.rb +83 -0
  15. data/examples/unit/pgdump_example.rb +45 -0
  16. data/examples/unit/rcloud_example.rb +110 -0
  17. data/examples/unit/s3_example.rb +112 -0
  18. data/examples/unit/svndump_example.rb +39 -0
  19. data/lib/astrails/safe.rb +71 -0
  20. data/lib/astrails/safe/archive.rb +24 -0
  21. data/lib/astrails/safe/backup.rb +20 -0
  22. data/lib/astrails/safe/config/builder.rb +62 -0
  23. data/lib/astrails/safe/config/node.rb +66 -0
  24. data/lib/astrails/safe/gpg.rb +45 -0
  25. data/lib/astrails/safe/gzip.rb +25 -0
  26. data/lib/astrails/safe/local.rb +48 -0
  27. data/lib/astrails/safe/mysqldump.rb +31 -0
  28. data/lib/astrails/safe/notification.rb +66 -0
  29. data/lib/astrails/safe/pgdump.rb +36 -0
  30. data/lib/astrails/safe/pipe.rb +13 -0
  31. data/lib/astrails/safe/rcloud.rb +73 -0
  32. data/lib/astrails/safe/s3.rb +68 -0
  33. data/lib/astrails/safe/sftp.rb +79 -0
  34. data/lib/astrails/safe/sink.rb +33 -0
  35. data/lib/astrails/safe/source.rb +46 -0
  36. data/lib/astrails/safe/stream.rb +19 -0
  37. data/lib/astrails/safe/svndump.rb +13 -0
  38. data/lib/astrails/safe/tmp_file.rb +48 -0
  39. data/lib/extensions/mktmpdir.rb +45 -0
  40. data/templates/script.rb +155 -0
  41. metadata +135 -0
@@ -0,0 +1,64 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../example_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__) + '/../example_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
+ @local = local
91
+ @files = [4,1,3,2].to_a.map { |i| "/mysqldump~blog~NoW/qweqwe.#{i}" }
92
+ stub(Dir).[]("/mysqldump~blog~NoW/qweqwe.*") {@files}
93
+ stub(File).file?(anything) {true}
94
+ stub(File).size(anything) {1}
95
+ stub(File).unlink
96
+ end
97
+
98
+ it "should check [:keep, :local]" do
99
+ @local.config[:keep][:local] = nil
100
+ dont_allow(Dir).[]
101
+ @local.send :cleanup
102
+ end
103
+
104
+ it "should delete extra files" do
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__) + '/../example_helper')
2
+
3
+ describe Astrails::Safe::Mysqldump do
4
+
5
+ def def_config
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
+ }
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" do
70
+ m = mysqldump
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 = pwd
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__) + '/../example_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
@@ -0,0 +1,110 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../example_helper')
2
+
3
+ describe Astrails::Safe::Rcloud do
4
+
5
+ def def_config
6
+ {
7
+ :rcloud => {
8
+ :container => "_bucket",
9
+ :username => "_key",
10
+ :api_key => "_secret",
11
+ },
12
+ :keep => {
13
+ :rcloud => 2
14
+ }
15
+ }
16
+ end
17
+
18
+ def def_backup
19
+ {
20
+ :kind => "_kind",
21
+ :filename => "/backup/somewhere/_kind-_id.NOW.bar",
22
+ :extension => ".bar",
23
+ :id => "_id",
24
+ :timestamp => "NOW"
25
+ }
26
+ end
27
+
28
+ def rcloud(config = def_config, backup = def_backup)
29
+ Astrails::Safe::Rcloud.new(
30
+ Astrails::Safe::Config::Node.new(nil, config),
31
+ Astrails::Safe::Backup.new(backup)
32
+ )
33
+ end
34
+
35
+ # describe :cleanup do
36
+
37
+ # before(:each) do
38
+ # @rcloud = rcloud
39
+
40
+ # @files = [4,1,3,2].to_a.map { |i| stub(o = {}).key {"aaaaa#{i}"}; o }
41
+
42
+ # stub(AWS::S3::Bucket).objects("_bucket", :prefix => "_kind/_id/_kind-_id.", :max_keys => 4) {@files}
43
+ # stub(AWS::S3::Bucket).find("_bucket").stub![anything].stub!.delete
44
+ # end
45
+
46
+ # it "should check [:keep, :rcloud]" do
47
+ # @rcloud.config[:keep][:rcloud] = nil
48
+ # dont_allow(@rcloud.backup).filename
49
+ # @rcloud.send :cleanup
50
+ # end
51
+
52
+ # it "should delete extra files" do
53
+ # mock(AWS::S3::Bucket).find("_bucket").mock!["aaaaa1"].mock!.delete
54
+ # mock(AWS::S3::Bucket).find("_bucket").mock!["aaaaa2"].mock!.delete
55
+ # @rcloud.send :cleanup
56
+ # end
57
+ # end
58
+
59
+ describe :active do
60
+ before(:each) do
61
+ @rcloud = rcloud
62
+ end
63
+
64
+ it "should be true when all params are set" do
65
+ @rcloud.should be_active
66
+ end
67
+
68
+ it "should be false if container is missing" do
69
+ @rcloud.config[:rcloud][:container] = nil
70
+ @rcloud.should_not be_active
71
+ end
72
+
73
+ it "should be false if api_key is missing" do
74
+ @rcloud.config[:rcloud][:api_key] = nil
75
+ @rcloud.should_not be_active
76
+ end
77
+
78
+ it "should be false if username is missing" do
79
+ @rcloud.config[:rcloud][:username] = nil
80
+ @rcloud.should_not be_active
81
+ end
82
+ end
83
+
84
+ describe :path do
85
+ before(:each) do
86
+ @rcloud = rcloud
87
+ end
88
+ it "should use rcloud/path 1st" do
89
+ @rcloud.config[:rcloud][:path] = "rcloud_path"
90
+ @rcloud.config[:local] = {:path => "local_path"}
91
+ @rcloud.send(:path).should == "rcloud_path"
92
+ end
93
+
94
+ it "should use local/path 2nd" do
95
+ @rcloud.config[:local] = {:path => "local_path"}
96
+ @rcloud.send(:path).should == "local_path"
97
+ end
98
+
99
+ it "should use constant 3rd" do
100
+ @rcloud.send(:path).should == "_kind/_id"
101
+ end
102
+ end
103
+
104
+ describe :save do
105
+ it "should establish rcloud connection"
106
+ it "should RuntimeError if no local file (i.e. :local didn't run)"
107
+ it "should upload file"
108
+ end
109
+
110
+ end
@@ -0,0 +1,112 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../example_helper')
2
+
3
+ describe Astrails::Safe::S3 do
4
+
5
+ def def_config
6
+ {
7
+ :s3 => {
8
+ :bucket => "_bucket",
9
+ :key => "_key",
10
+ :secret => "_secret",
11
+ },
12
+ :keep => {
13
+ :s3 => 2
14
+ }
15
+ }
16
+ end
17
+
18
+ def def_backup
19
+ {
20
+ :kind => "_kind",
21
+ :filename => "/backup/somewhere/_kind-_id.NOW.bar",
22
+ :extension => ".bar",
23
+ :id => "_id",
24
+ :timestamp => "NOW"
25
+ }
26
+ end
27
+
28
+ def s3(config = def_config, backup = def_backup)
29
+ Astrails::Safe::S3.new(
30
+ Astrails::Safe::Config::Node.new(nil, config),
31
+ Astrails::Safe::Backup.new(backup)
32
+ )
33
+ end
34
+
35
+ describe :cleanup do
36
+
37
+ before(:each) do
38
+ @s3 = s3
39
+
40
+ @files = [4,1,3,2].to_a.map { |i| stub(o = {}).key {"aaaaa#{i}"}; o }
41
+
42
+ stub(AWS::S3::Bucket).objects("_bucket", :prefix => "_kind/_id/_kind-_id.", :max_keys => 4) {@files}
43
+ stub(AWS::S3::Bucket).find("_bucket").stub![anything].stub!.delete
44
+ end
45
+
46
+ it "should check [:keep, :s3]" do
47
+ @s3.config[:keep][:s3] = nil
48
+ dont_allow(@s3.backup).filename
49
+ @s3.send :cleanup
50
+ end
51
+
52
+ it "should delete extra files" do
53
+ mock(AWS::S3::Bucket).find("_bucket").mock!["aaaaa1"].mock!.delete
54
+ mock(AWS::S3::Bucket).find("_bucket").mock!["aaaaa2"].mock!.delete
55
+ @s3.send :cleanup
56
+ end
57
+
58
+ end
59
+
60
+ describe :active do
61
+ before(:each) do
62
+ @s3 = s3
63
+ end
64
+
65
+ it "should be true when all params are set" do
66
+ @s3.should be_active
67
+ end
68
+
69
+ it "should be false if bucket is missing" do
70
+ @s3.config[:s3][:bucket] = nil
71
+ @s3.should_not be_active
72
+ end
73
+
74
+ it "should be false if key is missing" do
75
+ @s3.config[:s3][:key] = nil
76
+ @s3.should_not be_active
77
+ end
78
+
79
+ it "should be false if secret is missing" do
80
+ @s3.config[:s3][:secret] = nil
81
+ @s3.should_not be_active
82
+ end
83
+ end
84
+
85
+ describe :path do
86
+ before(:each) do
87
+ @s3 = s3
88
+ end
89
+ it "should use s3/path 1st" do
90
+ @s3.config[:s3][:path] = "s3_path"
91
+ @s3.config[:local] = {:path => "local_path"}
92
+ @s3.send(:path).should == "s3_path"
93
+ end
94
+
95
+ it "should use local/path 2nd" do
96
+ @s3.config[:local] = {:path => "local_path"}
97
+ @s3.send(:path).should == "local_path"
98
+ end
99
+
100
+ it "should use constant 3rd" do
101
+ @s3.send(:path).should == "_kind/_id"
102
+ end
103
+
104
+ end
105
+
106
+ describe :save do
107
+ it "should establish s3 connection"
108
+ it "should RuntimeError if no local file (i.e. :local didn't run)"
109
+ it "should open local file"
110
+ it "should upload file"
111
+ end
112
+ end