Skip to content
Draft
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
24 changes: 14 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,21 @@ The buildpack will do the following:
[c]: https://github.com/buildpacks/spec/blob/main/extensions/bindings.md

## Configuration
| Environment Variable | Description |
|---------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `$BP_SPRING_CLOUD_BINDINGS_DISABLED` | Whether to contribute Spring Cloud Bindings support to the image at build time. Defaults to false. |
| `$BPL_SPRING_CLOUD_BINDINGS_DISABLED` | Whether to auto-configure Spring Boot environment properties from bindings at runtime. This requires Spring Cloud Bindings to have been installed at build time or it will do nothing. Defaults to false. |
| Environment Variable | Description |
|---------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `$BP_SPRING_CLOUD_BINDINGS_DISABLED` | Whether to contribute Spring Cloud Bindings support to the image at build time. Defaults to false. |
| `$BPL_SPRING_CLOUD_BINDINGS_DISABLED` | Whether to auto-configure Spring Boot environment properties from bindings at runtime. This requires Spring Cloud Bindings to have been installed at build time or it will do nothing. Defaults to false. |
| `$BPL_SPRING_CLOUD_BINDINGS_ENABLED` | Deprecated in favour of `$BPL_SPRING_CLOUD_BINDINGS_DISABLED`. Whether to auto-configure Spring Boot environment properties from bindings at runtime. This requires Spring Cloud Bindings to have been installed at build time or it will do nothing. Defaults to true. |
| `$BP_SPRING_CLOUD_BINDINGS_VERSION` | Explicit version of Spring Cloud Bindings library to install. |
| `$BP_SPRING_AOT_ENABLED` | Whether to contribute `$BPL_SPRING_AOT_ENABLED` at runtime. Beware that the Spring Boot app needs to have been AOT instrumented (presence of `META-INF/native-image`) too. Defaults to false. |
| `$BPL_SPRING_AOT_ENABLED` | Whether to contribute `-Dspring.aot.enabled=true` to `JAVA_TOOL_OPTIONS` at runtime. Defaults to yes if the above conditions were met; false otherwise |
| `$BP_JVM_CDS_ENABLED` | Whether to perform the CDS training run (that will generate the caching file `application.jsa`). Defaults to false. |
| `$CDS_TRAINING_JAVA_TOOL_OPTIONS` | Allow the user to override the default `JAVA_TOOL_OPTIONS`, only for the CDS training run. Useful to configure your app not to reach external services during training run for example. |
| `$BPL_JVM_CDS_ENABLED` | Whether to load the CDS caching file (`-XX:SharedArchiveFile=application.jsa`) that was generated during the CDS training run. Defaults to the value of `BP_JVM_CDS_ENABLED` |
| `$BP_SPRING_CLOUD_BINDINGS_VERSION` | Explicit version of Spring Cloud Bindings library to install. |
| `$BP_SPRING_AOT_ENABLED` | Whether to contribute `$BPL_SPRING_AOT_ENABLED` at runtime. Beware that the Spring Boot app needs to have been AOT instrumented (presence of `META-INF/native-image`) too. Defaults to false. |
| `$BPL_SPRING_AOT_ENABLED` | Whether to contribute `-Dspring.aot.enabled=true` to `JAVA_TOOL_OPTIONS` at runtime. Defaults to yes if the above conditions were met; false otherwise |
| `$BP_UNPACK_LAYOUT_ONLY` | Whether to only unpack the Spring Boot app, and not apply a CDS / AOT Cache training run |
| `$BP_JVM_CDS_ENABLED` | Deprecated, use `BP_JVM_AOTCACHE_ENABLED` - Whether to perform the CDS training run (that will generate the caching file `application.jsa`). Defaults to false. |
| `$BPL_JVM_CDS_ENABLED` | Deprecated, use `BPL_JVM_AOTCACHE_ENABLED` - Whether to load the CDS caching file (`-XX:SharedArchiveFile=application.jsa`) that was generated during the CDS training run. Defaults to the value of `BP_JVM_CDS_ENABLED` |
| `$BP_JVM_AOTCACHE_ENABLED` | Whether to perform the AOT Cache training run (that will generate the caching file `application.jsa`). Defaults to false. |
| `$BPL_JVM_AOTCACHE_ENABLED` | Whether to load the CDS caching file (`-XX:SharedArchiveFile=application.jsa`) that was generated during the CDS training run. Defaults to the value of `BP_JVM_CDS_ENABLED` |
| `$CDS_TRAINING_JAVA_TOOL_OPTIONS` | Deprecated, use `TRAINING_RUN_JAVA_TOOL_OPTIONS` - Allow the user to override the default `JAVA_TOOL_OPTIONS`, only for the CDS training run. Useful to configure your app not to reach external services during training run for example. |
| `$TRAINING_RUN_JAVA_TOOL_OPTIONS` | Allow the user to override the default `JAVA_TOOL_OPTIONS`, only for training run. Useful to configure your app not to reach external services during training run for example. |
## Bindings
The buildpack optionally accepts the following bindings:

Expand Down
37 changes: 26 additions & 11 deletions boot/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ const (
NestedFileSystemProvider = "org.springframework.boot.loader.nio.file.NestedFileSystemProvider"
)

type SpringPerformanceType int

const (
Without SpringPerformanceType = iota
ExtractLayout
CdsAotCache
)

type Build struct {
Logger bard.Logger
Executor effect.Executor
Expand All @@ -74,7 +82,14 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
return libcnb.BuildResult{}, fmt.Errorf("unable to read manifest in %s\n%w", context.Application.Path, err)
}

trainingRun := sherpa.ResolveBool("BP_JVM_CDS_ENABLED")
performanceType := Without
if sherpa.ResolveBool("BP_UNPACK_LAYOUT_ONLY") {
performanceType = ExtractLayout
}

if sherpa.ResolveBool("BP_JVM_CDS_ENABLED") || sherpa.ResolveBool("BP_JVM_AOTCACHE_ENABLED") {
performanceType = CdsAotCache
}

version, versionFound := manifest.Get("Spring-Boot-Version")
if !versionFound {
Expand All @@ -90,12 +105,12 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
}
mainClass, _ := manifest.Get("Main-Class")

if trainingRun {
if performanceType == CdsAotCache || performanceType == ExtractLayout {
if bootCDSExtractionSupported(version) {
reZipExplodedJar = true
} else {
b.Logger.Bodyf("You enabled CDS optimization with BP_JVM_CDS_ENABLED=true but your Spring Boot app version is: %s, you need to upgrade to Spring Boot >= 3.3 first!\nCancelling CDS optimization", version)
trainingRun = false
b.Logger.Bodyf("You enabled CDS_AOTCACHE optimization or extract mode only but your Spring Boot app version is: %s, you need to upgrade to Spring Boot >= 3.3 first!\nCancelling performance optimization", version)
performanceType = Without
}

}
Expand Down Expand Up @@ -232,13 +247,13 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
b.Logger.Bodyf("unable to find AOT processed dir %s, however BP_SPRING_AOT_ENABLED has been set to true. Ensure that your app is AOT processed", dir)
}

cdsTrainingJavaToolOptions := sherpa.GetEnvWithDefault("CDS_TRAINING_JAVA_TOOL_OPTIONS", "")
if trainingRun || aotEnabled {
cdsTrainingJavaToolOptions := sherpa.GetEnvWithDefault("TRAINING_RUN_JAVA_TOOL_OPTIONS", sherpa.GetEnvWithDefault("CDS_TRAINING_JAVA_TOOL_OPTIONS", ""))
if aotEnabled || performanceType == CdsAotCache || performanceType == ExtractLayout {

helpers = append(helpers, "performance")

cdsTrainingJavaToolOptionsProvided := cdsTrainingJavaToolOptions != ""
if cdsTrainingJavaToolOptionsProvided && trainingRun && aotEnabled {
if cdsTrainingJavaToolOptionsProvided && performanceType == CdsAotCache && aotEnabled {
b.Logger.Infof(color.RedString("ERROR: CDS_TRAINING_JAVA_TOOL_OPTIONS is not compatible with BP_SPRING_AOT_ENABLED - as the AOT classes used during training run won't be compatible with a different set of JAVA_TOOL_OPTIONS at runtime \n" +
"The Spring team explains this issue in detail here: https://github.com/spring-projects/spring-boot/issues/41348 \n" +
"If you need to provide CDS_TRAINING_JAVA_TOOL_OPTIONS (to disable a connection to a remote service for example), you need to disable BP_SPRING_AOT_ENABLED "))
Expand All @@ -249,7 +264,7 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
cdsTrainingJavaToolOptions = sherpa.GetEnvWithDefault("JAVA_TOOL_OPTIONS", "")
}

if trainingRun {
if performanceType == CdsAotCache || performanceType == ExtractLayout {
mainClass, _ = manifest.Get("Start-Class")
classpathString = "runner.jar"
if len(additionalLibs) > 0 {
Expand All @@ -261,7 +276,7 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
}
}

cdsLayer := NewSpringPerformance(dc, context.Application.Path, manifest, aotEnabled, trainingRun, classpathString, reZipExplodedJar, cdsTrainingJavaToolOptions)
cdsLayer := NewSpringPerformance(dc, context.Application.Path, manifest, aotEnabled, performanceType, classpathString, reZipExplodedJar, cdsTrainingJavaToolOptions)
cdsLayer.Logger = b.Logger
result.Layers = append(result.Layers, cdsLayer)

Expand Down Expand Up @@ -300,7 +315,7 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
at.Logger = b.Logger
result.Layers = append(result.Layers, at)

if !trainingRun {
if performanceType == Without {
// Slices
if index, ok := manifest.Get("Spring-Boot-Layers-Index"); ok {
b.Logger.Header("Creating slices from layers index")
Expand All @@ -314,7 +329,7 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
result = b.contributeHelpers(context, result, helpers)
}

if bootJarFound || trainingRun {
if bootJarFound || performanceType == CdsAotCache || performanceType == ExtractLayout {
if mainClass != "" {
result.Processes = append(result.Processes, b.setProcessTypes(mainClass, classpathString)...)
} else {
Expand Down
12 changes: 6 additions & 6 deletions boot/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ Spring-Boot-Lib: BOOT-INF/lib

ctx.Buildpack.API = "0.6"

it("contributes CDS layer & helper for Boot 3.3+ apps", func() {
it("contributes CdsAotCache layer & helper for Boot 3.3+ apps", func() {
Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "META-INF", "MANIFEST.MF"), []byte(`
Spring-Boot-Version: 3.3.1
Start-Class: test-class
Expand All @@ -694,7 +694,7 @@ Spring-Boot-Lib: BOOT-INF/lib
Expect(result.Layers[2].(libpak.HelperLayerContributor).Names).To(Equal([]string{"performance"}))
})

it("contributes CDS layer & helper for Boot 3.3+ apps even when they're jar'ed", func() {
it("contributes CdsAotCache layer & helper for Boot 3.3+ apps even when they're jar'ed", func() {

Copy("cds", "spring-app-3.3-no-dependencies.jar", "")

Expand All @@ -706,7 +706,7 @@ Spring-Boot-Lib: BOOT-INF/lib
Expect(result.Layers[2].(libpak.HelperLayerContributor).Names).To(Equal([]string{"performance"}))
})

it("does not contribute CDS layer & helper for Boot < 3.3 apps", func() {
it("does not contribute CdsAotCache layer & helper for Boot < 3.3 apps", func() {
Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "META-INF", "MANIFEST.MF"), []byte(`
Spring-Boot-Version: 3.2.1
Start-Class: test-class
Expand All @@ -721,7 +721,7 @@ Spring-Boot-Lib: BOOT-INF/lib
Expect(result.Layers[0].Name()).To(Equal("web-application-type"))
})

it("contributes CDS layer & helper for Boot 3.3+ apps with BP_SPRING_AOT_ENABLED and CDS_TRAINING_JAVA_TOOL_OPTIONS not set", func() {
it("contributes CdsAotCache layer & helper for Boot 3.3+ apps with BP_SPRING_AOT_ENABLED and CDS_TRAINING_JAVA_TOOL_OPTIONS not set", func() {
t.Setenv("BP_SPRING_AOT_ENABLED", "true")
Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "META-INF", "MANIFEST.MF"), []byte(`
Spring-Boot-Version: 3.3.1
Expand Down Expand Up @@ -756,7 +756,7 @@ Spring-Boot-Lib: BOOT-INF/lib
Expect(err.Error()).To(Equal("build failed because of invalid user configuration"))
})

it("contributes CDS layer & helper for Boot 3.3+ apps with CDS_TRAINING_JAVA_TOOL_OPTIONS but BP_SPRING_AOT_ENABLED is disabled", func() {
it("contributes CdsAotCache layer & helper for Boot 3.3+ apps with CDS_TRAINING_JAVA_TOOL_OPTIONS but BP_SPRING_AOT_ENABLED is disabled", func() {
t.Setenv("BP_SPRING_AOT_ENABLED", "false")
t.Setenv("CDS_TRAINING_JAVA_TOOL_OPTIONS", "user-cds-opt")

Expand All @@ -782,7 +782,7 @@ Spring-Boot-Lib: BOOT-INF/lib
Expect(result.Layers[2].(libpak.HelperLayerContributor).Names).To(Equal([]string{"performance"}))
})

it("contributes CDS layer & helper for Boot 3.3+ apps with BP_SPRING_AOT_ENABLED and JAVA_TOOL_OPTIONS set", func() {
it("contributes CdsAotCache layer & helper for Boot 3.3+ apps with BP_SPRING_AOT_ENABLED and JAVA_TOOL_OPTIONS set", func() {
t.Setenv("BP_SPRING_AOT_ENABLED", "true")
t.Setenv("CDS_TRAINING_JAVA_TOOL_OPTIONS", "default-opt")

Expand Down
4 changes: 2 additions & 2 deletions boot/detect.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (d Detect) Detect(context libcnb.DetectContext) (libcnb.DetectResult, error
},
Requires: []libcnb.BuildPlanRequire{
{Name: PlanEntryJVMApplication},
{Name: PlanEntrySpringBoot} },
{Name: PlanEntrySpringBoot}},
},
},
}
Expand All @@ -75,7 +75,7 @@ func (d Detect) Detect(context libcnb.DetectContext) (libcnb.DetectResult, error
Requires: []libcnb.BuildPlanRequire{
{Name: PlanEntryJVMApplication},
{Name: PlanEntrySpringBoot},
// Require a JRE at build time to perform CDS training run
// Require a JRE at build time to perform CdsAotCache training run
{Name: PlanEntryJRE, Metadata: map[string]interface{}{"build": true}},
},
},
Expand Down
Loading
Loading