nfm-backup 4.0.1a
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.
- checksums.yaml +7 -0
- data/LICENSE.md +24 -0
- data/README.md +20 -0
- data/bin/backup +5 -0
- data/lib/backup.rb +133 -0
- data/lib/backup/archive.rb +170 -0
- data/lib/backup/binder.rb +22 -0
- data/lib/backup/cleaner.rb +116 -0
- data/lib/backup/cli.rb +364 -0
- data/lib/backup/cloud_io/base.rb +41 -0
- data/lib/backup/cloud_io/cloud_files.rb +298 -0
- data/lib/backup/cloud_io/s3.rb +260 -0
- data/lib/backup/compressor/base.rb +35 -0
- data/lib/backup/compressor/bzip2.rb +39 -0
- data/lib/backup/compressor/custom.rb +53 -0
- data/lib/backup/compressor/gzip.rb +74 -0
- data/lib/backup/config.rb +119 -0
- data/lib/backup/config/dsl.rb +102 -0
- data/lib/backup/config/helpers.rb +143 -0
- data/lib/backup/database/base.rb +85 -0
- data/lib/backup/database/mongodb.rb +186 -0
- data/lib/backup/database/mysql.rb +123 -0
- data/lib/backup/database/postgresql.rb +133 -0
- data/lib/backup/database/redis.rb +179 -0
- data/lib/backup/database/riak.rb +82 -0
- data/lib/backup/encryptor/base.rb +29 -0
- data/lib/backup/encryptor/gpg.rb +747 -0
- data/lib/backup/encryptor/open_ssl.rb +72 -0
- data/lib/backup/errors.rb +58 -0
- data/lib/backup/logger.rb +199 -0
- data/lib/backup/logger/console.rb +51 -0
- data/lib/backup/logger/fog_adapter.rb +29 -0
- data/lib/backup/logger/logfile.rb +119 -0
- data/lib/backup/logger/syslog.rb +116 -0
- data/lib/backup/model.rb +454 -0
- data/lib/backup/notifier/base.rb +98 -0
- data/lib/backup/notifier/campfire.rb +69 -0
- data/lib/backup/notifier/hipchat.rb +93 -0
- data/lib/backup/notifier/http_post.rb +122 -0
- data/lib/backup/notifier/mail.rb +238 -0
- data/lib/backup/notifier/nagios.rb +69 -0
- data/lib/backup/notifier/prowl.rb +69 -0
- data/lib/backup/notifier/pushover.rb +80 -0
- data/lib/backup/notifier/slack.rb +149 -0
- data/lib/backup/notifier/twitter.rb +65 -0
- data/lib/backup/package.rb +51 -0
- data/lib/backup/packager.rb +101 -0
- data/lib/backup/pipeline.rb +124 -0
- data/lib/backup/splitter.rb +76 -0
- data/lib/backup/storage/base.rb +57 -0
- data/lib/backup/storage/cloud_files.rb +158 -0
- data/lib/backup/storage/cycler.rb +65 -0
- data/lib/backup/storage/dropbox.rb +236 -0
- data/lib/backup/storage/ftp.rb +98 -0
- data/lib/backup/storage/local.rb +64 -0
- data/lib/backup/storage/ninefold.rb +74 -0
- data/lib/backup/storage/rsync.rb +248 -0
- data/lib/backup/storage/s3.rb +154 -0
- data/lib/backup/storage/scp.rb +67 -0
- data/lib/backup/storage/sftp.rb +82 -0
- data/lib/backup/syncer/base.rb +70 -0
- data/lib/backup/syncer/cloud/base.rb +179 -0
- data/lib/backup/syncer/cloud/cloud_files.rb +83 -0
- data/lib/backup/syncer/cloud/local_file.rb +100 -0
- data/lib/backup/syncer/cloud/s3.rb +110 -0
- data/lib/backup/syncer/rsync/base.rb +48 -0
- data/lib/backup/syncer/rsync/local.rb +31 -0
- data/lib/backup/syncer/rsync/pull.rb +51 -0
- data/lib/backup/syncer/rsync/push.rb +205 -0
- data/lib/backup/template.rb +46 -0
- data/lib/backup/utilities.rb +221 -0
- data/lib/backup/version.rb +5 -0
- data/templates/cli/archive +28 -0
- data/templates/cli/compressor/bzip2 +4 -0
- data/templates/cli/compressor/custom +7 -0
- data/templates/cli/compressor/gzip +4 -0
- data/templates/cli/config +123 -0
- data/templates/cli/databases/mongodb +15 -0
- data/templates/cli/databases/mysql +18 -0
- data/templates/cli/databases/postgresql +16 -0
- data/templates/cli/databases/redis +16 -0
- data/templates/cli/databases/riak +17 -0
- data/templates/cli/encryptor/gpg +27 -0
- data/templates/cli/encryptor/openssl +9 -0
- data/templates/cli/model +26 -0
- data/templates/cli/notifiers/campfire +12 -0
- data/templates/cli/notifiers/hipchat +15 -0
- data/templates/cli/notifiers/http_post +32 -0
- data/templates/cli/notifiers/mail +21 -0
- data/templates/cli/notifiers/nagios +13 -0
- data/templates/cli/notifiers/prowl +11 -0
- data/templates/cli/notifiers/pushover +11 -0
- data/templates/cli/notifiers/twitter +13 -0
- data/templates/cli/splitter +7 -0
- data/templates/cli/storages/cloud_files +11 -0
- data/templates/cli/storages/dropbox +19 -0
- data/templates/cli/storages/ftp +12 -0
- data/templates/cli/storages/local +7 -0
- data/templates/cli/storages/ninefold +9 -0
- data/templates/cli/storages/rsync +17 -0
- data/templates/cli/storages/s3 +14 -0
- data/templates/cli/storages/scp +14 -0
- data/templates/cli/storages/sftp +14 -0
- data/templates/cli/syncers/cloud_files +22 -0
- data/templates/cli/syncers/rsync_local +20 -0
- data/templates/cli/syncers/rsync_pull +28 -0
- data/templates/cli/syncers/rsync_push +28 -0
- data/templates/cli/syncers/s3 +27 -0
- data/templates/general/links +3 -0
- data/templates/general/version.erb +2 -0
- data/templates/notifier/mail/failure.erb +16 -0
- data/templates/notifier/mail/success.erb +16 -0
- data/templates/notifier/mail/warning.erb +16 -0
- data/templates/storage/dropbox/authorization_url.erb +6 -0
- data/templates/storage/dropbox/authorized.erb +4 -0
- data/templates/storage/dropbox/cache_file_written.erb +10 -0
- metadata +688 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'fog'
|
|
3
|
+
|
|
4
|
+
module Backup
|
|
5
|
+
module Storage
|
|
6
|
+
class Ninefold < Base
|
|
7
|
+
include Storage::Cycler
|
|
8
|
+
class Error < Backup::Error; end
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
# Ninefold Credentials
|
|
12
|
+
attr_accessor :storage_token, :storage_secret
|
|
13
|
+
|
|
14
|
+
def initialize(model, storage_id = nil)
|
|
15
|
+
super
|
|
16
|
+
|
|
17
|
+
@path ||= 'backups'
|
|
18
|
+
path.sub!(/^\//, '')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def connection
|
|
24
|
+
@connection ||= Fog::Storage.new(
|
|
25
|
+
:provider => 'Ninefold',
|
|
26
|
+
:ninefold_storage_token => storage_token,
|
|
27
|
+
:ninefold_storage_secret => storage_secret
|
|
28
|
+
)
|
|
29
|
+
end
|
|
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
|
+
|
|
43
|
+
##
|
|
44
|
+
# Queries the connection for the directory for the given +remote_path+
|
|
45
|
+
# Returns nil if not found, or creates the directory if +create+ is true.
|
|
46
|
+
def directory_for(remote_path, create = false)
|
|
47
|
+
directory = connection.directories.get(remote_path)
|
|
48
|
+
if directory.nil? && create
|
|
49
|
+
directory = connection.directories.create(:key => remote_path)
|
|
50
|
+
end
|
|
51
|
+
directory
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Called by the Cycler.
|
|
55
|
+
# Any error raised will be logged as a warning.
|
|
56
|
+
def remove!(package)
|
|
57
|
+
Logger.info "Removing backup package dated #{ package.time }..."
|
|
58
|
+
|
|
59
|
+
remote_path = remote_path_for(package)
|
|
60
|
+
directory = directory_for(remote_path)
|
|
61
|
+
|
|
62
|
+
raise Error, "Directory at '#{ remote_path }' not found" unless directory
|
|
63
|
+
|
|
64
|
+
package.filenames.each do |filename|
|
|
65
|
+
file = directory.files.get(filename)
|
|
66
|
+
file.destroy if file
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
directory.destroy
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Backup
|
|
4
|
+
module Storage
|
|
5
|
+
class RSync < Base
|
|
6
|
+
include Utilities::Helpers
|
|
7
|
+
|
|
8
|
+
##
|
|
9
|
+
# Mode of operation
|
|
10
|
+
#
|
|
11
|
+
# [:ssh (default)]
|
|
12
|
+
# Connects to the remote via SSH.
|
|
13
|
+
# Does not use an rsync daemon on the remote.
|
|
14
|
+
#
|
|
15
|
+
# [:ssh_daemon]
|
|
16
|
+
# Connects to the remote via SSH.
|
|
17
|
+
# Spawns a single-use daemon on the remote, which allows certain
|
|
18
|
+
# daemon features (like modules) to be used.
|
|
19
|
+
#
|
|
20
|
+
# [:rsync_daemon]
|
|
21
|
+
# Connects directly to an rsync daemon via TCP.
|
|
22
|
+
# Data transferred is not encrypted.
|
|
23
|
+
#
|
|
24
|
+
attr_accessor :mode
|
|
25
|
+
|
|
26
|
+
##
|
|
27
|
+
# Server Address
|
|
28
|
+
#
|
|
29
|
+
# If not specified, the storage operation will be local.
|
|
30
|
+
attr_accessor :host
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
# SSH or RSync port
|
|
34
|
+
#
|
|
35
|
+
# For `:ssh` or `:ssh_daemon` mode, this specifies the SSH port to use
|
|
36
|
+
# and defaults to 22.
|
|
37
|
+
#
|
|
38
|
+
# For `:rsync_daemon` mode, this specifies the TCP port to use
|
|
39
|
+
# and defaults to 873.
|
|
40
|
+
attr_accessor :port
|
|
41
|
+
|
|
42
|
+
##
|
|
43
|
+
# SSH User
|
|
44
|
+
#
|
|
45
|
+
# If the user running the backup is not the same user that needs to
|
|
46
|
+
# authenticate with the remote server, specify the user here.
|
|
47
|
+
#
|
|
48
|
+
# The user must have SSH keys setup for passphrase-less access to the
|
|
49
|
+
# remote. If the SSH User does not have passphrase-less keys, or no
|
|
50
|
+
# default keys in their `~/.ssh` directory, you will need to use the
|
|
51
|
+
# `-i` option in `:additional_ssh_options` to specify the
|
|
52
|
+
# passphrase-less key to use.
|
|
53
|
+
#
|
|
54
|
+
# Used only for `:ssh` and `:ssh_daemon` modes.
|
|
55
|
+
attr_accessor :ssh_user
|
|
56
|
+
|
|
57
|
+
##
|
|
58
|
+
# Additional SSH Options
|
|
59
|
+
#
|
|
60
|
+
# Used to supply a String or Array of options to be passed to the SSH
|
|
61
|
+
# command in `:ssh` and `:ssh_daemon` modes.
|
|
62
|
+
#
|
|
63
|
+
# For example, if you need to supply a specific SSH key for the `ssh_user`,
|
|
64
|
+
# you would set this to: "-i '/path/to/id_rsa'". Which would produce:
|
|
65
|
+
#
|
|
66
|
+
# rsync -e "ssh -p 22 -i '/path/to/id_rsa'"
|
|
67
|
+
#
|
|
68
|
+
# Arguments may be single-quoted, but should not contain any double-quotes.
|
|
69
|
+
#
|
|
70
|
+
# Used only for `:ssh` and `:ssh_daemon` modes.
|
|
71
|
+
attr_accessor :additional_ssh_options
|
|
72
|
+
|
|
73
|
+
##
|
|
74
|
+
# RSync User
|
|
75
|
+
#
|
|
76
|
+
# If the user running the backup is not the same user that needs to
|
|
77
|
+
# authenticate with the rsync daemon, specify the user here.
|
|
78
|
+
#
|
|
79
|
+
# Used only for `:ssh_daemon` and `:rsync_daemon` modes.
|
|
80
|
+
attr_accessor :rsync_user
|
|
81
|
+
|
|
82
|
+
##
|
|
83
|
+
# RSync Password
|
|
84
|
+
#
|
|
85
|
+
# If specified, Backup will write the password to a temporary file and
|
|
86
|
+
# use it with rsync's `--password-file` option for daemon authentication.
|
|
87
|
+
#
|
|
88
|
+
# Note that setting this will override `rsync_password_file`.
|
|
89
|
+
#
|
|
90
|
+
# Used only for `:ssh_daemon` and `:rsync_daemon` modes.
|
|
91
|
+
attr_accessor :rsync_password
|
|
92
|
+
|
|
93
|
+
##
|
|
94
|
+
# RSync Password File
|
|
95
|
+
#
|
|
96
|
+
# If specified, this path will be passed to rsync's `--password-file`
|
|
97
|
+
# option for daemon authentication.
|
|
98
|
+
#
|
|
99
|
+
# Used only for `:ssh_daemon` and `:rsync_daemon` modes.
|
|
100
|
+
attr_accessor :rsync_password_file
|
|
101
|
+
|
|
102
|
+
##
|
|
103
|
+
# Additional String or Array of options for the rsync cli
|
|
104
|
+
attr_accessor :additional_rsync_options
|
|
105
|
+
|
|
106
|
+
##
|
|
107
|
+
# Flag for compressing (only compresses for the transfer)
|
|
108
|
+
attr_accessor :compress
|
|
109
|
+
|
|
110
|
+
##
|
|
111
|
+
# Path to store the synced backup package file(s) to.
|
|
112
|
+
#
|
|
113
|
+
# If no +host+ is specified, then +path+ will be local, and the only
|
|
114
|
+
# other used option would be +additional_rsync_options+.
|
|
115
|
+
# +path+ will be expanded, so '~/my_path' will expand to '$HOME/my_path'.
|
|
116
|
+
#
|
|
117
|
+
# If a +host+ is specified, this will be a path on the host.
|
|
118
|
+
# If +mode+ is `:ssh` (default), then any relative path, or path starting
|
|
119
|
+
# with '~/' will be relative to the directory the ssh_user is logged
|
|
120
|
+
# into. For `:ssh_daemon` or `:rsync_daemon` modes, this would reference
|
|
121
|
+
# an rsync module/path.
|
|
122
|
+
#
|
|
123
|
+
# In :ssh_daemon and :rsync_daemon modes, +path+ (or path defined by
|
|
124
|
+
# your rsync module) must already exist.
|
|
125
|
+
#
|
|
126
|
+
# In :ssh mode or local operation (no +host+ specified), +path+ will
|
|
127
|
+
# be created if needed - either locally, or on the remote for :ssh mode.
|
|
128
|
+
attr_accessor :path
|
|
129
|
+
|
|
130
|
+
def initialize(model, storage_id = nil)
|
|
131
|
+
super
|
|
132
|
+
|
|
133
|
+
@mode ||= :ssh
|
|
134
|
+
@port ||= mode == :rsync_daemon ? 873 : 22
|
|
135
|
+
@compress ||= false
|
|
136
|
+
@path ||= '~/backups'
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
private
|
|
140
|
+
|
|
141
|
+
def transfer!
|
|
142
|
+
write_password_file
|
|
143
|
+
create_remote_path
|
|
144
|
+
|
|
145
|
+
package.filenames.each do |filename|
|
|
146
|
+
src = "'#{ File.join(Config.tmp_path, filename) }'"
|
|
147
|
+
dest = "#{ host_options }'#{ File.join(remote_path, filename) }'"
|
|
148
|
+
Logger.info "Syncing to #{ dest }..."
|
|
149
|
+
run("#{ rsync_command } #{ src } #{ dest }")
|
|
150
|
+
end
|
|
151
|
+
ensure
|
|
152
|
+
remove_password_file
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
##
|
|
156
|
+
# Other storages add an additional timestamp directory to this path.
|
|
157
|
+
# This is not desired here, since we need to transfer the package files
|
|
158
|
+
# to the same location each time.
|
|
159
|
+
def remote_path
|
|
160
|
+
@remote_path ||= begin
|
|
161
|
+
if host
|
|
162
|
+
path.sub(/^~\//, '').sub(/\/$/, '')
|
|
163
|
+
else
|
|
164
|
+
File.expand_path(path)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
##
|
|
170
|
+
# Runs a 'mkdir -p' command on the host (or locally) to ensure the
|
|
171
|
+
# dest_path exists. This is used because we're transferring a single
|
|
172
|
+
# file, and rsync won't attempt to create the intermediate directories.
|
|
173
|
+
#
|
|
174
|
+
# This is only applicable locally and in :ssh mode.
|
|
175
|
+
# In :ssh_daemon and :rsync_daemon modes the `path` would include a
|
|
176
|
+
# module name that must define a path on the remote that already exists.
|
|
177
|
+
def create_remote_path
|
|
178
|
+
if host
|
|
179
|
+
run("#{ utility(:ssh) } #{ ssh_transport_args } #{ host } " +
|
|
180
|
+
%Q["mkdir -p '#{ remote_path }'"]) if mode == :ssh
|
|
181
|
+
else
|
|
182
|
+
FileUtils.mkdir_p(remote_path)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def host_options
|
|
187
|
+
@host_options ||= begin
|
|
188
|
+
if !host
|
|
189
|
+
''
|
|
190
|
+
elsif mode == :ssh
|
|
191
|
+
"#{ host }:"
|
|
192
|
+
else
|
|
193
|
+
user = "#{ rsync_user }@" if rsync_user
|
|
194
|
+
"#{ user }#{ host }::"
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def rsync_command
|
|
200
|
+
@rsync_command ||= begin
|
|
201
|
+
cmd = utility(:rsync) << ' --archive' <<
|
|
202
|
+
" #{ Array(additional_rsync_options).join(' ') }".rstrip
|
|
203
|
+
cmd << compress_option << password_option << transport_options if host
|
|
204
|
+
cmd
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def compress_option
|
|
209
|
+
compress ? ' --compress' : ''
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def password_option
|
|
213
|
+
return '' if mode == :ssh
|
|
214
|
+
|
|
215
|
+
path = @password_file ? @password_file.path : rsync_password_file
|
|
216
|
+
path ? " --password-file='#{ File.expand_path(path) }'" : ''
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def transport_options
|
|
220
|
+
if mode == :rsync_daemon
|
|
221
|
+
" --port #{ port }"
|
|
222
|
+
else
|
|
223
|
+
%Q[ -e "#{ utility(:ssh) } #{ ssh_transport_args }"]
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def ssh_transport_args
|
|
228
|
+
args = "-p #{ port } "
|
|
229
|
+
args << "-l #{ ssh_user } " if ssh_user
|
|
230
|
+
args << Array(additional_ssh_options).join(' ')
|
|
231
|
+
args.rstrip
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def write_password_file
|
|
235
|
+
return unless host && rsync_password && mode != :ssh
|
|
236
|
+
|
|
237
|
+
@password_file = Tempfile.new('backup-rsync-password')
|
|
238
|
+
@password_file.write(rsync_password)
|
|
239
|
+
@password_file.close
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def remove_password_file
|
|
243
|
+
@password_file.delete if @password_file
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'backup/cloud_io/s3'
|
|
3
|
+
|
|
4
|
+
module Backup
|
|
5
|
+
module Storage
|
|
6
|
+
class S3 < Base
|
|
7
|
+
include Storage::Cycler
|
|
8
|
+
class Error < Backup::Error; end
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
# Amazon Simple Storage Service (S3) Credentials
|
|
12
|
+
attr_accessor :access_key_id, :secret_access_key, :use_iam_profile
|
|
13
|
+
|
|
14
|
+
##
|
|
15
|
+
# Amazon S3 bucket name
|
|
16
|
+
attr_accessor :bucket
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# Region of the specified S3 bucket
|
|
20
|
+
attr_accessor :region
|
|
21
|
+
|
|
22
|
+
##
|
|
23
|
+
# Multipart chunk size, specified in MiB.
|
|
24
|
+
#
|
|
25
|
+
# Each package file larger than +chunk_size+
|
|
26
|
+
# will be uploaded using S3 Multipart Upload.
|
|
27
|
+
#
|
|
28
|
+
# Minimum: 5 (but may be disabled with 0)
|
|
29
|
+
# Maximum: 5120
|
|
30
|
+
# Default: 5
|
|
31
|
+
attr_accessor :chunk_size
|
|
32
|
+
|
|
33
|
+
##
|
|
34
|
+
# Number of times to retry failed operations.
|
|
35
|
+
#
|
|
36
|
+
# Default: 10
|
|
37
|
+
attr_accessor :max_retries
|
|
38
|
+
|
|
39
|
+
##
|
|
40
|
+
# Time in seconds to pause before each retry.
|
|
41
|
+
#
|
|
42
|
+
# Default: 30
|
|
43
|
+
attr_accessor :retry_waitsec
|
|
44
|
+
|
|
45
|
+
##
|
|
46
|
+
# Encryption algorithm to use for Amazon Server-Side Encryption
|
|
47
|
+
#
|
|
48
|
+
# Supported values:
|
|
49
|
+
#
|
|
50
|
+
# - :aes256
|
|
51
|
+
#
|
|
52
|
+
# Default: nil
|
|
53
|
+
attr_accessor :encryption
|
|
54
|
+
|
|
55
|
+
##
|
|
56
|
+
# Storage class to use for the S3 objects uploaded
|
|
57
|
+
#
|
|
58
|
+
# Supported values:
|
|
59
|
+
#
|
|
60
|
+
# - :standard (default)
|
|
61
|
+
# - :reduced_redundancy
|
|
62
|
+
#
|
|
63
|
+
# Default: :standard
|
|
64
|
+
attr_accessor :storage_class
|
|
65
|
+
|
|
66
|
+
##
|
|
67
|
+
# Additional options to pass along to fog.
|
|
68
|
+
# e.g. Fog::Storage.new({ :provider => 'AWS' }.merge(fog_options))
|
|
69
|
+
attr_accessor :fog_options
|
|
70
|
+
|
|
71
|
+
def initialize(model, storage_id = nil)
|
|
72
|
+
super
|
|
73
|
+
|
|
74
|
+
@chunk_size ||= 5 # MiB
|
|
75
|
+
@max_retries ||= 10
|
|
76
|
+
@retry_waitsec ||= 30
|
|
77
|
+
@path ||= 'backups'
|
|
78
|
+
@storage_class ||= :standard
|
|
79
|
+
path.sub!(/^\//, '')
|
|
80
|
+
|
|
81
|
+
check_configuration
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def cloud_io
|
|
87
|
+
@cloud_io ||= CloudIO::S3.new(
|
|
88
|
+
:access_key_id => access_key_id,
|
|
89
|
+
:secret_access_key => secret_access_key,
|
|
90
|
+
:use_iam_profile => use_iam_profile,
|
|
91
|
+
:region => region,
|
|
92
|
+
:bucket => bucket,
|
|
93
|
+
:encryption => encryption,
|
|
94
|
+
:storage_class => storage_class,
|
|
95
|
+
:max_retries => max_retries,
|
|
96
|
+
:retry_waitsec => retry_waitsec,
|
|
97
|
+
:chunk_size => chunk_size,
|
|
98
|
+
:fog_options => fog_options
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def transfer!
|
|
103
|
+
package.filenames.each do |filename|
|
|
104
|
+
src = File.join(Config.tmp_path, filename)
|
|
105
|
+
dest = File.join(remote_path, filename)
|
|
106
|
+
Logger.info "Storing '#{ bucket }/#{ dest }'..."
|
|
107
|
+
cloud_io.upload(src, dest)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Called by the Cycler.
|
|
112
|
+
# Any error raised will be logged as a warning.
|
|
113
|
+
def remove!(package)
|
|
114
|
+
Logger.info "Removing backup package dated #{ package.time }..."
|
|
115
|
+
|
|
116
|
+
remote_path = remote_path_for(package)
|
|
117
|
+
objects = cloud_io.objects(remote_path)
|
|
118
|
+
|
|
119
|
+
raise Error, "Package at '#{ remote_path }' not found" if objects.empty?
|
|
120
|
+
|
|
121
|
+
cloud_io.delete(objects)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def check_configuration
|
|
125
|
+
if use_iam_profile
|
|
126
|
+
required = %w{ bucket }
|
|
127
|
+
else
|
|
128
|
+
required = %w{ access_key_id secret_access_key bucket }
|
|
129
|
+
end
|
|
130
|
+
raise Error, <<-EOS if required.map {|name| send(name) }.any?(&:nil?)
|
|
131
|
+
Configuration Error
|
|
132
|
+
#{ required.map {|name| "##{ name }"}.join(', ') } are all required
|
|
133
|
+
EOS
|
|
134
|
+
|
|
135
|
+
raise Error, <<-EOS if chunk_size > 0 && !chunk_size.between?(5, 5120)
|
|
136
|
+
Configuration Error
|
|
137
|
+
#chunk_size must be between 5 and 5120 (or 0 to disable multipart)
|
|
138
|
+
EOS
|
|
139
|
+
|
|
140
|
+
raise Error, <<-EOS if encryption && encryption.to_s.upcase != 'AES256'
|
|
141
|
+
Configuration Error
|
|
142
|
+
#encryption must be :aes256 or nil
|
|
143
|
+
EOS
|
|
144
|
+
|
|
145
|
+
classes = ['STANDARD', 'REDUCED_REDUNDANCY']
|
|
146
|
+
raise Error, <<-EOS unless classes.include?(storage_class.to_s.upcase)
|
|
147
|
+
Configuration Error
|
|
148
|
+
#storage_class must be :standard or :reduced_redundancy
|
|
149
|
+
EOS
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|