Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/java/containers/dist_zip.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,9 @@ export PATH=$DIST_ZIP_BIN:$PATH
}

// Most distZip scripts respect JAVA_OPTS environment variable
// Write JAVA_OPTS for the startup script to use
if err := d.context.Stager.WriteEnvFile("JAVA_OPTS",
strings.Join(javaOpts, " ")); err != nil {
return fmt.Errorf("failed to write JAVA_OPTS: %w", err)
javaOptsScript := fmt.Sprintf("export JAVA_OPTS=\"%s\"\n", strings.Join(javaOpts, " "))
if err := d.context.Stager.WriteProfileD("dist_zip_java_opts.sh", javaOptsScript); err != nil {
return fmt.Errorf("failed to write JAVA_OPTS profile.d script: %w", err)
}

d.context.Log.Info("DistZip finalization complete (using environment variables, not modifying scripts)")
Expand Down
10 changes: 10 additions & 0 deletions src/java/containers/dist_zip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,5 +201,15 @@ var _ = Describe("Dist ZIP Container", func() {
err := container.Finalize()
Expect(err).NotTo(HaveOccurred())
})

It("writes profile.d script that exports JAVA_OPTS with $TMPDIR", func() {
Expect(container.Finalize()).To(Succeed())
scriptPath := filepath.Join(depsDir, "0", "profile.d", "dist_zip_java_opts.sh")
Expect(scriptPath).To(BeAnExistingFile())
content, err := os.ReadFile(scriptPath)
Expect(err).NotTo(HaveOccurred())
Expect(string(content)).To(ContainSubstring("export JAVA_OPTS="))
Expect(string(content)).To(ContainSubstring("$TMPDIR"))
})
})
})
7 changes: 3 additions & 4 deletions src/java/containers/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,10 +423,9 @@ export PATH=$PLAY_BIN:$PATH
}

// Play start scripts respect JAVA_OPTS environment variable
// Write JAVA_OPTS for the startup script to use
if err := p.context.Stager.WriteEnvFile("JAVA_OPTS",
strings.Join(javaOpts, " ")); err != nil {
return fmt.Errorf("failed to write JAVA_OPTS: %w", err)
javaOptsScript := fmt.Sprintf("export JAVA_OPTS=\"%s\"\n", strings.Join(javaOpts, " "))
if err := p.context.Stager.WriteProfileD("play_java_opts.sh", javaOptsScript); err != nil {
return fmt.Errorf("failed to write JAVA_OPTS profile.d script: %w", err)
}

p.context.Log.Info("Play Framework finalization complete (using environment variables, not modifying scripts)")
Expand Down
11 changes: 11 additions & 0 deletions src/java/containers/play_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,17 @@ var _ = Describe("Play Container", func() {
err := container.Finalize()
Expect(err).NotTo(HaveOccurred())
})

It("writes profile.d script that exports JAVA_OPTS with $PORT and $TMPDIR", func() {
Expect(container.Finalize()).To(Succeed())
scriptPath := filepath.Join(depsDir, "0", "profile.d", "play_java_opts.sh")
Expect(scriptPath).To(BeAnExistingFile())
content, err := os.ReadFile(scriptPath)
Expect(err).NotTo(HaveOccurred())
Expect(string(content)).To(ContainSubstring("export JAVA_OPTS="))
Expect(string(content)).To(ContainSubstring("$PORT"))
Expect(string(content)).To(ContainSubstring("$TMPDIR"))
})
})
})
})
8 changes: 4 additions & 4 deletions src/java/containers/spring_boot_cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,12 @@ func (s *SpringBootCLIContainer) Supply() error {
func (s *SpringBootCLIContainer) Finalize() error {
s.context.Log.BeginStep("Finalizing Spring Boot CLI")

// Set environment variables for Spring Boot CLI
if err := s.context.Stager.WriteEnvFile("JAVA_OPTS", "$JAVA_OPTS"); err != nil {
s.context.Log.Warning("Failed to set JAVA_OPTS: %s", err.Error())
// $JAVA_OPTS and $PORT are runtime variables — WriteProfileD ensures they are
// expanded at container startup rather than stored as literal strings.
if err := s.context.Stager.WriteProfileD("spring_boot_cli_java_opts.sh", "export JAVA_OPTS=$JAVA_OPTS\n"); err != nil {
return fmt.Errorf("failed to write JAVA_OPTS profile.d script: %w", err)
}

// Use WriteProfileD so $PORT is shell-expanded at runtime (WriteEnvFile writes plain text, no expansion).
if err := s.context.Stager.WriteProfileD("spring_boot_cli_server_port.sh", "export SERVER_PORT=$PORT\n"); err != nil {
return fmt.Errorf("failed to write SERVER_PORT profile.d script: %w", err)
}
Expand Down
31 changes: 14 additions & 17 deletions src/java/frameworks/luna_security_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package frameworks

import (
"fmt"
"github.com/cloudfoundry/java-buildpack/src/java/common"
"github.com/cloudfoundry/java-buildpack/src/java/resources"
"os"
"path/filepath"
"strings"

"github.com/cloudfoundry/java-buildpack/src/java/common"
"github.com/cloudfoundry/java-buildpack/src/java/resources"
)

// LunaSecurityProviderFramework implements Safenet Luna HSM Java Security Provider support
Expand Down Expand Up @@ -116,13 +117,13 @@ func (l *LunaSecurityProviderFramework) installDefaultConfiguration(lunaDir stri

// Finalize configures the Luna security provider for runtime
func (l *LunaSecurityProviderFramework) Finalize() error {
// Get buildpack index for multi-buildpack support
// Set ChrystokiConfigurationPath and (for Java 9+) LD_LIBRARY_PATH via profile.d.
// $DEPS_DIR is a runtime variable — WriteProfileD ensures it is expanded at
// container startup rather than stored as a literal string.
depsIdx := l.context.Stager.DepsIdx()
lunaRuntimeDir := fmt.Sprintf("$DEPS_DIR/%s/luna_security_provider", depsIdx)

// Set ChrystokiConfigurationPath environment variable with runtime path
if err := l.context.Stager.WriteEnvFile("ChrystokiConfigurationPath", fmt.Sprintf("$DEPS_DIR/%s/luna_security_provider", depsIdx)); err != nil {
return fmt.Errorf("failed to set ChrystokiConfigurationPath: %w", err)
}
profileScript := fmt.Sprintf("export ChrystokiConfigurationPath=%s\n", lunaRuntimeDir)

// Detect Java version to determine extension mechanism
javaVersion, err := common.GetJavaMajorVersion()
Expand All @@ -140,22 +141,18 @@ func (l *LunaSecurityProviderFramework) Finalize() error {
// Build JAVA_OPTS with runtime path
javaOpts = fmt.Sprintf("-Xbootclasspath/a:%s", lunaProviderJar)

// Set LD_LIBRARY_PATH for native library loading
existingLdPath := os.Getenv("LD_LIBRARY_PATH")
newLdPath := ldLibPath
if existingLdPath != "" {
newLdPath = existingLdPath + ":" + ldLibPath
}

if err := l.context.Stager.WriteEnvFile("LD_LIBRARY_PATH", newLdPath); err != nil {
return fmt.Errorf("failed to set LD_LIBRARY_PATH for Luna Security Provider: %w", err)
}
// Append to LD_LIBRARY_PATH at runtime via profile.d so $DEPS_DIR is expanded.
profileScript += fmt.Sprintf("export LD_LIBRARY_PATH=%s${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}\n", ldLibPath)
} else {
// Java 8: Use extension directory
extDir := fmt.Sprintf("$DEPS_DIR/%s/luna_security_provider/ext", depsIdx)
javaOpts = fmt.Sprintf("-Djava.ext.dirs=%s:$JAVA_HOME/jre/lib/ext:$JAVA_HOME/lib/ext", extDir)
}

if err := l.context.Stager.WriteProfileD("luna_security_provider.sh", profileScript); err != nil {
return fmt.Errorf("failed to write Luna Security Provider profile.d script: %w", err)
}

// Write to .opts file using priority 32
if err := writeJavaOptsFile(l.context, 32, "luna_security_provider", javaOpts); err != nil {
return fmt.Errorf("failed to write java_opts file: %w", err)
Expand Down
29 changes: 14 additions & 15 deletions src/java/frameworks/luna_security_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,32 +283,31 @@ var _ = Describe("LunaSecurityProvider", func() {
Expect(string(content)).NotTo(ContainSubstring(depsDir))
})

It("writes ChrystokiConfigurationPath env file pointing to runtime path", func() {
It("writes profile.d script exporting ChrystokiConfigurationPath with runtime path", func() {
Expect(fw.Finalize()).To(Succeed())
envPath := filepath.Join(depsDir, "0", "env", "ChrystokiConfigurationPath")
Expect(envPath).To(BeAnExistingFile())
content, err := os.ReadFile(envPath)
scriptPath := filepath.Join(depsDir, "0", "profile.d", "luna_security_provider.sh")
Expect(scriptPath).To(BeAnExistingFile())
content, err := os.ReadFile(scriptPath)
Expect(err).NotTo(HaveOccurred())
Expect(string(content)).To(Equal("$DEPS_DIR/0/luna_security_provider"))
Expect(string(content)).To(ContainSubstring("export ChrystokiConfigurationPath=$DEPS_DIR/0/luna_security_provider"))
})

It("writes LD_LIBRARY_PATH env file pointing to the native library directory", func() {
It("writes profile.d script exporting LD_LIBRARY_PATH with runtime path", func() {
Expect(fw.Finalize()).To(Succeed())
envPath := filepath.Join(depsDir, "0", "env", "LD_LIBRARY_PATH")
Expect(envPath).To(BeAnExistingFile())
content, err := os.ReadFile(envPath)
scriptPath := filepath.Join(depsDir, "0", "profile.d", "luna_security_provider.sh")
Expect(scriptPath).To(BeAnExistingFile())
content, err := os.ReadFile(scriptPath)
Expect(err).NotTo(HaveOccurred())
Expect(string(content)).To(ContainSubstring("$DEPS_DIR/0/luna_security_provider/jsp/64"))
})

It("appends to existing LD_LIBRARY_PATH", func() {
os.Setenv("LD_LIBRARY_PATH", "/existing/lib")
It("uses shell parameter expansion to preserve existing LD_LIBRARY_PATH at runtime", func() {
Expect(fw.Finalize()).To(Succeed())
envPath := filepath.Join(depsDir, "0", "env", "LD_LIBRARY_PATH")
content, err := os.ReadFile(envPath)
scriptPath := filepath.Join(depsDir, "0", "profile.d", "luna_security_provider.sh")
content, err := os.ReadFile(scriptPath)
Expect(err).NotTo(HaveOccurred())
Expect(string(content)).To(ContainSubstring("/existing/lib"))
Expect(string(content)).To(ContainSubstring("$DEPS_DIR/0/luna_security_provider/jsp/64"))
// Shell expansion ${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} appends existing value at runtime
Expect(string(content)).To(ContainSubstring("${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"))
})
})

Expand Down