From 02dcf0a3459aa5f079c41e138071841488655739 Mon Sep 17 00:00:00 2001 From: Tim Wright Date: Wed, 27 May 2026 14:51:18 +1200 Subject: [PATCH 1/3] windows ut pipeline --- .github/workflows/validate-windows-ut.yml | 125 ++++++++++++++++++++-- 1 file changed, 119 insertions(+), 6 deletions(-) diff --git a/.github/workflows/validate-windows-ut.yml b/.github/workflows/validate-windows-ut.yml index 69b1a896b64..cfbcf45d126 100644 --- a/.github/workflows/validate-windows-ut.yml +++ b/.github/workflows/validate-windows-ut.yml @@ -9,21 +9,134 @@ jobs: pester-test: name: Pester test runs-on: windows-latest + permissions: + contents: read + checks: write + issues: write + pull-requests: write steps: - name: Check out repository code uses: actions/checkout@v6 with: fetch-depth: 1 + + - name: Setup + shell: pwsh + run: | + + Get-Module -ListAvailable -Name Pester + if (-not (Test-Path test-results)) { New-Item -ItemType Directory -Force test-results | Out-Null } + if (-not (Test-Path coverage)) { New-Item -ItemType Directory -Force coverage | Out-Null } + Install-Module -Name powershell-yaml -Force + - name: Perform a Pester test from the parts/windows/*.tests.ps1 file - shell: powershell + if: (!cancelled()) + shell: pwsh run: | - Invoke-Pester parts/windows/*.tests.ps1 -Passthru + + $config = New-PesterConfiguration + $config.Run.Path = "parts/windows/" + $config.Run.PassThru = $true + #$config.CodeCoverage.CoveragePercentTarget = 80 + $config.CodeCoverage.OutputPath = "coverage/parts_windows.xml" + $config.CodeCoverage.Enabled = $true + $config.CodeCoverage.ExcludeTests = $true + $config.CodeCoverage.OutputFormat = "Cobertura" + $config.TestResult.Enabled = $true + $config.TestResult.OutputFormat = "JUnitXml" + $config.TestResult.OutputPath = "test-results/parts_windows.xml" + + # Because you're looking here, you might want to know how to run a single test. Add "-Tag Focus" to + # the test, then set this filter to only run tests with that tag. Don't forget to remove the tag and + # filter when you're done! + # $config.Filter.Tag = "Focus" + + $result = Invoke-Pester -Configuration $config + + $result + $result.Failed | Format-Table + - name: Perform a Pester test from the vhdbuilder/packer/windows/*.tests.ps1 file + if: (!cancelled()) shell: pwsh run: | - Invoke-Pester vhdbuilder/packer/windows/*.tests.ps1 -Passthru + + $config = New-PesterConfiguration + $config.Run.Path = "vhdbuilder/packer/windows/" + $config.Run.PassThru = $true + #$config.CodeCoverage.CoveragePercentTarget = 80 + $config.CodeCoverage.OutputPath = "coverage/vhdbuilder_packer_windows.xml" + $config.CodeCoverage.Enabled = $true + $config.CodeCoverage.ExcludeTests = $true + $config.CodeCoverage.OutputFormat = "Cobertura" + $config.TestResult.Enabled = $true + $config.TestResult.OutputFormat = "JUnitXml" + $config.TestResult.OutputPath = "test-results/vhdbuilder_packer_windows.xml" + + # Because you're looking here, you might want to know how to run a single test. Add "-Tag Focus" to + # the test, then set this filter to only run tests with that tag. Don't forget to remove the tag and + # filter when you're done! + # $config.Filter.Tag = "Focus" + + $result = Invoke-Pester -Configuration $config + + $result + $result.Failed | Format-Table + - name: Perform a Pester test from the staging/cse/windows/*.tests.ps1 file - shell: powershell + if: (!cancelled()) + shell: pwsh run: | - Install-Module -Name powershell-yaml -Force - Invoke-Pester staging/cse/windows/*.tests.ps1 -Passthru + + $config = New-PesterConfiguration + $config.Run.Path = "staging/cse/windows/" + $config.Run.PassThru = $true + #$config.CodeCoverage.CoveragePercentTarget = 80 + $config.CodeCoverage.OutputPath = "coverage/staging_cse_windows.xml" + $config.CodeCoverage.Enabled = $true + $config.CodeCoverage.ExcludeTests = $true + $config.CodeCoverage.OutputFormat = "Cobertura" + $config.TestResult.Enabled = $true + $config.TestResult.OutputFormat = "JUnitXml" + $config.TestResult.OutputPath = "test-results/staging_cse_windows.xml" + + # Because you're looking here, you might want to know how to run a single test. Add "-Tag Focus" to + # the test, then set this filter to only run tests with that tag. Don't forget to remove the tag and + # filter when you're done! + # $config.Filter.Tag = "Focus" + + $result = Invoke-Pester -Configuration $config + + $result + $result.Failed | Format-Table + + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action/windows@v2 + if: (!cancelled()) + with: + files: | + test-results/parts_windows.xml + test-results/vhdbuilder_packer_windows.xml + test-results/staging_cse_windows.xml + + # coverage upload is linux runner only - need to find a windows way of doing this. + # - name: Code Coverage Report + # uses: irongut/CodeCoverageSummary@v1.3.0 + # if: (!cancelled()) + # with: + # filename: coverage/*.xml + # badge: true + # fail_below_min: false + # format: markdown + # hide_branch_rate: false + # hide_complexity: true + # indicators: true + # output: both + # thresholds: '60 80' + + # - name: Add Coverage PR Comment + # uses: marocchino/sticky-pull-request-comment@v2 + # if: github.event_name == 'pull_request' + # with: + # recreate: true + # path: code-coverage-results.md From d103398caa3c85838b51f8470884c88f4805bf9a Mon Sep 17 00:00:00 2001 From: Tim Wright Date: Wed, 27 May 2026 15:20:10 +1200 Subject: [PATCH 2/3] fixes --- .gitignore | 2 + staging/cse/windows/README | 123 ++- staging/cse/windows/azurecnifunc.tests.ps1 | 26 +- staging/cse/windows/containerdfunc.tests.ps1 | 797 ++++++++++--------- 4 files changed, 512 insertions(+), 436 deletions(-) diff --git a/.gitignore b/.gitignore index c0a41cd6619..064ec93b75e 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,8 @@ out/ build/ test/junit/ +coverage/ +test-results/ test/e2e/kubernetes/junit.xml _logs/ *.swp diff --git a/staging/cse/windows/README b/staging/cse/windows/README index 65e63d96c82..419c8c03986 100644 --- a/staging/cse/windows/README +++ b/staging/cse/windows/README @@ -1,21 +1,22 @@ # Unit Tests + 1. Execute unit tests on pipeline. The yaml file of unit test pipeline is .github/workflows/validate-windows-ut.yml. 1. Execute unit tests locally: - 1. Please install [pester](https://github.com/pester/Pester) with the command `Install-Module -Name pester -SkipPublisherCheck -Force`. - - Doc: https://pester.dev/docs/quick-start - - Courses: https://docs.microsoft.com/en-us/shows/testing-powershell-with-pester/ - 1. You need to run unit tests on Windows. - 1. If your git repo is stored in WSL, you can invoke the tests by doing something like this in powershell from windows (not from a WSL shell itself): - - `cd \\wsl$\Ubuntu\home\tim\git\AgentBaker` (or wherever you have your WSL AgentBaker repository) - - `Invoke-Pester vhdbuilder/packer/windows/*.tests.ps1 -Passthru` (or whatever pester test you want to run) - 1. Run unit test: - - `Invoke-Pester parts/windows/*.tests.ps1 -Passthru` - - `Invoke-Pester staging/cse/windows/*.tests.ps1 -Passthru` - - `Invoke-Pester vhdbuilder/packer/windows/*.tests.ps1 -Passthru` - 1. Watch out different powershell versions. Sometimes JSON parsing works differently in your local powershell and in the pipeline. Which is annoying. +1. Please install [pester](https://github.com/pester/Pester) with the command `Install-Module -Name pester -SkipPublisherCheck -Force`. + - Doc: + - Courses: +1. You need to run unit tests on Windows. +1. If your git repo is stored in WSL, you can invoke the tests by doing something like this in powershell from windows (not from a WSL shell itself): + - `cd \\wsl$\Ubuntu\home\tim\git\AgentBaker` (or wherever you have your WSL AgentBaker repository) +1. Run unit test: + - look at the pipeline for how to do this. It has a comment about how to use Tags to run only a particular test: + - .github/workflows/validate-windows-ut.yml +1. Watch out different powershell versions. Sometimes JSON parsing works differently in your local powershell and in the pipeline. Which is annoying. # Test with AKS RP + 1. Run below commands to build a test package + ```bash branchName="master" currentCseVersion="v0.0.51" # `WindowsCSEScriptsPackage` defined in `parts/windows/kuberneteswindowssetup.ps1` @@ -29,78 +30,92 @@ pushd aks-windows-cse unzip ../aks-windows-cse-scripts-*.zip rm ../*.zip - files=("azurecnifunc.ps1" "calicofunc.ps1" "configfunc.ps1" "containerdfunc.ps1" "containerdtemplate.toml" "kubeletfunc.ps1" "kubernetesfunc.ps1" "nvidiagpudriverfunc.ps1" "securetlsbootstrapfunc.ps1" "windowsciliumnetworkingfunc.ps1") - for file in ${files[@]}; do - echo "Downloading $file from $url/$file" - curl -O "$url/$file" - done - - pushd debug - files=("VFP.psm1" "captureNetworkFlows.ps1" "collect-windows-logs.ps1" "collectlogs.ps1" "dumpVfpPolicies.ps1" "helper.psm1" "hns.psm1" "hns.v2.psm1" "networkhealth.ps1" "portReservationTest.ps1" "starthnstrace.cmd" "starthnstrace.ps1" "startpacketcapture.cmd" "startpacketcapture.ps1" "stoppacketcapture.cmd") - for file in ${files[@]}; do - echo "Downloading $file from $url/debug/$file" - curl -O "$url/debug/$file" - done - popd - - pushd provisioningscripts - files=("cleanupnetwork.ps1" "hnsremediator.ps1" "hostsconfigagent.ps1" "kubeletstart.ps1" "kubeproxystart.ps1" "loggenerator.ps1" "windowslogscleanup.ps1" "windowsnodereset.ps1" "windowssecuretls.ps1" "securetlsbootstrap.ps1") - for file in ${files[@]}; do - echo "Downloading $file from $url/provisioningscripts/$file" - curl -O "$url/provisioningscripts/$file" - done - popd - - zip -r "../aks-windows-cse-scripts-$testCseVersion.zip" * + files=("azurecnifunc.ps1" "calicofunc.ps1" "configfunc.ps1" "containerdfunc.ps1" "containerdtemplate.toml" "kubeletfunc.ps1" "kubernetesfunc.ps1" "nvidiagpudriverfunc.ps1" "securetlsbootstrapfunc.ps1" "windowsciliumnetworkingfunc.ps1") + for file in ${files[@]}; do + echo "Downloading $file from $url/$file" + curl -O "$url/$file" + done + + pushd debug + files=("VFP.psm1" "captureNetworkFlows.ps1" "collect-windows-logs.ps1" "collectlogs.ps1" "dumpVfpPolicies.ps1" "helper.psm1" "hns.psm1" "hns.v2.psm1" "networkhealth.ps1" "portReservationTest.ps1" "starthnstrace.cmd" "starthnstrace.ps1" "startpacketcapture.cmd" "startpacketcapture.ps1" "stoppacketcapture.cmd") + for file in ${files[@]}; do + echo "Downloading $file from $url/debug/$file" + curl -O "$url/debug/$file" + done + popd + + pushd provisioningscripts + files=("cleanupnetwork.ps1" "hnsremediator.ps1" "hostsconfigagent.ps1" "kubeletstart.ps1" "kubeproxystart.ps1" "loggenerator.ps1" "windowslogscleanup.ps1" "windowsnodereset.ps1" "windowssecuretls.ps1" "securetlsbootstrap.ps1") + for file in ${files[@]}; do + echo "Downloading $file from $url/provisioningscripts/$file" + curl -O "$url/provisioningscripts/$file" + done + popd + + zip -r "../aks-windows-cse-scripts-$testCseVersion.zip" * popd ``` + 1. Upload the test package to your Azure storage container `$web` - - You need to enable `Static website` in your storage account on Azure portal - - You need to note down your `Primary endpoint` of `Static website` of your storage account - - The test package download URL should be `[Primary endpoint]/aks-windows-cse-scripts-[$testCseVersion].zip` + +- You need to enable `Static website` in your storage account on Azure portal +- You need to note down your `Primary endpoint` of `Static website` of your storage account +- The test package download URL should be `[Primary endpoint]/aks-windows-cse-scripts-[$testCseVersion].zip` + 1. Test it with AKS RP in the toggle `windows-cse-package-url.yaml` # AKS Windows CSE Scripts Package + All files except *.test.ps1 and README will be published in AKS Windows CSE Scripts Package. ## v0.0.51 + - feat: Add support for setting a different CNI config on cilium clusters #5482 ## v0.0.50 + - fix: do not use return in updating kubelet node labels #5151 ## v0.0.49 + - refactor: configure kubelet serving certificate rotation label at runtime #5115 ## v0.0.48 + - chore: refine collect-windows-logs.ps1 to enable crt by default #4863 - chore: add windowsnodereset.log in loggenerator.ps1 #4869 - feat: respect nodepool tags for disabling kubelet serving certificate rotation #4911 ## v0.0.46 + - feat: add log events in Windows CSE #4657 - feat: block 32526 to wireserver on Windows nodes #4758 ## v0.0.45 + - Edit containerd snapshot size calculation #4345 - Add more containerd logs #4398 - feat: use PowerShell commands to set pagefile size instead of wmic #4594 ## v0.0.44 + - fix: do not collect vfp ports in loggenerator.ps1 #4542 ## v0.0.43 + - feat: support Windows private k8s packages #4496 - doc: update Windows CSE script package doc #4447 - feat: cse supports windows-exporter log #4389 - feat: refine collect-windows-logs.ps1 #4383 ## v0.0.42 + - feat: add parameters for collect-windows-logs.ps1 #4325 - feat: support skipping legacy code CleanupNetwork in AKS Windows CSE #4131 - chore: sync the latest Windows debug scripts #4279 ## v0.0.41 + - feat: update window cse to install credental provider #4281 - Add more containerd logs to collect-windows-logs.ps1 #3952 - feat: add system startup task to expand os volume #4213 @@ -110,44 +125,56 @@ All files except *.test.ps1 and README will be published in AKS Windows CSE Scri - fix: avoid running Windows CSE multiple times. #4158 ## v0.0.40 + - feat: set --node-ip for Windows kubelet in k8s v1.29+ #4148 ## v0.0.39 + - feat: support Windows container local dumps #3684 ## v0.0.38 + - chore: support cse log for windows #3944 ## v0.0.37 + - chore: add the output of collect-windows-logs-output into the node log #3919 - fix: check the path of nvidiaInstallLogFolder #3920 - feat: collect kubectl information in AKS Windows node log #3936 ## v0.0.36 + - chore: support to test Windows 2025 #3871 ## v0.0.35 + - feat: update cse script package changes for aks windows gpu feature #3794 - feat: optimize resize-osdrive replacing get-partitionsupportedsize with diskpart #3804 ## v0.0.34 + - feat: adding changes to AgentBaker for Stateless CNI on Windows #3704 - chore: update the MaxRetryCount to 60 in Check-APIServerConnectivity of Windows CSE #3737 ## v0.0.33 + - update signature of binaries in windows cse script package ## v0.0.32 + - chore: update regkey for disabling OutboundNAT on WS2022 nodes #3655 ## v0.0.31 + - fix: WS2022 support disabling OutboundNAT without updating regkey in CSE #3621 - chore: use Get-WindowsVersion for os version check #3630 ## v0.0.30 + - chore: added check on Windows CSE for vmss type so we don't throw an error #3520 ## v0.0.29 + - chore: remove unused function #3415 - chore: cleanup docker in Windows CSE scripts #3403 - feat: collect dump files #3437 @@ -155,22 +182,27 @@ All files except *.test.ps1 and README will be published in AKS Windows CSE Scri - fix: fix wrong log in Windows debug script #3446 ## v0.0.27 + - feat: clear existing images tags in non-AzureChinaCloud #3422 - feat: retag images for AzureChinaCloud in provisioning Windows nodes #3414 - feat: collect windowslogscleanup.log in GuestVMLogs #3355 ## v0.0.26 + - fix: exit Windows CSE when it fails in Resize-OSDrive #3212 ## v0.0.25 + - feat: remove Kubelet flag --container-runtime in k8s 1.27+ #3181 ## v0.0.24 + - fix: do not add IPv6DualStack to Windows kube-proxy feature-gates when K8s >= 1.25.0 #3024 - fix: add file check for windows log script #3029 - fix: Windows Azure CNI conflist should split ExceptionList by IP family #3049 ## v0.0.23 + - fix: Remove all download steps in Windows debug scripts #2738 - fix: Validate whether the PID of HNS service is valid #2781 - fix: Add hosts config agent logs in lockedFiles #2846 @@ -178,17 +210,21 @@ All files except *.test.ps1 and README will be published in AKS Windows CSE Scri - feat: use cluster subnets for dual-stack #2867 ## v0.0.22 + - fix: Remove vfpOutput.txt before generating new logs #2566 ## v0.0.21 + - feat: Support Windows calico v3.23.3 in AKS Windows CSE scripts #2515 - fix: Enable WinDSR with the new Azure CNI config #2516 - fix: Start Windows calico services for v3.23.3 #2525 ## v0.0.20 + - feat: Cache log collect scripts in Windows CSE script package #2481 ## v0.0.19 + - feat: Add AKS logs in Windows GuestVMLogs #2426 - Don't add VnetCidrs to exceptionlist when network plugin mode is overlay #2428 - chore: Update RegKey HNSControlFlag when disable Windows OutboundNAT #2417 @@ -196,38 +232,49 @@ All files except *.test.ps1 and README will be published in AKS Windows CSE Scri NOTE: v0.0.17 and v0.0.18 have some issues so we skip them. ## v0.0.16 + - chore: move the installation of containerd from containerdfunc.ps1 to windowscsehelper.ps1 #2199 ## v0.0.15 + - feat: Use hns.v2.psm1 for Windows containerd in CSE package #2223 - fix: Restart Calico services in HNS remediato #2233 - feat: Copy new Windows debug scripts in node provisioning #2164 ## v0.0.14 + - feat: Windows CSE support disabling OutboundNAT and removing ROUTE if WinDSR is enabled #2144 ## v0.0.13 + - feat: Add hnsremediator for hns crash in Windows nodes #2085 ## v0.0.12 + - feat: Add specified code in Windows CSE #2033 ## v0.0.11 + - feat: Support mutilple Windows containerd versions in cse package #1945 ## v0.0.10 + - feat: Update the command to exclude UDP source port in Windows node #1924 **NOTE: DO NOT USE** ## v0.0.9 + - fix: Exclude UDP source port 65330 in Windows nodes #1784 **NOTE: DO NOT USE** ## v0.0.8 + - Use provisioning scripts in CSEScriptsPackage. #1623 ## v0.0.7 + - Exclude dockerd in Microsoft Defender #1600 ## v0.0.6 + - Refine Get-WindowsVersion to support Windows 2022 #1565 ## v0.0.5 diff --git a/staging/cse/windows/azurecnifunc.tests.ps1 b/staging/cse/windows/azurecnifunc.tests.ps1 index 0194a2fa379..8f4b50ffdcd 100644 --- a/staging/cse/windows/azurecnifunc.tests.ps1 +++ b/staging/cse/windows/azurecnifunc.tests.ps1 @@ -8,6 +8,11 @@ BeforeAll { $script:capturedContent = $Value } -Verifiable + function Create-Directory { + param($FullPath, $DirectoryUsage) + New-Item -ItemType Directory -Path $FullPath -ErrorAction SilentlyContinue | Out-Null + } + function Invoke-WebRequest { return @" [ @@ -27,7 +32,26 @@ BeforeAll { # this often doesn't exist in test environment, so create it here so we can mock it later. function New-HNSNetwork {} + function Get-NetIPAddress {} + function Get-NetAdapter {} + function Get-NetIPConfiguration {} + function Restart-Service {} + function Get-Service {} + function Start-Service {} + function Assert-FileExists { + param($Filename, $ExitCode) + } + + # Stubs for Windows-only service management cmdlets unavailable on Linux + Mock Get-Service -MockWith { + param($Name, $ErrorAction) + } + + Mock Start-Service -MockWith { + param($Name, $ErrorAction) + } + filter RemoveNulls { $_ -replace '\0', '' } # overwrite Start-Sleep and Write-Host to avoid unnecessary waiting and output during tests. function Start-Sleep {} function Write-Host {} @@ -151,7 +175,7 @@ Describe 'Set-AzureCNIConfig' { } Context 'Cilium (ebpf dataplane) is enabled' { - It "Should use azure-cns as IPAM" { + It "Should use azure-cns as IPAM" -Tag Focus { Set-Default-AzureCNI "AzureCNI.Default.conflist" $global:CiliumDataplaneEnabled = $true diff --git a/staging/cse/windows/containerdfunc.tests.ps1 b/staging/cse/windows/containerdfunc.tests.ps1 index 1a7942c3bee..baa4874939f 100644 --- a/staging/cse/windows/containerdfunc.tests.ps1 +++ b/staging/cse/windows/containerdfunc.tests.ps1 @@ -1,523 +1,526 @@ -BeforeAll { - if (-not (Get-PSDrive -Name C -ErrorAction SilentlyContinue)) { - New-PSDrive -Name C -PSProvider FileSystem -Root ([System.IO.Path]::GetTempPath()) | Out-Null - } - - # Define mock functions before loading the scripts - function Write-Log { - param($Message) - Write-Host "$Message" - } +Describe "Containerd Functions Tests" { - function Set-ExitCode { - param($ExitCode, $ErrorMessage) - Write-Host "MOCK: Exit Code would be: $ExitCode, Error: $ErrorMessage" - # Don't actually exit in tests - } + BeforeAll { + if (-not (Get-PSDrive -Name C -ErrorAction SilentlyContinue)) { + New-PSDrive -Name C -PSProvider FileSystem -Root ([System.IO.Path]::GetTempPath()) | Out-Null + } - # Define Create-Directory stub function (used by Set-ContainerdRegistryConfig) - function Create-Directory { - param($FullPath, $DirectoryUsage) - # Do nothing in tests - just a stub - } + # Define mock functions before loading the scripts + function Write-Log { + param($Message) + Write-Host "$Message" + } - # Mock Set-Content to avoid permission denied errors - Mock Set-Content -MockWith { - param($Path, $Value) - Write-Host "SET-CONTENT: Path: $Path, Content: $Value" - } + function Set-ExitCode { + param($ExitCode, $ErrorMessage) + Write-Host "MOCK: Exit Code would be: $ExitCode, Error: $ErrorMessage" + # Don't actually exit in tests + } - function Get-WindowsPauseVersion { - return "ltsc2022" - } + # Define Create-Directory stub function (used by Set-ContainerdRegistryConfig) + function Create-Directory { + param($FullPath, $DirectoryUsage) + # Do nothing in tests - just a stub + } - function Get-WindowsVersion { - return "ltsc2022" - } + # Mock Set-Content to avoid permission denied errors + Mock Set-Content -MockWith { + param($Path, $Value) + Write-Host "SET-CONTENT: Path: $Path, Content: $Value" + } - . $PSScriptRoot\containerdfunc.ps1 - . $PSScriptRoot\..\..\..\parts\windows\windowscsehelper.ps1 - . $PSScriptRoot\networkisolatedclusterfunc.ps1 - . $PSScriptRoot\configfunc.ps1 - . $PSCommandPath.Replace('.tests.ps1', '.ps1') -} + function Get-WindowsPauseVersion { + return "ltsc2022" + } -Describe "ProcessAndWriteContainerdConfig" { - BeforeAll { - Mock Get-Content -ParameterFilter { $Path -like "*kubeclusterconfig.json" } -MockWith { - return "{`"Cri`":{`"Images`":{`"Pause`":`"$pauseImage`"}}}" + function Get-WindowsVersion { + return "ltsc2022" } - # Mock Out-File for registry config writes to avoid file system errors - Mock Out-File -ParameterFilter { $FilePath -like "*certs.d*" } -MockWith { } + . $PSScriptRoot\containerdfunc.ps1 + . $PSScriptRoot\..\..\..\parts\windows\windowscsehelper.ps1 + . $PSScriptRoot\networkisolatedclusterfunc.ps1 + . $PSScriptRoot\configfunc.ps1 + . $PSCommandPath.Replace('.tests.ps1', '.ps1') } - Context 'containerd template v1 ' { + Describe "ProcessAndWriteContainerdConfig" { BeforeAll { - $containerdDir = "$PSScriptRoot\containerdfunc.tests.suites" - $cniBinDir = 'C:/cni/bin' - $cniConfDir = 'C:/cni/conf' - $pauseImage = 'mcr.microsoft.com/oss/v2/kubernetes/pause:3.6' - - $global:KubeClusterConfigPath = [Io.path]::Combine("", "kubeclusterconfig.json") - $global:ContainerdInstallLocation = $containerdDir - $global:WindowsDataDir = $PSScriptRoot - $configPath = Join-Path $global:ContainerdInstallLocation "config.toml" + Mock Get-Content -ParameterFilter { $Path -like "*kubeclusterconfig.json" } -MockWith { + return "{`"Cri`":{`"Images`":{`"Pause`":`"$pauseImage`"}}}" + } + + # Mock Out-File for registry config writes to avoid file system errors + Mock Out-File -ParameterFilter { $FilePath -like "*certs.d*" } -MockWith { } } - It "Should process containerdtemplate.toml with basic configuration" { - # Set up paths for the test - $global:DefaultContainerdWindowsSandboxIsolation = "process" # default to process isolation - $global:ContainerdWindowsRuntimeHandlers = "" # default to no hyperv handlers + Context 'containerd template v1 ' { + BeforeAll { + $containerdDir = "$PSScriptRoot\containerdfunc.tests.suites" + $cniBinDir = 'C:/cni/bin' + $cniConfDir = 'C:/cni/conf' + $pauseImage = 'mcr.microsoft.com/oss/v2/kubernetes/pause:3.6' - { ProcessAndWriteContainerdConfig -ContainerDVersion "1.7.9" -CNIBinDir $cniBinDir -CNIConfDir $cniConfDir } | Should -Not -Throw + $global:KubeClusterConfigPath = [Io.path]::Combine("", "kubeclusterconfig.json") + $global:ContainerdInstallLocation = $containerdDir + $global:WindowsDataDir = $PSScriptRoot + $configPath = Join-Path $global:ContainerdInstallLocation "config.toml" + } - $configPath | Should -Exist - $content = Get-Content -Path $configPath -Raw - $content | Should -Not -BeNullOrEmpty + It "Should process containerdtemplate.toml with basic configuration" { + # Set up paths for the test + $global:DefaultContainerdWindowsSandboxIsolation = "process" # default to process isolation + $global:ContainerdWindowsRuntimeHandlers = "" # default to no hyperv handlers - # Check that placeholders are replaced - $content | Should -Not -Match ([regex]::Escape("{{")) + { ProcessAndWriteContainerdConfig -ContainerDVersion "1.7.9" -CNIBinDir $cniBinDir -CNIConfDir $cniConfDir } | Should -Not -Throw - # Check that the values were replaced correctly - $content | Should -Match $pauseImage - $content | Should -Match $cniBinDir - $content | Should -Match $cniConfDir - $content | Should -Not -Match 'version = 3' - $content | Should -Match 'SandboxIsolation = 0' - } + $configPath | Should -Exist + $content = Get-Content -Path $configPath -Raw + $content | Should -Not -BeNullOrEmpty - It "Should include hyperv runtimes when hyperv is enabled" { - $global:DefaultContainerdWindowsSandboxIsolation = "hyperv" - $global:ContainerdWindowsRuntimeHandlers = "1234,5678" - { ProcessAndWriteContainerdConfig -ContainerDVersion "1.7.9" -CNIBinDir $cniBinDir -CNIConfDir $cniConfDir } | Should -Not -Throw + # Check that placeholders are replaced + $content | Should -Not -Match ([regex]::Escape("{{")) - $content = Get-Content -Path $configPath -Raw - $content | Should -Match 'plugins.cri.containerd.runtimes.runhcs-wcow-hypervisor-1234' - $content | Should -Match 'SandboxIsolation = 1' - $content | Should -Not -Match 'version = 3' - } + # Check that the values were replaced correctly + $content | Should -Match $pauseImage + $content | Should -Match $cniBinDir + $content | Should -Match $cniConfDir + $content | Should -Not -Match 'version = 3' + $content | Should -Match 'SandboxIsolation = 0' + } - It "Should handle older containerd versions (<1.7.9) by removing annotations" { - # Call the function under test and ensure it doesn't throw - { ProcessAndWriteContainerdConfig -ContainerDVersion "1.6.9" -CNIBinDir $cniBinDir -CNIConfDir $cniConfDir } | Should -Not -Throw + It "Should include hyperv runtimes when hyperv is enabled" { + $global:DefaultContainerdWindowsSandboxIsolation = "hyperv" + $global:ContainerdWindowsRuntimeHandlers = "1234,5678" + { ProcessAndWriteContainerdConfig -ContainerDVersion "1.7.9" -CNIBinDir $cniBinDir -CNIConfDir $cniConfDir } | Should -Not -Throw - $content = Get-Content -Path $configPath -Raw + $content = Get-Content -Path $configPath -Raw + $content | Should -Match 'plugins.cri.containerd.runtimes.runhcs-wcow-hypervisor-1234' + $content | Should -Match 'SandboxIsolation = 1' + $content | Should -Not -Match 'version = 3' + } - # Should not contain annotation placeholders or values - $content | Should -Not -Match 'container_annotations' - $content | Should -Not -Match 'pod_annotations' - $content | Should -Not -Match 'version = 3' + It "Should handle older containerd versions (<1.7.9) by removing annotations" { + # Call the function under test and ensure it doesn't throw + { ProcessAndWriteContainerdConfig -ContainerDVersion "1.6.9" -CNIBinDir $cniBinDir -CNIConfDir $cniConfDir } | Should -Not -Throw + + $content = Get-Content -Path $configPath -Raw + + # Should not contain annotation placeholders or values + $content | Should -Not -Match 'container_annotations' + $content | Should -Not -Match 'pod_annotations' + $content | Should -Not -Match 'version = 3' + } } - } - Context 'containerd template v2' { + Context 'containerd template v2' { - BeforeAll { - $containerdDir = "$PSScriptRoot\containerdfunc.tests.suites" - $cniBinDir = 'C:/cni/bin' - $cniConfDir = 'C:/cni/conf' - $pauseImage = 'mcr.microsoft.com/oss/v2/kubernetes/pause:3.6' - - $global:KubeClusterConfigPath = [Io.path]::Combine("", "kubeclusterconfig.json") - $global:ContainerdInstallLocation = $containerdDir - $global:WindowsDataDir = $PSScriptRoot - $configPath = Join-Path $global:ContainerdInstallLocation "config.toml" - } + BeforeAll { + $containerdDir = "$PSScriptRoot\containerdfunc.tests.suites" + $cniBinDir = 'C:/cni/bin' + $cniConfDir = 'C:/cni/conf' + $pauseImage = 'mcr.microsoft.com/oss/v2/kubernetes/pause:3.6' - It "Should process containerdtemplate.toml with basic configuration" { - # Set up paths for the test - $global:DefaultContainerdWindowsSandboxIsolation = "process" # default to process isolation - $global:ContainerdWindowsRuntimeHandlers = "" # default to no hyperv handlers + $global:KubeClusterConfigPath = [Io.path]::Combine("", "kubeclusterconfig.json") + $global:ContainerdInstallLocation = $containerdDir + $global:WindowsDataDir = $PSScriptRoot + $configPath = Join-Path $global:ContainerdInstallLocation "config.toml" + } - { ProcessAndWriteContainerdConfig -ContainerDVersion "2.0.5" -CNIBinDir $cniBinDir -CNIConfDir $cniConfDir } | Should -Not -Throw + It "Should process containerdtemplate.toml with basic configuration" { + # Set up paths for the test + $global:DefaultContainerdWindowsSandboxIsolation = "process" # default to process isolation + $global:ContainerdWindowsRuntimeHandlers = "" # default to no hyperv handlers - $configPath | Should -Exist - $content = Get-Content -Path $configPath -Raw - $content | Should -Not -BeNullOrEmpty + { ProcessAndWriteContainerdConfig -ContainerDVersion "2.0.5" -CNIBinDir $cniBinDir -CNIConfDir $cniConfDir } | Should -Not -Throw - # Check that placeholders are replaced - $content | Should -Not -Match ([regex]::Escape("{{")) + $configPath | Should -Exist + $content = Get-Content -Path $configPath -Raw + $content | Should -Not -BeNullOrEmpty - # Check that the values were replaced correctly - $content | Should -Match $pauseImage - $content | Should -Match $cniBinDir - $content | Should -Match $cniConfDir - $content | Should -Match 'version = 3' - $content | Should -Match 'SandboxIsolation = 0' - } + # Check that placeholders are replaced + $content | Should -Not -Match ([regex]::Escape("{{")) - It "Should include hyperv runtimes when hyperv is enabled" { - $global:DefaultContainerdWindowsSandboxIsolation = "hyperv" - $global:ContainerdWindowsRuntimeHandlers = "1234,5678" - { ProcessAndWriteContainerdConfig -ContainerDVersion "2.0.5" -CNIBinDir $cniBinDir -CNIConfDir $cniConfDir } | Should -Not -Throw + # Check that the values were replaced correctly + $content | Should -Match $pauseImage + $content | Should -Match $cniBinDir + $content | Should -Match $cniConfDir + $content | Should -Match 'version = 3' + $content | Should -Match 'SandboxIsolation = 0' + } - $content = Get-Content -Path $configPath -Raw - $content | Should -Match 'plugins.cri.containerd.runtimes.runhcs-wcow-hypervisor-1234' - $content | Should -Match 'SandboxIsolation = 1' - $content | Should -Match 'version = 3' - } - } -} + It "Should include hyperv runtimes when hyperv is enabled" { + $global:DefaultContainerdWindowsSandboxIsolation = "hyperv" + $global:ContainerdWindowsRuntimeHandlers = "1234,5678" + { ProcessAndWriteContainerdConfig -ContainerDVersion "2.0.5" -CNIBinDir $cniBinDir -CNIConfDir $cniConfDir } | Should -Not -Throw -Describe "Set-ContainerdRegistryConfig" { - BeforeAll { - # Define Create-Directory mock function if not already defined - function Create-Directory { - param($FullPath, $DirectoryUsage) + $content = Get-Content -Path $configPath -Raw + $content | Should -Match 'plugins.cri.containerd.runtimes.runhcs-wcow-hypervisor-1234' + $content | Should -Match 'SandboxIsolation = 1' + $content | Should -Match 'version = 3' + } } } - BeforeEach { - # Mock Create-Directory to track calls - Mock Create-Directory -MockWith { - param($FullPath, $DirectoryUsage) - # Do nothing in tests - we'll verify the call was made + Describe "Set-ContainerdRegistryConfig" { + BeforeAll { + # Define Create-Directory mock function if not already defined + function Create-Directory { + param($FullPath, $DirectoryUsage) + } } - # Mock Out-File to capture content without writing to disk - Mock Out-File -MockWith { - param($FilePath, $Encoding) + BeforeEach { + # Mock Create-Directory to track calls + Mock Create-Directory -MockWith { + param($FullPath, $DirectoryUsage) + # Do nothing in tests - we'll verify the call was made + } + + # Mock Out-File to capture content without writing to disk + Mock Out-File -MockWith { + param($FilePath, $Encoding) + } } - } - It "Should create hosts.toml file for docker.io registry" { - $registry = "docker.io" - $registryHost = "registry-1.docker.io" + It "Should create hosts.toml file for docker.io registry" { + $registry = "docker.io" + $registryHost = "registry-1.docker.io" - Set-ContainerdRegistryConfig -Registry $registry -RegistryHost $registryHost + Set-ContainerdRegistryConfig -Registry $registry -RegistryHost $registryHost - # Verify Create-Directory was called with correct parameters - Assert-MockCalled -CommandName 'Create-Directory' -Exactly -Times 1 -ParameterFilter { - $FullPath -eq "C:\ProgramData\containerd\certs.d\docker.io" -and - $DirectoryUsage -eq "storing containerd registry hosts config" - } + # Verify Create-Directory was called with correct parameters + Assert-MockCalled -CommandName 'Create-Directory' -Exactly -Times 1 -ParameterFilter { + $FullPath -eq "C:\ProgramData\containerd\certs.d\docker.io" -and + $DirectoryUsage -eq "storing containerd registry hosts config" + } - # Verify Out-File was called with correct path - Assert-MockCalled -CommandName 'Out-File' -Exactly -Times 1 -ParameterFilter { - $FilePath -eq "C:\ProgramData\containerd\certs.d\docker.io\hosts.toml" -and - $Encoding -eq "ascii" + # Verify Out-File was called with correct path + Assert-MockCalled -CommandName 'Out-File' -Exactly -Times 1 -ParameterFilter { + $FilePath -eq "C:\ProgramData\containerd\certs.d\docker.io\hosts.toml" -and + $Encoding -eq "ascii" + } } - } - It "Should create hosts.toml file for mcr.azk8s.cn registry" { - $registry = "mcr.azk8s.cn" - $registryHost = "mcr.azure.cn" + It "Should create hosts.toml file for mcr.azk8s.cn registry" { + $registry = "mcr.azk8s.cn" + $registryHost = "mcr.azure.cn" - Set-ContainerdRegistryConfig -Registry $registry -RegistryHost $registryHost + Set-ContainerdRegistryConfig -Registry $registry -RegistryHost $registryHost - # Verify Create-Directory was called with correct parameters - Assert-MockCalled -CommandName 'Create-Directory' -Exactly -Times 1 -ParameterFilter { - $FullPath -eq "C:\ProgramData\containerd\certs.d\mcr.azk8s.cn" -and - $DirectoryUsage -eq "storing containerd registry hosts config" - } + # Verify Create-Directory was called with correct parameters + Assert-MockCalled -CommandName 'Create-Directory' -Exactly -Times 1 -ParameterFilter { + $FullPath -eq "C:\ProgramData\containerd\certs.d\mcr.azk8s.cn" -and + $DirectoryUsage -eq "storing containerd registry hosts config" + } - # Verify Out-File was called with correct path - Assert-MockCalled -CommandName 'Out-File' -Exactly -Times 1 -ParameterFilter { - $FilePath -eq "C:\ProgramData\containerd\certs.d\mcr.azk8s.cn\hosts.toml" -and - $Encoding -eq "ascii" + # Verify Out-File was called with correct path + Assert-MockCalled -CommandName 'Out-File' -Exactly -Times 1 -ParameterFilter { + $FilePath -eq "C:\ProgramData\containerd\certs.d\mcr.azk8s.cn\hosts.toml" -and + $Encoding -eq "ascii" + } } - } - It "Should generate correct hosts.toml content structure" { - $registry = "docker.io" - $registryHost = "registry-1.docker.io" + It "Should generate correct hosts.toml content structure" { + $registry = "docker.io" + $registryHost = "registry-1.docker.io" - # Mock Out-File to do nothing (we verify structure by checking function implementation) - Mock Out-File -MockWith { } + # Mock Out-File to do nothing (we verify structure by checking function implementation) + Mock Out-File -MockWith { } - Set-ContainerdRegistryConfig -Registry $registry -RegistryHost $registryHost + Set-ContainerdRegistryConfig -Registry $registry -RegistryHost $registryHost + + # Verify Out-File was called with correct path + Assert-MockCalled -CommandName 'Out-File' -Exactly -Times 1 -ParameterFilter { + $FilePath -eq "C:\ProgramData\containerd\certs.d\docker.io\hosts.toml" -and + $Encoding -eq "ascii" + } - # Verify Out-File was called with correct path - Assert-MockCalled -CommandName 'Out-File' -Exactly -Times 1 -ParameterFilter { - $FilePath -eq "C:\ProgramData\containerd\certs.d\docker.io\hosts.toml" -and - $Encoding -eq "ascii" + # Note: The content structure is verified by the function's implementation + # The expected content format is: + # server = "https://$Registry" + # [host."https://$RegistryHost"] + # capabilities = ["pull", "resolve"] + # [host."https://$RegistryHost".header] + # X-Forwarded-For = ["$Registry"] } - # Note: The content structure is verified by the function's implementation - # The expected content format is: - # server = "https://$Registry" - # [host."https://$RegistryHost"] - # capabilities = ["pull", "resolve"] - # [host."https://$RegistryHost".header] - # X-Forwarded-For = ["$Registry"] - } + It "Should handle custom registry and host correctly" { + $registry = "myregistry.example.com" + $registryHost = "mirror.example.com" - It "Should handle custom registry and host correctly" { - $registry = "myregistry.example.com" - $registryHost = "mirror.example.com" + Mock Out-File -MockWith { } - Mock Out-File -MockWith { } + Set-ContainerdRegistryConfig -Registry $registry -RegistryHost $registryHost - Set-ContainerdRegistryConfig -Registry $registry -RegistryHost $registryHost + # Verify Create-Directory was called with correct registry path + Assert-MockCalled -CommandName 'Create-Directory' -ParameterFilter { + $FullPath -eq "C:\ProgramData\containerd\certs.d\myregistry.example.com" + } - # Verify Create-Directory was called with correct registry path - Assert-MockCalled -CommandName 'Create-Directory' -ParameterFilter { - $FullPath -eq "C:\ProgramData\containerd\certs.d\myregistry.example.com" + # Verify Out-File was called with correct path + Assert-MockCalled -CommandName 'Out-File' -ParameterFilter { + $FilePath -eq "C:\ProgramData\containerd\certs.d\myregistry.example.com\hosts.toml" + } } - # Verify Out-File was called with correct path - Assert-MockCalled -CommandName 'Out-File' -ParameterFilter { - $FilePath -eq "C:\ProgramData\containerd\certs.d\myregistry.example.com\hosts.toml" - } - } + It "Should write to correct hosts.toml file path" { + $registry = "test.registry.io" + $registryHost = "test.mirror.io" + $script:capturedFilePath = $null + + Mock Out-File -MockWith { + param($FilePath, $Encoding) + $script:capturedFilePath = $FilePath + } - It "Should write to correct hosts.toml file path" { - $registry = "test.registry.io" - $registryHost = "test.mirror.io" - $script:capturedFilePath = $null + Set-ContainerdRegistryConfig -Registry $registry -RegistryHost $registryHost - Mock Out-File -MockWith { - param($FilePath, $Encoding) - $script:capturedFilePath = $FilePath + $script:capturedFilePath | Should -Be "C:\ProgramData\containerd\certs.d\test.registry.io\hosts.toml" } - Set-ContainerdRegistryConfig -Registry $registry -RegistryHost $registryHost + It "Should use ascii encoding when writing hosts.toml" { + $registry = "docker.io" + $registryHost = "registry-1.docker.io" + $script:capturedEncoding = $null - $script:capturedFilePath | Should -Be "C:\ProgramData\containerd\certs.d\test.registry.io\hosts.toml" - } + Mock Out-File -MockWith { + param($FilePath, $Encoding) + $script:capturedEncoding = $Encoding + } - It "Should use ascii encoding when writing hosts.toml" { - $registry = "docker.io" - $registryHost = "registry-1.docker.io" - $script:capturedEncoding = $null + Set-ContainerdRegistryConfig -Registry $registry -RegistryHost $registryHost - Mock Out-File -MockWith { - param($FilePath, $Encoding) - $script:capturedEncoding = $Encoding + $script:capturedEncoding | Should -Be "ascii" } - - Set-ContainerdRegistryConfig -Registry $registry -RegistryHost $registryHost - - $script:capturedEncoding | Should -Be "ascii" } -} -Describe "Set-BootstrapProfileRegistryContainerdHost" { - BeforeEach { - Mock Create-Directory -MockWith { - param($FullPath, $DirectoryUsage) - } + Describe "Set-BootstrapProfileRegistryContainerdHost" { + BeforeEach { + Mock Create-Directory -MockWith { + param($FullPath, $DirectoryUsage) + } - $script:capturedFilePath = $null - $script:capturedEncoding = $null - $script:capturedContent = $null - Mock Out-File -MockWith { - param($InputObject, $FilePath, $Encoding) - $script:capturedFilePath = $FilePath - $script:capturedEncoding = $Encoding - $script:capturedContent = $InputObject + $script:capturedFilePath = $null + $script:capturedEncoding = $null + $script:capturedContent = $null + Mock Out-File -MockWith { + param($InputObject, $FilePath, $Encoding) + $script:capturedFilePath = $FilePath + $script:capturedEncoding = $Encoding + $script:capturedContent = $InputObject + } } - } - It "Should write hosts.toml for default mcr.microsoft.com when MCRRepositoryBase is not set" { - $global:BootstrapProfileContainerRegistryServer = "myacr.azurecr.io" - if (Test-Path variable:global:MCRRepositoryBase) { - Remove-Variable -Name MCRRepositoryBase -Scope Global - } + It "Should write hosts.toml for default mcr.microsoft.com when MCRRepositoryBase is not set" { + $global:BootstrapProfileContainerRegistryServer = "myacr.azurecr.io" + if (Test-Path variable:global:MCRRepositoryBase) { + Remove-Variable -Name MCRRepositoryBase -Scope Global + } - Set-BootstrapProfileRegistryContainerdHost + Set-BootstrapProfileRegistryContainerdHost - Assert-MockCalled -CommandName 'Create-Directory' -Exactly -Times 1 -ParameterFilter { - $FullPath -eq "C:\ProgramData\containerd\certs.d\mcr.microsoft.com" -and - $DirectoryUsage -eq "storing containerd registry hosts config" + Assert-MockCalled -CommandName 'Create-Directory' -Exactly -Times 1 -ParameterFilter { + $FullPath -eq "C:\ProgramData\containerd\certs.d\mcr.microsoft.com" -and + $DirectoryUsage -eq "storing containerd registry hosts config" + } + $script:capturedFilePath | Should -Be "C:\ProgramData\containerd\certs.d\mcr.microsoft.com\hosts.toml" + $script:capturedEncoding | Should -Be "ascii" + $script:capturedContent | Should -Match 'server = "https://mcr.microsoft.com"' + $script:capturedContent | Should -Match '\[host\."https://myacr.azurecr.io/v2"\]' + $script:capturedContent | Should -Match 'override_path = true' } - $script:capturedFilePath | Should -Be "C:\ProgramData\containerd\certs.d\mcr.microsoft.com\hosts.toml" - $script:capturedEncoding | Should -Be "ascii" - $script:capturedContent | Should -Match 'server = "https://mcr.microsoft.com"' - $script:capturedContent | Should -Match '\[host\."https://myacr.azurecr.io/v2"\]' - $script:capturedContent | Should -Match 'override_path = true' - } - It "Should sanitize bootstrap profile host and use custom mcr repository base" { - $global:MCRRepositoryBase = "my.mcr.mirror" - $global:BootstrapProfileContainerRegistryServer = "https://myacr.azurecr.io/some/path/" + It "Should sanitize bootstrap profile host and use custom mcr repository base" { + $global:MCRRepositoryBase = "my.mcr.mirror" + $global:BootstrapProfileContainerRegistryServer = "https://myacr.azurecr.io/some/path/" - Set-BootstrapProfileRegistryContainerdHost + Set-BootstrapProfileRegistryContainerdHost - Assert-MockCalled -CommandName 'Create-Directory' -Exactly -Times 1 -ParameterFilter { - $FullPath -eq "C:\ProgramData\containerd\certs.d\my.mcr.mirror" + Assert-MockCalled -CommandName 'Create-Directory' -Exactly -Times 1 -ParameterFilter { + $FullPath -eq "C:\ProgramData\containerd\certs.d\my.mcr.mirror" + } + $script:capturedFilePath | Should -Be "C:\ProgramData\containerd\certs.d\my.mcr.mirror\hosts.toml" + $script:capturedContent | Should -Match 'server = "https://my.mcr.mirror"' + $script:capturedContent | Should -Match '\[host\."https://myacr.azurecr.io/v2/some/path"\]' } - $script:capturedFilePath | Should -Be "C:\ProgramData\containerd\certs.d\my.mcr.mirror\hosts.toml" - $script:capturedContent | Should -Match 'server = "https://my.mcr.mirror"' - $script:capturedContent | Should -Match '\[host\."https://myacr.azurecr.io/v2/some/path"\]' - } - - It "Should map host with repository prefix to v2 path" { - $global:MCRRepositoryBase = "mcr.microsoft.com" - $global:BootstrapProfileContainerRegistryServer = "myacr.azurecr.io/aaa" - Set-BootstrapProfileRegistryContainerdHost + It "Should map host with repository prefix to v2 path" { + $global:MCRRepositoryBase = "mcr.microsoft.com" + $global:BootstrapProfileContainerRegistryServer = "myacr.azurecr.io/aaa" - $script:capturedContent | Should -Match '\[host\."https://myacr.azurecr.io/v2/aaa"\]' - } + Set-BootstrapProfileRegistryContainerdHost - AfterEach { - $global:BootstrapProfileContainerRegistryServer = $null - $global:MCRRepositoryBase = $null - } -} + $script:capturedContent | Should -Match '\[host\."https://myacr.azurecr.io/v2/aaa"\]' + } -Describe 'Install-Containerd' { - BeforeEach { - Mock Logs-To-Event - Mock Get-Service -MockWith { $null } - Mock Create-Directory - Mock Add-SystemPathEntry - Mock ProcessAndWriteContainerdConfig - Mock RegisterContainerDService - Mock Enable-Logging - Mock Remove-Item - Mock Move-Item - Mock Push-Location - Mock Pop-Location - Mock Set-ExitCode - - $global:ContainerdInstallLocation = [Io.path]::Combine($ENV:TEMP, "containerd") - $global:BootstrapProfileContainerRegistryServer = $null + AfterEach { + $global:BootstrapProfileContainerRegistryServer = $null + $global:MCRRepositoryBase = $null + } } - Context 'BootstrapProfileContainerRegistryServer is set - downloads with ORAS' { + Describe 'Install-Containerd' { BeforeEach { - Mock DownloadFileWithOras -MockWith {} - Mock tar -MockWith { $global:LASTEXITCODE = 0 } - Mock Set-PodInfraContainerImage - - $global:BootstrapProfileContainerRegistryServer = "myregistry.azurecr.io" - } + Mock Logs-To-Event + Mock Get-Service -MockWith { $null } + Mock Create-Directory + Mock Add-SystemPathEntry + Mock ProcessAndWriteContainerdConfig + Mock RegisterContainerDService + Mock Enable-Logging + Mock Remove-Item + Mock Move-Item + Mock Push-Location + Mock Pop-Location + Mock Set-ExitCode - AfterEach { + $global:ContainerdInstallLocation = [Io.path]::Combine($ENV:TEMP, "containerd") $global:BootstrapProfileContainerRegistryServer = $null } - It "Should not call DownloadFileOverHttp when BootstrapProfileContainerRegistryServer is set" { - Mock DownloadFileOverHttp - $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v1.7.20-azure.1/binaries/containerd-v1.7.20-azure.1-windows-amd64.tar.gz" - Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" - Assert-MockCalled -CommandName 'DownloadFileOverHttp' -Exactly -Times 0 - } + Context 'BootstrapProfileContainerRegistryServer is set - downloads with ORAS' { + BeforeEach { + Mock DownloadFileWithOras -MockWith {} + Mock tar -MockWith { $global:LASTEXITCODE = 0 } + Mock Set-PodInfraContainerImage - It "Should call DownloadFileWithOras with correct reference when BootstrapProfileContainerRegistryServer is set" { - $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v1.7.20-azure.1/binaries/containerd-v1.7.20-azure.1-windows-amd64.tar.gz" - Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" - Assert-MockCalled -CommandName 'DownloadFileWithOras' -Exactly -Times 1 -ParameterFilter { - $Reference -eq 'myregistry.azurecr.io/aks/packages/containerd/containerd:v1.7.20' -and - $DestinationPath -like '*containerd.tar.gz' + $global:BootstrapProfileContainerRegistryServer = "myregistry.azurecr.io" } - } - It "Should call Logs-To-Event with ORAS task name when BootstrapProfileContainerRegistryServer is set" { - $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v1.7.20-azure.1/binaries/containerd-v1.7.20-azure.1-windows-amd64.tar.gz" - Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" - Assert-MockCalled -CommandName 'Logs-To-Event' -ParameterFilter { $TaskName -eq 'AKS.WindowsCSE.DownloadContainerdWithOras' } - } + AfterEach { + $global:BootstrapProfileContainerRegistryServer = $null + } - It "Should extract correct version tag for containerd 2.x" { - $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v2.0.4-azure.1/binaries/containerd-v2.0.4-azure.1-windows-amd64.tar.gz" - Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" - Assert-MockCalled -CommandName 'DownloadFileWithOras' -Exactly -Times 1 -ParameterFilter { - $Reference -eq 'myregistry.azurecr.io/aks/packages/containerd/containerd:v2.0.4' + It "Should not call DownloadFileOverHttp when BootstrapProfileContainerRegistryServer is set" { + Mock DownloadFileOverHttp + $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v1.7.20-azure.1/binaries/containerd-v1.7.20-azure.1-windows-amd64.tar.gz" + Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" + Assert-MockCalled -CommandName 'DownloadFileOverHttp' -Exactly -Times 0 } - } - It "Should strip https:// scheme from BootstrapProfileContainerRegistryServer in ORAS reference" { - $global:BootstrapProfileContainerRegistryServer = "https://myregistry.azurecr.io" - $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v1.7.20-azure.1/binaries/containerd-v1.7.20-azure.1-windows-amd64.tar.gz" - Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" - Assert-MockCalled -CommandName 'DownloadFileWithOras' -Exactly -Times 1 -ParameterFilter { - $Reference -eq 'myregistry.azurecr.io/aks/packages/containerd/containerd:v1.7.20' + It "Should call DownloadFileWithOras with correct reference when BootstrapProfileContainerRegistryServer is set" { + $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v1.7.20-azure.1/binaries/containerd-v1.7.20-azure.1-windows-amd64.tar.gz" + Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" + Assert-MockCalled -CommandName 'DownloadFileWithOras' -Exactly -Times 1 -ParameterFilter { + $Reference -eq 'myregistry.azurecr.io/aks/packages/containerd/containerd:v1.7.20' -and + $DestinationPath -like '*containerd.tar.gz' + } } - } - It "Should strip http:// scheme and trailing slash from BootstrapProfileContainerRegistryServer in ORAS reference" { - $global:BootstrapProfileContainerRegistryServer = "http://myregistry.azurecr.io/" - $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v1.7.20-azure.1/binaries/containerd-v1.7.20-azure.1-windows-amd64.tar.gz" - Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" - Assert-MockCalled -CommandName 'DownloadFileWithOras' -Exactly -Times 1 -ParameterFilter { - $Reference -eq 'myregistry.azurecr.io/aks/packages/containerd/containerd:v1.7.20' + It "Should call Logs-To-Event with ORAS task name when BootstrapProfileContainerRegistryServer is set" { + $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v1.7.20-azure.1/binaries/containerd-v1.7.20-azure.1-windows-amd64.tar.gz" + Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" + Assert-MockCalled -CommandName 'Logs-To-Event' -ParameterFilter { $TaskName -eq 'AKS.WindowsCSE.DownloadContainerdWithOras' } } - } - It "Should preserve repo prefix in BootstrapProfileContainerRegistryServer when constructing ORAS reference" { - $global:BootstrapProfileContainerRegistryServer = "https://myregistry.azurecr.io/some/prefix/" - $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v1.7.20-azure.1/binaries/containerd-v1.7.20-azure.1-windows-amd64.tar.gz" - Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" - Assert-MockCalled -CommandName 'DownloadFileWithOras' -Exactly -Times 1 -ParameterFilter { - $Reference -eq 'myregistry.azurecr.io/some/prefix/aks/packages/containerd/containerd:v1.7.20' + It "Should extract correct version tag for containerd 2.x" { + $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v2.0.4-azure.1/binaries/containerd-v2.0.4-azure.1-windows-amd64.tar.gz" + Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" + Assert-MockCalled -CommandName 'DownloadFileWithOras' -Exactly -Times 1 -ParameterFilter { + $Reference -eq 'myregistry.azurecr.io/aks/packages/containerd/containerd:v2.0.4' + } } - } - It "Should call Set-ExitCode after exhausting retries when DownloadFileWithOras keeps failing" { - Mock DownloadFileWithOras -MockWith { throw "oras pull failed" } - Mock Start-Sleep -MockWith {} - Mock Set-ExitCode -MockWith { - Param($ExitCode, $ErrorMessage) - throw "Set-ExitCode: $ExitCode - $ErrorMessage" + It "Should strip https:// scheme from BootstrapProfileContainerRegistryServer in ORAS reference" { + $global:BootstrapProfileContainerRegistryServer = "https://myregistry.azurecr.io" + $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v1.7.20-azure.1/binaries/containerd-v1.7.20-azure.1-windows-amd64.tar.gz" + Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" + Assert-MockCalled -CommandName 'DownloadFileWithOras' -Exactly -Times 1 -ParameterFilter { + $Reference -eq 'myregistry.azurecr.io/aks/packages/containerd/containerd:v1.7.20' + } } - $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v1.7.20-azure.1/binaries/containerd-v1.7.20-azure.1-windows-amd64.tar.gz" - { Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" } | Should -Throw "*Exhausted retries for oras pull*" - Assert-MockCalled -CommandName 'DownloadFileWithOras' -Exactly -Times 5 - } - } + It "Should strip http:// scheme and trailing slash from BootstrapProfileContainerRegistryServer in ORAS reference" { + $global:BootstrapProfileContainerRegistryServer = "http://myregistry.azurecr.io/" + $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v1.7.20-azure.1/binaries/containerd-v1.7.20-azure.1-windows-amd64.tar.gz" + Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" + Assert-MockCalled -CommandName 'DownloadFileWithOras' -Exactly -Times 1 -ParameterFilter { + $Reference -eq 'myregistry.azurecr.io/aks/packages/containerd/containerd:v1.7.20' + } + } - Context 'BootstrapProfileContainerRegistryServer is set but DownloadFileWithOras is not available' { - BeforeEach { - Mock Set-ExitCode -MockWith { - Param($ExitCode, $ErrorMessage) - throw "Set-ExitCode: $ExitCode - $ErrorMessage" + It "Should preserve repo prefix in BootstrapProfileContainerRegistryServer when constructing ORAS reference" { + $global:BootstrapProfileContainerRegistryServer = "https://myregistry.azurecr.io/some/prefix/" + $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v1.7.20-azure.1/binaries/containerd-v1.7.20-azure.1-windows-amd64.tar.gz" + Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" + Assert-MockCalled -CommandName 'DownloadFileWithOras' -Exactly -Times 1 -ParameterFilter { + $Reference -eq 'myregistry.azurecr.io/some/prefix/aks/packages/containerd/containerd:v1.7.20' + } } - Mock Get-Command -MockWith { return $null } -ParameterFilter { $Name -eq 'DownloadFileWithOras' } - $global:BootstrapProfileContainerRegistryServer = "myregistry.azurecr.io" + It "Should call Set-ExitCode after exhausting retries when DownloadFileWithOras keeps failing" { + Mock DownloadFileWithOras -MockWith { throw "oras pull failed" } + Mock Start-Sleep -MockWith {} + Mock Set-ExitCode -MockWith { + Param($ExitCode, $ErrorMessage) + throw "Set-ExitCode: $ExitCode - $ErrorMessage" + } + + $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v1.7.20-azure.1/binaries/containerd-v1.7.20-azure.1-windows-amd64.tar.gz" + { Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" } | Should -Throw "*Exhausted retries for oras pull*" + Assert-MockCalled -CommandName 'DownloadFileWithOras' -Exactly -Times 5 + } } - AfterEach { - $global:BootstrapProfileContainerRegistryServer = $null - } + Context 'BootstrapProfileContainerRegistryServer is set but DownloadFileWithOras is not available' { + BeforeEach { + Mock Set-ExitCode -MockWith { + Param($ExitCode, $ErrorMessage) + throw "Set-ExitCode: $ExitCode - $ErrorMessage" + } + Mock Get-Command -MockWith { return $null } -ParameterFilter { $Name -eq 'DownloadFileWithOras' } - It "Should call Set-ExitCode when DownloadFileWithOras function is not available" { - $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v1.7.20-azure.1/binaries/containerd-v1.7.20-azure.1-windows-amd64.tar.gz" - { Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" } | Should -Throw "*DownloadFileWithOras function is not available*" - } - } + $global:BootstrapProfileContainerRegistryServer = "myregistry.azurecr.io" + } - Context 'BootstrapProfileContainerRegistryServer is set but URL does not match expected pattern' { - BeforeEach { - Mock Set-ExitCode -MockWith { - Param($ExitCode, $ErrorMessage) - throw "Set-ExitCode: $ExitCode - $ErrorMessage" + AfterEach { + $global:BootstrapProfileContainerRegistryServer = $null } - $global:BootstrapProfileContainerRegistryServer = "myregistry.azurecr.io" + It "Should call Set-ExitCode when DownloadFileWithOras function is not available" { + $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v1.7.20-azure.1/binaries/containerd-v1.7.20-azure.1-windows-amd64.tar.gz" + { Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" } | Should -Throw "*DownloadFileWithOras function is not available*" + } } - AfterEach { - $global:BootstrapProfileContainerRegistryServer = $null - } + Context 'BootstrapProfileContainerRegistryServer is set but URL does not match expected pattern' { + BeforeEach { + Mock Set-ExitCode -MockWith { + Param($ExitCode, $ErrorMessage) + throw "Set-ExitCode: $ExitCode - $ErrorMessage" + } - It "Should call Set-ExitCode when URL does not contain expected containerd version pattern" { - $containerdUrl = "https://packages.aks.azure.com/containerd/windows/some-unknown-format.tar.gz" - { Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" } | Should -Throw "*Failed to extract containerd version tag from URL*" - } - } + $global:BootstrapProfileContainerRegistryServer = "myregistry.azurecr.io" + } - Context 'BootstrapProfileContainerRegistryServer is not set - falls back to HTTP download' { - BeforeEach { - $global:BootstrapProfileContainerRegistryServer = $null - Mock DownloadFileOverHttp - Mock tar -MockWith { $global:LASTEXITCODE = 0 } - Mock Push-Location - Mock Pop-Location - Mock Set-ExitCode + AfterEach { + $global:BootstrapProfileContainerRegistryServer = $null + } + + It "Should call Set-ExitCode when URL does not contain expected containerd version pattern" { + $containerdUrl = "https://packages.aks.azure.com/containerd/windows/some-unknown-format.tar.gz" + { Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" } | Should -Throw "*Failed to extract containerd version tag from URL*" + } } - It "Should call DownloadFileOverHttp when BootstrapProfileContainerRegistryServer is not set" { - $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v1.7.20-azure.1/binaries/containerd-v1.7.20-azure.1-windows-amd64.tar.gz" - Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" - Assert-MockCalled -CommandName 'DownloadFileOverHttp' -Exactly -Times 1 -ParameterFilter { - $Url -eq $containerdUrl + Context 'BootstrapProfileContainerRegistryServer is not set - falls back to HTTP download' { + BeforeEach { + $global:BootstrapProfileContainerRegistryServer = $null + Mock DownloadFileOverHttp + Mock tar -MockWith { $global:LASTEXITCODE = 0 } + Mock Push-Location + Mock Pop-Location + Mock Set-ExitCode + } + + It "Should call DownloadFileOverHttp when BootstrapProfileContainerRegistryServer is not set" { + $containerdUrl = "https://packages.aks.azure.com/containerd/windows/v1.7.20-azure.1/binaries/containerd-v1.7.20-azure.1-windows-amd64.tar.gz" + Install-Containerd -ContainerdUrl $containerdUrl -CNIBinDir "cniBin" -CNIConfDir "cniConf" -KubeDir "kubeDir" + Assert-MockCalled -CommandName 'DownloadFileOverHttp' -Exactly -Times 1 -ParameterFilter { + $Url -eq $containerdUrl + } } } } From 79abfe0bd75ac7225dc691bee84b7ac105c21c42 Mon Sep 17 00:00:00 2001 From: Tim Wright Date: Wed, 27 May 2026 15:21:42 +1200 Subject: [PATCH 3/3] no file --- staging/cse/windows/.gitignore | 2 ++ .../credential-provider-config.yaml | 14 -------------- 2 files changed, 2 insertions(+), 14 deletions(-) create mode 100644 staging/cse/windows/.gitignore delete mode 100644 staging/cse/windows/credentialProvider.tests.suites/credential-provider-config.yaml diff --git a/staging/cse/windows/.gitignore b/staging/cse/windows/.gitignore new file mode 100644 index 00000000000..b2f5915dbee --- /dev/null +++ b/staging/cse/windows/.gitignore @@ -0,0 +1,2 @@ + +credentialProvider.tests.suites/ diff --git a/staging/cse/windows/credentialProvider.tests.suites/credential-provider-config.yaml b/staging/cse/windows/credentialProvider.tests.suites/credential-provider-config.yaml deleted file mode 100644 index dc33e520ce0..00000000000 --- a/staging/cse/windows/credentialProvider.tests.suites/credential-provider-config.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: kubelet.config.k8s.io/v1 -kind: CredentialProviderConfig -providers: - - name: acr-credential-provider - matchImages: - - "*.azurecr.io" - - "*.azurecr.cn" - - "*.azurecr.de" - - "*.azurecr.us" - - "*.azurecr.microsoft.fakecloud" - defaultCacheDuration: "10m" - apiVersion: credentialprovider.kubelet.k8s.io/v1 - args: - - staging\cse\windows\credentialProvider.tests.suites\azure.json