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.
Files changed (188) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +9 -8
  3. data/Gemfile.lock +19 -1
  4. data/Guardfile +13 -9
  5. data/README.md +93 -31
  6. data/backup.gemspec +3 -3
  7. data/bin/backup +6 -283
  8. data/lib/backup.rb +101 -72
  9. data/lib/backup/archive.rb +21 -9
  10. data/lib/backup/binder.rb +22 -0
  11. data/lib/backup/cleaner.rb +36 -0
  12. data/lib/backup/cli/helpers.rb +103 -0
  13. data/lib/backup/cli/utility.rb +308 -0
  14. data/lib/backup/compressor/base.rb +2 -2
  15. data/lib/backup/compressor/pbzip2.rb +76 -0
  16. data/lib/backup/configuration/compressor/pbzip2.rb +28 -0
  17. data/lib/backup/configuration/database/riak.rb +25 -0
  18. data/lib/backup/configuration/encryptor/open_ssl.rb +6 -0
  19. data/lib/backup/configuration/helpers.rb +5 -18
  20. data/lib/backup/configuration/notifier/base.rb +13 -0
  21. data/lib/backup/configuration/notifier/hipchat.rb +41 -0
  22. data/lib/backup/configuration/notifier/mail.rb +38 -0
  23. data/lib/backup/configuration/notifier/prowl.rb +23 -0
  24. data/lib/backup/configuration/storage/cloudfiles.rb +4 -0
  25. data/lib/backup/configuration/storage/dropbox.rb +8 -4
  26. data/lib/backup/database/base.rb +10 -2
  27. data/lib/backup/database/mongodb.rb +16 -19
  28. data/lib/backup/database/mysql.rb +2 -2
  29. data/lib/backup/database/postgresql.rb +2 -2
  30. data/lib/backup/database/redis.rb +15 -7
  31. data/lib/backup/database/riak.rb +45 -0
  32. data/lib/backup/dependency.rb +21 -7
  33. data/lib/backup/encryptor/base.rb +1 -1
  34. data/lib/backup/encryptor/open_ssl.rb +20 -5
  35. data/lib/backup/errors.rb +124 -0
  36. data/lib/backup/finder.rb +11 -3
  37. data/lib/backup/logger.rb +121 -82
  38. data/lib/backup/model.rb +103 -44
  39. data/lib/backup/notifier/base.rb +50 -0
  40. data/lib/backup/notifier/campfire.rb +32 -52
  41. data/lib/backup/notifier/hipchat.rb +99 -0
  42. data/lib/backup/notifier/mail.rb +100 -61
  43. data/lib/backup/notifier/presently.rb +31 -40
  44. data/lib/backup/notifier/prowl.rb +73 -0
  45. data/lib/backup/notifier/twitter.rb +29 -39
  46. data/lib/backup/packager.rb +25 -0
  47. data/lib/backup/splitter.rb +62 -0
  48. data/lib/backup/storage/base.rb +178 -18
  49. data/lib/backup/storage/cloudfiles.rb +34 -28
  50. data/lib/backup/storage/dropbox.rb +64 -67
  51. data/lib/backup/storage/ftp.rb +48 -40
  52. data/lib/backup/storage/local.rb +33 -28
  53. data/lib/backup/storage/ninefold.rb +40 -26
  54. data/lib/backup/storage/object.rb +8 -6
  55. data/lib/backup/storage/rsync.rb +61 -51
  56. data/lib/backup/storage/s3.rb +29 -27
  57. data/lib/backup/storage/scp.rb +56 -36
  58. data/lib/backup/storage/sftp.rb +49 -33
  59. data/lib/backup/syncer/base.rb +1 -1
  60. data/lib/backup/syncer/rsync.rb +1 -1
  61. data/lib/backup/template.rb +46 -0
  62. data/lib/backup/version.rb +1 -1
  63. data/spec/archive_spec.rb +34 -9
  64. data/spec/backup_spec.rb +1 -1
  65. data/spec/cli/helpers_spec.rb +35 -0
  66. data/spec/cli/utility_spec.rb +38 -0
  67. data/spec/compressor/bzip2_spec.rb +1 -1
  68. data/spec/compressor/gzip_spec.rb +1 -1
  69. data/spec/compressor/lzma_spec.rb +1 -1
  70. data/spec/compressor/pbzip2_spec.rb +63 -0
  71. data/spec/configuration/base_spec.rb +1 -1
  72. data/spec/configuration/compressor/bzip2_spec.rb +1 -1
  73. data/spec/configuration/compressor/gzip_spec.rb +1 -1
  74. data/spec/configuration/compressor/lzma_spec.rb +1 -1
  75. data/spec/configuration/database/base_spec.rb +1 -1
  76. data/spec/configuration/database/mongodb_spec.rb +1 -1
  77. data/spec/configuration/database/mysql_spec.rb +1 -1
  78. data/spec/configuration/database/postgresql_spec.rb +1 -1
  79. data/spec/configuration/database/redis_spec.rb +1 -1
  80. data/spec/configuration/database/riak_spec.rb +31 -0
  81. data/spec/configuration/encryptor/gpg_spec.rb +1 -1
  82. data/spec/configuration/encryptor/open_ssl_spec.rb +4 -1
  83. data/spec/configuration/notifier/campfire_spec.rb +1 -1
  84. data/spec/configuration/notifier/hipchat_spec.rb +43 -0
  85. data/spec/configuration/notifier/mail_spec.rb +34 -22
  86. data/spec/configuration/notifier/presently_spec.rb +1 -1
  87. data/spec/configuration/notifier/prowl_spec.rb +28 -0
  88. data/spec/configuration/notifier/twitter_spec.rb +1 -1
  89. data/spec/configuration/storage/cloudfiles_spec.rb +19 -16
  90. data/spec/configuration/storage/dropbox_spec.rb +1 -1
  91. data/spec/configuration/storage/ftp_spec.rb +1 -1
  92. data/spec/configuration/storage/local_spec.rb +1 -1
  93. data/spec/configuration/storage/ninefold_spec.rb +1 -1
  94. data/spec/configuration/storage/rsync_spec.rb +1 -1
  95. data/spec/configuration/storage/s3_spec.rb +1 -1
  96. data/spec/configuration/storage/scp_spec.rb +1 -1
  97. data/spec/configuration/storage/sftp_spec.rb +1 -1
  98. data/spec/configuration/syncer/rsync_spec.rb +1 -1
  99. data/spec/configuration/syncer/s3_spec.rb +1 -1
  100. data/spec/database/base_spec.rb +10 -1
  101. data/spec/database/mongodb_spec.rb +34 -7
  102. data/spec/database/mysql_spec.rb +8 -7
  103. data/spec/database/postgresql_spec.rb +8 -7
  104. data/spec/database/redis_spec.rb +39 -9
  105. data/spec/database/riak_spec.rb +50 -0
  106. data/spec/encryptor/gpg_spec.rb +1 -1
  107. data/spec/encryptor/open_ssl_spec.rb +77 -20
  108. data/spec/errors_spec.rb +306 -0
  109. data/spec/finder_spec.rb +91 -0
  110. data/spec/logger_spec.rb +254 -33
  111. data/spec/model_spec.rb +120 -15
  112. data/spec/notifier/campfire_spec.rb +127 -52
  113. data/spec/notifier/hipchat_spec.rb +193 -0
  114. data/spec/notifier/mail_spec.rb +290 -74
  115. data/spec/notifier/presently_spec.rb +290 -73
  116. data/spec/notifier/prowl_spec.rb +149 -0
  117. data/spec/notifier/twitter_spec.rb +106 -41
  118. data/spec/spec_helper.rb +8 -2
  119. data/spec/splitter_spec.rb +71 -0
  120. data/spec/storage/base_spec.rb +280 -19
  121. data/spec/storage/cloudfiles_spec.rb +38 -22
  122. data/spec/storage/dropbox_spec.rb +17 -13
  123. data/spec/storage/ftp_spec.rb +145 -55
  124. data/spec/storage/local_spec.rb +6 -6
  125. data/spec/storage/ninefold_spec.rb +70 -29
  126. data/spec/storage/object_spec.rb +44 -44
  127. data/spec/storage/rsync_spec.rb +186 -63
  128. data/spec/storage/s3_spec.rb +23 -24
  129. data/spec/storage/scp_spec.rb +116 -41
  130. data/spec/storage/sftp_spec.rb +124 -46
  131. data/spec/syncer/rsync_spec.rb +3 -3
  132. data/spec/syncer/s3_spec.rb +1 -1
  133. data/spec/version_spec.rb +1 -1
  134. data/templates/cli/utility/archive +13 -0
  135. data/{lib/templates → templates/cli/utility}/compressor/bzip2 +1 -1
  136. data/{lib/templates → templates/cli/utility}/compressor/gzip +1 -1
  137. data/{lib/templates → templates/cli/utility}/compressor/lzma +0 -0
  138. data/templates/cli/utility/compressor/pbzip2 +7 -0
  139. data/templates/cli/utility/config +31 -0
  140. data/{lib/templates → templates/cli/utility}/database/mongodb +1 -1
  141. data/{lib/templates → templates/cli/utility}/database/mysql +1 -1
  142. data/{lib/templates → templates/cli/utility}/database/postgresql +1 -1
  143. data/{lib/templates → templates/cli/utility}/database/redis +1 -1
  144. data/templates/cli/utility/database/riak +8 -0
  145. data/{lib/templates → templates/cli/utility}/encryptor/gpg +1 -1
  146. data/templates/cli/utility/encryptor/openssl +9 -0
  147. data/templates/cli/utility/model.erb +23 -0
  148. data/{lib/templates → templates/cli/utility}/notifier/campfire +2 -1
  149. data/templates/cli/utility/notifier/hipchat +15 -0
  150. data/{lib/templates → templates/cli/utility}/notifier/mail +6 -1
  151. data/{lib/templates → templates/cli/utility}/notifier/presently +1 -0
  152. data/templates/cli/utility/notifier/prowl +11 -0
  153. data/{lib/templates → templates/cli/utility}/notifier/twitter +2 -1
  154. data/templates/cli/utility/splitter +7 -0
  155. data/templates/cli/utility/storage/cloudfiles +12 -0
  156. data/{lib/templates → templates/cli/utility}/storage/dropbox +1 -1
  157. data/{lib/templates → templates/cli/utility}/storage/ftp +0 -0
  158. data/templates/cli/utility/storage/local +7 -0
  159. data/{lib/templates → templates/cli/utility}/storage/ninefold +1 -1
  160. data/templates/cli/utility/storage/rsync +11 -0
  161. data/{lib/templates → templates/cli/utility}/storage/s3 +0 -2
  162. data/templates/cli/utility/storage/scp +11 -0
  163. data/templates/cli/utility/storage/sftp +11 -0
  164. data/{lib/templates → templates/cli/utility}/syncer/rsync +1 -1
  165. data/{lib/templates → templates/cli/utility}/syncer/s3 +1 -1
  166. data/templates/general/links +11 -0
  167. data/templates/general/version.erb +2 -0
  168. data/templates/notifier/mail/failure.erb +9 -0
  169. data/templates/notifier/mail/success.erb +7 -0
  170. data/templates/notifier/mail/warning.erb +9 -0
  171. data/templates/storage/dropbox/authorization_url.erb +6 -0
  172. data/templates/storage/dropbox/authorized.erb +4 -0
  173. data/templates/storage/dropbox/cache_file_written.erb +10 -0
  174. metadata +81 -45
  175. data/lib/backup/cli.rb +0 -110
  176. data/lib/backup/exception/command_failed.rb +0 -8
  177. data/lib/backup/exception/command_not_found.rb +0 -8
  178. data/lib/backup/notifier/binder.rb +0 -32
  179. data/lib/backup/notifier/templates/notify_failure.erb +0 -33
  180. data/lib/backup/notifier/templates/notify_success.erb +0 -16
  181. data/lib/templates/archive +0 -7
  182. data/lib/templates/encryptor/openssl +0 -8
  183. data/lib/templates/readme +0 -15
  184. data/lib/templates/storage/cloudfiles +0 -11
  185. data/lib/templates/storage/local +0 -7
  186. data/lib/templates/storage/rsync +0 -11
  187. data/lib/templates/storage/scp +0 -11
  188. 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
@@ -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 limit exceeds it will remove the oldest backup to make room for the newest
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 the local archive filename
25
- def local_file
26
- @local_file ||= File.basename(Backup::Model.file)
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 the name of the file that's stored on the remote location
31
- def remote_file
32
- @remote_file ||= local_file
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 and will be overridden when using
37
- # a service-based storage such as Amazon S3, Rackspace Cloud Files or Dropbox
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 by @keep).
47
- # After that it'll re-assign the objects variable with an array of objects that still remain
48
- # after the removal of the older objects and files (that exceeded the @keep range). And finally
49
- # these remaining objects will be converted to YAML format and are written back to the YAML file
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 = [self] + storage_object.load
54
- if keep.is_a?(Integer) and keep > 0 and objects.count > keep
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 "#{ self.class } started removing (cycling) \"#{ object.remote_file }\"."
58
- object.send(:remove!)
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 Cloud Files container name and path
17
- attr_accessor :container, :path
16
+ # Rackspace Service Net (Allows for LAN-based transfers to avoid charges and improve performance)
17
+ attr_accessor :servicenet
18
18
 
19
19
  ##
20
- # Creates a new instance of the Rackspace Cloud Files storage object
21
- # First it sets the defaults (if any exist) and then evaluates
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 => provider,
63
- :rackspace_username => username,
64
- :rackspace_api_key => api_key,
65
- :rackspace_auth_url => 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
- begin
73
- Logger.message("#{ self.class } started transferring \"#{ remote_file }\".")
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
- begin
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
- rescue Excon::Errors::SocketError; end
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 => error
73
- Logger.warn "Could not read session data from cache. Cache data might be corrupt."
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
- Logger.message("#{ self.class } started transferring \"#{ remote_file }\".")
85
- connection.upload(File.join(local_path, local_file), remote_path, :timeout => timeout)
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
- begin
92
- connection.delete(File.join(remote_path, remote_file))
93
- rescue ::Dropbox::FileNotFoundError
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 "Authorized!"
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
- session
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, "#{api_key + api_secret}")
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
- public # DEPRECATED METHODS #############################################
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
- def email
151
- Logger.warn "[DEPRECATED] Backup::Storage::Dropbox.email is deprecated and will be removed at some point."
152
- end
145
+ template = Backup::Template.new({:session => session, :cached_file => cached_file})
146
+ template.render("storage/dropbox/authorization_url.erb")
153
147
 
154
- def email=(value)
155
- Logger.warn "[DEPRECATED] Backup::Storage::Dropbox.email= is deprecated and will be removed at some point."
156
- end
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
- def password
159
- Logger.warn "[DEPRECATED] Backup::Storage::Dropbox.password is deprecated and will be removed at some point."
160
- end
156
+ template.render("storage/dropbox/authorized.erb")
157
+ write_cache!(session)
158
+ template.render("storage/dropbox/cache_file_written.erb")
161
159
 
162
- def password=(value)
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