-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Expand file tree
/
Copy pathcontext.go
More file actions
242 lines (214 loc) · 7.05 KB
/
context.go
File metadata and controls
242 lines (214 loc) · 7.05 KB
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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
package common
import (
"encoding/json"
"fmt"
"github.com/cloudfoundry/libbuildpack"
"io"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
)
//go:generate mockgen -source=context.go --destination=../../internal/mocks/mocks.go --package=mocks
type Command interface {
Execute(string, io.Writer, io.Writer, string, ...string) error
}
type Stager interface {
LinkDirectoryInDepDir(string, string) error
AddBinDependencyLink(string, string) error
BuildDir() string
DepDir() string
DepsIdx() string
CacheDir() string
WriteConfigYml(interface{}) error
WriteEnvFile(string, string) error
WriteProfileD(string, string) error
}
type Manifest interface {
AllDependencyVersions(string) []string
DefaultVersion(string) (libbuildpack.Dependency, error)
GetEntry(libbuildpack.Dependency) (*libbuildpack.ManifestEntry, error)
}
type Installer interface {
InstallDependency(libbuildpack.Dependency, string) error
InstallDependencyWithStrip(libbuildpack.Dependency, string, int) error
}
// Context holds shared dependencies for buildpack components
// Used by containers, frameworks, and JREs to access buildpack infrastructure
type Context struct {
Stager Stager
Manifest Manifest
Installer Installer
Log *libbuildpack.Logger
Command Command
}
// DetermineTomcatVersion determines the version of the tomcat
// based on the JBP_CONFIG_TOMCAT field from manifest
func DetermineTomcatVersion(raw string) (string, error) {
re := regexp.MustCompile(`(\d+)`)
match := re.FindStringSubmatch(raw)
if len(match) < 2 {
return "", fmt.Errorf("unable to extract Tomcat version from %q", raw)
}
return match[1] + ".x", nil
}
// DetermineJavaVersion determines the major Java version from a Java installation
// by reading the JAVA_VERSION field from the release file.
//
// Parameters:
// - javaHome: Path to the Java installation (e.g., /deps/0/jre)
//
// Returns the major version (8, 11, 17, etc.) or an error if unable to determine.
//
// Example:
//
// version, err := DetermineJavaVersion("/deps/0/jre")
func DetermineJavaVersion(javaHome string) (int, error) {
releaseFile := filepath.Join(javaHome, "release")
content, err := os.ReadFile(releaseFile)
if err != nil {
// Default to Java 17 if release file is missing
if os.IsNotExist(err) {
return 17, nil
}
return 0, fmt.Errorf("failed to read release file: %w", err)
}
// Parse JAVA_VERSION from release file
// Format: JAVA_VERSION="1.8.0_422" or JAVA_VERSION="17.0.13"
lines := strings.Split(string(content), "\n")
for _, line := range lines {
if !strings.HasPrefix(line, "JAVA_VERSION=") {
continue
}
// Extract version string from JAVA_VERSION="..."
version := strings.Trim(strings.TrimPrefix(line, "JAVA_VERSION="), "\"")
// Handle Java 7/8 format: 1.8.x or 1.7.x
if strings.HasPrefix(version, "1.8") {
return 8, nil
}
if strings.HasPrefix(version, "1.7") {
return 7, nil
}
// Handle Java 9+ format: major version is first number
// Examples: "11.0.1", "17.0.13", "21.0.1"
dotIndex := strings.Index(version, ".")
if dotIndex > 0 {
majorStr := version[:dotIndex]
if major, err := strconv.Atoi(majorStr); err == nil {
return major, nil
}
}
}
return 0, fmt.Errorf("unable to parse Java version from release file")
}
// GetJavaMajorVersion returns the Java major version from the JAVA_HOME environment variable.
// This is a convenience wrapper around DetermineJavaVersion that reads JAVA_HOME from the environment.
//
// Returns the major version (8, 11, 17, etc.) or an error if JAVA_HOME is not set
// or the version cannot be determined.
//
// Example:
//
// version, err := GetJavaMajorVersion()
func GetJavaMajorVersion() (int, error) {
javaHome := os.Getenv("JAVA_HOME")
if javaHome == "" {
return 0, fmt.Errorf("JAVA_HOME not set")
}
return DetermineJavaVersion(javaHome)
}
// VCAPServices represents the VCAP_SERVICES environment variable structure
// This is a map of service labels to arrays of service instances
type VCAPServices map[string][]VCAPService
// VCAPService represents a single Cloud Foundry service binding
type VCAPService struct {
Name string `json:"name"`
Label string `json:"label"`
Tags []string `json:"tags"`
Credentials map[string]interface{} `json:"credentials"`
}
// GetVCAPServices parses the VCAP_SERVICES environment variable
// Returns an empty VCAPServices map if VCAP_SERVICES is not set
func GetVCAPServices() (VCAPServices, error) {
vcapServicesStr := os.Getenv("VCAP_SERVICES")
if vcapServicesStr == "" {
return VCAPServices{}, nil
}
var services VCAPServices
if err := json.Unmarshal([]byte(vcapServicesStr), &services); err != nil {
return nil, err
}
return services, nil
}
// HasService checks if a service with the given label exists
// Matching is case-insensitive to handle various service broker conventions
func (v VCAPServices) HasService(label string) bool {
labelLower := strings.ToLower(label)
for key := range v {
if strings.ToLower(key) == labelLower {
return true
}
}
return false
}
// GetService returns the first service with the given label
// Returns nil if no service with the label exists
func (v VCAPServices) GetService(label string) *VCAPService {
services, exists := v[label]
if !exists || len(services) == 0 {
return nil
}
return &services[0]
}
// HasTag checks if any service has the given tag
// Matching is case-insensitive to handle various service broker tag conventions
func (v VCAPServices) HasTag(tag string) bool {
tagLower := strings.ToLower(tag)
for _, serviceList := range v {
for _, service := range serviceList {
for _, t := range service.Tags {
if strings.ToLower(t) == tagLower {
return true
}
}
}
}
return false
}
// HasServiceByNamePattern checks if any service matches the pattern
// Pattern matching is case-insensitive substring matching
// Searches across all service labels, not just "user-provided"
func (v VCAPServices) HasServiceByNamePattern(pattern string) bool {
return v.GetServiceByNamePattern(pattern) != nil
}
// GetServiceByNamePattern returns the first service that matches the pattern
// Returns nil if no matching service is found
// Pattern matching is case-insensitive substring matching (e.g., "newrelic" matches "my-newrelic-service")
// Searches across all service labels, not just "user-provided"
func (v VCAPServices) GetServiceByNamePattern(pattern string) *VCAPService {
// Case-insensitive substring matching
patternLower := strings.ToLower(pattern)
for _, services := range v {
for _, service := range services {
if strings.Contains(strings.ToLower(service.Name), patternLower) {
return &service
}
}
}
return nil
}
// HasTag checks if this service has the specified tag
func (s *VCAPService) HasTag(tag string) bool {
for _, t := range s.Tags {
if t == tag {
return true
}
}
return false
}
// ContainsIgnoreCase checks if string s contains substr (case-insensitive)
// This is a utility function used by frameworks for flexible matching
func ContainsIgnoreCase(s, substr string) bool {
return strings.Contains(strings.ToLower(s), strings.ToLower(substr))
}