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.
Files changed (244) hide show
  1. data/.gitignore +7 -0
  2. data/.travis.yml +10 -0
  3. data/Gemfile +28 -0
  4. data/Gemfile.lock +130 -0
  5. data/Guardfile +21 -0
  6. data/LICENSE.md +24 -0
  7. data/README.md +476 -0
  8. data/backup_checksum.gemspec +32 -0
  9. data/bin/backup +11 -0
  10. data/lib/backup.rb +217 -0
  11. data/lib/backup/archive.rb +117 -0
  12. data/lib/backup/binder.rb +22 -0
  13. data/lib/backup/checksum/base.rb +44 -0
  14. data/lib/backup/checksum/shasum.rb +16 -0
  15. data/lib/backup/cleaner.rb +121 -0
  16. data/lib/backup/cli/helpers.rb +88 -0
  17. data/lib/backup/cli/utility.rb +247 -0
  18. data/lib/backup/compressor/base.rb +29 -0
  19. data/lib/backup/compressor/bzip2.rb +50 -0
  20. data/lib/backup/compressor/gzip.rb +47 -0
  21. data/lib/backup/compressor/lzma.rb +50 -0
  22. data/lib/backup/compressor/pbzip2.rb +56 -0
  23. data/lib/backup/config.rb +173 -0
  24. data/lib/backup/configuration/base.rb +15 -0
  25. data/lib/backup/configuration/checksum/base.rb +9 -0
  26. data/lib/backup/configuration/checksum/shasum.rb +9 -0
  27. data/lib/backup/configuration/compressor/base.rb +9 -0
  28. data/lib/backup/configuration/compressor/bzip2.rb +23 -0
  29. data/lib/backup/configuration/compressor/gzip.rb +23 -0
  30. data/lib/backup/configuration/compressor/lzma.rb +23 -0
  31. data/lib/backup/configuration/compressor/pbzip2.rb +28 -0
  32. data/lib/backup/configuration/database/base.rb +19 -0
  33. data/lib/backup/configuration/database/mongodb.rb +49 -0
  34. data/lib/backup/configuration/database/mysql.rb +42 -0
  35. data/lib/backup/configuration/database/postgresql.rb +41 -0
  36. data/lib/backup/configuration/database/redis.rb +39 -0
  37. data/lib/backup/configuration/database/riak.rb +29 -0
  38. data/lib/backup/configuration/encryptor/base.rb +9 -0
  39. data/lib/backup/configuration/encryptor/gpg.rb +17 -0
  40. data/lib/backup/configuration/encryptor/open_ssl.rb +32 -0
  41. data/lib/backup/configuration/helpers.rb +52 -0
  42. data/lib/backup/configuration/notifier/base.rb +28 -0
  43. data/lib/backup/configuration/notifier/campfire.rb +25 -0
  44. data/lib/backup/configuration/notifier/hipchat.rb +41 -0
  45. data/lib/backup/configuration/notifier/mail.rb +112 -0
  46. data/lib/backup/configuration/notifier/presently.rb +25 -0
  47. data/lib/backup/configuration/notifier/prowl.rb +23 -0
  48. data/lib/backup/configuration/notifier/twitter.rb +21 -0
  49. data/lib/backup/configuration/storage/base.rb +18 -0
  50. data/lib/backup/configuration/storage/cloudfiles.rb +25 -0
  51. data/lib/backup/configuration/storage/dropbox.rb +58 -0
  52. data/lib/backup/configuration/storage/ftp.rb +29 -0
  53. data/lib/backup/configuration/storage/local.rb +17 -0
  54. data/lib/backup/configuration/storage/ninefold.rb +20 -0
  55. data/lib/backup/configuration/storage/rsync.rb +29 -0
  56. data/lib/backup/configuration/storage/s3.rb +25 -0
  57. data/lib/backup/configuration/storage/scp.rb +25 -0
  58. data/lib/backup/configuration/storage/sftp.rb +25 -0
  59. data/lib/backup/configuration/syncer/base.rb +10 -0
  60. data/lib/backup/configuration/syncer/cloud.rb +23 -0
  61. data/lib/backup/configuration/syncer/cloud_files.rb +30 -0
  62. data/lib/backup/configuration/syncer/rsync/base.rb +28 -0
  63. data/lib/backup/configuration/syncer/rsync/local.rb +11 -0
  64. data/lib/backup/configuration/syncer/rsync/pull.rb +11 -0
  65. data/lib/backup/configuration/syncer/rsync/push.rb +31 -0
  66. data/lib/backup/configuration/syncer/s3.rb +23 -0
  67. data/lib/backup/database/base.rb +59 -0
  68. data/lib/backup/database/mongodb.rb +232 -0
  69. data/lib/backup/database/mysql.rb +163 -0
  70. data/lib/backup/database/postgresql.rb +146 -0
  71. data/lib/backup/database/redis.rb +139 -0
  72. data/lib/backup/database/riak.rb +69 -0
  73. data/lib/backup/dependency.rb +114 -0
  74. data/lib/backup/encryptor/base.rb +29 -0
  75. data/lib/backup/encryptor/gpg.rb +80 -0
  76. data/lib/backup/encryptor/open_ssl.rb +72 -0
  77. data/lib/backup/errors.rb +124 -0
  78. data/lib/backup/logger.rb +152 -0
  79. data/lib/backup/model.rb +386 -0
  80. data/lib/backup/notifier/base.rb +81 -0
  81. data/lib/backup/notifier/campfire.rb +168 -0
  82. data/lib/backup/notifier/hipchat.rb +99 -0
  83. data/lib/backup/notifier/mail.rb +206 -0
  84. data/lib/backup/notifier/presently.rb +88 -0
  85. data/lib/backup/notifier/prowl.rb +65 -0
  86. data/lib/backup/notifier/twitter.rb +70 -0
  87. data/lib/backup/package.rb +51 -0
  88. data/lib/backup/packager.rb +108 -0
  89. data/lib/backup/pipeline.rb +107 -0
  90. data/lib/backup/splitter.rb +75 -0
  91. data/lib/backup/storage/base.rb +119 -0
  92. data/lib/backup/storage/cloudfiles.rb +87 -0
  93. data/lib/backup/storage/cycler.rb +117 -0
  94. data/lib/backup/storage/dropbox.rb +181 -0
  95. data/lib/backup/storage/ftp.rb +119 -0
  96. data/lib/backup/storage/local.rb +82 -0
  97. data/lib/backup/storage/ninefold.rb +116 -0
  98. data/lib/backup/storage/rsync.rb +149 -0
  99. data/lib/backup/storage/s3.rb +94 -0
  100. data/lib/backup/storage/scp.rb +99 -0
  101. data/lib/backup/storage/sftp.rb +108 -0
  102. data/lib/backup/syncer/base.rb +42 -0
  103. data/lib/backup/syncer/cloud.rb +190 -0
  104. data/lib/backup/syncer/cloud_files.rb +56 -0
  105. data/lib/backup/syncer/rsync/base.rb +52 -0
  106. data/lib/backup/syncer/rsync/local.rb +53 -0
  107. data/lib/backup/syncer/rsync/pull.rb +38 -0
  108. data/lib/backup/syncer/rsync/push.rb +113 -0
  109. data/lib/backup/syncer/s3.rb +47 -0
  110. data/lib/backup/template.rb +46 -0
  111. data/lib/backup/version.rb +43 -0
  112. data/spec/archive_spec.rb +335 -0
  113. data/spec/cleaner_spec.rb +304 -0
  114. data/spec/cli/helpers_spec.rb +176 -0
  115. data/spec/cli/utility_spec.rb +363 -0
  116. data/spec/compressor/base_spec.rb +31 -0
  117. data/spec/compressor/bzip2_spec.rb +83 -0
  118. data/spec/compressor/gzip_spec.rb +83 -0
  119. data/spec/compressor/lzma_spec.rb +83 -0
  120. data/spec/compressor/pbzip2_spec.rb +124 -0
  121. data/spec/config_spec.rb +321 -0
  122. data/spec/configuration/base_spec.rb +35 -0
  123. data/spec/configuration/compressor/bzip2_spec.rb +29 -0
  124. data/spec/configuration/compressor/gzip_spec.rb +29 -0
  125. data/spec/configuration/compressor/lzma_spec.rb +29 -0
  126. data/spec/configuration/compressor/pbzip2_spec.rb +32 -0
  127. data/spec/configuration/database/base_spec.rb +17 -0
  128. data/spec/configuration/database/mongodb_spec.rb +56 -0
  129. data/spec/configuration/database/mysql_spec.rb +53 -0
  130. data/spec/configuration/database/postgresql_spec.rb +53 -0
  131. data/spec/configuration/database/redis_spec.rb +50 -0
  132. data/spec/configuration/database/riak_spec.rb +35 -0
  133. data/spec/configuration/encryptor/gpg_spec.rb +26 -0
  134. data/spec/configuration/encryptor/open_ssl_spec.rb +35 -0
  135. data/spec/configuration/notifier/base_spec.rb +32 -0
  136. data/spec/configuration/notifier/campfire_spec.rb +32 -0
  137. data/spec/configuration/notifier/hipchat_spec.rb +44 -0
  138. data/spec/configuration/notifier/mail_spec.rb +71 -0
  139. data/spec/configuration/notifier/presently_spec.rb +35 -0
  140. data/spec/configuration/notifier/prowl_spec.rb +29 -0
  141. data/spec/configuration/notifier/twitter_spec.rb +35 -0
  142. data/spec/configuration/storage/cloudfiles_spec.rb +41 -0
  143. data/spec/configuration/storage/dropbox_spec.rb +38 -0
  144. data/spec/configuration/storage/ftp_spec.rb +44 -0
  145. data/spec/configuration/storage/local_spec.rb +29 -0
  146. data/spec/configuration/storage/ninefold_spec.rb +32 -0
  147. data/spec/configuration/storage/rsync_spec.rb +41 -0
  148. data/spec/configuration/storage/s3_spec.rb +38 -0
  149. data/spec/configuration/storage/scp_spec.rb +41 -0
  150. data/spec/configuration/storage/sftp_spec.rb +41 -0
  151. data/spec/configuration/syncer/cloud_files_spec.rb +44 -0
  152. data/spec/configuration/syncer/rsync/base_spec.rb +33 -0
  153. data/spec/configuration/syncer/rsync/local_spec.rb +10 -0
  154. data/spec/configuration/syncer/rsync/pull_spec.rb +10 -0
  155. data/spec/configuration/syncer/rsync/push_spec.rb +43 -0
  156. data/spec/configuration/syncer/s3_spec.rb +38 -0
  157. data/spec/database/base_spec.rb +54 -0
  158. data/spec/database/mongodb_spec.rb +428 -0
  159. data/spec/database/mysql_spec.rb +335 -0
  160. data/spec/database/postgresql_spec.rb +278 -0
  161. data/spec/database/redis_spec.rb +260 -0
  162. data/spec/database/riak_spec.rb +108 -0
  163. data/spec/dependency_spec.rb +49 -0
  164. data/spec/encryptor/base_spec.rb +30 -0
  165. data/spec/encryptor/gpg_spec.rb +134 -0
  166. data/spec/encryptor/open_ssl_spec.rb +129 -0
  167. data/spec/errors_spec.rb +306 -0
  168. data/spec/logger_spec.rb +363 -0
  169. data/spec/model_spec.rb +649 -0
  170. data/spec/notifier/base_spec.rb +89 -0
  171. data/spec/notifier/campfire_spec.rb +199 -0
  172. data/spec/notifier/hipchat_spec.rb +188 -0
  173. data/spec/notifier/mail_spec.rb +280 -0
  174. data/spec/notifier/presently_spec.rb +181 -0
  175. data/spec/notifier/prowl_spec.rb +117 -0
  176. data/spec/notifier/twitter_spec.rb +132 -0
  177. data/spec/package_spec.rb +61 -0
  178. data/spec/packager_spec.rb +225 -0
  179. data/spec/pipeline_spec.rb +257 -0
  180. data/spec/spec_helper.rb +59 -0
  181. data/spec/splitter_spec.rb +120 -0
  182. data/spec/storage/base_spec.rb +160 -0
  183. data/spec/storage/cloudfiles_spec.rb +230 -0
  184. data/spec/storage/cycler_spec.rb +239 -0
  185. data/spec/storage/dropbox_spec.rb +370 -0
  186. data/spec/storage/ftp_spec.rb +247 -0
  187. data/spec/storage/local_spec.rb +235 -0
  188. data/spec/storage/ninefold_spec.rb +319 -0
  189. data/spec/storage/rsync_spec.rb +345 -0
  190. data/spec/storage/s3_spec.rb +221 -0
  191. data/spec/storage/scp_spec.rb +209 -0
  192. data/spec/storage/sftp_spec.rb +220 -0
  193. data/spec/syncer/base_spec.rb +22 -0
  194. data/spec/syncer/cloud_files_spec.rb +192 -0
  195. data/spec/syncer/rsync/base_spec.rb +118 -0
  196. data/spec/syncer/rsync/local_spec.rb +121 -0
  197. data/spec/syncer/rsync/pull_spec.rb +90 -0
  198. data/spec/syncer/rsync/push_spec.rb +327 -0
  199. data/spec/syncer/s3_spec.rb +192 -0
  200. data/spec/version_spec.rb +21 -0
  201. data/templates/cli/utility/archive +25 -0
  202. data/templates/cli/utility/compressor/bzip2 +7 -0
  203. data/templates/cli/utility/compressor/gzip +7 -0
  204. data/templates/cli/utility/compressor/lzma +7 -0
  205. data/templates/cli/utility/compressor/pbzip2 +7 -0
  206. data/templates/cli/utility/config +31 -0
  207. data/templates/cli/utility/database/mongodb +18 -0
  208. data/templates/cli/utility/database/mysql +21 -0
  209. data/templates/cli/utility/database/postgresql +17 -0
  210. data/templates/cli/utility/database/redis +16 -0
  211. data/templates/cli/utility/database/riak +11 -0
  212. data/templates/cli/utility/encryptor/gpg +12 -0
  213. data/templates/cli/utility/encryptor/openssl +9 -0
  214. data/templates/cli/utility/model.erb +23 -0
  215. data/templates/cli/utility/notifier/campfire +12 -0
  216. data/templates/cli/utility/notifier/hipchat +15 -0
  217. data/templates/cli/utility/notifier/mail +22 -0
  218. data/templates/cli/utility/notifier/presently +13 -0
  219. data/templates/cli/utility/notifier/prowl +11 -0
  220. data/templates/cli/utility/notifier/twitter +13 -0
  221. data/templates/cli/utility/splitter +7 -0
  222. data/templates/cli/utility/storage/cloud_files +22 -0
  223. data/templates/cli/utility/storage/dropbox +20 -0
  224. data/templates/cli/utility/storage/ftp +12 -0
  225. data/templates/cli/utility/storage/local +7 -0
  226. data/templates/cli/utility/storage/ninefold +9 -0
  227. data/templates/cli/utility/storage/rsync +11 -0
  228. data/templates/cli/utility/storage/s3 +19 -0
  229. data/templates/cli/utility/storage/scp +11 -0
  230. data/templates/cli/utility/storage/sftp +11 -0
  231. data/templates/cli/utility/syncer/cloud_files +48 -0
  232. data/templates/cli/utility/syncer/rsync_local +12 -0
  233. data/templates/cli/utility/syncer/rsync_pull +17 -0
  234. data/templates/cli/utility/syncer/rsync_push +17 -0
  235. data/templates/cli/utility/syncer/s3 +45 -0
  236. data/templates/general/links +11 -0
  237. data/templates/general/version.erb +2 -0
  238. data/templates/notifier/mail/failure.erb +9 -0
  239. data/templates/notifier/mail/success.erb +7 -0
  240. data/templates/notifier/mail/warning.erb +9 -0
  241. data/templates/storage/dropbox/authorization_url.erb +6 -0
  242. data/templates/storage/dropbox/authorized.erb +4 -0
  243. data/templates/storage/dropbox/cache_file_written.erb +10 -0
  244. 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