astrails-safe 0.1.6 → 0.1.7
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/README.markdown +33 -3
- data/Rakefile +3 -3
- data/VERSION.yml +1 -1
- data/bin/astrails-safe +1 -7
- data/examples/example_helper.rb +3 -3
- data/examples/integration/archive_integration_example.rb +86 -0
- data/examples/unit/archive_example.rb +67 -0
- data/examples/unit/config_example.rb +49 -3
- data/examples/unit/gpg_example.rb +138 -0
- data/examples/unit/gzip_example.rb +64 -0
- data/examples/unit/local_example.rb +82 -0
- data/examples/unit/mysqldump_example.rb +83 -0
- data/examples/unit/pgdump_example.rb +45 -0
- data/examples/unit/s3_example.rb +28 -0
- data/examples/unit/svndump_example.rb +39 -0
- data/lib/astrails/safe.rb +24 -7
- data/lib/astrails/safe/archive.rb +2 -2
- data/lib/astrails/safe/backup.rb +20 -0
- data/lib/astrails/safe/config/builder.rb +6 -6
- data/lib/astrails/safe/config/node.rb +1 -2
- data/lib/astrails/safe/gpg.rb +14 -11
- data/lib/astrails/safe/gzip.rb +8 -8
- data/lib/astrails/safe/local.rb +8 -17
- data/lib/astrails/safe/mysqldump.rb +2 -2
- data/lib/astrails/safe/pgdump.rb +36 -0
- data/lib/astrails/safe/pipe.rb +5 -7
- data/lib/astrails/safe/s3.rb +9 -11
- data/lib/astrails/safe/sink.rb +7 -11
- data/lib/astrails/safe/source.rb +30 -15
- data/lib/astrails/safe/stream.rb +7 -33
- data/lib/astrails/safe/svndump.rb +13 -0
- data/lib/astrails/safe/tmp_file.rb +9 -5
- data/templates/script.rb +13 -0
- metadata +18 -5
- data/examples/unit/stream_example.rb +0 -33
@@ -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,82 @@
|
|
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
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def def_backup
|
13
|
+
{
|
14
|
+
:kind => "mysqldump",
|
15
|
+
:id => "blog",
|
16
|
+
:timestamp => "NoW",
|
17
|
+
:compressed => true,
|
18
|
+
:command => "command",
|
19
|
+
:extension => ".foo.gz",
|
20
|
+
:filename => "qweqwe"
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def local(config = def_config, backup = def_backup)
|
25
|
+
Astrails::Safe::Local.new(
|
26
|
+
@config = Astrails::Safe::Config::Node.new(nil, config),
|
27
|
+
@backup = Astrails::Safe::Backup.new(backup)
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
describe :active? do
|
32
|
+
it "should be true" do
|
33
|
+
local.should be_active
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe :prefix do
|
38
|
+
it "should raise RuntimeError when no path" do
|
39
|
+
lambda {
|
40
|
+
local({}).send :prefix
|
41
|
+
}.should raise_error(RuntimeError, "missing :local/:path")
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should use local/path" do
|
45
|
+
local.send(:prefix).should == "/mysqldump~blog~NoW"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe :save do
|
50
|
+
before(:each) do
|
51
|
+
@local = local
|
52
|
+
stub(@local).system
|
53
|
+
stub(@local).path {"file-path"}
|
54
|
+
stub(FileUtils).mkdir_p
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should call system to save the file" do
|
58
|
+
mock(@local).system("command>file-path")
|
59
|
+
@local.send(:save)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should create directory" do
|
63
|
+
mock(FileUtils).mkdir_p("/mysqldump~blog~NoW")
|
64
|
+
@local.send(:save)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should set backup.path" do
|
68
|
+
mock(@backup).path = "file-path"
|
69
|
+
@local.send(:save)
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "dry run" do
|
73
|
+
it "should not create directory"
|
74
|
+
it "should not call system"
|
75
|
+
it "should set backup.path"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe :cleanup do
|
80
|
+
it "should have test"
|
81
|
+
end
|
82
|
+
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,28 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../example_helper')
|
2
|
+
|
3
|
+
describe Astrails::Safe::S3 do
|
4
|
+
|
5
|
+
describe :active do
|
6
|
+
it "should be true when all params are set"
|
7
|
+
it "should be false if bucket is missing"
|
8
|
+
it "should be false if key is missing"
|
9
|
+
it "should be false if secret is missing"
|
10
|
+
end
|
11
|
+
|
12
|
+
describe :prefix do
|
13
|
+
it "should use s3/path 1st"
|
14
|
+
it "should use local/path 2nd"
|
15
|
+
it "should use constant 3rd"
|
16
|
+
end
|
17
|
+
|
18
|
+
describe :save do
|
19
|
+
it "should establish s3 connection"
|
20
|
+
it "should RuntimeError if no local file (i.e. :local didn't run)"
|
21
|
+
it "should open local file"
|
22
|
+
it "should upload file"
|
23
|
+
end
|
24
|
+
|
25
|
+
describe :cleanup do
|
26
|
+
it "should have some tests"
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../example_helper')
|
2
|
+
|
3
|
+
describe Astrails::Safe::Svndump do
|
4
|
+
def def_config
|
5
|
+
{
|
6
|
+
:options => "OPTS",
|
7
|
+
:repo_path => "bar/baz"
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
def svndump(id = :foo, config = def_config)
|
12
|
+
Astrails::Safe::Svndump.new(id, Astrails::Safe::Config::Node.new(nil, config))
|
13
|
+
end
|
14
|
+
|
15
|
+
before(:each) do
|
16
|
+
stub(Time).now.stub!.strftime {"NOW"}
|
17
|
+
end
|
18
|
+
|
19
|
+
after(:each) { Astrails::Safe::TmpFile.cleanup }
|
20
|
+
|
21
|
+
describe :backup do
|
22
|
+
before(:each) do
|
23
|
+
@svn = svndump
|
24
|
+
end
|
25
|
+
|
26
|
+
{
|
27
|
+
:id => "foo",
|
28
|
+
:kind => "svndump",
|
29
|
+
:extension => ".svn",
|
30
|
+
:filename => "svndump-foo.NOW",
|
31
|
+
:command => "svnadmin dump OPTS bar/baz",
|
32
|
+
}.each do |k, v|
|
33
|
+
it "should set #{k} to #{v}" do
|
34
|
+
@svn.backup.send(k).should == v
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
data/lib/astrails/safe.rb
CHANGED
@@ -1,4 +1,9 @@
|
|
1
|
+
require "aws/s3"
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
require 'tempfile'
|
1
5
|
require 'extensions/mktmpdir'
|
6
|
+
|
2
7
|
require 'astrails/safe/tmp_file'
|
3
8
|
|
4
9
|
require 'astrails/safe/config/node'
|
@@ -6,9 +11,15 @@ require 'astrails/safe/config/builder'
|
|
6
11
|
|
7
12
|
require 'astrails/safe/stream'
|
8
13
|
|
14
|
+
require 'astrails/safe/backup'
|
15
|
+
|
16
|
+
require 'astrails/safe/backup'
|
17
|
+
|
9
18
|
require 'astrails/safe/source'
|
10
19
|
require 'astrails/safe/mysqldump'
|
20
|
+
require 'astrails/safe/pgdump'
|
11
21
|
require 'astrails/safe/archive'
|
22
|
+
require 'astrails/safe/svndump'
|
12
23
|
|
13
24
|
require 'astrails/safe/pipe'
|
14
25
|
require 'astrails/safe/gpg'
|
@@ -23,19 +34,25 @@ module Astrails
|
|
23
34
|
module Safe
|
24
35
|
ROOT = File.join(File.dirname(__FILE__), "..", "..")
|
25
36
|
|
26
|
-
def timestamp
|
27
|
-
@timestamp ||= Time.now.strftime("%y%m%d-%H%M")
|
28
|
-
end
|
29
|
-
|
30
37
|
def safe(&block)
|
31
38
|
config = Config::Node.new(&block)
|
32
39
|
#config.dump
|
33
40
|
|
34
|
-
|
35
|
-
Astrails::Safe::
|
41
|
+
|
42
|
+
[[Astrails::Safe::Mysqldump, [:mysqldump, :databases]],
|
43
|
+
[Astrails::Safe::Pgdump, [:pgdump, :databases]],
|
44
|
+
[Astrails::Safe::Archive, [:tar, :archives]],
|
45
|
+
[Astrails::Safe::Svndump, [:svndump, :repos]]
|
46
|
+
].each do |klass, path|
|
47
|
+
if collection = config[*path]
|
48
|
+
collection.each do |name, config|
|
49
|
+
klass.new(name, config).backup.run(config, :gpg, :gzip, :local, :s3)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
36
53
|
|
37
54
|
Astrails::Safe::TmpFile.cleanup
|
38
55
|
end
|
56
|
+
module_function :safe
|
39
57
|
end
|
40
58
|
end
|
41
|
-
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Astrails
|
2
|
+
module Safe
|
3
|
+
class Backup
|
4
|
+
attr_accessor :id, :kind, :filename, :extension, :command, :compressed, :timestamp, :path
|
5
|
+
def initialize(opts = {})
|
6
|
+
opts.each do |k, v|
|
7
|
+
self.send("#{k}=", v)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def run(config, *mods)
|
12
|
+
mods.each do |mod|
|
13
|
+
mod = mod.to_s
|
14
|
+
mod[0] = mod[0..0].upcase
|
15
|
+
Astrails::Safe.const_get(mod).new(config, self).process
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -2,9 +2,9 @@ module Astrails
|
|
2
2
|
module Safe
|
3
3
|
module Config
|
4
4
|
class Builder
|
5
|
-
COLLECTIONS = %w/database archive/
|
6
|
-
ITEMS = %w/s3 key secret bucket path gpg password keep local mysqldump options
|
7
|
-
user host port socket skip_tables tar files exclude filename/
|
5
|
+
COLLECTIONS = %w/database archive repo/
|
6
|
+
ITEMS = %w/s3 key secret bucket path gpg password keep local mysqldump pgdump options
|
7
|
+
user host port socket skip_tables tar files exclude filename svndump repo_path/
|
8
8
|
NAMES = COLLECTIONS + ITEMS
|
9
9
|
def initialize(node)
|
10
10
|
@node = node
|
@@ -27,13 +27,13 @@ module Astrails
|
|
27
27
|
|
28
28
|
# do we have data hash?
|
29
29
|
if data = args.shift
|
30
|
-
|
30
|
+
raise "#{sym}: hash expected: #{data.inspect}" unless data.is_a?(Hash)
|
31
31
|
end
|
32
32
|
|
33
33
|
#puts "#{sym}: args=#{args.inspect}, id_or_value=#{id_or_value}, data=#{data.inspect}, block=#{block.inspect}"
|
34
34
|
|
35
|
-
|
36
|
-
|
35
|
+
raise "#{sym}: unexpected: #{args.inspect}" unless args.empty?
|
36
|
+
raise "#{sym}: missing arguments" unless id_or_value || data || block
|
37
37
|
|
38
38
|
if COLLECTIONS.include?(sym.to_s) && id_or_value
|
39
39
|
data ||= {}
|