Skip to content

Commit f4ad8d4

Browse files
author
Glyn Normington
committed
Improved check for internet connection
Check for an internet connection by attempting a HTTP GET with 10 second timeout of a well known buildpack download artefact. Cache the result in a class variable so that each invocation of the buildpack makes the test once only. Beware known problems with timeouts (https://www.ruby- forum.com/topic/143840), although the code uses a timeout option on the API, so a problem would represent a bug in the Ruby HTTP code. Transient internet connection problems can cause the buildpack to enter "no internet" mode for the duration of a buildpack invocation, but that can be rectified by re-staging. If transient connection problems occur after the check for "no internet", then the read-only buildpack is not used as a look aside. This is desirable as a full repository index may have been downloaded while the internet was available and this index may refer to artefacts which are not present in the read-only buildpack cache. Stub the above HTTP GET in spec_helper.rb so that the test run ok with webmock. [#54290822]
1 parent 177555f commit f4ad8d4

3 files changed

Lines changed: 105 additions & 72 deletions

File tree

lib/java_buildpack/util/download_cache.rb

Lines changed: 99 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def evict(uri)
9393

9494
private
9595

96-
HTTP_ERRORS = [
96+
HTTP_ERRORS = [
9797
EOFError,
9898
Errno::ECONNREFUSED,
9999
Errno::ECONNRESET,
@@ -106,112 +106,139 @@ def evict(uri)
106106
Net::ProtocolError,
107107
SocketError,
108108
Timeout::Error
109-
]
109+
]
110110

111-
def delete_file(filename)
112-
File.delete filename if File.exists? filename
111+
TEST_URI = 'http://download.pivotal.io.s3.amazonaws.com/openjdk/lucid/x86_64/index.yml'.freeze
112+
113+
HTTP_OK = '200'
114+
115+
def self.internet_available?
116+
uri = TEST_URI
117+
rich_uri = URI(uri)
118+
119+
# Beware known problems with timeouts: https://www.ruby-forum.com/topic/143840
120+
Net::HTTP.start(rich_uri.host, rich_uri.port, read_timeout: 10) do |http|
121+
request = Net::HTTP::Get.new(uri)
122+
http.request request do |response|
123+
return response.code == HTTP_OK
124+
end
113125
end
126+
rescue *HTTP_ERRORS
127+
false
128+
end
129+
130+
@@internet_up = DownloadCache.internet_available?
131+
132+
def delete_file(filename)
133+
File.delete filename if File.exists? filename
134+
end
114135

115-
def download(filenames, uri)
116-
rich_uri = URI(uri)
136+
def self.internet_up
137+
@@internet_up
138+
end
139+
140+
def download(filenames, uri)
141+
if DownloadCache.internet_up
142+
begin
143+
rich_uri = URI(uri)
117144

118-
Net::HTTP.start(rich_uri.host, rich_uri.port, use_ssl: use_ssl?(rich_uri)) do |http|
119-
request = Net::HTTP::Get.new(uri)
120-
http.request request do |response|
121-
write_response(filenames, response)
145+
Net::HTTP.start(rich_uri.host, rich_uri.port, use_ssl: use_ssl?(rich_uri)) do |http|
146+
request = Net::HTTP::Get.new(uri)
147+
http.request request do |response|
148+
write_response(filenames, response)
149+
end
122150
end
123-
end
124151

125-
rescue *HTTP_ERRORS
126-
unless look_aside(filenames, uri)
152+
rescue *HTTP_ERRORS
127153
puts 'FAIL'
128154
raise "Unable to download from #{uri}"
129155
end
156+
else
157+
look_aside(filenames, uri)
130158
end
159+
end
131160

132-
def filenames(uri)
133-
key = URI.escape(uri, '/')
134-
{
161+
def filenames(uri)
162+
key = URI.escape(uri, '/')
163+
{
135164
cached: File.join(@cache_root, "#{key}.cached"),
136165
etag: File.join(@cache_root, "#{key}.etag"),
137166
last_modified: File.join(@cache_root, "#{key}.last_modified"),
138167
lock: File.join(@cache_root, "#{key}.lock")
139-
}
140-
end
168+
}
169+
end
141170

142-
# A download has failed, so check the read-only buildpack cache for the file
143-
# and use the copy there if it exists.
144-
def look_aside(filenames, uri)
145-
@logger.debug "Unable to download from #{uri}. Looking in buildpack cache."
146-
key = URI.escape(uri, '/')
147-
stashed = File.join(ENV['BUILDPACK_CACHE'], 'java-buildpack', "#{key}.cached")
148-
@logger.debug { "Looking in buildpack cache for file '#{stashed}'" }
149-
if File.exist? stashed
150-
FileUtils.cp(stashed, filenames[:cached])
151-
@logger.debug "Using copy of #{uri} from buildpack cache."
152-
true
153-
else
154-
@logger.warn "Buildpack cache does not contain #{uri}. Failing the download."
155-
@logger.debug { "Buildpack cache contents:\n#{`ls -lR #{File.join(ENV['BUILDPACK_CACHE'], 'java-buildpack')}`}" }
156-
false
157-
end
171+
# A download has failed, so check the read-only buildpack cache for the file
172+
# and use the copy there if it exists.
173+
def look_aside(filenames, uri)
174+
@logger.debug "Unable to download from #{uri}. Looking in buildpack cache."
175+
key = URI.escape(uri, '/')
176+
stashed = File.join(ENV['BUILDPACK_CACHE'], 'java-buildpack', "#{key}.cached")
177+
@logger.debug { "Looking in buildpack cache for file '#{stashed}'" }
178+
if File.exist? stashed
179+
FileUtils.cp(stashed, filenames[:cached])
180+
@logger.debug "Using copy of #{uri} from buildpack cache."
181+
else
182+
@logger.warn "Buildpack cache does not contain #{uri}. Failing the download."
183+
@logger.debug { "Buildpack cache contents:\n#{`ls -lR #{File.join(ENV['BUILDPACK_CACHE'], 'java-buildpack')}`}" }
158184
end
185+
end
159186

160-
def persist_header(response, header, filename)
161-
unless response[header].nil?
162-
File.open(filename, File::CREAT | File::WRONLY) do |file|
163-
file.write(response[header])
164-
end
187+
def persist_header(response, header, filename)
188+
unless response[header].nil?
189+
File.open(filename, File::CREAT | File::WRONLY) do |file|
190+
file.write(response[header])
165191
end
166192
end
193+
end
167194

168-
def set_header(request, header, filename)
169-
if File.exists?(filename)
170-
File.open(filename, File::RDONLY) do |file|
171-
request[header] = file.read
172-
end
195+
def set_header(request, header, filename)
196+
if File.exists?(filename)
197+
File.open(filename, File::RDONLY) do |file|
198+
request[header] = file.read
173199
end
174200
end
201+
end
175202

176-
def should_download(filenames)
177-
!File.exists?(filenames[:cached])
178-
end
203+
def should_download(filenames)
204+
!File.exists?(filenames[:cached])
205+
end
179206

180-
def should_update(filenames)
181-
File.exists?(filenames[:cached]) && (File.exists?(filenames[:etag]) || File.exists?(filenames[:last_modified]))
182-
end
207+
def should_update(filenames)
208+
File.exists?(filenames[:cached]) && (File.exists?(filenames[:etag]) || File.exists?(filenames[:last_modified]))
209+
end
183210

184-
def update(filenames, uri)
185-
rich_uri = URI(uri)
211+
def update(filenames, uri)
212+
rich_uri = URI(uri)
186213

187-
Net::HTTP.start(rich_uri.host, rich_uri.port, use_ssl: use_ssl?(rich_uri)) do |http|
188-
request = Net::HTTP::Get.new(uri)
189-
set_header request, 'If-None-Match', filenames[:etag]
190-
set_header request, 'If-Modified-Since', filenames[:last_modified]
214+
Net::HTTP.start(rich_uri.host, rich_uri.port, use_ssl: use_ssl?(rich_uri)) do |http|
215+
request = Net::HTTP::Get.new(uri)
216+
set_header request, 'If-None-Match', filenames[:etag]
217+
set_header request, 'If-Modified-Since', filenames[:last_modified]
191218

192-
http.request request do |response|
193-
write_response(filenames, response) unless response.code == '304'
194-
end
219+
http.request request do |response|
220+
write_response(filenames, response) unless response.code == '304'
195221
end
196-
197-
rescue *HTTP_ERRORS
198-
@logger.warn "Unable to update from #{uri}. Using cached version."
199222
end
200223

201-
def use_ssl?(uri)
202-
uri.scheme == 'https'
203-
end
224+
rescue *HTTP_ERRORS
225+
@logger.warn "Unable to update from #{uri}. Using cached version."
226+
end
204227

205-
def write_response(filenames, response)
206-
persist_header response, 'Etag', filenames[:etag]
207-
persist_header response, 'Last-Modified', filenames[:last_modified]
228+
def use_ssl?(uri)
229+
uri.scheme == 'https'
230+
end
208231

209-
File.open(filenames[:cached], File::CREAT | File::WRONLY) do |cached_file|
210-
response.read_body do |chunk|
211-
cached_file.write(chunk)
212-
end
232+
def write_response(filenames, response)
233+
persist_header response, 'Etag', filenames[:etag]
234+
persist_header response, 'Last-Modified', filenames[:last_modified]
235+
236+
File.open(filenames[:cached], File::CREAT | File::WRONLY) do |cached_file|
237+
response.read_body do |chunk|
238+
cached_file.write(chunk)
213239
end
214240
end
241+
end
215242

216243
end
217244

spec/java_buildpack/util/download_cache_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ module JavaBuildpack::Util
281281
FileUtils.mkdir_p java_buildpack_cache
282282
touch java_buildpack_cache, 'cached', 'foo-stashed'
283283
with_buildpack_cache(buildpack_cache) do
284+
DownloadCache.stub(:internet_up).and_return(false)
284285
DownloadCache.new(root).get('http://foo-uri/') do |file|
285286
expect(file.read).to eq('foo-stashed')
286287
end

spec/spec_helper.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828
require 'java_buildpack/diagnostics/common'
2929
require 'java_buildpack/diagnostics/logger_factory'
3030

31+
# Following required for class variable initialisation in DownloadCache.
32+
WebMock::API.stub_request(:get, 'http://download.pivotal.io.s3.amazonaws.com/openjdk/lucid/x86_64/index.yml')
33+
.with(headers: { 'Accept' => '*/*', 'User-Agent' => 'Ruby' })
34+
.to_return(status: 200, body: '', headers: {})
35+
3136
RSpec.configure do |config|
3237
config.treat_symbols_as_metadata_keys_with_true_values = true
3338
config.run_all_when_everything_filtered = true

0 commit comments

Comments
 (0)