backup 3.0.19 → 3.0.20
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/.gitignore +4 -0
- data/Gemfile +9 -8
- data/Gemfile.lock +19 -1
- data/Guardfile +13 -9
- data/README.md +93 -31
- data/backup.gemspec +3 -3
- data/bin/backup +6 -283
- data/lib/backup.rb +101 -72
- data/lib/backup/archive.rb +21 -9
- data/lib/backup/binder.rb +22 -0
- data/lib/backup/cleaner.rb +36 -0
- data/lib/backup/cli/helpers.rb +103 -0
- data/lib/backup/cli/utility.rb +308 -0
- data/lib/backup/compressor/base.rb +2 -2
- data/lib/backup/compressor/pbzip2.rb +76 -0
- data/lib/backup/configuration/compressor/pbzip2.rb +28 -0
- data/lib/backup/configuration/database/riak.rb +25 -0
- data/lib/backup/configuration/encryptor/open_ssl.rb +6 -0
- data/lib/backup/configuration/helpers.rb +5 -18
- data/lib/backup/configuration/notifier/base.rb +13 -0
- data/lib/backup/configuration/notifier/hipchat.rb +41 -0
- data/lib/backup/configuration/notifier/mail.rb +38 -0
- data/lib/backup/configuration/notifier/prowl.rb +23 -0
- data/lib/backup/configuration/storage/cloudfiles.rb +4 -0
- data/lib/backup/configuration/storage/dropbox.rb +8 -4
- data/lib/backup/database/base.rb +10 -2
- data/lib/backup/database/mongodb.rb +16 -19
- data/lib/backup/database/mysql.rb +2 -2
- data/lib/backup/database/postgresql.rb +2 -2
- data/lib/backup/database/redis.rb +15 -7
- data/lib/backup/database/riak.rb +45 -0
- data/lib/backup/dependency.rb +21 -7
- data/lib/backup/encryptor/base.rb +1 -1
- data/lib/backup/encryptor/open_ssl.rb +20 -5
- data/lib/backup/errors.rb +124 -0
- data/lib/backup/finder.rb +11 -3
- data/lib/backup/logger.rb +121 -82
- data/lib/backup/model.rb +103 -44
- data/lib/backup/notifier/base.rb +50 -0
- data/lib/backup/notifier/campfire.rb +32 -52
- data/lib/backup/notifier/hipchat.rb +99 -0
- data/lib/backup/notifier/mail.rb +100 -61
- data/lib/backup/notifier/presently.rb +31 -40
- data/lib/backup/notifier/prowl.rb +73 -0
- data/lib/backup/notifier/twitter.rb +29 -39
- data/lib/backup/packager.rb +25 -0
- data/lib/backup/splitter.rb +62 -0
- data/lib/backup/storage/base.rb +178 -18
- data/lib/backup/storage/cloudfiles.rb +34 -28
- data/lib/backup/storage/dropbox.rb +64 -67
- data/lib/backup/storage/ftp.rb +48 -40
- data/lib/backup/storage/local.rb +33 -28
- data/lib/backup/storage/ninefold.rb +40 -26
- data/lib/backup/storage/object.rb +8 -6
- data/lib/backup/storage/rsync.rb +61 -51
- data/lib/backup/storage/s3.rb +29 -27
- data/lib/backup/storage/scp.rb +56 -36
- data/lib/backup/storage/sftp.rb +49 -33
- data/lib/backup/syncer/base.rb +1 -1
- data/lib/backup/syncer/rsync.rb +1 -1
- data/lib/backup/template.rb +46 -0
- data/lib/backup/version.rb +1 -1
- data/spec/archive_spec.rb +34 -9
- data/spec/backup_spec.rb +1 -1
- data/spec/cli/helpers_spec.rb +35 -0
- data/spec/cli/utility_spec.rb +38 -0
- data/spec/compressor/bzip2_spec.rb +1 -1
- data/spec/compressor/gzip_spec.rb +1 -1
- data/spec/compressor/lzma_spec.rb +1 -1
- data/spec/compressor/pbzip2_spec.rb +63 -0
- data/spec/configuration/base_spec.rb +1 -1
- data/spec/configuration/compressor/bzip2_spec.rb +1 -1
- data/spec/configuration/compressor/gzip_spec.rb +1 -1
- data/spec/configuration/compressor/lzma_spec.rb +1 -1
- data/spec/configuration/database/base_spec.rb +1 -1
- data/spec/configuration/database/mongodb_spec.rb +1 -1
- data/spec/configuration/database/mysql_spec.rb +1 -1
- data/spec/configuration/database/postgresql_spec.rb +1 -1
- data/spec/configuration/database/redis_spec.rb +1 -1
- data/spec/configuration/database/riak_spec.rb +31 -0
- data/spec/configuration/encryptor/gpg_spec.rb +1 -1
- data/spec/configuration/encryptor/open_ssl_spec.rb +4 -1
- data/spec/configuration/notifier/campfire_spec.rb +1 -1
- data/spec/configuration/notifier/hipchat_spec.rb +43 -0
- data/spec/configuration/notifier/mail_spec.rb +34 -22
- data/spec/configuration/notifier/presently_spec.rb +1 -1
- data/spec/configuration/notifier/prowl_spec.rb +28 -0
- data/spec/configuration/notifier/twitter_spec.rb +1 -1
- data/spec/configuration/storage/cloudfiles_spec.rb +19 -16
- data/spec/configuration/storage/dropbox_spec.rb +1 -1
- data/spec/configuration/storage/ftp_spec.rb +1 -1
- data/spec/configuration/storage/local_spec.rb +1 -1
- data/spec/configuration/storage/ninefold_spec.rb +1 -1
- data/spec/configuration/storage/rsync_spec.rb +1 -1
- data/spec/configuration/storage/s3_spec.rb +1 -1
- data/spec/configuration/storage/scp_spec.rb +1 -1
- data/spec/configuration/storage/sftp_spec.rb +1 -1
- data/spec/configuration/syncer/rsync_spec.rb +1 -1
- data/spec/configuration/syncer/s3_spec.rb +1 -1
- data/spec/database/base_spec.rb +10 -1
- data/spec/database/mongodb_spec.rb +34 -7
- data/spec/database/mysql_spec.rb +8 -7
- data/spec/database/postgresql_spec.rb +8 -7
- data/spec/database/redis_spec.rb +39 -9
- data/spec/database/riak_spec.rb +50 -0
- data/spec/encryptor/gpg_spec.rb +1 -1
- data/spec/encryptor/open_ssl_spec.rb +77 -20
- data/spec/errors_spec.rb +306 -0
- data/spec/finder_spec.rb +91 -0
- data/spec/logger_spec.rb +254 -33
- data/spec/model_spec.rb +120 -15
- data/spec/notifier/campfire_spec.rb +127 -52
- data/spec/notifier/hipchat_spec.rb +193 -0
- data/spec/notifier/mail_spec.rb +290 -74
- data/spec/notifier/presently_spec.rb +290 -73
- data/spec/notifier/prowl_spec.rb +149 -0
- data/spec/notifier/twitter_spec.rb +106 -41
- data/spec/spec_helper.rb +8 -2
- data/spec/splitter_spec.rb +71 -0
- data/spec/storage/base_spec.rb +280 -19
- data/spec/storage/cloudfiles_spec.rb +38 -22
- data/spec/storage/dropbox_spec.rb +17 -13
- data/spec/storage/ftp_spec.rb +145 -55
- data/spec/storage/local_spec.rb +6 -6
- data/spec/storage/ninefold_spec.rb +70 -29
- data/spec/storage/object_spec.rb +44 -44
- data/spec/storage/rsync_spec.rb +186 -63
- data/spec/storage/s3_spec.rb +23 -24
- data/spec/storage/scp_spec.rb +116 -41
- data/spec/storage/sftp_spec.rb +124 -46
- data/spec/syncer/rsync_spec.rb +3 -3
- data/spec/syncer/s3_spec.rb +1 -1
- data/spec/version_spec.rb +1 -1
- data/templates/cli/utility/archive +13 -0
- data/{lib/templates → templates/cli/utility}/compressor/bzip2 +1 -1
- data/{lib/templates → templates/cli/utility}/compressor/gzip +1 -1
- data/{lib/templates → templates/cli/utility}/compressor/lzma +0 -0
- data/templates/cli/utility/compressor/pbzip2 +7 -0
- data/templates/cli/utility/config +31 -0
- data/{lib/templates → templates/cli/utility}/database/mongodb +1 -1
- data/{lib/templates → templates/cli/utility}/database/mysql +1 -1
- data/{lib/templates → templates/cli/utility}/database/postgresql +1 -1
- data/{lib/templates → templates/cli/utility}/database/redis +1 -1
- data/templates/cli/utility/database/riak +8 -0
- data/{lib/templates → templates/cli/utility}/encryptor/gpg +1 -1
- data/templates/cli/utility/encryptor/openssl +9 -0
- data/templates/cli/utility/model.erb +23 -0
- data/{lib/templates → templates/cli/utility}/notifier/campfire +2 -1
- data/templates/cli/utility/notifier/hipchat +15 -0
- data/{lib/templates → templates/cli/utility}/notifier/mail +6 -1
- data/{lib/templates → templates/cli/utility}/notifier/presently +1 -0
- data/templates/cli/utility/notifier/prowl +11 -0
- data/{lib/templates → templates/cli/utility}/notifier/twitter +2 -1
- data/templates/cli/utility/splitter +7 -0
- data/templates/cli/utility/storage/cloudfiles +12 -0
- data/{lib/templates → templates/cli/utility}/storage/dropbox +1 -1
- data/{lib/templates → templates/cli/utility}/storage/ftp +0 -0
- data/templates/cli/utility/storage/local +7 -0
- data/{lib/templates → templates/cli/utility}/storage/ninefold +1 -1
- data/templates/cli/utility/storage/rsync +11 -0
- data/{lib/templates → templates/cli/utility}/storage/s3 +0 -2
- data/templates/cli/utility/storage/scp +11 -0
- data/templates/cli/utility/storage/sftp +11 -0
- data/{lib/templates → templates/cli/utility}/syncer/rsync +1 -1
- data/{lib/templates → templates/cli/utility}/syncer/s3 +1 -1
- data/templates/general/links +11 -0
- data/templates/general/version.erb +2 -0
- data/templates/notifier/mail/failure.erb +9 -0
- data/templates/notifier/mail/success.erb +7 -0
- data/templates/notifier/mail/warning.erb +9 -0
- data/templates/storage/dropbox/authorization_url.erb +6 -0
- data/templates/storage/dropbox/authorized.erb +4 -0
- data/templates/storage/dropbox/cache_file_written.erb +10 -0
- metadata +81 -45
- data/lib/backup/cli.rb +0 -110
- data/lib/backup/exception/command_failed.rb +0 -8
- data/lib/backup/exception/command_not_found.rb +0 -8
- data/lib/backup/notifier/binder.rb +0 -32
- data/lib/backup/notifier/templates/notify_failure.erb +0 -33
- data/lib/backup/notifier/templates/notify_success.erb +0 -16
- data/lib/templates/archive +0 -7
- data/lib/templates/encryptor/openssl +0 -8
- data/lib/templates/readme +0 -15
- data/lib/templates/storage/cloudfiles +0 -11
- data/lib/templates/storage/local +0 -7
- data/lib/templates/storage/rsync +0 -11
- data/lib/templates/storage/scp +0 -11
- data/lib/templates/storage/sftp +0 -11
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Backup
|
|
4
|
+
class Packager
|
|
5
|
+
include Backup::CLI::Helpers
|
|
6
|
+
|
|
7
|
+
##
|
|
8
|
+
# Holds an instance of the current Backup model
|
|
9
|
+
attr_accessor :model
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
# Creates a new instance of the Backup::Packager class
|
|
13
|
+
def initialize(model)
|
|
14
|
+
@model = model
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
##
|
|
18
|
+
# Packages the current state of the backup in to a single archived file.
|
|
19
|
+
def package!
|
|
20
|
+
Logger.message "#{ self.class } started packaging the backup files."
|
|
21
|
+
run("#{ utility(:tar) } -c -f '#{ File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar") }' -C '#{ Backup::TMP_PATH }' '#{ Backup::TRIGGER }'")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Backup
|
|
4
|
+
class Splitter
|
|
5
|
+
include Backup::CLI::Helpers
|
|
6
|
+
|
|
7
|
+
##
|
|
8
|
+
# Separates the end of the file from the chunk extension name
|
|
9
|
+
SUFFIX_SEPARATOR = "-"
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
# Holds an instance of the current Backup model
|
|
13
|
+
attr_accessor :model
|
|
14
|
+
|
|
15
|
+
##
|
|
16
|
+
# Instantiates a new instance of Backup::Splitter and takes
|
|
17
|
+
# a Backup model as an argument.
|
|
18
|
+
# Also, (re)set the Backup::Model.chunk_suffixes to an empty array.
|
|
19
|
+
def initialize(model)
|
|
20
|
+
@model = model
|
|
21
|
+
Backup::Model.chunk_suffixes = Array.new
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
# Splits the file in multiple chunks if necessary, and it's necessary
|
|
26
|
+
# when the requested chunk size is smaller than the actual backup file
|
|
27
|
+
def split!
|
|
28
|
+
return unless model.chunk_size.is_a?(Integer)
|
|
29
|
+
|
|
30
|
+
if File.size(model.file) > bytes_representation_of(model.chunk_size)
|
|
31
|
+
Logger.message "#{ self.class } started splitting the packaged archive in to chunks of #{ model.chunk_size } megabytes."
|
|
32
|
+
run("#{ utility(:split) } -b #{ model.chunk_size }m '#{ model.file }' '#{ model.file + SUFFIX_SEPARATOR }'")
|
|
33
|
+
Backup::Model.chunk_suffixes = chunk_suffixes
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
##
|
|
40
|
+
# Returns an array of suffixes for each chunk.
|
|
41
|
+
# For example: [aa, ab, ac, ad, ae] - Chunk suffixes are sorted on alphabetical order
|
|
42
|
+
def chunk_suffixes
|
|
43
|
+
chunks.map do |chunk|
|
|
44
|
+
File.extname(chunk).split("-").last
|
|
45
|
+
end.sort
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
##
|
|
49
|
+
# Returns an array of full paths to the backup chunks.
|
|
50
|
+
# Chunks aresorted on alphabetical order
|
|
51
|
+
def chunks
|
|
52
|
+
Dir["#{model.file}-*"].sort
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
##
|
|
56
|
+
# Converts the provided megabytes to a bytes representation
|
|
57
|
+
def bytes_representation_of(megabytes)
|
|
58
|
+
megabytes * 1024 * 1024
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
end
|
data/lib/backup/storage/base.rb
CHANGED
|
@@ -11,9 +11,65 @@ module Backup
|
|
|
11
11
|
|
|
12
12
|
##
|
|
13
13
|
# Sets the limit to how many backups to keep in the remote location.
|
|
14
|
-
# If the
|
|
14
|
+
# If exceeded, the oldest will be removed to make room for the newest
|
|
15
15
|
attr_accessor :keep
|
|
16
16
|
|
|
17
|
+
# Temporarily holds the configuration block used to instantiate the
|
|
18
|
+
# storage object. Used for updating storage objects loaded from YAML
|
|
19
|
+
# during backup rotation in #cycle!
|
|
20
|
+
attr_accessor :configure_block
|
|
21
|
+
|
|
22
|
+
##
|
|
23
|
+
# Contains an array of chunk suffixes (if any)
|
|
24
|
+
# If none are set, this will be an empty array, in which case Backup assumes
|
|
25
|
+
# we haven't been splitting the backup in to multiple chunks. The storage object
|
|
26
|
+
# will only attempt to transfer/remove chunks if this array contains chunk suffixes.
|
|
27
|
+
attr_accessor :chunk_suffixes
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# Super method for the child classes' perform! method. "super" should
|
|
31
|
+
# always be invoked from the child classes' perform! method to ensure that the
|
|
32
|
+
# @chunk_suffixes array gets set to the storage object, which will be used to transfer all the
|
|
33
|
+
# chunks to the remote location, rather than the single backup file. Also, this will be persisted
|
|
34
|
+
# and loaded back in during the cycling process, so it gets properly deleted from the remote location.
|
|
35
|
+
|
|
36
|
+
##
|
|
37
|
+
# (Optional)
|
|
38
|
+
# User-defined string used to uniquely identify multiple storages of the same type.
|
|
39
|
+
# This will be appended to the YAML storage file used for cycling backups.
|
|
40
|
+
attr_accessor :storage_id
|
|
41
|
+
|
|
42
|
+
##
|
|
43
|
+
# Set to Backup::Version.current just before the object is stored
|
|
44
|
+
# in the YAML file for cycling. This way, we know when the object
|
|
45
|
+
# is loaded from the YAML file, which version of Backup stored it.
|
|
46
|
+
attr_reader :version
|
|
47
|
+
|
|
48
|
+
def perform!
|
|
49
|
+
@chunk_suffixes ||= Backup::Model.chunk_suffixes
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
##
|
|
53
|
+
# Creates a new instance of the storage object
|
|
54
|
+
def initialize(storage_id = nil, &block)
|
|
55
|
+
@configure_block = block
|
|
56
|
+
@storage_id = storage_id
|
|
57
|
+
configure!
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
##
|
|
61
|
+
# Return the storage name, with optional storage_id
|
|
62
|
+
def storage_name
|
|
63
|
+
self.class.to_s.sub('Backup::', '') +
|
|
64
|
+
(storage_id ? " (#{storage_id})" : '')
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
##
|
|
68
|
+
# Returns the full filename of the processed backup file
|
|
69
|
+
def filename
|
|
70
|
+
@filename ||= File.basename(Backup::Model.file)
|
|
71
|
+
end
|
|
72
|
+
|
|
17
73
|
##
|
|
18
74
|
# Returns the local path
|
|
19
75
|
def local_path
|
|
@@ -21,44 +77,148 @@ module Backup
|
|
|
21
77
|
end
|
|
22
78
|
|
|
23
79
|
##
|
|
24
|
-
# Returns
|
|
25
|
-
def
|
|
26
|
-
|
|
80
|
+
# Returns an array of backup chunks
|
|
81
|
+
def chunks
|
|
82
|
+
chunk_suffixes.map do |chunk_suffix|
|
|
83
|
+
"#{ filename }-#{ chunk_suffix }"
|
|
84
|
+
end.sort
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
##
|
|
88
|
+
# Returns a block with two arguments: "local_file, remote_file"
|
|
89
|
+
# The local_file is the full file name: "2011.08.30.11.00.02.backup.tar.gz.enc"
|
|
90
|
+
# The remote_file is the full file name, minus the timestamp: "backup.tar.gz.enc"
|
|
91
|
+
def files_to_transfer
|
|
92
|
+
if chunks?
|
|
93
|
+
chunks.each do |chunk|
|
|
94
|
+
yield chunk, chunk[20..-1]
|
|
95
|
+
end
|
|
96
|
+
else
|
|
97
|
+
yield filename, filename[20..-1]
|
|
98
|
+
end
|
|
27
99
|
end
|
|
28
100
|
|
|
101
|
+
alias :transferred_files :files_to_transfer
|
|
102
|
+
|
|
29
103
|
##
|
|
30
|
-
# Returns
|
|
31
|
-
|
|
32
|
-
|
|
104
|
+
# Returns true if we're working with chunks
|
|
105
|
+
# that were splitted by Backup
|
|
106
|
+
def chunks?
|
|
107
|
+
chunk_suffixes.is_a?(Array) and chunk_suffixes.count > 0
|
|
33
108
|
end
|
|
34
109
|
|
|
35
110
|
##
|
|
36
|
-
# Provider defaults to false
|
|
37
|
-
#
|
|
111
|
+
# Provider defaults to false. Overridden when using a service-based
|
|
112
|
+
# storage such as Amazon S3, Rackspace Cloud Files or Dropbox
|
|
38
113
|
def provider
|
|
39
114
|
false
|
|
40
115
|
end
|
|
41
116
|
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
##
|
|
120
|
+
# Configure the storage object, using optional configuration block
|
|
121
|
+
# Uses #pre_configure to set defaults (if any exist) and then evaluates
|
|
122
|
+
# the optional configuration block which may overwrite these defaults.
|
|
123
|
+
# Then uses #post_configure to adjust the configuration as needed.
|
|
124
|
+
#
|
|
125
|
+
# This method is also used to update storage objects loaded from the
|
|
126
|
+
# YAML data storage file used for backup rotation in #cycle!
|
|
127
|
+
def configure!
|
|
128
|
+
pre_configure
|
|
129
|
+
instance_eval(&@configure_block) if @configure_block
|
|
130
|
+
post_configure
|
|
131
|
+
self
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
##
|
|
135
|
+
# Set configuration defaults before evaluating configuration block.
|
|
136
|
+
# Each subclass may perform additional actions after calling super()
|
|
137
|
+
def pre_configure
|
|
138
|
+
load_defaults!
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
##
|
|
142
|
+
# Adjust configuration after evaluating configuration block.
|
|
143
|
+
# Each subclass may perform additional actions after calling super()
|
|
144
|
+
def post_configure
|
|
145
|
+
@time ||= TIME
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
##
|
|
149
|
+
# Update the configuration with the given +configure_block+
|
|
150
|
+
# This is to update the configuration for this storage object
|
|
151
|
+
# once it's been loaded from the YAML storage file (within #cycle!)
|
|
152
|
+
# so that cycling operations can be performed using the latest
|
|
153
|
+
# configuration from the current backup job.
|
|
154
|
+
def update!(configure_block)
|
|
155
|
+
upgrade_if_needed!
|
|
156
|
+
instance_exec(configure_block) do |block|
|
|
157
|
+
@configure_block = block; configure!
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
##
|
|
162
|
+
# Upgrades the format of an object loaded from the YAML storage file
|
|
163
|
+
# if it was stored with a previous, incompatible version of Backup,
|
|
164
|
+
# before it is updated using the new configure_block in #update!
|
|
165
|
+
def upgrade_if_needed!
|
|
166
|
+
return if version == Backup::Version.current
|
|
167
|
+
case
|
|
168
|
+
when version.nil? # <= 3.0.19
|
|
169
|
+
@filename = @remote_file
|
|
170
|
+
@chunk_suffixes = []
|
|
171
|
+
clean!
|
|
172
|
+
else; # upgrade not required
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
##
|
|
177
|
+
# Clear all attributes except those which need to be stored
|
|
178
|
+
# in the YAML storage file for cycling, and version stamp it.
|
|
179
|
+
def clean!
|
|
180
|
+
stored_attrs = [:@filename, :@time, :@chunk_suffixes]
|
|
181
|
+
(instance_variables.map(&:to_sym) - stored_attrs).each do |var|
|
|
182
|
+
remove_instance_variable var
|
|
183
|
+
end
|
|
184
|
+
@version = Backup::Version.current
|
|
185
|
+
end
|
|
186
|
+
|
|
42
187
|
##
|
|
43
188
|
# Checks the persisted storage data by type (S3, CloudFiles, SCP, etc)
|
|
44
189
|
# to see if the amount of stored backups is greater than the amount of
|
|
45
190
|
# backups allowed. If this is the case it'll invoke the #remove! method
|
|
46
|
-
# on each of the oldest backups that exceed the storage limit (specified
|
|
47
|
-
# After that it'll re-assign the objects variable with an
|
|
48
|
-
#
|
|
49
|
-
#
|
|
191
|
+
# on each of the oldest backups that exceed the storage limit (specified
|
|
192
|
+
# by @keep). After that it'll re-assign the objects variable with an
|
|
193
|
+
# array of objects that still remain after the removal of the older
|
|
194
|
+
# objects and files (that exceeded the @keep range). And finally these
|
|
195
|
+
# remaining objects will be converted to YAML format and are written back
|
|
196
|
+
# to the YAML file.
|
|
197
|
+
# Each remaining storage object's attributes will be updated using the
|
|
198
|
+
# defaults and configuration block defined for the current backup job
|
|
199
|
+
# in case the storage location is changed or credentials are updated.
|
|
50
200
|
def cycle!
|
|
201
|
+
return unless keep.to_i > 0
|
|
51
202
|
type = self.class.name.split("::").last
|
|
52
|
-
storage_object = Backup::Storage::Object.new(type)
|
|
53
|
-
objects =
|
|
54
|
-
|
|
203
|
+
storage_object = Backup::Storage::Object.new(type, storage_id)
|
|
204
|
+
objects = storage_object.load
|
|
205
|
+
objects.each {|object| object.send(:update!, @configure_block) }
|
|
206
|
+
objects.unshift(self)
|
|
207
|
+
if objects.count > keep
|
|
55
208
|
objects_to_remove = objects[keep..-1]
|
|
56
209
|
objects_to_remove.each do |object|
|
|
57
|
-
Logger.message "#{
|
|
58
|
-
|
|
210
|
+
Logger.message "#{storage_name} started removing (cycling) " +
|
|
211
|
+
"'#{ object.filename }'."
|
|
212
|
+
begin
|
|
213
|
+
object.send(:remove!)
|
|
214
|
+
rescue => err
|
|
215
|
+
Logger.warn Errors::Storage::CycleError.wrap(err,
|
|
216
|
+
"#{storage_name} failed to remove '#{object.filename}'")
|
|
217
|
+
end
|
|
59
218
|
end
|
|
60
219
|
objects = objects - objects_to_remove
|
|
61
220
|
end
|
|
221
|
+
objects.each {|object| object.send(:clean!) }
|
|
62
222
|
storage_object.write(objects)
|
|
63
223
|
end
|
|
64
224
|
|
|
@@ -13,27 +13,17 @@ module Backup
|
|
|
13
13
|
attr_accessor :username, :api_key, :auth_url
|
|
14
14
|
|
|
15
15
|
##
|
|
16
|
-
# Rackspace
|
|
17
|
-
attr_accessor :
|
|
16
|
+
# Rackspace Service Net (Allows for LAN-based transfers to avoid charges and improve performance)
|
|
17
|
+
attr_accessor :servicenet
|
|
18
18
|
|
|
19
19
|
##
|
|
20
|
-
#
|
|
21
|
-
|
|
22
|
-
# the configuration block which may overwrite these defaults
|
|
23
|
-
def initialize(&block)
|
|
24
|
-
load_defaults!
|
|
25
|
-
|
|
26
|
-
@path ||= 'backups'
|
|
27
|
-
|
|
28
|
-
instance_eval(&block) if block_given?
|
|
29
|
-
|
|
30
|
-
@time = TIME
|
|
31
|
-
end
|
|
20
|
+
# Rackspace Cloud Files container name and path
|
|
21
|
+
attr_accessor :container, :path
|
|
32
22
|
|
|
33
23
|
##
|
|
34
24
|
# This is the remote path to where the backup files will be stored
|
|
35
25
|
def remote_path
|
|
36
|
-
File.join(path, TRIGGER)
|
|
26
|
+
File.join(path, TRIGGER, @time)
|
|
37
27
|
end
|
|
38
28
|
|
|
39
29
|
##
|
|
@@ -45,12 +35,29 @@ module Backup
|
|
|
45
35
|
##
|
|
46
36
|
# Performs the backup transfer
|
|
47
37
|
def perform!
|
|
38
|
+
super
|
|
48
39
|
transfer!
|
|
49
40
|
cycle!
|
|
50
41
|
end
|
|
51
42
|
|
|
52
43
|
private
|
|
53
44
|
|
|
45
|
+
##
|
|
46
|
+
# Set configuration defaults before evaluating configuration block,
|
|
47
|
+
# after setting defaults from Storage::Base
|
|
48
|
+
def pre_configure
|
|
49
|
+
super
|
|
50
|
+
@servicenet ||= false
|
|
51
|
+
@path ||= 'backups'
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
##
|
|
55
|
+
# Adjust configuration after evaluating configuration block,
|
|
56
|
+
# after adjustments from Storage::Base
|
|
57
|
+
def post_configure
|
|
58
|
+
super
|
|
59
|
+
end
|
|
60
|
+
|
|
54
61
|
##
|
|
55
62
|
# Establishes a connection to Rackspace Cloud Files and returns the Fog object.
|
|
56
63
|
# Not doing any instance variable caching because this object gets persisted in YAML
|
|
@@ -58,37 +65,36 @@ module Backup
|
|
|
58
65
|
# gets invoked once per object for a #transfer! and once for a remove! Backups run in the
|
|
59
66
|
# background anyway so even if it were a bit slower it shouldn't matter.
|
|
60
67
|
def connection
|
|
61
|
-
Fog::Storage.new(
|
|
62
|
-
:provider
|
|
63
|
-
:rackspace_username
|
|
64
|
-
:rackspace_api_key
|
|
65
|
-
:rackspace_auth_url
|
|
68
|
+
@connection ||= Fog::Storage.new(
|
|
69
|
+
:provider => provider,
|
|
70
|
+
:rackspace_username => username,
|
|
71
|
+
:rackspace_api_key => api_key,
|
|
72
|
+
:rackspace_auth_url => auth_url,
|
|
73
|
+
:rackspace_servicenet => servicenet
|
|
66
74
|
)
|
|
67
75
|
end
|
|
68
76
|
|
|
69
77
|
##
|
|
70
78
|
# Transfers the archived file to the specified Cloud Files container
|
|
71
79
|
def transfer!
|
|
72
|
-
|
|
73
|
-
Logger.message
|
|
80
|
+
files_to_transfer do |local_file, remote_file|
|
|
81
|
+
Logger.message "#{storage_name} started transferring '#{ local_file }'."
|
|
74
82
|
connection.put_object(
|
|
75
83
|
container,
|
|
76
84
|
File.join(remote_path, remote_file),
|
|
77
85
|
File.open(File.join(local_path, local_file))
|
|
78
86
|
)
|
|
79
|
-
rescue Excon::Errors::SocketError => e
|
|
80
|
-
puts "\nAn error occurred while trying to transfer the backup."
|
|
81
|
-
puts "Make sure the container exists and try again.\n\n"
|
|
82
|
-
exit
|
|
83
87
|
end
|
|
84
88
|
end
|
|
85
89
|
|
|
86
90
|
##
|
|
87
91
|
# Removes the transferred archive file from the Cloud Files container
|
|
88
92
|
def remove!
|
|
89
|
-
|
|
93
|
+
transferred_files do |local_file, remote_file|
|
|
94
|
+
Logger.message "#{storage_name} started removing '#{ local_file }'" +
|
|
95
|
+
"from container '#{ container }'"
|
|
90
96
|
connection.delete_object(container, File.join(remote_path, remote_file))
|
|
91
|
-
|
|
97
|
+
end
|
|
92
98
|
end
|
|
93
99
|
|
|
94
100
|
end
|
|
@@ -24,36 +24,38 @@ module Backup
|
|
|
24
24
|
# Dropbox connection timeout
|
|
25
25
|
attr_accessor :timeout
|
|
26
26
|
|
|
27
|
-
##
|
|
28
|
-
# Creates a new instance of the Dropbox storage object
|
|
29
|
-
# First it sets the defaults (if any exist) and then evaluates
|
|
30
|
-
# the configuration block which may overwrite these defaults
|
|
31
|
-
def initialize(&block)
|
|
32
|
-
load_defaults!(:except => ['password', 'email'])
|
|
33
|
-
|
|
34
|
-
@path ||= 'backups'
|
|
35
|
-
|
|
36
|
-
instance_eval(&block) if block_given?
|
|
37
|
-
|
|
38
|
-
@timeout ||= 300
|
|
39
|
-
@time = TIME
|
|
40
|
-
end
|
|
41
|
-
|
|
42
27
|
##
|
|
43
28
|
# This is the remote path to where the backup files will be stored
|
|
44
29
|
def remote_path
|
|
45
|
-
File.join(path, TRIGGER)
|
|
30
|
+
File.join(path, TRIGGER, @time)
|
|
46
31
|
end
|
|
47
32
|
|
|
48
33
|
##
|
|
49
34
|
# Performs the backup transfer
|
|
50
35
|
def perform!
|
|
36
|
+
super
|
|
51
37
|
transfer!
|
|
52
38
|
cycle!
|
|
53
39
|
end
|
|
54
40
|
|
|
55
41
|
private
|
|
56
42
|
|
|
43
|
+
##
|
|
44
|
+
# Set configuration defaults before evaluating configuration block,
|
|
45
|
+
# after setting defaults from Storage::Base
|
|
46
|
+
def pre_configure
|
|
47
|
+
super
|
|
48
|
+
@path ||= 'backups'
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
##
|
|
52
|
+
# Adjust configuration after evaluating configuration block,
|
|
53
|
+
# after adjustments from Storage::Base
|
|
54
|
+
def post_configure
|
|
55
|
+
super
|
|
56
|
+
@timeout ||= 300
|
|
57
|
+
end
|
|
58
|
+
|
|
57
59
|
##
|
|
58
60
|
# The initial connection to Dropbox will provide the user with an authorization url.
|
|
59
61
|
# The user must open this URL and confirm that the authorization successfully took place.
|
|
@@ -62,15 +64,20 @@ module Backup
|
|
|
62
64
|
# in Backup::CACHE_PATH. The cached file will be used from that point on to re-establish a connection with
|
|
63
65
|
# Dropbox at a later time. This allows the user to avoid having to go to a new Dropbox URL to authorize over and over again.
|
|
64
66
|
def connection
|
|
67
|
+
return @connection if @connection
|
|
68
|
+
|
|
65
69
|
if cache_exists?
|
|
66
70
|
begin
|
|
67
71
|
cached_session = ::Dropbox::Session.deserialize(File.read(cached_file))
|
|
68
72
|
if cached_session.authorized?
|
|
69
73
|
Logger.message "Session data loaded from cache!"
|
|
70
|
-
return cached_session
|
|
74
|
+
return @connection = cached_session
|
|
71
75
|
end
|
|
72
|
-
rescue ArgumentError =>
|
|
73
|
-
Logger.warn
|
|
76
|
+
rescue ArgumentError => err
|
|
77
|
+
Logger.warn Errors::Storage::Dropbox::ConnectionError.wrap(err, <<-EOS)
|
|
78
|
+
Could not read session data from cache.
|
|
79
|
+
Cache data might be corrupt.
|
|
80
|
+
EOS
|
|
74
81
|
end
|
|
75
82
|
end
|
|
76
83
|
|
|
@@ -81,54 +88,37 @@ module Backup
|
|
|
81
88
|
##
|
|
82
89
|
# Transfers the archived file to the specified Dropbox folder
|
|
83
90
|
def transfer!
|
|
84
|
-
|
|
85
|
-
|
|
91
|
+
files_to_transfer do |local_file, remote_file|
|
|
92
|
+
Logger.message "#{storage_name} started transferring '#{ local_file }'."
|
|
93
|
+
connection.upload(
|
|
94
|
+
File.join(local_path, local_file),
|
|
95
|
+
remote_path,
|
|
96
|
+
:as => remote_file,
|
|
97
|
+
:timeout => timeout
|
|
98
|
+
)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
remove_instance_variable(:@connection) if instance_variable_defined?(:@connection)
|
|
86
102
|
end
|
|
87
103
|
|
|
88
104
|
##
|
|
89
105
|
# Removes the transferred archive file from the Dropbox folder
|
|
90
106
|
def remove!
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
Logger.warn "File \"#{ File.join(remote_path, remote_file) }\" does not exist, skipping removal."
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
##
|
|
99
|
-
# Create a new session, write a serialized version of it to the
|
|
100
|
-
# .cache directory, and return the session object
|
|
101
|
-
def create_write_and_return_new_session!
|
|
102
|
-
session = ::Dropbox::Session.new(api_key, api_secret)
|
|
103
|
-
session.mode = :dropbox
|
|
104
|
-
Logger.message "Open the following URL in a browser to authorize a session for your Dropbox account:"
|
|
105
|
-
Logger.message ""
|
|
106
|
-
Logger.message "\s\s#{session.authorize_url}"
|
|
107
|
-
Logger.message ""
|
|
108
|
-
Logger.message "Once Dropbox says you're authorized, hit enter to proceed."
|
|
109
|
-
Timeout::timeout(180) { STDIN.gets }
|
|
110
|
-
begin
|
|
111
|
-
session.authorize
|
|
112
|
-
rescue OAuth::Unauthorized => error
|
|
113
|
-
Logger.error "Authorization failed!"
|
|
114
|
-
raise error
|
|
107
|
+
messages = []
|
|
108
|
+
transferred_files do |local_file, remote_file|
|
|
109
|
+
messages << "#{storage_name} started removing '#{ local_file }' from Dropbox."
|
|
115
110
|
end
|
|
116
|
-
Logger.message "
|
|
117
|
-
|
|
118
|
-
Logger.message "Caching session data to file: #{cached_file}.."
|
|
119
|
-
write_cache!(session)
|
|
120
|
-
Logger.message "Cache data written! You will no longer need to manually authorize this Dropbox account via an URL on this machine."
|
|
121
|
-
Logger.message "Note: If you run Backup with this Dropbox account on other machines, you will need to either authorize them the same way,"
|
|
122
|
-
Logger.message "\s\sor simply copy over #{cached_file} to the cache directory"
|
|
123
|
-
Logger.message "\s\son your other machines to use this Dropbox account there as well."
|
|
111
|
+
Logger.message messages.join("\n")
|
|
124
112
|
|
|
125
|
-
|
|
113
|
+
connection.delete(remote_path)
|
|
114
|
+
ensure
|
|
115
|
+
remove_instance_variable(:@connection) if instance_variable_defined?(:@connection)
|
|
126
116
|
end
|
|
127
117
|
|
|
128
118
|
##
|
|
129
119
|
# Returns the path to the cached file
|
|
130
120
|
def cached_file
|
|
131
|
-
File.join(Backup::CACHE_PATH,
|
|
121
|
+
File.join(Backup::CACHE_PATH, api_key + api_secret)
|
|
132
122
|
end
|
|
133
123
|
|
|
134
124
|
##
|
|
@@ -145,22 +135,29 @@ module Backup
|
|
|
145
135
|
end
|
|
146
136
|
end
|
|
147
137
|
|
|
148
|
-
|
|
138
|
+
##
|
|
139
|
+
# Create a new session, write a serialized version of it to the
|
|
140
|
+
# .cache directory, and return the session object
|
|
141
|
+
def create_write_and_return_new_session!
|
|
142
|
+
session = ::Dropbox::Session.new(api_key, api_secret)
|
|
143
|
+
session.mode = :dropbox
|
|
149
144
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
end
|
|
145
|
+
template = Backup::Template.new({:session => session, :cached_file => cached_file})
|
|
146
|
+
template.render("storage/dropbox/authorization_url.erb")
|
|
153
147
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
148
|
+
begin
|
|
149
|
+
Timeout::timeout(180) { STDIN.gets }
|
|
150
|
+
session.authorize
|
|
151
|
+
rescue OAuth::Unauthorized => err
|
|
152
|
+
raise Errors::Storage::Dropbox::ConnectionError.
|
|
153
|
+
wrap(err, 'Authorization Failed!')
|
|
154
|
+
end
|
|
157
155
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
156
|
+
template.render("storage/dropbox/authorized.erb")
|
|
157
|
+
write_cache!(session)
|
|
158
|
+
template.render("storage/dropbox/cache_file_written.erb")
|
|
161
159
|
|
|
162
|
-
|
|
163
|
-
Logger.warn "[DEPRECATED] Backup::Storage::Dropbox.password= is deprecated and will be removed at some point."
|
|
160
|
+
session
|
|
164
161
|
end
|
|
165
162
|
|
|
166
163
|
end
|