geminabox-s3-store 0.0.1

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