astrails-safe 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/lib/astrails/safe/gpg.rb
CHANGED
@@ -2,35 +2,38 @@ module Astrails
|
|
2
2
|
module Safe
|
3
3
|
class Gpg < Pipe
|
4
4
|
|
5
|
-
def compressed?
|
6
|
-
active? || @parent.compressed?
|
7
|
-
end
|
8
|
-
|
9
5
|
protected
|
10
6
|
|
7
|
+
def post_process
|
8
|
+
@backup.compressed = true
|
9
|
+
end
|
10
|
+
|
11
11
|
def pipe
|
12
12
|
if key
|
13
|
-
|
14
|
-
"|gpg -e -r #{key}"
|
13
|
+
"|gpg #{@config[:options]} -e -r #{key}"
|
15
14
|
elsif password
|
16
|
-
"|gpg -c --passphrase-file #{gpg_password_file(password)}"
|
15
|
+
"|gpg #{@config[:options]} -c --passphrase-file #{gpg_password_file(password)}"
|
17
16
|
end
|
18
17
|
end
|
19
18
|
|
20
19
|
def extension
|
21
|
-
".gpg"
|
20
|
+
".gpg"
|
22
21
|
end
|
23
22
|
|
24
23
|
def active?
|
24
|
+
raise RuntimeError, "can't use both gpg password and pubkey" if key && password
|
25
|
+
|
25
26
|
password || key
|
26
27
|
end
|
27
28
|
|
29
|
+
private
|
30
|
+
|
28
31
|
def password
|
29
|
-
@password ||= config[:gpg, :password]
|
32
|
+
@password ||= @config[:gpg, :password]
|
30
33
|
end
|
31
34
|
|
32
35
|
def key
|
33
|
-
@key ||= config[:gpg, :key]
|
36
|
+
@key ||= @config[:gpg, :key]
|
34
37
|
end
|
35
38
|
|
36
39
|
def gpg_password_file(pass)
|
@@ -39,4 +42,4 @@ module Astrails
|
|
39
42
|
end
|
40
43
|
end
|
41
44
|
end
|
42
|
-
end
|
45
|
+
end
|
data/lib/astrails/safe/gzip.rb
CHANGED
@@ -2,24 +2,24 @@ module Astrails
|
|
2
2
|
module Safe
|
3
3
|
class Gzip < Pipe
|
4
4
|
|
5
|
-
def compressed?
|
6
|
-
true
|
7
|
-
end
|
8
|
-
|
9
5
|
protected
|
10
6
|
|
7
|
+
def post_process
|
8
|
+
@backup.compressed = true
|
9
|
+
end
|
10
|
+
|
11
11
|
def pipe
|
12
|
-
"|gzip"
|
12
|
+
"|gzip"
|
13
13
|
end
|
14
14
|
|
15
15
|
def extension
|
16
|
-
".gz"
|
16
|
+
".gz"
|
17
17
|
end
|
18
18
|
|
19
19
|
def active?
|
20
|
-
!@
|
20
|
+
!@backup.compressed
|
21
21
|
end
|
22
22
|
|
23
23
|
end
|
24
24
|
end
|
25
|
-
end
|
25
|
+
end
|
data/lib/astrails/safe/local.rb
CHANGED
@@ -2,45 +2,37 @@ module Astrails
|
|
2
2
|
module Safe
|
3
3
|
class Local < Sink
|
4
4
|
|
5
|
-
def open(&block)
|
6
|
-
return @parent.open(&block) unless active?
|
7
|
-
run
|
8
|
-
File.open(path, &block) unless $DRY_RUN
|
9
|
-
end
|
10
|
-
|
11
5
|
protected
|
12
6
|
|
13
7
|
def active?
|
14
8
|
# S3 can't upload from pipe. it needs to know file size, so we must pass through :local
|
15
|
-
# will change once we add SSH sink
|
9
|
+
# will change once we add SSH/FTP sink
|
16
10
|
true
|
17
11
|
end
|
18
12
|
|
19
13
|
def prefix
|
20
|
-
@prefix ||= File.expand_path(expand(@config[:local, :path] || raise(RuntimeError, "missing :local/:path
|
21
|
-
end
|
22
|
-
|
23
|
-
def command
|
24
|
-
"#{@parent.command} > #{path}"
|
14
|
+
@prefix ||= File.expand_path(expand(@config[:local, :path] || raise(RuntimeError, "missing :local/:path")))
|
25
15
|
end
|
26
16
|
|
27
17
|
def save
|
28
|
-
puts "command: #{command}" if $_VERBOSE
|
18
|
+
puts "command: #{@backup.command}" if $_VERBOSE
|
19
|
+
|
29
20
|
unless $DRY_RUN
|
30
21
|
FileUtils.mkdir_p(prefix) unless File.directory?(prefix)
|
31
|
-
system command
|
22
|
+
system "#{@backup.command}>#{@backup.path = path}"
|
32
23
|
end
|
24
|
+
|
33
25
|
end
|
34
26
|
|
35
27
|
def cleanup
|
36
28
|
return unless keep = @config[:keep, :local]
|
37
29
|
|
38
|
-
base = File.basename(filename).split(".").first
|
30
|
+
base = File.basename(@backup.filename).split(".").first
|
39
31
|
|
40
32
|
pattern = File.join(prefix, "#{base}*")
|
41
33
|
puts "listing files #{pattern.inspect}" if $_VERBOSE
|
42
34
|
files = Dir[pattern] .
|
43
|
-
select{|f| File.file?(f)} .
|
35
|
+
select{|f| File.file?(f) && File.size(f) > 0} .
|
44
36
|
sort
|
45
37
|
|
46
38
|
cleanup_with_limit(files, keep) do |f|
|
@@ -48,7 +40,6 @@ module Astrails
|
|
48
40
|
File.unlink(f) unless $DRY_RUN
|
49
41
|
end
|
50
42
|
end
|
51
|
-
|
52
43
|
end
|
53
44
|
end
|
54
45
|
end
|
@@ -3,7 +3,7 @@ module Astrails
|
|
3
3
|
class Mysqldump < Source
|
4
4
|
|
5
5
|
def command
|
6
|
-
|
6
|
+
"mysqldump --defaults-extra-file=#{mysql_password_file} #{@config[:options]} #{mysql_skip_tables} #{@id}"
|
7
7
|
end
|
8
8
|
|
9
9
|
def extension; '.sql'; end
|
@@ -28,4 +28,4 @@ module Astrails
|
|
28
28
|
|
29
29
|
end
|
30
30
|
end
|
31
|
-
end
|
31
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Astrails
|
2
|
+
module Safe
|
3
|
+
class Pgdump < Source
|
4
|
+
|
5
|
+
def command
|
6
|
+
if @config["password"]
|
7
|
+
ENV['PGPASSWORD'] = @config["password"]
|
8
|
+
else
|
9
|
+
ENV['PGPASSWORD'] = nil
|
10
|
+
end
|
11
|
+
"pg_dump #{postgres_options} #{postgres_username} #{postgres_host} #{postgres_port} #{@id}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def extension; '.sql'; end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def postgres_options
|
19
|
+
@config[:options]
|
20
|
+
end
|
21
|
+
|
22
|
+
def postgres_host
|
23
|
+
@config["host"] && "--host='#{@config["host"]}'"
|
24
|
+
end
|
25
|
+
|
26
|
+
def postgres_port
|
27
|
+
@config["port"] && "--port='#{@config["port"]}'"
|
28
|
+
end
|
29
|
+
|
30
|
+
def postgres_username
|
31
|
+
@config["user"] && "--username='#{@config["user"]}'"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/astrails/safe/pipe.rb
CHANGED
@@ -1,15 +1,13 @@
|
|
1
1
|
module Astrails
|
2
2
|
module Safe
|
3
3
|
class Pipe < Stream
|
4
|
+
def process
|
5
|
+
return unless active?
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
+
@backup.command << pipe
|
8
|
+
@backup.extension << extension
|
9
|
+
post_process
|
7
10
|
end
|
8
|
-
|
9
|
-
def filename
|
10
|
-
"#{@parent.filename}#{extension}"
|
11
|
-
end
|
12
|
-
|
13
11
|
end
|
14
12
|
end
|
15
13
|
end
|
data/lib/astrails/safe/s3.rb
CHANGED
@@ -9,32 +9,30 @@ module Astrails
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def prefix
|
12
|
-
@prefix ||= expand(config[:s3, :path] ||
|
12
|
+
@prefix ||= expand(config[:s3, :path] || config[:local, :path] || ":kind/:id")
|
13
13
|
end
|
14
14
|
|
15
15
|
def save
|
16
|
+
raise RuntimeError, "pipe-streaming not supported for S3." unless @backup.path
|
17
|
+
|
16
18
|
# needed in cleanup even on dry run
|
17
19
|
AWS::S3::Base.establish_connection!(:access_key_id => key, :secret_access_key => secret, :use_ssl => true) unless $LOCAL
|
18
20
|
|
19
|
-
file = @parent.open
|
20
21
|
puts "Uploading #{bucket}:#{path}" if $_VERBOSE || $DRY_RUN
|
21
22
|
unless $DRY_RUN || $LOCAL
|
22
23
|
AWS::S3::Bucket.create(bucket)
|
23
|
-
|
24
|
+
File.open(@backup.path) do |file|
|
25
|
+
AWS::S3::S3Object.store(path, file, bucket)
|
26
|
+
end
|
24
27
|
puts "...done" if $_VERBOSE
|
25
28
|
end
|
26
|
-
file.close if file
|
27
|
-
|
28
29
|
end
|
29
30
|
|
30
31
|
def cleanup
|
31
|
-
|
32
32
|
return if $LOCAL
|
33
33
|
|
34
34
|
return unless keep = @config[:keep, :s3]
|
35
35
|
|
36
|
-
bucket = @config[:s3, :bucket]
|
37
|
-
|
38
36
|
base = File.basename(filename).split(".").first
|
39
37
|
|
40
38
|
puts "listing files in #{bucket}:#{prefix}/#{base}"
|
@@ -52,15 +50,15 @@ module Astrails
|
|
52
50
|
end
|
53
51
|
|
54
52
|
def bucket
|
55
|
-
config[:s3, :bucket]
|
53
|
+
@config[:s3, :bucket]
|
56
54
|
end
|
57
55
|
|
58
56
|
def key
|
59
|
-
config[:s3, :key]
|
57
|
+
@config[:s3, :key]
|
60
58
|
end
|
61
59
|
|
62
60
|
def secret
|
63
|
-
config[:s3, :secret]
|
61
|
+
@config[:s3, :secret]
|
64
62
|
end
|
65
63
|
|
66
64
|
end
|
data/lib/astrails/safe/sink.rb
CHANGED
@@ -2,20 +2,18 @@ module Astrails
|
|
2
2
|
module Safe
|
3
3
|
class Sink < Stream
|
4
4
|
|
5
|
-
def
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
@parent.run
|
11
|
-
end
|
5
|
+
def process
|
6
|
+
return unless active?
|
7
|
+
|
8
|
+
save
|
9
|
+
cleanup
|
12
10
|
end
|
13
11
|
|
14
12
|
protected
|
15
13
|
|
16
14
|
# prefix is defined in subclass
|
17
15
|
def path
|
18
|
-
@path ||= File.join(prefix, filename)
|
16
|
+
@path ||= File.join(prefix, @backup.filename) + @backup.extension
|
19
17
|
end
|
20
18
|
|
21
19
|
# call block on files to be removed (all except for the LAST 'limit' files
|
@@ -26,8 +24,6 @@ module Astrails
|
|
26
24
|
# TODO: validate here
|
27
25
|
to_remove.each(&block)
|
28
26
|
end
|
29
|
-
|
30
27
|
end
|
31
28
|
end
|
32
|
-
end
|
33
|
-
|
29
|
+
end
|
data/lib/astrails/safe/source.rb
CHANGED
@@ -2,27 +2,42 @@ module Astrails
|
|
2
2
|
module Safe
|
3
3
|
class Source < Stream
|
4
4
|
|
5
|
+
attr_accessor :id
|
5
6
|
def initialize(id, config)
|
6
|
-
@id, @config = id, config
|
7
|
+
@id, @config = id.to_s, config
|
8
|
+
end
|
9
|
+
|
10
|
+
def timestamp
|
11
|
+
Time.now.strftime("%y%m%d-%H%M")
|
12
|
+
end
|
13
|
+
|
14
|
+
def kind
|
15
|
+
self.class.human_name
|
7
16
|
end
|
8
17
|
|
9
18
|
def filename
|
10
|
-
@filename ||= expand(":kind-:id.:timestamp
|
19
|
+
@filename ||= expand(":kind-:id.:timestamp")
|
11
20
|
end
|
12
21
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
def backup
|
23
|
+
return @backup if @backup
|
24
|
+
@backup = Backup.new(
|
25
|
+
:id => @id,
|
26
|
+
:kind => kind,
|
27
|
+
:extension => extension,
|
28
|
+
:command => command,
|
29
|
+
:timestamp => timestamp
|
30
|
+
)
|
31
|
+
# can't do this in the initializer hash above since
|
32
|
+
# filename() calls expand() which requires @backup
|
33
|
+
@backup.filename = filename
|
34
|
+
@backup
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
def self.human_name
|
40
|
+
name.split('::').last.downcase
|
26
41
|
end
|
27
42
|
|
28
43
|
end
|
data/lib/astrails/safe/stream.rb
CHANGED
@@ -2,44 +2,18 @@ module Astrails
|
|
2
2
|
module Safe
|
3
3
|
class Stream
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
def id
|
10
|
-
@id ||= @parent.id
|
11
|
-
end
|
12
|
-
|
13
|
-
def config
|
14
|
-
@config ||= @parent.config
|
15
|
-
end
|
16
|
-
|
17
|
-
def filename
|
18
|
-
@parent.filename
|
19
|
-
end
|
20
|
-
|
21
|
-
def compressed?
|
22
|
-
@parent && @parent.compressed?
|
23
|
-
end
|
24
|
-
|
25
|
-
protected
|
26
|
-
|
27
|
-
def self.human_name
|
28
|
-
name.split('::').last.downcase
|
29
|
-
end
|
30
|
-
|
31
|
-
def kind
|
32
|
-
@parent ? @parent.kind : self.class.human_name
|
5
|
+
attr_accessor :config, :backup
|
6
|
+
def initialize(config, backup)
|
7
|
+
@config, @backup = config, backup
|
33
8
|
end
|
34
9
|
|
35
10
|
def expand(path)
|
36
11
|
path .
|
37
|
-
|
38
|
-
|
39
|
-
|
12
|
+
gsub(/:kind\b/, @backup.kind.to_s) .
|
13
|
+
gsub(/:id\b/, @backup.id.to_s) .
|
14
|
+
gsub(/:timestamp\b/, @backup.timestamp)
|
40
15
|
end
|
41
16
|
|
42
17
|
end
|
43
18
|
end
|
44
|
-
end
|
45
|
-
|
19
|
+
end
|