Skip to content

Commit

Permalink
Merge pull request jenkinsci#154 from slide/directory_changes
Browse files Browse the repository at this point in the history
Rework directory structure and Windows containers + Update Windows parent image versions
  • Loading branch information
oleg-nenashev committed Apr 11, 2020
2 parents 2ea457d + 8a221ef commit 260bb8c
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 93 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ To run a Docker container with [Work Directory](https://github.com/jenkinsci/rem
docker run --init jenkins/jnlp-slave -url http://jenkins-server:port -workDir=/home/jenkins/agent <secret> <agent name>

Windows agent:

docker run jenkins/jnlp-agent-windows -Url http://jenkins-server:port -WorkDir=C:/Jenkins/agent -Secret <secret> -Name <agent name>

Optional environment variables:
Expand Down
139 changes: 76 additions & 63 deletions jenkins-agent.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@

[CmdletBinding()]
Param(
$Cmd = '', # this is only used when docker run has one arg positional arg
$Url = '',
$Secret = '',
$Name = '',
$Cmd = '', # this is only used when docker run has one arg positional arg
$Url = $( if([System.String]::IsNullOrWhiteSpace($Cmd) -and [System.String]::IsNullOrWhiteSpace($env:JENKINS_URL)) { throw ("Url is required") } else { '' } ),
$Secret = $( if([System.String]::IsNullOrWhiteSpace($Cmd) -and [System.String]::IsNullOrWhiteSpace($env:JENKINS_SECRET)) { throw ("Secret is required") } else { '' } ),
$Name = $( if([System.String]::IsNullOrWhiteSpace($Cmd) -and [System.String]::IsNullOrWhiteSpace($env:JENKINS_AGENT_NAME)) { throw ("Name is required") } else { '' } ),
$Tunnel = '',
$WorkDir = 'C:/Users/jenkins/Agent',
$WebSocket = '',
$WorkDir = '',
[switch] $WebSocket = $false,
$DirectConnection = '',
$InstanceIdentity = '',
$Protocols = '',
$JavaHome = $env:JAVA_HOME
)

Expand All @@ -40,79 +43,89 @@ Param(
# * JENKINS_AGENT_NAME : agent name, if not set as an argument
# * JENKINS_AGENT_WORKDIR : agent work directory, if not set by optional parameter -workDir
# * JENKINS_WEB_SOCKET : true if the connection should be made via WebSocket rather than TCP
# * JENKINS_DIRECT_CONNECTION: Connect directly to this TCP agent port, skipping the HTTP(S) connection parameter download.
# Value: "<HOST>:<PORT>"
# * JENKINS_INSTANCE_IDENTITY: The base64 encoded InstanceIdentity byte array of the Jenkins master. When this is set,
# the agent skips connecting to an HTTP(S) port for connection info.
# * JENKINS_PROTOCOLS: Specify the remoting protocols to attempt when instanceIdentity is provided.

if(![System.String]::IsNullOrWhiteSpace($Cmd)) {
# if `docker run` only has one arguments, we assume user is running alternate command like `bash` to inspect the image
Invoke-Expression "$Cmd"
} else {
# if -Tunnel is not provided, try env vars
if(![System.String]::IsNullOrWhiteSpace($env:JENKINS_TUNNEL)) {
if(![System.String]::IsNullOrWhiteSpace($Tunnel)) {
Write-Warning "Tunnel is defined twice; in command-line arguments and the environment variable"
}
$Tunnel = $($env:JENKINS_TUNNEL).Trim()
}
$Tunnel = $Tunnel.Trim()
if(![System.String]::IsNullOrWhiteSpace($Tunnel)) {
$Tunnel = " -tunnel `"$Tunnel`""
}
$AgentArguments = @("-cp", "C:/ProgramData/Jenkins/agent.jar", "hudson.remoting.jnlp.Main", "-headless")

# if -WorkDir is not provided, try env vars
if(![System.String]::IsNullOrWhiteSpace($env:JENKINS_AGENT_WORKDIR)) {
if(![System.String]::IsNullOrWhiteSpace($WorkDir)) {
Write-Warning "Work directory is defined twice; in command-line arguments and the environment variable"
}
$WorkDir = $env:JENKINS_AGENT_WORKDIR
}
$WorkDir = $WorkDir.Trim()
if(![System.String]::IsNullOrWhiteSpace($WorkDir)) {
$WorkDir = " -workDir `"$WorkDir`""
}
# this maps the variable name from th CmdletBinding to environment variables
$ParamMap = @{
'Tunnel' = 'JENKINS_TUNNEL';
'Url' = 'JENKINS_URL';
'Secret' = 'JENKINS_SECRET';
'Name' = 'JENKINS_AGENT_NAME';
'WorkDir' = 'JENKINS_AGENT_WORKDIR';
'WebSocket' = 'JENKINS_WEB_SOCKET';
'DirectConnection' = 'JENKINS_DIRECT_CONNECTION';
'InstanceIdentity' = 'JENKINS_INSTANCE_IDENTITY';
'Protocols' = 'JENKINS_PROTOCOLS';
}

if($Env:JENKINS_WEB_SOCKET -eq "true") {
$WebSocket = " -webSocket"
}
# this does some trickery to update the variable from the CmdletBinding
# with the value of the
foreach($p in $ParamMap.Keys) {
$var = Get-Variable $p
$envVar = Get-ChildItem -Path "env:$($ParamMap[$p])" -ErrorAction 'SilentlyContinue'

# if -Url is not provided, try env vars
if(![System.String]::IsNullOrWhiteSpace($env:JENKINS_URL)) {
if(![System.String]::IsNullOrWhiteSpace($Url)) {
Write-Warning "Url is defined twice; in command-line arguments and the environment variable"
}
$Url = $($env:JENKINS_URL).Trim()
}
$Url = $Url.Trim()
if(![System.String]::IsNullOrWhiteSpace($Url)) {
$Url = " -url `"$Url`""
}
if(($null -ne $envVar) -and ((($envVar.Value -is [System.String]) -and (![System.String]::IsNullOrWhiteSpace($envVar.Value))) -or ($null -ne $envVar.Value))) {
if(($null -ne $var) -and ((($var.Value -is [System.String]) -and (![System.String]::IsNullOrWhiteSpace($var.Value))))) {
Write-Warning "${p} is defined twice; in command-line arguments (-${p}) and in the environment variable ${envVar.Name}"
}
if($var.Value -is [System.String]) {
$var.Value = $envVar.Value
} elseif($var.Value -is [System.Management.Automation.SwitchParameter]) {
$var.Value = [bool]$envVar.Value
}
}
if($var.Value -is [System.String]) {
$var.Value = $var.Value.Trim()
}
}

# if -Name is not provided, try env vars
if(![System.String]::IsNullOrWhiteSpace($env:JENKINS_NAME)) {
if(![System.String]::IsNullOrWhiteSpace($Name)) {
Write-Warning "Name is defined twice; in command-line arguments and the environment variable"
}
$Name = $env:JENKINS_NAME
}
$Name = $Name.Trim()
if(![System.String]::IsNullOrWhiteSpace($Tunnel)) {
$AgentArguments += @("-tunnel", "`"$Tunnel`"")
}

if(![System.String]::IsNullOrWhiteSpace($WorkDir)) {
$AgentArguments += @("-workDir", "`"$WorkDir`"")
} else {
$AgentArguments += @("-workDir", "`"C:/Users/jenkins/Work`"")
}

if($WebSocket) {
$AgentArguments += @("-webSocket")
}

if(![System.String]::IsNullOrWhiteSpace($Url)) {
$AgentArguments += @("-url", "`"$Url`"")
}

if(![System.String]::IsNullOrWhiteSpace($DirectConnection)) {
$AgentArguments += @('-direct', $DirectConnection)
}

if(![System.String]::IsNullOrWhiteSpace($InstanceIdentity)) {
$AgentArguments += @('-instanceIdentity', $InstanceIdentity)
}

# these need to be the last things added since they are positional
# parameters to agent.jar
$AgentArguments += @($Secret, $Name)

# if java home is defined, use it
$JAVA_BIN="java.exe"
if(![System.String]::IsNullOrWhiteSpace($JavaHome)) {
$JAVA_BIN="$JavaHome/bin/java.exe"
}

# if -Url is not provided, try env vars
if(![System.String]::IsNullOrWhiteSpace($env:JENKINS_SECRET)) {
if(![System.String]::IsNullOrWhiteSpace($Secret)) {
Write-Warning "Secret is defined twice; in command-line arguments and the environment variable"
}
$Secret = $env:JENKINS_SECRET
}
$Secret = $Secret.Trim()
if(![System.String]::IsNullOrWhiteSpace($Secret)) {
$Secret = " $Secret"
}

#TODO: Handle the case when the command-line and Environment variable contain different values.
#It is fine it blows up for now since it should lead to an error anyway.
Start-Process -FilePath $JAVA_BIN -Wait -NoNewWindow -ArgumentList $("-cp C:/ProgramData/Jenkins/agent.jar hudson.remoting.jnlp.Main -headless$Tunnel$Url$WorkDir$WebSocket$Secret $Name")
Start-Process -FilePath $JAVA_BIN -Wait -NoNewWindow -ArgumentList $AgentArguments
}
40 changes: 40 additions & 0 deletions tests/netcat-helper/Dockerfile-windows
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# escape=`

# The MIT License
#
# Copyright (c) 2019-2020, Alex Earl 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.

FROM mcr.microsoft.com/windows/servercore:1809

SHELL ["powershell.exe", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]

ARG NMAP_VERSION=7.80
ENV NMAP_VERSION $NMAP_VERSION

RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; `
$url = $('https://nmap.org/dist/nmap-{0}-setup.exe' -f $env:NMAP_VERSION) ; `
Write-Host "Retrieving $url..." ; `
Invoke-WebRequest $url -OutFile 'nmap-install.exe' -UseBasicParsing ; `
$proc = Start-Process "C:\nmap-install.exe" -PassThru -ArgumentList '/S' ; `
$proc.WaitForExit() ; `
Remove-Item -Path nmap-install.exe


68 changes: 39 additions & 29 deletions tests/tests.bats
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
#!/usr/bin/env bats

DOCKERFILE=Dockerfile
JDK=8
SLAVE_IMAGE=jenkins-jnlp-slave
SLAVE_CONTAINER=bats-jenkins-jnlp-slave
AGENT_IMAGE=jenkins-jnlp-agent
AGENT_CONTAINER=bats-jenkins-jnlp-agent
NETCAT_HELPER_CONTAINER=netcat-helper

if [[ -z "${FLAVOR}" ]]
REGEX='^([0-9]+)/(.+)$'

REAL_FOLDER=$(realpath "${BATS_TEST_DIRNAME}/../${FOLDER}")

if [[ ${FOLDER} =~ ${REGEX} ]] && [[ -d "${REAL_FOLDER}" ]]
then
FLAVOR="debian"
elif [[ "${FLAVOR}" = "jdk11" ]]
JDK="${BASH_REMATCH[1]}"
FLAVOR="${BASH_REMATCH[2]}"
else
echo "Wrong folder format or folder does not exist: ${FOLDER}"
exit 1
fi

if [[ "${JDK}" = "11" ]]
then
DOCKERFILE+="-jdk11"
JDK=11
SLAVE_IMAGE+=":jdk11"
SLAVE_CONTAINER+="-jdk11"
AGENT_IMAGE+=":jdk11"
AGENT_CONTAINER+="-jdk11"
else
DOCKERFILE+="-alpine"
SLAVE_IMAGE+=":alpine"
SLAVE_CONTAINER+="-alpine"
if [[ "${FLAVOR}" = "alpine*" ]]
then
AGENT_IMAGE+=":alpine"
AGENT_CONTAINER+="-alpine"
else
AGENT_IMAGE+=":latest"
fi
fi

load test_helpers
Expand All @@ -31,29 +41,29 @@ function teardown () {
clean_test_container
}

@test "[${FLAVOR}] build image" {
@test "[${JDK} ${FLAVOR}] build image" {
cd "${BATS_TEST_DIRNAME}"/.. || false
docker build -t "${SLAVE_IMAGE}" -f "${DOCKERFILE}" .
docker build -t "${AGENT_IMAGE}" ${FOLDER}
}

@test "[${FLAVOR}] image has installed jenkins-agent in PATH" {
docker run -d -it --name "${SLAVE_CONTAINER}" -P "${SLAVE_IMAGE}" /bin/bash
@test "[${JDK} ${FLAVOR}] image has installed jenkins-agent in PATH" {
docker run -d -it --name "${AGENT_CONTAINER}" -P "${AGENT_IMAGE}" /bin/bash

is_slave_container_running

run docker exec "${SLAVE_CONTAINER}" which jenkins-slave
[ "/usr/local/bin/jenkins-slave" = "${lines[0]}" ]
run docker exec "${AGENT_CONTAINER}" which jenkins-agent
[ "/usr/local/bin/jenkins-agent" = "${lines[0]}" ]

run docker exec "${SLAVE_CONTAINER}" which jenkins-agent
run docker exec "${AGENT_CONTAINER}" which jenkins-agent
[ "/usr/local/bin/jenkins-agent" = "${lines[0]}" ]
}

@test "[${FLAVOR}] image starts jenkins-agent correctly (slow test)" {
@test "[${JDK} ${FLAVOR}] image starts jenkins-agent correctly (slow test)" {
# Spin off a helper image which contains netcat
docker run -d -it --name netcat-helper netcat-helper:latest /bin/sh

# Run jenkins agent which tries to connect to the netcat-helper container at port 5000
docker run -d --link netcat-helper --name "${SLAVE_CONTAINER}" "${SLAVE_IMAGE}" -url http://netcat-helper:5000 aaa bbb
docker run -d --link netcat-helper --name "${AGENT_CONTAINER}" "${AGENT_IMAGE}" -url http://netcat-helper:5000 aaa bbb

# Launch the netcat utility, listening at port 5000 for 30 sec
# bats will capture the output from netcat and compare the first line
Expand All @@ -64,7 +74,7 @@ function teardown () {
[ $'GET /tcpSlaveAgentListener/ HTTP/1.1\r' = "${lines[0]}" ]
}

@test "[${FLAVOR}] use build args correctly" {
@test "[${JDK} ${FLAVOR}] use build args correctly" {
cd "${BATS_TEST_DIRNAME}"/.. || false

local ARG_TEST_VERSION
Expand All @@ -84,16 +94,16 @@ function teardown () {
docker build \
--build-arg "version=${ARG_TEST_VERSION}" \
--build-arg "user=${TEST_USER}" \
-t "${SLAVE_IMAGE}" \
-f "${DOCKERFILE}" .
-t "${AGENT_IMAGE}" \
${FOLDER}

docker run -d -it --name "${SLAVE_CONTAINER}" -P "${SLAVE_IMAGE}" /bin/sh
docker run -d -it --name "${AGENT_CONTAINER}" -P "${AGENT_IMAGE}" /bin/sh

is_slave_container_running

run docker exec "${SLAVE_CONTAINER}" sh -c "java -cp /usr/share/jenkins/agent.jar hudson.remoting.jnlp.Main -version"
run docker exec "${AGENT_CONTAINER}" sh -c "java -cp /usr/share/jenkins/agent.jar hudson.remoting.jnlp.Main -version"
[ "${TEST_VERSION}" = "${lines[0]}" ]

run docker exec "${SLAVE_CONTAINER}" sh -c "id -u -n ${TEST_USER}"
run docker exec "${AGENT_CONTAINER}" sh -c "id -u -n ${TEST_USER}"
[ "${TEST_USER}" = "${lines[0]}" ]
}

0 comments on commit 260bb8c

Please sign in to comment.