backup 3.3.2 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,8 +10,9 @@ module Backup
10
10
  # to the given +storage+ and Package#trigger (Model#trigger).
11
11
  # Then, calls the +storage+ to remove the files for any older
12
12
  # packages that were removed from the YAML storage file.
13
- def cycle!(storage, package)
14
- @storage, @package = storage, package
13
+ def cycle!(storage)
14
+ @storage = storage
15
+ @package = storage.package
15
16
  @storage_file = storage_file
16
17
 
17
18
  update_storage_file!
@@ -52,10 +53,9 @@ module Backup
52
53
  # Return full path to the YAML data file,
53
54
  # based on the current values of @storage and @package
54
55
  def storage_file
55
- type = @storage.class.to_s.split('::').last
56
- suffix = @storage.storage_id.to_s.strip.gsub(/[\W]/, '_')
57
- filename = suffix.empty? ? type : "#{type}-#{suffix}"
58
- File.join(Config.data_path, @package.trigger, "#{filename}.yml")
56
+ filename = @storage.class.to_s.split('::').last
57
+ filename << "-#{ @storage.storage_id }" if @storage.storage_id
58
+ File.join(Config.data_path, @package.trigger, "#{ filename }.yml")
59
59
  end
60
60
 
61
61
  ##
@@ -1,7 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
- ##
4
- # Only load the Dropbox gem when the Backup::Storage::Dropbox class is loaded
5
3
  Backup::Dependency.load('dropbox-sdk')
6
4
 
7
5
  module Backup
@@ -19,10 +17,6 @@ module Backup
19
17
  # :dropbox (full access)
20
18
  attr_accessor :access_type
21
19
 
22
- ##
23
- # Path to where the backups will be stored
24
- attr_accessor :path
25
-
26
20
  ##
27
21
  # Chunk size, specified in MiB, for the ChunkedUploader.
28
22
  attr_accessor :chunk_size
@@ -35,23 +29,18 @@ module Backup
35
29
  # Seconds to wait between chunk retries.
36
30
  attr_accessor :retry_waitsec
37
31
 
38
- attr_deprecate :email, :version => '3.0.17'
39
- attr_deprecate :password, :version => '3.0.17'
40
-
41
- attr_deprecate :timeout, :version => '3.0.21'
42
-
43
32
  ##
44
33
  # Creates a new instance of the storage object
45
34
  def initialize(model, storage_id = nil, &block)
46
- super(model, storage_id)
35
+ super
36
+ instance_eval(&block) if block_given?
47
37
 
48
38
  @path ||= 'backups'
49
39
  @access_type ||= :app_folder
50
40
  @chunk_size ||= 4 # MiB
51
41
  @chunk_retries ||= 10
52
42
  @retry_waitsec ||= 30
53
-
54
- instance_eval(&block) if block_given?
43
+ path.sub!(/^\//, '')
55
44
  end
56
45
 
57
46
  private
@@ -85,7 +74,7 @@ module Backup
85
74
  # Attempt to load a cached session
86
75
  def cached_session
87
76
  session = false
88
- if cache_exists?
77
+ if File.exist?(cached_file)
89
78
  begin
90
79
  session = DropboxSession.deserialize(File.read(cached_file))
91
80
  Logger.info "Session data loaded from cache!"
@@ -105,65 +94,58 @@ module Backup
105
94
  # Each chunk will be retried +chunk_retries+ times, pausing +retry_waitsec+
106
95
  # between retries, if errors occur.
107
96
  def transfer!
108
- remote_path = remote_path_for(@package)
97
+ package.filenames.each do |filename|
98
+ src = File.join(Config.tmp_path, filename)
99
+ dest = File.join(remote_path, filename)
100
+ Logger.info "Storing '#{ dest }'..."
109
101
 
110
- files_to_transfer_for(@package) do |local_file, remote_file|
111
- Logger.info "#{ storage_name } started transferring '#{ local_file }'."
112
-
113
- uploader, retries = nil, 0
114
- File.open(File.join(local_path, local_file), 'r') do |file|
102
+ uploader = nil
103
+ File.open(src, 'r') do |file|
115
104
  uploader = connection.get_chunked_uploader(file, file.stat.size)
116
105
  while uploader.offset < uploader.total_size
117
- begin
106
+ with_retries do
118
107
  uploader.upload(1024**2 * chunk_size)
119
- retries = 0
120
- # Timeout::Error is not a StandardError under ruby-1.8.7
121
- rescue StandardError, Timeout::Error => err
122
- retries += 1
123
- if retries <= chunk_retries
124
- Logger.info "Chunk retry #{ retries } of #{ chunk_retries }."
125
- sleep(retry_waitsec)
126
- retry
127
- end
128
- raise Errors::Storage::Dropbox::TransferError.
129
- wrap(err, 'Dropbox upload failed!')
130
108
  end
131
109
  end
132
110
  end
133
111
 
134
- uploader.finish(File.join(remote_path, remote_file))
112
+ with_retries do
113
+ uploader.finish(dest)
114
+ end
135
115
  end
136
- end
137
116
 
138
- ##
139
- # Removes the transferred archive file(s) from the storage location.
140
- # Any error raised will be rescued during Cycling
141
- # and a warning will be logged, containing the error message.
142
- def remove!(package)
143
- remote_path = remote_path_for(package)
117
+ rescue => err
118
+ raise Errors::Storage::Dropbox::TransferError.wrap(err, 'Upload Failed!')
119
+ end
144
120
 
145
- messages = []
146
- transferred_files_for(package) do |local_file, remote_file|
147
- messages << "#{storage_name} started removing " +
148
- "'#{ local_file }' from Dropbox."
121
+ # Timeout::Error is not a StandardError under ruby-1.8.7
122
+ def with_retries
123
+ retries = 0
124
+ begin
125
+ yield
126
+ rescue StandardError, Timeout::Error => err
127
+ retries += 1
128
+ raise if retries > chunk_retries
129
+
130
+ Logger.info Errors::Storage::Dropbox::TransferError.
131
+ wrap(err, "Retry ##{ retries } of #{ chunk_retries }.")
132
+ sleep(retry_waitsec)
133
+ retry
149
134
  end
150
- Logger.info messages.join("\n")
135
+ end
151
136
 
152
- connection.file_delete(remote_path)
137
+ # Called by the Cycler.
138
+ # Any error raised will be logged as a warning.
139
+ def remove!(package)
140
+ Logger.info "Removing backup package dated #{ package.time }..."
141
+
142
+ connection.file_delete(remote_path_for(package))
153
143
  end
154
144
 
155
- ##
156
- # Returns the path to the cached file
157
145
  def cached_file
158
146
  File.join(Config.cache_path, api_key + api_secret)
159
147
  end
160
148
 
161
- ##
162
- # Checks to see if the cache file exists
163
- def cache_exists?
164
- File.exist?(cached_file)
165
- end
166
-
167
149
  ##
168
150
  # Serializes and writes the Dropbox session to a cache file
169
151
  def write_cache!(session)
@@ -211,6 +193,10 @@ module Backup
211
193
  )
212
194
  end
213
195
 
196
+ attr_deprecate :email, :version => '3.0.17'
197
+ attr_deprecate :password, :version => '3.0.17'
198
+ attr_deprecate :timeout, :version => '3.0.21'
199
+
214
200
  end
215
201
  end
216
202
  end
@@ -1,7 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
- ##
4
- # Only load the Net::FTP library/gem when the Backup::Storage::FTP class is loaded
5
3
  require 'net/ftp'
6
4
 
7
5
  module Backup
@@ -16,26 +14,18 @@ module Backup
16
14
  # Server IP Address and FTP port
17
15
  attr_accessor :ip, :port
18
16
 
19
- ##
20
- # Path to store backups to
21
- attr_accessor :path
22
-
23
17
  ##
24
18
  # use passive mode?
25
19
  attr_accessor :passive_mode
26
20
 
27
- ##
28
- # Creates a new instance of the storage object
29
21
  def initialize(model, storage_id = nil, &block)
30
- super(model, storage_id)
22
+ super
23
+ instance_eval(&block) if block_given?
31
24
 
32
25
  @port ||= 21
33
26
  @path ||= 'backups'
34
27
  @passive_mode ||= false
35
-
36
- instance_eval(&block) if block_given?
37
-
38
- @path = path.sub(/^\~\//, '')
28
+ path.sub!(/^~\//, '')
39
29
  end
40
30
 
41
31
  private
@@ -58,38 +48,28 @@ module Backup
58
48
  end
59
49
  end
60
50
 
61
- ##
62
- # Transfers the archived file to the specified remote server
63
51
  def transfer!
64
- remote_path = remote_path_for(@package)
65
-
66
52
  connection do |ftp|
67
- create_remote_path(remote_path, ftp)
68
-
69
- files_to_transfer_for(@package) do |local_file, remote_file|
70
- Logger.info "#{storage_name} started transferring " +
71
- "'#{ local_file }' to '#{ ip }'."
72
- ftp.put(
73
- File.join(local_path, local_file),
74
- File.join(remote_path, remote_file)
75
- )
53
+ create_remote_path(ftp)
54
+
55
+ package.filenames.each do |filename|
56
+ src = File.join(Config.tmp_path, filename)
57
+ dest = File.join(remote_path, filename)
58
+ Logger.info "Storing '#{ ip }:#{ dest }'..."
59
+ ftp.put(src, dest)
76
60
  end
77
61
  end
78
62
  end
79
63
 
80
- ##
81
- # Removes the transferred archive file(s) from the storage location.
82
- # Any error raised will be rescued during Cycling
83
- # and a warning will be logged, containing the error message.
64
+ # Called by the Cycler.
65
+ # Any error raised will be logged as a warning.
84
66
  def remove!(package)
85
- remote_path = remote_path_for(package)
67
+ Logger.info "Removing backup package dated #{ package.time }..."
86
68
 
69
+ remote_path = remote_path_for(package)
87
70
  connection do |ftp|
88
- transferred_files_for(package) do |local_file, remote_file|
89
- Logger.info "#{storage_name} started removing " +
90
- "'#{ local_file }' from '#{ ip }'."
91
-
92
- ftp.delete(File.join(remote_path, remote_file))
71
+ package.filenames.each do |filename|
72
+ ftp.delete(File.join(remote_path, filename))
93
73
  end
94
74
 
95
75
  ftp.rmdir(remote_path)
@@ -104,7 +84,7 @@ module Backup
104
84
  # '/') and loop through that to create the directories one by one.
105
85
  # Net::FTP raises an exception when the directory it's trying to create
106
86
  # already exists, so we have rescue it
107
- def create_remote_path(remote_path, ftp)
87
+ def create_remote_path(ftp)
108
88
  path_parts = Array.new
109
89
  remote_path.split('/').each do |path_part|
110
90
  path_parts << path_part
@@ -4,76 +4,57 @@ module Backup
4
4
  module Storage
5
5
  class Local < Base
6
6
 
7
- ##
8
- # Path where the backup will be stored.
9
- attr_accessor :path
10
-
11
- ##
12
- # Creates a new instance of the storage object
13
7
  def initialize(model, storage_id = nil, &block)
14
- super(model, storage_id)
15
-
16
- @path ||= File.join(
17
- File.expand_path(ENV['HOME'] || ''),
18
- 'backups'
19
- )
20
-
8
+ super
21
9
  instance_eval(&block) if block_given?
22
10
 
23
- @path = File.expand_path(@path)
11
+ @path ||= '~/backups'
24
12
  end
25
13
 
26
14
  private
27
15
 
28
- ##
29
- # Transfers the archived file to the specified path
30
16
  def transfer!
31
- remote_path = remote_path_for(@package)
32
17
  FileUtils.mkdir_p(remote_path)
33
18
 
34
- files_to_transfer_for(@package) do |local_file, remote_file|
35
- Logger.info "#{storage_name} started transferring '#{ local_file }'."
19
+ transfer_method = package_movable? ? :mv : :cp
20
+ package.filenames.each do |filename|
21
+ src = File.join(Config.tmp_path, filename)
22
+ dest = File.join(remote_path, filename)
23
+ Logger.info "Storing '#{ dest }'..."
36
24
 
37
- src_path = File.join(local_path, local_file)
38
- dst_path = File.join(remote_path, remote_file)
39
- FileUtils.send(transfer_method, src_path, dst_path)
25
+ FileUtils.send(transfer_method, src, dest)
40
26
  end
41
27
  end
42
28
 
43
- ##
44
- # Removes the transferred archive file(s) from the storage location.
45
- # Any error raised will be rescued during Cycling
46
- # and a warning will be logged, containing the error message.
29
+ # Called by the Cycler.
30
+ # Any error raised will be logged as a warning.
47
31
  def remove!(package)
48
- remote_path = remote_path_for(package)
32
+ Logger.info "Removing backup package dated #{ package.time }..."
49
33
 
50
- messages = []
51
- transferred_files_for(package) do |local_file, remote_file|
52
- messages << "#{storage_name} started removing '#{ local_file }'."
53
- end
54
- Logger.info messages.join("\n")
34
+ FileUtils.rm_r(remote_path_for(package))
35
+ end
55
36
 
56
- FileUtils.rm_r(remote_path)
37
+ # expanded since this is a local path
38
+ def remote_path(pkg = package)
39
+ File.expand_path(super)
57
40
  end
41
+ alias :remote_path_for :remote_path
58
42
 
59
43
  ##
60
- # Set and return the transfer method.
61
44
  # If this Local Storage is not the last Storage for the Model,
62
45
  # force the transfer to use a *copy* operation and issue a warning.
63
- def transfer_method
64
- return @transfer_method if @transfer_method
65
-
66
- if self == @model.storages.last
67
- @transfer_method = :mv
46
+ def package_movable?
47
+ if self == model.storages.last
48
+ true
68
49
  else
69
50
  Logger.warn Errors::Storage::Local::TransferError.new(<<-EOS)
70
51
  Local File Copy Warning!
71
- The final backup file(s) for '#{@model.label}' (#{@model.trigger})
72
- will be *copied* to '#{remote_path_for(@package)}'
52
+ The final backup file(s) for '#{ model.label }' (#{ model.trigger })
53
+ will be *copied* to '#{ remote_path }'
73
54
  To avoid this, when using more than one Storage, the 'Local' Storage
74
55
  should be added *last* so the files may be *moved* to their destination.
75
56
  EOS
76
- @transfer_method = :cp
57
+ false
77
58
  end
78
59
  end
79
60
 
@@ -1,7 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
- ##
4
- # Only load the Fog gem when the Backup::Storage::Ninefold class is loaded
5
3
  Backup::Dependency.load('fog')
6
4
 
7
5
  module Backup
@@ -12,39 +10,36 @@ module Backup
12
10
  # Ninefold Credentials
13
11
  attr_accessor :storage_token, :storage_secret
14
12
 
15
- ##
16
- # Ninefold directory path
17
- attr_accessor :path
18
-
19
- ##
20
- # Creates a new instance of the storage object
21
13
  def initialize(model, storage_id = nil, &block)
22
- super(model, storage_id)
14
+ super
15
+ instance_eval(&block) if block_given?
23
16
 
24
17
  @path ||= 'backups'
25
-
26
- instance_eval(&block) if block_given?
18
+ path.sub!(/^\//, '')
27
19
  end
28
20
 
29
-
30
21
  private
31
22
 
32
- ##
33
- # This is the provider that Fog uses for the Ninefold storage
34
- def provider
35
- 'Ninefold'
36
- end
37
-
38
- ##
39
- # Establishes a connection to Amazon S3
40
23
  def connection
41
24
  @connection ||= Fog::Storage.new(
42
- :provider => provider,
25
+ :provider => 'Ninefold',
43
26
  :ninefold_storage_token => storage_token,
44
27
  :ninefold_storage_secret => storage_secret
45
28
  )
46
29
  end
47
30
 
31
+ def transfer!
32
+ directory = directory_for(remote_path, true)
33
+ package.filenames.each do |filename|
34
+ src = File.join(Config.tmp_path, filename)
35
+ dest = File.join(remote_path, filename)
36
+ Logger.info "Storing '#{ dest }'..."
37
+ File.open(src, 'r') do |file|
38
+ directory.files.create(:key => filename, :body => file)
39
+ end
40
+ end
41
+ end
42
+
48
43
  ##
49
44
  # Queries the connection for the directory for the given +remote_path+
50
45
  # Returns nil if not found, or creates the directory if +create+ is true.
@@ -56,59 +51,23 @@ module Backup
56
51
  directory
57
52
  end
58
53
 
59
- def remote_path_for(package)
60
- super(package).sub(/^\//, '')
61
- end
62
-
63
- ##
64
- # Transfers the archived file to the specified directory
65
- def transfer!
66
- remote_path = remote_path_for(@package)
67
-
68
- directory = directory_for(remote_path, true)
69
-
70
- files_to_transfer_for(@package) do |local_file, remote_file|
71
- Logger.info "#{storage_name} started transferring '#{ local_file }'."
72
-
73
- File.open(File.join(local_path, local_file), 'r') do |file|
74
- directory.files.create(:key => remote_file, :body => file)
75
- end
76
- end
77
- end
78
-
79
- ##
80
- # Removes the transferred archive file(s) from the storage location.
81
- # Any error raised will be rescued during Cycling
82
- # and a warning will be logged, containing the error message.
54
+ # Called by the Cycler.
55
+ # Any error raised will be logged as a warning.
83
56
  def remove!(package)
84
- remote_path = remote_path_for(package)
85
-
86
- if directory = directory_for(remote_path)
87
- not_found = []
88
-
89
- transferred_files_for(package) do |local_file, remote_file|
90
- Logger.info "#{storage_name} started removing " +
91
- "'#{ local_file }' from Ninefold."
57
+ Logger.info "Removing backup package dated #{ package.time }..."
92
58
 
93
- if file = directory.files.get(remote_file)
94
- file.destroy
95
- else
96
- not_found << remote_file
97
- end
98
- end
59
+ remote_path = remote_path_for(package)
60
+ directory = directory_for(remote_path)
99
61
 
100
- directory.destroy
62
+ raise Errors::Storage::Ninefold::NotFoundError,
63
+ "Directory at '#{ remote_path }' not found" unless directory
101
64
 
102
- unless not_found.empty?
103
- raise Errors::Storage::Ninefold::NotFoundError, <<-EOS
104
- The following file(s) were not found in '#{ remote_path }'
105
- #{ not_found.join("\n") }
106
- EOS
107
- end
108
- else
109
- raise Errors::Storage::Ninefold::NotFoundError,
110
- "Directory at '#{remote_path}' not found"
65
+ package.filenames.each do |filename|
66
+ file = directory.files.get(filename)
67
+ file.destroy if file
111
68
  end
69
+
70
+ directory.destroy
112
71
  end
113
72
 
114
73
  end