From 0ab91ff94889dcfdea1f99ed0d49b84aaaa15f13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Le=20Meur?= Date: Fri, 12 Jan 2024 00:34:06 +0100 Subject: [PATCH] feat: build both `agent` and `inbound-agent` images --- .../update-dockerhub-description.yaml | 14 +- .gitignore | 1 + Jenkinsfile | 2 +- Makefile | 14 +- README.md | 21 ++ alpine/Dockerfile | 29 +- build.ps1 | 269 ++++++++++-------- build.sh | 6 +- debian/Dockerfile | 30 +- debian/preview/Dockerfile | 29 +- docker-bake.hcl | 187 ++++++++---- tests/agent.Tests.ps1 | 39 +-- tests/inbound-agent.Tests.ps1 | 171 +++-------- tests/test_helpers.bash | 17 +- tests/test_helpers.psm1 | 30 +- tests/tests_agent.bats | 28 +- tests/tests_inbound-agent.bats | 16 +- windows/nanoserver/Dockerfile | 19 +- windows/windowsservercore/Dockerfile | 19 +- 19 files changed, 557 insertions(+), 384 deletions(-) create mode 100644 README.md diff --git a/.github/workflows/update-dockerhub-description.yaml b/.github/workflows/update-dockerhub-description.yaml index 037b79c2c..bba896fe4 100644 --- a/.github/workflows/update-dockerhub-description.yaml +++ b/.github/workflows/update-dockerhub-description.yaml @@ -9,11 +9,21 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Update Docker Hub description + - name: Update Docker Hub description for agent uses: peter-evans/dockerhub-description@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} enable-url-completion: true - short-description: ${{ github.event.repository.description }} + short-description: This is a base image, which provides the Jenkins agent executable (agent.jar) + repository: jenkins/agent + readme-filepath: ./README_agent.md + - name: Update Docker Hub description for inbound-agent + uses: peter-evans/dockerhub-description@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + enable-url-completion: true + short-description: This is an image for Jenkins agents using TCP or WebSockets to establish inbound connection to the Jenkins controller repository: jenkins/inbound-agent + readme-filepath: ./README_inbound-agent.md diff --git a/.gitignore b/.gitignore index 76ff725db..069b0c8f1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ bats-core/ bats/ target/ +build-windows-current.yaml diff --git a/Jenkinsfile b/Jenkinsfile index ea8a82ff3..557a5f002 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -69,7 +69,7 @@ pipeline { } post { always { - junit(allowEmptyResults: true, keepLongStdio: true, testResults: 'target/**/junit-results.xml') + junit(allowEmptyResults: true, keepLongStdio: true, testResults: 'target/**/junit-results*.xml') } } } diff --git a/Makefile b/Makefile index 3ad48865b..f55418549 100644 --- a/Makefile +++ b/Makefile @@ -10,12 +10,6 @@ export BUILDKIT_PROGRESS=plain current_arch := $(shell uname -m) export ARCH ?= $(shell case $(current_arch) in (x86_64) echo "amd64" ;; (i386) echo "386";; (aarch64|arm64) echo "arm64" ;; (armv6*) echo "arm/v6";; (armv7*) echo "arm/v7";; (s390*|riscv*|ppc64le) echo $(current_arch);; (*) echo "UNKNOWN-CPU";; esac) -IMAGE_NAME:=jenkins4eval/agent - -# Set to the path of a specific test suite to restrict execution only to this -# default is "all test suites in the "tests/" directory -TEST_SUITES ?= $(CURDIR)/tests - ##### Macros ## Check the presence of a CLI in the current PATH check_cli = type "$(1)" >/dev/null 2>&1 || { echo "Error: command '$(1)' required but not found. Exiting." ; exit 1 ; } @@ -42,6 +36,7 @@ build: check-reqs build-%: @$(call check_image,$*) + @echo "== building $*" @set -x; $(bake_base_cli) --set '*.platform=linux/$(ARCH)' '$*' show: @@ -59,7 +54,7 @@ prepare-test: bats check-reqs ## Define bats options based on environment # common flags for all tests -bats_flags := $(TEST_SUITES) +bats_flags := "" # if DISABLE_PARALLEL_TESTS true, then disable parallel execution ifneq (true,$(DISABLE_PARALLEL_TESTS)) # If the GNU 'parallel' command line is absent, then disable parallel execution @@ -75,9 +70,10 @@ test-%: prepare-test @$(call check_image,$*) # Ensure that the image is built @make --silent build-$* -# Execute the test harness and write result to a TAP file + @echo "== testing $*" set -x - IMAGE=$* bats/bin/bats $(bats_flags) | tee target/results-$*.tap +# Each type of image ("agent" or "inbound-agent") has its own tests suite + IMAGE=$* bats/bin/bats $(CURDIR)/tests/tests_$(shell echo $* | cut -d "_" -f 1).bats $(bats_flags) | tee target/results-$*.tap # convert TAP to JUNIT docker run --rm -v "$(CURDIR)":/usr/src/app -w /usr/src/app node:16-alpine \ sh -c "npm install tap-xunit -g && cat target/results-$*.tap | tap-xunit --package='jenkinsci.docker.$*' > target/junit-results-$*.xml" diff --git a/README.md b/README.md new file mode 100644 index 000000000..6b43ca1ce --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# Jenkins Agent and Inbound Agent Docker images + +[![Join the chat at https://gitter.im/jenkinsci/docker](https://badges.gitter.im/jenkinsci/docker.svg)](https://gitter.im/jenkinsci/docker?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![GitHub stars](https://img.shields.io/github/stars/jenkinsci/docker-agent?label=GitHub%20stars)](https://github.com/jenkinsci/docker-agent) +[![GitHub release](https://img.shields.io/github/release/jenkinsci/docker-agent.svg?label=changelog)](https://github.com/jenkinsci/docker-agent/releases/latest) + +This repository contains the definition of two images: + +## agent +[![Docker Pulls](https://img.shields.io/docker/pulls/jenkins/agent.svg)](https://hub.docker.com/r/jenkins/agent/) + +This is a base image for Docker, which includes JDK and the Jenkins agent executable (agent.jar). + +See [the `agent` README](./README_agent.md) + +## inbound-agent +[![Docker Pulls](https://img.shields.io/docker/pulls/jenkins/inbound-agent.svg)](https://hub.docker.com/r/jenkins/inbound-agent/) + +This is an image based on `agent` for [Jenkins](https://jenkins.io) agents using TCP or WebSockets to establish inbound connection to the Jenkins master. + +See [the `inbound-agent` README](./README_inbound-agent.md) diff --git a/alpine/Dockerfile b/alpine/Dockerfile index 0e9c4e4c1..0fa14a319 100644 --- a/alpine/Dockerfile +++ b/alpine/Dockerfile @@ -40,7 +40,8 @@ RUN if [ "$TARGETPLATFORM" != 'linux/arm/v7' ]; then \ cp -r /opt/java/openjdk /javaruntime; \ fi -FROM alpine:"${ALPINE_TAG}" AS build +## Agent image target +FROM alpine:"${ALPINE_TAG}" AS agent ARG user=jenkins ARG group=jenkins @@ -48,7 +49,7 @@ ARG uid=1000 ARG gid=1000 RUN addgroup -g "${gid}" "${group}" \ - && adduser -h /home/"${user}" -u "${uid}" -G "${group}" -D "${user}" + && adduser -h /home/"${user}" -u "${uid}" -G "${group}" -D "${user}" || echo "user ${user} already exists." ARG AGENT_WORKDIR=/home/"${user}"/agent @@ -82,7 +83,7 @@ ENV PATH="${JAVA_HOME}/bin:${PATH}" USER "${user}" ENV AGENT_WORKDIR="${AGENT_WORKDIR}" -RUN mkdir /home/"${user}"/.jenkins && mkdir -p "${AGENT_WORKDIR}" +RUN mkdir -p /home/"${user}"/.jenkins && mkdir -p "${AGENT_WORKDIR}" VOLUME /home/"${user}"/.jenkins VOLUME "${AGENT_WORKDIR}" @@ -96,3 +97,25 @@ LABEL \ org.opencontainers.image.url="https://www.jenkins.io/" \ org.opencontainers.image.source="https://github.com/jenkinsci/docker-agent" \ org.opencontainers.image.licenses="MIT" + +## Inbound Agent image target +FROM agent AS inbound-agent + +ARG user=jenkins + +USER root +COPY ../../jenkins-agent /usr/local/bin/jenkins-agent +RUN chmod +x /usr/local/bin/jenkins-agent &&\ + ln -s /usr/local/bin/jenkins-agent /usr/local/bin/jenkins-slave +USER ${user} + +LABEL \ + org.opencontainers.image.vendor="Jenkins project" \ + org.opencontainers.image.title="Official Jenkins Agent Base Docker image" \ + org.opencontainers.image.description="This is an image for Jenkins agents using TCP or WebSockets to establish inbound connection to the Jenkins controller" \ + org.opencontainers.image.version="${VERSION}" \ + org.opencontainers.image.url="https://www.jenkins.io/" \ + org.opencontainers.image.source="https://github.com/jenkinsci/docker-agent" \ + org.opencontainers.image.licenses="MIT" + +ENTRYPOINT ["/usr/local/bin/jenkins-agent"] diff --git a/build.ps1 b/build.ps1 index 508b61d26..8bb257791 100644 --- a/build.ps1 +++ b/build.ps1 @@ -3,15 +3,21 @@ Param( [Parameter(Position=1)] [String] $Target = "build", [String] $RemotingVersion = '3206.vb_15dcf73f6a_9', + [String] $AgentType = '', [String] $BuildNumber = '1', [switch] $DisableEnvProps = $false, [switch] $DryRun = $false ) $ErrorActionPreference = 'Stop' -$Repository = 'agent' -$Organization = 'jenkins' -$ImageType = 'windows-ltsc2019' +$AgentTypes = @('agent', 'inbound-agent') +if ($AgentType -ne '' -and $AgentType -in $AgentTypes) { + $AgentTypes = @($AgentType) +} +$ImageType = 'windowsservercore-ltsc2019' +$Organisation = 'jenkins4eval' +$AgentRepository = 'agent' +$InboundAgentRepository = 'inbound-agent' if(!$DisableEnvProps) { Get-Content env.props | ForEach-Object { @@ -24,12 +30,16 @@ if(!$DisableEnvProps) { } } -if(![String]::IsNullOrWhiteSpace($env:DOCKERHUB_REPO)) { - $Repository = $env:DOCKERHUB_REPO +if(![String]::IsNullOrWhiteSpace($env:DOCKERHUB_ORGANISATION)) { + $Organisation = $env:DOCKERHUB_ORGANISATION } -if(![String]::IsNullOrWhiteSpace($env:DOCKERHUB_ORGANISATION)) { - $Organization = $env:DOCKERHUB_ORGANISATION +if(![String]::IsNullOrWhiteSpace($env:DOCKERHUB_REPO_AGENT)) { + $AgentRepository = $env:DOCKERHUB_REPO_AGENT +} + +if(![String]::IsNullOrWhiteSpace($env:DOCKERHUB_REPO_INBOUND_AGENT)) { + $InboundAgentRepository = $env:DOCKERHUB_REPO_INBOUND_AGENT } if(![String]::IsNullOrWhiteSpace($env:REMOTING_VERSION)) { @@ -64,7 +74,6 @@ Function Test-CommandExists { # this is the jdk version that will be used for the 'bare tag' images, e.g., jdk17-windowsservercore-1809 -> windowsserver-1809 $defaultJdk = '17' -$builds = @{} $env:REMOTING_VERSION = "$RemotingVersion" $items = $ImageType.Split("-") @@ -82,121 +91,41 @@ Test-CommandExists "docker" Test-CommandExists "docker-compose" Test-CommandExists "yq" -$baseDockerCmd = 'docker-compose --file=build-windows.yaml' -$baseDockerBuildCmd = '{0} build --parallel --pull' -f $baseDockerCmd - -Invoke-Expression "$baseDockerCmd config --services" 2>$null | ForEach-Object { - $image = '{0}-{1}-{2}' -f $_, $env:WINDOWS_FLAVOR, $env:WINDOWS_VERSION_TAG # Ex: "jdk17-nanoserver-1809" - - # Remove the 'jdk' prefix - $jdkMajorVersion = $_.Remove(0,3) - - $versionTag = "${RemotingVersion}-${BuildNumber}-${image}" - $tags = @( $image, $versionTag ) - - # Additional image tag without any 'jdk' prefix for the default JDK - $baseImage = "${env:WINDOWS_FLAVOR}-${env:WINDOWS_VERSION_TAG}" - if($jdkMajorVersion -eq "$defaultJdk") { - $tags += $baseImage - $tags += "${RemotingVersion}-${BuildNumber}-${baseImage}" - } - - $builds[$image] = @{ - 'Tags' = $tags; - } -} - -Write-Host "= PREPARE: List of $Organization/$Repository images and tags to be processed:" -ConvertTo-Json $builds - -Write-Host "= BUILD: Building all images..." -if ($DryRun) { - Write-Host "(dry-run) $baseDockerBuildCmd" -} else { - Invoke-Expression $baseDockerBuildCmd -} -Write-Host "= BUILD: Finished building all images." - -if($lastExitCode -ne 0) { - exit $lastExitCode -} - function Test-Image { param ( - $ImageName + $AgentTypeAndImageName ) - Write-Host "= TEST: Testing image ${ImageName}:" + $items = $AgentTypeAndImageName.Split("|") + $agentType = $items[0] + $imageName = $items[1] - $env:AGENT_IMAGE = $ImageName - $serviceName = $ImageName.SubString(0, $ImageName.IndexOf('-')) - $env:BUILD_CONTEXT = Invoke-Expression "$baseDockerCmd config" 2>$null | yq -r ".services.${serviceName}.build.context" + Write-Host "= TEST: Testing ${agentType} image ${imageName}:" + + $env:AGENT_TYPE = $agentType + $env:AGENT_IMAGE = $imageName $env:VERSION = "$RemotingVersion-$BuildNumber" - if(Test-Path ".\target\$ImageName") { - Remove-Item -Recurse -Force ".\target\$ImageName" + $targetPath = '.\target\{0}\{1}' -f $agentType, $imageName + if(Test-Path $targetPath) { + Remove-Item -Recurse -Force $targetPath } - New-Item -Path ".\target\$ImageName" -Type Directory | Out-Null - $configuration.TestResult.OutputPath = ".\target\$ImageName\junit-results.xml" + New-Item -Path $targetPath -Type Directory | Out-Null + $configuration.Run.Path = 'tests\{0}.Tests.ps1' -f $agentType + $configuration.TestResult.OutputPath = '{0}\junit-results.xml' -f $targetPath $TestResults = Invoke-Pester -Configuration $configuration if ($TestResults.FailedCount -gt 0) { - Write-Host "There were $($TestResults.FailedCount) failed tests in $ImageName" + Write-Host "There were $($TestResults.FailedCount) failed tests in ${agentType} $imageName" $testFailed = $true } else { - Write-Host "There were $($TestResults.PassedCount) passed tests out of $($TestResults.TotalCount) in $ImageName" + Write-Host "There were $($TestResults.PassedCount) passed tests out of $($TestResults.TotalCount) in ${agentType} $imageName" } + + Remove-Item env:\AGENT_TYPE Remove-Item env:\AGENT_IMAGE - Remove-Item env:\BUILD_CONTEXT Remove-Item env:\VERSION } -if($target -eq "test") { - if ($DryRun) { - Write-Host "= TEST: (dry-run) test harness" - } else { - Write-Host "= TEST: Starting test harness" - - # Only fail the run afterwards in case of any test failures - $testFailed = $false - $mod = Get-InstalledModule -Name Pester -MinimumVersion 5.3.0 -MaximumVersion 5.3.3 -ErrorAction SilentlyContinue - if($null -eq $mod) { - Write-Host "= TEST: Pester 5.3.x not found: installing..." - $module = "c:\Program Files\WindowsPowerShell\Modules\Pester" - if(Test-Path $module) { - takeown /F $module /A /R - icacls $module /reset - icacls $module /grant Administrators:'F' /inheritance:d /T - Remove-Item -Path $module -Recurse -Force -Confirm:$false - } - Install-Module -Force -Name Pester -MaximumVersion 5.3.3 - } - - Import-Module Pester - Write-Host "= TEST: Setting up Pester environment..." - $configuration = [PesterConfiguration]::Default - $configuration.Run.PassThru = $true - $configuration.Run.Path = '.\tests' - $configuration.Run.Exit = $true - $configuration.TestResult.Enabled = $true - $configuration.TestResult.OutputFormat = 'JUnitXml' - $configuration.Output.Verbosity = 'Diagnostic' - $configuration.CodeCoverage.Enabled = $false - - Write-Host "= TEST: Testing all images..." - foreach($image in $builds.Keys) { - Test-Image $image - } - - # Fail if any test failures - if($testFailed -ne $false) { - Write-Error "Test stage failed!" - exit 1 - } else { - Write-Host "= TEST: stage passed!" - } - } -} - function Publish-Image { param ( [String] $Build, @@ -214,22 +143,126 @@ function Publish-Image { } -if($target -eq "publish") { - # Only fail the run afterwards in case of any issues when publishing the docker images - $publishFailed = 0 - foreach($b in $builds.Keys) { - foreach($tag in $Builds[$b]['Tags']) { - Publish-Image "$b" "${Organization}/${Repository}:${tag}" - if($lastExitCode -ne 0) { - $publishFailed = 1 +$originalDockerComposeFile = 'build-windows.yaml' +$finalDockerComposeFile = 'build-windows-current.yaml' +$baseDockerCmd = 'docker-compose --file={0}' -f $finalDockerComposeFile +$baseDockerBuildCmd = '{0} build --parallel --pull' -f $baseDockerCmd + +foreach($agentType in $AgentTypes) { + $env:AGENT_TYPE = $agentType + + # Temporary docker compose file (git ignored) + Copy-Item -Path $originalDockerComposeFile -Destination $finalDockerComposeFile + $repository = $InboundAgentRepository + # If it's a type "agent", set corresponding target and repository + if($agentType -eq 'agent') { + yq '.services.[].build.target = \"agent\"' $originalDockerComposeFile | Out-File -FilePath $finalDockerComposeFile + $repository = $AgentRepository + } + + $builds = @{} + + Invoke-Expression "$baseDockerCmd config --services" 2>$null | ForEach-Object { + $image = '{0}-{1}-{2}' -f $_, $env:WINDOWS_FLAVOR, $env:WINDOWS_VERSION_TAG # Ex: "jdk17-nanoserver-1809" + + # Remove the 'jdk' prefix + $jdkMajorVersion = $_.Remove(0,3) + + $versionTag = "${RemotingVersion}-${BuildNumber}-${image}" + $tags = @( $image, $versionTag ) + + # Additional image tag without any 'jdk' prefix for the default JDK + $baseImage = "${env:WINDOWS_FLAVOR}-${env:WINDOWS_VERSION_TAG}" + if($jdkMajorVersion -eq "$defaultJdk") { + $tags += $baseImage + $tags += "${RemotingVersion}-${BuildNumber}-${baseImage}" + } + + $builds[$image] = @{ + 'Tags' = $tags; + } + } + + Write-Host "= PREPARE: List of $Organisation/$repository images and tags to be processed:" + ConvertTo-Json $builds + + Write-Host "= BUILD: Building all images..." + if ($DryRun) { + Write-Host "(dry-run) $baseDockerBuildCmd" + } else { + Invoke-Expression $baseDockerBuildCmd + } + Write-Host "= BUILD: Finished building all images." + + if($lastExitCode -ne 0) { + exit $lastExitCode + } + + if($target -eq "test") { + if ($DryRun) { + Write-Host "= TEST: (dry-run) test harness" + } else { + Write-Host "= TEST: Starting test harness" + + # Only fail the run afterwards in case of any test failures + $testFailed = $false + $mod = Get-InstalledModule -Name Pester -MinimumVersion 5.3.0 -MaximumVersion 5.3.3 -ErrorAction SilentlyContinue + if($null -eq $mod) { + Write-Host "= TEST: Pester 5.3.x not found: installing..." + $module = "c:\Program Files\WindowsPowerShell\Modules\Pester" + if(Test-Path $module) { + takeown /F $module /A /R + icacls $module /reset + icacls $module /grant Administrators:'F' /inheritance:d /T + Remove-Item -Path $module -Recurse -Force -Confirm:$false + } + Install-Module -Force -Name Pester -MaximumVersion 5.3.3 + } + + Import-Module Pester + Write-Host "= TEST: Setting up Pester environment..." + $configuration = [PesterConfiguration]::Default + $configuration.Run.PassThru = $true + $configuration.Run.Path = '.\tests' + $configuration.Run.Exit = $true + $configuration.TestResult.Enabled = $true + $configuration.TestResult.OutputFormat = 'JUnitXml' + $configuration.Output.Verbosity = 'Diagnostic' + $configuration.CodeCoverage.Enabled = $false + + Write-Host "= TEST: Testing all images..." + Write-Host "= TEST: Testing all ${agentType} images..." + foreach($image in $builds.Keys) { + Test-Image ('{0}|{1}' -f $agentType, $image) + } + + # Fail if any test failures + if($testFailed -ne $false) { + Write-Error "Test stage failed for ${agentType}!" + exit 1 + } else { + Write-Host "= TEST: stage passed for ${agentType}!" } } } - # Fail if any issues when publising the docker images - if($publishFailed -ne 0) { - Write-Error "Publish failed!" - exit 1 + if($target -eq "publish") { + # Only fail the run afterwards in case of any issues when publishing the docker images + $publishFailed = 0 + foreach($b in $builds.Keys) { + foreach($tag in $Builds[$b]['Tags']) { + Publish-Image "$b" "${Organisation}/${Repository}:${tag}" + if($lastExitCode -ne 0) { + $publishFailed = 1 + } + } + } + + # Fail if any issues when publising the docker images + if($publishFailed -ne 0) { + Write-Error "Publish failed for ${Organisation}/${repository}!" + exit 1 + } } } diff --git a/build.sh b/build.sh index f8963a3c7..1c3da8bcd 100755 --- a/build.sh +++ b/build.sh @@ -51,8 +51,9 @@ if [[ "${disable_env_props}" = "0" ]] ; then export "$(cut -d= -f1 env.props)" fi -REPOSITORY=${DOCKERHUB_REPO:-agent} -ORGANIZATION=${DOCKERHUB_ORGANISATION:-jenkins} +export REGISTRY_ORG=${DOCKERHUB_ORGANISATION:-jenkins4eval} +export REGISTRY_REPO_AGENT=${DOCKERHUB_REPO_AGENT:-agent} +export REGISTRY_REPO_INBOUND_AGENT=${DOCKERHUB_REPO_INBOUND_AGENT:-inbound-agent} remoting_version=${REMOTING_VERSION:-${remoting_version}} if [[ "${target}" = "build" ]] ; then @@ -72,7 +73,6 @@ fi if [[ "${target}" = "publish" ]] ; then set -x - export JENKINS_REPO="${ORGANIZATION}/${REPOSITORY}" export REMOTING_VERSION="${remoting_version}" if [[ -n "${build_number}" ]] ; then export ON_TAG=true diff --git a/debian/Dockerfile b/debian/Dockerfile index 26a5ee249..3333b261e 100644 --- a/debian/Dockerfile +++ b/debian/Dockerfile @@ -50,8 +50,8 @@ RUN if test "${TARGETPLATFORM}" != 'linux/arm/v7'; then \ else cp -r /opt/java/openjdk /javaruntime; \ fi - -FROM debian:"${DEBIAN_RELEASE}"-slim AS build +## Agent image target +FROM debian:"${DEBIAN_RELEASE}"-slim AS agent ARG user=jenkins ARG group=jenkins @@ -59,7 +59,7 @@ ARG uid=1000 ARG gid=1000 RUN groupadd -g "${gid}" "${group}" \ - && useradd -l -c "Jenkins user" -d /home/"${user}" -u "${uid}" -g "${gid}" -m "${user}" + && useradd -l -c "Jenkins user" -d /home/"${user}" -u "${uid}" -g "${gid}" -m "${user}" || echo "user ${user} already exists." ARG AGENT_WORKDIR=/home/"${user}"/agent ENV TZ=Etc/UTC @@ -94,7 +94,7 @@ ENV PATH="${JAVA_HOME}/bin:${PATH}" USER "${user}" ENV AGENT_WORKDIR=${AGENT_WORKDIR} -RUN mkdir /home/${user}/.jenkins && mkdir -p "${AGENT_WORKDIR}" +RUN mkdir -p /home/"${user}"/.jenkins && mkdir -p "${AGENT_WORKDIR}" VOLUME /home/"${user}"/.jenkins VOLUME "${AGENT_WORKDIR}" @@ -108,3 +108,25 @@ LABEL \ org.opencontainers.image.url="https://www.jenkins.io/" \ org.opencontainers.image.source="https://github.com/jenkinsci/docker-agent" \ org.opencontainers.image.licenses="MIT" + +## Inbound Agent image target +FROM agent AS inbound-agent + +ARG user=jenkins + +USER root +COPY ../../jenkins-agent /usr/local/bin/jenkins-agent +RUN chmod +x /usr/local/bin/jenkins-agent &&\ + ln -s /usr/local/bin/jenkins-agent /usr/local/bin/jenkins-slave +USER ${user} + +LABEL \ + org.opencontainers.image.vendor="Jenkins project" \ + org.opencontainers.image.title="Official Jenkins Inbound Agent Base Docker image" \ + org.opencontainers.image.description="This is an image for Jenkins agents using TCP or WebSockets to establish inbound connection to the Jenkins controller" \ + org.opencontainers.image.version="${VERSION}" \ + org.opencontainers.image.url="https://www.jenkins.io/" \ + org.opencontainers.image.source="https://github.com/jenkinsci/docker-agent-inbound" \ + org.opencontainers.image.licenses="MIT" + +ENTRYPOINT ["/usr/local/bin/jenkins-agent"] diff --git a/debian/preview/Dockerfile b/debian/preview/Dockerfile index b727cea5c..32a2caea1 100644 --- a/debian/preview/Dockerfile +++ b/debian/preview/Dockerfile @@ -40,7 +40,8 @@ RUN if test "${TARGETPLATFORM}" != 'linux/arm/v7'; then \ cp -r "/opt/jdk-${JAVA_VERSION}" /javaruntime; \ fi -FROM debian:"${DEBIAN_RELEASE}" AS build +## Agent image target +FROM debian:"${DEBIAN_RELEASE}" AS agent ARG user=jenkins ARG group=jenkins @@ -48,7 +49,7 @@ ARG uid=1000 ARG gid=1000 RUN groupadd -g "${gid}" "${group}" \ - && useradd -l -c "Jenkins user" -d /home/"${user}" -u "${uid}" -g "${gid}" -m "${user}" + && useradd -l -c "Jenkins user" -d /home/"${user}" -u "${uid}" -g "${gid}" -m "${user}" || echo "user ${user} already exists." ARG AGENT_WORKDIR=/home/"${user}"/agent ENV TZ=Etc/UTC @@ -83,7 +84,7 @@ ENV PATH="${JAVA_HOME}/bin:${PATH}" USER "${user}" ENV AGENT_WORKDIR=${AGENT_WORKDIR} -RUN mkdir /home/${user}/.jenkins && mkdir -p "${AGENT_WORKDIR}" +RUN mkdir -p /home/${user}/.jenkins && mkdir -p "${AGENT_WORKDIR}" VOLUME /home/"${user}"/.jenkins VOLUME "${AGENT_WORKDIR}" @@ -97,3 +98,25 @@ LABEL \ org.opencontainers.image.url="https://www.jenkins.io/" \ org.opencontainers.image.source="https://github.com/jenkinsci/docker-agent" \ org.opencontainers.image.licenses="MIT" + +## Inbound Agent image target +FROM agent AS inbound-agent + +ARG user=jenkins + +USER root +COPY ../../jenkins-agent /usr/local/bin/jenkins-agent +RUN chmod +x /usr/local/bin/jenkins-agent &&\ + ln -s /usr/local/bin/jenkins-agent /usr/local/bin/jenkins-slave +USER ${user} + +LABEL \ + org.opencontainers.image.vendor="Jenkins project" \ + org.opencontainers.image.title="Official Jenkins Inbound Agent Base Docker image" \ + org.opencontainers.image.description="This is an image for Jenkins agents using TCP or WebSockets to establish inbound connection to the Jenkins controller" \ + org.opencontainers.image.version="${VERSION}" \ + org.opencontainers.image.url="https://www.jenkins.io/" \ + org.opencontainers.image.source="https://github.com/jenkinsci/docker-agent-inbound" \ + org.opencontainers.image.licenses="MIT" + +ENTRYPOINT ["/usr/local/bin/jenkins-agent"] diff --git a/docker-bake.hcl b/docker-bake.hcl index b7b8eea36..79149924c 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -3,7 +3,7 @@ group "linux" { "alpine_jdk11", "alpine_jdk17", "alpine_jdk21", - "archlinux_jdk11", + "agent_archlinux_jdk11", "debian_jdk11", "debian_jdk17", "debian_jdk21", @@ -11,6 +11,31 @@ group "linux" { ] } +group "linux-agent-only" { + targets = [ + "agent_alpine_jdk11", + "agent_alpine_jdk17", + "agent_alpine_jdk21", + "agent_archlinux_jdk11", + "agent_debian_jdk11", + "agent_debian_jdk17", + "agent_debian_jdk21", + "agent_debian_jdk21_preview" + ] +} + +group "linux-inbound-agent-only" { + targets = [ + "inbound-agent_alpine_jdk11", + "inbound-agent_alpine_jdk17", + "inbound-agent_alpine_jdk21", + "inbound-agent_debian_jdk11", + "inbound-agent_debian_jdk17", + "inbound-agent_debian_jdk21", + "inbound-agent_debian_jdk21_preview" + ] +} + group "linux-arm64" { targets = [ "debian_jdk11", @@ -51,8 +76,16 @@ variable "REGISTRY" { default = "docker.io" } -variable "JENKINS_REPO" { - default = "jenkins/agent" +variable "REGISTRY_ORG" { + default = "jenkins" +} + +variable "REGISTRY_REPO_AGENT" { + default = "agent" +} + +variable "REGISTRY_REPO_INBOUND_AGENT" { + default = "inbound-agent" } variable "BUILD_NUMBER" { @@ -91,7 +124,12 @@ variable "JAVA21_PREVIEW_VERSION" { default = "21.0.1+12" } -target "archlinux_jdk11" { +function "orgrepo" { + params = [agentType] + result = equal("agent", agentType) ? "${REGISTRY_ORG}/${REGISTRY_REPO_AGENT}" : "${REGISTRY_ORG}/${REGISTRY_REPO_INBOUND_AGENT}" +} + +target "agent_archlinux_jdk11" { dockerfile = "archlinux/Dockerfile" context = "." args = { @@ -99,17 +137,22 @@ target "archlinux_jdk11" { VERSION = REMOTING_VERSION } tags = [ - equal(ON_TAG, "true") ? "${REGISTRY}/${JENKINS_REPO}:${REMOTING_VERSION}-${BUILD_NUMBER}-archlinux" : "", - equal(ON_TAG, "true") ? "${REGISTRY}/${JENKINS_REPO}:${REMOTING_VERSION}-${BUILD_NUMBER}-archlinux-jdk11" : "", - "${REGISTRY}/${JENKINS_REPO}:archlinux", - "${REGISTRY}/${JENKINS_REPO}:latest-archlinux", - "${REGISTRY}/${JENKINS_REPO}:archlinux-jdk11", - "${REGISTRY}/${JENKINS_REPO}:latest-archlinux-jdk11", + equal(ON_TAG, "true") ? "${REGISTRY}/${orgrepo("agent")}:${REMOTING_VERSION}-${BUILD_NUMBER}-archlinux" : "", + equal(ON_TAG, "true") ? "${REGISTRY}/${orgrepo("agent")}:${REMOTING_VERSION}-${BUILD_NUMBER}-archlinux-jdk11" : "", + "${REGISTRY}/${orgrepo("agent")}:archlinux", + "${REGISTRY}/${orgrepo("agent")}:latest-archlinux", + "${REGISTRY}/${orgrepo("agent")}:archlinux-jdk11", + "${REGISTRY}/${orgrepo("agent")}:latest-archlinux-jdk11", ] platforms = ["linux/amd64"] } target "alpine_jdk11" { + matrix = { + type = ["agent", "inbound-agent"] + } + name = "${type}_alpine_jdk11" + target = type dockerfile = "alpine/Dockerfile" context = "." args = { @@ -118,17 +161,22 @@ target "alpine_jdk11" { VERSION = REMOTING_VERSION } tags = [ - equal(ON_TAG, "true") ? "${REGISTRY}/${JENKINS_REPO}:${REMOTING_VERSION}-${BUILD_NUMBER}-alpine-jdk11" : "", - equal(ON_TAG, "true") ? "${REGISTRY}/${JENKINS_REPO}:${REMOTING_VERSION}-${BUILD_NUMBER}-alpine${ALPINE_SHORT_TAG}-jdk11" : "", - "${REGISTRY}/${JENKINS_REPO}:alpine-jdk11", - "${REGISTRY}/${JENKINS_REPO}:alpine${ALPINE_SHORT_TAG}-jdk11", - "${REGISTRY}/${JENKINS_REPO}:latest-alpine-jdk11", - "${REGISTRY}/${JENKINS_REPO}:latest-alpine${ALPINE_SHORT_TAG}-jdk11", + equal(ON_TAG, "true") ? "${REGISTRY}/${orgrepo(type)}:${REMOTING_VERSION}-${BUILD_NUMBER}-alpine-jdk11" : "", + equal(ON_TAG, "true") ? "${REGISTRY}/${orgrepo(type)}:${REMOTING_VERSION}-${BUILD_NUMBER}-alpine${ALPINE_SHORT_TAG}-jdk11" : "", + "${REGISTRY}/${orgrepo(type)}:alpine-jdk11", + "${REGISTRY}/${orgrepo(type)}:alpine${ALPINE_SHORT_TAG}-jdk11", + "${REGISTRY}/${orgrepo(type)}:latest-alpine-jdk11", + "${REGISTRY}/${orgrepo(type)}:latest-alpine${ALPINE_SHORT_TAG}-jdk11", ] platforms = ["linux/amd64"] } target "alpine_jdk17" { + matrix = { + type = ["agent", "inbound-agent"] + } + name = "${type}_alpine_jdk17" + target = type dockerfile = "alpine/Dockerfile" context = "." args = { @@ -137,23 +185,28 @@ target "alpine_jdk17" { VERSION = REMOTING_VERSION } tags = [ - equal(ON_TAG, "true") ? "${REGISTRY}/${JENKINS_REPO}:${REMOTING_VERSION}-${BUILD_NUMBER}-alpine" : "", - equal(ON_TAG, "true") ? "${REGISTRY}/${JENKINS_REPO}:${REMOTING_VERSION}-${BUILD_NUMBER}-alpine${ALPINE_SHORT_TAG}" : "", - equal(ON_TAG, "true") ? "${REGISTRY}/${JENKINS_REPO}:${REMOTING_VERSION}-${BUILD_NUMBER}-alpine-jdk17" : "", - equal(ON_TAG, "true") ? "${REGISTRY}/${JENKINS_REPO}:${REMOTING_VERSION}-${BUILD_NUMBER}-alpine${ALPINE_SHORT_TAG}-jdk17" : "", - "${REGISTRY}/${JENKINS_REPO}:alpine", - "${REGISTRY}/${JENKINS_REPO}:alpine${ALPINE_SHORT_TAG}", - "${REGISTRY}/${JENKINS_REPO}:alpine-jdk17", - "${REGISTRY}/${JENKINS_REPO}:alpine${ALPINE_SHORT_TAG}-jdk17", - "${REGISTRY}/${JENKINS_REPO}:latest-alpine", - "${REGISTRY}/${JENKINS_REPO}:latest-alpine${ALPINE_SHORT_TAG}", - "${REGISTRY}/${JENKINS_REPO}:latest-alpine-jdk17", - "${REGISTRY}/${JENKINS_REPO}:latest-alpine${ALPINE_SHORT_TAG}-jdk17", + equal(ON_TAG, "true") ? "${REGISTRY}/${orgrepo(type)}:${REMOTING_VERSION}-${BUILD_NUMBER}-alpine" : "", + equal(ON_TAG, "true") ? "${REGISTRY}/${orgrepo(type)}:${REMOTING_VERSION}-${BUILD_NUMBER}-alpine${ALPINE_SHORT_TAG}" : "", + equal(ON_TAG, "true") ? "${REGISTRY}/${orgrepo(type)}:${REMOTING_VERSION}-${BUILD_NUMBER}-alpine-jdk17" : "", + equal(ON_TAG, "true") ? "${REGISTRY}/${orgrepo(type)}:${REMOTING_VERSION}-${BUILD_NUMBER}-alpine${ALPINE_SHORT_TAG}-jdk17" : "", + "${REGISTRY}/${orgrepo(type)}:alpine", + "${REGISTRY}/${orgrepo(type)}:alpine${ALPINE_SHORT_TAG}", + "${REGISTRY}/${orgrepo(type)}:alpine-jdk17", + "${REGISTRY}/${orgrepo(type)}:alpine${ALPINE_SHORT_TAG}-jdk17", + "${REGISTRY}/${orgrepo(type)}:latest-alpine", + "${REGISTRY}/${orgrepo(type)}:latest-alpine${ALPINE_SHORT_TAG}", + "${REGISTRY}/${orgrepo(type)}:latest-alpine-jdk17", + "${REGISTRY}/${orgrepo(type)}:latest-alpine${ALPINE_SHORT_TAG}-jdk17", ] platforms = ["linux/amd64"] } target "alpine_jdk21" { + matrix = { + type = ["agent", "inbound-agent"] + } + name = "${type}_alpine_jdk21" + target = type dockerfile = "alpine/Dockerfile" context = "." args = { @@ -162,17 +215,22 @@ target "alpine_jdk21" { VERSION = REMOTING_VERSION } tags = [ - equal(ON_TAG, "true") ? "${REGISTRY}/${JENKINS_REPO}:${REMOTING_VERSION}-${BUILD_NUMBER}-alpine-jdk21" : "", - equal(ON_TAG, "true") ? "${REGISTRY}/${JENKINS_REPO}:${REMOTING_VERSION}-${BUILD_NUMBER}-alpine${ALPINE_SHORT_TAG}-jdk21" : "", - "${REGISTRY}/${JENKINS_REPO}:alpine-jdk21", - "${REGISTRY}/${JENKINS_REPO}:alpine${ALPINE_SHORT_TAG}-jdk21", - "${REGISTRY}/${JENKINS_REPO}:latest-alpine-jdk21", - "${REGISTRY}/${JENKINS_REPO}:latest-alpine${ALPINE_SHORT_TAG}-jdk21", + equal(ON_TAG, "true") ? "${REGISTRY}/${orgrepo(type)}:${REMOTING_VERSION}-${BUILD_NUMBER}-alpine-jdk21" : "", + equal(ON_TAG, "true") ? "${REGISTRY}/${orgrepo(type)}:${REMOTING_VERSION}-${BUILD_NUMBER}-alpine${ALPINE_SHORT_TAG}-jdk21" : "", + "${REGISTRY}/${orgrepo(type)}:alpine-jdk21", + "${REGISTRY}/${orgrepo(type)}:alpine${ALPINE_SHORT_TAG}-jdk21", + "${REGISTRY}/${orgrepo(type)}:latest-alpine-jdk21", + "${REGISTRY}/${orgrepo(type)}:latest-alpine${ALPINE_SHORT_TAG}-jdk21", ] platforms = ["linux/amd64", "linux/arm64"] } target "debian_jdk11" { + matrix = { + type = ["agent", "inbound-agent"] + } + name = "${type}_debian_jdk11" + target = type dockerfile = "debian/Dockerfile" context = "." args = { @@ -181,16 +239,21 @@ target "debian_jdk11" { DEBIAN_RELEASE = DEBIAN_RELEASE } tags = [ - equal(ON_TAG, "true") ? "${REGISTRY}/${JENKINS_REPO}:${REMOTING_VERSION}-${BUILD_NUMBER}-jdk11" : "", - "${REGISTRY}/${JENKINS_REPO}:bookworm-jdk11", - "${REGISTRY}/${JENKINS_REPO}:jdk11", - "${REGISTRY}/${JENKINS_REPO}:latest-bookworm-jdk11", - "${REGISTRY}/${JENKINS_REPO}:latest-jdk11", + equal(ON_TAG, "true") ? "${REGISTRY}/${orgrepo(type)}:${REMOTING_VERSION}-${BUILD_NUMBER}-jdk11" : "", + "${REGISTRY}/${orgrepo(type)}:bookworm-jdk11", + "${REGISTRY}/${orgrepo(type)}:jdk11", + "${REGISTRY}/${orgrepo(type)}:latest-bookworm-jdk11", + "${REGISTRY}/${orgrepo(type)}:latest-jdk11", ] platforms = ["linux/amd64", "linux/arm64", "linux/arm/v7", "linux/s390x", "linux/ppc64le"] } target "debian_jdk17" { + matrix = { + type = ["agent", "inbound-agent"] + } + name = "${type}_debian_jdk17" + target = type dockerfile = "debian/Dockerfile" context = "." args = { @@ -199,19 +262,24 @@ target "debian_jdk17" { DEBIAN_RELEASE = DEBIAN_RELEASE } tags = [ - equal(ON_TAG, "true") ? "${REGISTRY}/${JENKINS_REPO}:${REMOTING_VERSION}-${BUILD_NUMBER}" : "", - equal(ON_TAG, "true") ? "${REGISTRY}/${JENKINS_REPO}:${REMOTING_VERSION}-${BUILD_NUMBER}-jdk17" : "", - "${REGISTRY}/${JENKINS_REPO}:bookworm-jdk17", - "${REGISTRY}/${JENKINS_REPO}:jdk17", - "${REGISTRY}/${JENKINS_REPO}:latest", - "${REGISTRY}/${JENKINS_REPO}:latest-bookworm", - "${REGISTRY}/${JENKINS_REPO}:latest-bookworm-jdk17", - "${REGISTRY}/${JENKINS_REPO}:latest-jdk17", + equal(ON_TAG, "true") ? "${REGISTRY}/${orgrepo(type)}:${REMOTING_VERSION}-${BUILD_NUMBER}" : "", + equal(ON_TAG, "true") ? "${REGISTRY}/${orgrepo(type)}:${REMOTING_VERSION}-${BUILD_NUMBER}-jdk17" : "", + "${REGISTRY}/${orgrepo(type)}:bookworm-jdk17", + "${REGISTRY}/${orgrepo(type)}:jdk17", + "${REGISTRY}/${orgrepo(type)}:latest", + "${REGISTRY}/${orgrepo(type)}:latest-bookworm", + "${REGISTRY}/${orgrepo(type)}:latest-bookworm-jdk17", + "${REGISTRY}/${orgrepo(type)}:latest-jdk17", ] platforms = ["linux/amd64", "linux/arm64", "linux/arm/v7", "linux/ppc64le"] } target "debian_jdk21" { + matrix = { + type = ["agent", "inbound-agent"] + } + name = "${type}_debian_jdk21" + target = type dockerfile = "debian/Dockerfile" context = "." args = { @@ -220,16 +288,21 @@ target "debian_jdk21" { DEBIAN_RELEASE = DEBIAN_RELEASE } tags = [ - equal(ON_TAG, "true") ? "${REGISTRY}/${JENKINS_REPO}:${REMOTING_VERSION}-${BUILD_NUMBER}-jdk21" : "", - "${REGISTRY}/${JENKINS_REPO}:bookworm-jdk21", - "${REGISTRY}/${JENKINS_REPO}:jdk21", - "${REGISTRY}/${JENKINS_REPO}:latest-bookworm-jdk21", - "${REGISTRY}/${JENKINS_REPO}:latest-jdk21", + equal(ON_TAG, "true") ? "${REGISTRY}/${orgrepo(type)}:${REMOTING_VERSION}-${BUILD_NUMBER}-jdk21" : "", + "${REGISTRY}/${orgrepo(type)}:bookworm-jdk21", + "${REGISTRY}/${orgrepo(type)}:jdk21", + "${REGISTRY}/${orgrepo(type)}:latest-bookworm-jdk21", + "${REGISTRY}/${orgrepo(type)}:latest-jdk21", ] platforms = ["linux/amd64", "linux/arm64"] } target "debian_jdk21_preview" { + matrix = { + type = ["agent", "inbound-agent"] + } + name = "${type}_debian_jdk21_preview" + target = type dockerfile = "debian/preview/Dockerfile" context = "." args = { @@ -238,11 +311,11 @@ target "debian_jdk21_preview" { DEBIAN_RELEASE = DEBIAN_RELEASE } tags = [ - equal(ON_TAG, "true") ? "${REGISTRY}/${JENKINS_REPO}:${REMOTING_VERSION}-${BUILD_NUMBER}-jdk21-preview" : "", - "${REGISTRY}/${JENKINS_REPO}:bookworm-jdk21-preview", - "${REGISTRY}/${JENKINS_REPO}:jdk21-preview", - "${REGISTRY}/${JENKINS_REPO}:latest-bookworm-jdk21-preview", - "${REGISTRY}/${JENKINS_REPO}:latest-jdk21-preview", + equal(ON_TAG, "true") ? "${REGISTRY}/${orgrepo(type)}:${REMOTING_VERSION}-${BUILD_NUMBER}-jdk21-preview" : "", + "${REGISTRY}/${orgrepo(type)}:bookworm-jdk21-preview", + "${REGISTRY}/${orgrepo(type)}:jdk21-preview", + "${REGISTRY}/${orgrepo(type)}:latest-bookworm-jdk21-preview", + "${REGISTRY}/${orgrepo(type)}:latest-jdk21-preview", ] platforms = ["linux/ppc64le", "linux/s390x", "linux/arm/v7"] } diff --git a/tests/agent.Tests.ps1 b/tests/agent.Tests.ps1 index 745dcc1ff..88b22b395 100644 --- a/tests/agent.Tests.ps1 +++ b/tests/agent.Tests.ps1 @@ -1,7 +1,7 @@ Import-Module -DisableNameChecking -Force $PSScriptRoot/test_helpers.psm1 +$global:AGENT_TYPE = Get-EnvOrDefault 'AGENT_TYPE' '' $global:AGENT_IMAGE = Get-EnvOrDefault 'AGENT_IMAGE' '' -$global:BUILD_CONTEXT = Get-EnvOrDefault 'BUILD_CONTEXT' '' $global:VERSION = Get-EnvOrDefault 'VERSION' '' $items = $global:AGENT_IMAGE.Split("-") @@ -27,14 +27,14 @@ $global:GITLFSVERSION = '3.4.1' Cleanup($global:CONTAINERNAME) -Describe "[$global:AGENT_IMAGE] image is present" { +Describe "[$global:AGENT_TYPE > $global:AGENT_IMAGE] image is present" { It 'builds image' { $exitCode, $stdout, $stderr = Run-Program 'docker' "inspect $global:AGENT_IMAGE" $exitCode | Should -Be 0 } } -Describe "[$global:AGENT_IMAGE] correct image metadata" { +Describe "[$global:AGENT_TYPE > $global:AGENT_IMAGE] correct image metadata" { It 'has correct volumes' { $exitCode, $stdout, $stderr = Run-Program 'docker' "inspect --format='{{.Config.Volumes}}' $global:AGENT_IMAGE" $stdout = $stdout.Trim() @@ -43,10 +43,10 @@ Describe "[$global:AGENT_IMAGE] correct image metadata" { } } -Describe "[$global:AGENT_IMAGE] image has correct applications in the PATH" { +Describe "[$global:AGENT_TYPE > $global:AGENT_IMAGE] image has correct applications in the PATH" { BeforeAll { docker run --detach --interactive --tty --name "$global:CONTAINERNAME" "$global:AGENT_IMAGE" "$global:CONTAINERSHELL" - Is-AgentContainerRunning $global:CONTAINERNAME + Is-ContainerRunning $global:CONTAINERNAME | Should -BeTrue } It 'has java installed and in the path' { @@ -78,15 +78,20 @@ Describe "[$global:AGENT_IMAGE] image has correct applications in the PATH" { $stdout.Trim() | Should -Match "git-lfs/${global:GITLFSVERSION}" } + It 'does not include jenkins-agent.ps1 (inbound-agent)' { + $exitCode, $stdout, $stderr = Run-Program 'docker' "exec $global:CONTAINERNAME $global:CONTAINERSHELL -C `"if(Test-Path C:/ProgramData/Jenkins/jenkins-agent.ps1) { exit -1 } else { exit 0 }`"" + $exitCode | Should -Be 0 + } + AfterAll { Cleanup($global:CONTAINERNAME) } } -Describe "[$global:AGENT_IMAGE] check user account" { +Describe "[$global:AGENT_TYPE > $global:AGENT_IMAGE] check user account" { BeforeAll { docker run -d -it --name "$global:CONTAINERNAME" -P "$global:AGENT_IMAGE" "$global:CONTAINERSHELL" - Is-AgentContainerRunning $global:CONTAINERNAME + Is-ContainerRunning $global:CONTAINERNAME | Should -BeTrue } It 'Password never expires' { @@ -104,10 +109,10 @@ Describe "[$global:AGENT_IMAGE] check user account" { } } -Describe "[$global:AGENT_IMAGE] check user access to directories" { +Describe "[$global:AGENT_TYPE > $global:AGENT_IMAGE] check user access to directories" { BeforeAll { docker run -d -it --name "$global:CONTAINERNAME" -P "$global:AGENT_IMAGE" "$global:CONTAINERSHELL" - Is-AgentContainerRunning $global:CONTAINERNAME + Is-ContainerRunning $global:CONTAINERNAME | Should -BeTrue } It 'can write to HOME' { @@ -134,33 +139,33 @@ $global:TEST_VERSION="4.0" $global:TEST_USER="test-user" $global:TEST_AGENT_WORKDIR="C:/test-user/something" -Describe "[$global:AGENT_IMAGE] can be built with custom build arguments" { +Describe "[$global:AGENT_TYPE > $global:AGENT_IMAGE] can be built with custom build arguments" { BeforeAll { Push-Location -StackName 'agent' -Path "$PSScriptRoot/.." - $exitCode, $stdout, $stderr = Run-Program 'docker' "build --build-arg `"VERSION=${global:TEST_VERSION}`" --build-arg `"WINDOWS_VERSION_TAG=${global:WINDOWSVERSIONTAG}`" --build-arg `"TOOLS_WINDOWS_VERSION=${global:WINDOWSVERSIONFALLBACKTAG}`" --build-arg `"user=${global:TEST_USER}`" --build-arg `"AGENT_WORKDIR=${global:TEST_AGENT_WORKDIR}`" --tag ${global:AGENT_IMAGE} --file ./windows/${global:WINDOWSFLAVOR}/Dockerfile ${global:BUILD_CONTEXT}" + $exitCode, $stdout, $stderr = Run-Program 'docker' "build --target agent --build-arg `"VERSION=${global:TEST_VERSION}`" --build-arg `"WINDOWS_VERSION_TAG=${global:WINDOWSVERSIONTAG}`" --build-arg `"TOOLS_WINDOWS_VERSION=${global:WINDOWSVERSIONFALLBACKTAG}`" --build-arg `"user=${global:TEST_USER}`" --build-arg `"AGENT_WORKDIR=${global:TEST_AGENT_WORKDIR}`" --tag ${global:AGENT_IMAGE} --file ./windows/${global:WINDOWSFLAVOR}/Dockerfile ." $exitCode | Should -Be 0 - docker run -d -it --name "$global:CONTAINERNAME" -P "$global:AGENT_IMAGE" "$global:CONTAINERSHELL" - Is-AgentContainerRunning $global:CONTAINERNAME + $exitCode, $stdout, $stderr = Run-Program 'docker' "run -d -it --name $global:CONTAINERNAME -P $global:AGENT_IMAGE $global:CONTAINERSHELL" + Is-ContainerRunning $global:CONTAINERNAME | Should -BeTrue } It 'has the correct version of remoting' { $exitCode, $stdout, $stderr = Run-Program 'docker' "exec $global:CONTAINERNAME $global:CONTAINERSHELL -C `"`$global:VERSION = java -jar C:/ProgramData/Jenkins/agent.jar -version ; Write-Host `$global:VERSION`"" $exitCode | Should -Be 0 - $stdout.Trim() | Should -Match $TEST_VERSION + $stdout.Trim() | Should -Match $global:TEST_VERSION } It 'has correct user' { $exitCode, $stdout, $stderr = Run-Program 'docker' "exec $global:CONTAINERNAME $global:CONTAINERSHELL -C `"(Get-ChildItem env:\ | Where-Object { `$_.Name -eq 'USERNAME' }).Value`"" $exitCode | Should -Be 0 - $stdout.Trim() | Should -Match $TEST_USER + $stdout.Trim() | Should -Match $global:TEST_USER } It 'has correct AGENT_WORKDIR' { $exitCode, $stdout, $stderr = Run-Program 'docker' "exec $global:CONTAINERNAME $global:CONTAINERSHELL -C `"Get-ChildItem env:`"" $exitCode | Should -Be 0 - $stdout | Should -Match "AGENT_WORKDIR.*${TEST_AGENT_WORKDIR}" + $stdout | Should -Match "AGENT_WORKDIR.*${global:TEST_AGENT_WORKDIR}" } It 'can write to HOME' { @@ -181,7 +186,7 @@ Describe "[$global:AGENT_IMAGE] can be built with custom build arguments" { It 'version in docker metadata' { $exitCode, $stdout, $stderr = Run-Program 'docker' "inspect --format=`"{{index .Config.Labels \`"org.opencontainers.image.version\`"}}`" $global:AGENT_IMAGE" $exitCode | Should -Be 0 - $stdout.Trim() | Should -Match $TEST_VERSION + $stdout.Trim() | Should -Match $global:sTEST_VERSION } AfterAll { diff --git a/tests/inbound-agent.Tests.ps1 b/tests/inbound-agent.Tests.ps1 index a8e5d5d38..a10ab6b8f 100644 --- a/tests/inbound-agent.Tests.ps1 +++ b/tests/inbound-agent.Tests.ps1 @@ -1,7 +1,7 @@ Import-Module -DisableNameChecking -Force $PSScriptRoot/test_helpers.psm1 +$global:AGENT_TYPE = Get-EnvOrDefault 'AGENT_TYPE' '' $global:AGENT_IMAGE = Get-EnvOrDefault 'AGENT_IMAGE' '' -$global:BUILD_CONTEXT = Get-EnvOrDefault 'BUILD_CONTEXT' '' $global:version = Get-EnvOrDefault 'VERSION' '' $items = $global:AGENT_IMAGE.Split("-") @@ -31,14 +31,14 @@ CleanupNetwork("jnlp-network") BuildNcatImage($global:WINDOWSVERSIONTAG) -Describe "[$global:AGENT_IMAGE] build image" { +Describe "[$global:AGENT_TYPE > $global:AGENT_IMAGE] build image" { It 'builds image' { - $exitCode, $stdout, $stderr = Run-Program 'docker' "build --build-arg version=${global:version} --build-arg `"WINDOWS_VERSION_TAG=${global:WINDOWSVERSIONTAG}`" --build-arg JAVA_MAJOR_VERSION=${global:JAVAMAJORVERSION} --tag=${global:AGENT_IMAGE} --file ./windows/${global:WINDOWSFLAVOR}/Dockerfile ${global:BUILD_CONTEXT}" + $exitCode, $stdout, $stderr = Run-Program 'docker' "build --build-arg version=${global:version} --build-arg `"WINDOWS_VERSION_TAG=${global:WINDOWSVERSIONTAG}`" --build-arg `"JAVA_VERSION=${global:JAVAMAJORVERSION}`" --build-arg `"JAVA_HOME=C:\openjdk-${global:JAVAMAJORVERSION}`" --tag=${global:AGENT_IMAGE} --file ./windows/${global:WINDOWSFLAVOR}/Dockerfile ." $exitCode | Should -Be 0 } } -Describe "[$global:AGENT_IMAGE] check default user account" { +Describe "[$global:AGENT_TYPE > $global:AGENT_IMAGE] check default user account" { BeforeAll { docker run --detach --tty --name "$global:CONTAINERNAME" "$global:AGENT_IMAGE" -Cmd "$global:CONTAINERSHELL" $LASTEXITCODE | Should -Be 0 @@ -60,7 +60,7 @@ Describe "[$global:AGENT_IMAGE] check default user account" { } } -Describe "[$global:AGENT_IMAGE] image has jenkins-agent.ps1 in the correct location" { +Describe "[$global:AGENT_TYPE > $global:AGENT_IMAGE] image has jenkins-agent.ps1 in the correct location" { BeforeAll { docker run --detach --tty --name "$global:CONTAINERNAME" "$global:AGENT_IMAGE" -Cmd "$global:CONTAINERSHELL" $LASTEXITCODE | Should -Be 0 @@ -77,7 +77,7 @@ Describe "[$global:AGENT_IMAGE] image has jenkins-agent.ps1 in the correct locat } } -Describe "[$global:AGENT_IMAGE] image starts jenkins-agent.ps1 correctly (slow test)" { +Describe "[$global:AGENT_TYPE > $global:AGENT_IMAGE] image starts jenkins-agent.ps1 correctly (slow test)" { It 'connects to the nmap container' { $exitCode, $stdout, $stderr = Run-Program 'docker' "network create --driver nat jnlp-network" # Launch the netcat utility, listening at port 5000 for 30 sec @@ -112,19 +112,17 @@ Describe "[$global:AGENT_IMAGE] image starts jenkins-agent.ps1 correctly (slow t } } -Describe "[$global:AGENT_IMAGE] custom build args" { +Describe "[$global:AGENT_TYPE > $global:AGENT_IMAGE] custom build args" { BeforeAll { Push-Location -StackName 'agent' -Path "$PSScriptRoot/.." # Old version used to test overriding the build arguments. # This old version must have the same tag suffixes as the current windows images (`-jdk17-nanoserver` etc.), and the same Windows version (2019, 2022, etc.) - $TEST_VERSION = "3148.v532a_7e715ee3" - $PARENT_IMAGE_VERSION_SUFFIX = "12" - $ARG_TEST_VERSION = "${TEST_VERSION}-${PARENT_IMAGE_VERSION_SUFFIX}" + $TEST_VERSION = "3206.vb_15dcf73f6a_9" $customImageName = "custom-${global:AGENT_IMAGE}" } It 'builds image with arguments' { - $exitCode, $stdout, $stderr = Run-Program 'docker' "build --build-arg version=${ARG_TEST_VERSION} --build-arg `"WINDOWS_VERSION_TAG=${global:WINDOWSVERSIONTAG}`" --build-arg JAVA_MAJOR_VERSION=${global:JAVAMAJORVERSION} --build-arg WINDOWS_FLAVOR=${global:WINDOWSFLAVOR} --build-arg CONTAINER_SHELL=${global:CONTAINERSHELL} --tag=${customImageName} --file=./windows/${global:WINDOWSFLAVOR}/Dockerfile ${global:BUILD_CONTEXT}" + $exitCode, $stdout, $stderr = Run-Program 'docker' "build --build-arg version=${TEST_VERSION} --build-arg `"WINDOWS_VERSION_TAG=${global:WINDOWSVERSIONTAG}`" --build-arg `"JAVA_VERSION=${global:JAVAMAJORVERSION}`" --build-arg `"JAVA_HOME=C:\openjdk-${global:JAVAMAJORVERSION}`" --build-arg WINDOWS_FLAVOR=${global:WINDOWSFLAVOR} --build-arg CONTAINER_SHELL=${global:CONTAINERSHELL} --tag=${customImageName} --file=./windows/${global:WINDOWSFLAVOR}/Dockerfile ." $exitCode | Should -Be 0 $exitCode, $stdout, $stderr = Run-Program 'docker' "run --detach --tty --name $global:CONTAINERNAME $customImageName -Cmd $global:CONTAINERSHELL" @@ -133,7 +131,7 @@ Describe "[$global:AGENT_IMAGE] custom build args" { } It "has the correct agent.jar version" { - $exitCode, $stdout, $stderr = Run-Program 'docker' "exec $global:CONTAINERNAME $global:CONTAINERSHELL -c `"java -cp C:/ProgramData/Jenkins/agent.jar hudson.remoting.jnlp.Main -version`"" + $exitCode, $stdout, $stderr = Run-Program 'docker' "exec $global:CONTAINERNAME $global:CONTAINERSHELL -c `"java -jar C:/ProgramData/Jenkins/agent.jar -version`"" $exitCode | Should -Be 0 $stdout | Should -Match $TEST_VERSION } @@ -144,125 +142,36 @@ Describe "[$global:AGENT_IMAGE] custom build args" { } } -# === TODO: uncomment test later, see error log below -# === this test passes on a Windows machine - -# Describe "[$global:AGENT_IMAGE] passing JVM options (slow test)" { -# It "shows the java version ${global:JAVAMAJORVERSION} with --show-version" { -# $exitCode, $stdout, $stderr = Run-Program 'docker' "network create --driver nat jnlp-network" -# # Launch the netcat utility, listening at port 5000 for 30 sec -# # bats will capture the output from netcat and compare the first line -# # of the header of the first HTTP request with the expected one -# $exitCode, $stdout, $stderr = Run-Program 'docker' "run --detach --tty --name nmap --network=jnlp-network nmap:latest ncat.exe -w 30 -l 5000" -# $exitCode | Should -Be 0 -# Is-ContainerRunning "nmap" | Should -BeTrue - -# # get the ip address of the nmap container -# $exitCode, $stdout, $stderr = Run-Program 'docker' "inspect --format `"{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}`" nmap" -# $exitCode | Should -Be 0 -# $nmap_ip = $stdout.Trim() - -# # run Jenkins agent which tries to connect to the nmap container at port 5000 -# $secret = "aaa" -# $name = "bbb" -# $exitCode, $stdout, $stderr = Run-Program 'docker' "run --detach --tty --network=jnlp-network --name $global:CONTAINERNAME $global:AGENT_IMAGE -Url http://${nmap_ip}:5000 -JenkinsJavaOpts `"--show-version`" $secret $name" -# $exitCode | Should -Be 0 -# Is-ContainerRunning $global:CONTAINERNAME | Should -BeTrue -# $exitCode, $stdout, $stderr = Run-Program 'docker' "logs $global:CONTAINERNAME" -# $exitCode | Should -Be 0 -# $stdout | Should -Match "OpenJDK Runtime Environment Temurin-${global:JAVAMAJORVERSION}" -# } - -# AfterAll { -# Cleanup($global:CONTAINERNAME) -# Cleanup("nmap") -# CleanupNetwork("jnlp-network") -# } -# } - - -# === Corresponding error log: - -# Running tests from 'inboundAgent.Tests.ps1' -# Describing [jdk17-windowsservercore-1809] build image -# cmd = docker, params = build --build-arg version=3148.v532a_7e715ee3-3 --build-arg "WINDOWS_VERSION_TAG=1809" --build-arg JAVA_MAJOR_VERSION=17 --tag=jdk17-windowsservercore-1809 --file ./windows/windowsservercore/Dockerfile C:\Jenkins\agent\workspace\ging_docker-inbound-agent_PR-396 -# [+] builds image 572ms (378ms|195ms) -# cmd = docker.exe, params = inspect -f "{{.State.Running}}" pester-jenkins-inbound-agent-jdk17-windowsservercore-1809 - -# Describing [jdk17-windowsservercore-1809] check default user account -# cmd = docker, params = exec pester-jenkins-inbound-agent-jdk17-windowsservercore-1809 powershell.exe -C "if((net user jenkins | Select-String -Pattern 'Password expires') -match 'Never') { exit 0 } else { net user jenkins ; exit -1 }" -# [+] has a password that never expires 4.55s (4.55s|5ms) -# cmd = docker, params = exec pester-jenkins-inbound-agent-jdk17-windowsservercore-1809 powershell.exe -C "if((net user jenkins | Select-String -Pattern 'Password required') -match 'No') { exit 0 } else { net user jenkins ; exit -1 }" -# [+] has password policy of "not required" 2.74s (2.73s|3ms) -# cmd = docker.exe, params = inspect -f "{{.State.Running}}" pester-jenkins-inbound-agent-jdk17-windowsservercore-1809 - -# Describing [jdk17-windowsservercore-1809] image has jenkins-agent.ps1 in the correct location -# cmd = docker, params = exec pester-jenkins-inbound-agent-jdk17-windowsservercore-1809 powershell.exe -C "if(Test-Path 'C:/ProgramData/Jenkins/jenkins-agent.ps1') { exit 0 } else { exit 1 }" -# [+] has jenkins-agent.ps1 in C:/ProgramData/Jenkins 4.35s (4.27s|85ms) - -# Describing [jdk17-windowsservercore-1809] image starts jenkins-agent.ps1 correctly (slow test) -# cmd = docker, params = network create --driver nat jnlp-network -# cmd = docker, params = run --detach --tty --name nmap --network=jnlp-network nmap:latest ncat.exe -w 30 -l 5000 -# cmd = docker.exe, params = inspect -f "{{.State.Running}}" nmap -# cmd = docker, params = inspect --format "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" nmap -# cmd = docker, params = run --detach --tty --network=jnlp-network --name pester-jenkins-inbound-agent-jdk17-windowsservercore-1809 jdk17-windowsservercore-1809 -Url http://172.23.176.67:5000 aaa bbb -# cmd = docker.exe, params = inspect -f "{{.State.Running}}" pester-jenkins-inbound-agent-jdk17-windowsservercore-1809 -# cmd = docker, params = wait nmap -# cmd = docker, params = logs nmap -# [+] connects to the nmap container 89.43s (89.43s|8ms) +Describe "[$global:AGENT_TYPE > $global:AGENT_IMAGE] passing JVM options (slow test)" { + It "shows the java version ${global:JAVAMAJORVERSION} with --show-version" { + $exitCode, $stdout, $stderr = Run-Program 'docker' "network create --driver nat jnlp-network" + # Launch the netcat utility, listening at port 5000 for 30 sec + # bats will capture the output from netcat and compare the first line + # of the header of the first HTTP request with the expected one + $exitCode, $stdout, $stderr = Run-Program 'docker' "run --detach --tty --name nmap --network=jnlp-network nmap:latest ncat.exe -w 30 -l 5000" + $exitCode | Should -Be 0 + Is-ContainerRunning "nmap" | Should -BeTrue -# Describing [jdk17-windowsservercore-1809] custom build args -# cmd = docker, params = build --build-arg version=3148.v532a_7e715ee3-3 --build-arg "WINDOWS_VERSION_TAG=1809" --build-arg JAVA_MAJOR_VERSION=17 --build-arg WINDOWS_FLAVOR=windowsservercore --build-arg CONTAINER_SHELL=powershell.exe --tag=custom-jdk17-windowsservercore-1809 --file=./windows/windowsservercore/Dockerfile C:\Jenkins\agent\workspace\ging_docker-inbound-agent_PR-396 -# cmd = docker, params = run --detach --tty --name pester-jenkins-inbound-agent-jdk17-windowsservercore-1809 custom-jdk17-windowsservercore-1809 -Cmd powershell.exe -# cmd = docker.exe, params = inspect -f "{{.State.Running}}" pester-jenkins-inbound-agent-jdk17-windowsservercore-1809 -# [+] builds image with arguments 11.3s (11.3s|5ms) -# cmd = docker, params = exec pester-jenkins-inbound-agent-jdk17-windowsservercore-1809 powershell.exe -c "java -cp C:/ProgramData/Jenkins/agent.jar hudson.remoting.jnlp.Main -version" -# [+] has the correct agent.jar version 2.25s (2.25s|5ms) + # get the ip address of the nmap container + $exitCode, $stdout, $stderr = Run-Program 'docker' "inspect --format `"{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}`" nmap" + $exitCode | Should -Be 0 + $nmap_ip = $stdout.Trim() -# Describing [jdk17-windowsservercore-1809] passing JVM options (slow test) -# cmd = docker, params = network create --driver nat jnlp-network -# cmd = docker, params = run --detach --tty --name nmap --network=jnlp-network nmap:latest ncat.exe -w 30 -l 5000 -# cmd = docker.exe, params = inspect -f "{{.State.Running}}" nmap -# cmd = docker, params = inspect --format "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" nmap -# cmd = docker, params = run --detach --tty --network=jnlp-network --name pester-jenkins-inbound-agent-jdk17-windowsservercore-1809 jdk17-windowsservercore-1809 -Url http://172.23.254.19:5000 -JenkinsJavaOpts "--show-version" aaa bbb -# cmd = docker.exe, params = inspect -f "{{.State.Running}}" pester-jenkins-inbound-agent-jdk17-windowsservercore-1809 -# cmd = docker, params = logs pester-jenkins-inbound-agent-jdk17-windowsservercore-1809 -# [-] shows the java version 17 with --show-version 24.36s (24.35s|11ms) -# Expected regular expression 'OpenJDK Runtime Environment Temurin-17' to match ' -# ', but it did not match. -# at $stdout | Should -Match "OpenJDK Runtime Environment Temurin-${global:JAVAMAJORVERSION}", C:\Jenkins\agent\workspace\ging_docker-inbound-agent_PR-396\tests\inboundAgent.Tests.ps1:173 -# at , C:\Jenkins\agent\workspace\ging_docker-inbound-agent_PR-396\tests\inboundAgent.Tests.ps1:173 -# Tests completed in 219.22s -# Tests Passed: 7, Failed: 1, Skipped: 0 NotRun: 0 -# System.Management.Automation.MethodInvocationException: Exception calling "WriteAttributeString" with "2" argument(s): "'exadecimal value 0x1B, is an invalid character." ---> System.ArgumentException: 'exadecimal value 0x1B, is an invalid character. -# at System.Xml.XmlUtf8RawTextWriter.InvalidXmlChar(Int32 ch, Byte* pDst, Boolean entitize) -# at System.Xml.XmlUtf8RawTextWriter.WriteAttributeTextBlock(Char* pSrc, Char* pSrcEnd) -# at System.Xml.XmlUtf8RawTextWriter.WriteString(String text) -# at System.Xml.XmlWellFormedWriter.WriteString(String text) -# at System.Xml.XmlWriter.WriteAttributeString(String localName, String value) -# at CallSite.Target(Closure , CallSite , XmlWriter , String , Object ) -# --- End of inner exception stack trace --- -# at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception) -# at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame) -# at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame) -# at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame) -# at System.Management.Automation.Interpreter.Interpreter.Run(InterpretedFrame frame) -# at System.Management.Automation.Interpreter.LightLambda.RunVoid1[T0](T0 arg0) -# at System.Management.Automation.PSScriptCmdlet.RunClause(Action`1 clause, Object dollarUnderbar, Object inputToProcess) -# at System.Management.Automation.PSScriptCmdlet.DoEndProcessing() -# at System.Management.Automation.CommandProcessorBase.Complete() -# at Write-JUnitTestCaseMessageElements, C:\Program Files\WindowsPowerShell\Modules\Pester\5.3.3\Pester.psm1: line 16460 -# at Write-JUnitTestCaseAttributes, C:\Program Files\WindowsPowerShell\Modules\Pester\5.3.3\Pester.psm1: line 16452 -# at Write-JUnitTestCaseElements, C:\Program Files\WindowsPowerShell\Modules\Pester\5.3.3\Pester.psm1: line 16424 -# at Write-JUnitTestSuiteElements, C:\Program Files\WindowsPowerShell\Modules\Pester\5.3.3\Pester.psm1: line 16385 -# at Write-JUnitReport, C:\Program Files\WindowsPowerShell\Modules\Pester\5.3.3\Pester.psm1: line 16269 -# at Export-XmlReport, C:\Program Files\WindowsPowerShell\Modules\Pester\5.3.3\Pester.psm1: line 16005 -# at Export-PesterResults, C:\Program Files\WindowsPowerShell\Modules\Pester\5.3.3\Pester.psm1: line 15863 -# at Invoke-Pester, C:\Program Files\WindowsPowerShell\Modules\Pester\5.3.3\Pester.psm1: line 5263 -# at Test-Image, C:\Jenkins\agent\workspace\ging_docker-inbound-agent_PR-396\build.ps1: line 130 -# at , C:\Jenkins\agent\workspace\ging_docker-inbound-agent_PR-396\build.ps1: line 176 -# at , C:\Jenkins\agent\workspace\ging_docker-inbound-agent_PR-396@tmp\durable-513b70db\powershellScript.ps1: line 1 -# at , : line 1 -# at , : line 1 + # run Jenkins agent which tries to connect to the nmap container at port 5000 + $secret = "aaa" + $name = "bbb" + $exitCode, $stdout, $stderr = Run-Program 'docker' "run --detach --tty --network=jnlp-network --name $global:CONTAINERNAME $global:AGENT_IMAGE -Url http://${nmap_ip}:5000 -JenkinsJavaOpts `"--show-version`" $secret $name" + $exitCode | Should -Be 0 + Is-ContainerRunning $global:CONTAINERNAME | Should -BeTrue + Start-Sleep -Seconds 20 + $exitCode, $stdout, $stderr = Run-Program 'docker' "logs $global:CONTAINERNAME" + $exitCode | Should -Be 0 + $stdout | Should -Match "OpenJDK Runtime Environment Temurin-${global:JAVAMAJORVERSION}" + } -# === end of error log + AfterAll { + Cleanup($global:CONTAINERNAME) + Cleanup("nmap") + CleanupNetwork("jnlp-network") + } +} diff --git a/tests/test_helpers.bash b/tests/test_helpers.bash index 5f99823e6..1b61dba95 100755 --- a/tests/test_helpers.bash +++ b/tests/test_helpers.bash @@ -30,13 +30,13 @@ function get_sut_image { # Option --print for 'docker buildx bake' prints the JSON configuration on the stdout # Option --silent for 'make' suppresses the echoing of command so the output is valid JSON # The image name is the 1st of the "tags" array, on the first "image" found - make --silent show | jq -r ".target.${IMAGE}.tags[0]" + make --silent show | jq -r ".target.\"${IMAGE}\".tags[0]" } function get_remoting_version() { test -n "${IMAGE:?"[sut_image] Please set the variable 'IMAGE' to the name of the image to test in 'docker-bake.hcl'."}" - make --silent show | jq -r ".target.${IMAGE}.args.VERSION" + make --silent show | jq -r ".target.\"${IMAGE}\".args.VERSION" } function cleanup { @@ -47,7 +47,7 @@ function cleanup { function get_dockerfile_directory() { test -n "${IMAGE:?"[sut_image] Please set the variable 'IMAGE' to the name of the image to test in 'docker-bake.hcl'."}" - DOCKERFILE=$(make --silent show | jq -r ".target.${IMAGE}.dockerfile") + DOCKERFILE=$(make --silent show | jq -r ".target.\"${IMAGE}\".dockerfile") echo "${DOCKERFILE%"/Dockerfile"}" } @@ -79,3 +79,14 @@ function is_agent_container_running { sleep 1 retry 3 1 assert "true" docker inspect -f '{{.State.Running}}' "${1}" } + +function buildNetcatImage() { + if ! docker inspect --type=image netcat-helper:latest &>/dev/null; then + docker build -t netcat-helper:latest tests/netcat-helper/ &>/dev/null + fi +} + +function clean_test_container { + docker kill "${AGENT_CONTAINER}" "${NETCAT_HELPER_CONTAINER}" &>/dev/null || : + docker rm -fv "${AGENT_CONTAINER}" "${NETCAT_HELPER_CONTAINER}" &>/dev/null || : +} diff --git a/tests/test_helpers.psm1 b/tests/test_helpers.psm1 index 985d485de..8a2e6e054 100644 --- a/tests/test_helpers.psm1 +++ b/tests/test_helpers.psm1 @@ -77,29 +77,30 @@ function Cleanup($name='') { } if(![System.String]::IsNullOrWhiteSpace($name)) { - #Write-Host "Cleaning up $name" docker kill "$name" 2>&1 | Out-Null docker rm -fv "$name" 2>&1 | Out-Null } } -function Is-AgentContainerRunning($container='') { +function Is-ContainerRunning($container='') { if([System.String]::IsNullOrWhiteSpace($container)) { $container = Get-EnvOrDefault 'AGENT_CONTAINER' '' } Start-Sleep -Seconds 5 - Retry-Command -RetryCount 3 -Delay 1 -ScriptBlock { + Retry-Command -RetryCount 10 -Delay 3 -ScriptBlock { $exitCode, $stdout, $stderr = Run-Program 'docker.exe' "inspect -f `"{{.State.Running}}`" $container" if(($exitCode -ne 0) -or (-not $stdout.Contains('true')) ) { throw('Exit code incorrect, or invalid value for running state') } return $true - } | Should -BeTrue + } } -function Run-Program($cmd, $params) { - #Write-Host "cmd = $cmd, params = $params" +function Run-Program($cmd, $params, $quiet=$true) { + if(-not $quiet) { + Write-Host "cmd & params: $cmd $params" + } $psi = New-Object System.Diagnostics.ProcessStartInfo $psi.CreateNoWindow = $true $psi.UseShellExecute = $false @@ -114,7 +115,7 @@ function Run-Program($cmd, $params) { $stdout = $proc.StandardOutput.ReadToEnd() $stderr = $proc.StandardError.ReadToEnd() $proc.WaitForExit() - if($proc.ExitCode -ne 0) { + if(($proc.ExitCode -ne 0) -and (-not $quiet)) { Write-Host "[err] stdout:`n$stdout" Write-Host "[err] stderr:`n$stderr" Write-Host "[err] cmd:`n$cmd" @@ -123,3 +124,18 @@ function Run-Program($cmd, $params) { return $proc.ExitCode, $stdout, $stderr } + +function BuildNcatImage($windowsVersionTag) { + Write-Host "Building nmap image (Windows version '${windowsVersionTag}') for testing" + $exitCode, $stdout, $stderr = Run-Program 'docker.exe' "inspect --type=image nmap" $true + if($exitCode -ne 0) { + Push-Location -StackName 'agent' -Path "$PSScriptRoot/.." + $exitCode, $stdout, $stderr = Run-Program 'docker.exe' "build -t nmap --build-arg `"WINDOWS_VERSION_TAG=${windowsVersionTag}`" -f ./tests/netcat-helper/Dockerfile-windows ./tests/netcat-helper" + $exitCode | Should -Be 0 + Pop-Location -StackName 'agent' + } +} + +function CleanupNetwork($name) { + docker network rm $name 2>&1 | Out-Null +} diff --git a/tests/tests_agent.bats b/tests/tests_agent.bats index 451a41788..def0c679c 100755 --- a/tests/tests_agent.bats +++ b/tests/tests_agent.bats @@ -4,7 +4,7 @@ load test_helpers load 'test_helper/bats-support/load' # this is required by bats-assert! load 'test_helper/bats-assert/load' -IMAGE=${IMAGE:-debian_jdk11} +IMAGE=${IMAGE:-debian_jdk17} SUT_IMAGE=$(get_sut_image) ARCH=${ARCH:-x86_64} @@ -99,19 +99,19 @@ ARCH=${ARCH:-x86_64} local TEST_AGENT_WORKDIR="/home/test-user/something" local sut_image="${SUT_IMAGE}-tests-${BATS_TEST_NUMBER}" -# false positive detecting platform -# shellcheck disable=SC2140 -docker buildx bake \ - --set "${IMAGE}".args.VERSION="${TEST_VERSION}" \ - --set "${IMAGE}".args.user="${TEST_USER}" \ - --set "${IMAGE}".args.group="${TEST_GROUP}" \ - --set "${IMAGE}".args.uid="${TEST_UID}" \ - --set "${IMAGE}".args.gid="${TEST_GID}" \ - --set "${IMAGE}".args.AGENT_WORKDIR="${TEST_AGENT_WORKDIR}" \ - --set "${IMAGE}".platform="linux/${ARCH}" \ - --set "${IMAGE}".tags="${sut_image}" \ - --load `# Image should be loaded on the Docker engine`\ - "${IMAGE}" + # false positive detecting platform + # shellcheck disable=SC2140 + docker buildx bake \ + --set "${IMAGE}".args.VERSION="${TEST_VERSION}" \ + --set "${IMAGE}".args.user="${TEST_USER}" \ + --set "${IMAGE}".args.group="${TEST_GROUP}" \ + --set "${IMAGE}".args.uid="${TEST_UID}" \ + --set "${IMAGE}".args.gid="${TEST_GID}" \ + --set "${IMAGE}".args.AGENT_WORKDIR="${TEST_AGENT_WORKDIR}" \ + --set "${IMAGE}".platform="linux/${ARCH}" \ + --set "${IMAGE}".tags="${sut_image}" \ + --load `# Image should be loaded on the Docker engine`\ + "${IMAGE}" local cid cid="$(docker run -d -it -P "${sut_image}" /bin/sh)" diff --git a/tests/tests_inbound-agent.bats b/tests/tests_inbound-agent.bats index 30dc0bbab..c07ccf588 100755 --- a/tests/tests_inbound-agent.bats +++ b/tests/tests_inbound-agent.bats @@ -45,23 +45,19 @@ SUT_IMAGE="$(get_sut_image)" @test "[${SUT_IMAGE}] use build args correctly" { cd "${BATS_TEST_DIRNAME}"/.. || false - local TEST_VERSION PARENT_IMAGE_VERSION_SUFFIX ARG_TEST_VERSION TEST_USER sut_image sut_cid + local TEST_VERSION TEST_USER sut_image sut_cid # Old version used to test overriding the build arguments. - # This old version must have the same tag suffixes as the ones defined in the docker-bake file (`-jdk17`, `jdk11`, etc.) TEST_VERSION="3180.v3dd999d24861" - PARENT_IMAGE_VERSION_SUFFIX="2" - - ARG_TEST_VERSION="${TEST_VERSION}-${PARENT_IMAGE_VERSION_SUFFIX}" TEST_USER="root" sut_image="${SUT_IMAGE}-tests-${BATS_TEST_NUMBER}" docker buildx bake \ - --set "${IMAGE}".args.version="${ARG_TEST_VERSION}" \ - --set "${IMAGE}".args.user="${TEST_USER}" \ - --set "${IMAGE}".platform=linux/"${ARCH}" \ - --set "${IMAGE}".tags="${sut_image}" \ + --set "${IMAGE}.args.VERSION=${TEST_VERSION}" \ + --set "${IMAGE}.args.user=${TEST_USER}" \ + --set "${IMAGE}.platform=linux/${ARCH}" \ + --set "${IMAGE}.tags=${sut_image}" \ --load \ "${IMAGE}" @@ -69,7 +65,7 @@ SUT_IMAGE="$(get_sut_image)" is_agent_container_running "${sut_cid}" - run docker exec "${sut_cid}" sh -c "java -cp /usr/share/jenkins/agent.jar hudson.remoting.jnlp.Main -version" + run docker exec "${sut_cid}" sh -c "java -jar /usr/share/jenkins/agent.jar -version" [ "${TEST_VERSION}" = "${lines[0]}" ] run docker exec "${AGENT_CONTAINER}" sh -c "id -u -n ${TEST_USER}" diff --git a/windows/nanoserver/Dockerfile b/windows/nanoserver/Dockerfile index 8dd900c28..ecd95f994 100644 --- a/windows/nanoserver/Dockerfile +++ b/windows/nanoserver/Dockerfile @@ -29,7 +29,8 @@ FROM eclipse-temurin:"${JAVA_VERSION}"-jdk-windowsservercore-"${TOOLS_WINDOWS_VE FROM mcr.microsoft.com/powershell:nanoserver-"${TOOLS_WINDOWS_VERSION}" AS pwsh-source -FROM mcr.microsoft.com/windows/nanoserver:"${WINDOWS_VERSION_TAG}" +## Agent image target +FROM mcr.microsoft.com/windows/nanoserver:"${WINDOWS_VERSION_TAG}" AS agent ARG JAVA_HOME="C:\openjdk-17" ENV PSHOME="C:\Program Files\PowerShell" @@ -114,3 +115,19 @@ LABEL ` org.opencontainers.image.url="https://www.jenkins.io/" ` org.opencontainers.image.source="https://github.com/jenkinsci/docker-agent" ` org.opencontainers.image.licenses="MIT" + +## Inbound Agent image target +FROM agent AS inbound-agent + +COPY jenkins-agent.ps1 C:/ProgramData/Jenkins + +LABEL ` + org.opencontainers.image.vendor="Jenkins project" ` + org.opencontainers.image.title="Official Jenkins Inbound Agent Base Docker image" ` + org.opencontainers.image.description="This is an image for Jenkins agents using TCP or WebSockets to establish inbound connection to the Jenkins controller" ` + org.opencontainers.image.version="${VERSION}" ` + org.opencontainers.image.url="https://www.jenkins.io/" ` + org.opencontainers.image.source="https://github.com/jenkinsci/docker-agent-inbound" ` + org.opencontainers.image.licenses="MIT" + +ENTRYPOINT ["pwsh.exe", "-f", "C:/ProgramData/Jenkins/jenkins-agent.ps1"] diff --git a/windows/windowsservercore/Dockerfile b/windows/windowsservercore/Dockerfile index 8f4b71903..622a605db 100644 --- a/windows/windowsservercore/Dockerfile +++ b/windows/windowsservercore/Dockerfile @@ -27,7 +27,8 @@ ARG WINDOWS_VERSION_TAG=ltsc2019 ARG TOOLS_WINDOWS_VERSION=1809 FROM eclipse-temurin:"${JAVA_VERSION}"-jdk-windowsservercore-"${TOOLS_WINDOWS_VERSION}" AS jdk-core -FROM mcr.microsoft.com/windows/servercore:"${WINDOWS_VERSION_TAG}" +## Agent image target +FROM mcr.microsoft.com/windows/servercore:"${WINDOWS_VERSION_TAG}" AS agent ARG JAVA_HOME="C:\openjdk-17" ENV JAVA_HOME=${JAVA_HOME} @@ -107,3 +108,19 @@ LABEL ` org.opencontainers.image.url="https://www.jenkins.io/" ` org.opencontainers.image.source="https://github.com/jenkinsci/docker-agent" ` org.opencontainers.image.licenses="MIT" + +## Inbound Agent image target +FROM agent AS inbound-agent + +COPY jenkins-agent.ps1 C:/ProgramData/Jenkins + +LABEL ` + org.opencontainers.image.vendor="Jenkins project" ` + org.opencontainers.image.title="Official Jenkins Inbound Agent Base Docker image" ` + org.opencontainers.image.description="This is an image for Jenkins agents using TCP or WebSockets to establish inbound connection to the Jenkins controller" ` + org.opencontainers.image.version="${VERSION}" ` + org.opencontainers.image.url="https://www.jenkins.io/" ` + org.opencontainers.image.source="https://github.com/jenkinsci/docker-agent-inbound" ` + org.opencontainers.image.licenses="MIT" + +ENTRYPOINT ["powershell.exe", "-f", "C:/ProgramData/Jenkins/jenkins-agent.ps1"]