backupsss 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -0
- data/backupsss.gemspec +4 -3
- data/lib/backupsss/backup.rb +108 -7
- data/lib/backupsss/tar.rb +38 -13
- data/lib/backupsss/version.rb +1 -1
- metadata +28 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 473324a2ff4a2affcf336b2ac9adaf8314f3a2a8
|
4
|
+
data.tar.gz: efa42347a6d6e9b4bb2d9aa7c0af3a4fac323bb0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 970be5e7c287f045ecaed99f506291df4626746ad662ada92ac7654d37c29144c208abc09845afdbe7ea1c24ca5466124187263891edea6f6b65d8d350816227
|
7
|
+
data.tar.gz: 9f32195679dcc98f7d9944a8003d5bc0c938ef3a8f06a24c44ccc1e2e32a7cfe716f3fcc72412c3a32e4f9a663fabdf88b8ccea0b3b05b4d762732c7c7368c2f
|
data/.rubocop.yml
CHANGED
data/backupsss.gemspec
CHANGED
@@ -35,11 +35,12 @@ Gem::Specification.new do |spec|
|
|
35
35
|
spec.add_development_dependency 'guard-rspec', '~> 4.6.4'
|
36
36
|
spec.add_development_dependency 'guard-rubocop', '~> 1.2'
|
37
37
|
spec.add_development_dependency 'guard-bundler', '~> 2.1'
|
38
|
-
spec.add_development_dependency 'rubocop', '
|
38
|
+
spec.add_development_dependency 'rubocop', '0.46'
|
39
39
|
spec.add_development_dependency 'simplecov', '~> 0.11.2'
|
40
40
|
spec.add_development_dependency 'simplecov-console', '~> 0.3.0'
|
41
41
|
spec.add_development_dependency 'codeclimate-test-reporter', '~> 0.4'
|
42
42
|
|
43
|
-
spec.add_runtime_dependency 'aws-sdk'
|
44
|
-
spec.add_runtime_dependency '
|
43
|
+
spec.add_runtime_dependency 'aws-sdk', '~> 2.7.0'
|
44
|
+
spec.add_runtime_dependency 'parallel', '~> 1.10.0'
|
45
|
+
spec.add_runtime_dependency 'rufus-scheduler', '~> 3.3.2'
|
45
46
|
end
|
data/lib/backupsss/backup.rb
CHANGED
@@ -1,25 +1,126 @@
|
|
1
|
+
require 'parallel'
|
2
|
+
|
1
3
|
module Backupsss
|
2
4
|
# A class for delivering a tar to S3
|
3
5
|
class Backup
|
6
|
+
MAX_FILE_SIZE = 1024 * 1024 * 100 # 100MB
|
7
|
+
|
4
8
|
attr_reader :config, :client, :filename
|
5
9
|
|
6
10
|
def initialize(config, client)
|
7
|
-
@config
|
8
|
-
@client
|
11
|
+
@config = config
|
12
|
+
@client = client
|
9
13
|
@filename = config[:filename]
|
10
14
|
end
|
11
15
|
|
12
16
|
def put_file(file)
|
13
|
-
|
17
|
+
large_file(file) ? multi_upload(file) : single_upload(file)
|
14
18
|
end
|
15
19
|
|
16
20
|
private
|
17
21
|
|
22
|
+
def create_multipart_upload
|
23
|
+
$stdout.puts 'Creating a multipart upload'
|
24
|
+
|
25
|
+
client.create_multipart_upload(bucket_opts)
|
26
|
+
end
|
27
|
+
|
28
|
+
def large_file(file)
|
29
|
+
$stdout.puts 'Checking backup size ...'
|
30
|
+
is_lg = file.size > MAX_FILE_SIZE
|
31
|
+
status = is_lg ? 'greater than' : 'less than or equal to'
|
32
|
+
$stdout.puts "Size of backup is #{status} 100MB"
|
33
|
+
|
34
|
+
is_lg
|
35
|
+
end
|
36
|
+
|
37
|
+
def complete_multipart_upload_request(upload_id, parts)
|
38
|
+
bucket_opts.merge(
|
39
|
+
upload_id: upload_id, multipart_upload: { parts: parts }
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
def timed_multipart_upload
|
44
|
+
s = Time.now
|
45
|
+
$stdout.puts "Starting multipart upload at #{s}"
|
46
|
+
yield
|
47
|
+
e = Time.now
|
48
|
+
duration = ((e - s) / 60).round(2)
|
49
|
+
output = ["Completed multipart upload at #{e}"]
|
50
|
+
output << "Completed in #{duration} minutes."
|
51
|
+
|
52
|
+
$stdout.puts output.join("\n")
|
53
|
+
end
|
54
|
+
|
55
|
+
def abort_multipart_message(error, upload_id)
|
56
|
+
"#{error}\n#{upload_id}: Aborting multipart upload"
|
57
|
+
end
|
58
|
+
|
59
|
+
def multi_upload(file)
|
60
|
+
upload_id = create_multipart_upload.upload_id
|
61
|
+
|
62
|
+
bail_multipart_on_fail(upload_id) do
|
63
|
+
timed_multipart_upload do
|
64
|
+
parts = upload_parts(file, upload_id).sort do |a, b|
|
65
|
+
a[:part_number] <=> b[:part_number]
|
66
|
+
end
|
67
|
+
|
68
|
+
req = complete_multipart_upload_request(upload_id, parts)
|
69
|
+
client.complete_multipart_upload(req)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def upload_parts(file, upload_id)
|
75
|
+
Parallel.map(1..part_count(file), in_threads: 10) do |part|
|
76
|
+
bail_upload_part_on_fail(part, upload_id) do
|
77
|
+
$stdout.puts "#{upload_id}: Uploading part number #{part}\n"
|
78
|
+
r = client.upload_part(upload_part_params(file, part, upload_id))
|
79
|
+
success_msg = "#{upload_id}: Completed uploading part number #{part}"
|
80
|
+
r.on_success { $stdout.puts success_msg }
|
81
|
+
|
82
|
+
{ etag: r.etag, part_number: part }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def bail_multipart_on_fail(upload_id)
|
88
|
+
yield
|
89
|
+
rescue StandardError => e
|
90
|
+
$stdout.puts abort_multipart_message(e, upload_id)
|
91
|
+
client.abort_multipart_upload(bucket_opts.merge(upload_id: upload_id))
|
92
|
+
end
|
93
|
+
|
94
|
+
def bail_upload_part_on_fail(part, upload_id)
|
95
|
+
yield
|
96
|
+
rescue StandardError => e
|
97
|
+
output = ["#{upload_id}: Failed to upload part number #{part}"]
|
98
|
+
output << "because of #{e.message}"
|
99
|
+
output << "#{upload_id}: Aborting remaining parts"
|
100
|
+
raise output.join("\n")
|
101
|
+
end
|
102
|
+
|
103
|
+
def upload_part_params(file, part, upload_id)
|
104
|
+
start = (part - 1) * MAX_FILE_SIZE
|
105
|
+
body = IO.read(file.path, MAX_FILE_SIZE, start)
|
106
|
+
|
107
|
+
bucket_opts.merge(part_number: part, body: body, upload_id: upload_id)
|
108
|
+
end
|
109
|
+
|
110
|
+
def part_count(file)
|
111
|
+
c = (file.size.to_f / MAX_FILE_SIZE.to_f).ceil
|
112
|
+
$stdout.puts "Uploading backup as #{c} parts"
|
113
|
+
|
114
|
+
c
|
115
|
+
end
|
116
|
+
|
117
|
+
def single_upload(file)
|
118
|
+
client.put_object(bucket_opts.merge(body: file.read))
|
119
|
+
end
|
120
|
+
|
18
121
|
def bucket_opts
|
19
|
-
{
|
20
|
-
|
21
|
-
key: "#{config[:s3_bucket_prefix]}/#{filename}"
|
22
|
-
}
|
122
|
+
{ bucket: config[:s3_bucket],
|
123
|
+
key: "#{config[:s3_bucket_prefix]}/#{filename}" }
|
23
124
|
end
|
24
125
|
end
|
25
126
|
end
|
data/lib/backupsss/tar.rb
CHANGED
@@ -12,21 +12,34 @@ module Backupsss
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def make
|
15
|
-
return
|
15
|
+
return unless valid_dest? && valid_src?
|
16
16
|
_, err, status = Open3.capture3("#{tar_command} #{dest} #{src}")
|
17
|
-
|
18
|
-
check_tar_result(status)
|
19
|
-
File.open(dest)
|
17
|
+
File.open(dest) if valid_exit?(status, err) && valid_file?
|
20
18
|
end
|
21
19
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
20
|
+
def valid_exit?(status, err)
|
21
|
+
output = []
|
22
|
+
output << "command.......#{tar_command}"
|
23
|
+
output << "stderr........#{err}" unless err.empty?
|
24
|
+
output << "status........#{status}"
|
25
|
+
output << "exit code.....#{status.to_i}"
|
26
|
+
$stdout.puts output.join("\n")
|
27
|
+
|
28
|
+
return true if success_cases(status.to_i, err)
|
29
|
+
raise "ERROR: #{tar_command} exited #{status.to_i}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def valid_file?
|
33
|
+
raise messages[:no_file] unless File.exist?(dest)
|
34
|
+
raise messages[:zero_byte] if File.size(dest).zero?
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
def messages
|
39
|
+
{
|
40
|
+
no_file: 'ERROR: Tar destination file does not exist.',
|
41
|
+
zero_byte: 'ERROR: Tar destination file is 0 bytes.'
|
42
|
+
}
|
30
43
|
end
|
31
44
|
|
32
45
|
def valid_dest?
|
@@ -47,6 +60,10 @@ module Backupsss
|
|
47
60
|
|
48
61
|
private
|
49
62
|
|
63
|
+
def clean_exit(status)
|
64
|
+
status.zero?
|
65
|
+
end
|
66
|
+
|
50
67
|
def dest_dir
|
51
68
|
File.dirname(dest)
|
52
69
|
end
|
@@ -56,13 +73,21 @@ module Backupsss
|
|
56
73
|
end
|
57
74
|
|
58
75
|
def dir_exists?(dir)
|
59
|
-
File.exist?(dir) || raise_sys_err(dir, Errno::ENOENT::Errno)
|
76
|
+
File.exist?(File.open(dir)) || raise_sys_err(dir, Errno::ENOENT::Errno)
|
77
|
+
end
|
78
|
+
|
79
|
+
def file_changed?(signal_int, err)
|
80
|
+
signal_int == 1 && err.match(/file changed as we read it/)
|
60
81
|
end
|
61
82
|
|
62
83
|
def src_readable?
|
63
84
|
File.readable?(src) || raise_sys_err(src, Errno::EPERM::Errno)
|
64
85
|
end
|
65
86
|
|
87
|
+
def success_cases(signal_int, err)
|
88
|
+
clean_exit(signal_int) || file_changed?(signal_int, err)
|
89
|
+
end
|
90
|
+
|
66
91
|
def raise_sys_err(dir, err)
|
67
92
|
raise SystemCallError.new(dir.to_s, err)
|
68
93
|
end
|
data/lib/backupsss/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: backupsss
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Reppard Walker
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2017-01-30 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|
@@ -114,16 +114,16 @@ dependencies:
|
|
114
114
|
name: rubocop
|
115
115
|
requirement: !ruby/object:Gem::Requirement
|
116
116
|
requirements:
|
117
|
-
- -
|
117
|
+
- - '='
|
118
118
|
- !ruby/object:Gem::Version
|
119
|
-
version: '0.
|
119
|
+
version: '0.46'
|
120
120
|
type: :development
|
121
121
|
prerelease: false
|
122
122
|
version_requirements: !ruby/object:Gem::Requirement
|
123
123
|
requirements:
|
124
|
-
- -
|
124
|
+
- - '='
|
125
125
|
- !ruby/object:Gem::Version
|
126
|
-
version: '0.
|
126
|
+
version: '0.46'
|
127
127
|
- !ruby/object:Gem::Dependency
|
128
128
|
name: simplecov
|
129
129
|
requirement: !ruby/object:Gem::Requirement
|
@@ -170,30 +170,44 @@ dependencies:
|
|
170
170
|
name: aws-sdk
|
171
171
|
requirement: !ruby/object:Gem::Requirement
|
172
172
|
requirements:
|
173
|
-
- - "
|
173
|
+
- - "~>"
|
174
174
|
- !ruby/object:Gem::Version
|
175
|
-
version:
|
175
|
+
version: 2.7.0
|
176
176
|
type: :runtime
|
177
177
|
prerelease: false
|
178
178
|
version_requirements: !ruby/object:Gem::Requirement
|
179
179
|
requirements:
|
180
|
-
- - "
|
180
|
+
- - "~>"
|
181
181
|
- !ruby/object:Gem::Version
|
182
|
-
version:
|
182
|
+
version: 2.7.0
|
183
|
+
- !ruby/object:Gem::Dependency
|
184
|
+
name: parallel
|
185
|
+
requirement: !ruby/object:Gem::Requirement
|
186
|
+
requirements:
|
187
|
+
- - "~>"
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: 1.10.0
|
190
|
+
type: :runtime
|
191
|
+
prerelease: false
|
192
|
+
version_requirements: !ruby/object:Gem::Requirement
|
193
|
+
requirements:
|
194
|
+
- - "~>"
|
195
|
+
- !ruby/object:Gem::Version
|
196
|
+
version: 1.10.0
|
183
197
|
- !ruby/object:Gem::Dependency
|
184
198
|
name: rufus-scheduler
|
185
199
|
requirement: !ruby/object:Gem::Requirement
|
186
200
|
requirements:
|
187
|
-
- - "
|
201
|
+
- - "~>"
|
188
202
|
- !ruby/object:Gem::Version
|
189
|
-
version:
|
203
|
+
version: 3.3.2
|
190
204
|
type: :runtime
|
191
205
|
prerelease: false
|
192
206
|
version_requirements: !ruby/object:Gem::Requirement
|
193
207
|
requirements:
|
194
|
-
- - "
|
208
|
+
- - "~>"
|
195
209
|
- !ruby/object:Gem::Version
|
196
|
-
version:
|
210
|
+
version: 3.3.2
|
197
211
|
description: Backup any file or directory as a tar and push the tar to a specificed
|
198
212
|
S3 bucket.
|
199
213
|
email:
|