Skip to content

Commit 8c2a0c3

Browse files
author
Glyn Normington
committed
Merge 56854324-no-internet-configuration to master
[Completes #56854324]
2 parents 4c304d0 + dd07601 commit 8c2a0c3

10 files changed

Lines changed: 183 additions & 32 deletions

File tree

Downloading

Whitespace-only changes.

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ The buildpack supports configuration and extension through the use of Git reposi
3636
* [JREs](docs/extending-jres.md)
3737
* [Frameworks](docs/extending-frameworks.md)
3838
* Utilities
39-
* [Caches](docs/util-caches.md)
40-
* [Logging](docs/logging.md)
39+
* [Caches](docs/util-caches.md) ([Configuration](docs/util-caches.md#configuration))
40+
* [Logging](docs/logging.md) ([Configuration](docs/logging.md#configuration))
4141
* [Repositories](docs/util-repositories.md)
4242
* [Repository Builder](docs/util-repository-builder.md)
4343
* [Test Applications](docs/util-test-applications.md)

config/cache.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Cloud Foundry Java Buildpack
2+
# Copyright (c) 2013 the original author or authors.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
# Download cache configuration
17+
---
18+
remote_downloads: enabled

docs/util-caches.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,18 @@ The [`DownloadCache`][] is the most generic of the three caches. It allows you
4848
def initialize(cache_root = Dir.tmpdir)
4949
```
5050

51+
### Configuration
52+
For general information on configuring the buildpack, refer to [Configuration and Extension][].
53+
54+
Caching can be configured by modifying the [`config/cache.yml`][] file.
55+
56+
| Name | Description
57+
| ---- | -----------
58+
| `remote_downloads` | This property can take the value `enabled` or `disabled`. <br><br>The default value of `enabled` means that the buildpack will check the internet connection and remember the result for the remainder of the buildpack invocation. If the internet is available, it will then be used to download files. If the internet is not available, cache will be consulted instead. <br><br>Alternatively, the property may be set to `disabled` which avoids the check for an internet connection, does not attempt downloads, and consults the cache instead.
59+
60+
[Configuration and Extension]: ../README.md#Configuration-and-Extension
61+
[`config/downloadcache.yml`]: ../config/downloadcache.yml
62+
5163
## `JavaBuildpack::Util::ApplicationCache`
5264
The [`ApplicationCache`][] is a cache that persists files into the application cache passed to the `compile` script. It examines `ARGV[1]` for the cache location and configures itself accordingly.
5365

lib/java_buildpack/util/download_cache.rb

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
require 'java_buildpack/diagnostics'
1919
require 'java_buildpack/diagnostics/logger_factory'
2020
require 'java_buildpack/util'
21+
require 'monitor'
2122
require 'net/http'
2223
require 'tmpdir'
2324
require 'uri'
25+
require 'yaml'
2426

2527
module JavaBuildpack::Util
2628

@@ -58,14 +60,16 @@ def initialize(cache_root = Dir.tmpdir)
5860
# deleted while it is being used, the cached item can only be accessed as part of a block.
5961
# @return [void]
6062
def get(uri)
63+
internet_up = DownloadCache.internet_available? uri
64+
6165
filenames = filenames(uri)
6266
File.open(filenames[:lock], File::CREAT) do |lock_file|
6367
lock_file.flock(File::LOCK_EX)
6468

65-
if should_update(filenames)
69+
if internet_up && should_update(filenames)
6670
update(filenames, uri)
6771
elsif should_download(filenames)
68-
download(filenames, uri)
72+
download(filenames, uri, internet_up)
6973
end
7074

7175
lock_file.flock(File::LOCK_SH)
@@ -94,6 +98,8 @@ def evict(uri)
9498

9599
private
96100

101+
CACHE_CONFIG = '../../../config/cache.yml'.freeze
102+
97103
HTTP_ERRORS = [
98104
EOFError,
99105
Errno::ECONNREFUSED,
@@ -107,39 +113,62 @@ def evict(uri)
107113
Net::ProtocolError,
108114
SocketError,
109115
Timeout::Error
110-
]
116+
].freeze
111117

112-
TEST_URI = 'http://download.pivotal.io.s3.amazonaws.com/openjdk/lucid/x86_64/index.yml'.freeze
118+
HTTP_OK = '200'.freeze
113119

114-
HTTP_OK = '200'
120+
@@monitor = Monitor.new
121+
@@internet_checked = false
122+
@@internet_up = true
115123

116-
def self.internet_available?
117-
uri = TEST_URI
118-
rich_uri = URI(uri)
124+
def self.get_configuration
125+
expanded_path = File.expand_path(CACHE_CONFIG, File.dirname(__FILE__))
126+
YAML.load_file(expanded_path)
127+
end
119128

120-
# Beware known problems with timeouts: https://www.ruby-forum.com/topic/143840
121-
Net::HTTP.start(rich_uri.host, rich_uri.port, read_timeout: 10, connect_timeout: 10, open_timeout: 10) do |http|
122-
request = Net::HTTP::Get.new(uri)
123-
http.request request do |response|
124-
return response.code == HTTP_OK
129+
TIMEOUT_SECONDS = 10
130+
131+
def self.internet_available?(uri)
132+
@@monitor.synchronize do
133+
return @@internet_up if @@internet_checked
134+
end
135+
cache_configuration = get_configuration
136+
if cache_configuration['remote_downloads'] == 'disabled'
137+
store_internet_availability false
138+
elsif cache_configuration['remote_downloads'] == 'enabled'
139+
begin
140+
rich_uri = URI(uri)
141+
142+
# Beware known problems with timeouts: https://www.ruby-forum.com/topic/143840
143+
Net::HTTP.start(rich_uri.host, rich_uri.port, read_timeout: TIMEOUT_SECONDS, connect_timeout: TIMEOUT_SECONDS, open_timeout: TIMEOUT_SECONDS) do |http|
144+
request = Net::HTTP::Get.new(uri)
145+
http.request request do |response|
146+
internet_up = response.code == HTTP_OK
147+
store_internet_availability internet_up
148+
end
149+
end
150+
rescue *HTTP_ERRORS
151+
store_internet_availability false
125152
end
153+
else
154+
raise "Invalid remote_downloads property in cache configuration: #{cache_configuration}"
126155
end
127-
rescue *HTTP_ERRORS
128-
false
129156
end
130157

131-
@@internet_up = DownloadCache.internet_available?
158+
def self.store_internet_availability(internet_up)
159+
@@monitor.synchronize do
160+
@@internet_up = internet_up
161+
@@internet_checked = true
162+
end
163+
internet_up
164+
end
132165

133166
def delete_file(filename)
134167
File.delete filename if File.exists? filename
135168
end
136169

137-
def self.internet_up
138-
@@internet_up
139-
end
140-
141-
def download(filenames, uri)
142-
if DownloadCache.internet_up
170+
def download(filenames, uri, internet_up)
171+
if internet_up
143172
begin
144173
rich_uri = URI(uri)
145174

@@ -181,8 +210,10 @@ def look_aside(filenames, uri)
181210
FileUtils.cp(stashed, filenames[:cached])
182211
@logger.debug "Using copy of #{uri} from buildpack cache."
183212
else
184-
@logger.warn "Buildpack cache does not contain #{uri}. Failing the download."
213+
message = "Buildpack cache does not contain #{uri}. Failing the download."
214+
@logger.error message
185215
@logger.debug { "Buildpack cache contents:\n#{`ls -lR #{File.join(ENV['BUILDPACK_CACHE'], 'java-buildpack')}`}" }
216+
raise message
186217
end
187218
end
188219

spec/java_buildpack/repository/repository_index_spec.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ module JavaBuildpack::Repository
3737

3838
it 'should use the read-only buildpack cache when index.yaml cannot be downloaded because the internet is not available' do
3939
stub_request(:get, 'http://foo.com/index.yml').to_raise(SocketError)
40-
JavaBuildpack::Util::DownloadCache.stub(:internet_up).and_return(false)
4140

4241
Dir.mktmpdir do |buildpack_cache|
4342
java_buildpack_cache = File.join(buildpack_cache, 'java-buildpack')

spec/java_buildpack/util/application_cache_spec.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,17 @@ module JavaBuildpack::Util
2626
@previous_value = ARGV[1]
2727
ARGV[1] = nil
2828
$stdout = StringIO.new
29+
30+
stub_request(:get, 'http://foo-uri/')
31+
.with(headers: { 'Accept' => '*/*', 'User-Agent' => 'Ruby' })
32+
.to_return(status: 200, body: '', headers: {})
33+
34+
DownloadCache.class_variable_set :@@internet_checked, false
2935
end
3036

3137
after do
3238
ARGV[1] = @previous_value
39+
DownloadCache.class_variable_set :@@internet_checked, false
3340
end
3441

3542
TEST_URI = 'http://foo-uri/'

spec/java_buildpack/util/download_cache_spec.rb

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,16 @@ module JavaBuildpack::Util
3030
FileUtils.rm_rf diagnostics_directory
3131
JavaBuildpack::Diagnostics::LoggerFactory.create_logger tmpdir
3232
$stdout = StringIO.new
33+
34+
stub_request(:get, 'http://foo-uri/')
35+
.with(headers: { 'Accept' => '*/*', 'User-Agent' => 'Ruby' })
36+
.to_return(status: 200, body: '', headers: {})
37+
38+
DownloadCache.class_variable_set :@@internet_checked, false
39+
end
40+
41+
after do
42+
DownloadCache.class_variable_set :@@internet_checked, false
3343
end
3444

3545
it 'should download from a uri if the cached file does not exist' do
@@ -238,6 +248,29 @@ module JavaBuildpack::Util
238248
end
239249
end
240250

251+
it 'should not overwrite existing information if the update request fails' do
252+
stub_request(:get, 'http://foo-uri/').with(
253+
headers: {
254+
'If-None-Match' => 'foo-etag',
255+
'If-Modified-Since' => 'foo-last-modified'
256+
}
257+
).to_raise(SocketError)
258+
259+
Dir.mktmpdir do |root|
260+
touch root, 'cached', 'foo-cached'
261+
touch root, 'etag', 'foo-etag'
262+
touch root, 'last_modified', 'foo-last-modified'
263+
264+
DownloadCache.new(root).get('http://foo-uri/') {}
265+
266+
expect_file_content root, 'cached', 'foo-cached'
267+
expect_file_content root, 'etag', 'foo-etag'
268+
expect_file_content root, 'last_modified', 'foo-last-modified'
269+
270+
expect($stderr.string).to match('Unable to update from http://foo-uri/ due to Exception from WebMock. Using cached version.')
271+
end
272+
end
273+
241274
it 'should pass read-only file to block' do
242275
stub_request(:get, 'http://foo-uri/').to_return(
243276
status: 200,
@@ -281,7 +314,6 @@ module JavaBuildpack::Util
281314
FileUtils.mkdir_p java_buildpack_cache
282315
touch java_buildpack_cache, 'cached', 'foo-stashed'
283316
with_buildpack_cache(buildpack_cache) do
284-
DownloadCache.stub(:internet_up).and_return(false)
285317
DownloadCache.new(root).get('http://foo-uri/') do |file|
286318
expect(file.read).to eq('foo-stashed')
287319
end
@@ -290,6 +322,38 @@ module JavaBuildpack::Util
290322
end
291323
end
292324

325+
it 'should use the buildpack cache if the cache configuration disables remote downloads' do
326+
YAML.stub(:load_file).with(File.expand_path('config/cache.yml')).and_return(
327+
'remote_downloads' => 'disabled')
328+
Dir.mktmpdir do |root|
329+
Dir.mktmpdir do |buildpack_cache|
330+
java_buildpack_cache = File.join(buildpack_cache, 'java-buildpack')
331+
FileUtils.mkdir_p java_buildpack_cache
332+
touch java_buildpack_cache, 'cached', 'foo-stashed'
333+
with_buildpack_cache(buildpack_cache) do
334+
DownloadCache.new(root).get('http://foo-uri/') do |file|
335+
expect(file.read).to eq('foo-stashed')
336+
end
337+
end
338+
end
339+
end
340+
end
341+
342+
it 'should raise an error if the cache configuration remote downloads setting is invalid' do
343+
YAML.stub(:load_file).with(File.expand_path('config/cache.yml')).and_return(
344+
'remote_downloads' => 'junk')
345+
Dir.mktmpdir do |root|
346+
Dir.mktmpdir do |buildpack_cache|
347+
java_buildpack_cache = File.join(buildpack_cache, 'java-buildpack')
348+
FileUtils.mkdir_p java_buildpack_cache
349+
touch java_buildpack_cache, 'cached', 'foo-stashed'
350+
with_buildpack_cache(buildpack_cache) do
351+
expect { DownloadCache.new(root).get('http://foo-uri/') }.to raise_error(/Invalid remote_downloads property in cache configuration:/)
352+
end
353+
end
354+
end
355+
end
356+
293357
it 'should raise error if download cannot be completed and buildpack cache does not contain the file' do
294358
stub_request(:get, 'http://foo-uri/').to_raise(SocketError)
295359

@@ -304,6 +368,24 @@ module JavaBuildpack::Util
304368
end
305369
end
306370

371+
it 'should fail if a download attempt fails' do
372+
stub_request(:get, 'http://foo-uri/').to_return(
373+
status: 200,
374+
body: 'foo-cached',
375+
headers: {
376+
Etag: 'foo-etag',
377+
'Last-Modified' => 'foo-last-modified'
378+
}
379+
)
380+
381+
stub_request(:get, 'http://bar-uri/').to_raise(SocketError)
382+
383+
Dir.mktmpdir do |root|
384+
DownloadCache.new(root).get('http://foo-uri/') {}
385+
expect { DownloadCache.new(root).get('http://bar-uri/') {} }.to raise_error(%r(Unable to download from http://bar-uri/))
386+
end
387+
end
388+
307389
def touch(root, extension, content = '')
308390
file = File.join(root, "http:%2F%2Ffoo-uri%2F.#{extension}")
309391
File.open(file, 'w') { |f| f.write(content) }

spec/java_buildpack/util/global_cache_spec.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,17 @@ module JavaBuildpack::Util
2323

2424
before do
2525
@previous_value = ENV.delete 'BUILDPACK_CACHE'
26+
27+
stub_request(:get, 'http://foo-uri/')
28+
.with(headers: { 'Accept' => '*/*', 'User-Agent' => 'Ruby' })
29+
.to_return(status: 200, body: '', headers: {})
30+
31+
DownloadCache.class_variable_set :@@internet_checked, false
2632
end
2733

2834
after do
2935
ENV['BUILDPACK_CACHE'] = @previous_value
36+
DownloadCache.class_variable_set :@@internet_checked, false
3037
end
3138

3239
it 'should raise an error if BUILDPACK_CACHE is not defined' do

spec/spec_helper.rb

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

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

0 commit comments

Comments
 (0)