Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: improve Windows tests debug and small code cleanup #396

Merged
merged 3 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pipeline {
label agentSelector(env.IMAGE_TYPE)
}
options {
timeout(time: 30, unit: 'MINUTES')
timeout(time: 60, unit: 'MINUTES')
}
environment {
DOCKERHUB_ORGANISATION = "${infra.isTrusted() ? 'jenkins' : 'jenkins4eval'}"
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,23 @@ make bats
make: 'bats' is up to date.
```

### Building and testing on Windows

From a Powershell console, set first the `IMAGE_TYPE` environment variable defining the Windows flavor ("nanoserver"/"windowsservercore") and version you want to build.

For example:
```
New-Item -Path env:IMAGE_TYPE -Value "nanoserver-ltsc2019"
```

Then run `.\build.ps1` to launch the build of the images for each jexdk specified in the build-windows.yaml docker compose file.

Run `.\build.ps1 test` if you also want to run the tests harness suit.
Run `.\build.ps1 test -TestsDebug 'debug'` to also get commands & stderr of tests, displayed on top of them.
You can set it to 'verbose' to also get stdout of every test command.

Finally, instead of passing -TestsDebug parameter to build.ps1, you can also set the desired value to $env:TESTS_DEBUG.

## Changelog

See [GitHub Releases](https://github.com/jenkinsci/docker-ssh-agent/releases/latest).
Expand Down
85 changes: 49 additions & 36 deletions build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,29 @@ Param(
[String] $Target = 'build',
[String] $Build = '',
[String] $VersionTag = '1.0-1',
[switch] $DryRun = $false
[switch] $DryRun = $false,
# Output debug info for tests. Accepted values:
# - empty (no additional test output)
# - 'debug' (test cmd & stderr outputed)
# - 'verbose' (test cmd, stderr, stdout outputed)
[String] $TestsDebug = ''
)

$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue' # Disable Progress bar for faster downloads

$Repository = 'ssh-agent'
$Organisation = 'jenkins'
$ImageType = 'windows-ltsc2019'

$baseDockerCmd = 'docker-compose --file=build-windows.yaml'
$baseDockerBuildCmd = '{0} build --parallel --pull' -f $baseDockerCmd

if(![String]::IsNullOrWhiteSpace($env:TESTS_DEBUG)) {
$ImageType = $env:IMAGE_TYPE
}
$env:TESTS_DEBUG = $TestsDebug

if(![String]::IsNullOrWhiteSpace($env:DOCKERHUB_REPO)) {
$Repository = $env:DOCKERHUB_REPO
}
Expand All @@ -24,6 +39,20 @@ if(![String]::IsNullOrWhiteSpace($env:IMAGE_TYPE)) {
$ImageType = $env:IMAGE_TYPE
}

# Ensure constant env vars used in the docker compose file are defined
$env:DOCKERHUB_ORGANISATION = "$Organisation"
$env:DOCKERHUB_REPO = "$Repository"
$env:VERSION = "$VersionTag"

$items = $ImageType.Split('-')
$env:WINDOWS_FLAVOR = $items[0]
$env:WINDOWS_VERSION_TAG = $items[1]
$env:TOOLS_WINDOWS_VERSION = $items[1]
if ($items[1] -eq 'ltsc2019') {
# There are no eclipse-temurin:*-ltsc2019 or mcr.microsoft.com/powershell:*-ltsc2019 docker images unfortunately, only "1809" ones
$env:TOOLS_WINDOWS_VERSION = '1809'
}

# Check for required commands
Function Test-CommandExists {
# From https://devblogs.microsoft.com/scripting/use-a-powershell-function-to-see-if-a-command-exists/
Expand All @@ -46,25 +75,9 @@ Function Test-CommandExists {
}
}

# Ensure constant env vars used in the docker compose file are defined
$env:DOCKERHUB_ORGANISATION = "$Organisation"
$env:DOCKERHUB_REPO = "$Repository"
$env:VERSION = "$VersionTag"

$items = $ImageType.Split("-")
$env:WINDOWS_FLAVOR = $items[0]
$env:WINDOWS_VERSION_TAG = $items[1]
$env:TOOLS_WINDOWS_VERSION = $items[1]
if ($items[1] -eq 'ltsc2019') {
# There are no eclipse-temurin:*-ltsc2019 or mcr.microsoft.com/powershell:*-ltsc2019 docker images unfortunately, only "1809" ones
$env:TOOLS_WINDOWS_VERSION = '1809'
}

$ProgressPreference = 'SilentlyContinue' # Disable Progress bar for faster downloads

Test-CommandExists "docker"
Test-CommandExists "docker-compose"
Test-CommandExists "yq"
Test-CommandExists 'docker'
Test-CommandExists 'docker-compose'
Test-CommandExists 'yq'

function Test-Image {
param (
Expand Down Expand Up @@ -104,27 +117,27 @@ $baseDockerBuildCmd = '{0} build --parallel --pull' -f $baseDockerCmd
Write-Host "= PREPARE: List of $Organisation/$env:DOCKERHUB_REPO images and tags to be processed:"
Invoke-Expression "$baseDockerCmd config"

Write-Host "= BUILD: Building all images..."
Write-Host '= BUILD: Building all images...'
switch ($DryRun) {
$true { Write-Host "(dry-run) $baseDockerBuildCmd" }
$false { Invoke-Expression $baseDockerBuildCmd }
}
Write-Host "= BUILD: Finished building all images."
Write-Host '= BUILD: Finished building all images.'

if($lastExitCode -ne 0) {
exit $lastExitCode
}

if($target -eq "test") {
if($target -eq 'test') {
if ($DryRun) {
Write-Host "= TEST: (dry-run) test harness"
Write-Host '= TEST: (dry-run) test harness'
} else {
Write-Host "= TEST: Starting test harness"
Write-Host '= TEST: Starting test harness'

$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"
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
Expand All @@ -135,7 +148,7 @@ if($target -eq "test") {
}

Import-Module Pester
Write-Host "= TEST: Setting up Pester environment..."
Write-Host '= TEST: Setting up Pester environment...'
$configuration = [PesterConfiguration]::Default
$configuration.Run.PassThru = $true
$configuration.Run.Path = '.\tests'
Expand All @@ -145,7 +158,7 @@ if($target -eq "test") {
$configuration.Output.Verbosity = 'Diagnostic'
$configuration.CodeCoverage.Enabled = $false

Write-Host "= TEST: Testing all ${agentType} images..."
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 {
Expand All @@ -154,31 +167,31 @@ if($target -eq "test") {

# Fail if any test failures
if($testFailed -ne $false) {
Write-Error "= TEST: stage failed!"
Write-Error '= TEST: stage failed!'
exit 1
} else {
Write-Host "= TEST: stage passed!"
Write-Host '= TEST: stage passed!'
}
}
}

if($target -eq "publish") {
Write-Host "= PUBLISH: push all images and tags"
if($target -eq 'publish') {
Write-Host '= PUBLISH: push all images and tags'
switch($DryRun) {
$true { Write-Host "(dry-run) $baseDockerCmd push" }
$false { Invoke-Expression "$baseDockerCmd push" }
}

# Fail if any issues when publising the docker images
if($lastExitCode -ne 0) {
Write-Error "= PUBLISH: failed!"
Write-Error '= PUBLISH: failed!'
exit 1
}
}

if($lastExitCode -ne 0) {
Write-Error "Build failed!"
Write-Error 'Build failed!'
} else {
Write-Host "Build finished successfully"
Write-Host 'Build finished successfully'
}
exit $lastExitCode
48 changes: 27 additions & 21 deletions tests/sshAgent.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ Import-Module -DisableNameChecking -Force $PSScriptRoot/test_helpers.psm1

$global:IMAGE_NAME = Get-EnvOrDefault 'IMAGE_NAME' '' # Ex: jenkins4eval/ssh-agent:nanoserver-1809-jdk17

$imageItems = $global:IMAGE_NAME.Split(":")
$imageItems = $global:IMAGE_NAME.Split(':')
$global:IMAGE_TAG = $imageItems[1]

$items = $global:IMAGE_TAG.Split("-")
$items = $global:IMAGE_TAG.Split('-')
# Remove the 'jdk' prefix
$global:JAVAMAJORVERSION = $items[2].Remove(0,3)
$global:WINDOWSFLAVOR = $items[0]
Expand All @@ -19,12 +19,12 @@ if ($items[1] -eq 'ltsc2019') {
# TODO: make this name unique for concurency
$global:CONTAINERNAME = 'pester-jenkins-ssh-agent-{0}' -f $global:IMAGE_TAG

$global:CONTAINERSHELL = "powershell.exe"
$global:CONTAINERSHELL = 'powershell.exe'
if($global:WINDOWSFLAVOR -eq 'nanoserver') {
$global:CONTAINERSHELL = "pwsh.exe"
$global:CONTAINERSHELL = 'pwsh.exe'
}

$global:PUBLIC_SSH_KEY = "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAvnRN27LdPPQq2OH3GiFFGWX/SH5TCPVePLR21ngMFV8nAthXgYrFkRi/t+Wafe3ByTu2XYUDlXHKGIPIoAKo4gz5dIjUFfoac1ZuCDIbEiqPEjkk4tkfc2qr/BnIZsOYQi4Mbu+Z40VZEsAQU7eBinnZaHE1qGMHjS1xfrRtp2rdeO1EBz92FJ8dfnkUnohTXo3qPVSFGIPbh7UKEoKcyCosRO1P41iWD1rVsH1SLLXYAh2t49L7IPiplg09Dep6H47LyQVbxU9eXY8yMtUrRuwEk9IUX/IqpxNhk5hngHPP3JjsP0hyyrYSPkZlbs3izd9kk3y09Wn/ElHidiEk0Q=="
$global:PUBLIC_SSH_KEY = 'ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAvnRN27LdPPQq2OH3GiFFGWX/SH5TCPVePLR21ngMFV8nAthXgYrFkRi/t+Wafe3ByTu2XYUDlXHKGIPIoAKo4gz5dIjUFfoac1ZuCDIbEiqPEjkk4tkfc2qr/BnIZsOYQi4Mbu+Z40VZEsAQU7eBinnZaHE1qGMHjS1xfrRtp2rdeO1EBz92FJ8dfnkUnohTXo3qPVSFGIPbh7UKEoKcyCosRO1P41iWD1rVsH1SLLXYAh2t49L7IPiplg09Dep6H47LyQVbxU9eXY8yMtUrRuwEk9IUX/IqpxNhk5hngHPP3JjsP0hyyrYSPkZlbs3izd9kk3y09Wn/ElHidiEk0Q=='
$global:PRIVATE_SSH_KEY = @"
-----BEGIN RSA PRIVATE KEY-----
MIIEoQIBAAKCAQEAvnRN27LdPPQq2OH3GiFFGWX/SH5TCPVePLR21ngMFV8nAthX
Expand Down Expand Up @@ -68,12 +68,13 @@ Describe "[$global:IMAGE_NAME] image is present" {

Describe "[$global:IMAGE_NAME] image has setup-sshd.ps1 in the correct location" {
BeforeAll {
docker run --detach --tty --name "$global:CONTAINERNAME" --publish-all "$global:IMAGE_NAME" "$global:CONTAINERSHELL"
$exitCode, $stdout, $stderr = Run-Program 'docker' "run --detach --tty --name=`"$global:CONTAINERNAME`" --publish-all `"$global:IMAGE_NAME`" `"$global:CONTAINERSHELL`""
$exitCode | Should -Be 0
Is-ContainerRunning $global:CONTAINERNAME | Should -BeTrue
}

It 'has setup-sshd.ps1 in C:/ProgramData/Jenkins' {
$exitCode, $stdout, $stderr = Run-Program 'docker' "exec $global:CONTAINERNAME $global:CONTAINERSHELL -C `"if(Test-Path C:/ProgramData/Jenkins/setup-sshd.ps1) { exit 0 } else { exit 1}`"" $global:TESTS_DEBUG
$exitCode, $stdout, $stderr = Run-Program 'docker' "exec $global:CONTAINERNAME $global:CONTAINERSHELL -C `"if(Test-Path C:/ProgramData/Jenkins/setup-sshd.ps1) { exit 0 } else { exit 1}`""
$exitCode | Should -Be 0
}

Expand All @@ -84,38 +85,39 @@ Describe "[$global:IMAGE_NAME] image has setup-sshd.ps1 in the correct location"

Describe "[$global:IMAGE_NAME] checking image metadata" {
It 'has correct volumes' {
$exitCode, $stdout, $stderr = Run-Program 'docker' "inspect --format '{{.Config.Volumes}}' $global:IMAGE_NAME" $global:TESTS_DEBUG
$exitCode, $stdout, $stderr = Run-Program 'docker' "inspect --format '{{.Config.Volumes}}' $global:IMAGE_NAME"
$exitCode | Should -Be 0

$stdout | Should -Match 'C:/Users/jenkins/AppData/Local/Temp'
$stdout | Should -Match 'C:/Users/jenkins/Work'
}

It 'has the source GitHub URL in docker metadata' {
$exitCode, $stdout, $stderr = Run-Program 'docker' "inspect --format=`"{{index .Config.Labels \`"org.opencontainers.image.source\`"}}`" $global:IMAGE_NAME" $global:TESTS_DEBUG
$exitCode, $stdout, $stderr = Run-Program 'docker' "inspect --format=`"{{index .Config.Labels \`"org.opencontainers.image.source\`"}}`" $global:IMAGE_NAME"
$exitCode | Should -Be 0
$stdout.Trim() | Should -Match 'https://github.com/jenkinsci/docker-ssh-agent'
}
}

Describe "[$global:IMAGE_NAME] image has correct version of java and git-lfs installed and in the PATH" {
BeforeAll {
docker run --detach --tty --name="$global:CONTAINERNAME" --publish-all "$global:IMAGE_NAME" $global:CONTAINERSHELL
$exitCode, $stdout, $stderr = Run-Program 'docker' "run --detach --tty --name=`"$global:CONTAINERNAME`" --publish-all `"$global:IMAGE_NAME`" `"$global:PUBLIC_SSH_KEY`""
$exitCode | Should -Be 0
Is-ContainerRunning $global:CONTAINERNAME
}

It 'has java installed and in the path' {
$exitCode, $stdout, $stderr = Run-Program 'docker' "exec $global:CONTAINERNAME $global:CONTAINERSHELL -C `"if(`$null -eq (Get-Command java.exe -ErrorAction SilentlyContinue)) { exit -1 } else { exit 0 }`"" $global:TESTS_DEBUG
$exitCode, $stdout, $stderr = Run-Program 'docker' "exec $global:CONTAINERNAME $global:CONTAINERSHELL -C `"if(`$null -eq (Get-Command java.exe -ErrorAction SilentlyContinue)) { exit -1 } else { exit 0 }`""
$exitCode | Should -Be 0

$exitCode, $stdout, $stderr = Run-Program 'docker' "exec $global:CONTAINERNAME $global:CONTAINERSHELL -C `"`$version = java -version 2>&1 ; Write-Host `$version`"" $global:TESTS_DEBUG
$exitCode, $stdout, $stderr = Run-Program 'docker' "exec $global:CONTAINERNAME $global:CONTAINERSHELL -C `"`$version = java -version 2>&1 ; Write-Host `$version`""
$r = [regex] "^openjdk version `"(?<major>\d+)"
$m = $r.Match($stdout)
$m | Should -Not -Be $null
$m.Groups['major'].ToString() | Should -Be "$global:JAVAMAJORVERSION"
}

It 'has git-lfs (and thus git) installed' {
It 'has git-lfs (and thus git) installed and in the path' {
$exitCode, $stdout, $stderr = Run-Program 'docker' "exec $global:CONTAINERNAME $global:CONTAINERSHELL -C `"`& git lfs version`""
$exitCode | Should -Be 0
$stdout.Trim() | Should -Match "git-lfs/$global:GITLFSVERSION"
Expand All @@ -128,15 +130,16 @@ Describe "[$global:IMAGE_NAME] image has correct version of java and git-lfs ins

Describe "[$global:IMAGE_NAME] create agent container with pubkey as argument" {
BeforeAll {
docker run --detach --tty --name="$global:CONTAINERNAME" --publish-all "$global:IMAGE_NAME" "$global:PUBLIC_SSH_KEY"
$exitCode, $stdout, $stderr = Run-Program 'docker' "run --detach --tty --name=`"$global:CONTAINERNAME`" --publish-all `"$global:IMAGE_NAME`" `"$global:PUBLIC_SSH_KEY`""
$exitCode | Should -Be 0
Is-ContainerRunning $global:CONTAINERNAME | Should -BeTrue
Start-Sleep -Seconds 10
}

It 'runs commands via ssh' {
$exitCode, $stdout, $stderr = Run-ThruSSH $global:CONTAINERNAME "$global:PRIVATE_SSH_KEY" "$global:CONTAINERSHELL -NoLogo -C `"Write-Host 'f00'`""
$exitCode | Should -Be 0
$stdout | Should -Match "f00"
$stdout | Should -Match 'f00'
}

AfterAll {
Expand All @@ -146,15 +149,17 @@ Describe "[$global:IMAGE_NAME] create agent container with pubkey as argument" {

Describe "[$global:IMAGE_NAME] create agent container with pubkey as envvar" {
BeforeAll {
docker run --detach --tty --name="$global:CONTAINERNAME" --publish-all --env="JENKINS_AGENT_SSH_PUBKEY=$global:PUBLIC_SSH_KEY" "$global:IMAGE_NAME"
$exitCode, $stdout, $stderr = Run-Program 'docker' "run --detach --tty --name=`"$global:CONTAINERNAME`" --publish-all `"$global:IMAGE_NAME`" `"$global:PUBLIC_SSH_KEY`""
$exitCode | Should -Be 0
Is-ContainerRunning $global:CONTAINERNAME | Should -BeTrue
Start-Sleep -Seconds 10
}

It 'runs commands via ssh' {
$exitCode, $stdout, $stderr = Run-ThruSSH $global:CONTAINERNAME "$global:PRIVATE_SSH_KEY" "$global:CONTAINERSHELL -NoLogo -C `"Write-Host 'f00'`""
$exitCode | Should -Be 0
$stdout | Should -Match "f00"
$stdout | Should -Match 'f00'
Start-Sleep -Seconds 10
}

AfterAll {
Expand All @@ -167,15 +172,16 @@ $global:DOCKER_PLUGIN_DEFAULT_ARG="/usr/sbin/sshd -D -p 22"
Describe "[$global:IMAGE_NAME] create agent container like docker-plugin with '$global:DOCKER_PLUGIN_DEFAULT_ARG' as argument" {
BeforeAll {
[string]::IsNullOrWhiteSpace($global:DOCKER_PLUGIN_DEFAULT_ARG) | Should -BeFalse
docker run --detach --tty --name="$global:CONTAINERNAME" --publish-all --env="JENKINS_AGENT_SSH_PUBKEY=$global:PUBLIC_SSH_KEY" "$global:IMAGE_NAME" "$global:DOCKER_PLUGIN_DEFAULT_ARG"
$exitCode, $stdout, $stderr = Run-Program 'docker' "run --detach --tty --name=`"$global:CONTAINERNAME`" --publish-all --env=`"JENKINS_AGENT_SSH_PUBKEY=$global:PUBLIC_SSH_KEY`" `"$global:IMAGE_NAME`" `"$global:DOCKER_PLUGIN_DEFAULT_ARG`""
$exitCode | Should -Be 0
Is-ContainerRunning $global:CONTAINERNAME | Should -BeTrue
Start-Sleep -Seconds 10
}

It 'runs commands via ssh' {
$exitCode, $stdout, $stderr = Run-ThruSSH $global:CONTAINERNAME "$global:PRIVATE_SSH_KEY" "$global:CONTAINERSHELL -NoLogo -C `"Write-Host 'f00'`""
$exitCode | Should -Be 0
$stdout | Should -Match "f00"
$stdout | Should -Match 'f00'
}

AfterAll {
Expand All @@ -189,8 +195,8 @@ Describe "[$global:IMAGE_NAME] build args" {
}

It 'uses build args correctly' {
$TEST_USER="testuser"
$TEST_JAW="C:/hamster"
$TEST_USER = 'testuser'
$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 ."
Expand Down
Loading