geminabox-s3-store 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|