s3_meta_sync 0.13.1 → 0.15.2
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/lib/s3_meta_sync.rb +2 -0
- data/lib/s3_meta_sync/syncer.rb +90 -25
- data/lib/s3_meta_sync/version.rb +3 -1
- data/lib/s3_meta_sync/zip.rb +3 -1
- metadata +20 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: eb3dcbb8679154cb0863f3c13ba43d5c828220cbc2f0624346c19cf3617dd358
|
4
|
+
data.tar.gz: 6973f43e1a0bad829be2748ec65e447a2f7e96c16b2d95746d3bfad1e8daa0ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e6aca9b4086bf08b5bb18ea74622fd4ad45fa783440246499459e5d8663b331ab3b63cef7ce2c78a8eb66eb7ae6470e5fffd2beb43ab95363d38f96bf60128b5
|
7
|
+
data.tar.gz: d8c6e284debf19ac52094a3ec1e36cb158b318b3c655fbc5a63a9c65ebc236048c24af184327a8085acfe11b082395a53a3e199e654e63cb0ca6195b455e4a8c
|
data/lib/s3_meta_sync.rb
CHANGED
data/lib/s3_meta_sync/syncer.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "net/http"
|
2
4
|
require "open-uri"
|
3
5
|
require "yaml"
|
@@ -5,17 +7,24 @@ require "digest/md5"
|
|
5
7
|
require "fileutils"
|
6
8
|
require "tmpdir"
|
7
9
|
require "openssl"
|
10
|
+
require "mime/types"
|
8
11
|
|
9
|
-
require "aws-sdk-
|
12
|
+
require "aws-sdk-s3"
|
10
13
|
require "s3_meta_sync/zip"
|
11
14
|
|
12
15
|
module S3MetaSync
|
13
16
|
class Syncer
|
14
|
-
DEFAULT_REGION =
|
17
|
+
DEFAULT_REGION = "us-east-1"
|
15
18
|
STAGING_AREA_PREFIX = "s3ms_"
|
16
19
|
|
20
|
+
AWS_PUBLIC_ACCESS = "public-read"
|
21
|
+
AWS_PRIVATE_ACCESS = "private"
|
22
|
+
|
17
23
|
def initialize(config)
|
18
|
-
@config =
|
24
|
+
@config = {
|
25
|
+
acl: AWS_PUBLIC_ACCESS,
|
26
|
+
region: DEFAULT_REGION
|
27
|
+
}.merge(config)
|
19
28
|
end
|
20
29
|
|
21
30
|
def sync(source, destination)
|
@@ -93,7 +102,7 @@ module S3MetaSync
|
|
93
102
|
# Sometimes SIGTERM causes Dir.mktmpdir to not properly delete the temp folder
|
94
103
|
# Remove 1 day old folders
|
95
104
|
def delete_old_temp_folders
|
96
|
-
path = File.join(Dir.tmpdir, STAGING_AREA_PREFIX +
|
105
|
+
path = File.join(Dir.tmpdir, STAGING_AREA_PREFIX + "*")
|
97
106
|
|
98
107
|
day = 24 * 60 * 60
|
99
108
|
dirs = Dir.glob(path)
|
@@ -161,10 +170,12 @@ module S3MetaSync
|
|
161
170
|
content = Zip.zip(content) if @config[:zip] && path != META_FILE
|
162
171
|
|
163
172
|
object = {
|
173
|
+
acl: @config[:acl],
|
164
174
|
bucket: @bucket,
|
165
175
|
body: content,
|
166
|
-
|
167
|
-
|
176
|
+
content_encoding: content.encoding.to_s,
|
177
|
+
content_type: MIME::Types.of(path).first.to_s,
|
178
|
+
key: "#{destination}/#{path}"
|
168
179
|
}
|
169
180
|
|
170
181
|
object[:server_side_encryption] = @config[:server_side_encryption] if @config[:server_side_encryption]
|
@@ -195,11 +206,19 @@ module S3MetaSync
|
|
195
206
|
end
|
196
207
|
|
197
208
|
def s3
|
198
|
-
@s3 ||=
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
209
|
+
@s3 ||= begin
|
210
|
+
config = { region: @config[:region] }
|
211
|
+
|
212
|
+
if @config[:credentials_path]
|
213
|
+
config[:credentials] = Aws::SharedCredentials.new(path: @config[:credentials_path], profile_name: "default")
|
214
|
+
else
|
215
|
+
config[:access_key_id] = @config[:key]
|
216
|
+
config[:secret_access_key] = @config[:secret]
|
217
|
+
config[:session_token] = @config[:session_token] if @config[:session_token]
|
218
|
+
end
|
219
|
+
|
220
|
+
Aws::S3::Client.new(config)
|
221
|
+
end
|
203
222
|
end
|
204
223
|
|
205
224
|
def generate_meta(source)
|
@@ -230,21 +249,50 @@ module S3MetaSync
|
|
230
249
|
|
231
250
|
def read_meta(source)
|
232
251
|
file = "#{source}/#{META_FILE}"
|
233
|
-
|
252
|
+
if File.exist?(file)
|
253
|
+
content = File.read(file)
|
254
|
+
parse_yaml_content(content) if content.size > 0
|
255
|
+
end
|
234
256
|
end
|
235
257
|
|
236
258
|
def download_meta(destination)
|
259
|
+
if private?
|
260
|
+
private_access_download_meta(destination)
|
261
|
+
else
|
262
|
+
public_access_download_meta(destination)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def private_access_download_meta(destination)
|
267
|
+
content = private_content_download(destination, META_FILE).string
|
268
|
+
|
269
|
+
raise S3MetaSync::RemoteWithoutMeta if content.empty? # if missing, upload everything
|
270
|
+
|
271
|
+
parse_yaml_content(content)
|
272
|
+
rescue Aws::S3::Errors::NoSuchKey, Aws::S3::Errors::AccessDenied # if requesting a file that doesn't exist AccessDenied is raised
|
273
|
+
retries ||= 0
|
274
|
+
|
275
|
+
raise S3MetaSync::RemoteWithoutMeta if retries >= 1
|
276
|
+
|
277
|
+
retries += 1
|
278
|
+
sleep 1 # maybe the remote meta was just updated ... give aws a second chance ...
|
279
|
+
retry
|
280
|
+
end
|
281
|
+
|
282
|
+
def public_access_download_meta(destination)
|
237
283
|
content = download_content("#{destination}/#{META_FILE}") { |io| io.read }
|
284
|
+
|
285
|
+
raise OpenURI::HTTPError.new("Content is empty", nil) if content.size == 0
|
286
|
+
|
238
287
|
parse_yaml_content(content)
|
239
288
|
rescue OpenURI::HTTPError
|
240
289
|
retries ||= 0
|
290
|
+
|
291
|
+
raise S3MetaSync::RemoteWithoutMeta if retries >= 1
|
292
|
+
|
241
293
|
retries += 1
|
242
|
-
|
243
|
-
|
244
|
-
retry
|
245
|
-
else
|
246
|
-
raise RemoteWithoutMeta
|
247
|
-
end
|
294
|
+
sleep 1 # maybe the remote meta was just updated ... give aws a second chance ...
|
295
|
+
retry
|
248
296
|
end
|
249
297
|
|
250
298
|
def parse_yaml_content(content)
|
@@ -253,16 +301,30 @@ module S3MetaSync
|
|
253
301
|
end
|
254
302
|
|
255
303
|
def download_file(source, path, destination, zip)
|
256
|
-
download =
|
257
|
-
|
258
|
-
|
259
|
-
|
304
|
+
download = if private?
|
305
|
+
private_content_download(source, path)
|
306
|
+
else
|
307
|
+
public_content_download(source, path)
|
308
|
+
end
|
309
|
+
|
310
|
+
download = S3MetaSync::Zip.unzip(download) if zip
|
311
|
+
FileUtils.mkdir_p(File.dirname("#{destination}/#{path}"))
|
260
312
|
|
261
313
|
# consumes less ram then File.write(path, content), possibly also faster
|
262
|
-
File.open(path,
|
314
|
+
File.open("#{destination}/#{path}", "wb") { |f| IO.copy_stream(download, f) }
|
263
315
|
download.close
|
264
316
|
end
|
265
317
|
|
318
|
+
def private_content_download(source, path)
|
319
|
+
log "Downloading #{path}"
|
320
|
+
obj = s3.get_object(bucket: @bucket, key: "#{source}/#{path}")
|
321
|
+
obj.body
|
322
|
+
end
|
323
|
+
|
324
|
+
def public_content_download(source, path)
|
325
|
+
download_content("#{source}/#{path}") # warning: using block form consumes more ram
|
326
|
+
end
|
327
|
+
|
266
328
|
def download_content(path)
|
267
329
|
log "Downloading #{path}"
|
268
330
|
url =
|
@@ -287,8 +349,7 @@ module S3MetaSync
|
|
287
349
|
log "#{e.class} error downloading #{url}, retrying #{http_error_retries}/#{max_retries}"
|
288
350
|
retry
|
289
351
|
else
|
290
|
-
|
291
|
-
raise
|
352
|
+
raise $!, "#{$!.message} -- while trying to download #{url}", $!.backtrace
|
292
353
|
end
|
293
354
|
rescue OpenSSL::SSL::SSLError
|
294
355
|
ssl_error_retries ||= 0
|
@@ -340,5 +401,9 @@ module S3MetaSync
|
|
340
401
|
def log(text, important=false)
|
341
402
|
$stderr.puts text if @config[:verbose] or important
|
342
403
|
end
|
404
|
+
|
405
|
+
def private?
|
406
|
+
@config[:acl] == AWS_PRIVATE_ACCESS
|
407
|
+
end
|
343
408
|
end
|
344
409
|
end
|
data/lib/s3_meta_sync/version.rb
CHANGED
data/lib/s3_meta_sync/zip.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "zlib"
|
2
4
|
require "stringio"
|
3
5
|
|
@@ -5,7 +7,7 @@ module S3MetaSync
|
|
5
7
|
module Zip
|
6
8
|
class << self
|
7
9
|
def zip(string)
|
8
|
-
io = StringIO.new("w")
|
10
|
+
io = StringIO.new("w".dup)
|
9
11
|
w_gz = Zlib::GzipWriter.new(io)
|
10
12
|
w_gz.write(string)
|
11
13
|
w_gz.close
|
metadata
CHANGED
@@ -1,29 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: s3_meta_sync
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.15.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Grosser
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-09-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name: aws-sdk-
|
14
|
+
name: aws-sdk-s3
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '1.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: mime-types
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
description:
|
28
42
|
email: michael@grosser.it
|
29
43
|
executables:
|
@@ -55,8 +69,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
55
69
|
- !ruby/object:Gem::Version
|
56
70
|
version: '0'
|
57
71
|
requirements: []
|
58
|
-
|
59
|
-
rubygems_version: 2.4.5.1
|
72
|
+
rubygems_version: 3.1.4
|
60
73
|
signing_key:
|
61
74
|
specification_version: 4
|
62
75
|
summary: Sync folders with s3 using a metadata file and md5 diffs
|