geminabox-s3-store 0.0.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 +7 -0
- data/lib/geminabox-s3-store.rb +1 -0
- data/lib/geminabox/store/s3.rb +186 -0
- metadata +73 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7be7730bb6bad6cc92e7aecc4e3924a24ee9bf85
|
4
|
+
data.tar.gz: 03ff92c2f42ec849105cdc1705193d8888319063
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fbd268c668ac3e43579e86da0f400280ad8be94872413e2e7c5baee802fd4829315cc08439a3f8713bb6d14bf9252087fecc34c7d7746b632106a311527ef363
|
7
|
+
data.tar.gz: 456f362481a2fe489d0d0c5cec4fa979b07c0ae32f6d26b1292c979e079eea940721de867584c896c0ee73a47dd3b1068f0cabebe0da9f808394de8d43162cde
|
@@ -0,0 +1 @@
|
|
1
|
+
require "geminabox/store/s3"
|
@@ -0,0 +1,186 @@
|
|
1
|
+
module Geminabox
|
2
|
+
module Store
|
3
|
+
class S3
|
4
|
+
attr_reader :logger
|
5
|
+
|
6
|
+
SPLICEABLE_GZIPPED_FILES = %w[
|
7
|
+
specs.4.8.gz
|
8
|
+
latest_specs.4.8.gz
|
9
|
+
prerelease_specs.4.8.gz
|
10
|
+
]
|
11
|
+
SPLICEABLE_TEXT_FILES = %w[
|
12
|
+
yaml
|
13
|
+
Marshal.4.8
|
14
|
+
specs.4.8
|
15
|
+
latest_specs.4.8
|
16
|
+
prerelease_specs.4.8
|
17
|
+
]
|
18
|
+
|
19
|
+
def initialize(bucket: nil, lock_manager: lock_manager, file_store: Geminabox::GemStore, logger: Logger.new(STDOUT))
|
20
|
+
@bucket = bucket
|
21
|
+
@file_store = file_store
|
22
|
+
@lock_manager = lock_manager
|
23
|
+
@logger = logger
|
24
|
+
end
|
25
|
+
|
26
|
+
def create(gem, overwrite = false)
|
27
|
+
@file_store.create gem, overwrite
|
28
|
+
|
29
|
+
object_name = gem_object_name("/gems/" + gem.name)
|
30
|
+
|
31
|
+
logger.info "Gem: local -> S3 #{object_name}"
|
32
|
+
|
33
|
+
@bucket
|
34
|
+
.objects[object_name]
|
35
|
+
.write gem.gem_data
|
36
|
+
update_metadata
|
37
|
+
end
|
38
|
+
|
39
|
+
# Note: deleting doesn't make much sense in this case anyway, as
|
40
|
+
# other instances will continue serving cached copies of this
|
41
|
+
# gem (there's no way to notify them that gem has been deleted)
|
42
|
+
#
|
43
|
+
# Do consider using Geminabox.allow_delete = false
|
44
|
+
#
|
45
|
+
def delete(path_info)
|
46
|
+
@file_store.delete path_info
|
47
|
+
|
48
|
+
@bucket.objects[gem_object_name(path_info)].delete
|
49
|
+
end
|
50
|
+
|
51
|
+
def update_local_file(path_info)
|
52
|
+
gem_file = @file_store.local_path path_info
|
53
|
+
|
54
|
+
unless File.exists? gem_file
|
55
|
+
gem_object = @bucket.objects[gem_object_name(path_info)]
|
56
|
+
if gem_object.exists?
|
57
|
+
# Note: this will load the entire contents of the gem into
|
58
|
+
# memory We might switch to using streaming IO or
|
59
|
+
# temporary files if this proves to be critical in our
|
60
|
+
# environment
|
61
|
+
io = StringIO.new gem_object.read
|
62
|
+
incoming_gem = Geminabox::IncomingGem.new io
|
63
|
+
@file_store.create incoming_gem
|
64
|
+
|
65
|
+
update_metadata
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
@file_store.update_local_file path_info
|
70
|
+
end
|
71
|
+
|
72
|
+
def update_local_metadata_file(path_info)
|
73
|
+
file_name = File.basename path_info
|
74
|
+
pull_file file_name do |local, remote|
|
75
|
+
if file_name =~ /\.gz$/
|
76
|
+
merge_gzipped local, remote
|
77
|
+
else
|
78
|
+
merge_text local, remote
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def reindex &block
|
84
|
+
FileUtils.mkpath @file_store.local_path "gems"
|
85
|
+
@bucket.objects.with_prefix("gems/").each do |object|
|
86
|
+
path_info = "/" + object.key
|
87
|
+
local_file_path = @file_store.local_path path_info
|
88
|
+
|
89
|
+
file_does_not_exist = !File.exists?(local_file_path)
|
90
|
+
|
91
|
+
# File.size raises an exception if file does not exist
|
92
|
+
file_size = file_does_not_exist ? 0 : File.size(local_file_path)
|
93
|
+
file_has_different_size = object.content_length != file_size
|
94
|
+
|
95
|
+
if file_does_not_exist || file_has_different_size
|
96
|
+
logger.info "Gem: S3 -> local #{local_file_path}"
|
97
|
+
File.write local_file_path, object.read
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
@file_store.reindex(&block)
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def update_metadata
|
107
|
+
@lock_manager.lock ".metadata" do
|
108
|
+
push_files SPLICEABLE_GZIPPED_FILES do |local_contents, remote_contents|
|
109
|
+
merge_gzipped local_contents, remote_contents
|
110
|
+
end
|
111
|
+
|
112
|
+
push_files SPLICEABLE_TEXT_FILES do |local_contents, remote_contents|
|
113
|
+
merge_text local_contents, remote_contents
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def push_files file_list, &block
|
119
|
+
file_list.each do |file_name|
|
120
|
+
push_file file_name, &block
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def merge_file_with_remote file_name
|
125
|
+
local_index_file = @file_store.local_path file_name
|
126
|
+
if File.exists? local_index_file
|
127
|
+
old_contents = File.read(local_index_file, open_args: ["rb"])
|
128
|
+
else
|
129
|
+
old_contents = ''
|
130
|
+
end
|
131
|
+
|
132
|
+
object = s3_object(file_name)
|
133
|
+
unless object.exists?
|
134
|
+
old_contents
|
135
|
+
else
|
136
|
+
yield old_contents, object.read
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def s3_object file_name
|
141
|
+
object_name = metadata_object_name('/' + file_name)
|
142
|
+
@bucket.objects[object_name]
|
143
|
+
end
|
144
|
+
|
145
|
+
def push_file file_name, &block
|
146
|
+
logger.info "Push: local -> S3 #{file_name}"
|
147
|
+
|
148
|
+
new_contents = merge_file_with_remote file_name, &block
|
149
|
+
s3_object(file_name).write new_contents
|
150
|
+
end
|
151
|
+
|
152
|
+
def pull_file file_name, &block
|
153
|
+
logger.info "Pull: S3 -> local #{file_name}"
|
154
|
+
|
155
|
+
new_contents = merge_file_with_remote file_name, &block
|
156
|
+
file_path = @file_store.local_path file_name
|
157
|
+
File.write file_path, new_contents
|
158
|
+
end
|
159
|
+
|
160
|
+
def gem_object_name(path_info)
|
161
|
+
# Remove loading slash from the path
|
162
|
+
path_info[1..-1]
|
163
|
+
end
|
164
|
+
|
165
|
+
def metadata_object_name(path_info)
|
166
|
+
path_info.gsub %r{/}, 'metadata/'
|
167
|
+
end
|
168
|
+
|
169
|
+
def merge_gzipped(a, b)
|
170
|
+
package(unpackage(a) | unpackage(b))
|
171
|
+
end
|
172
|
+
|
173
|
+
def merge_text(a, b)
|
174
|
+
a.to_s + b.to_s
|
175
|
+
end
|
176
|
+
|
177
|
+
def unpackage(content)
|
178
|
+
Marshal.load(Gem.gunzip(content))
|
179
|
+
end
|
180
|
+
|
181
|
+
def package(content)
|
182
|
+
Gem.gzip(Marshal.dump(content))
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: geminabox-s3-store
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Konstantin Burnaev
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-12-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: geminabox
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: lock-smith
|
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'
|
41
|
+
description: A simple hello world gem
|
42
|
+
email: kbourn@gmail.com
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- lib/geminabox-s3-store.rb
|
48
|
+
- lib/geminabox/store/s3.rb
|
49
|
+
homepage: https://github.com/bkon/geminabox-s3-store
|
50
|
+
licenses:
|
51
|
+
- MIT
|
52
|
+
metadata: {}
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 2.6.13
|
70
|
+
signing_key:
|
71
|
+
specification_version: 4
|
72
|
+
summary: AWS S3 store for geminabox
|
73
|
+
test_files: []
|