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

Windows container support #129

Merged
merged 43 commits into from
Nov 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
f370e1c
Fix #34
slide Aug 7, 2019
edbfbb7
Update README.md
slide Aug 23, 2019
5f50f0c
Update README.md
slide Aug 23, 2019
0243415
Update README.md with recommendations
slide Aug 23, 2019
7da4320
Remove commented code
slide Aug 23, 2019
1a7112d
Update tests/maven.Tests.ps1
slide Aug 23, 2019
f26f19d
Refactor the directory structure for Windows images
slide Aug 23, 2019
e483302
Merge branch 'master' into master
slide Aug 26, 2019
188e9f1
Capture failure code from expression
slide Aug 26, 2019
6d4f058
Capture failure code from expression
slide Aug 26, 2019
2d1ce89
Capture failure code from expression
slide Aug 26, 2019
87864fb
Capture failure code from expression
slide Aug 26, 2019
336b312
Capture failure code from expression
slide Aug 26, 2019
297328d
Capture failure code from expression
slide Aug 26, 2019
2ee9fff
Capture failure code from expression
slide Aug 26, 2019
929e16b
Capture failure code from expression
slide Aug 26, 2019
4d2411c
Capture failure code from expression
slide Aug 26, 2019
dc81bc7
Fix test
slide Aug 26, 2019
5f26db8
Fix test
slide Aug 26, 2019
cc45372
Remove update of settings-docker.xml
slide Oct 23, 2019
69ee3aa
Merge branch 'master' of https://github.com/carlossg/docker-maven
slide Oct 23, 2019
c1420f5
Merge branch 'structure-refactor'
slide Oct 23, 2019
e6ca0db
Windows support updates
slide Oct 24, 2019
d3b32bc
Only build on windows-2019
slide Oct 24, 2019
e9cf9a4
Install Pester
slide Oct 24, 2019
4324f84
Install-module only
slide Oct 24, 2019
facd6b8
Fix parameter
slide Oct 24, 2019
db697b5
Don't use matrix.os
slide Oct 24, 2019
be83b14
Fix paren
slide Oct 24, 2019
b530522
Fix other paren
slide Oct 24, 2019
156ed00
Add push/pop location
slide Oct 24, 2019
dc69b46
Fix tags and JAVA_HOME
slide Oct 24, 2019
230d745
Merge branch 'master' of https://github.com/slide/docker-maven
slide Oct 24, 2019
12473ca
Fix test check
slide Oct 24, 2019
9c0a865
Remove '-it' for CI
slide Oct 24, 2019
2c43c9a
Switch up to slideomix repo for now
slide Oct 24, 2019
9a95941
ErrorAction
slide Oct 24, 2019
02c975e
One last mod before PR
slide Oct 24, 2019
7d2f647
Simplify
slide Oct 24, 2019
a041da5
Fix rhs
slide Oct 24, 2019
1c4e0b4
Switch to official repo
slide Oct 24, 2019
d53fb34
Merge branch 'master' into master
carlossg Nov 1, 2019
5483ced
Remove jdk 12
carlossg Nov 1, 2019
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
58 changes: 58 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Windows Docker Image CI

on: [push, pull_request]

jobs:
build:

runs-on: windows-2019
steps:
- uses: actions/checkout@v1
- name: Build Images
run: |
Install-Module -Name Pester -Force -RequiredVersion 4.9.0
$tags = @('3.6.2', '3.6', '3')
Get-ChildItem -Path windows\* -File -Include "Dockerfile.windows-*" | ForEach-Object {
Push-Location
$dockerfile = $_

$windowsType = 'windowsservercore'
$windowsDockerTag = 'ltsc2019'
$windowsReleaseId = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name ReleaseID | Select-Object ReleaseID).ReleaseID

if($dockerfile.Name.Contains('nanoserver')) {
$windowsType = 'windowsnanoserver'
$windowsDockerTag = $windowsReleaseId
}

if($dockerfile.Name.Contains('jdk-')) {
$windowsDockerTag = $windowsReleaseId
}

# run tests
Push-Location
$env:TAG=$dockerfile.Name.Replace('Dockerfile.windows-', '')
$env:WINDOWS_DOCKER_TAG=$windowsDockerTag
Invoke-Pester -Path tests
Remove-Item env:\TAG
Remove-Item env:\WINDOWS_DOCKER_TAG
Pop-Location

$tags | ForEach-Object {
Push-Location windows
$tag = ('maven:{0}-{1}-{2}-{3}' -f $_,$dockerfile.Name.Replace('Dockerfile.windows-', ''),$windowsType,$windowsDockerTag)
docker build -f $dockerfile --tag $tag --build-arg WINDOWS_DOCKER_TAG=${windowsDockerTag} .

if('${{ github.event_name }}' -eq 'push') {
# docker login with cause a warning which will cause this to fail unless we SilentlyContinue
$ErrorActionPreference = 'SilentlyContinue'
'${{ secrets.DOCKER_PASSWORD }}' | & docker login --username ${{ secrets.DOCKER_USERNAME }} --password-stdin
$ErrorActionPreference = 'Stop'
Write-Host "Pushing $tag"
& docker push $tag
}
Pop-Location
}
Pop-Location
}
shell: pwsh
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/tests/.classpath
/tests/.project
/tests/.settings/
/tests/target/
53 changes: 46 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,21 @@ See [Docker Hub](https://hub.docker.com/_/maven) for updated list of tags
* [jdk-8](https://github.com/carlossg/docker-maven/blob/master/jdk-8/Dockerfile)
* [jdk-8-openj9](https://github.com/carlossg/docker-maven/blob/master/jdk-8-openj9/Dockerfile)
* [jdk-8-slim](https://github.com/carlossg/docker-maven/blob/master/jdk-8-slim/Dockerfile)
* [jdk-8-windows](https://github.com/carlossg/docker-maven/blob/master/jdk-8/Dockerfile.windows)
* [jdk-11](https://github.com/carlossg/docker-maven/blob/master/jdk-11/Dockerfile)
* [jdk-11-openj9](https://github.com/carlossg/docker-maven/blob/master/jdk-11-openj9/Dockerfile)
* [jdk-11-slim](https://github.com/carlossg/docker-maven/blob/master/jdk-11-slim/Dockerfile)
* [jdk-11-windows](https://github.com/carlossg/docker-maven/blob/master/jdk-11/Dockerfile.windows)
* [jdk-13](https://github.com/carlossg/docker-maven/blob/master/jdk-13/Dockerfile)
* [jdk-13-windows](https://github.com/carlossg/docker-maven/blob/master/jdk-13/Dockerfile.windows)
* [jdk-14](https://github.com/carlossg/docker-maven/blob/master/jdk-14/Dockerfile)
* [ibmjava-8](https://github.com/carlossg/docker-maven/blob/master/ibmjava-8/Dockerfile)
* [ibmjava-8-alpine](https://github.com/carlossg/docker-maven/blob/master/ibmjava-8-alpine/Dockerfile)
* [amazoncorretto-8](https://github.com/carlossg/docker-maven/blob/master/amazoncorretto-8/Dockerfile)
* [amazoncorretto-11](https://github.com/carlossg/docker-maven/blob/master/amazoncorretto-11/Dockerfile)
* [amazoncorretto-11](https://github.com/carlossg/docker-maven/blob/master/amazoncorretto-11/)
* [amazoncorretto-11-windows](https://github.com/carlossg/docker-maven/blob/master/amazoncorretto-11/Dockerfile.windows)
* [azulzulu-11](https://github.com/carlossg/docker-maven/blob/master/azulzulu-11/Dockerfile)
* [azulzulu-11-windows](https://github.com/carlossg/docker-maven/blob/master/azulzulu-11/Dockerfile.windows)

# What is Maven?

Expand All @@ -32,14 +37,28 @@ reporting and documentation from a central piece of information.
You can run a Maven project by using the Maven Docker image directly,
passing a Maven command to `docker run`:

### Linux

docker run -it --rm --name my-maven-project -v "$(pwd)":/usr/src/mymaven -w /usr/src/mymaven maven:3.3-jdk-8 mvn clean install

### Windows

```powershell
docker run -it --rm --name my-maven-project -v "$(Get-Location)":C:/Src -w C:/Src maven:3.3-jdk-8-windows mvn clean install
```

## Building local Docker image (optional)

This is a base image that you can extend, so it has the bare minimum packages needed. If you add custom package(s) to the `Dockerfile`, then you can build your local Docker image like this:

### Linux

docker build --tag my_local_maven:3.6.0-jdk-8 .

### Windows

docker build -f Dockerfile.windows --tag my_local_maven:3-jdk-9-windows --build-arg WINDOWS_DOCKER_TAG=1803 .


# Multi-stage Builds

Expand Down Expand Up @@ -74,12 +93,15 @@ Or you can just use your home .m2 cache directory that you share e.g. with your

# Packaging a local repository with the image

The `$MAVEN_CONFIG` dir (default to `/root/.m2`) could be configured as a volume so anything copied there in a Dockerfile at build time is lost.
For that reason the dir `/usr/share/maven/ref/` exists, and anything in that directory will be copied on container startup to `$MAVEN_CONFIG`.
The `$MAVEN_CONFIG` dir (default to `/root/.m2` or `C:\Users\ContainerUser\.m2`) could be configured as a volume so anything copied there in a Dockerfile
at build time is lost. For that reason the dir `/usr/share/maven/ref/` (or `C:\ProgramData\Maven\Reference`) exists, and anything in that directory will be copied
on container startup to `$MAVEN_CONFIG`.

To create a pre-packaged repository, create a `pom.xml` with the dependencies you need and use this in your `Dockerfile`.
`/usr/share/maven/ref/settings-docker.xml` is a settings file that changes the local repository to `/usr/share/maven/ref/repository`,
but you can use your own settings file as long as it uses `/usr/share/maven/ref/repository` as local repo.
`/usr/share/maven/ref/settings-docker.xml` (`C:\ProgramData\Maven\Reference\settings-docker.xml`) is a settings file that
changes the local repository to `/usr/share/maven/ref/repository` (`C:\Programdata\Maven\Reference\repository`),
but you can use your own settings file as long as it uses `/usr/share/maven/ref/repository` (`C:\ProgramData\Maven\Reference\repository`)
as local repo.

COPY pom.xml /tmp/pom.xml
RUN mvn -B -f /tmp/pom.xml -s /usr/share/maven/ref/settings-docker.xml dependency:resolve
Expand All @@ -91,7 +113,7 @@ To add your custom `settings.xml` file to the image use
For an example, check the `tests` dir


# Running as non-root
# Running as non-root (not supported on Windows)

Maven needs the user home to download artifacts to, and if the user does not exist in the image an extra
`user.home` Java property needs to be set.
Expand All @@ -115,15 +137,29 @@ Build with the usual

docker build -t maven .

Tests are written using [bats](https://github.com/sstephenson/bats) under the `tests` dir.
Tests are written using [bats](https://github.com/sstephenson/bats) for Linux images and [pester](https://github.com/pester/Pester) for Windows images
(requires Pester 4.x) under the `tests` dir.

Use the env var TAG to choose what image to run tests against.

### Linux
TAG=jdk-11 bats tests

### Windows
```powershell
$env:TAG="jdk-11" ; Invoke-Pester -Path tests
```

or run all the tests with

### Linux
for dir in $(/bin/ls -1 -d */ | grep -v tests); do TAG=$(basename $dir) bats tests; done

### Windows
```powershell
Get-ChildItem -Path windows\* -File -Include "Dockerfile.windows-*" | ForEach-Object { Push-Location ; $env:TAG=$_.Name.Replace('Dockerfile.windows-', '') ; Invoke-Pester -Path tests ; Pop-Location }
```

Bats can be easily installed with `brew install bats` on OS X.

Note that you may first need to:
Expand All @@ -133,6 +169,9 @@ git submodule init
git submodule update
```

Pester comes with most modern Windows (Windows 10 and Windows Server 2019), but is an older version than required. You may need to follow [this tutorial](https://blog.damianflynn.com/Windows10-Pester/) on upgrading Pester to 4.x.


## Publishing to Docker Hub

In order to publish the images a PR needs to be opened against [docker-library/official-images](https://github.com/docker-library/official-images)
Expand Down
7 changes: 7 additions & 0 deletions tests/Dockerfile.windows
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM pester-maven

COPY settings.xml C:/ProgramData/Maven/Reference/
COPY pom.xml C:/Temp/pom.xml
COPY src/ C:/Temp/src

RUN mvn -B -f C:/Temp/pom.xml -s C:/ProgramData/Maven/Reference/settings-docker.xml dependency:resolve
80 changes: 80 additions & 0 deletions tests/maven.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
$SUT_IMAGE="pester-maven"
$SUT_CONTAINER="pester-maven"
$SUT_TAG="jdk-8"
if(![System.String]::IsNullOrWhiteSpace($env:TAG)) {
$SUT_TAG=$env:TAG
}
$SUT_TEST_IMAGE="pester-maven-test"
$SUT_TEST_CONTAINER="pester-maven-test"

Import-Module -Force -DisableNameChecking $PSScriptRoot/test_helpers.psm1

Describe "$SUT_TAG build image" {
BeforeEach {
Push-Location -StackName 'maven' -Path "$PSScriptRoot/../windows"
}

It 'builds image' {
$exitCode, $stdout, $stderr = Build-Docker -ImageType $SUT_TAG --pull -t $SUT_IMAGE
$exitCode | Should -Be 0
}

AfterEach {
Pop-Location -StackName 'maven'
}
}

Describe "$SUT_TAG build test image" {
BeforeEach {
Push-Location -StackName 'maven' -Path "$PSScriptRoot"
}

It 'builds image' {
$exitCode, $stdout, $stderr = Build-Docker -t $SUT_TEST_IMAGE
$exitCode | Should -Be 0
}

AfterEach {
Pop-Location -StackName 'maven'
}
}

Describe "$SUT_TAG create test container" {
It 'creates test container' {
$version = $(Get-Content -Path "$PSScriptRoot/../windows/Dockerfile.windows-$SUT_TAG" | Select-String -Pattern 'ARG MAVEN_VERSION.*' | ForEach-Object { $_ -replace 'ARG MAVEN_VERSION=','' })
$exitCode, $stdout, $stderr = Run-Program -Cmd "docker.exe" -Params "run --rm $SUT_IMAGE mvn -version"
$exitCode | Should -Be 0
$stdout | Should -Match "Apache Maven $version"
}
}

Describe "$SUT_TAG settings.xml is setup" {
It 'sets up settings.xml' {
$exitCode, $stdout, $stderr = Run-Program -Cmd "docker.exe" -Params "run --rm $SUT_TEST_IMAGE Get-Content C:/Users/ContainerUser/.m2/settings.xml"
$exitCode | Should -Be 0
"$PSScriptRoot/settings.xml" | Should -FileContentMatchMultiline $stdout
}
}


Describe "$SUT_TAG repository is created" {
It 'created repository' {
$exitCode, $stdout, $stderr = Run-Program -Cmd "docker.exe" -Params "run --rm $SUT_TEST_IMAGE if(Test-Path C:/Users/ContainerUser/.m2/repository/junit/junit/3.8.1/junit-3.8.1.jar) { exit 0 } else {exit 1 }"
$exitCode | Should -Be 0
}
}


Describe "$SUT_TAG run Maven" {
It 'runs maven' {
$exitCode, $stdout, $stderr = Run-Program -Cmd "docker.exe" -Params "run --rm $SUT_TEST_IMAGE mvn -B -ntp -f C:/Temp install"
$exitCode | Should -Be 0
}
}

Describe "$SUT_TAG generate sample project" {
It 'generates sample project' {
$exitCode, $stdout, $stderr = Run-Program -Cmd "docker.exe" -Params "run --rm $SUT_TEST_IMAGE mvn -B archetype:generate -DgroupId=pester-testing -DartifactId=pester-test-project -DarchetypeArtifactId=maven-archetype-quickstart"
$exitCode | Should -Be 0
}
}
103 changes: 103 additions & 0 deletions tests/test_helpers.psm1
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@

function Test-CommandExists($command) {
$oldPreference = $ErrorActionPreference
$ErrorActionPreference = 'stop'
$res = $false
try {
if(Get-Command $command) {
$res = $true
}
} catch {
$res = $false
} finally {
$ErrorActionPreference=$oldPreference
}
return $res
}

# check dependencies
if(-Not (Test-CommandExists docker)) {
Write-Error "docker is not available"
}

function Run-Program($Cmd, $Params) {
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.CreateNoWindow = $true
$psi.UseShellExecute = $false
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$psi.WorkingDirectory = (Get-Location)
$psi.FileName = $Cmd
$psi.Arguments = $Params
$proc = New-Object System.Diagnostics.Process
$proc.StartInfo = $psi
[void]$proc.Start()
$stdout = $proc.StandardOutput.ReadToEnd()
$stderr = $proc.StandardError.ReadToEnd()
$proc.WaitForExit()
if($proc.ExitCode -ne 0) {
Write-Host "`n`nstdout:`n$stdout`n`nstderr:`n$stderr`n`n"
}
return $proc.ExitCode, $stdout, $stderr
}

function Build-Docker($ImageType) {
if(![String]::IsNullOrWhitespace($ImageType) -and !$ImageType.StartsWith('-')) {
$ImageType = "-$ImageType"
}

$windowsDockerTag=''
if(![String]::IsNullOrWhiteSpace($env:WINDOWS_DOCKER_TAG)) {
$windowsDockerTag = "--build-arg WINDOWS_DOCKER_TAG=$env:WINDOWS_DOCKER_TAG"
}
return (Run-Program 'docker.exe' "build -f Dockerfile.windows$ImageType $windowsDockerTag $args .")
}

function Retry-Command {
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
[scriptblock] $ScriptBlock,
[int] $RetryCount = 3,
[int] $Delay = 30,
[string] $SuccessMessage = "Command executed successfuly!",
[string] $FailureMessage = "Failed to execute the command"
)

process {
$Attempt = 1
$Flag = $true

do {
try {
$PreviousPreference = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
Invoke-Command -NoNewScope -ScriptBlock $ScriptBlock -OutVariable Result 4>&1
$ErrorActionPreference = $PreviousPreference

# flow control will execute the next line only if the command in the scriptblock executed without any errors
# if an error is thrown, flow control will go to the 'catch' block
Write-Verbose "$SuccessMessage `n"
$Flag = $false
}
catch {
if ($Attempt -gt $RetryCount) {
Write-Verbose "$FailureMessage! Total retry attempts: $RetryCount"
Write-Verbose "[Error Message] $($_.exception.message) `n"
$Flag = $false
} else {
Write-Verbose "[$Attempt/$RetryCount] $FailureMessage. Retrying in $Delay seconds..."
Start-Sleep -Seconds $Delay
$Attempt = $Attempt + 1
}
}
}
While ($Flag)
}
}

function Remove-Container($Container) {
docker kill "$Container" 2>&1 | Out-Null
docker rm -fv "$Container" 2>&1 | Out-Null
}
Loading