astrails-safe 0.2.6 → 0.2.7
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +30 -5
- data/Rakefile +8 -3
- data/VERSION.yml +1 -1
- data/bin/astrails-safe +3 -3
- data/examples/unit/gpg_example.rb +19 -9
- data/examples/unit/mysqldump_example.rb +8 -8
- data/lib/astrails/safe.rb +3 -1
- data/lib/astrails/safe/cloudfiles.rb +64 -0
- data/lib/astrails/safe/config/builder.rb +1 -1
- data/lib/astrails/safe/gpg.rb +3 -2
- data/lib/astrails/safe/mysqldump.rb +2 -1
- data/lib/astrails/safe/s3.rb +0 -1
- data/templates/script.rb +20 -1
- metadata +19 -5
data/README.markdown
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
astrails-safe
|
2
2
|
=============
|
3
3
|
|
4
|
-
Simple database and filesystem backups with S3 support (with optional encryption)
|
4
|
+
Simple database and filesystem backups with S3 and Rackspace Cloud Files support (with optional encryption)
|
5
5
|
|
6
6
|
Home: http://blog.astrails.com/astrails-safe
|
7
7
|
|
@@ -15,8 +15,8 @@ We needed a backup solution that will satisfy the following requirements:
|
|
15
15
|
* support for simple ‘tar’ backups of directories (with includes/excludes)
|
16
16
|
* support for simple mysqldump of mysql databases
|
17
17
|
* support for symmetric or public key encryption
|
18
|
-
* support for local filesystem
|
19
|
-
* support for backup rotation. we don’t want backups filling all the diskspace or cost a fortune on S3
|
18
|
+
* support for local filesystem, Amazon S3, and Rackspace Cloud Files for storage
|
19
|
+
* support for backup rotation. we don’t want backups filling all the diskspace or cost a fortune on S3 or Cloud Files
|
20
20
|
|
21
21
|
And since we didn't find any, we wrote our own :)
|
22
22
|
|
@@ -29,6 +29,10 @@ The following functionality was contributed by astrails-safe users:
|
|
29
29
|
* Subversion dump using svndump (by Richard Luther <richard.luther@gmail.com>)
|
30
30
|
* SFTP remote storage (by Adam <adam@mediadrive.ca>)
|
31
31
|
* benchmarking output (By Neer)
|
32
|
+
* README fixes (by Bobby Wilson)
|
33
|
+
* improved config file parsing (by Fedor Kocherga <fkocherga@gmail.com>)
|
34
|
+
* mysql password file quoting (by Jonathan Sutherland <jonathan.sutherland@gmail.com>)
|
35
|
+
* Rackspace Cloud Files support (by H. Wade Minter <minter@lunenburg.org>)
|
32
36
|
|
33
37
|
Thanks to all :)
|
34
38
|
|
@@ -51,7 +55,7 @@ Usage
|
|
51
55
|
-h, --help This help screen
|
52
56
|
-v, --verbose be verbose, duh!
|
53
57
|
-n, --dry-run just pretend, don't do anything.
|
54
|
-
-L, --local skip
|
58
|
+
-L, --local skip remote storage, only do local backups
|
55
59
|
|
56
60
|
Note: CONFIG_FILE will be created from template if missing
|
57
61
|
|
@@ -62,6 +66,15 @@ If you want to encrypt your backups you have 2 options:
|
|
62
66
|
* use simple password encryption
|
63
67
|
* use GPG public key encryption
|
64
68
|
|
69
|
+
> IMPORTANT: some gpg installations automatically set 'use-agent' option in the default
|
70
|
+
> configuration file that is created when you run gpg for the first time. This will cause
|
71
|
+
> gpg to fail on the 2nd run if you don't have the agent running. The result is that
|
72
|
+
> 'astrails-safe' will work ONCE when you manually test it and then fail on any subsequent run.
|
73
|
+
> The solution is to remove the 'use-agent' from the config file (usually /root/.gnupg/gpg.conf)
|
74
|
+
> To mitigate this problem for the gpg 1.x series '--no-use-agent' option is added by defaults
|
75
|
+
> to the autogenerated config file, but for gpg2 is doesn't work. as the manpage says it:
|
76
|
+
> "This is dummy option. gpg2 always requires the agent." :(
|
77
|
+
|
65
78
|
For simple password, just add password entry in gpg section.
|
66
79
|
For public key encryption you will need to create a public/secret keypair.
|
67
80
|
|
@@ -133,6 +146,14 @@ Example configuration
|
|
133
146
|
path "servers/alpha/:kind/:id"
|
134
147
|
end
|
135
148
|
|
149
|
+
cloudfiles do
|
150
|
+
username "..........."
|
151
|
+
api_key "................................."
|
152
|
+
container "safe_backup"
|
153
|
+
path ":kind/" # this is default
|
154
|
+
service_net false
|
155
|
+
end
|
156
|
+
|
136
157
|
sftp do
|
137
158
|
host "sftp.astrails.com"
|
138
159
|
user "astrails"
|
@@ -141,6 +162,8 @@ Example configuration
|
|
141
162
|
end
|
142
163
|
|
143
164
|
gpg do
|
165
|
+
command "/usr/local/bin/gpg"
|
166
|
+
options "--no-use-agent"
|
144
167
|
# symmetric encryption key
|
145
168
|
# password "qwe"
|
146
169
|
|
@@ -150,7 +173,9 @@ Example configuration
|
|
150
173
|
|
151
174
|
keep do
|
152
175
|
local 20
|
153
|
-
s3
|
176
|
+
s3 100
|
177
|
+
cloudfiles 100
|
178
|
+
sftp 100
|
154
179
|
end
|
155
180
|
|
156
181
|
mysqldump do
|
data/Rakefile
CHANGED
@@ -5,14 +5,19 @@ begin
|
|
5
5
|
require 'jeweler'
|
6
6
|
Jeweler::Tasks.new do |gem|
|
7
7
|
gem.name = "astrails-safe"
|
8
|
-
gem.summary = %Q{Backup filesystem and databases (MySQL and PostgreSQL) to
|
9
|
-
gem.description =
|
8
|
+
gem.summary = %Q{Backup filesystem and databases (MySQL and PostgreSQL) locally or to a remote server/service (with encryption)}
|
9
|
+
gem.description = <<-DESC
|
10
|
+
Astrails-Safe is a simple tool to backup databases (MySQL and PostgreSQL), Subversion repositories (with svndump) and just files.
|
11
|
+
Backups can be stored locally or remotely and can be enctypted.
|
12
|
+
Remote storage is supported on Amazon S3, Rackspace Cloud Files, or just plain SFTP.
|
13
|
+
DESC
|
10
14
|
gem.email = "we@astrails.com"
|
11
15
|
gem.homepage = "http://blog.astrails.com/astrails-safe"
|
12
|
-
gem.authors = ["Astrails Ltd."
|
16
|
+
gem.authors = ["Astrails Ltd."]
|
13
17
|
gem.files = FileList["[A-Z]*.*", "{bin,examples,generators,lib,rails,spec,test,templates}/**/*", 'Rakefile', 'LICENSE*']
|
14
18
|
|
15
19
|
gem.add_dependency("aws-s3")
|
20
|
+
gem.add_dependency("cloudfiles")
|
16
21
|
gem.add_dependency("net-sftp")
|
17
22
|
|
18
23
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
data/VERSION.yml
CHANGED
data/bin/astrails-safe
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
4
|
|
5
|
-
#require 'ruby-debug'
|
6
|
-
|
5
|
+
# require 'ruby-debug'
|
6
|
+
$:.unshift File.expand_path("../../lib", __FILE__)
|
7
7
|
|
8
8
|
require 'astrails/safe'
|
9
9
|
include Astrails::Safe
|
@@ -20,7 +20,7 @@ Options:
|
|
20
20
|
-h, --help This help screen
|
21
21
|
-v, --verbose be verbose, duh!
|
22
22
|
-n, --dry-run just pretend, don't do anything.
|
23
|
-
-L, --local skip S3
|
23
|
+
-L, --local skip S3 and Cloud Files
|
24
24
|
|
25
25
|
Note: config file will be created from template if missing
|
26
26
|
END
|
@@ -102,28 +102,38 @@ describe Astrails::Safe::Gpg do
|
|
102
102
|
describe :pipe do
|
103
103
|
|
104
104
|
describe "with key" do
|
105
|
-
|
106
|
-
|
105
|
+
def kgpg(extra={})
|
106
|
+
gpg({:gpg => {:key => "foo", :options => "GPG-OPT"}.merge(extra), :options => "OPT"})
|
107
107
|
end
|
108
108
|
|
109
109
|
it "should not call gpg_password_file" do
|
110
|
-
|
111
|
-
|
110
|
+
g = kgpg
|
111
|
+
dont_allow(g).gpg_password_file(anything)
|
112
|
+
g.send(:pipe)
|
112
113
|
end
|
113
114
|
|
114
115
|
it "should use '-r' and :options" do
|
115
|
-
|
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"
|
116
121
|
end
|
117
122
|
end
|
118
123
|
|
119
124
|
describe "with password" do
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
123
129
|
end
|
124
130
|
|
125
131
|
it "should use '--passphrase-file' and :options" do
|
126
|
-
|
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"
|
127
137
|
end
|
128
138
|
end
|
129
139
|
end
|
@@ -2,7 +2,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../example_helper')
|
|
2
2
|
|
3
3
|
describe Astrails::Safe::Mysqldump do
|
4
4
|
|
5
|
-
def def_config
|
5
|
+
def def_config(extra = {})
|
6
6
|
{
|
7
7
|
:options => "OPTS",
|
8
8
|
:user => "User",
|
@@ -11,7 +11,7 @@ describe Astrails::Safe::Mysqldump do
|
|
11
11
|
:port => 7777,
|
12
12
|
:socket => "socket",
|
13
13
|
:skip_tables => [:bar, :baz]
|
14
|
-
}
|
14
|
+
}.merge(extra)
|
15
15
|
end
|
16
16
|
|
17
17
|
def mysqldump(id = :foo, config = def_config)
|
@@ -66,16 +66,16 @@ describe Astrails::Safe::Mysqldump do
|
|
66
66
|
end
|
67
67
|
|
68
68
|
describe :mysql_password_file do
|
69
|
-
it "should create passwords file" do
|
70
|
-
m = mysqldump
|
69
|
+
it "should create passwords file with quoted values" do
|
70
|
+
m = mysqldump(:foo, def_config(:password => '#qwe"asd\'zxc'))
|
71
71
|
file = m.send(:mysql_password_file)
|
72
72
|
File.exists?(file).should == true
|
73
73
|
File.read(file).should == <<-PWD
|
74
74
|
[mysqldump]
|
75
|
-
user = User
|
76
|
-
password =
|
77
|
-
socket = socket
|
78
|
-
host = localhost
|
75
|
+
user = "User"
|
76
|
+
password = "#qwe\\"asd'zxc"
|
77
|
+
socket = "socket"
|
78
|
+
host = "localhost"
|
79
79
|
port = 7777
|
80
80
|
PWD
|
81
81
|
end
|
data/lib/astrails/safe.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "aws/s3"
|
2
|
+
require "cloudfiles"
|
2
3
|
require 'net/sftp'
|
3
4
|
require 'fileutils'
|
4
5
|
require 'benchmark'
|
@@ -30,6 +31,7 @@ require 'astrails/safe/gzip'
|
|
30
31
|
require 'astrails/safe/sink'
|
31
32
|
require 'astrails/safe/local'
|
32
33
|
require 'astrails/safe/s3'
|
34
|
+
require 'astrails/safe/cloudfiles'
|
33
35
|
require 'astrails/safe/sftp'
|
34
36
|
|
35
37
|
module Astrails
|
@@ -48,7 +50,7 @@ module Astrails
|
|
48
50
|
].each do |klass, path|
|
49
51
|
if collection = config[*path]
|
50
52
|
collection.each do |name, config|
|
51
|
-
klass.new(name, config).backup.run(config, :gpg, :gzip, :local, :s3, :sftp)
|
53
|
+
klass.new(name, config).backup.run(config, :gpg, :gzip, :local, :s3, :cloudfiles, :sftp)
|
52
54
|
end
|
53
55
|
end
|
54
56
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Astrails
|
2
|
+
module Safe
|
3
|
+
class Cloudfiles < Sink
|
4
|
+
|
5
|
+
protected
|
6
|
+
|
7
|
+
def active?
|
8
|
+
container && user && api_key
|
9
|
+
end
|
10
|
+
|
11
|
+
def path
|
12
|
+
@path ||= expand(config[:cloudfiles, :path] || config[:local, :path] || ":kind/:id")
|
13
|
+
end
|
14
|
+
|
15
|
+
def save
|
16
|
+
raise RuntimeError, "pipe-streaming not supported for S3." unless @backup.path
|
17
|
+
|
18
|
+
# needed in cleanup even on dry run
|
19
|
+
cf = CloudFiles::Connection.new(user, api_key, true, service_net) unless $LOCAL
|
20
|
+
puts "Uploading #{container}:#{full_path} from #{@backup.path}" if $_VERBOSE || $DRY_RUN
|
21
|
+
unless $DRY_RUN || $LOCAL
|
22
|
+
benchmark = Benchmark.realtime do
|
23
|
+
cf_container = cf.create_container(container)
|
24
|
+
o = cf_container.create_object(full_path,true)
|
25
|
+
o.write(open(@backup.path))
|
26
|
+
end
|
27
|
+
puts "...done" if $_VERBOSE
|
28
|
+
puts("Upload took " + sprintf("%.2f", benchmark) + " second(s).") if $_VERBOSE
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def cleanup
|
33
|
+
return if $LOCAL
|
34
|
+
|
35
|
+
return unless keep = @config[:keep, :cloudfiles]
|
36
|
+
|
37
|
+
puts "listing files: #{container}:#{base}*" if $_VERBOSE
|
38
|
+
cf = CloudFiles::Connection.new(user, api_key, true, service_net) unless $LOCAL
|
39
|
+
files = cf.container(container).objects(:prefix => base)
|
40
|
+
|
41
|
+
cleanup_with_limit(files, keep) do |f|
|
42
|
+
puts "removing Cloud File #{container}:#{f}" if $DRY_RUN || $_VERBOSE
|
43
|
+
cf.container(container).delete_object(f) unless $DRY_RUN || $LOCAL
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def container
|
48
|
+
@config[:cloudfiles, :container]
|
49
|
+
end
|
50
|
+
|
51
|
+
def user
|
52
|
+
@config[:cloudfiles, :user]
|
53
|
+
end
|
54
|
+
|
55
|
+
def api_key
|
56
|
+
@config[:cloudfiles, :api_key]
|
57
|
+
end
|
58
|
+
|
59
|
+
def service_net
|
60
|
+
@config[:cloudfiles, :service_net] || false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -3,7 +3,7 @@ module Astrails
|
|
3
3
|
module Config
|
4
4
|
class Builder
|
5
5
|
COLLECTIONS = %w/database archive repo/
|
6
|
-
ITEMS = %w/s3 key secret bucket path gpg password keep local mysqldump pgdump options
|
6
|
+
ITEMS = %w/s3 cloudfiles key secret bucket api_key container service_net path gpg password keep local mysqldump pgdump command options
|
7
7
|
user host port socket skip_tables tar files exclude filename svndump repo_path sftp/
|
8
8
|
NAMES = COLLECTIONS + ITEMS
|
9
9
|
def initialize(node)
|
data/lib/astrails/safe/gpg.rb
CHANGED
@@ -9,10 +9,11 @@ module Astrails
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def pipe
|
12
|
+
command = @config[:gpg, :command] || 'gpg'
|
12
13
|
if key
|
13
|
-
"
|
14
|
+
"|#{command} #{@config[:gpg, :options]} -e -r #{key}"
|
14
15
|
elsif password
|
15
|
-
"
|
16
|
+
"|#{command} #{@config[:gpg, :options]} -c --passphrase-file #{gpg_password_file(password)}"
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
data/lib/astrails/safe/s3.rb
CHANGED
data/templates/script.rb
CHANGED
@@ -12,7 +12,6 @@ safe do
|
|
12
12
|
|
13
13
|
## uncomment to enable uploads to Amazon S3
|
14
14
|
## Amazon S3 auth (optional)
|
15
|
-
## don't forget to add :s3 to the 'store' list
|
16
15
|
# s3 do
|
17
16
|
# key YOUR_S3_KEY
|
18
17
|
# secret YOUR_S3_SECRET
|
@@ -24,6 +23,20 @@ safe do
|
|
24
23
|
## alternative style:
|
25
24
|
# s3 :key => YOUR_S3_KEY, :secret => YOUR_S3_SECRET, :bucket => S3_BUCKET, :path => ":kind/"
|
26
25
|
|
26
|
+
## uncomment to enable uploads to Rackspace Cloud Files
|
27
|
+
## http://www.rackspacecloud.com/cloud_hosting_products/files
|
28
|
+
## Rackspace auth (optional)
|
29
|
+
# cloudfiles do
|
30
|
+
# user "YOUR_RACKSPACE_CLOUD_USERNAME"
|
31
|
+
# api_key "YOUR_RACKSPACE_API_KEY"
|
32
|
+
# container "YOUR_CONTAINER_NAME"
|
33
|
+
# # path for uploads to Cloud Files, supports same substitution like :local/:path
|
34
|
+
# path ":kind/" # this is default
|
35
|
+
# # If you are running the backup from a system within the Rackspace/Slicehost network and would like
|
36
|
+
# # to back up over the private (unbilled) service net, set this value to true.
|
37
|
+
# # service_net true
|
38
|
+
# end
|
39
|
+
|
27
40
|
## uncomment to enable uploads via SFTP
|
28
41
|
# sftp do
|
29
42
|
# host "YOUR_REMOTE_HOSTNAME"
|
@@ -36,6 +49,12 @@ safe do
|
|
36
49
|
## uncomment to enable GPG encryption.
|
37
50
|
## Note: you can use public 'key' or symmetric password but not both!
|
38
51
|
# gpg do
|
52
|
+
# # you can specify your own gpg executable with the 'command' options
|
53
|
+
# # this can be useful for example to choose b/w gpg and gpg2 if both are installed
|
54
|
+
# # some gpg installations will automatically set 'use-agent' option in the
|
55
|
+
# # config file on the 1st run. see README for more details
|
56
|
+
# options "--no-use-agent"
|
57
|
+
# # command "/usr/local/bin/gpg"
|
39
58
|
# # key "backup@astrails.com"
|
40
59
|
# password "astrails"
|
41
60
|
# end
|
metadata
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: astrails-safe
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Astrails Ltd.
|
8
|
-
- Mark Mansour
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
11
|
|
13
|
-
date:
|
12
|
+
date: 2010-01-21 00:00:00 +02:00
|
14
13
|
default_executable: astrails-safe
|
15
14
|
dependencies:
|
16
15
|
- !ruby/object:Gem::Dependency
|
@@ -23,6 +22,16 @@ dependencies:
|
|
23
22
|
- !ruby/object:Gem::Version
|
24
23
|
version: "0"
|
25
24
|
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: cloudfiles
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
26
35
|
- !ruby/object:Gem::Dependency
|
27
36
|
name: net-sftp
|
28
37
|
type: :runtime
|
@@ -33,7 +42,11 @@ dependencies:
|
|
33
42
|
- !ruby/object:Gem::Version
|
34
43
|
version: "0"
|
35
44
|
version:
|
36
|
-
description:
|
45
|
+
description: |
|
46
|
+
Astrails-Safe is a simple tool to backup databases (MySQL and PostgreSQL), Subversion repositories (with svndump) and just files.
|
47
|
+
Backups can be stored locally or remotely and can be enctypted.
|
48
|
+
Remote storage is supported on Amazon S3, Rackspace Cloud Files, or just plain SFTP.
|
49
|
+
|
37
50
|
email: we@astrails.com
|
38
51
|
executables:
|
39
52
|
- astrails-safe
|
@@ -63,6 +76,7 @@ files:
|
|
63
76
|
- lib/astrails/safe.rb
|
64
77
|
- lib/astrails/safe/archive.rb
|
65
78
|
- lib/astrails/safe/backup.rb
|
79
|
+
- lib/astrails/safe/cloudfiles.rb
|
66
80
|
- lib/astrails/safe/config/builder.rb
|
67
81
|
- lib/astrails/safe/config/node.rb
|
68
82
|
- lib/astrails/safe/gpg.rb
|
@@ -107,7 +121,7 @@ rubyforge_project:
|
|
107
121
|
rubygems_version: 1.3.5
|
108
122
|
signing_key:
|
109
123
|
specification_version: 3
|
110
|
-
summary: Backup filesystem and databases (MySQL and PostgreSQL) to
|
124
|
+
summary: Backup filesystem and databases (MySQL and PostgreSQL) locally or to a remote server/service (with encryption)
|
111
125
|
test_files:
|
112
126
|
- examples/example_helper.rb
|
113
127
|
- examples/integration/archive_integration_example.rb
|