a1a17e81a1
Includes updated JS challenge scripts with Claude-User whitelist, same-site referer bypass, Blackbox-Exporter allowed bot, and all new exporters, cheat sheets, and automation scripts.
638 lines
22 KiB
PowerShell
638 lines
22 KiB
PowerShell
###############################################################################
|
|
#### windows-container-smoke-tests.ps1 — Verify Windows container health ####
|
|
#### Checks Docker/containerd service, container lifecycle, isolation ####
|
|
#### modes, NAT networking, port mapping, volumes, and event logs. ####
|
|
#### ####
|
|
#### Author: Phil Connor ####
|
|
#### Contact: contact@mylinux.work ####
|
|
#### License: MIT ####
|
|
#### Version: 1.0 ####
|
|
#### ####
|
|
#### Usage: .\windows-container-smoke-tests.ps1 ####
|
|
#### .\windows-container-smoke-tests.ps1 -ContainerRuntime docker ####
|
|
#### .\windows-container-smoke-tests.ps1 -OutputFormat tap ####
|
|
#### ####
|
|
#### See -Help for all options. ####
|
|
###############################################################################
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[ValidateSet("docker","containerd")]
|
|
[string]$ContainerRuntime = "docker",
|
|
[string]$TestImage = "mcr.microsoft.com/windows/nanoserver:ltsc2022",
|
|
[ValidateSet("text","tap")]
|
|
[string]$OutputFormat = "text",
|
|
[switch]$NoColor,
|
|
[switch]$Help
|
|
)
|
|
|
|
$ErrorActionPreference = "Continue"
|
|
|
|
# ============================================================================
|
|
# HELP
|
|
# ============================================================================
|
|
|
|
if ($Help) {
|
|
@"
|
|
Usage: .\windows-container-smoke-tests.ps1 [OPTIONS]
|
|
|
|
Smoke-test Windows container infrastructure. PowerShell 5.1+.
|
|
Designed for Windows Server or Windows 10/11 with containers enabled.
|
|
|
|
Parameters:
|
|
-ContainerRuntime RT Container runtime: docker (default), containerd
|
|
-TestImage IMAGE Test image (default: mcr.microsoft.com/windows/nanoserver:ltsc2022)
|
|
-OutputFormat FORMAT Output: text (default), tap
|
|
-NoColor Disable coloured output
|
|
-Verbose Show debug output
|
|
-Help Show this help
|
|
|
|
Examples:
|
|
.\windows-container-smoke-tests.ps1
|
|
.\windows-container-smoke-tests.ps1 -ContainerRuntime containerd
|
|
.\windows-container-smoke-tests.ps1 -TestImage mcr.microsoft.com/windows/servercore:ltsc2022
|
|
.\windows-container-smoke-tests.ps1 -OutputFormat tap -NoColor
|
|
"@
|
|
exit 0
|
|
}
|
|
|
|
# ============================================================================
|
|
# STATE
|
|
# ============================================================================
|
|
|
|
$script:Pass = 0
|
|
$script:Fail = 0
|
|
$script:Skip = 0
|
|
$script:Total = 0
|
|
$script:Results = @()
|
|
$script:StartTime = Get-Date
|
|
|
|
# ============================================================================
|
|
# COLORS
|
|
# ============================================================================
|
|
|
|
function Write-Color {
|
|
param([string]$Text, [string]$Color = "White")
|
|
if ($NoColor) {
|
|
Write-Host $Text
|
|
} else {
|
|
Write-Host $Text -ForegroundColor $Color
|
|
}
|
|
}
|
|
|
|
function Write-Log { param([string]$Msg) Write-Color "[INFO] $Msg" "Cyan" }
|
|
function Write-Warn { param([string]$Msg) Write-Color "[WARN] $Msg" "Yellow" }
|
|
function Write-Err { param([string]$Msg) Write-Color "[ERROR] $Msg" "Red" }
|
|
|
|
# ============================================================================
|
|
# TEST RESULT RECORDING
|
|
# ============================================================================
|
|
|
|
function Record-Pass {
|
|
param([string]$Name, [string]$Detail = "")
|
|
$script:Pass++
|
|
$script:Total++
|
|
$script:Results += [PSCustomObject]@{ Status="PASS"; Name=$Name; Detail=$Detail }
|
|
if ($OutputFormat -eq "tap") {
|
|
Write-Host "ok $($script:Total) - $Name$(if($Detail){" ($Detail)"})"
|
|
} else {
|
|
$mark = if ($NoColor) { "[PASS]" } else { [char]0x2713 }
|
|
$msg = " $mark $Name"
|
|
if ($Detail) { $msg += " - $Detail" }
|
|
Write-Color $msg "Green"
|
|
}
|
|
}
|
|
|
|
function Record-Fail {
|
|
param([string]$Name, [string]$Detail = "")
|
|
$script:Fail++
|
|
$script:Total++
|
|
$script:Results += [PSCustomObject]@{ Status="FAIL"; Name=$Name; Detail=$Detail }
|
|
if ($OutputFormat -eq "tap") {
|
|
Write-Host "not ok $($script:Total) - $Name"
|
|
if ($Detail) { Write-Host " # $Detail" }
|
|
} else {
|
|
$mark = if ($NoColor) { "[FAIL]" } else { [char]0x2717 }
|
|
$msg = " $mark $Name"
|
|
if ($Detail) { $msg += " - $Detail" }
|
|
Write-Color $msg "Red"
|
|
}
|
|
}
|
|
|
|
function Record-Skip {
|
|
param([string]$Name, [string]$Reason = "")
|
|
$script:Skip++
|
|
$script:Total++
|
|
$script:Results += [PSCustomObject]@{ Status="SKIP"; Name=$Name; Detail=$Reason }
|
|
if ($OutputFormat -eq "tap") {
|
|
Write-Host "ok $($script:Total) - $Name # SKIP $Reason"
|
|
} else {
|
|
$mark = if ($NoColor) { "[SKIP]" } else { [char]0x2298 }
|
|
$msg = " $mark $Name"
|
|
if ($Reason) { $msg += " - $Reason" }
|
|
Write-Color $msg "Yellow"
|
|
}
|
|
}
|
|
|
|
# ============================================================================
|
|
# HELPERS
|
|
# ============================================================================
|
|
|
|
function Test-CommandExists {
|
|
param([string]$Command)
|
|
$null -ne (Get-Command $Command -ErrorAction SilentlyContinue)
|
|
}
|
|
|
|
function Write-Section {
|
|
param([string]$Name)
|
|
if ($OutputFormat -eq "text") {
|
|
Write-Host ""
|
|
Write-Color $Name "White"
|
|
}
|
|
}
|
|
|
|
function Remove-TestContainer {
|
|
param([string]$Name)
|
|
try { docker rm -f $Name 2>&1 | Out-Null } catch {}
|
|
}
|
|
|
|
function Test-DockerReady {
|
|
param([string]$TestName, [switch]$NeedImage)
|
|
if (-not (Test-CommandExists "docker")) {
|
|
Record-Skip $TestName "docker command not found"; return $false
|
|
}
|
|
if ($NeedImage -and -not $script:ImageAvailable) {
|
|
Record-Skip $TestName "test image not available"; return $false
|
|
}
|
|
return $true
|
|
}
|
|
|
|
$script:ImageAvailable = $false
|
|
$script:TestContainerPrefix = "smoketest-container"
|
|
|
|
# ============================================================================
|
|
# TESTS
|
|
# ============================================================================
|
|
|
|
# -- 1. Container Service Running -------------------------------------------
|
|
|
|
function Test-ContainerService {
|
|
Write-Section "Service"
|
|
|
|
$serviceName = if ($ContainerRuntime -eq "containerd") { "containerd" } else { "docker" }
|
|
|
|
try {
|
|
$svc = Get-Service -Name $serviceName -ErrorAction Stop
|
|
if ($svc.Status -eq "Running") {
|
|
Record-Pass "Container service running" "$serviceName ($($svc.Status))"
|
|
} else {
|
|
Record-Fail "Container service running" "$serviceName is $($svc.Status)"
|
|
}
|
|
} catch {
|
|
Record-Fail "Container service running" "$serviceName service not found"
|
|
}
|
|
}
|
|
|
|
# -- 2. Daemon Responding ---------------------------------------------------
|
|
|
|
function Test-DaemonHealth {
|
|
if ($ContainerRuntime -eq "containerd") {
|
|
if (Test-CommandExists "ctr") {
|
|
try {
|
|
$output = ctr version 2>&1 | Out-String
|
|
if ($output -match "Version:" -or $output -match "Revision:") {
|
|
Record-Pass "Daemon responding" "containerd responding"
|
|
} else {
|
|
Record-Fail "Daemon responding" "ctr version returned unexpected output"
|
|
}
|
|
} catch {
|
|
Record-Fail "Daemon responding" "ctr error - $($_.Exception.Message)"
|
|
}
|
|
} else {
|
|
Record-Fail "Daemon responding" "ctr command not found"
|
|
}
|
|
return
|
|
}
|
|
|
|
if (-not (Test-CommandExists "docker")) {
|
|
Record-Fail "Daemon responding" "docker command not found"
|
|
return
|
|
}
|
|
|
|
try {
|
|
$output = docker info --format '{{.ServerVersion}}' 2>&1 | Out-String
|
|
if ($LASTEXITCODE -eq 0 -and $output -match "\d+\.\d+") {
|
|
Record-Pass "Daemon responding" "Docker Engine v$($output.Trim())"
|
|
} else {
|
|
Record-Fail "Daemon responding" "Docker daemon not responding"
|
|
}
|
|
} catch {
|
|
Record-Fail "Daemon responding" "docker error - $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
# -- 3. Container Runtime Mode ----------------------------------------------
|
|
|
|
function Test-RuntimeMode {
|
|
Write-Section "Runtime"
|
|
|
|
if ($ContainerRuntime -eq "containerd") {
|
|
Record-Skip "Container runtime mode" "mode detection not applicable to containerd"
|
|
return
|
|
}
|
|
if (-not (Test-DockerReady "Container runtime mode")) { return }
|
|
|
|
try {
|
|
$info = docker info 2>&1 | Out-String
|
|
if ($info -match "OSType:\s*windows") {
|
|
Record-Pass "Container runtime mode" "Windows containers"
|
|
} elseif ($info -match "OSType:\s*linux") {
|
|
Record-Fail "Container runtime mode" "Linux containers mode - switch to Windows containers"
|
|
} else {
|
|
Record-Fail "Container runtime mode" "unable to determine container mode"
|
|
}
|
|
} catch {
|
|
Record-Fail "Container runtime mode" "docker info error - $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
# -- 4. Image Pull -----------------------------------------------------------
|
|
|
|
function Test-ImagePull {
|
|
Write-Section "Images"
|
|
|
|
if (-not (Test-DockerReady "Image pull")) { return }
|
|
|
|
try {
|
|
# Check if image already exists locally
|
|
$existing = docker images --format '{{.Repository}}:{{.Tag}}' 2>&1 | Out-String
|
|
if ($existing -match [regex]::Escape($TestImage)) {
|
|
$script:ImageAvailable = $true
|
|
Record-Pass "Image pull" "$TestImage (already present)"
|
|
return
|
|
}
|
|
|
|
# Attempt pull
|
|
$pullOutput = docker pull $TestImage 2>&1 | Out-String
|
|
if ($LASTEXITCODE -eq 0) {
|
|
$script:ImageAvailable = $true
|
|
Record-Pass "Image pull" $TestImage
|
|
} elseif ($pullOutput -match "timeout|network|unreachable|no match") {
|
|
Record-Skip "Image pull" "network unavailable or image not found"
|
|
} else {
|
|
Record-Fail "Image pull" "pull failed - $($pullOutput.Trim())"
|
|
}
|
|
} catch {
|
|
Record-Skip "Image pull" "pull error - $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
# -- 5. Container Lifecycle --------------------------------------------------
|
|
|
|
function Test-ContainerLifecycle {
|
|
Write-Section "Lifecycle"
|
|
|
|
if (-not (Test-DockerReady "Container lifecycle" -NeedImage)) { return }
|
|
|
|
$name = "$($script:TestContainerPrefix)-lifecycle"
|
|
Remove-TestContainer $name
|
|
|
|
try {
|
|
# Create
|
|
docker create --name $name $TestImage cmd /c "echo smoketest" 2>&1 | Out-Null
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Record-Fail "Container lifecycle" "create failed"
|
|
Remove-TestContainer $name
|
|
return
|
|
}
|
|
docker start $name 2>&1 | Out-Null
|
|
Start-Sleep -Seconds 2
|
|
docker stop $name -t 5 2>&1 | Out-Null
|
|
docker rm $name 2>&1 | Out-Null
|
|
if ($LASTEXITCODE -eq 0) {
|
|
Record-Pass "Container lifecycle" "create, start, stop, remove"
|
|
} else {
|
|
Record-Fail "Container lifecycle" "remove failed"
|
|
}
|
|
} catch {
|
|
Record-Fail "Container lifecycle" $_.Exception.Message
|
|
Remove-TestContainer $name
|
|
}
|
|
}
|
|
|
|
# -- 6. Process Isolation Mode -----------------------------------------------
|
|
|
|
function Test-ProcessIsolation {
|
|
Write-Section "Isolation"
|
|
|
|
if (-not (Test-DockerReady "Process isolation mode" -NeedImage)) { return }
|
|
|
|
$name = "$($script:TestContainerPrefix)-process"
|
|
Remove-TestContainer $name
|
|
try {
|
|
$output = docker run --rm --isolation=process --name $name $TestImage cmd /c "echo process-ok" 2>&1 | Out-String
|
|
if ($LASTEXITCODE -eq 0 -and $output -match "process-ok") {
|
|
Record-Pass "Process isolation mode" "process isolation available"
|
|
} elseif ($output -match "not supported|not available|HCS") {
|
|
Record-Skip "Process isolation mode" "not supported on this host (requires Windows Server)"
|
|
} else {
|
|
Record-Fail "Process isolation mode" "unexpected result - $($output.Trim())"
|
|
}
|
|
} catch {
|
|
Record-Fail "Process isolation mode" $_.Exception.Message
|
|
} finally {
|
|
Remove-TestContainer $name
|
|
}
|
|
}
|
|
|
|
# -- 7. Hyper-V Isolation Mode -----------------------------------------------
|
|
|
|
function Test-HyperVIsolation {
|
|
if (-not (Test-DockerReady "Hyper-V isolation mode" -NeedImage)) { return }
|
|
|
|
# Check if Hyper-V is installed (client vs server detection)
|
|
$hypervEnabled = $false
|
|
try {
|
|
$hyperv = Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -ErrorAction Stop
|
|
$hypervEnabled = ($hyperv.State -eq "Enabled")
|
|
} catch {
|
|
try {
|
|
$hypervRole = Get-WindowsFeature -Name Hyper-V -ErrorAction Stop
|
|
$hypervEnabled = $hypervRole.Installed
|
|
} catch {}
|
|
}
|
|
if (-not $hypervEnabled) {
|
|
Record-Skip "Hyper-V isolation mode" "Hyper-V not installed"
|
|
return
|
|
}
|
|
|
|
$name = "$($script:TestContainerPrefix)-hyperv"
|
|
Remove-TestContainer $name
|
|
try {
|
|
$output = docker run --rm --isolation=hyperv --name $name $TestImage cmd /c "echo hyperv-ok" 2>&1 | Out-String
|
|
if ($LASTEXITCODE -eq 0 -and $output -match "hyperv-ok") {
|
|
Record-Pass "Hyper-V isolation mode" "Hyper-V isolation available"
|
|
} elseif ($output -match "not supported|not available|HCS") {
|
|
Record-Skip "Hyper-V isolation mode" "Hyper-V isolation not available"
|
|
} else {
|
|
Record-Fail "Hyper-V isolation mode" "unexpected result - $($output.Trim())"
|
|
}
|
|
} catch {
|
|
Record-Fail "Hyper-V isolation mode" $_.Exception.Message
|
|
} finally {
|
|
Remove-TestContainer $name
|
|
}
|
|
}
|
|
|
|
# -- 8. NAT Network Exists ---------------------------------------------------
|
|
|
|
function Test-NATNetwork {
|
|
Write-Section "Networking"
|
|
|
|
if (-not (Test-DockerReady "NAT network exists")) { return }
|
|
|
|
try {
|
|
$networks = docker network ls --format '{{.Name}}' 2>&1 | Out-String
|
|
if ($networks -match "(?m)^nat$") {
|
|
Record-Pass "NAT network exists" "default nat network present"
|
|
} else {
|
|
Record-Fail "NAT network exists" "nat network not found"
|
|
}
|
|
} catch {
|
|
Record-Fail "NAT network exists" $_.Exception.Message
|
|
}
|
|
}
|
|
|
|
# -- 9. Outbound Connectivity -----------------------------------------------
|
|
|
|
function Test-OutboundConnectivity {
|
|
if (-not (Test-DockerReady "Container outbound connectivity" -NeedImage)) { return }
|
|
|
|
$name = "$($script:TestContainerPrefix)-outbound"
|
|
Remove-TestContainer $name
|
|
try {
|
|
$output = docker run --rm --name $name $TestImage cmd /c "ping -n 1 8.8.8.8" 2>&1 | Out-String
|
|
if ($LASTEXITCODE -eq 0 -and $output -match "Reply from|bytes=") {
|
|
Record-Pass "Container outbound connectivity" "ping to 8.8.8.8 succeeded"
|
|
} elseif ($output -match "timed out|unreachable|could not find|General failure") {
|
|
Record-Fail "Container outbound connectivity" "no outbound connectivity"
|
|
} else {
|
|
Record-Fail "Container outbound connectivity" "exit code $LASTEXITCODE"
|
|
}
|
|
} catch {
|
|
Record-Fail "Container outbound connectivity" $_.Exception.Message
|
|
} finally {
|
|
Remove-TestContainer $name
|
|
}
|
|
}
|
|
|
|
# -- 10. Port Mapping --------------------------------------------------------
|
|
|
|
function Test-PortMapping {
|
|
if (-not (Test-DockerReady "Port mapping" -NeedImage)) { return }
|
|
|
|
$name = "$($script:TestContainerPrefix)-portmap"
|
|
$hostPort = 48199
|
|
Remove-TestContainer $name
|
|
try {
|
|
docker run -d --rm --name $name -p "${hostPort}:80" $TestImage cmd /c "ping -n 30 127.0.0.1 >nul" 2>&1 | Out-Null
|
|
Start-Sleep -Seconds 3
|
|
|
|
$state = docker inspect --format '{{.State.Running}}' $name 2>&1 | Out-String
|
|
if ($state.Trim() -eq "true") {
|
|
$portInfo = docker port $name 2>&1 | Out-String
|
|
if ($portInfo -match "$hostPort" -or $portInfo -match "80/tcp") {
|
|
Record-Pass "Port mapping" "port $hostPort mapped to container"
|
|
} else {
|
|
Record-Pass "Port mapping" "container running with port binding"
|
|
}
|
|
} else {
|
|
Record-Fail "Port mapping" "container did not stay running for port test"
|
|
}
|
|
} catch {
|
|
Record-Fail "Port mapping" $_.Exception.Message
|
|
} finally {
|
|
Remove-TestContainer $name
|
|
}
|
|
}
|
|
|
|
# -- 11. Volume Mount --------------------------------------------------------
|
|
|
|
function Test-VolumeMount {
|
|
Write-Section "Storage"
|
|
|
|
if (-not (Test-DockerReady "Volume mount" -NeedImage)) { return }
|
|
|
|
$name = "$($script:TestContainerPrefix)-volume"
|
|
$tempDir = Join-Path $env:TEMP "container-smoke-test-vol"
|
|
Remove-TestContainer $name
|
|
try {
|
|
if (Test-Path $tempDir) { Remove-Item $tempDir -Recurse -Force }
|
|
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
|
|
Set-Content -Path (Join-Path $tempDir "testfile.txt") -Value "smoke-test-data"
|
|
|
|
$output = docker run --rm --name $name -v "${tempDir}:C:\testmount" $TestImage cmd /c "type C:\testmount\testfile.txt" 2>&1 | Out-String
|
|
if ($LASTEXITCODE -eq 0 -and $output -match "smoke-test-data") {
|
|
Record-Pass "Volume mount" "bind mount read successful"
|
|
} else {
|
|
Record-Fail "Volume mount" "bind mount failed - $($output.Trim())"
|
|
}
|
|
} catch {
|
|
Record-Fail "Volume mount" $_.Exception.Message
|
|
} finally {
|
|
Remove-TestContainer $name
|
|
if (Test-Path $tempDir) { Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue }
|
|
}
|
|
}
|
|
|
|
# -- 12. Container Logs ------------------------------------------------------
|
|
|
|
function Test-ContainerLogs {
|
|
Write-Section "Logging"
|
|
|
|
if (-not (Test-DockerReady "Container logs" -NeedImage)) { return }
|
|
|
|
$name = "$($script:TestContainerPrefix)-logs"
|
|
Remove-TestContainer $name
|
|
try {
|
|
docker run --name $name $TestImage cmd /c "echo log-output-test" 2>&1 | Out-Null
|
|
Start-Sleep -Seconds 1
|
|
|
|
$logs = docker logs $name 2>&1 | Out-String
|
|
if ($logs -match "log-output-test") {
|
|
Record-Pass "Container logs" "docker logs returned expected output"
|
|
} else {
|
|
Record-Fail "Container logs" "docker logs did not return expected output"
|
|
}
|
|
} catch {
|
|
Record-Fail "Container logs" $_.Exception.Message
|
|
} finally {
|
|
Remove-TestContainer $name
|
|
}
|
|
}
|
|
|
|
# -- 13. Image List ----------------------------------------------------------
|
|
|
|
function Test-ImageList {
|
|
if (-not (Test-DockerReady "Image list")) { return }
|
|
|
|
try {
|
|
$images = docker images --format '{{.Repository}}:{{.Tag}}' 2>&1
|
|
$imageCount = ($images | Where-Object { $_ -match "\S" -and $_ -notmatch "ERROR" } | Measure-Object).Count
|
|
if ($imageCount -gt 0) {
|
|
Record-Pass "Image list" "$imageCount image(s)"
|
|
} else {
|
|
Record-Fail "Image list" "no images found"
|
|
}
|
|
} catch {
|
|
Record-Fail "Image list" $_.Exception.Message
|
|
}
|
|
}
|
|
|
|
# -- 14. Event Log -----------------------------------------------------------
|
|
|
|
function Test-EventLog {
|
|
Write-Section "Event Log"
|
|
|
|
try {
|
|
$containerErrors = @()
|
|
|
|
foreach ($logName in @('System', 'Application')) {
|
|
try {
|
|
$events = Get-WinEvent -FilterHashtable @{
|
|
LogName = $logName
|
|
Level = 2 # Error
|
|
StartTime = (Get-Date).AddHours(-24)
|
|
} -ErrorAction Stop | Where-Object {
|
|
$_.ProviderName -match "docker|container|Hyper-V" -or
|
|
$_.Message -match "container|docker|containerd"
|
|
}
|
|
if ($events) { $containerErrors += $events }
|
|
} catch [Exception] {
|
|
if ($_.Exception.Message -notmatch "No events were found") {
|
|
Write-Verbose "$logName log query error: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
}
|
|
|
|
$errorCount = ($containerErrors | Measure-Object).Count
|
|
if ($errorCount -eq 0) {
|
|
Record-Pass "Event log" "no container errors in last 24h"
|
|
} else {
|
|
$latest = $containerErrors | Sort-Object TimeCreated -Descending | Select-Object -First 1
|
|
$detail = "$errorCount error(s) in last 24h - latest: $($latest.ProviderName) - $($latest.Message.Substring(0, [Math]::Min(80, $latest.Message.Length)))"
|
|
Record-Fail "Event log" $detail
|
|
}
|
|
} catch {
|
|
Record-Skip "Event log" "unable to query event logs - $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
# ============================================================================
|
|
# OUTPUT
|
|
# ============================================================================
|
|
|
|
function Write-Header {
|
|
if ($OutputFormat -eq "tap") {
|
|
Write-Host "TAP version 13"
|
|
} else {
|
|
Write-Host ""
|
|
Write-Color "Windows Container Smoke Tests" "White"
|
|
Write-Host "Runtime: $ContainerRuntime"
|
|
Write-Host "Image: $TestImage"
|
|
Write-Host "Time: $(Get-Date -Format 'yyyy-MM-ddTHH:mm:ssZ')"
|
|
}
|
|
}
|
|
|
|
function Write-Summary {
|
|
$duration = [math]::Floor(((Get-Date) - $script:StartTime).TotalSeconds)
|
|
|
|
if ($OutputFormat -eq "tap") {
|
|
Write-Host "1..$($script:Total)"
|
|
Write-Host "# pass $($script:Pass)"
|
|
Write-Host "# fail $($script:Fail)"
|
|
Write-Host "# skip $($script:Skip)"
|
|
} else {
|
|
Write-Host ""
|
|
$separator = [string]::new([char]0x2500, 40)
|
|
Write-Color $separator "White"
|
|
Write-Color "Summary $ContainerRuntime ($TestImage)" "White"
|
|
|
|
$summaryLine = " $($script:Pass) passed $($script:Fail) failed $($script:Skip) skipped (${duration}s)"
|
|
Write-Host $summaryLine
|
|
Write-Color $separator "White"
|
|
|
|
if ($script:Fail -eq 0) {
|
|
Write-Color "All tests passed." "Green"
|
|
} else {
|
|
Write-Color "$($script:Fail) test(s) failed." "Red"
|
|
}
|
|
}
|
|
}
|
|
|
|
# ============================================================================
|
|
# MAIN
|
|
# ============================================================================
|
|
|
|
Write-Header
|
|
|
|
# Run all tests
|
|
Test-ContainerService
|
|
Test-DaemonHealth
|
|
Test-RuntimeMode
|
|
Test-ImagePull
|
|
Test-ContainerLifecycle
|
|
Test-ProcessIsolation
|
|
Test-HyperVIsolation
|
|
Test-NATNetwork
|
|
Test-OutboundConnectivity
|
|
Test-PortMapping
|
|
Test-VolumeMount
|
|
Test-ContainerLogs
|
|
Test-ImageList
|
|
Test-EventLog
|
|
|
|
Write-Summary
|
|
|
|
if ($script:Fail -eq 0) { exit 0 } else { exit 1 }
|