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.
@@ -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 and Amazon S3 for storage
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 S3
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 30
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 Amazon S3 (with encryption)}
9
- gem.description = "Simple tool to backup databases (MySQL and PostgreSQL) and filesystem locally or to Amazon S3 (with optional encryption)"
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.", "Mark Mansour"]
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
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 0
3
3
  :minor: 2
4
- :patch: 6
4
+ :patch: 7
@@ -2,8 +2,8 @@
2
2
 
3
3
  require 'rubygems'
4
4
 
5
- #require 'ruby-debug'
6
- #$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
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
- before(:each) do
106
- @gpg = gpg(:gpg => {:key => "foo", :options => "GPG-OPT"}, :options => "OPT")
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
- dont_allow(@gpg).gpg_password_file(anything)
111
- @gpg.send(:pipe)
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
- @gpg.send(:pipe).should == "|gpg GPG-OPT -e -r foo"
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
- before(:each) do
121
- @gpg = gpg(:gpg => {:password => "bar", :options => "GPG-OPT"}, :options => "OPT")
122
- stub(@gpg).gpg_password_file(anything) {"pass-file"}
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
- @gpg.send(:pipe).should == "|gpg GPG-OPT -c --passphrase-file pass-file"
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 = pwd
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
@@ -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)
@@ -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
- "|gpg #{@config[:gpg, :options]} -e -r #{key}"
14
+ "|#{command} #{@config[:gpg, :options]} -e -r #{key}"
14
15
  elsif password
15
- "|gpg #{@config[:gpg,:options]} -c --passphrase-file #{gpg_password_file(password)}"
16
+ "|#{command} #{@config[:gpg, :options]} -c --passphrase-file #{gpg_password_file(password)}"
16
17
  end
17
18
  end
18
19
 
@@ -15,7 +15,8 @@ module Astrails
15
15
  file.puts "[mysqldump]"
16
16
  %w/user password socket host port/.each do |k|
17
17
  v = @config[k]
18
- file.puts "#{k} = #{v}" if v
18
+ # values are quoted if needed
19
+ file.puts "#{k} = #{v.inspect}" if v
19
20
  end
20
21
  end
21
22
  end
@@ -41,7 +41,6 @@ module Astrails
41
41
 
42
42
  return unless keep = @config[:keep, :s3]
43
43
 
44
-
45
44
  puts "listing files: #{bucket}:#{base}*" if $_VERBOSE
46
45
  files = AWS::S3::Bucket.objects(bucket, :prefix => base, :max_keys => keep * 2)
47
46
  puts files.collect {|x| x.key} if $_VERBOSE
@@ -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.6
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: 2009-12-21 00:00:00 +02:00
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: Simple tool to backup databases (MySQL and PostgreSQL) and filesystem locally or to Amazon S3 (with optional encryption)
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 Amazon S3 (with encryption)
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