diff --git a/alpine/Dockerfile b/alpine/Dockerfile index fcd8b6c9..e27a22c4 100644 --- a/alpine/Dockerfile +++ b/alpine/Dockerfile @@ -22,7 +22,26 @@ ARG JAVA_VERSION=17.0.11_9 ARG ALPINE_TAG=3.19.1 -FROM eclipse-temurin:"${JAVA_VERSION}"-jdk-alpine AS jre-build +FROM alpine:"${ALPINE_TAG}" AS jre-build + +SHELL ["/bin/ash", "-eo", "pipefail", "-c"] + +# This Build ARG is populated by Docker +# Ref. https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope +ARG TARGETPLATFORM + +COPY jdk-download-url.sh /usr/bin/local/jdk-download-url.sh +COPY jdk-download.sh /usr/bin/local/jdk-download.sh + +ARG JAVA_VERSION=17.0.11_9 +# hadolint ignore=DL3018 +RUN apk add --no-cache \ + ca-certificates \ + jq \ + curl \ + && /usr/bin/local/jdk-download.sh alpine + +ENV PATH="/opt/jdk-${JAVA_VERSION}/bin:${PATH}" RUN case "$(jlink --version 2>&1)" in \ # jlink version 11 has less features than JDK17+ diff --git a/build-windows.yaml b/build-windows.yaml index 82ef0740..63758465 100644 --- a/build-windows.yaml +++ b/build-windows.yaml @@ -6,7 +6,7 @@ services: dockerfile: ./windows/${WINDOWS_FLAVOR}/Dockerfile args: JAVA_HOME: "C:/openjdk-11" - JAVA_VERSION: 11.0.23_9 + JAVA_VERSION: 11.0.23+9 WINDOWS_VERSION_TAG: ${WINDOWS_VERSION_TAG} TOOLS_WINDOWS_VERSION: ${TOOLS_WINDOWS_VERSION} tags: @@ -18,7 +18,7 @@ services: dockerfile: ./windows/${WINDOWS_FLAVOR}/Dockerfile args: JAVA_HOME: "C:/openjdk-17" - JAVA_VERSION: 17.0.11_9 + JAVA_VERSION: 17.0.11+9 WINDOWS_VERSION_TAG: ${WINDOWS_VERSION_TAG} TOOLS_WINDOWS_VERSION: ${TOOLS_WINDOWS_VERSION} tags: @@ -32,7 +32,7 @@ services: dockerfile: ./windows/${WINDOWS_FLAVOR}/Dockerfile args: JAVA_HOME: "C:/openjdk-21" - JAVA_VERSION: 21.0.3_9 + JAVA_VERSION: 21.0.3+9 WINDOWS_VERSION_TAG: ${WINDOWS_VERSION_TAG} TOOLS_WINDOWS_VERSION: ${TOOLS_WINDOWS_VERSION} tags: diff --git a/build.ps1 b/build.ps1 index 451486e6..474327ae 100644 --- a/build.ps1 +++ b/build.ps1 @@ -81,15 +81,19 @@ Test-CommandExists 'yq' function Test-Image { param ( - $ImageName + $ImageNameAndJavaVersion ) + $items = $ImageNameAndJavaVersion.Split("|") + $imageName = $items[0] + $javaVersion = $items[1] $imageNameItems = $imageName.Split(":") $imageTag = $imageNameItems[1] Write-Host "= TEST: Testing ${ImageName} image" $env:IMAGE_NAME = $ImageName + $env:JAVA_VERSION = "$javaVersion" $targetPath = '.\target\{0}' -f $imageTag if(Test-Path $targetPath) { @@ -107,6 +111,7 @@ function Test-Image { Write-Host "There were $($TestResults.PassedCount) passed tests in ${ImageName}" } Remove-Item env:\IMAGE_NAME + Remove-Item env:\JAVA_VERSION return $failed } @@ -161,8 +166,9 @@ if($target -eq 'test') { Write-Host '= TEST: Testing all images...' # Only fail the run afterwards in case of any test failures $testFailed = $false - Invoke-Expression "$baseDockerCmd config" | yq '.services[].image' | ForEach-Object { - $testFailed = $testFailed -or (Test-Image $_) + $jdks = Invoke-Expression "$baseDockerCmd config" | yq --unwrapScalar --output-format json '.services' | ConvertFrom-Json + foreach ($jdk in $jdks.PSObject.Properties) { + $testFailed = $testFailed -or (Test-Image ('{0}|{1}' -f $jdk.Value.image, $jdk.Value.build.args.JAVA_VERSION)) } # Fail if any test failures diff --git a/debian/Dockerfile b/debian/Dockerfile index a208c8f4..6c28bf16 100644 --- a/debian/Dockerfile +++ b/debian/Dockerfile @@ -1,12 +1,46 @@ -ARG JAVA_VERSION=17.0.11_9 +# The MIT License +# +# Copyright (c) 2015-2024, CloudBees, Inc. and other Jenkins contributors +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. ARG DEBIAN_RELEASE=bookworm-20240513 -FROM eclipse-temurin:"${JAVA_VERSION}"-jdk-jammy AS jre-build +FROM debian:"${DEBIAN_RELEASE}"-slim as jre-build + +SHELL ["/bin/bash", "-e", "-u", "-o", "pipefail", "-c"] # This Build ARG is populated by Docker # Ref. https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope ARG TARGETPLATFORM -SHELL ["/bin/bash","-e", "-u", "-o", "pipefail", "-c"] +COPY jdk-download-url.sh /usr/bin/local/jdk-download-url.sh +COPY jdk-download.sh /usr/bin/local/jdk-download.sh + +ARG JAVA_VERSION=17.0.11_9 +# hadolint ignore=DL3008 +RUN set -x; apt-get update \ + && apt-get install --no-install-recommends -y \ + ca-certificates \ + jq \ + curl \ + && /usr/bin/local/jdk-download.sh + +ENV PATH="/opt/jdk-${JAVA_VERSION}/bin:${PATH}" # Generate smaller java runtime without unneeded files # for now we include the full module path to maintain compatibility diff --git a/jdk-download-url.sh b/jdk-download-url.sh new file mode 100755 index 00000000..cd901bd8 --- /dev/null +++ b/jdk-download-url.sh @@ -0,0 +1,106 @@ +#!/bin/sh + +# Check if at least one argument was passed to the script +# If one argument was passed and JAVA_VERSION is set, assign the argument to OS +# If two arguments were passed, assign them to JAVA_VERSION and OS respectively +# If three arguments were passed, assign them to JAVA_VERSION, OS and ARCHS respectively +# If not, check if JAVA_VERSION and OS are already set. If they're not set, exit the script with an error message +if [ $# -eq 1 ] && [ -n "$JAVA_VERSION" ]; then + OS=$1 +elif [ $# -eq 2 ]; then + JAVA_VERSION=$1 + OS=$2 +elif [ $# -eq 3 ]; then + JAVA_VERSION=$1 + OS=$2 + ARCHS=$3 +elif [ -z "$JAVA_VERSION" ] && [ -z "$OS" ]; then + echo "Error: No Java version and OS specified. Please set the JAVA_VERSION and OS environment variables or pass them as arguments." >&2 + exit 1 +elif [ -z "$JAVA_VERSION" ]; then + echo "Error: No Java version specified. Please set the JAVA_VERSION environment variable or pass it as an argument." >&2 + exit 1 +elif [ -z "$OS" ]; then + OS=$1 + if [ -z "$OS" ]; then + echo "Error: No OS specified. Please set the OS environment variable or pass it as an argument." >&2 + exit 1 + fi +fi + +# Check if ARCHS is set. If it's not set, assign the current architecture to it +if [ -z "$ARCHS" ]; then + ARCHS=$(uname -m | sed -e 's/x86_64/x64/' -e 's/armv7l/arm/') +else + # Convert ARCHS to an array + OLD_IFS=$IFS + IFS=',' + set -- "$ARCHS" + ARCHS="" + for arch in "$@"; do + ARCHS="$ARCHS $arch" + done + IFS=$OLD_IFS +fi + +# Check if jq and curl are installed +# If they are not installed, exit the script with an error message +if ! command -v jq >/dev/null 2>&1 || ! command -v curl >/dev/null 2>&1; then + echo "jq and curl are required but not installed. Exiting with status 1." >&2 + exit 1 +fi + +# Replace underscores with plus signs in JAVA_VERSION +ARCHIVE_DIRECTORY=$(echo "$JAVA_VERSION" | tr '_' '+') + +# URL encode ARCHIVE_DIRECTORY +ENCODED_ARCHIVE_DIRECTORY=$(echo "$ARCHIVE_DIRECTORY" | xargs -I {} printf %s {} | jq "@uri" -jRr) + +# Determine the OS type for the URL +OS_TYPE="linux" +if [ "$OS" = "alpine" ]; then + OS_TYPE="alpine-linux" +fi +if [ "$OS" = "windows" ]; then + OS_TYPE="windows" +fi + +# Initialize a variable to store the URL for the first architecture +FIRST_ARCH_URL="" + +# Loop over the array of architectures +for ARCH in $ARCHS; do + # Fetch the download URL from the Adoptium API + URL="https://api.adoptium.net/v3/binary/version/jdk-${ENCODED_ARCHIVE_DIRECTORY}/${OS_TYPE}/${ARCH}/jdk/hotspot/normal/eclipse?project=jdk" + + if ! RESPONSE=$(curl -fsI "$URL"); then + echo "Error: Failed to fetch the URL for architecture ${ARCH}. Exiting with status 1." >&2 + echo "Response: $RESPONSE" >&2 + exit 1 + fi + + # Extract the redirect URL from the HTTP response + REDIRECTED_URL=$(echo "$RESPONSE" | grep Location | awk '{print $2}' | tr -d '\r') + + # If no redirect URL was found, exit the script with an error message + if [ -z "$REDIRECTED_URL" ]; then + echo "Error: No redirect URL found for architecture ${ARCH}. Exiting with status 1." >&2 + echo "Response: $RESPONSE" >&2 + exit 1 + fi + + # Use curl to check if the URL is reachable + # If the URL is not reachable, print an error message and exit the script with status 1 + if ! curl -v -fs "$REDIRECTED_URL" >/dev/null 2>&1; then + echo "${REDIRECTED_URL}" is not reachable for architecture "${ARCH}". >&2 + exit 1 + fi + + # If FIRST_ARCH_URL is empty, store the current URL + if [ -z "$FIRST_ARCH_URL" ]; then + FIRST_ARCH_URL=$REDIRECTED_URL + fi +done + +# If all downloads are successful, print the URL for the first architecture +echo "$FIRST_ARCH_URL" diff --git a/jdk-download.sh b/jdk-download.sh new file mode 100755 index 00000000..51324922 --- /dev/null +++ b/jdk-download.sh @@ -0,0 +1,51 @@ +#!/bin/sh +set -x +# Check if curl and tar are installed +if ! command -v curl >/dev/null 2>&1 || ! command -v tar >/dev/null 2>&1 ; then + echo "curl and tar are required but not installed. Exiting with status 1." >&2 + exit 1 +fi + +# Set the OS to "standard" by default +OS="standard" + +# If a second argument is provided, use it as the OS +if [ $# -eq 1 ]; then + OS=$1 +fi + +# Call jdk-download-url.sh with JAVA_VERSION and OS as arguments +# The two scripts should be in the same directory. +# That's why we're trying to find the directory of the current script and use it to call the other script. +SCRIPT_DIR=$(cd "$(dirname "$0")" || exit; pwd) +if ! DOWNLOAD_URL=$("${SCRIPT_DIR}"/jdk-download-url.sh "${JAVA_VERSION}" "${OS}"); then + echo "Error: Failed to fetch the URL. Exiting with status 1." >&2 + exit 1 +fi + +# Use curl to download the JDK archive from the URL +if ! curl --silent --location --output /tmp/jdk.tar.gz "${DOWNLOAD_URL}"; then + echo "Error: Failed to download the JDK archive. Exiting with status 1." >&2 + exit 1 +fi + +# Extract the archive to the /opt/ directory +if ! tar -xzf /tmp/jdk.tar.gz -C /opt/; then + echo "Error: Failed to extract the JDK archive. Exiting with status 1." >&2 + exit 1 +fi + +# Get the name of the extracted directory +EXTRACTED_DIR=$(tar -tzf /tmp/jdk.tar.gz | head -n 1 | cut -f1 -d"/") + +# Rename the extracted directory to /opt/jdk-${JAVA_VERSION} +if ! mv "/opt/${EXTRACTED_DIR}" "/opt/jdk-${JAVA_VERSION}"; then + echo "Error: Failed to rename the extracted directory. Exiting with status 1." >&2 + exit 1 +fi + +# Remove the downloaded archive +if ! rm -f /tmp/jdk.tar.gz; then + echo "Error: Failed to remove the downloaded archive. Exiting with status 1." >&2 + exit 1 +fi diff --git a/tests/sshAgent.Tests.ps1 b/tests/sshAgent.Tests.ps1 index 6565fd29..bf063cf6 100644 --- a/tests/sshAgent.Tests.ps1 +++ b/tests/sshAgent.Tests.ps1 @@ -1,6 +1,7 @@ Import-Module -DisableNameChecking -Force $PSScriptRoot/test_helpers.psm1 $global:IMAGE_NAME = Get-EnvOrDefault 'IMAGE_NAME' '' # Ex: jenkins4eval/ssh-agent:nanoserver-1809-jdk17 +$global:JAVA_VERSION = Get-EnvOrDefault 'JAVA_VERSION' '' $imageItems = $global:IMAGE_NAME.Split(':') $global:IMAGE_TAG = $imageItems[1] @@ -61,7 +62,7 @@ Cleanup($global:CONTAINERNAME) Describe "[$global:IMAGE_NAME] image is present" { It 'builds image' { - $exitCode, $stdout, $stderr = Run-Program 'docker' "build --build-arg `"WINDOWS_VERSION_TAG=${global:WINDOWSVERSIONTAG}`" --build-arg `"TOOLS_WINDOWS_VERSION=${global:TOOLSWINDOWSVERSION}`" --build-arg `"JAVA_VERSION=${global:JAVAMAJORVERSION}`" --build-arg `"JAVA_HOME=C:\openjdk-${global:JAVAMAJORVERSION}`" --tag=${global:IMAGE_TAG} --file ./windows/${global:WINDOWSFLAVOR}/Dockerfile ." + $exitCode, $stdout, $stderr = Run-Program 'docker' "build --build-arg `"WINDOWS_VERSION_TAG=${global:WINDOWSVERSIONTAG}`" --build-arg `"TOOLS_WINDOWS_VERSION=${global:TOOLSWINDOWSVERSION}`" --build-arg `"JAVA_VERSION=${global:JAVA_VERSION}`" --build-arg `"JAVA_HOME=C:\openjdk-${global:JAVAMAJORVERSION}`" --tag=${global:IMAGE_TAG} --file ./windows/${global:WINDOWSFLAVOR}/Dockerfile ." $exitCode | Should -Be 0 } } @@ -199,7 +200,7 @@ Describe "[$global:IMAGE_NAME] build args" { $TEST_JAW = 'C:/hamster' $CUSTOM_IMAGE_NAME = "custom-${IMAGE_NAME}" - $exitCode, $stdout, $stderr = Run-Program 'docker' "build --build-arg `"WINDOWS_VERSION_TAG=${global:WINDOWSVERSIONTAG}`" --build-arg `"TOOLS_WINDOWS_VERSION=${global:TOOLSWINDOWSVERSION}`" --build-arg `"JAVA_VERSION=${global:JAVAMAJORVERSION}`" --build-arg `"JAVA_HOME=C:\openjdk-${global:JAVAMAJORVERSION}`" --build-arg `"user=$TEST_USER`" --build-arg `"JENKINS_AGENT_WORK=$TEST_JAW`" --tag=$CUSTOM_IMAGE_NAME --file ./windows/${global:WINDOWSFLAVOR}/Dockerfile ." + $exitCode, $stdout, $stderr = Run-Program 'docker' "build --build-arg `"WINDOWS_VERSION_TAG=${global:WINDOWSVERSIONTAG}`" --build-arg `"TOOLS_WINDOWS_VERSION=${global:TOOLSWINDOWSVERSION}`" --build-arg `"JAVA_VERSION=${global:JAVA_VERSION}`" --build-arg `"JAVA_HOME=C:\openjdk-${global:JAVAMAJORVERSION}`" --build-arg `"user=$TEST_USER`" --build-arg `"JENKINS_AGENT_WORK=$TEST_JAW`" --tag=$CUSTOM_IMAGE_NAME --file ./windows/${global:WINDOWSFLAVOR}/Dockerfile ." $exitCode | Should -Be 0 $exitCode, $stdout, $stderr = Run-Program 'docker' "run --detach --tty --name=$global:CONTAINERNAME --publish-all $CUSTOM_IMAGE_NAME $global:CONTAINERSHELL" diff --git a/windows/nanoserver/Dockerfile b/windows/nanoserver/Dockerfile index 7f56a060..a89107d2 100644 --- a/windows/nanoserver/Dockerfile +++ b/windows/nanoserver/Dockerfile @@ -22,10 +22,21 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -ARG JAVA_VERSION ARG WINDOWS_VERSION_TAG ARG TOOLS_WINDOWS_VERSION -FROM eclipse-temurin:"${JAVA_VERSION}"-jdk-windowsservercore-"${TOOLS_WINDOWS_VERSION}" AS jdk-core +FROM mcr.microsoft.com/windows/servercore:"${WINDOWS_VERSION_TAG}" AS jdk-core + +# $ProgressPreference: https://github.com/PowerShell/PowerShell/issues/2138#issuecomment-251261324 +SHELL ["powershell.exe", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] + +ARG JAVA_VERSION +RUN New-Item -ItemType Directory -Path C:\temp | Out-Null ; ` + $javaMajorVersion = $env:JAVA_VERSION.substring(0,2) ; ` + $msiUrl = 'https://api.adoptium.net/v3/installer/version/jdk-{0}/windows/x64/jdk/hotspot/normal/eclipse?project=jdk' -f $env:JAVA_VERSION.Replace('+', '%2B') ; ` + Invoke-WebRequest $msiUrl -OutFile 'C:\temp\jdk.msi' ; ` + $proc = Start-Process -FilePath 'msiexec.exe' -ArgumentList '/i', 'C:\temp\jdk.msi', '/L*V', 'C:\temp\OpenJDK.log', '/quiet', 'ADDLOCAL=FeatureEnvironment,FeatureJarFileRunWith,FeatureJavaHome', "INSTALLDIR=C:\openjdk-${javaMajorVersion}" -Wait -Passthru ; ` + $proc.WaitForExit() ; ` + Remove-Item -Path C:\temp -Recurse | Out-Null FROM mcr.microsoft.com/powershell:nanoserver-"${TOOLS_WINDOWS_VERSION}" AS pwsh-source diff --git a/windows/windowsservercore/Dockerfile b/windows/windowsservercore/Dockerfile index d181b10c..dfba4c8d 100644 --- a/windows/windowsservercore/Dockerfile +++ b/windows/windowsservercore/Dockerfile @@ -22,10 +22,21 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -ARG JAVA_VERSION ARG WINDOWS_VERSION_TAG ARG TOOLS_WINDOWS_VERSION -FROM eclipse-temurin:"${JAVA_VERSION}"-jdk-windowsservercore-"${TOOLS_WINDOWS_VERSION}" AS jdk-core +FROM mcr.microsoft.com/windows/servercore:"${WINDOWS_VERSION_TAG}" AS jdk-core + +# $ProgressPreference: https://github.com/PowerShell/PowerShell/issues/2138#issuecomment-251261324 +SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] + +ARG JAVA_VERSION +RUN New-Item -ItemType Directory -Path C:\temp | Out-Null ; ` + $javaMajorVersion = $env:JAVA_VERSION.substring(0,2) ; ` + $msiUrl = 'https://api.adoptium.net/v3/installer/version/jdk-{0}/windows/x64/jdk/hotspot/normal/eclipse?project=jdk' -f $env:JAVA_VERSION.Replace('+', '%2B') ; ` + Invoke-WebRequest $msiUrl -OutFile 'C:\temp\jdk.msi' ; ` + $proc = Start-Process -FilePath 'msiexec.exe' -ArgumentList '/i', 'C:\temp\jdk.msi', '/L*V', 'C:\temp\OpenJDK.log', '/quiet', 'ADDLOCAL=FeatureEnvironment,FeatureJarFileRunWith,FeatureJavaHome', "INSTALLDIR=C:\openjdk-${javaMajorVersion}" -Wait -Passthru ; ` + $proc.WaitForExit() ; ` + Remove-Item -Path C:\temp -Recurse | Out-Null FROM mcr.microsoft.com/windows/servercore:"${WINDOWS_VERSION_TAG}"