-
Notifications
You must be signed in to change notification settings - Fork 157
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
Run Builds in Docker Container Locally #328
Closed
SebastianSchuetze
wants to merge
13
commits into
MethodsAndPractices:master
from
RazorSPoint:topic/VsCodeRemoteMultiContainerBuild
+403
−1
Closed
Changes from 10 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
6a79f6a
added initial dev container for windows and linux ubuntu
SebastianSchuetze 86f933e
1a08794
Merge branch 'master' into topic/VsCodeRemoteMultiContainerBuild
SebastianSchuetze 8782c27
remove devcontainer for now
SebastianSchuetze ea0f303
updated linux container
SebastianSchuetze c2af380
updated windows container
SebastianSchuetze 652d6eb
added run script for local builds in container
SebastianSchuetze d1d2ee1
moved files to new tools folder
SebastianSchuetze 8cf4a30
added parameter to only run linux based containers
SebastianSchuetze 6c989c3
added documentation
SebastianSchuetze a8c9110
removed unneeded comments
SebastianSchuetze f866891
minor bug fixes in docker script
SebastianSchuetze eb9d101
added troubleshooting part and help to documentation
SebastianSchuetze File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,278 @@ | ||
[CmdletBinding()] | ||
param ( | ||
#if you want to make sure that only linux based containers are used, since windows based containers are not supported on your OS | ||
[Parameter(Mandatory=$false)] | ||
[switch] | ||
$UseLinux | ||
) | ||
|
||
function Set-DockerHost { | ||
<# | ||
.SYNOPSIS | ||
Switch between windows and linux containers. IMPORTANT: Works only for windows based systems | ||
#> | ||
[CmdletBinding()] | ||
param ( | ||
#sets the container OS | ||
[Parameter(Mandatory = $true)] | ||
[ValidateSet("windows", "linux")] | ||
[string] | ||
$Os | ||
) | ||
|
||
|
||
process { | ||
$dockerVersion = docker version --format '{{json .}}' | ConvertFrom-Json | ||
|
||
if ($dockerVersion.Server.Os -ne $Os ) { | ||
& "$env:ProgramFiles\Docker\Docker\DockerCli.exe" -SwitchDaemon | ||
} | ||
else { | ||
Write-Verbose "docker host is already set to $Os... ignoring command" | ||
} | ||
} | ||
} | ||
|
||
function Add-DockerBuild { | ||
<# | ||
.SYNOPSIS | ||
Create a docker build with a tag. Optionally force to rebuild the container. | ||
#> | ||
[CmdletBinding()] | ||
param ( | ||
# Path to the dockerfile | ||
[Parameter(Mandatory = $true)] | ||
[string] | ||
$DockerFile, | ||
# give it a named tag. Best with repository and image name | ||
[Parameter(Mandatory = $true)] | ||
[string] | ||
$Tag, | ||
# Force the rebuild of the container | ||
[Switch] | ||
$Force | ||
) | ||
|
||
process { | ||
$dockerImageId = docker images -q $Tag | ||
|
||
#only build if image does not exist or it's forced | ||
#when not using force: It can't be checked if a docker file has is different from the existing image | ||
if ($null -eq $dockerImageId -or $Force) { | ||
docker build -f $DockerFile --tag $Tag . | ||
} | ||
else { | ||
Write-Verbose "image $Tag already exists with id $dockerImageId" | ||
} | ||
} | ||
} | ||
|
||
function Start-DockerVSTeamTests { | ||
[CmdletBinding()] | ||
param ( | ||
# Name of the container to run | ||
[Parameter(Mandatory = $true)] | ||
[string] | ||
$Container, | ||
# volume mapping string | ||
# see: https://docs.docker.com/storage/volumes/#start-a-container-with-a-volume | ||
[Parameter(Mandatory = $true)] | ||
[string] | ||
$Volume, | ||
# default directoy when the container is started | ||
[Parameter(Mandatory = $true)] | ||
[string] | ||
$DefaultWorkDir, | ||
# Image to start | ||
[Parameter(Mandatory = $true)] | ||
[string] | ||
$Image, | ||
# choose which shell to start | ||
[Parameter(Mandatory = $false)] | ||
[ValidateSet("pwsh", "powershell")] | ||
[string] | ||
$Shell = "pwsh", | ||
# Use if powershell should wait for the container to exit | ||
[Parameter(Mandatory = $false)] | ||
[Switch] | ||
$Wait, | ||
# Open another powershell session to show the logs from the container | ||
[Parameter(Mandatory = $false)] | ||
[Switch] | ||
$FollowLogs | ||
) | ||
|
||
begin { | ||
|
||
# using a script block here to have syntax checking and highlightning. | ||
# Later it is converted to a string to start the container with it | ||
$pesterBuild = { | ||
|
||
.\Build-Module.ps1 -installDep; | ||
$null = Import-Module Pester; | ||
|
||
$pesterArgs = [PesterConfiguration]::Default; | ||
$pesterArgs.Run.Exit = $true; | ||
$pesterArgs.Run.Path = '.\unit'; | ||
$pesterArgs.Run.PassThru = $true; | ||
$pesterArgs.Output.Verbosity = 'Normal'; | ||
$pesterArgs.TestResult.Enabled = $true; | ||
$pesterArgs.TestResult.OutputPath = '#Container#_result.xml' | ||
|
||
Invoke-Pester -Configuration $pesterArgs; | ||
|
||
# exist with PowerShells last exist code for docker. | ||
# without deliverate exit code the -Wait switch could cause to wait indefinetely | ||
Exit $LASTEXITCODE | ||
} | ||
} | ||
|
||
process { | ||
|
||
$containerId = docker ps --all --filter name=$Container -q | ||
$containerIsRunning = $null -ne (docker ps --filter name=$Container -q) | ||
|
||
if ($containerIsRunning) { | ||
docker stop $containerId | ||
} | ||
|
||
if ($containerId) { | ||
docker rm $Container | ||
} | ||
|
||
$psCommandString = ($pesterBuild.ToString()) -replace '#Container#', $Container | ||
|
||
docker run ` | ||
-dit ` | ||
--name $Container ` | ||
--volume $Volume ` | ||
-w $DefaultWorkDir ` | ||
$Image ` | ||
$Shell -Command $psCommandString | ||
|
||
if ($FollowLogs) { | ||
$output = (docker exec -it $Container $Shell -c '$PSVersionTable | ConvertTo-Json -Compress;') -join '' | ||
$outputFirst = $output.IndexOf('{') | ||
$ouputLast = $output.LastIndexOf('}') | ||
$versiontable = $output.Substring($outputFirst, $ouputLast+1-$outputFirst) | ConvertFrom-Json -Depth 50 | ||
$psVersion = "$($versiontable.PSVersion.Major).$($versiontable.PSVersion.Minor).$($versiontable.PSVersion.Patch)" | ||
|
||
$argList = "-NoExit -Command `"`$Host.UI.RawUI.WindowTitle = 'VSTeam Unit Tests | PowerShell $($versiontable.PSEdition) $psVersion | $($versiontable.Os)'; docker logs $Container -f`"" | ||
Start-Process $Shell -argumentlist $argList | ||
} | ||
|
||
if ($Wait) { | ||
docker wait $Container | ||
} | ||
|
||
} | ||
} | ||
|
||
function Wait-DockerContainer { | ||
<# | ||
.SYNOPSIS | ||
Wait for the given containers to finish. If they don't run, then error is thrown | ||
#> | ||
[CmdletBinding()] | ||
param ( | ||
# Containers to wait for | ||
[Parameter(Mandatory = $true)] | ||
[string[]] | ||
$Container | ||
) | ||
|
||
process { | ||
|
||
$exitCodes = @() | ||
|
||
$runningContainers = docker ps --format '{{json .}}' | ConvertFrom-Json | ||
|
||
$notRunningContainers = @() | ||
$notAllContainersRunning = $Container.Count -ne ($Container | Where-Object { | ||
|
||
$contains = $runningContainers.Names.Contains($_) | ||
if ($contains) { | ||
return $true | ||
} | ||
else { | ||
$notRunningContainers += $_ | ||
return $false | ||
} | ||
|
||
}).Count | ||
|
||
if ($notAllContainersRunning) { | ||
Write-Error "Contains with the following names are not running: $($notRunningContainers -join ', ')" | ||
} | ||
else { | ||
|
||
$Container | ForEach-Object { | ||
$exitCode = docker wait $_ | ||
|
||
$exitCodes += @{ | ||
containerName = $_ | ||
exitCode = $exitCode | ||
} | ||
} | ||
} | ||
|
||
return $exitCodes | ||
} | ||
} | ||
|
||
$platform = $PSVersionTable.Platform | ||
if ($platform -ne "Win32NT" -or $UseLinux) { | ||
Write-Warning "Platform is not Win32NT based but '$platform'. Windows container do not work on linux based systems. Ignoring windows containers..." | ||
} | ||
|
||
$scriptPath = $PSScriptRoot | ||
$rootDir = (Resolve-Path -Path "$scriptPath\..\..\").ToString().trim('\') | ||
$containerFolder = "c:/vsteam" | ||
$containerFilePath = "$rootDir/build/docker" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should be "$rootDir/Tools/docker" not build/docker. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also fixed the dir. I moved it before. |
||
|
||
$dockerRepository = "vsteam" | ||
|
||
|
||
$windowsImage = "$dockerRepository/wcore1903" | ||
$windowsContainerPS7 = "$($dockerRepository)_wcore1903_ps7_tests" | ||
$windowsContainerPS5 = "$($dockerRepository)_wcore1903_ps5_tests" | ||
|
||
# Build / run windows based container | ||
if ($platform -eq "Win32NT" -and !$UseLinux) { | ||
Set-DockerHost -Os windows -Verbose | ||
Add-DockerBuild -DockerFile "$containerFilePath/wcore1903/Dockerfile" -Tag $windowsImage | ||
|
||
Start-DockerVSTeamTests ` | ||
-Container $windowsContainerPS7 ` | ||
-Volume "$rootDir`:$containerFolder" ` | ||
-DefaultWorkDir $containerFolder ` | ||
-Image $windowsImage ` | ||
-FollowLogs | ||
|
||
Start-DockerVSTeamTests ` | ||
-Container $windowsContainerPS5 ` | ||
-Volume "$rootDir`:$containerFolder" ` | ||
-DefaultWorkDir $containerFolder ` | ||
-Image $windowsImage ` | ||
-Shell powershell ` | ||
-FollowLogs | ||
|
||
$null = Wait-DockerContainer -Container @($windowsContainerPS5,$windowsContainerPS7) | ||
} | ||
|
||
$linuxImage = "$dockerRepository/linux" | ||
$linuxContainer = "$($dockerRepository)_linux_ps7_tests" | ||
$linuxContainerFolder = $containerFolder.Replace('c:/', '/c/') | ||
|
||
# Build / run linux based container | ||
if ($platform -eq "Win32NT" -and !$UseLinux) { | ||
Set-DockerHost -Os linux -Verbose | ||
} | ||
Add-DockerBuild -DockerFile "$containerFilePath/linux/Dockerfile" -Tag $linuxImage | ||
$null = Start-DockerVSTeamTests ` | ||
-Container $linuxContainer ` | ||
-Volume "$rootDir`:$linuxContainerFolder" ` | ||
-DefaultWorkDir $linuxContainerFolder ` | ||
-Image $linuxImage ` | ||
-Wait ` | ||
-FollowLogs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# Run Unit Tests Locally with Docker | ||
|
||
When helping us to develop this module or when we want to expand the module. We always write unit tests. The problem with that is, that most of the time everybody is using his own machine with different version of OS or different versions of dependencies. | ||
|
||
This is why we created docker files for windows and linux. Currently we have prepared to run the unit tests in | ||
|
||
* linux - PowerShell Core 7 | ||
* windows server core - PowerShell 5 | ||
* windows server core - PowerShell Core 7 | ||
|
||
In best case you should only need to run the powershell script below. The dockerfiles being used for this have only the needed PowerShell modules installed. So theoretically you could also develop in theese containers with VSCode and remote development. | ||
|
||
# Prerequisites | ||
|
||
> **Note:** This feature is still in testing and was developed on a windows machine. Therefore, the script to run all tests for you works for that OS. But it is itended to work also for linux. But this comes a bit later. | ||
|
||
* [Docker](https://docs.docker.com/engine/install/) | ||
* depending on your OS you need to install Docker Desktop or only Server | ||
* Windows based OS if you want to run Windows containers | ||
* optional: [Install WSL](https://code.visualstudio.com/docs/remote/wsl#_installation) | ||
* if you even [use WSL2](https://docs.microsoft.com/en-us/windows/wsl/install-win10#update-to-wsl-2) then container switch and run with much more performance! | ||
|
||
Also be aware that we cannot know all prerequisites as there often many different dependencies for different host OS systems. | ||
|
||
# How to Run | ||
|
||
1. Install Docker: https://docs.docker.com/engine/install | ||
2. Run [Run-ContainerTests.ps1](Run-ContainerTests.ps1) located under ./tools/docker | ||
|
||
```powershell | ||
#Example | ||
Run-ContainerTests.ps1 | ||
``` | ||
|
||
# Limitations | ||
|
||
* Windows container only work with windows based systems. | ||
* If you want to use the container to develop with VSCode remote development, then it currently only works with linux systems. | ||
* The log in PowerShell 5 window is scrambled, only the Pester results at the end can be properly observed. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# escape=` | ||
ARG fromTag=7.0.1-ubuntu-18.04 | ||
ARG pesterMinVer=5.0.0-rc8 | ||
|
||
FROM mcr.microsoft.com/powershell:${fromTag} | ||
|
||
SHELL ["pwsh", "-Command", "$ErrorActionPreference = 'Stop';"] | ||
|
||
RUN pwsh ` | ||
-NoLogo ` | ||
-NoProfile ` | ||
-Command " ` | ||
`$ProgressPreference = 'SilentlyContinue';` | ||
Write-host 'Installing needed PowerShell modules...';` | ||
Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted;` | ||
Install-Module -Name SHiPS -Repository PSGallery -SkipPublisherCheck;` | ||
Install-Module -Name Trackyon.Utils -Repository PSGallery;` | ||
Install-Module -Name Pester -Repository PSGallery -Force -AllowPrerelease -MinimumVersion '${pesterMinVer}' -AllowClobber -SkipPublisherCheck;` | ||
Install-Module -Name PSSCriptAnalyzer -Repository PSGallery;` | ||
Install-Module -Name Plaster -Repository PSGallery; ` | ||
" | ||
|
||
CMD ["pwsh.exe"] |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ConvertFrom-Json does not have a -Depth. Only ConvertTo-Json has that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed it. Didnt get an error before and VSCode seems to suggest that as a parameter.