backup 3.3.2 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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