From 27c6522ad70ade2a9e71652424d0ba78f329e8d0 Mon Sep 17 00:00:00 2001 From: Manuel Fuchs Date: Tue, 28 May 2024 11:10:49 +0200 Subject: [PATCH] Change default OpenJDK version (#681) --- Cargo.lock | 1 + buildpacks/jvm/CHANGELOG.md | 2 + buildpacks/jvm/Cargo.toml | 1 + buildpacks/jvm/src/constants.rs | 1 + buildpacks/jvm/src/main.rs | 71 ++++++++++++++----- buildpacks/jvm/src/salesforce_functions.rs | 22 ++++++ .../jvm/test-apps/java-default-app/pom.xml | 6 ++ .../salesforce-functions-app/pom.xml | 6 ++ .../salesforce-functions-app/project.toml | 2 + buildpacks/jvm/tests/integration/versions.rs | 67 ++++++++++++++++- .../system.properties | 1 + 11 files changed, 163 insertions(+), 17 deletions(-) create mode 100644 buildpacks/jvm/src/salesforce_functions.rs create mode 100644 buildpacks/jvm/test-apps/java-default-app/pom.xml create mode 100644 buildpacks/jvm/test-apps/salesforce-functions-app/pom.xml create mode 100644 buildpacks/jvm/test-apps/salesforce-functions-app/project.toml create mode 100644 buildpacks/maven/test-apps/simple-http-service-groovy-polyglot/system.properties diff --git a/Cargo.lock b/Cargo.lock index 65e65911..43276882 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,6 +87,7 @@ dependencies = [ "sha2", "tempfile", "thiserror", + "toml", "url", ] diff --git a/buildpacks/jvm/CHANGELOG.md b/buildpacks/jvm/CHANGELOG.md index 5653a53b..40e374d9 100644 --- a/buildpacks/jvm/CHANGELOG.md +++ b/buildpacks/jvm/CHANGELOG.md @@ -10,9 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Checksum validation of downloaded OpenJDK distribution files. ([#680](https://github.com/heroku/buildpacks-jvm/pull/680)) +- A warning will now be shown when the OpenJDK version is not explicitly configured for an application. ([#681](https://github.com/heroku/buildpacks-jvm/pull/681)) ### Changed +- This buildpack now installs the latest long-term support release (currently 21) of OpenJDK if no version is explicitly configured. Previously, OpenJDK 8 was installed as the default. ([#681](https://github.com/heroku/buildpacks-jvm/pull/681)) - Some error messages have changed so they longer suggest to open a Heroku support ticket. Instead, users are now provided with a link to create an issue on GitHub. ([#674](https://github.com/heroku/buildpacks-jvm/pull/674)) ### Removed diff --git a/buildpacks/jvm/Cargo.toml b/buildpacks/jvm/Cargo.toml index bba9c8ef..01b8e1dd 100644 --- a/buildpacks/jvm/Cargo.toml +++ b/buildpacks/jvm/Cargo.toml @@ -20,6 +20,7 @@ inventory = { git = "https://github.com/Malax/inventory", features = ["sha2"] } thiserror = "1" sha2 = "0.10" hex = "0.4" +toml = "0.8" [dev-dependencies] buildpacks-jvm-shared-test.workspace = true diff --git a/buildpacks/jvm/src/constants.rs b/buildpacks/jvm/src/constants.rs index 684733fe..ea6ce060 100644 --- a/buildpacks/jvm/src/constants.rs +++ b/buildpacks/jvm/src/constants.rs @@ -1,3 +1,4 @@ pub(crate) const JAVA_TOOL_OPTIONS_ENV_VAR_DELIMITER: &str = " "; pub(crate) const JAVA_TOOL_OPTIONS_ENV_VAR_NAME: &str = "JAVA_TOOL_OPTIONS"; pub(crate) const JDK_OVERLAY_DIR_NAME: &str = ".jdk_overlay"; +pub(crate) const OPENJDK_LATEST_LTS_VERSION: u32 = 21; diff --git a/buildpacks/jvm/src/main.rs b/buildpacks/jvm/src/main.rs index 1bda932f..ca1f5371 100644 --- a/buildpacks/jvm/src/main.rs +++ b/buildpacks/jvm/src/main.rs @@ -3,18 +3,26 @@ mod errors; mod layers; mod openjdk_artifact; mod openjdk_version; +mod salesforce_functions; mod util; +use crate::constants::OPENJDK_LATEST_LTS_VERSION; use crate::errors::on_error_jvm_buildpack; use crate::layers::openjdk::OpenJdkLayer; use crate::layers::runtime::RuntimeLayer; use crate::openjdk_artifact::{ - OpenJdkArtifactMetadata, OpenJdkArtifactRequirement, OpenJdkArtifactRequirementParseError, + HerokuOpenJdkVersionRequirement, OpenJdkArtifactMetadata, OpenJdkArtifactRequirement, + OpenJdkArtifactRequirementParseError, OpenJdkDistribution, }; +use crate::openjdk_version::OpenJdkVersion; +use crate::salesforce_functions::is_salesforce_function_app; use buildpacks_jvm_shared::system_properties::{read_system_properties, ReadSystemPropertiesError}; +#[cfg(test)] +use buildpacks_jvm_shared_test as _; pub(crate) use constants::{ JAVA_TOOL_OPTIONS_ENV_VAR_DELIMITER, JAVA_TOOL_OPTIONS_ENV_VAR_NAME, JDK_OVERLAY_DIR_NAME, }; +use indoc::formatdoc; use inventory::artifact::{Arch, Os}; use inventory::inventory::{Inventory, ParseInventoryError}; use libcnb::build::{BuildContext, BuildResult, BuildResultBuilder}; @@ -24,16 +32,13 @@ use libcnb::data::layer_name; use libcnb::detect::{DetectContext, DetectResult, DetectResultBuilder}; use libcnb::generic::{GenericMetadata, GenericPlatform}; use libcnb::Buildpack; -use libherokubuildpack::download::DownloadError; -use std::env::consts; -use url as _; // Used by exec.d binary - -use crate::openjdk_version::OpenJdkVersion; -#[cfg(test)] -use buildpacks_jvm_shared_test as _; #[cfg(test)] use libcnb_test as _; +use libherokubuildpack::download::DownloadError; +use libherokubuildpack::log::log_warning; use sha2::Sha256; +use std::env::consts; +use url as _; // Used by exec.d binary struct OpenJdkBuildpack; @@ -86,19 +91,53 @@ impl Buildpack for OpenJdkBuildpack { fn build(&self, context: BuildContext) -> libcnb::Result { let openjdk_artifact_requirement = read_system_properties(&context.app_dir) - .map(|properties| { - properties - .get("java.runtime.version") - .cloned() - .unwrap_or(String::from("8")) - }) .map_err(OpenJdkBuildpackError::ReadSystemPropertiesError) + .map(|properties| properties.get("java.runtime.version").cloned()) .and_then(|string| { string - .parse::() - .map_err(OpenJdkBuildpackError::OpenJdkArtifactRequirementParseError) + .map(|string| { + string + .parse::() + .map_err(OpenJdkBuildpackError::OpenJdkArtifactRequirementParseError) + }) + .transpose() })?; + let openjdk_artifact_requirement = if let Some(openjdk_artifact_requirement) = + openjdk_artifact_requirement + { + openjdk_artifact_requirement + // The default version for Salesforce functions is always OpenJDK 8. Keep this conditional + // around until Salesforce functions is EOL and then remove it. + } else if is_salesforce_function_app(&context.app_dir) { + OpenJdkArtifactRequirement { + version: HerokuOpenJdkVersionRequirement::Major(8), + distribution: OpenJdkDistribution::default(), + } + } else { + log_warning( + "No OpenJDK version specified", + formatdoc! {" + Your application does not explicitly specify an OpenJDK version. The latest + long-term support (LTS) version will be installed. This currently is OpenJDK {OPENJDK_LATEST_LTS_VERSION}. + + This default version will change when a new LTS version is released. Your + application might fail to build with the new version. We recommend explicitly + setting the required OpenJDK version for your application. + + To set the OpenJDK version, add or edit the system.properties file in the root + directory of your application to contain: + + java.runtime.version = {OPENJDK_LATEST_LTS_VERSION} + "}, + ); + + OpenJdkArtifactRequirement { + version: HerokuOpenJdkVersionRequirement::Major(OPENJDK_LATEST_LTS_VERSION), + distribution: OpenJdkDistribution::default(), + } + }; + let openjdk_inventory = include_str!("../openjdk_inventory.toml") .parse::>() .map_err(OpenJdkBuildpackError::ParseInventoryError)?; diff --git a/buildpacks/jvm/src/salesforce_functions.rs b/buildpacks/jvm/src/salesforce_functions.rs new file mode 100644 index 00000000..11ddfee5 --- /dev/null +++ b/buildpacks/jvm/src/salesforce_functions.rs @@ -0,0 +1,22 @@ +use libcnb::read_toml_file; +use std::path::Path; + +pub(crate) fn is_salesforce_function_app(app_dir: &Path) -> bool { + read_toml_file::(app_dir.join("project.toml")) + .is_ok_and(|project_toml_table| matches!(value_at_path(&project_toml_table, &["com", "salesforce", "type"]), Some(toml::Value::String(salesforce_type)) if salesforce_type == "function")) +} + +fn value_at_path<'a>(table: &'a toml::value::Value, path: &[&str]) -> Option<&'a toml::Value> { + let mut value = table; + + for path_segment in path { + if let toml::Value::Table(table) = value { + match table.get(*path_segment) { + Some(next_value) => value = next_value, + None => return None, + } + } + } + + Some(value) +} diff --git a/buildpacks/jvm/test-apps/java-default-app/pom.xml b/buildpacks/jvm/test-apps/java-default-app/pom.xml new file mode 100644 index 00000000..c61465a0 --- /dev/null +++ b/buildpacks/jvm/test-apps/java-default-app/pom.xml @@ -0,0 +1,6 @@ + + 4.0.0 + bogus + bogus + 1.0.0 + diff --git a/buildpacks/jvm/test-apps/salesforce-functions-app/pom.xml b/buildpacks/jvm/test-apps/salesforce-functions-app/pom.xml new file mode 100644 index 00000000..c61465a0 --- /dev/null +++ b/buildpacks/jvm/test-apps/salesforce-functions-app/pom.xml @@ -0,0 +1,6 @@ + + 4.0.0 + bogus + bogus + 1.0.0 + diff --git a/buildpacks/jvm/test-apps/salesforce-functions-app/project.toml b/buildpacks/jvm/test-apps/salesforce-functions-app/project.toml new file mode 100644 index 00000000..ef6d5f84 --- /dev/null +++ b/buildpacks/jvm/test-apps/salesforce-functions-app/project.toml @@ -0,0 +1,2 @@ +[com.salesforce] +type = "function" diff --git a/buildpacks/jvm/tests/integration/versions.rs b/buildpacks/jvm/tests/integration/versions.rs index e9f27faf..4ae6f8d2 100644 --- a/buildpacks/jvm/tests/integration/versions.rs +++ b/buildpacks/jvm/tests/integration/versions.rs @@ -1,5 +1,70 @@ use crate::default_build_config; -use libcnb_test::{assert_contains, TestRunner}; +use indoc::formatdoc; +use libcnb::data::buildpack_id; +use libcnb_test::{assert_contains, assert_not_contains, BuildpackReference, TestRunner}; + +#[test] +#[ignore = "integration test"] +fn openjdk_default() { + TestRunner::default().build( + default_build_config("test-apps/java-default-app").buildpacks([ + BuildpackReference::CurrentCrate, + // We need another buildpack to require 'jvm' in the build plan to be able to use a + // default OpenJDK version. It could be any other buildpack, heroku/maven is just + // convenient to use here. + BuildpackReference::WorkspaceBuildpack(buildpack_id!("heroku/maven")), + ]), + |context| { + assert_contains!( + context.pack_stderr, + &formatdoc! {" + [Warning: No OpenJDK version specified] + Your application does not explicitly specify an OpenJDK version. The latest + long-term support (LTS) version will be installed. This currently is OpenJDK 21. + + This default version will change when a new LTS version is released. Your + application might fail to build with the new version. We recommend explicitly + setting the required OpenJDK version for your application. + + To set the OpenJDK version, add or edit the system.properties file in the root + directory of your application to contain: + + java.runtime.version = 21 + "} + ); + + assert_contains!( + context.run_shell_command("java -version").stderr, + "openjdk version \"21.0.3\"" + ); + }, + ); +} + +#[test] +#[ignore = "integration test"] +fn openjdk_functions_default() { + TestRunner::default().build( + default_build_config("test-apps/salesforce-functions-app").buildpacks([ + BuildpackReference::CurrentCrate, + // We need another buildpack to require 'jvm' in the build plan to be able to use a + // default OpenJDK version. It could be any other buildpack, heroku/maven is just + // convenient to use here. + BuildpackReference::WorkspaceBuildpack(buildpack_id!("heroku/maven")), + ]), + |context| { + assert_not_contains!( + context.pack_stderr, + "[Warning: No OpenJDK version specified]" + ); + + assert_contains!( + context.run_shell_command("java -version").stderr, + "openjdk version \"1.8.0_412\"" + ); + }, + ); +} #[test] #[ignore = "integration test"] diff --git a/buildpacks/maven/test-apps/simple-http-service-groovy-polyglot/system.properties b/buildpacks/maven/test-apps/simple-http-service-groovy-polyglot/system.properties new file mode 100644 index 00000000..1a3093b5 --- /dev/null +++ b/buildpacks/maven/test-apps/simple-http-service-groovy-polyglot/system.properties @@ -0,0 +1 @@ +java.runtime.version = 8