diff --git a/docker-plugin/src/main/java/io/micronaut/gradle/docker/NativeImageDockerfile.java b/docker-plugin/src/main/java/io/micronaut/gradle/docker/NativeImageDockerfile.java index 541bd4bf..99557dec 100644 --- a/docker-plugin/src/main/java/io/micronaut/gradle/docker/NativeImageDockerfile.java +++ b/docker-plugin/src/main/java/io/micronaut/gradle/docker/NativeImageDockerfile.java @@ -57,6 +57,8 @@ */ public abstract class NativeImageDockerfile extends Dockerfile implements DockerBuildOptions { + public static final String AMAZON_LINUX_BASE_IMAGE = "public.ecr.aws/amazonlinux/amazonlinux:" + DefaultVersions.AMAZONLINUX; + private static final List SUPPORTED_JAVA_VERSIONS = List.of( // keep those in descending order 21, @@ -65,6 +67,11 @@ public abstract class NativeImageDockerfile extends Dockerfile implements Docker private static final String ARM_ARCH = "aarch64"; private static final String X86_64_ARCH = "x64"; + private static final String GRAALVM_DOWNLOAD_BASE_URL = "https://download.oracle.com/graalvm"; + private static final String GRAALVM_DISTRIBUTION_PATH = "/%s/%s/graalvm-jdk-%s_linux-%s_bin.tar.gz"; + //Latest version of GraalVM for JDK 17 available under the GraalVM Free Terms and Conditions (GFTC) licence + private static final String GRAALVM_FOR_JDK17 = "17.0.12"; + /** * @return The JDK version to use with native image. Defaults to the toolchain version, or the current Java version. */ @@ -132,6 +139,17 @@ public abstract class NativeImageDockerfile extends Dockerfile implements Docker @Optional public abstract Property getGraalReleasesUrl(); + /** + * Return the full URL of the GraalVM distribution to use. By default, it + * uses the base URL from {@link #getGraalReleasesUrl()} and searches in a + * location known to exist when the plugin was built. + * + * @return the URL of the GraalVM distribution to use + */ + @Input + @Optional + public abstract Property getGraalVMDistributionUrl(); + @Input @Override public abstract Property getTargetWorkingDirectory(); @@ -188,6 +206,17 @@ public NativeImageDockerfile() { getTargetWorkingDirectory().convention(DEFAULT_WORKING_DIR); getExposedPorts().convention(Collections.singletonList(8080)); getGraalImage().convention(getJdkVersion().map(NativeImageDockerfile::toGraalVMBaseImageName)); + getGraalReleasesUrl().convention(GRAALVM_DOWNLOAD_BASE_URL); + var distributionPath = getJdkVersion().zip(getGraalArch(), (jdk, arch) -> { + if ("17".equals(jdk)) { + getLogger().warn("You are using the latest release of GraalVM available under the GraalVM Free Terms and Conditions (GFTC) licence (" + GRAALVM_FOR_JDK17 + "). Consider upgrading to Java 21."); + return GRAALVM_DISTRIBUTION_PATH.formatted(jdk, "archive", GRAALVM_FOR_JDK17, arch); + } + return GRAALVM_DISTRIBUTION_PATH.formatted(jdk, "latest", jdk, arch); + }); + getGraalVMDistributionUrl().convention( + getGraalReleasesUrl().zip(distributionPath, (base, path) -> base + path) + ); getNativeImageOptions().convention(project .getTasks() .named(NativeImagePlugin.NATIVE_COMPILE_TASK_NAME, BuildNativeImageTask.class) @@ -431,13 +460,13 @@ private void setupInstructions(List additionalInstructions) { if (buildStrategy == DockerBuildStrategy.LAMBDA) { from(new From(imageResolver.resolve()).withStage("graalvm")); environmentVariable("LANG", "en_US.UTF-8"); - runCommand("yum install -y gcc gcc-c++ glibc-devel glibc-langpack-en curl-minimal bash zlib zlib-devel zlib-static zip tar gzip"); + runCommand("dnf update -y && dnf install -y gcc glibc-devel zlib-devel libstdc++-static tar && dnf clean all && rm -rf /var/cache/dnf"); String jdkVersion = getJdkVersion().get(); String graalArch = getGraalArch().get(); // https://download.oracle.com/graalvm/17/latest/graalvm-jdk-17_linux-aarch64_bin.tar.gz String fileName = "graalvm-jdk-" + jdkVersion + "_linux-" + graalArch + "_bin.tar.gz"; - String releasesUrl = getGraalReleasesUrl().getOrElse("https://download.oracle.com/graalvm"); - runCommand("curl -4 -L " + releasesUrl + "/" + jdkVersion + "/latest/" + fileName + " -o /tmp/" + fileName); + String graalvmDistributionUrl = getGraalVMDistributionUrl().get(); + runCommand("curl -4 -L " + graalvmDistributionUrl + " -o /tmp/" + fileName); runCommand("tar -zxf /tmp/" + fileName + " -C /tmp && ls -d /tmp/graalvm-jdk-"+ jdkVersion + "* | grep -v \"tar.gz\" | xargs -I_ mv _ /usr/lib/graalvm"); runCommand("rm -rf /tmp/*"); if (toMajorVersion(jdkVersion) < 21) { @@ -448,6 +477,7 @@ private void setupInstructions(List additionalInstructions) { defaultCommand("/usr/lib/graalvm/bin/native-image"); environmentVariable("PATH", "/usr/lib/graalvm/bin:${PATH}"); from(new From("graalvm").withStage("builder")); + runCommand("dnf update -y && dnf install -y zip && dnf clean all"); } else { from(new From(getGraalImage().get()).withStage("graalvm")); } @@ -504,7 +534,7 @@ private void setupInstructions(List additionalInstructions) { case LAMBDA: from(baseImageProvider); workingDir("/function"); - runCommand("yum install -y zip"); + runCommand("dnf install -y zip"); copyFile(new CopyFile(workDir + "/application", "/function/func").withStage("builder")); String funcCmd = String.join(" ", getArgs().map(strings -> { List newList = new ArrayList<>(strings.size() + 1); @@ -744,7 +774,7 @@ private String resolve() { String baseImage = getBaseImage().getOrNull(); if (strategy == DockerBuildStrategy.LAMBDA && baseImage == null) { - baseImage = "amazonlinux:2023"; + baseImage = AMAZON_LINUX_BASE_IMAGE; } else if (baseImage == null) { baseImage = "cgr.dev/chainguard/wolfi-base:latest"; } diff --git a/functional-tests/src/test/groovy/io/micronaut/gradle/docker/DockerNativeFunctionalTest.groovy b/functional-tests/src/test/groovy/io/micronaut/gradle/docker/DockerNativeFunctionalTest.groovy index d2138b4f..df38a0c1 100644 --- a/functional-tests/src/test/groovy/io/micronaut/gradle/docker/DockerNativeFunctionalTest.groovy +++ b/functional-tests/src/test/groovy/io/micronaut/gradle/docker/DockerNativeFunctionalTest.groovy @@ -17,7 +17,7 @@ class DockerNativeFunctionalTest extends AbstractEagerConfiguringFunctionalTest @Lazy String defaultDockerFrom = "FROM $defaultBaseImage" - def "test build docker native image for runtime #runtime"() { + def "test build docker native image for runtime #runtime (JDK #jdk)"() { given: settingsFile << "rootProject.name = 'hello-world'" println settingsFile.text @@ -45,6 +45,7 @@ class DockerNativeFunctionalTest extends AbstractEagerConfiguringFunctionalTest dockerfileNative { args('-Xmx64m') instruction \"\"\"HEALTHCHECK CMD curl -s localhost:8090/health | grep '"status":"UP"'\"\"\" + jdkVersion = "$jdk" } graalvmNative.binaries.all { @@ -101,10 +102,11 @@ micronaut: task.outcome == TaskOutcome.SUCCESS where: - runtime | nativeImage - "netty" | "FROM ghcr.io/graalvm/native-image-community:17-ol${DefaultVersions.ORACLELINUX}" - "lambda_provided" | 'FROM amazonlinux:2023 AS graalvm' - "jetty" | "FROM ghcr.io/graalvm/native-image-community:17-ol${DefaultVersions.ORACLELINUX}" + runtime | jdk | nativeImage + "netty" | 17 | "FROM ghcr.io/graalvm/native-image-community:17-ol${DefaultVersions.ORACLELINUX}" + "lambda_provided" | 17 | "FROM public.ecr.aws/amazonlinux/amazonlinux:${DefaultVersions.AMAZONLINUX} AS graalvm" + "lambda_provided" | 21 | "FROM public.ecr.aws/amazonlinux/amazonlinux:${DefaultVersions.AMAZONLINUX} AS graalvm" + "jetty" | 17 | "FROM ghcr.io/graalvm/native-image-community:17-ol${DefaultVersions.ORACLELINUX}" } void 'build mostly static native images when using distroless docker image for runtime=#runtime'() { @@ -649,7 +651,7 @@ micronaut: build "dockerfileNative" def dockerFile = normalizeLineEndings(file("build/docker/native-main/DockerfileNative").text) dockerFile = dockerFile.replaceAll("[0-9]\\.[0-9]+\\.[0-9]+", "4.0.0") - .trim() + .trim() then: dockerFile == """ diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b17a8cf1..5f6dc97f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,6 +7,7 @@ shadow = "8.1.1" groovy = "3.0.22" spock = "2.3-groovy-3.0" oraclelinux = "9" +amazonlinux = "2023-minimal" graalvmPlugin = "0.10.3" mockserver = "5.15.0" log4j2 = "2.24.1" diff --git a/minimal-plugin/build.gradle b/minimal-plugin/build.gradle index c509c357..81dd166e 100644 --- a/minimal-plugin/build.gradle +++ b/minimal-plugin/build.gradle @@ -28,6 +28,7 @@ var writeVersions = tasks.register("writeDefaultVersions", WriteVersions) { versions.put('test-resources', libs.versions.micronaut.testresources) versions.put('openapi', libs.versions.micronaut.openapi) versions.put('oraclelinux', libs.versions.oraclelinux) + versions.put('amazonlinux', libs.versions.amazonlinux) packageName = 'io.micronaut.gradle' }