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

feat(windows): refactor docker-ssh-agent build process like docker-agent #289

Merged
merged 3 commits into from
Mar 27, 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
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
Loading