Skip to content

Commit

Permalink
chore(windows): refactor docker-ssh-agent build process like `docke…
Browse files Browse the repository at this point in the history
…r-agent`
  • Loading branch information
lemeurherve committed Mar 23, 2024
1 parent e6bf13f commit 4b74737
Show file tree
Hide file tree
Showing 11 changed files with 422 additions and 339 deletions.
4 changes: 2 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ updates:
interval: weekly
open-pull-requests-limit: 10
- package-ecosystem: docker
directory: "/windows/nanoserver-ltsc2019"
directory: "/windows/nanoserver"
schedule:
interval: weekly
open-pull-requests-limit: 10
- package-ecosystem: docker
directory: "/windows/windowsservercore-ltsc2019"
directory: "/windows/windowsservercore"
schedule:
interval: weekly
open-pull-requests-limit: 10
Expand Down
149 changes: 81 additions & 68 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -1,85 +1,98 @@
def agentSelector(String imageType) {
// Image type running on a Linux agent
if (imageType == 'linux') {
return 'linux'
}
// Image types running on a Windows Server Core 2022 agent
if (imageType.contains('2022')) {
return 'windows-2022'
}
// Remaining image types running on a Windows Server Core 2019 agent: (nanoserver|windowservercore)-(1809|2019)
return 'windows-2019'
}

pipeline {
agent none

options {
buildDiscarder(logRotator(daysToKeepStr: '10'))
timestamps()
}

stages {
stage('Build Docker Image') {
parallel {
stage('Windows') {
agent {
label "docker-windows"
stage('docker-ssh-agent') {
matrix {
axes {
axis {
name 'IMAGE_TYPE'
values 'linux', 'nanoserver-1809', 'windowsservercore-ltsc2019'
}
options {
timeout(time: 60, unit: 'MINUTES')
}
environment {
DOCKERHUB_ORGANISATION = "${infra.isTrusted() ? 'jenkins' : 'jenkins4eval'}"
}
steps {
powershell '& ./make.ps1 test'
script {
def branchName = "${env.BRANCH_NAME}"
if (branchName ==~ 'master') {
// we can't use dockerhub builds for windows
// so we publish here
infra.withDockerCredentials {
powershell '& ./make.ps1 publish'
}
stages {
stage('Main') {
agent {
label agentSelector(env.IMAGE_TYPE)
}
options {
timeout(time: 30, unit: 'MINUTES')
}
environment {
DOCKERHUB_ORGANISATION = "${infra.isTrusted() ? 'jenkins' : 'jenkins4eval'}"
}
stages {
stage('Prepare Docker') {
when {
environment name: 'IMAGE_TYPE', value: 'linux'
}
}

def tagName = "${env.TAG_NAME}"
if(tagName =~ /\d(\.\d)+(-\d+)?/) {
// we need to build and publish the tagged version
infra.withDockerCredentials {
powershell "& ./make.ps1 -PushVersions -VersionTag $tagName publish"
steps {
sh '''
docker buildx create --use
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
'''
}
}
}
}
}
stage('Linux') {
agent {
label "docker&&linux"
}
options {
timeout(time: 30, unit: 'MINUTES')
}
steps {
script {
infra.withDockerCredentials {
def branchName = "${env.BRANCH_NAME}"
if (infra.isTrusted()) {
if (branchName ==~ 'master') {
sh '''
docker buildx create --use
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
docker buildx bake --push --file docker-bake.hcl linux
'''
} else if (env.TAG_NAME != null) {
sh """
export ON_TAG=true
export VERSION=$TAG_NAME
docker buildx create --use
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
docker buildx bake --push --file docker-bake.hcl linux
"""
stage('Build and Test') {
// This stage is the "CI" and should be run on all code changes triggered by a code change
when {
not { buildingTag() }
}
steps {
script {
if(isUnix()) {
sh 'make build'
sh 'make test'
// If the tests are passing for Linux AMD64, then we can build all the CPU architectures
sh 'docker buildx bake --file docker-bake.hcl linux'
} else {
powershell '& ./build.ps1 test'
}
}
} else {
sh 'make build'
try {
sh 'make test'
} finally {
junit('target/*.xml')
}
post {
always {
junit(allowEmptyResults: true, keepLongStdio: true, testResults: 'target/**/junit-results.xml')
}
}
}
stage('Deploy to DockerHub') {
// This stage is the "CD" and should only be run when a tag triggered the build
when {
buildingTag()
}
steps {
script {
// This function is defined in the jenkins-infra/pipeline-library
infra.withDockerCredentials {
if (isUnix()) {
sh """
export ON_TAG=true
export VERSION=$TAG_NAME
docker buildx bake --push --file docker-bake.hcl linux
"""
} else {
powershell "& ./build.ps1 -PushVersions -VersionTag ${env.TAG_NAME} publish"
}
}
}
sh '''
docker buildx create --use
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
docker buildx bake --file docker-bake.hcl linux
'''
}
}
}
Expand Down
15 changes: 15 additions & 0 deletions build-windows.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
services:
jdk11:
image: ${DOCKERHUB_ORGANISATION}/${DOCKERHUB_REPO}:${WINDOWS_FLAVOR}-${WINDOWS_VERSION_TAG}-jdk11
build:
context: ./
dockerfile: ./windows/${WINDOWS_FLAVOR}/Dockerfile
args:
JAVA_HOME: "C:/openjdk-11"
JAVA_VERSION: 11.0.20_8
WINDOWS_VERSION_TAG: ${WINDOWS_VERSION_TAG}
TOOLS_WINDOWS_VERSION: ${TOOLS_WINDOWS_VERSION}
tags:
- "${DOCKERHUB_ORGANISATION}/${DOCKERHUB_REPO}:${VERSION}-${WINDOWS_FLAVOR}-${WINDOWS_VERSION_TAG}-jdk11"
- "${DOCKERHUB_ORGANISATION}/${DOCKERHUB_REPO}:${VERSION}-${WINDOWS_FLAVOR}-${WINDOWS_VERSION_TAG}"
- "${DOCKERHUB_ORGANISATION}/${DOCKERHUB_REPO}:${WINDOWS_FLAVOR}-${WINDOWS_VERSION_TAG}"
184 changes: 184 additions & 0 deletions build.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
[CmdletBinding()]
Param(
[Parameter(Position=1)]
[String] $Target = 'build',
[String] $Build = '',
[String] $VersionTag = '1.0-1',
[switch] $DryRun = $false
)

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

if(![String]::IsNullOrWhiteSpace($env:DOCKERHUB_REPO)) {
$Repository = $env:DOCKERHUB_REPO
}

if(![String]::IsNullOrWhiteSpace($env:DOCKERHUB_ORGANISATION)) {
$Organisation = $env:DOCKERHUB_ORGANISATION
}

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

# Check for required commands
Function Test-CommandExists {
# From https://devblogs.microsoft.com/scripting/use-a-powershell-function-to-see-if-a-command-exists/
Param (
[String] $command
)

$oldPreference = $ErrorActionPreference
$ErrorActionPreference = 'stop'
try {
if(Get-Command $command){
Write-Debug "$command exists"
}
}
Catch {
"$command does not exist"
}
Finally {
$ErrorActionPreference=$oldPreference
}
}

# 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"

function Test-Image {
param (
$ImageName
)

$imageNameItems = $imageName.Split(":")
$imageTag = $imageNameItems[1]

Write-Host "= TEST: Testing ${ImageName} image"

$env:IMAGE_NAME = $ImageName

$targetPath = '.\target\{0}' -f $imageTag
if(Test-Path $targetPath) {
Remove-Item -Recurse -Force $targetPath
}
New-Item -Path $targetPath -Type Directory | Out-Null
# $configuration.Run.Path = 'tests\sshAgent.Tests.ps1'
$configuration.TestResult.OutputPath = '{0}\junit-results.xml' -f $targetPath
$TestResults = Invoke-Pester -Configuration $configuration
$failed = $false
if ($TestResults.FailedCount -gt 0) {
Write-Host "There were $($TestResults.FailedCount) failed tests out of $($TestResults.TotalCount) in ${ImageName}"
$failed = $true
} else {
Write-Host "There were $($TestResults.PassedCount) passed tests in ${ImageName}"
}
Remove-Item env:\IMAGE_NAME

return $failed
}

$baseDockerCmd = 'docker-compose --file=build-windows.yaml'
$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..."
switch ($DryRun) {
$true { Write-Host "(dry-run) $baseDockerBuildCmd" }
$false { 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"

$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 ${agentType} images..."
# Only fail the run afterwards in case of any test failures
$testFailed = $false
Invoke-Expression "$baseDockerCmd config" | yq '.services[].image' | ForEach-Object {
$testFailed = $testFailed -or (Test-Image $_)
}

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

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!"
exit 1
}
}

if($lastExitCode -ne 0) {
Write-Error "Build failed!"
} else {
Write-Host "Build finished successfully"
}
exit $lastExitCode
Loading

0 comments on commit 4b74737

Please sign in to comment.