buildpack-support 1.0.0

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.
Files changed (122) hide show
  1. data/LICENSE +202 -0
  2. data/NOTICE +2 -0
  3. data/docs/cache.md +77 -0
  4. data/docs/component.md +1 -0
  5. data/docs/configuration.md +27 -0
  6. data/docs/logging.md +54 -0
  7. data/docs/other.md +1 -0
  8. data/docs/rake.md +1 -0
  9. data/docs/repository.md +116 -0
  10. data/docs/test.md +1 -0
  11. data/lib/buildpack_support.rb +18 -0
  12. data/lib/buildpack_support/base_buildpack.rb +166 -0
  13. data/lib/buildpack_support/buildpack_version.rb +124 -0
  14. data/lib/buildpack_support/cache.rb +24 -0
  15. data/lib/buildpack_support/cache/application_cache.rb +41 -0
  16. data/lib/buildpack_support/cache/cached_file.rb +103 -0
  17. data/lib/buildpack_support/cache/download_cache.rb +280 -0
  18. data/lib/buildpack_support/cache/inferred_network_failure.rb +26 -0
  19. data/lib/buildpack_support/cache/internet_availability.rb +64 -0
  20. data/lib/buildpack_support/component.rb +24 -0
  21. data/lib/buildpack_support/component/application.rb +76 -0
  22. data/lib/buildpack_support/component/base_component.rb +78 -0
  23. data/lib/buildpack_support/component/base_droplet.rb +96 -0
  24. data/lib/buildpack_support/component/downloads.rb +88 -0
  25. data/lib/buildpack_support/component/services.rb +84 -0
  26. data/lib/buildpack_support/component/versioned_dependency_component.rb +71 -0
  27. data/lib/buildpack_support/component/versioned_downloads.rb +57 -0
  28. data/lib/buildpack_support/component/with_timing.rb +40 -0
  29. data/lib/buildpack_support/configuration_utils.rb +58 -0
  30. data/lib/buildpack_support/constantize.rb +46 -0
  31. data/lib/buildpack_support/dash_case.rb +29 -0
  32. data/lib/buildpack_support/directory_finder.rb +45 -0
  33. data/lib/buildpack_support/filtering_pathname.rb +227 -0
  34. data/lib/buildpack_support/format_duration.rb +57 -0
  35. data/lib/buildpack_support/logging.rb +22 -0
  36. data/lib/buildpack_support/logging/delegating_logger.rb +48 -0
  37. data/lib/buildpack_support/logging/logger_factory.rb +148 -0
  38. data/lib/buildpack_support/qualify_path.rb +36 -0
  39. data/lib/buildpack_support/rake.rb +22 -0
  40. data/lib/buildpack_support/rake/buildpack_stage_task.rb +86 -0
  41. data/lib/buildpack_support/rake/cached_artifact_finder.rb +99 -0
  42. data/lib/buildpack_support/rake/check_api_doc_task.rb +70 -0
  43. data/lib/buildpack_support/rake/dependency_cache_task.rb +87 -0
  44. data/lib/buildpack_support/rake/disable_remote_downloads_task.rb +80 -0
  45. data/lib/buildpack_support/rake/package_task.rb +133 -0
  46. data/lib/buildpack_support/rake/package_zip_task.rb +80 -0
  47. data/lib/buildpack_support/rake/repository_configuration_finder.rb +66 -0
  48. data/lib/buildpack_support/rake/write_version_file_task.rb +82 -0
  49. data/lib/buildpack_support/repository.rb +24 -0
  50. data/lib/buildpack_support/repository/configured_item.rb +81 -0
  51. data/lib/buildpack_support/repository/repository_index.rb +98 -0
  52. data/lib/buildpack_support/repository/wildcard_version_resolver.rb +75 -0
  53. data/lib/buildpack_support/shell.rb +41 -0
  54. data/lib/buildpack_support/snake_case.rb +30 -0
  55. data/lib/buildpack_support/space_case.rb +29 -0
  56. data/lib/buildpack_support/test/application_helper.rb +41 -0
  57. data/lib/buildpack_support/test/base_component_helper.rb +59 -0
  58. data/lib/buildpack_support/test/base_droplet_helper.rb +36 -0
  59. data/lib/buildpack_support/test/console_helper.rb +57 -0
  60. data/lib/buildpack_support/test/environment_helper.rb +32 -0
  61. data/lib/buildpack_support/test/internet_availability_helper.rb +29 -0
  62. data/lib/buildpack_support/test/logging_helper.rb +50 -0
  63. data/lib/buildpack_support/test/scratch_helper.rb +32 -0
  64. data/lib/buildpack_support/test/versioned_dependency_component_helper.rb +32 -0
  65. data/lib/buildpack_support/test/with_load_path_helper.rb +27 -0
  66. data/lib/buildpack_support/to_b.rb +38 -0
  67. data/lib/buildpack_support/tokenized_version.rb +157 -0
  68. data/lib/buildpack_support/version.rb +23 -0
  69. data/spec/buildpack_support/base_buildpack_spec.rb +112 -0
  70. data/spec/buildpack_support/buildpack_version_spec.rb +122 -0
  71. data/spec/buildpack_support/cache/application_cache_spec.rb +56 -0
  72. data/spec/buildpack_support/cache/cached_file_spec.rb +94 -0
  73. data/spec/buildpack_support/cache/download_cache_spec.rb +293 -0
  74. data/spec/buildpack_support/cache/internet_availability_spec.rb +57 -0
  75. data/spec/buildpack_support/cache/yield_file_with_content.rb +30 -0
  76. data/spec/buildpack_support/component/application_spec.rb +81 -0
  77. data/spec/buildpack_support/component/base_component_spec.rb +81 -0
  78. data/spec/buildpack_support/component/base_droplet_spec.rb +72 -0
  79. data/spec/buildpack_support/component/downloads_spec.rb +63 -0
  80. data/spec/buildpack_support/component/services_spec.rb +80 -0
  81. data/spec/buildpack_support/component/versioned_dependency_component_spec.rb +58 -0
  82. data/spec/buildpack_support/component/versioned_downloads_spec.rb +58 -0
  83. data/spec/buildpack_support/component/with_timing_spec.rb +30 -0
  84. data/spec/buildpack_support/configuration_utils_spec.rb +39 -0
  85. data/spec/buildpack_support/constantize_spec.rb +34 -0
  86. data/spec/buildpack_support/dash_case_spec.rb +41 -0
  87. data/spec/buildpack_support/directory_finder_spec.rb +41 -0
  88. data/spec/buildpack_support/filtering_pathname_spec.rb +443 -0
  89. data/spec/buildpack_support/format_duration_spec.rb +60 -0
  90. data/spec/buildpack_support/logging/delegating_logger_spec.rb +62 -0
  91. data/spec/buildpack_support/logging/logger_factory_spec.rb +262 -0
  92. data/spec/buildpack_support/qualify_path_spec.rb +42 -0
  93. data/spec/buildpack_support/rake/buildpack_stage_task_spec.rb +88 -0
  94. data/spec/buildpack_support/rake/cached_artifact_finder_spec.rb +73 -0
  95. data/spec/buildpack_support/rake/check_api_doc_task_spec.rb +69 -0
  96. data/spec/buildpack_support/rake/dependency_cache_task_spec.rb +133 -0
  97. data/spec/buildpack_support/rake/disable_remote_downloads_task_spec.rb +91 -0
  98. data/spec/buildpack_support/rake/package_task_spec.rb +335 -0
  99. data/spec/buildpack_support/rake/package_zip_task_spec.rb +91 -0
  100. data/spec/buildpack_support/rake/repository_configuration_finder_spec.rb +61 -0
  101. data/spec/buildpack_support/rake/write_version_file_task_spec.rb +96 -0
  102. data/spec/buildpack_support/repository/configured_item_spec.rb +78 -0
  103. data/spec/buildpack_support/repository/repository_index_spec.rb +118 -0
  104. data/spec/buildpack_support/repository/wildcard_version_resolver_spec.rb +73 -0
  105. data/spec/buildpack_support/shell_spec.rb +32 -0
  106. data/spec/buildpack_support/snake_case_spec.rb +45 -0
  107. data/spec/buildpack_support/space_case_spec.rb +41 -0
  108. data/spec/buildpack_support/to_b_spec.rb +41 -0
  109. data/spec/buildpack_support/tokenized_version_spec.rb +132 -0
  110. data/spec/fixtures/application/test-file +0 -0
  111. data/spec/fixtures/config/found-config.yml +2 -0
  112. data/spec/fixtures/droplet-resources/droplet-resource +0 -0
  113. data/spec/fixtures/stub-download-with-top-level.zip +0 -0
  114. data/spec/fixtures/stub-download.tar.gz +0 -0
  115. data/spec/fixtures/stub-download.zip +0 -0
  116. data/spec/fixtures/test-cache.yml +18 -0
  117. data/spec/fixtures/test-index.yml +2 -0
  118. data/spec/fixtures/test_component.rb +0 -0
  119. data/spec/fixtures/zip-contents/test-directory/test-deep-file +0 -0
  120. data/spec/fixtures/zip-contents/test-file +0 -0
  121. data/spec/spec_helper.rb +30 -0
  122. metadata +416 -0
@@ -0,0 +1,280 @@
1
+ # Encoding: utf-8
2
+ # Copyright 2013-2014 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
+ require 'buildpack_support/cache'
17
+ require 'buildpack_support/cache/cached_file'
18
+ require 'buildpack_support/cache/inferred_network_failure'
19
+ require 'buildpack_support/cache/internet_availability'
20
+ require 'buildpack_support/directory_finder'
21
+ require 'buildpack_support/logging/logger_factory'
22
+ require 'monitor'
23
+ require 'net/http'
24
+ require 'tmpdir'
25
+ require 'uri'
26
+
27
+ module BuildpackSupport
28
+ module Cache
29
+
30
+ # A cache for downloaded files that is configured to use a filesystem as the backing store.
31
+ #
32
+ # Note: this class is thread-safe, however access to the cached files is not
33
+ #
34
+ # References:
35
+ # * {https://en.wikipedia.org/wiki/HTTP_ETag ETag Wikipedia Definition}
36
+ # * {http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html HTTP/1.1 Header Field Definitions}
37
+ class DownloadCache
38
+ extend DirectoryFinder
39
+
40
+ # The location to find cached resources in the buildpack
41
+ CACHED_RESOURCES_DIRECTORY = DownloadCache.load_path_peer('resources/cache').freeze
42
+
43
+ # Creates an instance of the cache that is backed by a number of filesystem locations. The first argument
44
+ # (+mutable_cache_root+) is the only location that downloaded files will be stored in.
45
+ #
46
+ # @param [Pathname] mutable_cache_root the filesystem location in which find cached files in. This will also be
47
+ # the location that all downloaded files are written to.
48
+ # @param [Pathname] immutable_cache_roots other filesystem locations to find cached files in. No files will be
49
+ # written to these locations.
50
+ def initialize(mutable_cache_root = Pathname.new(Dir.tmpdir), *immutable_cache_roots)
51
+ @logger = BuildpackSupport::Logging::LoggerFactory.instance.get_logger DownloadCache
52
+ @mutable_cache_root = mutable_cache_root
53
+ @immutable_cache_roots = immutable_cache_roots.unshift mutable_cache_root
54
+ end
55
+
56
+ # Retrieves an item from the cache. Yields an open file containing the item's content or raises an exception if
57
+ # the item cannot be retrieved.
58
+ #
59
+ # @param [String] uri the URI of the item
60
+ # @yield [file, downloaded] the file representing the cached item and whether the file was downloaded or was
61
+ # already in the cache
62
+ # @return [Void]
63
+ def get(uri, &block)
64
+ cached_file, downloaded = nil, nil
65
+ cached_file, downloaded = from_mutable_cache uri if InternetAvailability.instance.available?
66
+ cached_file, downloaded = from_immutable_caches(uri), false unless cached_file
67
+
68
+ fail "Unable to find cached file for #{uri}" unless cached_file
69
+ cached_file.cached(File::RDONLY, downloaded, &block)
70
+ end
71
+
72
+ # Removes an item from the mutable cache.
73
+ #
74
+ # @param [String] uri the URI of the item
75
+ # @return [Void]
76
+ def evict(uri)
77
+ CachedFile.new(@mutable_cache_root, uri).destroy
78
+ end
79
+
80
+ private
81
+
82
+ FAILURE_LIMIT = 5.freeze
83
+
84
+ HTTP_ERRORS = [
85
+ EOFError,
86
+ Errno::ECONNABORTED,
87
+ Errno::ECONNREFUSED,
88
+ Errno::ECONNRESET,
89
+ Errno::EHOSTDOWN,
90
+ Errno::EHOSTUNREACH,
91
+ Errno::EINVAL,
92
+ Errno::ENETDOWN,
93
+ Errno::ENETRESET,
94
+ Errno::ENETUNREACH,
95
+ Errno::ENONET,
96
+ Errno::ENOTCONN,
97
+ Errno::EPIPE,
98
+ Errno::ETIMEDOUT,
99
+ Net::HTTPBadResponse,
100
+ Net::HTTPHeaderSyntaxError,
101
+ Net::ProtocolError,
102
+ SocketError,
103
+ Timeout::Error
104
+ ].freeze
105
+
106
+ REDIRECT_TYPES = [
107
+ Net::HTTPMovedPermanently,
108
+ Net::HTTPFound,
109
+ Net::HTTPSeeOther,
110
+ Net::HTTPTemporaryRedirect
111
+ ].freeze
112
+
113
+ TIMEOUT_SECONDS = 10.freeze
114
+
115
+ private_constant :FAILURE_LIMIT, :HTTP_ERRORS, :REDIRECT_TYPES, :TIMEOUT_SECONDS
116
+
117
+ def attempt(http, request, cached_file)
118
+ downloaded = false
119
+
120
+ http.request request do |response|
121
+ @logger.debug { "Status: #{response.code}" }
122
+
123
+ if response.is_a? Net::HTTPOK
124
+ cache_etag response, cached_file
125
+ cache_last_modified response, cached_file
126
+ cache_content response, cached_file
127
+ downloaded = true
128
+ elsif response.is_a? Net::HTTPNotModified
129
+ @logger.debug { 'Cached copy up to date' }
130
+ elsif redirect?(response)
131
+ downloaded = update URI(response['Location']), cached_file
132
+ else
133
+ fail InferredNetworkFailure, "Bad response: #{response}"
134
+ end
135
+ end
136
+
137
+ downloaded
138
+ end
139
+
140
+ def cache_content(response, cached_file)
141
+ cached_file.cached(File::CREAT | File::WRONLY) do |f|
142
+ @logger.debug { "Persisting content to #{f.path}" }
143
+
144
+ f.truncate(0)
145
+ response.read_body { |chunk| f.write chunk }
146
+ f.fsync
147
+ end
148
+
149
+ validate_size response['Content-Length'], cached_file
150
+ end
151
+
152
+ def cache_etag(response, cached_file)
153
+ etag = response['Etag']
154
+
155
+ return unless etag
156
+
157
+ @logger.debug { "Persisting etag: #{etag}" }
158
+
159
+ cached_file.etag(File::CREAT | File::WRONLY) do |f|
160
+ f.truncate(0)
161
+ f.write etag
162
+ f.fsync
163
+ end
164
+ end
165
+
166
+ def cache_last_modified(response, cached_file)
167
+ last_modified = response['Last-Modified']
168
+
169
+ return unless last_modified
170
+
171
+ @logger.debug { "Persisting last-modified: #{last_modified}" }
172
+
173
+ cached_file.last_modified(File::CREAT | File::WRONLY) do |f|
174
+ f.truncate(0)
175
+ f.write last_modified
176
+ f.fsync
177
+ end
178
+ end
179
+
180
+ def from_mutable_cache(uri)
181
+ cached_file = CachedFile.new(@mutable_cache_root, uri)
182
+ cached = update URI(uri), cached_file
183
+ [cached_file, cached]
184
+ rescue => e
185
+ @logger.warn { "Unable to download #{uri} into cache #{@mutable_cache_root}: #{e.message}" }
186
+ nil
187
+ end
188
+
189
+ def from_immutable_caches(uri)
190
+ @immutable_cache_roots.each do |cache_root|
191
+ candidate = CachedFile.new cache_root, uri
192
+
193
+ next unless candidate.cached?
194
+
195
+ @logger.debug { "#{uri} found in cache #{cache_root}" }
196
+ return candidate
197
+ end
198
+
199
+ nil
200
+ end
201
+
202
+ # Beware known problems with timeouts: https://www.ruby-forum.com/topic/143840
203
+ def http_options(rich_uri)
204
+ { read_timeout: TIMEOUT_SECONDS,
205
+ connect_timeout: TIMEOUT_SECONDS,
206
+ open_timeout: TIMEOUT_SECONDS,
207
+ use_ssl: secure?(rich_uri) }
208
+ end
209
+
210
+ def proxy(uri)
211
+ proxy_uri = if secure?(uri)
212
+ URI.parse(ENV['https_proxy'] || ENV['HTTPS_PROXY'] || '')
213
+ else
214
+ URI.parse(ENV['http_proxy'] || ENV['HTTP_PROXY'] || '')
215
+ end
216
+
217
+ @logger.debug { "Proxy: #{proxy_uri.host}, #{proxy_uri.port}, #{proxy_uri.user}, #{proxy_uri.password}" }
218
+ Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
219
+ end
220
+
221
+ def redirect?(response)
222
+ REDIRECT_TYPES.any? { |t| response.is_a? t }
223
+ end
224
+
225
+ def request(uri, cached_file)
226
+ request = Net::HTTP::Get.new(uri.request_uri)
227
+
228
+ if cached_file.etag?
229
+ cached_file.etag(File::RDONLY) { |f| request['If-None-Match'] = File.read(f) }
230
+ end
231
+
232
+ if cached_file.last_modified?
233
+ cached_file.last_modified(File::RDONLY) { |f| request['If-Modified-Since'] = File.read(f) }
234
+ end
235
+
236
+ @logger.debug { "Request: #{request.path}, #{request.to_hash}" }
237
+ request
238
+ end
239
+
240
+ def secure?(uri)
241
+ uri.scheme == 'https'
242
+ end
243
+
244
+ def update(uri, cached_file)
245
+ proxy(uri).start(uri.host, uri.port, http_options(uri)) do |http|
246
+ @logger.debug { "HTTP: #{http.address}, #{http.port}, #{http_options(uri)}" }
247
+ request = request uri, cached_file
248
+ request.basic_auth uri.user, uri.password
249
+
250
+ failures = 0
251
+ begin
252
+ attempt http, request, cached_file
253
+ rescue InferredNetworkFailure, *HTTP_ERRORS => e
254
+ if (failures += 1) > FAILURE_LIMIT
255
+ InternetAvailability.instance.available false, "Request failed: #{e.message}"
256
+ raise e
257
+ else
258
+ @logger.warn { "Request failure #{failures}, retrying: #{e.message}" }
259
+ retry
260
+ end
261
+ end
262
+ end
263
+ end
264
+
265
+ def validate_size(expected_size, cached_file)
266
+ return unless expected_size
267
+
268
+ actual_size = cached_file.cached(File::RDONLY) { |f| f.size }
269
+ @logger.debug { "Validated content size #{actual_size} is #{expected_size}" }
270
+
271
+ return if expected_size.to_i == actual_size
272
+
273
+ cached_file.destroy
274
+ fail InferredNetworkFailure, "Content has invalid size. Was #{actual_size}, should be #{expected_size}."
275
+ end
276
+
277
+ end
278
+
279
+ end
280
+ end
@@ -0,0 +1,26 @@
1
+ # Encoding: utf-8
2
+ # Copyright 2013-2014 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
+ require 'buildpack_support/cache'
17
+
18
+ module BuildpackSupport
19
+ module Cache
20
+
21
+ # An error thrown when a we infer that an error has occurred (rather than receiving an explicit indication)
22
+ class InferredNetworkFailure < StandardError
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,64 @@
1
+ # Encoding: utf-8
2
+ # Copyright 2013-2014 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
+ require 'buildpack_support/cache'
17
+ require 'buildpack_support/configuration_utils'
18
+ require 'buildpack_support/logging/logger_factory'
19
+ require 'monitor'
20
+ require 'singleton'
21
+
22
+ module BuildpackSupport
23
+ module Cache
24
+
25
+ # Maintains the current state of internet availability.
26
+ class InternetAvailability
27
+ include ::Singleton
28
+
29
+ # Creates a new instance. Availability is assumed to be +true+ unless +remote_downloads+ is set to +disabled+
30
+ # in +config/cache.yml+.
31
+ def initialize
32
+ @logger = BuildpackSupport::Logging::LoggerFactory.instance.get_logger InternetAvailability
33
+ @monitor = Monitor.new
34
+ @monitor.synchronize { @available = remote_downloads? }
35
+ end
36
+
37
+ # Returns whether the internet is available
38
+ #
39
+ # @return [Boolean] +true+ if the internet is available, +false+ otherwise
40
+ def available?
41
+ @monitor.synchronize { @available }
42
+ end
43
+
44
+ # Sets whether the internet is available
45
+ #
46
+ # @param [Boolean] available whether the internet is available
47
+ # @param [String, nil] message an optional message to be printed when the availability is set
48
+ def available(available, message = nil)
49
+ @monitor.synchronize do
50
+ @available = available
51
+ @logger.warn { "Internet availability set to #{available}: #{message}" } if message
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def remote_downloads?
58
+ BuildpackSupport::ConfigurationUtils.new.load('cache')['remote_downloads'] != 'disabled'
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,24 @@
1
+ # Encoding: utf-8
2
+ # Copyright 2013-2014 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
+ require 'buildpack_support'
17
+
18
+ module BuildpackSupport
19
+
20
+ # A module encapsulating all of the code for the component utilities
21
+ module Component
22
+ end
23
+
24
+ end
@@ -0,0 +1,76 @@
1
+ # Encoding: utf-8
2
+ # Copyright 2013-2014 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
+ require 'buildpack_support/component'
17
+ require 'buildpack_support/component/services'
18
+ require 'buildpack_support/filtering_pathname'
19
+ require 'yaml'
20
+
21
+ module BuildpackSupport
22
+ module Component
23
+
24
+ # An abstraction around the application as uploaded by the user. This abstraction is intended to hide any
25
+ # modifications made to the filesystem by other components. Think of this as an immutable representation of the
26
+ # application as it was uploaded.
27
+ #
28
+ # A new instance of this type should be created once for the application.
29
+ class Application
30
+
31
+ # @!attribute [r] details
32
+ # @return [Hash] the parsed contents of the +VCAP_APPLICATION+ environment variable
33
+ attr_reader :details
34
+
35
+ # @!attribute [r] environment
36
+ # @return [Hash] all environment variables except +VCAP_APPLICATION+ and +VCAP_SERVICES+. Those values are
37
+ # available separately in parsed form.
38
+ attr_reader :environment
39
+
40
+ # @!attribute [r] root
41
+ # @return [BuildpackSupport::FilteringPathname] the root of the application's fileystem filtered so that it only
42
+ # shows files that have been uploaded by the user
43
+ attr_reader :root
44
+
45
+ # @!attribute [r] services
46
+ # @return [Hash] the parsed contents of the +VCAP_SERVICES+ environment variable
47
+ attr_reader :services
48
+
49
+ # Create a new instance of the application abstraction
50
+ #
51
+ # @param [Pathname] root the root of the application
52
+ def initialize(root)
53
+ initial = children(root)
54
+ @root = BuildpackSupport::FilteringPathname.new(root, ->(path) { initial.member? path }, false)
55
+
56
+ @environment = ENV.to_hash
57
+ @details = parse(@environment.delete('VCAP_APPLICATION'))
58
+ @services = Services.new(parse(@environment.delete('VCAP_SERVICES')))
59
+ end
60
+
61
+ private
62
+
63
+ def children(root, s = Set.new)
64
+ s << root
65
+ root.children.each { |child| children(child, s) } if root.directory?
66
+ s
67
+ end
68
+
69
+ def parse(input)
70
+ input ? YAML.load(input) : {}
71
+ end
72
+
73
+ end
74
+
75
+ end
76
+ end