backup_checksum 3.0.23
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 +7 -0
- data/.travis.yml +10 -0
- data/Gemfile +28 -0
- data/Gemfile.lock +130 -0
- data/Guardfile +21 -0
- data/LICENSE.md +24 -0
- data/README.md +476 -0
- data/backup_checksum.gemspec +32 -0
- data/bin/backup +11 -0
- data/lib/backup.rb +217 -0
- data/lib/backup/archive.rb +117 -0
- data/lib/backup/binder.rb +22 -0
- data/lib/backup/checksum/base.rb +44 -0
- data/lib/backup/checksum/shasum.rb +16 -0
- data/lib/backup/cleaner.rb +121 -0
- data/lib/backup/cli/helpers.rb +88 -0
- data/lib/backup/cli/utility.rb +247 -0
- data/lib/backup/compressor/base.rb +29 -0
- data/lib/backup/compressor/bzip2.rb +50 -0
- data/lib/backup/compressor/gzip.rb +47 -0
- data/lib/backup/compressor/lzma.rb +50 -0
- data/lib/backup/compressor/pbzip2.rb +56 -0
- data/lib/backup/config.rb +173 -0
- data/lib/backup/configuration/base.rb +15 -0
- data/lib/backup/configuration/checksum/base.rb +9 -0
- data/lib/backup/configuration/checksum/shasum.rb +9 -0
- data/lib/backup/configuration/compressor/base.rb +9 -0
- data/lib/backup/configuration/compressor/bzip2.rb +23 -0
- data/lib/backup/configuration/compressor/gzip.rb +23 -0
- data/lib/backup/configuration/compressor/lzma.rb +23 -0
- data/lib/backup/configuration/compressor/pbzip2.rb +28 -0
- data/lib/backup/configuration/database/base.rb +19 -0
- data/lib/backup/configuration/database/mongodb.rb +49 -0
- data/lib/backup/configuration/database/mysql.rb +42 -0
- data/lib/backup/configuration/database/postgresql.rb +41 -0
- data/lib/backup/configuration/database/redis.rb +39 -0
- data/lib/backup/configuration/database/riak.rb +29 -0
- data/lib/backup/configuration/encryptor/base.rb +9 -0
- data/lib/backup/configuration/encryptor/gpg.rb +17 -0
- data/lib/backup/configuration/encryptor/open_ssl.rb +32 -0
- data/lib/backup/configuration/helpers.rb +52 -0
- data/lib/backup/configuration/notifier/base.rb +28 -0
- data/lib/backup/configuration/notifier/campfire.rb +25 -0
- data/lib/backup/configuration/notifier/hipchat.rb +41 -0
- data/lib/backup/configuration/notifier/mail.rb +112 -0
- data/lib/backup/configuration/notifier/presently.rb +25 -0
- data/lib/backup/configuration/notifier/prowl.rb +23 -0
- data/lib/backup/configuration/notifier/twitter.rb +21 -0
- data/lib/backup/configuration/storage/base.rb +18 -0
- data/lib/backup/configuration/storage/cloudfiles.rb +25 -0
- data/lib/backup/configuration/storage/dropbox.rb +58 -0
- data/lib/backup/configuration/storage/ftp.rb +29 -0
- data/lib/backup/configuration/storage/local.rb +17 -0
- data/lib/backup/configuration/storage/ninefold.rb +20 -0
- data/lib/backup/configuration/storage/rsync.rb +29 -0
- data/lib/backup/configuration/storage/s3.rb +25 -0
- data/lib/backup/configuration/storage/scp.rb +25 -0
- data/lib/backup/configuration/storage/sftp.rb +25 -0
- data/lib/backup/configuration/syncer/base.rb +10 -0
- data/lib/backup/configuration/syncer/cloud.rb +23 -0
- data/lib/backup/configuration/syncer/cloud_files.rb +30 -0
- data/lib/backup/configuration/syncer/rsync/base.rb +28 -0
- data/lib/backup/configuration/syncer/rsync/local.rb +11 -0
- data/lib/backup/configuration/syncer/rsync/pull.rb +11 -0
- data/lib/backup/configuration/syncer/rsync/push.rb +31 -0
- data/lib/backup/configuration/syncer/s3.rb +23 -0
- data/lib/backup/database/base.rb +59 -0
- data/lib/backup/database/mongodb.rb +232 -0
- data/lib/backup/database/mysql.rb +163 -0
- data/lib/backup/database/postgresql.rb +146 -0
- data/lib/backup/database/redis.rb +139 -0
- data/lib/backup/database/riak.rb +69 -0
- data/lib/backup/dependency.rb +114 -0
- data/lib/backup/encryptor/base.rb +29 -0
- data/lib/backup/encryptor/gpg.rb +80 -0
- data/lib/backup/encryptor/open_ssl.rb +72 -0
- data/lib/backup/errors.rb +124 -0
- data/lib/backup/logger.rb +152 -0
- data/lib/backup/model.rb +386 -0
- data/lib/backup/notifier/base.rb +81 -0
- data/lib/backup/notifier/campfire.rb +168 -0
- data/lib/backup/notifier/hipchat.rb +99 -0
- data/lib/backup/notifier/mail.rb +206 -0
- data/lib/backup/notifier/presently.rb +88 -0
- data/lib/backup/notifier/prowl.rb +65 -0
- data/lib/backup/notifier/twitter.rb +70 -0
- data/lib/backup/package.rb +51 -0
- data/lib/backup/packager.rb +108 -0
- data/lib/backup/pipeline.rb +107 -0
- data/lib/backup/splitter.rb +75 -0
- data/lib/backup/storage/base.rb +119 -0
- data/lib/backup/storage/cloudfiles.rb +87 -0
- data/lib/backup/storage/cycler.rb +117 -0
- data/lib/backup/storage/dropbox.rb +181 -0
- data/lib/backup/storage/ftp.rb +119 -0
- data/lib/backup/storage/local.rb +82 -0
- data/lib/backup/storage/ninefold.rb +116 -0
- data/lib/backup/storage/rsync.rb +149 -0
- data/lib/backup/storage/s3.rb +94 -0
- data/lib/backup/storage/scp.rb +99 -0
- data/lib/backup/storage/sftp.rb +108 -0
- data/lib/backup/syncer/base.rb +42 -0
- data/lib/backup/syncer/cloud.rb +190 -0
- data/lib/backup/syncer/cloud_files.rb +56 -0
- data/lib/backup/syncer/rsync/base.rb +52 -0
- data/lib/backup/syncer/rsync/local.rb +53 -0
- data/lib/backup/syncer/rsync/pull.rb +38 -0
- data/lib/backup/syncer/rsync/push.rb +113 -0
- data/lib/backup/syncer/s3.rb +47 -0
- data/lib/backup/template.rb +46 -0
- data/lib/backup/version.rb +43 -0
- data/spec/archive_spec.rb +335 -0
- data/spec/cleaner_spec.rb +304 -0
- data/spec/cli/helpers_spec.rb +176 -0
- data/spec/cli/utility_spec.rb +363 -0
- data/spec/compressor/base_spec.rb +31 -0
- data/spec/compressor/bzip2_spec.rb +83 -0
- data/spec/compressor/gzip_spec.rb +83 -0
- data/spec/compressor/lzma_spec.rb +83 -0
- data/spec/compressor/pbzip2_spec.rb +124 -0
- data/spec/config_spec.rb +321 -0
- data/spec/configuration/base_spec.rb +35 -0
- data/spec/configuration/compressor/bzip2_spec.rb +29 -0
- data/spec/configuration/compressor/gzip_spec.rb +29 -0
- data/spec/configuration/compressor/lzma_spec.rb +29 -0
- data/spec/configuration/compressor/pbzip2_spec.rb +32 -0
- data/spec/configuration/database/base_spec.rb +17 -0
- data/spec/configuration/database/mongodb_spec.rb +56 -0
- data/spec/configuration/database/mysql_spec.rb +53 -0
- data/spec/configuration/database/postgresql_spec.rb +53 -0
- data/spec/configuration/database/redis_spec.rb +50 -0
- data/spec/configuration/database/riak_spec.rb +35 -0
- data/spec/configuration/encryptor/gpg_spec.rb +26 -0
- data/spec/configuration/encryptor/open_ssl_spec.rb +35 -0
- data/spec/configuration/notifier/base_spec.rb +32 -0
- data/spec/configuration/notifier/campfire_spec.rb +32 -0
- data/spec/configuration/notifier/hipchat_spec.rb +44 -0
- data/spec/configuration/notifier/mail_spec.rb +71 -0
- data/spec/configuration/notifier/presently_spec.rb +35 -0
- data/spec/configuration/notifier/prowl_spec.rb +29 -0
- data/spec/configuration/notifier/twitter_spec.rb +35 -0
- data/spec/configuration/storage/cloudfiles_spec.rb +41 -0
- data/spec/configuration/storage/dropbox_spec.rb +38 -0
- data/spec/configuration/storage/ftp_spec.rb +44 -0
- data/spec/configuration/storage/local_spec.rb +29 -0
- data/spec/configuration/storage/ninefold_spec.rb +32 -0
- data/spec/configuration/storage/rsync_spec.rb +41 -0
- data/spec/configuration/storage/s3_spec.rb +38 -0
- data/spec/configuration/storage/scp_spec.rb +41 -0
- data/spec/configuration/storage/sftp_spec.rb +41 -0
- data/spec/configuration/syncer/cloud_files_spec.rb +44 -0
- data/spec/configuration/syncer/rsync/base_spec.rb +33 -0
- data/spec/configuration/syncer/rsync/local_spec.rb +10 -0
- data/spec/configuration/syncer/rsync/pull_spec.rb +10 -0
- data/spec/configuration/syncer/rsync/push_spec.rb +43 -0
- data/spec/configuration/syncer/s3_spec.rb +38 -0
- data/spec/database/base_spec.rb +54 -0
- data/spec/database/mongodb_spec.rb +428 -0
- data/spec/database/mysql_spec.rb +335 -0
- data/spec/database/postgresql_spec.rb +278 -0
- data/spec/database/redis_spec.rb +260 -0
- data/spec/database/riak_spec.rb +108 -0
- data/spec/dependency_spec.rb +49 -0
- data/spec/encryptor/base_spec.rb +30 -0
- data/spec/encryptor/gpg_spec.rb +134 -0
- data/spec/encryptor/open_ssl_spec.rb +129 -0
- data/spec/errors_spec.rb +306 -0
- data/spec/logger_spec.rb +363 -0
- data/spec/model_spec.rb +649 -0
- data/spec/notifier/base_spec.rb +89 -0
- data/spec/notifier/campfire_spec.rb +199 -0
- data/spec/notifier/hipchat_spec.rb +188 -0
- data/spec/notifier/mail_spec.rb +280 -0
- data/spec/notifier/presently_spec.rb +181 -0
- data/spec/notifier/prowl_spec.rb +117 -0
- data/spec/notifier/twitter_spec.rb +132 -0
- data/spec/package_spec.rb +61 -0
- data/spec/packager_spec.rb +225 -0
- data/spec/pipeline_spec.rb +257 -0
- data/spec/spec_helper.rb +59 -0
- data/spec/splitter_spec.rb +120 -0
- data/spec/storage/base_spec.rb +160 -0
- data/spec/storage/cloudfiles_spec.rb +230 -0
- data/spec/storage/cycler_spec.rb +239 -0
- data/spec/storage/dropbox_spec.rb +370 -0
- data/spec/storage/ftp_spec.rb +247 -0
- data/spec/storage/local_spec.rb +235 -0
- data/spec/storage/ninefold_spec.rb +319 -0
- data/spec/storage/rsync_spec.rb +345 -0
- data/spec/storage/s3_spec.rb +221 -0
- data/spec/storage/scp_spec.rb +209 -0
- data/spec/storage/sftp_spec.rb +220 -0
- data/spec/syncer/base_spec.rb +22 -0
- data/spec/syncer/cloud_files_spec.rb +192 -0
- data/spec/syncer/rsync/base_spec.rb +118 -0
- data/spec/syncer/rsync/local_spec.rb +121 -0
- data/spec/syncer/rsync/pull_spec.rb +90 -0
- data/spec/syncer/rsync/push_spec.rb +327 -0
- data/spec/syncer/s3_spec.rb +192 -0
- data/spec/version_spec.rb +21 -0
- data/templates/cli/utility/archive +25 -0
- data/templates/cli/utility/compressor/bzip2 +7 -0
- data/templates/cli/utility/compressor/gzip +7 -0
- data/templates/cli/utility/compressor/lzma +7 -0
- data/templates/cli/utility/compressor/pbzip2 +7 -0
- data/templates/cli/utility/config +31 -0
- data/templates/cli/utility/database/mongodb +18 -0
- data/templates/cli/utility/database/mysql +21 -0
- data/templates/cli/utility/database/postgresql +17 -0
- data/templates/cli/utility/database/redis +16 -0
- data/templates/cli/utility/database/riak +11 -0
- data/templates/cli/utility/encryptor/gpg +12 -0
- data/templates/cli/utility/encryptor/openssl +9 -0
- data/templates/cli/utility/model.erb +23 -0
- data/templates/cli/utility/notifier/campfire +12 -0
- data/templates/cli/utility/notifier/hipchat +15 -0
- data/templates/cli/utility/notifier/mail +22 -0
- data/templates/cli/utility/notifier/presently +13 -0
- data/templates/cli/utility/notifier/prowl +11 -0
- data/templates/cli/utility/notifier/twitter +13 -0
- data/templates/cli/utility/splitter +7 -0
- data/templates/cli/utility/storage/cloud_files +22 -0
- data/templates/cli/utility/storage/dropbox +20 -0
- data/templates/cli/utility/storage/ftp +12 -0
- data/templates/cli/utility/storage/local +7 -0
- data/templates/cli/utility/storage/ninefold +9 -0
- data/templates/cli/utility/storage/rsync +11 -0
- data/templates/cli/utility/storage/s3 +19 -0
- data/templates/cli/utility/storage/scp +11 -0
- data/templates/cli/utility/storage/sftp +11 -0
- data/templates/cli/utility/syncer/cloud_files +48 -0
- data/templates/cli/utility/syncer/rsync_local +12 -0
- data/templates/cli/utility/syncer/rsync_pull +17 -0
- data/templates/cli/utility/syncer/rsync_push +17 -0
- data/templates/cli/utility/syncer/s3 +45 -0
- 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 +311 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Backup
|
|
4
|
+
class Splitter
|
|
5
|
+
include Backup::CLI::Helpers
|
|
6
|
+
|
|
7
|
+
def initialize(model, chunk_size)
|
|
8
|
+
@model = model
|
|
9
|
+
@chunk_size = chunk_size
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
# This is called as part of the procedure used to build the final
|
|
14
|
+
# backup package file(s). It yields it's portion of the command line
|
|
15
|
+
# for this procedure, which will split the data being piped into it
|
|
16
|
+
# into multiple files, based on the @chunk_size.
|
|
17
|
+
# Once the packaging procedure is complete, it will return and
|
|
18
|
+
# @package.chunk_suffixes will be set based on the resulting files.
|
|
19
|
+
def split_with
|
|
20
|
+
before_packaging
|
|
21
|
+
yield @split_command
|
|
22
|
+
after_packaging
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
##
|
|
28
|
+
# The `split` command reads from $stdin and will store it's output in
|
|
29
|
+
# multiple files, based on the @chunk_size. The files will be
|
|
30
|
+
# written using the given `prefix`, which is the full path to the
|
|
31
|
+
# final @package.basename, plus a '-' separator. This `prefix` will then
|
|
32
|
+
# be suffixed using 'aa', 'ab', and so on... for each file.
|
|
33
|
+
def before_packaging
|
|
34
|
+
@package = @model.package
|
|
35
|
+
Logger.message "Splitter configured with a chunk size of " +
|
|
36
|
+
"#{ @chunk_size }MB."
|
|
37
|
+
|
|
38
|
+
@split_command = "#{ utility(:split) } -b #{ @chunk_size }m - " +
|
|
39
|
+
"'#{ File.join(Config.tmp_path, @package.basename + '-') }'"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
##
|
|
43
|
+
# Finds the resulting files from the packaging procedure
|
|
44
|
+
# and stores an Array of suffixes used in @package.chunk_suffixes.
|
|
45
|
+
# If the @chunk_size was never reached and only one file
|
|
46
|
+
# was written, that file will be suffixed with '-aa'.
|
|
47
|
+
# In which case, it will simply remove the suffix from the filename.
|
|
48
|
+
def after_packaging
|
|
49
|
+
suffixes = chunk_suffixes
|
|
50
|
+
if suffixes == ['aa']
|
|
51
|
+
FileUtils.mv(
|
|
52
|
+
File.join(Config.tmp_path, @package.basename + '-aa'),
|
|
53
|
+
File.join(Config.tmp_path, @package.basename)
|
|
54
|
+
)
|
|
55
|
+
else
|
|
56
|
+
@package.chunk_suffixes = suffixes
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
##
|
|
61
|
+
# Returns an array of suffixes for each chunk, in alphabetical order.
|
|
62
|
+
# For example: [aa, ab, ac, ad, ae]
|
|
63
|
+
def chunk_suffixes
|
|
64
|
+
chunks.map {|chunk| File.extname(chunk).split('-').last }.sort
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
##
|
|
68
|
+
# Returns an array of full paths to the backup chunks.
|
|
69
|
+
# Chunks are sorted in alphabetical order.
|
|
70
|
+
def chunks
|
|
71
|
+
Dir[File.join(Config.tmp_path, @package.basename + '-*')].sort
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Backup
|
|
4
|
+
module Storage
|
|
5
|
+
class Base
|
|
6
|
+
include Backup::Configuration::Helpers
|
|
7
|
+
|
|
8
|
+
##
|
|
9
|
+
# Sets the limit to how many backups to keep in the remote location.
|
|
10
|
+
# If exceeded, the oldest will be removed to make room for the newest
|
|
11
|
+
attr_accessor :keep
|
|
12
|
+
|
|
13
|
+
##
|
|
14
|
+
# (Optional)
|
|
15
|
+
# User-defined string used to uniquely identify multiple storages of the
|
|
16
|
+
# same type. This will be appended to the YAML storage file used for
|
|
17
|
+
# cycling backups.
|
|
18
|
+
attr_accessor :storage_id
|
|
19
|
+
|
|
20
|
+
##
|
|
21
|
+
# Creates a new instance of the storage object
|
|
22
|
+
# * Called with super(model, storage_id) from each subclass
|
|
23
|
+
def initialize(model, storage_id = nil)
|
|
24
|
+
load_defaults!
|
|
25
|
+
@model = model
|
|
26
|
+
@storage_id = storage_id
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# Performs the backup transfer
|
|
31
|
+
def perform!
|
|
32
|
+
@package = @model.package
|
|
33
|
+
@checksum_creator = @model.checksum_creator
|
|
34
|
+
if @checksum_creator
|
|
35
|
+
@checksum_creator.process_checksum_file_before_transfer(file_names_as_hash, @package.checksum_name, local_path)
|
|
36
|
+
end
|
|
37
|
+
transfer!
|
|
38
|
+
cycle!
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
##
|
|
44
|
+
# Provider defaults to false. Overridden when using a service-based
|
|
45
|
+
# storage such as Amazon S3, Rackspace Cloud Files or Dropbox
|
|
46
|
+
def provider
|
|
47
|
+
false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
##
|
|
51
|
+
# Each subclass must define a +path+ where remote files will be stored
|
|
52
|
+
def path; end
|
|
53
|
+
|
|
54
|
+
##
|
|
55
|
+
# Return the storage name, with optional storage_id
|
|
56
|
+
def storage_name
|
|
57
|
+
self.class.to_s.sub('Backup::', '') +
|
|
58
|
+
(storage_id ? " (#{storage_id})" : '')
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# Returns the local path
|
|
63
|
+
# This is where any Package to be transferred is located.
|
|
64
|
+
def local_path
|
|
65
|
+
Config.tmp_path
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
##
|
|
69
|
+
# Returns the remote path for the given Package
|
|
70
|
+
# This is where the Package will be stored, or was previously stored.
|
|
71
|
+
def remote_path_for(package)
|
|
72
|
+
File.join(path, package.trigger, package.time)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def file_names_as_hash(package = @package)
|
|
76
|
+
hash = {}
|
|
77
|
+
files_to_transfer_without_checksum package do |local, remote|
|
|
78
|
+
hash[local] = remote
|
|
79
|
+
end
|
|
80
|
+
hash
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def files_to_transfer_without_checksum(package)
|
|
84
|
+
package.filenames.each do |filename|
|
|
85
|
+
yield filename, filename[20..-1]
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
##
|
|
90
|
+
# Yields two arguments to the given block: "local_file, remote_file"
|
|
91
|
+
# The local_file is the full file name:
|
|
92
|
+
# e.g. "2011.08.30.11.00.02.backup.tar.enc"
|
|
93
|
+
# The remote_file is the full file name, minus the timestamp:
|
|
94
|
+
# e.g. "backup.tar.enc"
|
|
95
|
+
def files_to_transfer_for(package)
|
|
96
|
+
file_names_as_hash(package).each {|local, remote| yield local, remote }
|
|
97
|
+
|
|
98
|
+
if @model.checksum_creator
|
|
99
|
+
yield package.checksum_name, package.checksum_name[20..-1]
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
alias :transferred_files_for :files_to_transfer_for
|
|
103
|
+
|
|
104
|
+
##
|
|
105
|
+
# Adds the current package being stored to the YAML cycle data file
|
|
106
|
+
# and will remove any old Package file(s) when the storage limit
|
|
107
|
+
# set by #keep is exceeded. Any errors raised while attempting to
|
|
108
|
+
# remove older packages will be rescued and a warning will be logged
|
|
109
|
+
# containing the original error message.
|
|
110
|
+
def cycle!
|
|
111
|
+
return unless keep.to_i > 0
|
|
112
|
+
Logger.message "#{ storage_name }: Cycling Started..."
|
|
113
|
+
Cycler.cycle!(self, @package)
|
|
114
|
+
Logger.message "#{ storage_name }: Cycling Complete!"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# Only load the Fog gem when the Backup::Storage::CloudFiles class is loaded
|
|
5
|
+
Backup::Dependency.load('fog')
|
|
6
|
+
|
|
7
|
+
module Backup
|
|
8
|
+
module Storage
|
|
9
|
+
class CloudFiles < Base
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
# Rackspace Cloud Files Credentials
|
|
13
|
+
attr_accessor :username, :api_key, :auth_url
|
|
14
|
+
|
|
15
|
+
##
|
|
16
|
+
# Rackspace Service Net
|
|
17
|
+
# (LAN-based transfers to avoid charges and improve performance)
|
|
18
|
+
attr_accessor :servicenet
|
|
19
|
+
|
|
20
|
+
##
|
|
21
|
+
# Rackspace Cloud Files container name and path
|
|
22
|
+
attr_accessor :container, :path
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
# Creates a new instance of the storage object
|
|
26
|
+
def initialize(model, storage_id = nil, &block)
|
|
27
|
+
super(model, storage_id)
|
|
28
|
+
|
|
29
|
+
@servicenet ||= false
|
|
30
|
+
@path ||= 'backups'
|
|
31
|
+
|
|
32
|
+
instance_eval(&block) if block_given?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# This is the provider that Fog uses for the Cloud Files Storage
|
|
39
|
+
def provider
|
|
40
|
+
'Rackspace'
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
##
|
|
44
|
+
# Establishes a connection to Rackspace Cloud Files
|
|
45
|
+
def connection
|
|
46
|
+
@connection ||= Fog::Storage.new(
|
|
47
|
+
:provider => provider,
|
|
48
|
+
:rackspace_username => username,
|
|
49
|
+
:rackspace_api_key => api_key,
|
|
50
|
+
:rackspace_auth_url => auth_url,
|
|
51
|
+
:rackspace_servicenet => servicenet
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
##
|
|
56
|
+
# Transfers the archived file to the specified Cloud Files container
|
|
57
|
+
def transfer!
|
|
58
|
+
remote_path = remote_path_for(@package)
|
|
59
|
+
|
|
60
|
+
files_to_transfer_for(@package) do |local_file, remote_file|
|
|
61
|
+
Logger.message "#{storage_name} started transferring '#{ local_file }'."
|
|
62
|
+
|
|
63
|
+
File.open(File.join(local_path, local_file), 'r') do |file|
|
|
64
|
+
connection.put_object(
|
|
65
|
+
container, File.join(remote_path, remote_file), file
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
##
|
|
72
|
+
# Removes the transferred archive file(s) from the storage location.
|
|
73
|
+
# Any error raised will be rescued during Cycling
|
|
74
|
+
# and a warning will be logged, containing the error message.
|
|
75
|
+
def remove!(package)
|
|
76
|
+
remote_path = remote_path_for(package)
|
|
77
|
+
|
|
78
|
+
transferred_files_for(package) do |local_file, remote_file|
|
|
79
|
+
Logger.message "#{storage_name} started removing '#{ local_file }' " +
|
|
80
|
+
"from container '#{ container }'."
|
|
81
|
+
connection.delete_object(container, File.join(remote_path, remote_file))
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Backup
|
|
4
|
+
module Storage
|
|
5
|
+
module Cycler
|
|
6
|
+
class << self
|
|
7
|
+
|
|
8
|
+
##
|
|
9
|
+
# Adds the given +package+ to the YAML storage file corresponding
|
|
10
|
+
# to the given +storage+ and Package#trigger (Model#trigger).
|
|
11
|
+
# Then, calls the +storage+ to remove the files for any older
|
|
12
|
+
# packages that were removed from the YAML storage file.
|
|
13
|
+
def cycle!(storage, package)
|
|
14
|
+
@storage, @package = storage, package
|
|
15
|
+
@storage_file = storage_file
|
|
16
|
+
|
|
17
|
+
update_storage_file!
|
|
18
|
+
remove_packages!
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
##
|
|
24
|
+
# Updates the YAML data file according to the #keep setting
|
|
25
|
+
# for the storage and sets the @packages_to_remove
|
|
26
|
+
def update_storage_file!
|
|
27
|
+
packages = yaml_load.unshift(@package)
|
|
28
|
+
excess = packages.count - @storage.keep
|
|
29
|
+
@packages_to_remove = (excess > 0) ? packages.pop(excess) : []
|
|
30
|
+
yaml_save(packages)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
##
|
|
34
|
+
# Calls the @storage to remove any old packages
|
|
35
|
+
# which were cycled out of the storage file.
|
|
36
|
+
def remove_packages!
|
|
37
|
+
@packages_to_remove.each do |pkg|
|
|
38
|
+
begin
|
|
39
|
+
@storage.send(:remove!, pkg)
|
|
40
|
+
rescue => err
|
|
41
|
+
Logger.warn Errors::Storage::CyclerError.wrap(err, <<-EOS)
|
|
42
|
+
There was a problem removing the following package:
|
|
43
|
+
Trigger: #{pkg.trigger} :: Dated: #{pkg.time}
|
|
44
|
+
Package included the following #{ pkg.filenames.count } file(s):
|
|
45
|
+
#{ pkg.filenames.join("\n") }
|
|
46
|
+
EOS
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
##
|
|
52
|
+
# Return full path to the YAML data file,
|
|
53
|
+
# based on the current values of @storage and @package
|
|
54
|
+
def storage_file
|
|
55
|
+
type = @storage.class.to_s.split('::').last
|
|
56
|
+
suffix = @storage.storage_id.to_s.strip.gsub(/[\W\s]/, '_')
|
|
57
|
+
filename = suffix.empty? ? type : "#{type}-#{suffix}"
|
|
58
|
+
File.join(Config.data_path, @package.trigger, "#{filename}.yml")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# Load Package objects from YAML file.
|
|
63
|
+
# Returns an Array, sorted by @time descending.
|
|
64
|
+
# i.e. most recent is objects[0]
|
|
65
|
+
def yaml_load
|
|
66
|
+
packages = []
|
|
67
|
+
if File.exist?(@storage_file) && !File.zero?(@storage_file)
|
|
68
|
+
packages = check_upgrade(
|
|
69
|
+
YAML.load_file(@storage_file).sort do |a, b|
|
|
70
|
+
b.instance_variable_get(:@time) <=> a.instance_variable_get(:@time)
|
|
71
|
+
end
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
packages
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
##
|
|
78
|
+
# Store the given package objects to the YAML data file.
|
|
79
|
+
def yaml_save(packages)
|
|
80
|
+
File.open(@storage_file, 'w') do |file|
|
|
81
|
+
file.write(packages.to_yaml)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
##
|
|
86
|
+
# Upgrade the objects loaded from the YAML file, if needed.
|
|
87
|
+
def check_upgrade(objects)
|
|
88
|
+
if objects.any? {|obj| obj.class.to_s =~ /Backup::Storage/ }
|
|
89
|
+
# Version <= 3.0.20
|
|
90
|
+
model = @storage.instance_variable_get(:@model)
|
|
91
|
+
v3_0_20 = objects.any? {|obj| obj.instance_variable_defined?(:@version) }
|
|
92
|
+
objects.map! do |obj|
|
|
93
|
+
if v3_0_20 # Version == 3.0.20
|
|
94
|
+
filename = obj.instance_variable_get(:@filename)[20..-1]
|
|
95
|
+
chunk_suffixes = obj.instance_variable_get(:@chunk_suffixes)
|
|
96
|
+
else # Version <= 3.0.19
|
|
97
|
+
filename = obj.instance_variable_get(:@remote_file)[20..-1]
|
|
98
|
+
chunk_suffixes = []
|
|
99
|
+
end
|
|
100
|
+
time = obj.instance_variable_get(:@time)
|
|
101
|
+
extension = filename.match(/\.(tar.*)$/)[1]
|
|
102
|
+
|
|
103
|
+
package = Backup::Package.new(model)
|
|
104
|
+
package.instance_variable_set(:@time, time)
|
|
105
|
+
package.extension = extension
|
|
106
|
+
package.chunk_suffixes = chunk_suffixes
|
|
107
|
+
|
|
108
|
+
package
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
objects
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# Only load the Dropbox gem when the Backup::Storage::Dropbox class is loaded
|
|
5
|
+
Backup::Dependency.load('dropbox-sdk')
|
|
6
|
+
|
|
7
|
+
module Backup
|
|
8
|
+
module Storage
|
|
9
|
+
class Dropbox < Base
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
# Dropbox API credentials
|
|
13
|
+
attr_accessor :api_key, :api_secret
|
|
14
|
+
|
|
15
|
+
##
|
|
16
|
+
# Dropbox Access Type
|
|
17
|
+
# Valid values are:
|
|
18
|
+
# :app_folder (default)
|
|
19
|
+
# :dropbox (full access)
|
|
20
|
+
attr_accessor :access_type
|
|
21
|
+
|
|
22
|
+
##
|
|
23
|
+
# Path to where the backups will be stored
|
|
24
|
+
attr_accessor :path
|
|
25
|
+
|
|
26
|
+
# Deprecated as of v3.0.21 - for move to official 'dropbox-sdk' gem (v1.1)
|
|
27
|
+
def timeout=(value)
|
|
28
|
+
if value
|
|
29
|
+
Logger.warn "[DEPRECATED] Backup::Storage::Dropbox.timeout=\n" +
|
|
30
|
+
" is deprecated and will be removed at some point."
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
##
|
|
35
|
+
# Creates a new instance of the storage object
|
|
36
|
+
def initialize(model, storage_id = nil, &block)
|
|
37
|
+
super(model, storage_id)
|
|
38
|
+
|
|
39
|
+
@path ||= 'backups'
|
|
40
|
+
@access_type ||= :app_folder
|
|
41
|
+
|
|
42
|
+
instance_eval(&block) if block_given?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
##
|
|
48
|
+
# The initial connection to Dropbox will provide the user with an
|
|
49
|
+
# authorization url. The user must open this URL and confirm that the
|
|
50
|
+
# authorization successfully took place. If this is the case, then the
|
|
51
|
+
# user hits 'enter' and the session will be properly established.
|
|
52
|
+
# Immediately after establishing the session, the session will be
|
|
53
|
+
# serialized and written to a cache file in Backup::Config.cache_path.
|
|
54
|
+
# The cached file will be used from that point on to re-establish a
|
|
55
|
+
# connection with Dropbox at a later time. This allows the user to avoid
|
|
56
|
+
# having to go to a new Dropbox URL to authorize over and over again.
|
|
57
|
+
def connection
|
|
58
|
+
return @connection if @connection
|
|
59
|
+
|
|
60
|
+
unless session = cached_session
|
|
61
|
+
Logger.message "Creating a new session!"
|
|
62
|
+
session = create_write_and_return_new_session!
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# will raise an error if session not authorized
|
|
66
|
+
@connection = DropboxClient.new(session, access_type)
|
|
67
|
+
|
|
68
|
+
rescue => err
|
|
69
|
+
raise Errors::Storage::Dropbox::ConnectionError.wrap(err)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
##
|
|
73
|
+
# Attempt to load a cached session
|
|
74
|
+
def cached_session
|
|
75
|
+
session = false
|
|
76
|
+
if cache_exists?
|
|
77
|
+
begin
|
|
78
|
+
session = DropboxSession.deserialize(File.read(cached_file))
|
|
79
|
+
Logger.message "Session data loaded from cache!"
|
|
80
|
+
|
|
81
|
+
rescue => err
|
|
82
|
+
Logger.warn Errors::Storage::Dropbox::CacheError.wrap(err, <<-EOS)
|
|
83
|
+
Could not read session data from cache.
|
|
84
|
+
Cache data might be corrupt.
|
|
85
|
+
EOS
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
session
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
##
|
|
92
|
+
# Transfers the archived file to the specified Dropbox folder
|
|
93
|
+
def transfer!
|
|
94
|
+
remote_path = remote_path_for(@package)
|
|
95
|
+
|
|
96
|
+
files_to_transfer_for(@package) do |local_file, remote_file|
|
|
97
|
+
Logger.message "#{storage_name} started transferring '#{ local_file }'."
|
|
98
|
+
File.open(File.join(local_path, local_file), 'r') do |file|
|
|
99
|
+
connection.put_file(File.join(remote_path, remote_file), file)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
##
|
|
105
|
+
# Removes the transferred archive file(s) from the storage location.
|
|
106
|
+
# Any error raised will be rescued during Cycling
|
|
107
|
+
# and a warning will be logged, containing the error message.
|
|
108
|
+
def remove!(package)
|
|
109
|
+
remote_path = remote_path_for(package)
|
|
110
|
+
|
|
111
|
+
messages = []
|
|
112
|
+
transferred_files_for(package) do |local_file, remote_file|
|
|
113
|
+
messages << "#{storage_name} started removing " +
|
|
114
|
+
"'#{ local_file }' from Dropbox."
|
|
115
|
+
end
|
|
116
|
+
Logger.message messages.join("\n")
|
|
117
|
+
|
|
118
|
+
connection.file_delete(remote_path)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
##
|
|
122
|
+
# Returns the path to the cached file
|
|
123
|
+
def cached_file
|
|
124
|
+
File.join(Config.cache_path, api_key + api_secret)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
##
|
|
128
|
+
# Checks to see if the cache file exists
|
|
129
|
+
def cache_exists?
|
|
130
|
+
File.exist?(cached_file)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
##
|
|
134
|
+
# Serializes and writes the Dropbox session to a cache file
|
|
135
|
+
def write_cache!(session)
|
|
136
|
+
File.open(cached_file, "w") do |cache_file|
|
|
137
|
+
cache_file.write(session.serialize)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
##
|
|
142
|
+
# Create a new session, write a serialized version of it to the
|
|
143
|
+
# .cache directory, and return the session object
|
|
144
|
+
def create_write_and_return_new_session!
|
|
145
|
+
require 'timeout'
|
|
146
|
+
|
|
147
|
+
session = DropboxSession.new(api_key, api_secret)
|
|
148
|
+
|
|
149
|
+
# grab the request token for session
|
|
150
|
+
session.get_request_token
|
|
151
|
+
|
|
152
|
+
template = Backup::Template.new(
|
|
153
|
+
{:session => session, :cached_file => cached_file}
|
|
154
|
+
)
|
|
155
|
+
template.render("storage/dropbox/authorization_url.erb")
|
|
156
|
+
|
|
157
|
+
# wait for user to hit 'return' to continue
|
|
158
|
+
Timeout::timeout(180) { STDIN.gets }
|
|
159
|
+
|
|
160
|
+
# this will raise an error if the user did not
|
|
161
|
+
# visit the authorization_url and grant access
|
|
162
|
+
#
|
|
163
|
+
# get the access token from the server
|
|
164
|
+
# this will be stored with the session in the cache file
|
|
165
|
+
session.get_access_token
|
|
166
|
+
|
|
167
|
+
template.render("storage/dropbox/authorized.erb")
|
|
168
|
+
write_cache!(session)
|
|
169
|
+
template.render("storage/dropbox/cache_file_written.erb")
|
|
170
|
+
|
|
171
|
+
session
|
|
172
|
+
|
|
173
|
+
rescue => err
|
|
174
|
+
raise Errors::Storage::Dropbox::AuthenticationError.wrap(
|
|
175
|
+
err, 'Could not create or authenticate a new session'
|
|
176
|
+
)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|