forked from cloudfoundry/java-buildpack
-
Notifications
You must be signed in to change notification settings - Fork 0
/
base_component.rb
204 lines (179 loc) · 8.44 KB
/
base_component.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# frozen_string_literal: true
# Cloud Foundry Java Buildpack
# Copyright 2013-2020 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'fileutils'
require 'java_buildpack/component'
require 'java_buildpack/util/cache/cache_factory'
require 'java_buildpack/util/colorize'
require 'java_buildpack/util/format_duration'
require 'java_buildpack/util/shell'
require 'java_buildpack/util/space_case'
require 'java_buildpack/util/sanitizer'
module JavaBuildpack
module Component
# A convenience base class for all components in the buildpack. This base class ensures that the contents of the
# +context+ are assigned to instance variables matching their keys. It also ensures that all contract methods are
# implemented.
class BaseComponent
include JavaBuildpack::Util::Shell
# Creates an instance. The contents of +context+ are assigned to the instance variables matching their keys.
#
# @param [Hash] context a collection of utilities used by components
# @option context [JavaBuildpack::Component::Application] :application the application
# @option context [Hash] :configuration the component's configuration
# @option context [JavaBuildpack::Component::Droplet] :droplet the droplet
def initialize(context)
@application = context[:application]
@component_name = self.class.to_s.space_case
@configuration = context[:configuration]
@droplet = context[:droplet]
end
# If the component should be used when staging an application
#
# @return [Array<String>, String, nil] If the component should be used when staging the application, a +String+ or
# an +Array<String>+ that uniquely identifies the component (e.g.
# +open_jdk=1.7.0_40+). Otherwise, +nil+.
def detect
raise "Method 'detect' must be defined"
end
# Modifies the application's file system. The component is expected to transform the application's file system in
# whatever way is necessary (e.g. downloading files or creating symbolic links) to support the function of the
# component. Status output written to +STDOUT+ is expected as part of this invocation.
#
# @return [Void]
def compile
raise "Method 'compile' must be defined"
end
# Modifies the application's runtime configuration. The component is expected to transform members of the
# +context+ # (e.g. +@java_home+, +@java_opts+, etc.) in whatever way is necessary to support the function of the
# component.
#
# Container components are also expected to create the command required to run the application. These components
# are expected to read the +context+ values and take them into account when creating the command.
#
# @return [void, String] components other than containers and JREs are not expected to return any value.
# Container and JRE components are expected to return a command required to run the
# application.
def release
raise "Method 'release' must be defined"
end
protected
# Downloads an item with the given name and version from the given URI, then yields the resultant file to the
# given # block.
#
# @param [JavaBuildpack::Util::TokenizedVersion] version
# @param [String] uri
# @param [String] name an optional name for the download. Defaults to +@component_name+.
# @return [Void]
def download(version, uri, name = @component_name)
download_start_time = Time.now
print "#{'----->'.red.bold} Downloading #{name.blue.bold} #{version.to_s.blue} from #{uri.sanitize_uri} "
JavaBuildpack::Util::Cache::CacheFactory.create.get(uri) do |file, downloaded|
if downloaded
puts "(#{(Time.now - download_start_time).duration})".green.italic
else
puts '(found in cache)'.green.italic
end
yield file
end
end
# Downloads a given JAR file and stores it.
#
# @param [String] version the version of the download
# @param [String] uri the uri of the download
# @param [String] jar_name the name to save the jar as
# @param [Pathname] target_directory the directory to store the JAR file in. Defaults to the component's sandbox.
# @param [String] name an optional name for the download. Defaults to +@component_name+.
# @return [Void]
def download_jar(version, uri, jar_name, target_directory = @droplet.sandbox, name = @component_name)
download(version, uri, name) do |file|
FileUtils.mkdir_p target_directory
FileUtils.cp_r(file.path, target_directory + jar_name)
end
end
# Downloads a given TAR file and expands it.
#
# @param [String] version the version of the download
# @param [String] uri the uri of the download
# @param [Boolean] strip_top_level whether to strip the top-level directory when expanding. Defaults to +true+.
# @param [Pathname] target_directory the directory to expand the TAR file to. Defaults to the component's
# sandbox.
# @param [String] name an optional name for the download and expansion. Defaults to +@component_name+.
# @return [Void]
def download_tar(version, uri, strip_top_level = true, target_directory = @droplet.sandbox,
name = @component_name)
download(version, uri, name) do |file|
with_timing "Expanding #{name} to #{target_directory.relative_path_from(@droplet.root)}" do
FileUtils.mkdir_p target_directory
shell "tar x#{compression_flag(file)}f #{file.path} -C #{target_directory} " \
"#{'--strip 1' if strip_top_level} 2>&1"
end
end
end
# Downloads a given ZIP file and expands it.
#
# @param [String] version the version of the download
# @param [String] uri the uri of the download
# @param [Boolean] strip_top_level whether to strip the top-level directory when expanding. Defaults to +true+.
# @param [Pathname] target_directory the directory to expand the ZIP file to. Defaults to the component's
# sandbox.
# @param [String] name an optional name for the download. Defaults to +@component_name+.
# @return [Void]
def download_zip(version, uri, strip_top_level = true, target_directory = @droplet.sandbox,
name = @component_name)
download(version, uri, name) do |file|
with_timing "Expanding #{name} to #{target_directory.relative_path_from(@droplet.root)}" do
if strip_top_level
Dir.mktmpdir do |root|
shell "unzip -qq #{file.path} -d #{root} 2>&1"
FileUtils.mkdir_p target_directory.parent
FileUtils.mv Pathname.new(root).children.first, target_directory
end
else
FileUtils.mkdir_p target_directory
shell "unzip -qq #{file.path} -d #{target_directory} 2>&1"
end
end
end
end
# Wrap the execution of a block with timing information
#
# @param [String] caption the caption to print when timing starts
# @return [Void]
def with_timing(caption, include_arrow = false)
start_time = Time.now
print "#{include_arrow ? '----->'.red.bold : ' '} #{caption} "
yield
puts "(#{(Time.now - start_time).duration})".green.italic
end
private
def gzipped?(file)
file.path.end_with? '.gz'
end
def bzipped?(file)
file.path.end_with? '.bz2'
end
def compression_flag(file)
if gzipped?(file)
'z'
elsif bzipped?(file)
'j'
else
''
end
end
end
end
end