diff --git a/src/java/containers/dist_zip.go b/src/java/containers/dist_zip.go index 779461dbd..6ae2e97f5 100644 --- a/src/java/containers/dist_zip.go +++ b/src/java/containers/dist_zip.go @@ -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)") diff --git a/src/java/containers/dist_zip_test.go b/src/java/containers/dist_zip_test.go index 63a08d429..df7a34d94 100644 --- a/src/java/containers/dist_zip_test.go +++ b/src/java/containers/dist_zip_test.go @@ -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")) + }) }) }) diff --git a/src/java/containers/play.go b/src/java/containers/play.go index b88183ab4..2dc37717a 100644 --- a/src/java/containers/play.go +++ b/src/java/containers/play.go @@ -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)") diff --git a/src/java/containers/play_test.go b/src/java/containers/play_test.go index 9b3d711ae..d4d225434 100644 --- a/src/java/containers/play_test.go +++ b/src/java/containers/play_test.go @@ -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")) + }) }) }) }) diff --git a/src/java/containers/spring_boot_cli.go b/src/java/containers/spring_boot_cli.go index ff0c80211..53da3ce37 100644 --- a/src/java/containers/spring_boot_cli.go +++ b/src/java/containers/spring_boot_cli.go @@ -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) } diff --git a/src/java/frameworks/luna_security_provider.go b/src/java/frameworks/luna_security_provider.go index f376fa3b6..2da54ec01 100644 --- a/src/java/frameworks/luna_security_provider.go +++ b/src/java/frameworks/luna_security_provider.go @@ -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 @@ -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() @@ -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) diff --git a/src/java/frameworks/luna_security_provider_test.go b/src/java/frameworks/luna_security_provider_test.go index 8402e06da..5e78046c6 100644 --- a/src/java/frameworks/luna_security_provider_test.go +++ b/src/java/frameworks/luna_security_provider_test.go @@ -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}")) }) })