backup 3.6.0 → 3.7.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.
- checksums.yaml +4 -4
- data/README.md +2 -0
- data/lib/backup.rb +14 -4
- data/lib/backup/archive.rb +3 -2
- data/lib/backup/cleaner.rb +4 -2
- data/lib/backup/cli.rb +7 -5
- data/lib/backup/cloud_io/base.rb +41 -0
- data/lib/backup/cloud_io/cloud_files.rb +296 -0
- data/lib/backup/cloud_io/s3.rb +252 -0
- data/lib/backup/compressor/gzip.rb +2 -1
- data/lib/backup/config.rb +13 -5
- data/lib/backup/configuration.rb +1 -1
- data/lib/backup/configuration/helpers.rb +3 -1
- data/lib/backup/database/base.rb +3 -1
- data/lib/backup/database/mongodb.rb +2 -2
- data/lib/backup/database/mysql.rb +2 -2
- data/lib/backup/database/postgresql.rb +12 -2
- data/lib/backup/database/redis.rb +3 -2
- data/lib/backup/encryptor/gpg.rb +8 -10
- data/lib/backup/errors.rb +39 -70
- data/lib/backup/logger.rb +7 -2
- data/lib/backup/logger/fog_adapter.rb +30 -0
- data/lib/backup/model.rb +32 -14
- data/lib/backup/notifier/base.rb +4 -3
- data/lib/backup/notifier/campfire.rb +0 -1
- data/lib/backup/notifier/http_post.rb +122 -0
- data/lib/backup/notifier/mail.rb +38 -0
- data/lib/backup/notifier/nagios.rb +69 -0
- data/lib/backup/notifier/prowl.rb +0 -1
- data/lib/backup/notifier/pushover.rb +0 -1
- data/lib/backup/package.rb +5 -0
- data/lib/backup/packager.rb +3 -2
- data/lib/backup/pipeline.rb +4 -2
- data/lib/backup/storage/base.rb +2 -1
- data/lib/backup/storage/cloud_files.rb +151 -0
- data/lib/backup/storage/cycler.rb +4 -2
- data/lib/backup/storage/dropbox.rb +20 -16
- data/lib/backup/storage/ftp.rb +1 -2
- data/lib/backup/storage/local.rb +3 -3
- data/lib/backup/storage/ninefold.rb +3 -4
- data/lib/backup/storage/rsync.rb +1 -2
- data/lib/backup/storage/s3.rb +49 -158
- data/lib/backup/storage/scp.rb +3 -4
- data/lib/backup/storage/sftp.rb +1 -2
- data/lib/backup/syncer/base.rb +0 -1
- data/lib/backup/syncer/cloud/base.rb +129 -208
- data/lib/backup/syncer/cloud/cloud_files.rb +56 -41
- data/lib/backup/syncer/cloud/local_file.rb +93 -0
- data/lib/backup/syncer/cloud/s3.rb +78 -31
- data/lib/backup/syncer/rsync/base.rb +7 -0
- data/lib/backup/syncer/rsync/local.rb +0 -5
- data/lib/backup/syncer/rsync/push.rb +1 -2
- data/lib/backup/utilities.rb +18 -15
- data/lib/backup/version.rb +1 -1
- data/templates/cli/notifier/http_post +35 -0
- data/templates/cli/notifier/nagios +13 -0
- data/templates/cli/storage/cloud_files +8 -17
- data/templates/cli/storage/s3 +3 -10
- data/templates/cli/syncer/cloud_files +3 -31
- data/templates/cli/syncer/s3 +3 -27
- data/templates/notifier/mail/failure.erb +6 -1
- data/templates/notifier/mail/success.erb +6 -1
- data/templates/notifier/mail/warning.erb +6 -1
- metadata +37 -42
- data/lib/backup/storage/cloudfiles.rb +0 -68
@@ -0,0 +1,122 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Backup
|
5
|
+
module Notifier
|
6
|
+
class HttpPost < Base
|
7
|
+
|
8
|
+
##
|
9
|
+
# URI to post notification to.
|
10
|
+
#
|
11
|
+
# URI scheme may be `http` or `https`.
|
12
|
+
#
|
13
|
+
# If Basic Authentication is needed, supply the `user:password` in the URI.
|
14
|
+
# e.g. 'https://user:pass@www.example.com/path'
|
15
|
+
#
|
16
|
+
# Port may also be supplied.
|
17
|
+
# e.g. 'http://www.example.com:8080/path'
|
18
|
+
attr_accessor :uri
|
19
|
+
|
20
|
+
##
|
21
|
+
# Hash of additional HTTP headers to send.
|
22
|
+
#
|
23
|
+
# This notifier sets the following headers:
|
24
|
+
# { 'User-Agent' => "Backup/#{ Backup::VERSION }",
|
25
|
+
# 'Content-Type' => 'x-www-form-urlencoded' }
|
26
|
+
#
|
27
|
+
# 'Content-Type' may not be changed.
|
28
|
+
# 'User-Agent' may be overridden or omitted by setting it to +nil+.
|
29
|
+
# e.g. { 'Authorization' => 'my_auth_info', 'User-Agent' => nil }
|
30
|
+
attr_accessor :headers
|
31
|
+
|
32
|
+
##
|
33
|
+
# Hash of additional POST parameters to send.
|
34
|
+
#
|
35
|
+
# This notifier will set two parameters:
|
36
|
+
# { 'status' => 'success|warning|failure',
|
37
|
+
# 'message' => '[Backup::(Success|Warning|Failure)] label (trigger)' }
|
38
|
+
#
|
39
|
+
# 'status' may not be changed.
|
40
|
+
# 'message' may be overridden or omitted by setting a +nil+ value.
|
41
|
+
# e.g. { 'auth_token' => 'my_token', 'message' => nil }
|
42
|
+
attr_accessor :params
|
43
|
+
|
44
|
+
##
|
45
|
+
# Successful HTTP Status Code(s) that should be returned.
|
46
|
+
#
|
47
|
+
# This may be a single code or an Array of acceptable codes.
|
48
|
+
# e.g. [200, 201, 204]
|
49
|
+
#
|
50
|
+
# If any other response code is returned, the request will be retried
|
51
|
+
# using `max_retries` and `retry_waitsec`.
|
52
|
+
#
|
53
|
+
# Default: 200
|
54
|
+
attr_accessor :success_codes
|
55
|
+
|
56
|
+
##
|
57
|
+
# Verify the server's certificate when using SSL.
|
58
|
+
#
|
59
|
+
# This will default to +true+ for most systems.
|
60
|
+
# It may be forced by setting to +true+, or disabled by setting to +false+.
|
61
|
+
attr_accessor :ssl_verify_peer
|
62
|
+
|
63
|
+
##
|
64
|
+
# Path to a +cacert.pem+ file to use for +ssl_verify_peer+.
|
65
|
+
#
|
66
|
+
# This is provided (via Excon), but may be specified if needed.
|
67
|
+
attr_accessor :ssl_ca_file
|
68
|
+
|
69
|
+
def initialize(model, &block)
|
70
|
+
super
|
71
|
+
instance_eval(&block) if block_given?
|
72
|
+
|
73
|
+
@headers ||= {}
|
74
|
+
@params ||= {}
|
75
|
+
@success_codes ||= 200
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
##
|
81
|
+
# Notify the user of the backup operation results.
|
82
|
+
#
|
83
|
+
# `status` indicates one of the following:
|
84
|
+
#
|
85
|
+
# `:success`
|
86
|
+
# : The backup completed successfully.
|
87
|
+
# : Notification will be sent if `on_success` is `true`.
|
88
|
+
#
|
89
|
+
# `:warning`
|
90
|
+
# : The backup completed successfully, but warnings were logged.
|
91
|
+
# : Notification will be sent if `on_warning` or `on_success` is `true`.
|
92
|
+
#
|
93
|
+
# `:failure`
|
94
|
+
# : The backup operation failed.
|
95
|
+
# : Notification will be sent if `on_warning` or `on_success` is `true`.
|
96
|
+
#
|
97
|
+
def notify!(status)
|
98
|
+
tag = case status
|
99
|
+
when :success then '[Backup::Success]'
|
100
|
+
when :failure then '[Backup::Failure]'
|
101
|
+
when :warning then '[Backup::Warning]'
|
102
|
+
end
|
103
|
+
message = "#{ tag } #{ model.label } (#{ model.trigger })"
|
104
|
+
|
105
|
+
opts = {
|
106
|
+
:headers => { 'User-Agent' => "Backup/#{ VERSION }" }.
|
107
|
+
merge(headers).reject {|k,v| v.nil? }.
|
108
|
+
merge('Content-Type' => 'application/x-www-form-urlencoded'),
|
109
|
+
:body => encode_www_form({ 'message' => message }.
|
110
|
+
merge(params).reject {|k,v| v.nil? }.
|
111
|
+
merge('status' => status.to_s)),
|
112
|
+
:expects => success_codes # raise error if unsuccessful
|
113
|
+
}
|
114
|
+
opts.merge!(:ssl_verify_peer => ssl_verify_peer) unless ssl_verify_peer.nil?
|
115
|
+
opts.merge!(:ssl_ca_file => ssl_ca_file) if ssl_ca_file
|
116
|
+
|
117
|
+
Excon.post(uri, opts)
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/lib/backup/notifier/mail.rb
CHANGED
@@ -240,3 +240,41 @@ module Backup
|
|
240
240
|
end
|
241
241
|
end
|
242
242
|
end
|
243
|
+
|
244
|
+
# Patch mail v2.5.4 Exim delivery method
|
245
|
+
# https://github.com/meskyanichi/backup/issues/446
|
246
|
+
# https://github.com/mikel/mail/pull/546
|
247
|
+
module Mail
|
248
|
+
class Exim
|
249
|
+
def self.call(path, arguments, destinations, encoded_message)
|
250
|
+
popen "#{path} #{arguments}" do |io|
|
251
|
+
io.puts encoded_message.to_lf
|
252
|
+
io.flush
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# Patch mail v2.5.4 for ruby-1.8.7
|
259
|
+
# https://github.com/mikel/mail/issues/548
|
260
|
+
# https://github.com/mikel/mail/commit/c7318a6c03c1ecb3f574ccd2e3f06778687d1d15
|
261
|
+
if RUBY_VERSION < '1.9'
|
262
|
+
module Mail
|
263
|
+
class SMTP
|
264
|
+
private
|
265
|
+
def ssl_context
|
266
|
+
openssl_verify_mode = settings[:openssl_verify_mode]
|
267
|
+
|
268
|
+
if openssl_verify_mode.kind_of?(String)
|
269
|
+
openssl_verify_mode = "OpenSSL::SSL::VERIFY_#{openssl_verify_mode.upcase}".constantize
|
270
|
+
end
|
271
|
+
|
272
|
+
context = Net::SMTP.default_ssl_context
|
273
|
+
context.verify_mode = openssl_verify_mode
|
274
|
+
context.ca_path = settings[:ca_path] if settings[:ca_path]
|
275
|
+
context.ca_file = settings[:ca_file] if settings[:ca_file]
|
276
|
+
context
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Notifier
|
5
|
+
class Nagios < Base
|
6
|
+
|
7
|
+
##
|
8
|
+
# Host of Nagios server to notify on backup completion.
|
9
|
+
attr_accessor :nagios_host
|
10
|
+
|
11
|
+
##
|
12
|
+
# Port of Nagios server to notify on backup completion.
|
13
|
+
attr_accessor :nagios_port
|
14
|
+
|
15
|
+
##
|
16
|
+
# Name of the Nagios service for the backup check.
|
17
|
+
attr_accessor :service_name
|
18
|
+
|
19
|
+
##
|
20
|
+
# Host name in Nagios for the backup check.
|
21
|
+
attr_accessor :service_host
|
22
|
+
|
23
|
+
def initialize(model, &block)
|
24
|
+
super
|
25
|
+
instance_eval(&block) if block_given?
|
26
|
+
|
27
|
+
@nagios_host ||= Config.hostname
|
28
|
+
@nagios_port ||= 5667
|
29
|
+
@service_name ||= "Backup #{ model.trigger }"
|
30
|
+
@service_host ||= Config.hostname
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
##
|
36
|
+
# Notify the user of the backup operation results.
|
37
|
+
#
|
38
|
+
# `status` indicates one of the following:
|
39
|
+
#
|
40
|
+
# `:success`
|
41
|
+
# : The backup completed successfully.
|
42
|
+
# : Notification will be sent if `on_success` is `true`.
|
43
|
+
#
|
44
|
+
# `:warning`
|
45
|
+
# : The backup completed successfully, but warnings were logged.
|
46
|
+
# : Notification will be sent if `on_warning` or `on_success` is `true`.
|
47
|
+
#
|
48
|
+
# `:failure`
|
49
|
+
# : The backup operation failed.
|
50
|
+
# : Notification will be sent if `on_warning` or `on_success` is `true`.
|
51
|
+
#
|
52
|
+
def notify!(status)
|
53
|
+
message = case status
|
54
|
+
when :success then 'Completed Successfully'
|
55
|
+
when :warning then 'Completed Successfully (with Warnings)'
|
56
|
+
when :failure then 'Failed'
|
57
|
+
end
|
58
|
+
send_message("#{ message } in #{ model.duration }")
|
59
|
+
end
|
60
|
+
|
61
|
+
def send_message(message)
|
62
|
+
cmd = "#{ utility(:send_nsca) } -H '#{ nagios_host }' -p '#{ nagios_port }'"
|
63
|
+
msg = [service_host, service_name, model.exit_status, message].join("\t")
|
64
|
+
run("echo '#{ msg }' | #{ cmd }")
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/backup/package.rb
CHANGED
@@ -19,6 +19,10 @@ module Backup
|
|
19
19
|
# Set by the Splitter if the final archive was "chunked"
|
20
20
|
attr_accessor :chunk_suffixes
|
21
21
|
|
22
|
+
##
|
23
|
+
# If true, the Cycler will not attempt to remove the package when Cycling.
|
24
|
+
attr_accessor :no_cycle
|
25
|
+
|
22
26
|
##
|
23
27
|
# The version of Backup used to create the package
|
24
28
|
attr_reader :version
|
@@ -27,6 +31,7 @@ module Backup
|
|
27
31
|
@trigger = model.trigger
|
28
32
|
@extension = 'tar'
|
29
33
|
@chunk_suffixes = Array.new
|
34
|
+
@no_cycle = false
|
30
35
|
@version = VERSION
|
31
36
|
end
|
32
37
|
|
data/lib/backup/packager.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module Backup
|
4
4
|
module Packager
|
5
|
+
class Error < Backup::Error; end
|
6
|
+
|
5
7
|
class << self
|
6
8
|
include Backup::Utilities::Helpers
|
7
9
|
|
@@ -19,8 +21,7 @@ module Backup
|
|
19
21
|
if @pipeline.success?
|
20
22
|
Logger.info "Packaging Complete!"
|
21
23
|
else
|
22
|
-
raise
|
23
|
-
"Failed to Create Backup Package\n" +
|
24
|
+
raise Error, "Failed to Create Backup Package\n" +
|
24
25
|
@pipeline.error_messages
|
25
26
|
end
|
26
27
|
end
|
data/lib/backup/pipeline.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module Backup
|
4
4
|
class Pipeline
|
5
|
+
class Error < Backup::Error; end
|
6
|
+
|
5
7
|
include Backup::Utilities::Helpers
|
6
8
|
|
7
9
|
attr_reader :stderr, :errors
|
@@ -63,8 +65,8 @@ module Backup
|
|
63
65
|
@stderr = stderr.read.strip
|
64
66
|
end
|
65
67
|
Logger.warn(stderr_messages) if success? && stderr_messages
|
66
|
-
rescue Exception =>
|
67
|
-
raise
|
68
|
+
rescue Exception => err
|
69
|
+
raise Error.wrap(err, 'Pipeline failed to execute')
|
68
70
|
end
|
69
71
|
|
70
72
|
def success?
|
data/lib/backup/storage/base.rb
CHANGED
@@ -21,12 +21,13 @@ module Backup
|
|
21
21
|
# multiple storages of the same type. If multiple storages of the same
|
22
22
|
# type are added to a single backup model, this identifier must be set.
|
23
23
|
# This will be appended to the YAML storage file used for cycling backups.
|
24
|
-
def initialize(model, storage_id = nil)
|
24
|
+
def initialize(model, storage_id = nil, &block)
|
25
25
|
@model = model
|
26
26
|
@package = model.package
|
27
27
|
@storage_id = storage_id.to_s.gsub(/\W/, '_') if storage_id
|
28
28
|
|
29
29
|
load_defaults!
|
30
|
+
instance_eval(&block) if block_given?
|
30
31
|
end
|
31
32
|
|
32
33
|
def perform!
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'backup/cloud_io/cloud_files'
|
3
|
+
|
4
|
+
module Backup
|
5
|
+
module Storage
|
6
|
+
class CloudFiles < Base
|
7
|
+
class Error < Backup::Error; end
|
8
|
+
|
9
|
+
##
|
10
|
+
# Rackspace CloudFiles Credentials
|
11
|
+
attr_accessor :username, :api_key
|
12
|
+
|
13
|
+
##
|
14
|
+
# Rackspace Auth URL (optional)
|
15
|
+
attr_accessor :auth_url
|
16
|
+
|
17
|
+
##
|
18
|
+
# Rackspace Service Net
|
19
|
+
# (LAN-based transfers to avoid charges and improve performance)
|
20
|
+
attr_accessor :servicenet
|
21
|
+
|
22
|
+
##
|
23
|
+
# Rackspace Region (optional)
|
24
|
+
attr_accessor :region
|
25
|
+
|
26
|
+
##
|
27
|
+
# Rackspace Container Name
|
28
|
+
attr_accessor :container
|
29
|
+
|
30
|
+
##
|
31
|
+
# Rackspace Container Name for SLO Segments
|
32
|
+
# Required if #segment_size is set. Must be different from #container.
|
33
|
+
attr_accessor :segments_container
|
34
|
+
|
35
|
+
##
|
36
|
+
# SLO Segment size, specified in MiB.
|
37
|
+
#
|
38
|
+
# Each package file larger than +segment_size+
|
39
|
+
# will be uploaded as a Static Large Objects (SLO).
|
40
|
+
#
|
41
|
+
# Defaults to 0 for backward compatibility (pre v.3.7.0),
|
42
|
+
# since #segments_container would be required.
|
43
|
+
#
|
44
|
+
# Minimum: 1 (0 disables SLO support)
|
45
|
+
# Maximum: 5120 (5 GiB)
|
46
|
+
attr_accessor :segment_size
|
47
|
+
|
48
|
+
##
|
49
|
+
# If set, all backup package files (including SLO segments) will be
|
50
|
+
# scheduled for automatic removal by the server.
|
51
|
+
#
|
52
|
+
# The `keep` option should not be used if this is set,
|
53
|
+
# unless you're transitioning from the `keep` option.
|
54
|
+
attr_accessor :days_to_keep
|
55
|
+
|
56
|
+
##
|
57
|
+
# Number of times to retry failed operations.
|
58
|
+
#
|
59
|
+
# Default: 10
|
60
|
+
attr_accessor :max_retries
|
61
|
+
|
62
|
+
##
|
63
|
+
# Time in seconds to pause before each retry.
|
64
|
+
#
|
65
|
+
# Default: 30
|
66
|
+
attr_accessor :retry_waitsec
|
67
|
+
|
68
|
+
def initialize(model, storage_id = nil)
|
69
|
+
super
|
70
|
+
|
71
|
+
@servicenet ||= false
|
72
|
+
@segment_size ||= 0
|
73
|
+
@max_retries ||= 10
|
74
|
+
@retry_waitsec ||= 30
|
75
|
+
|
76
|
+
@path ||= 'backups'
|
77
|
+
path.sub!(/^\//, '')
|
78
|
+
|
79
|
+
check_configuration
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def cloud_io
|
85
|
+
@cloud_io ||= CloudIO::CloudFiles.new(
|
86
|
+
:username => username,
|
87
|
+
:api_key => api_key,
|
88
|
+
:auth_url => auth_url,
|
89
|
+
:region => region,
|
90
|
+
:servicenet => servicenet,
|
91
|
+
:container => container,
|
92
|
+
:segments_container => segments_container,
|
93
|
+
:segment_size => segment_size,
|
94
|
+
:days_to_keep => days_to_keep,
|
95
|
+
:max_retries => max_retries,
|
96
|
+
:retry_waitsec => retry_waitsec
|
97
|
+
)
|
98
|
+
end
|
99
|
+
|
100
|
+
def transfer!
|
101
|
+
package.filenames.each do |filename|
|
102
|
+
src = File.join(Config.tmp_path, filename)
|
103
|
+
dest = File.join(remote_path, filename)
|
104
|
+
Logger.info "Storing '#{ container }/#{ dest }'..."
|
105
|
+
cloud_io.upload(src, dest)
|
106
|
+
end
|
107
|
+
|
108
|
+
package.no_cycle = true if days_to_keep
|
109
|
+
end
|
110
|
+
|
111
|
+
# Called by the Cycler.
|
112
|
+
# Any error raised will be logged as a warning.
|
113
|
+
def remove!(package)
|
114
|
+
Logger.info "Removing backup package dated #{ package.time }..."
|
115
|
+
|
116
|
+
remote_path = remote_path_for(package)
|
117
|
+
objects = cloud_io.objects(remote_path)
|
118
|
+
|
119
|
+
raise Error, "Package at '#{ remote_path }' not found" if objects.empty?
|
120
|
+
|
121
|
+
slo_objects, objects = objects.partition(&:slo?)
|
122
|
+
cloud_io.delete_slo(slo_objects)
|
123
|
+
cloud_io.delete(objects)
|
124
|
+
end
|
125
|
+
|
126
|
+
def check_configuration
|
127
|
+
required = %w{ username api_key container }
|
128
|
+
raise Error, <<-EOS if required.map {|name| send(name) }.any?(&:nil?)
|
129
|
+
Configuration Error
|
130
|
+
#{ required.map {|name| "##{ name }"}.join(', ') } are all required
|
131
|
+
EOS
|
132
|
+
|
133
|
+
raise Error, <<-EOS if segment_size > 0 && segments_container.to_s.empty?
|
134
|
+
Configuration Error
|
135
|
+
#segments_container is required if #segment_size is > 0
|
136
|
+
EOS
|
137
|
+
|
138
|
+
raise Error, <<-EOS if container == segments_container
|
139
|
+
Configuration Error
|
140
|
+
#container and #segments_container must not be the same container.
|
141
|
+
EOS
|
142
|
+
|
143
|
+
raise Error, <<-EOS if segment_size > 5120
|
144
|
+
Configuration Error
|
145
|
+
#segment_size is too large (max 5120)
|
146
|
+
EOS
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|