backup 4.4.1 → 5.0.0.beta.1
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 +5 -5
- data/LICENSE +19 -0
- data/README.md +1 -1
- data/lib/backup.rb +74 -78
- data/lib/backup/archive.rb +31 -32
- data/lib/backup/binder.rb +2 -6
- data/lib/backup/cleaner.rb +14 -18
- data/lib/backup/cli.rb +104 -108
- data/lib/backup/cloud_io/base.rb +4 -7
- data/lib/backup/cloud_io/cloud_files.rb +60 -62
- data/lib/backup/cloud_io/s3.rb +69 -76
- data/lib/backup/compressor/base.rb +4 -7
- data/lib/backup/compressor/bzip2.rb +3 -7
- data/lib/backup/compressor/custom.rb +2 -6
- data/lib/backup/compressor/gzip.rb +16 -17
- data/lib/backup/config.rb +17 -18
- data/lib/backup/config/dsl.rb +16 -17
- data/lib/backup/config/helpers.rb +10 -16
- data/lib/backup/database/base.rb +22 -21
- data/lib/backup/database/mongodb.rb +36 -37
- data/lib/backup/database/mysql.rb +40 -41
- data/lib/backup/database/openldap.rb +8 -10
- data/lib/backup/database/postgresql.rb +29 -30
- data/lib/backup/database/redis.rb +27 -30
- data/lib/backup/database/riak.rb +15 -18
- data/lib/backup/database/sqlite.rb +4 -6
- data/lib/backup/encryptor/base.rb +2 -4
- data/lib/backup/encryptor/gpg.rb +49 -59
- data/lib/backup/encryptor/open_ssl.rb +11 -14
- data/lib/backup/errors.rb +7 -12
- data/lib/backup/logger.rb +16 -18
- data/lib/backup/logger/console.rb +5 -8
- data/lib/backup/logger/fog_adapter.rb +2 -6
- data/lib/backup/logger/logfile.rb +10 -12
- data/lib/backup/logger/syslog.rb +2 -4
- data/lib/backup/model.rb +75 -40
- data/lib/backup/notifier/base.rb +24 -26
- data/lib/backup/notifier/campfire.rb +9 -11
- data/lib/backup/notifier/command.rb +0 -3
- data/lib/backup/notifier/datadog.rb +9 -12
- data/lib/backup/notifier/flowdock.rb +13 -17
- data/lib/backup/notifier/hipchat.rb +11 -13
- data/lib/backup/notifier/http_post.rb +11 -14
- data/lib/backup/notifier/mail.rb +44 -47
- data/lib/backup/notifier/nagios.rb +5 -9
- data/lib/backup/notifier/pagerduty.rb +10 -12
- data/lib/backup/notifier/prowl.rb +15 -15
- data/lib/backup/notifier/pushover.rb +7 -10
- data/lib/backup/notifier/ses.rb +34 -16
- data/lib/backup/notifier/slack.rb +39 -40
- data/lib/backup/notifier/twitter.rb +2 -5
- data/lib/backup/notifier/zabbix.rb +11 -14
- data/lib/backup/package.rb +5 -9
- data/lib/backup/packager.rb +16 -17
- data/lib/backup/pipeline.rb +17 -21
- data/lib/backup/splitter.rb +8 -11
- data/lib/backup/storage/base.rb +5 -8
- data/lib/backup/storage/cloud_files.rb +21 -23
- data/lib/backup/storage/cycler.rb +10 -15
- data/lib/backup/storage/dropbox.rb +15 -21
- data/lib/backup/storage/ftp.rb +8 -10
- data/lib/backup/storage/local.rb +5 -8
- data/lib/backup/storage/qiniu.rb +8 -8
- data/lib/backup/storage/rsync.rb +24 -26
- data/lib/backup/storage/s3.rb +27 -28
- data/lib/backup/storage/scp.rb +10 -12
- data/lib/backup/storage/sftp.rb +10 -12
- data/lib/backup/syncer/base.rb +5 -8
- data/lib/backup/syncer/cloud/base.rb +27 -30
- data/lib/backup/syncer/cloud/cloud_files.rb +16 -18
- data/lib/backup/syncer/cloud/local_file.rb +5 -8
- data/lib/backup/syncer/cloud/s3.rb +23 -24
- data/lib/backup/syncer/rsync/base.rb +6 -10
- data/lib/backup/syncer/rsync/local.rb +1 -5
- data/lib/backup/syncer/rsync/pull.rb +6 -10
- data/lib/backup/syncer/rsync/push.rb +18 -22
- data/lib/backup/template.rb +9 -14
- data/lib/backup/utilities.rb +82 -69
- data/lib/backup/version.rb +1 -3
- metadata +100 -660
data/lib/backup/package.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module Backup
|
4
2
|
class Package
|
5
|
-
|
6
3
|
##
|
7
4
|
# The time when the backup initiated (in format: 2011.02.20.03.29.59)
|
8
5
|
attr_accessor :time
|
@@ -29,8 +26,8 @@ module Backup
|
|
29
26
|
|
30
27
|
def initialize(model)
|
31
28
|
@trigger = model.trigger
|
32
|
-
@extension =
|
33
|
-
@chunk_suffixes =
|
29
|
+
@extension = "tar"
|
30
|
+
@chunk_suffixes = []
|
34
31
|
@no_cycle = false
|
35
32
|
@version = VERSION
|
36
33
|
end
|
@@ -39,17 +36,16 @@ module Backup
|
|
39
36
|
if chunk_suffixes.empty?
|
40
37
|
[basename]
|
41
38
|
else
|
42
|
-
chunk_suffixes.map {|suffix| "#{
|
39
|
+
chunk_suffixes.map { |suffix| "#{basename}-#{suffix}" }
|
43
40
|
end
|
44
41
|
end
|
45
42
|
|
46
43
|
def basename
|
47
|
-
"#{
|
44
|
+
"#{trigger}.#{extension}"
|
48
45
|
end
|
49
46
|
|
50
47
|
def time_as_object
|
51
|
-
Time.strptime(time,
|
48
|
+
Time.strptime(time, "%Y.%m.%d.%H.%M.%S")
|
52
49
|
end
|
53
|
-
|
54
50
|
end
|
55
51
|
end
|
data/lib/backup/packager.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module Backup
|
4
2
|
module Packager
|
5
3
|
class Error < Backup::Error; end
|
@@ -22,7 +20,7 @@ module Backup
|
|
22
20
|
Logger.info "Packaging Complete!"
|
23
21
|
else
|
24
22
|
raise Error, "Failed to Create Backup Package\n" +
|
25
|
-
|
23
|
+
@pipeline.error_messages
|
26
24
|
end
|
27
25
|
end
|
28
26
|
|
@@ -43,8 +41,8 @@ module Backup
|
|
43
41
|
# or the Splitter (if no Encryptor), or through `cat` into the final
|
44
42
|
# output file if neither are configured.
|
45
43
|
@pipeline.add(
|
46
|
-
"#{
|
47
|
-
"-C '#{
|
44
|
+
"#{utility(:tar)} -cf - " \
|
45
|
+
"-C '#{Config.tmp_path}' '#{@package.trigger}'",
|
48
46
|
tar_success_codes
|
49
47
|
)
|
50
48
|
|
@@ -75,26 +73,27 @@ module Backup
|
|
75
73
|
#
|
76
74
|
# If no Splitter was configured, the final file output will be
|
77
75
|
# piped through `cat` into the final output file.
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
@
|
76
|
+
stack <<
|
77
|
+
if @splitter
|
78
|
+
lambda do
|
79
|
+
@splitter.split_with do |command|
|
80
|
+
@pipeline << command
|
81
|
+
stack.shift.call
|
82
|
+
end
|
83
|
+
end
|
84
|
+
else
|
85
|
+
lambda do
|
86
|
+
outfile = File.join(Config.tmp_path, @package.basename)
|
87
|
+
@pipeline << "#{utility(:cat)} > #{outfile}"
|
82
88
|
stack.shift.call
|
83
89
|
end
|
84
90
|
end
|
85
|
-
else
|
86
|
-
stack << lambda do
|
87
|
-
outfile = File.join(Config.tmp_path, @package.basename)
|
88
|
-
@pipeline << "#{ utility(:cat) } > #{ outfile }"
|
89
|
-
stack.shift.call
|
90
|
-
end
|
91
|
-
end
|
92
91
|
|
93
92
|
##
|
94
93
|
# Last Proc to be called runs the Pipeline the procedure built.
|
95
94
|
# Once complete, the call stack will unwind back through the
|
96
95
|
# preceeding Procs in the stack (if any)
|
97
|
-
stack <<
|
96
|
+
stack << -> { @pipeline.run }
|
98
97
|
|
99
98
|
stack.shift
|
100
99
|
end
|
data/lib/backup/pipeline.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module Backup
|
4
2
|
class Pipeline
|
5
3
|
class Error < Backup::Error; end
|
@@ -12,7 +10,7 @@ module Backup
|
|
12
10
|
@commands = []
|
13
11
|
@success_codes = []
|
14
12
|
@errors = []
|
15
|
-
@stderr =
|
13
|
+
@stderr = ""
|
16
14
|
end
|
17
15
|
|
18
16
|
##
|
@@ -51,22 +49,21 @@ module Backup
|
|
51
49
|
# Use `#success?` to determine if all commands in the pipeline succeeded.
|
52
50
|
# If `#success?` returns `false`, use `#error_messages` to get an error report.
|
53
51
|
def run
|
54
|
-
Open4.popen4(pipeline) do |
|
55
|
-
pipestatus = stdout.read.
|
52
|
+
Open4.popen4(pipeline) do |_pid, _stdin, stdout, stderr|
|
53
|
+
pipestatus = stdout.read.delete("\n").split(":").sort
|
56
54
|
pipestatus.each do |status|
|
57
|
-
index, exitstatus = status.split(
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
55
|
+
index, exitstatus = status.split("|").map(&:to_i)
|
56
|
+
next if @success_codes[index].include?(exitstatus)
|
57
|
+
command = command_name(@commands[index])
|
58
|
+
@errors << SystemCallError.new(
|
59
|
+
"'#{command}' returned exit code: #{exitstatus}", exitstatus
|
60
|
+
)
|
64
61
|
end
|
65
62
|
@stderr = stderr.read.strip
|
66
63
|
end
|
67
64
|
Logger.warn(stderr_messages) if success? && stderr_messages
|
68
65
|
rescue Exception => err
|
69
|
-
raise Error.wrap(err,
|
66
|
+
raise Error.wrap(err, "Pipeline failed to execute")
|
70
67
|
end
|
71
68
|
|
72
69
|
def success?
|
@@ -78,9 +75,9 @@ module Backup
|
|
78
75
|
# from the commands in the pipeline (if any), along with the SystemCallError
|
79
76
|
# (Errno) message for each command which had a non-zero exit status.
|
80
77
|
def error_messages
|
81
|
-
@error_messages ||= (stderr_messages ||
|
82
|
-
|
83
|
-
|
78
|
+
@error_messages ||= (stderr_messages || "") +
|
79
|
+
"The following system errors were returned:\n" +
|
80
|
+
@errors.map { |err| "#{err.class}: #{err.message}" }.join("\n")
|
84
81
|
end
|
85
82
|
|
86
83
|
private
|
@@ -106,19 +103,18 @@ module Backup
|
|
106
103
|
def pipeline
|
107
104
|
parts = []
|
108
105
|
@commands.each_with_index do |command, index|
|
109
|
-
parts << %
|
106
|
+
parts << %({ #{command} 2>&4 ; echo "#{index}|$?:" >&3 ; })
|
110
107
|
end
|
111
|
-
%
|
108
|
+
%({ #{parts.join(" | ")} } 3>&1 1>&2 4>&2)
|
112
109
|
end
|
113
110
|
|
114
111
|
def stderr_messages
|
115
|
-
@stderr_messages ||= @stderr.empty? ? false : <<-EOS.gsub(/^ +/,
|
112
|
+
@stderr_messages ||= @stderr.empty? ? false : <<-EOS.gsub(/^ +/, " ")
|
116
113
|
Pipeline STDERR Messages:
|
117
114
|
(Note: may be interleaved if multiple commands returned error messages)
|
118
115
|
|
119
|
-
#{
|
116
|
+
#{@stderr}
|
120
117
|
EOS
|
121
118
|
end
|
122
|
-
|
123
119
|
end
|
124
120
|
end
|
data/lib/backup/splitter.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module Backup
|
4
2
|
class Splitter
|
5
3
|
include Utilities::Helpers
|
@@ -21,8 +19,8 @@ module Backup
|
|
21
19
|
# Once the packaging procedure is complete, it will return and
|
22
20
|
# @package.chunk_suffixes will be set based on the resulting files.
|
23
21
|
def split_with
|
24
|
-
Logger.info "Splitter configured with a chunk size of #{
|
25
|
-
"and suffix length of #{
|
22
|
+
Logger.info "Splitter configured with a chunk size of #{chunk_size}MB " \
|
23
|
+
"and suffix length of #{suffix_length}."
|
26
24
|
yield split_command
|
27
25
|
after_packaging
|
28
26
|
end
|
@@ -34,8 +32,8 @@ module Backup
|
|
34
32
|
# multiple files, based on @chunk_size and @suffix_length, using the full
|
35
33
|
# path to the final @package.basename, plus a '-' separator as the `prefix`.
|
36
34
|
def split_command
|
37
|
-
"#{
|
38
|
-
"'#{
|
35
|
+
"#{utility(:split)} -a #{suffix_length} -b #{chunk_size}m - " \
|
36
|
+
"'#{File.join(Config.tmp_path, package.basename + "-")}'"
|
39
37
|
end
|
40
38
|
|
41
39
|
##
|
@@ -47,10 +45,10 @@ module Backup
|
|
47
45
|
# remove the suffix from the filename.
|
48
46
|
def after_packaging
|
49
47
|
suffixes = chunk_suffixes
|
50
|
-
first_suffix =
|
48
|
+
first_suffix = "a" * suffix_length
|
51
49
|
if suffixes == [first_suffix]
|
52
50
|
FileUtils.mv(
|
53
|
-
File.join(Config.tmp_path, "#{
|
51
|
+
File.join(Config.tmp_path, "#{package.basename}-#{first_suffix}"),
|
54
52
|
File.join(Config.tmp_path, package.basename)
|
55
53
|
)
|
56
54
|
else
|
@@ -62,15 +60,14 @@ module Backup
|
|
62
60
|
# Returns an array of suffixes for each chunk, in alphabetical order.
|
63
61
|
# For example: [aa, ab, ac, ad, ae] or [aaa, aab, aac aad]
|
64
62
|
def chunk_suffixes
|
65
|
-
chunks.map {|chunk| File.extname(chunk).split(
|
63
|
+
chunks.map { |chunk| File.extname(chunk).split("-").last }.sort
|
66
64
|
end
|
67
65
|
|
68
66
|
##
|
69
67
|
# Returns an array of full paths to the backup chunks.
|
70
68
|
# Chunks are sorted in alphabetical order.
|
71
69
|
def chunks
|
72
|
-
Dir[File.join(Config.tmp_path, package.basename +
|
70
|
+
Dir[File.join(Config.tmp_path, package.basename + "-*")].sort
|
73
71
|
end
|
74
|
-
|
75
72
|
end
|
76
73
|
end
|
data/lib/backup/storage/base.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module Backup
|
4
2
|
module Storage
|
5
3
|
class Base
|
@@ -34,19 +32,19 @@ module Backup
|
|
34
32
|
def initialize(model, storage_id = nil, &block)
|
35
33
|
@model = model
|
36
34
|
@package = model.package
|
37
|
-
@storage_id = storage_id.to_s.gsub(/\W/,
|
35
|
+
@storage_id = storage_id.to_s.gsub(/\W/, "_") if storage_id
|
38
36
|
|
39
37
|
load_defaults!
|
40
38
|
instance_eval(&block) if block_given?
|
41
39
|
end
|
42
40
|
|
43
41
|
def perform!
|
44
|
-
Logger.info "#{
|
42
|
+
Logger.info "#{storage_name} Started..."
|
45
43
|
transfer!
|
46
44
|
if respond_to?(:cycle!, true) && (keep.to_i > 0 || keep.is_a?(Time))
|
47
45
|
cycle!
|
48
46
|
end
|
49
|
-
Logger.info "#{
|
47
|
+
Logger.info "#{storage_name} Finished!"
|
50
48
|
end
|
51
49
|
|
52
50
|
private
|
@@ -60,10 +58,9 @@ module Backup
|
|
60
58
|
alias :remote_path_for :remote_path
|
61
59
|
|
62
60
|
def storage_name
|
63
|
-
@storage_name ||= self.class.to_s.sub(
|
64
|
-
|
61
|
+
@storage_name ||= self.class.to_s.sub("Backup::", "") +
|
62
|
+
(storage_id ? " (#{storage_id})" : "")
|
65
63
|
end
|
66
|
-
|
67
64
|
end
|
68
65
|
end
|
69
66
|
end
|
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
require 'backup/cloud_io/cloud_files'
|
1
|
+
require "backup/cloud_io/cloud_files"
|
3
2
|
|
4
3
|
module Backup
|
5
4
|
module Storage
|
@@ -79,8 +78,8 @@ module Backup
|
|
79
78
|
@max_retries ||= 10
|
80
79
|
@retry_waitsec ||= 30
|
81
80
|
|
82
|
-
@path ||=
|
83
|
-
path.sub!(/^\//,
|
81
|
+
@path ||= "backups"
|
82
|
+
path.sub!(/^\//, "")
|
84
83
|
|
85
84
|
check_configuration
|
86
85
|
end
|
@@ -89,18 +88,18 @@ module Backup
|
|
89
88
|
|
90
89
|
def cloud_io
|
91
90
|
@cloud_io ||= CloudIO::CloudFiles.new(
|
92
|
-
:
|
93
|
-
:
|
94
|
-
:
|
95
|
-
:
|
96
|
-
:
|
97
|
-
:
|
98
|
-
:
|
99
|
-
:
|
100
|
-
:
|
101
|
-
:
|
102
|
-
:
|
103
|
-
:
|
91
|
+
username: username,
|
92
|
+
api_key: api_key,
|
93
|
+
auth_url: auth_url,
|
94
|
+
region: region,
|
95
|
+
servicenet: servicenet,
|
96
|
+
container: container,
|
97
|
+
segments_container: segments_container,
|
98
|
+
segment_size: segment_size,
|
99
|
+
days_to_keep: days_to_keep,
|
100
|
+
max_retries: max_retries,
|
101
|
+
retry_waitsec: retry_waitsec,
|
102
|
+
fog_options: fog_options
|
104
103
|
)
|
105
104
|
end
|
106
105
|
|
@@ -108,7 +107,7 @@ module Backup
|
|
108
107
|
package.filenames.each do |filename|
|
109
108
|
src = File.join(Config.tmp_path, filename)
|
110
109
|
dest = File.join(remote_path, filename)
|
111
|
-
Logger.info "Storing '#{
|
110
|
+
Logger.info "Storing '#{container}/#{dest}'..."
|
112
111
|
cloud_io.upload(src, dest)
|
113
112
|
end
|
114
113
|
|
@@ -118,12 +117,12 @@ module Backup
|
|
118
117
|
# Called by the Cycler.
|
119
118
|
# Any error raised will be logged as a warning.
|
120
119
|
def remove!(package)
|
121
|
-
Logger.info "Removing backup package dated #{
|
120
|
+
Logger.info "Removing backup package dated #{package.time}..."
|
122
121
|
|
123
122
|
remote_path = remote_path_for(package)
|
124
123
|
objects = cloud_io.objects(remote_path)
|
125
124
|
|
126
|
-
raise Error, "Package at '#{
|
125
|
+
raise Error, "Package at '#{remote_path}' not found" if objects.empty?
|
127
126
|
|
128
127
|
slo_objects, objects = objects.partition(&:slo?)
|
129
128
|
cloud_io.delete_slo(slo_objects)
|
@@ -131,10 +130,10 @@ module Backup
|
|
131
130
|
end
|
132
131
|
|
133
132
|
def check_configuration
|
134
|
-
required = %w
|
135
|
-
raise Error, <<-EOS if required.map {|name| send(name) }.any?(&:nil?)
|
133
|
+
required = %w[username api_key container]
|
134
|
+
raise Error, <<-EOS if required.map { |name| send(name) }.any?(&:nil?)
|
136
135
|
Configuration Error
|
137
|
-
#{
|
136
|
+
#{required.map { |name| "##{name}" }.join(", ")} are all required
|
138
137
|
EOS
|
139
138
|
|
140
139
|
raise Error, <<-EOS if segment_size > 0 && segments_container.to_s.empty?
|
@@ -152,7 +151,6 @@ module Backup
|
|
152
151
|
#segment_size is too large (max 5120)
|
153
152
|
EOS
|
154
153
|
end
|
155
|
-
|
156
154
|
end
|
157
155
|
end
|
158
156
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module Backup
|
4
2
|
module Storage
|
5
3
|
module Cycler
|
@@ -11,7 +9,7 @@ module Backup
|
|
11
9
|
# and will remove any old package file(s) when the storage limit
|
12
10
|
# set by #keep is exceeded.
|
13
11
|
def cycle!
|
14
|
-
Logger.info
|
12
|
+
Logger.info "Cycling Started..."
|
15
13
|
|
16
14
|
packages = yaml_load.unshift(package)
|
17
15
|
cycled_packages = []
|
@@ -32,24 +30,22 @@ module Backup
|
|
32
30
|
end
|
33
31
|
|
34
32
|
def delete_package(package)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
Logger.warn Error.wrap(err, <<-EOS)
|
33
|
+
remove!(package) unless package.no_cycle
|
34
|
+
rescue => err
|
35
|
+
Logger.warn Error.wrap(err, <<-EOS)
|
39
36
|
There was a problem removing the following package:
|
40
37
|
Trigger: #{package.trigger} :: Dated: #{package.time}
|
41
|
-
Package included the following #{
|
42
|
-
#{
|
38
|
+
Package included the following #{package.filenames.count} file(s):
|
39
|
+
#{package.filenames.join("\n")}
|
43
40
|
EOS
|
44
|
-
end
|
45
41
|
end
|
46
42
|
|
47
43
|
# Returns path to the YAML data file.
|
48
44
|
def yaml_file
|
49
45
|
@yaml_file ||= begin
|
50
|
-
filename = self.class.to_s.split(
|
51
|
-
filename << "-#{
|
52
|
-
File.join(Config.data_path, package.trigger, "#{
|
46
|
+
filename = self.class.to_s.split("::").last
|
47
|
+
filename << "-#{storage_id}" if storage_id
|
48
|
+
File.join(Config.data_path, package.trigger, "#{filename}.yml")
|
53
49
|
end
|
54
50
|
end
|
55
51
|
|
@@ -65,11 +61,10 @@ module Backup
|
|
65
61
|
# Stores the given package objects to the YAML data file.
|
66
62
|
def yaml_save(packages)
|
67
63
|
FileUtils.mkdir_p(File.dirname(yaml_file))
|
68
|
-
File.open(yaml_file,
|
64
|
+
File.open(yaml_file, "w") do |file|
|
69
65
|
file.write(packages.to_yaml)
|
70
66
|
end
|
71
67
|
end
|
72
|
-
|
73
68
|
end
|
74
69
|
end
|
75
70
|
end
|
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
require 'dropbox_sdk'
|
1
|
+
require "dropbox_sdk"
|
3
2
|
|
4
3
|
module Backup
|
5
4
|
module Storage
|
@@ -50,13 +49,13 @@ module Backup
|
|
50
49
|
def initialize(model, storage_id = nil)
|
51
50
|
super
|
52
51
|
|
53
|
-
@path ||=
|
54
|
-
@cache_path ||=
|
52
|
+
@path ||= "backups"
|
53
|
+
@cache_path ||= ".cache"
|
55
54
|
@access_type ||= :app_folder
|
56
55
|
@chunk_size ||= 4 # MiB
|
57
56
|
@max_retries ||= 10
|
58
57
|
@retry_waitsec ||= 30
|
59
|
-
path.sub!(/^\//,
|
58
|
+
path.sub!(/^\//, "")
|
60
59
|
end
|
61
60
|
|
62
61
|
private
|
@@ -81,9 +80,8 @@ module Backup
|
|
81
80
|
|
82
81
|
# will raise an error if session not authorized
|
83
82
|
@connection = DropboxClient.new(session, access_type)
|
84
|
-
|
85
83
|
rescue => err
|
86
|
-
raise Error.wrap(err,
|
84
|
+
raise Error.wrap(err, "Authorization Failed")
|
87
85
|
end
|
88
86
|
|
89
87
|
##
|
@@ -94,7 +92,6 @@ module Backup
|
|
94
92
|
begin
|
95
93
|
session = DropboxSession.deserialize(File.read(cached_file))
|
96
94
|
Logger.info "Session data loaded from cache!"
|
97
|
-
|
98
95
|
rescue => err
|
99
96
|
Logger.warn Error.wrap(err, <<-EOS)
|
100
97
|
Could not read session data from cache.
|
@@ -113,10 +110,10 @@ module Backup
|
|
113
110
|
package.filenames.each do |filename|
|
114
111
|
src = File.join(Config.tmp_path, filename)
|
115
112
|
dest = File.join(remote_path, filename)
|
116
|
-
Logger.info "Storing '#{
|
113
|
+
Logger.info "Storing '#{dest}'..."
|
117
114
|
|
118
115
|
uploader = nil
|
119
|
-
File.open(src,
|
116
|
+
File.open(src, "r") do |file|
|
120
117
|
uploader = connection.get_chunked_uploader(file, file.stat.size)
|
121
118
|
while uploader.offset < uploader.total_size
|
122
119
|
with_retries do
|
@@ -129,9 +126,8 @@ module Backup
|
|
129
126
|
uploader.finish(dest)
|
130
127
|
end
|
131
128
|
end
|
132
|
-
|
133
129
|
rescue => err
|
134
|
-
raise Error.wrap(err,
|
130
|
+
raise Error.wrap(err, "Upload Failed!")
|
135
131
|
end
|
136
132
|
|
137
133
|
def with_retries
|
@@ -142,7 +138,7 @@ module Backup
|
|
142
138
|
retries += 1
|
143
139
|
raise if retries > max_retries
|
144
140
|
|
145
|
-
Logger.info Error.wrap(err, "Retry ##{
|
141
|
+
Logger.info Error.wrap(err, "Retry ##{retries} of #{max_retries}.")
|
146
142
|
sleep(retry_waitsec)
|
147
143
|
retry
|
148
144
|
end
|
@@ -151,13 +147,13 @@ module Backup
|
|
151
147
|
# Called by the Cycler.
|
152
148
|
# Any error raised will be logged as a warning.
|
153
149
|
def remove!(package)
|
154
|
-
Logger.info "Removing backup package dated #{
|
150
|
+
Logger.info "Removing backup package dated #{package.time}..."
|
155
151
|
|
156
152
|
connection.file_delete(remote_path_for(package))
|
157
153
|
end
|
158
154
|
|
159
155
|
def cached_file
|
160
|
-
path = cache_path.start_with?(
|
156
|
+
path = cache_path.start_with?("/") ?
|
161
157
|
cache_path : File.join(Config.root_path, cache_path)
|
162
158
|
File.join(path, api_key + api_secret)
|
163
159
|
end
|
@@ -175,7 +171,7 @@ module Backup
|
|
175
171
|
# Create a new session, write a serialized version of it to the
|
176
172
|
# .cache directory, and return the session object
|
177
173
|
def create_write_and_return_new_session!
|
178
|
-
require
|
174
|
+
require "timeout"
|
179
175
|
|
180
176
|
session = DropboxSession.new(api_key, api_secret)
|
181
177
|
|
@@ -183,12 +179,12 @@ module Backup
|
|
183
179
|
session.get_request_token
|
184
180
|
|
185
181
|
template = Backup::Template.new(
|
186
|
-
|
182
|
+
session: session, cached_file: cached_file
|
187
183
|
)
|
188
184
|
template.render("storage/dropbox/authorization_url.erb")
|
189
185
|
|
190
186
|
# wait for user to hit 'return' to continue
|
191
|
-
Timeout
|
187
|
+
Timeout.timeout(180) { STDIN.gets }
|
192
188
|
|
193
189
|
# this will raise an error if the user did not
|
194
190
|
# visit the authorization_url and grant access
|
@@ -202,11 +198,9 @@ module Backup
|
|
202
198
|
template.render("storage/dropbox/cache_file_written.erb")
|
203
199
|
|
204
200
|
session
|
205
|
-
|
206
201
|
rescue => err
|
207
|
-
raise Error.wrap(err,
|
202
|
+
raise Error.wrap(err, "Could not create or authenticate a new session")
|
208
203
|
end
|
209
|
-
|
210
204
|
end
|
211
205
|
end
|
212
206
|
end
|