diff --git a/.github/workflows/ci-build.yaml b/.github/workflows/ci-build.yaml index 2515c13..5352684 100644 --- a/.github/workflows/ci-build.yaml +++ b/.github/workflows/ci-build.yaml @@ -6,7 +6,7 @@ # # ########################################################################### -name: CI Build +name: CI-Build on: workflow_dispatch: diff --git a/.textlintrc.json b/.textlintrc.json index ecfc339..b3cb1bb 100644 --- a/.textlintrc.json +++ b/.textlintrc.json @@ -44,11 +44,13 @@ "InstallPath", "LatestVersion", "NetworkName", + "OSArchitecture", "RegisterServices", + "SourceRepo", "WhatIf", "WinCNIPath", "WinCNIVersion" ] } } -} \ No newline at end of file +} diff --git a/Tests/AllToolsUtilities.Tests.ps1 b/Tests/AllToolsUtilities.Tests.ps1 index 22515d4..5040e8a 100644 --- a/Tests/AllToolsUtilities.Tests.ps1 +++ b/Tests/AllToolsUtilities.Tests.ps1 @@ -56,9 +56,6 @@ Describe "AllToolsUtilities.psm1" { Context "Install-ContainerTools" -Tag "Install-ContainerTools" { BeforeAll { - Mock Get-ContainerdLatestVersion -ModuleName 'AllToolsUtilities' -MockWith { return '4.9.0' } - Mock Get-BuildkitLatestVersion -ModuleName 'AllToolsUtilities' -MockWith { return '8.9.7' } - Mock Get-NerdctlLatestVersion -ModuleName 'AllToolsUtilities' -MockWith { return '1.5.3' } Mock Install-Containerd -ModuleName 'AllToolsUtilities' Mock Install-Buildkit -ModuleName 'AllToolsUtilities' Mock Install-Nerdctl -ModuleName 'AllToolsUtilities' @@ -86,7 +83,7 @@ Describe "AllToolsUtilities.psm1" { Should -Invoke Install-Containerd -ModuleName 'AllToolsUtilities' ` -ParameterFilter { - $Version -eq '4.9.0' -and + $Version -eq 'latest' -and $InstallPath -eq "$Env:ProgramFiles\Containerd" -and $DownloadPath -eq "$HOME\Downloads" -and $Setup -eq $false @@ -94,7 +91,7 @@ Describe "AllToolsUtilities.psm1" { Should -Invoke Install-Buildkit -ModuleName 'AllToolsUtilities' ` -ParameterFilter { - $Version -eq '8.9.7' -and + $Version -eq 'latest' -and $InstallPath -eq "$Env:ProgramFiles\BuildKit" -and $DownloadPath -eq "$HOME\Downloads" -and $Setup -eq $false @@ -102,7 +99,7 @@ Describe "AllToolsUtilities.psm1" { Should -Invoke Install-Nerdctl -ModuleName 'AllToolsUtilities' ` -ParameterFilter { - $Version -eq '1.5.3' -and + $Version -eq 'latest' -and $InstallPath -eq "$Env:ProgramFiles\nerdctl" -and $DownloadPath -eq "$HOME\Downloads" } diff --git a/Tests/BuildkitTools.Tests.ps1 b/Tests/BuildkitTools.Tests.ps1 index 1129e7c..369d3bc 100644 --- a/Tests/BuildkitTools.Tests.ps1 +++ b/Tests/BuildkitTools.Tests.ps1 @@ -6,6 +6,7 @@ # # ########################################################################### +using module "..\containers-toolkit\Private\CommonToolUtilities.psm1" Describe "BuildkitTools.psm1" { BeforeAll { @@ -39,9 +40,12 @@ Describe "BuildkitTools.psm1" { } Context "Install-Buildkit" -Tag "Install-Buildkit" { - BeforeAll { + BeforeEach { + $Script:BuildkitRepo = 'https://github.com/moby/buildkit/releases/download' + $Script:TestDownloadPath = "$HOME\Downloads\buildkit-v1.0.0.windows-amd64.tar.gz" + Mock Get-BuildkitLatestVersion -ModuleName 'BuildkitTools' -MockWith { return '1.0.0' } - Mock Get-InstallationFile -ModuleName 'BuildkitTools' + Mock Get-InstallationFile -ModuleName 'BuildkitTools' -MockWith { return $Script:TestDownloadPath } Mock Install-RequiredFeature -ModuleName 'BuildkitTools' Mock Register-BuildkitdService -ModuleName 'BuildkitTools' Mock Start-BuildkitdService -ModuleName 'BuildkitTools' @@ -50,11 +54,11 @@ Describe "BuildkitTools.psm1" { Mock Get-ChildItem -ModuleName 'BuildkitTools' Mock Test-EmptyDirectory -ModuleName 'BuildkitTools' -MockWith { return $true } Mock Install-Buildkit -ModuleName 'BuildkitTools' - Mock Test-CheckSum -ModuleName 'BuildkitTools' -MockWith { return $true } Mock Remove-Item -ModuleName 'BuildkitTools' + } - $Script:BuildkitRepo = 'https://github.com/moby/buildkit/releases/download' - $Script:TestDownloadPath = "$HOME\Downloads\buildkit-v1.0.0.windows-amd64.tar.gz" + AfterEach { + Remove-Item -Path "TestDrive:\" -Force -ErrorAction Ignore } It 'Should not process on implicit request for validation (WhatIfPreference)' { @@ -74,26 +78,19 @@ Describe "BuildkitTools.psm1" { Install-Buildkit -Force -Confirm:$false Should -Invoke Uninstall-Buildkit -ModuleName 'BuildkitTools' -Times 0 -Exactly -Scope It - Should -Invoke Get-InstallationFile -ModuleName 'BuildkitTools' -ParameterFilter { - $Files -like @( - @{ - Feature = "Buildkit" - Uri = "$Script:BuildkitRepo/v1.0.0/buildkit-v1.0.0.windows-amd64.tar.gz" - Version = '1.0.0' - DownloadPath = "$Script:TestDownloadPath" - } - ) - } - - Should -Invoke Test-CheckSum -ModuleName 'BuildkitTools' -Times 1 -ParameterFilter { - $DownloadedFile -eq "$Script:TestDownloadPath" -and - $ChecksumUri -eq "$Script:BuildkitRepo/v1.0.0/buildkit-v1.0.0.windows-amd64.sbom.json" + Should -Invoke Get-InstallationFile -ModuleName 'BuildkitTools' -ParameterFilter { + $fileParameters[0].Feature -eq "Buildkit" -and + $fileParameters[0].Repo -eq "moby/buildkit" -and + $fileParameters[0].Version -eq 'latest' -and + $fileParameters[0].DownloadPath -eq "$HOME\Downloads" + $fileParameters[0].ChecksumSchemaFile -eq "$ModuleParentPath\Private\schemas\in-toto.sbom.schema.json" -and + [string]::IsNullOrWhiteSpace($fileParameters.FileFilterRegEx) } Should -Invoke Install-RequiredFeature -ModuleName 'BuildkitTools' -ParameterFilter { $Feature -eq "Buildkit" -and $InstallPath -eq "$Env:ProgramFiles\Buildkit" -and - $DownloadPath -eq "$Script:TestDownloadPath" -and + $SourceFile -eq "$Script:TestDownloadPath" -and $EnvPath -eq "$Env:ProgramFiles\Buildkit\bin" -and $cleanup -eq $true } @@ -102,37 +99,25 @@ Describe "BuildkitTools.psm1" { } It "Should call function with user-specified values" { - Install-Buildkit -Version '0.2.3' -InstallPath 'TestDrive:\BuildKit' -DownloadPath 'TestDrive:\Downloads' -Force -Confirm:$false + $customPath = "TestDrive:\Downloads\buildkit-v0.2.3.windows-amd64.tar.gz" + Mock Get-InstallationFile -ModuleName 'BuildkitTools' -MockWith { return $customPath } + + Install-Buildkit -Version '0.2.3' -InstallPath 'TestDrive:\BuildKit' -DownloadPath 'TestDrive:\Downloads' -OSArchitecture "arm64" -Force -Confirm:$false Should -Invoke Uninstall-Buildkit -ModuleName 'BuildkitTools' -Times 0 -Exactly -Scope It Should -Invoke Get-InstallationFile -ModuleName 'BuildkitTools' -ParameterFilter { - $Files -like @( - @{ - Feature = "Buildkit" - Uri = "$Script:BuildkitRepo/v0.2.3/buildkit-v0.2.3.windows-amd64.tar.gz" - Version = '0.2.3' - DownloadPath = "$HOME\Downloads" - } - ) + $fileParameters[0].Version -eq '0.2.3' + $fileParameters[0].OSArchitecture -eq 'arm64' } Should -Invoke Install-RequiredFeature -ModuleName 'BuildkitTools' -ParameterFilter { $Feature -eq "Buildkit" -and $InstallPath -eq 'TestDrive:\BuildKit' -and - $DownloadPath -eq 'TestDrive:\Downloads\buildkit-v0.2.3.windows-amd64.tar.gz' -and + $SourceFile -eq "$customPath" -and $EnvPath -eq 'TestDrive:\Buildkit\bin' -and $cleanup -eq $true } } - It "should throw an error when checksum verification fails" { - Mock Test-CheckSum -ModuleName 'BuildkitTools' -MockWith { return $false } - - { Install-Buildkit -Force -Confirm:$false } | Should -Throw "Checksum verification failed for $Script:TestDownloadPath" - Should -Invoke Remove-Item -ModuleName 'BuildkitTools' -ParameterFilter { - $Path -eq "$Script:TestDownloadPath" - } - } - It "Should setup Buildkitd service" { Mock Test-BuildkitdServiceExists -ModuleName 'BuildkitTools' -MockWith { return $true } diff --git a/Tests/CommonToolUtilities.Tests.ps1 b/Tests/CommonToolUtilities.Tests.ps1 index 36e5cfd..04bf28c 100644 --- a/Tests/CommonToolUtilities.Tests.ps1 +++ b/Tests/CommonToolUtilities.Tests.ps1 @@ -6,6 +6,7 @@ # # ########################################################################### + using module "..\containers-toolkit\Private\CommonToolUtilities.psm1" $Script:SampleSha256Sum = @' @@ -142,74 +143,128 @@ Describe "CommonToolUtilities.psm1" { } Context "Get-InstallationFile" -Tag "Get-InstallationFile" { - BeforeAll { + BeforeEach { Mock Get-Module -ParameterFilter { $Name -eq 'ThreadJob' } { } Mock Import-Module -ParameterFilter { $Name -eq 'ThreadJob' } { } - Mock Invoke-WebRequest { } -ModuleName "CommonToolUtilities" + Mock Invoke-WebRequest -ModuleName "CommonToolUtilities" { } + Mock Invoke-RestMethod -ModuleName "CommonToolUtilities" { + return (Get-Content -Path "$PSScriptRoot\TestData\release-assets.json" -Raw | ConvertFrom-Json -Depth 3 ) + } + # -ParameterFilter { $Uri -match "https://api.github.com/repos/containerd/nerdctl/releases" } $sampleJob = New-MockObject -Type 'ThreadJob.ThreadJob' -Properties @{ JobStateInfo = 'Completed' } Mock Start-ThreadJob -ModuleName "CommonToolUtilities" -MockWith { return $sampleJob } Mock Wait-Job -ModuleName "CommonToolUtilities" Mock Receive-Job -ModuleName "CommonToolUtilities" Mock Remove-Job -ModuleName "CommonToolUtilities" + Mock Test-Checksum -ModuleName "CommonToolUtilities" -MockWith { return $true } + + $Script:TestFileName = "nerdctl-2.0.0-rc.1-windows-amd64.tar.gz" + $Script:MockDownloadPath = "TestDrive:\Download\$testFileName" + $Script:MockCheckSumFile = "TestDrive:\Download\SHA256SUMS" + $Script:MockURL = "https://github.com/containerd/nerdctl/releases/download/v2.0.0-rc.1/$testFileName" + $Script:MockFiles = @( + @{ + Feature = "Containerd" + Repo = "containerd/nerdctl" + Version = 'latest' + OSArchitecture = 'amd64' + DownloadPath = "TestDrive:\Download" + } + ) + + Mock Test-Path -ModuleName "CommonToolUtilities" -MockWith { return $true } -ParameterFilter { $Path -eq $Script:MockDownloadPath } + Mock Test-Path -ModuleName "CommonToolUtilities" -MockWith { return $true } -ParameterFilter { $Path -eq $Script:MockCheckSumFile } } - It "Should successfully download single file" { - $params = @{ - Feature = "Containerd" - Uri = "https://github.com/v1.0.0/downloadedfile.tar.gz" - Version = '1.0.0' - DownloadPath = "$DownloadPath\downloadedfile.tar.gz" - } - $files = @($params) - Get-InstallationFile -Files $files + It "Should successfully download latest release assets" { + $testChecksumURI = "https://github.com/containerd/nerdctl/releases/download/v2.0.0-rc.1/SHA256SUMS" + $testChecksumFile = "TestDrive:\Download\SHA256SUMS" + + # Call method + $result = Get-InstallationFile -FileParameters $Script:MockFiles - Should -Invoke Invoke-WebRequest -Exactly 1 -Scope It -ModuleName "CommonToolUtilities" -ParameterFilter { $Uri -eq $params.Uri -and $Outfile -eq $params.DownloadPath } - Should -Invoke Invoke-WebRequest -Exactly 1 -Scope It -ModuleName "CommonToolUtilities" - Should -Invoke Start-ThreadJob -Exactly 0 -Scope It -ModuleName "CommonToolUtilities" + # Assert + $result | Should -Be $Script:MockDownloadPath + Should -Invoke Invoke-RestMethod -Exactly 1 -Scope It -ModuleName "CommonToolUtilities" + Should -Invoke Invoke-RestMethod -Exactly 1 -Scope It -ModuleName "CommonToolUtilities" ` + -ParameterFilter { $Uri -eq "https://api.github.com/repos/containerd/nerdctl/releases/latest" } + Should -Invoke Invoke-WebRequest -Exactly 1 -Scope It -ModuleName "CommonToolUtilities" ` + -ParameterFilter { $Uri -eq $Script:MockURL -and $Outfile -eq $Script:MockDownloadPath } + Should -Invoke Invoke-WebRequest -Exactly 1 -Scope It -ModuleName "CommonToolUtilities" ` + -ParameterFilter { $Uri -eq $testChecksumURI -and $Outfile -eq $testChecksumFile } } - It "Should successfully download muliple files asynchronously" { + It "Should successfully download release assets for specified version" { + Mock Invoke-RestMethod -ModuleName "CommonToolUtilities" { + return (Get-Content -Path "$PSScriptRoot\TestData\release-tags.json" -Raw | ConvertFrom-Json -Depth 10 ) + } -ParameterFilter { $Uri -eq "https://api.github.com/repos/containerd/nerdctl/tags" } + $files = $Script:MockFiles + $files[0].Version = 'v2.0.0-rc.1' + $files[0].FileFilterRegEx = "(?:.tar.gz|SHA256SUMS)$" + + # Call method + $result = Get-InstallationFile -FileParameters $files + + # Assert + $result | Should -Be "$Script:MockDownloadPath" + # 1. tags, 2. releases for the specified version + Should -Invoke Invoke-RestMethod -Exactly 2 -Scope It -ModuleName "CommonToolUtilities" + Should -Invoke Invoke-RestMethod -Exactly 1 -Scope It -ModuleName "CommonToolUtilities" ` + -ParameterFilter { $Uri -eq "https://api.github.com/repos/containerd/nerdctl/tags" } + Should -Invoke Invoke-RestMethod -Exactly 1 -Scope It -ModuleName "CommonToolUtilities" ` + -ParameterFilter { $Uri -eq "https://api.github.com/repos/containerd/nerdctl/releases/tags/v2.0.0-rc.1" } + Should -Invoke Invoke-WebRequest -Exactly 1 -Scope It -ModuleName "CommonToolUtilities" ` + -ParameterFilter { $Uri -eq $Script:MockURL -and $Outfile -eq $Script:MockDownloadPath } + } + + It "Should throw an error if no release exists for the specified version" { $files = @( @{ - Feature = "Containerd" - Uri = "https://github.com/v1.0.0/Containerdfile.tar.gz" - Version = '1.0.0' - DownloadPath = "$DownloadPath\downloadedfile.tar.gz" - } - @{ - Feature = "Buildkit" - Uri = "https://github.com/v1.0.0/Buildkitfile.tar.gz" - Version = '1.0.0' - DownloadPath = "$DownloadPath\downloadedfile.tar.gz" + Feature = "nerdctl" + Repo = "containerd/nerdctl" + Version = 'v8.i.0' + DownloadPath = "TestDrive:\Download" } ) - # $containerdParams = $files[0] - # $buildkitParams = $files[1] - Get-InstallationFile -Files $files - # Should -Invoke Invoke-WebRequest -Exactly 1 -Scope It -ModuleName "CommonToolUtilities" ` - # -ParameterFilter { $Uri -eq $containerdParams.Uri -and $Outfile -eq $containerdParams.DownloadPath } - # Should -Invoke Invoke-WebRequest -Exactly 1 -Scope It -ModuleName "CommonToolUtilities" ` - # -ParameterFilter { $Uri -eq $buildkitParams.Uri -and $Outfile -eq $buildkitParams.DownloadPath } - # Should -Invoke Invoke-WebRequest -Exactly 2 -Scope It -ModuleName "CommonToolUtilities" - Should -Invoke Start-ThreadJob -Exactly 2 -Scope It -ModuleName "CommonToolUtilities" - Should -Invoke Receive-Job -Exactly 2 -Scope It -ModuleName "CommonToolUtilities" + # Call method + { Get-InstallationFile -FileParameters $files } | Should -Throw "Couldn't find release tags for the provided version: 'v8.i.0'" } - It "Should throw an error if download fails" { - $params = @{ - Feature = "Containerd" - Uri = "https://github.com/v1.0.0/downloadedfile.tar.gz" - Version = '1.0.0' - DownloadPath = "$DownloadPath\downloadedfile.tar.gz" - } - $files = @($params) + It "Should throw an error if no release exists for the specified architecture" { + $invalidArch = $Script:MockFiles + $invalidArch[0].OSArchitecture = "invalid" + { Get-InstallationFile -FileParameters $invalidArch } | Should -Throw "Couldn't find release assets for the provided architecture: 'invalid'" + } + + It "Should throw an error if no checksum file is found" { + $invalidArch = $Script:MockFiles + $invalidArch[0].FileFilterRegEx = "(.tar.gz)$" # Change the filter to not include checksum file + { Get-InstallationFile -FileParameters $invalidArch } | Should -Throw "Some files were not downloaded. Failed to find checksum files for $Script:TestFileName." + } + + It "Should throw an error if verification fails" { + Mock Test-Checksum -ModuleName "CommonToolUtilities" -MockWith { return $false } + { Get-InstallationFile -FileParameters $Script:MockFiles } | Should -Throw "Failed to download asset*" + $Error[1].Exception.Message | Should -BeLike 'Failed to download assets for "v2.0.0-rc.1". Checksum verification failed.*' + } + + It "Should throw an error if GitHub API call fails" { + $errorMessage = "Response status code does not indicate success: 404 (Not Found)." + Mock Invoke-RestMethod { Throw $errorMessage } -ModuleName "CommonToolUtilities" + + { Get-InstallationFile -FileParameters $Script:MockFiles } | Should -Throw "GitHub API error.*" + } + + It "Should throw an error if download fails" { $errorMessage = "Response status code does not indicate success: 404 (Not Found)." Mock Invoke-WebRequest { Throw $errorMessage } -ModuleName "CommonToolUtilities" - { Get-InstallationFile -Files $files } | Should -Throw "Containerd downlooad failed: https://github.com/v1.0.0/downloadedfile.tar.gz.`n$errorMessage" + + { Get-InstallationFile -FileParameters $Script:MockFiles } | Should -Throw "Failed to download asset*" + $Error[1].Exception.Message | Should -BeLike "Failed to download assets for `"v2.0.0-rc.1`". Couldn`'t download `"v2.0.0-rc.1`" release assets.*" } } @@ -240,31 +295,39 @@ Describe "CommonToolUtilities.psm1" { } Context "Test-FileCheckSum" -Tag "Test-FileCheckSum" { - BeforeAll { - Mock Invoke-WebRequest -ModuleName "CommonToolUtilities" - Mock Remove-Item -ModuleName "CommonToolUtilities" + BeforeEach { $Script:DownloadedFile = "TestDrive:\nerdctl-2.0.0-windows-amd64.tar.gz" - $Script:ChecksumUri = "https://test.com/SHA256SUMS" $Script:ChecksumFile = "TestDrive:\SHA256SUMS" + Mock Remove-Item -ModuleName "CommonToolUtilities" + Mock Test-Path -ModuleName "CommonToolUtilities" -MockWith { return $true } + Mock Test-Path -ModuleName "CommonToolUtilities" -MockWith { return $true } -ParameterFilter { $Path -eq $Script:DownloadedFile } + Mock Test-Path -ModuleName "CommonToolUtilities" -MockWith { return $true } -ParameterFilter { $Path -eq $Script:ChecksumFile } + # Create the test file New-Item -Path $Script:DownloadedFile -ItemType File -Force | Out-Null Set-Content -Path $Script:DownloadedFile -Value "This is a test file." } + AfterEach { + Get-ChildItem "TestDrive:\" | Remove-Item -Recurse -Force + } + + It "Should throw an error if the downloaded file does not exist" { + Mock Test-Path -ModuleName "CommonToolUtilities" -MockWith { return $false } -ParameterFilter { $Path -eq $Script:DownloadedFile } + + { Test-CheckSum -DownloadedFile $Script:DownloadedFile -ChecksumFile $Script:ChecksumFile } | Should -Throw "Couldn't find source file: `"$Script:DownloadedFile`"." + } + It "should verify checksum successfully" { Mock Get-FileHash -ModuleName "CommonToolUtilities" -MockWith { return @{ Hash = "SampleHash" } } Mock Get-Content -ModuleName "CommonToolUtilities" ` -ParameterFilter { $Path -eq $Script:ChecksumFile } ` -MockWith { return "SampleHash nerdctl-2.0.0-windows-amd64.tar.gz" } - $result = Test-CheckSum -downloadedFile $Script:downloadedFile -checksumUri $Script:checksumUri + $result = Test-CheckSum -downloadedFile $Script:downloadedFile -ChecksumFile $Script:ChecksumFile $result | Should -Be $true - Should -Invoke Invoke-WebRequest -Times 1 -Scope It -ModuleName "CommonToolUtilities" -ParameterFilter { - $Uri -eq $Script:checksumUri -and - $Outfile -eq $Script:ChecksumFile - } Should -Invoke Get-FileHash -Times 1 -Scope It -ModuleName "CommonToolUtilities" -ParameterFilter { $Path -eq $Script:downloadedFile -and $Algorithm -eq 'SHA256' @@ -272,94 +335,69 @@ Describe "CommonToolUtilities.psm1" { Should -Invoke Get-Content -Times 1 -Scope It -ModuleName "CommonToolUtilities" -ParameterFilter { $Path -eq $Script:ChecksumFile } - Should -Invoke Remove-Item -Times 1 -Scope It -ModuleName "CommonToolUtilities" -ParameterFilter { - $Path -eq $Script:ChecksumFile - } + Should -Invoke Remove-Item -Times 1 -Scope It -ModuleName "CommonToolUtilities" ` + -ParameterFilter { $Path -eq $Script:ChecksumFile } } It "should return true when checksums match" { - Mock Invoke-WebRequest -ModuleName "CommonToolUtilities" -MockWith { - $downloadedFileHash = (Get-FileHash -Path $Script:DownloadedFile -Algorithm SHA256).Hash - # Create the checksum file - New-Item -Path $Script:ChecksumFile -ItemType File -Force | Out-Null - Set-Content -Path $Script:ChecksumFile -Value ( - $SampleSha256Sum -replace "__CHECKSUM__", $downloadedFileHash) - } - - $result = Test-CheckSum -DownloadedFile $Script:DownloadedFile -ChecksumUri $Script:ChecksumUri + # Do an actual file hash + # Create the checksum file + $downloadedFileHash = (Get-FileHash -Path $Script:DownloadedFile -Algorithm SHA256).Hash + New-Item -Path $Script:ChecksumFile -ItemType File -Force | Out-Null + Set-Content -Path $Script:ChecksumFile -Value ( + $SampleSha256Sum -replace "__CHECKSUM__", $downloadedFileHash) + + $result = Test-CheckSum -DownloadedFile $Script:DownloadedFile -ChecksumFile $Script:ChecksumFile $result | Should -Be $true - Should -Invoke Invoke-WebRequest -Times 1 -Scope It -ModuleName "CommonToolUtilities" ` - -ParameterFilter { $Uri -eq $Script:ChecksumUri -and $OutFile -eq $Script:ChecksumFile } - Should -Invoke Remove-Item -Times 1 -Scope It -ModuleName "CommonToolUtilities" ` - -ParameterFilter { $Path -eq $Script:ChecksumFile } - # Test regex $filePath = "TestDrive:\nerdctl-2.0.0-linux-arm64.tar.gz" New-Item -Path $filePath -ItemType File -Force | Out-Null Mock Get-FileHash -ModuleName "CommonToolUtilities" -MockWith { return @{ Hash = "0286780561d8eb915922b9SaMpLeSHA45abdfeef3e" } } - $result = Test-CheckSum -DownloadedFile $filePath -ChecksumUri $Script:ChecksumUri + $result = Test-CheckSum -DownloadedFile $filePath -ChecksumFile $Script:ChecksumFile $result | Should -Be $true - } - - It "should throw an error when checksum file download fails" { - # Mock the Invoke-WebRequest cmdlet to throw an error - Mock -CommandName Invoke-WebRequest -ModuleName "CommonToolUtilities" -MockWith { Throw "Download failed" } - - # Test the function - { Test-CheckSum -DownloadedFile $Script:DownloadedFile -ChecksumUri $Script:ChecksumUri } | ` - Should -Throw "Checksum file download failed: $Script:ChecksumUri.*" + Should -Invoke Remove-Item -Times 1 -Scope It -ModuleName "CommonToolUtilities" ` + -ParameterFilter { $Path -eq $Script:ChecksumFile } } It "should throw and error if invalid hash function is used" { - $invalidChecksumUri = "https://test.com/SHA99SUMS" - { Test-CheckSum -DownloadedFile $Script:DownloadedFile -ChecksumUri $invalidChecksumUri } | ` + $invalidChecksumFile = "TestDrive:\SHA99SUMS" + { Test-CheckSum -DownloadedFile $Script:DownloadedFile -ChecksumFile $invalidChecksumFile } | ` Should -Throw "Checksum verification failed for $Script:DownloadedFile. Invalid hash function.*" - Should -Invoke Remove-Item -Times 1 -Scope It -ModuleName "CommonToolUtilities" ` - -ParameterFilter { $Path -eq "TestDrive:\SHA99SUMS" } } It "should return false when checksums do not match" { - Mock Invoke-WebRequest -ModuleName "CommonToolUtilities" -MockWith { - New-Item -Path $Script:ChecksumFile -ItemType File -Force | Out-Null - Set-Content -Path $Script:ChecksumFile -Value ( - $SampleSha256Sum -replace "__CHECKSUM__", "InvalidHash") - } - - $result = Test-CheckSum -DownloadedFile $Script:DownloadedFile -ChecksumUri $Script:ChecksumUri + New-Item -Path $Script:ChecksumFile -ItemType File -Force | Out-Null + Set-Content -Path $Script:ChecksumFile -Value ( + $SampleSha256Sum -replace "__CHECKSUM__", "InvalidHash") + $result = Test-CheckSum -DownloadedFile $Script:DownloadedFile -ChecksumFile $Script:ChecksumFile $result | Should -Be $false - Should -Invoke Invoke-WebRequest -Times 1 -Scope It -ModuleName "CommonToolUtilities" ` - -ParameterFilter { $Uri -eq $Script:ChecksumUri -and $OutFile -eq $Script:ChecksumFile } Should -Invoke Remove-Item -Times 1 -Scope It -ModuleName "CommonToolUtilities" ` -ParameterFilter { $Path -eq $Script:ChecksumFile } } It "should return false when downloaded file name does not match SHA256SUMS file names" { - Mock Invoke-WebRequest -ModuleName "CommonToolUtilities" -MockWith { - # Create the checksum file - New-Item -Path $Script:ChecksumFile -ItemType File -Force | Out-Null - Set-Content -Path $Script:ChecksumFile -Value "SampleHash nerdctl-2.0.0-linux-amd64.tar.gz" - } + # Create the checksum file + New-Item -Path $Script:ChecksumFile -ItemType File -Force | Out-Null + Set-Content -Path $Script:ChecksumFile -Value "SampleHash nerdctl-2.0.0-linux-amd64.tar.gz" $invalid_download_file = "TestDrive:\invalid-file-name.tar.gz" New-Item -Path $invalid_download_file -ItemType File -Force | Out-Null - { Test-CheckSum -DownloadedFile $invalid_download_file -ChecksumUri $Script:ChecksumUri } | ` + { Test-CheckSum -DownloadedFile $invalid_download_file -ChecksumFile $Script:ChecksumFile } | ` Should -Throw "Checksum verification failed for $invalid_download_file. Checksum not found for `"invalid-file-name.tar.gz`" in $Script:ChecksumFile" Should -Invoke Remove-Item -Times 1 -Scope It -ModuleName "CommonToolUtilities" ` -ParameterFilter { $Path -eq $Script:ChecksumFile } } It "should throw an error for invalid file content format" { - Mock Invoke-WebRequest -ModuleName "CommonToolUtilities" -MockWith { - New-Item -Path $Script:ChecksumFile -ItemType File -Force | Out-Null - Set-Content -Path $Script:ChecksumFile -Value "sha256sum sample-tool.tar.gz invalid-format" - } + New-Item -Path $Script:ChecksumFile -ItemType File -Force | Out-Null + Set-Content -Path $Script:ChecksumFile -Value "sha256sum sample-tool.tar.gz invalid-format" - { Test-CheckSum -DownloadedFile $Script:DownloadedFile -ChecksumUri $Script:ChecksumUri } | ` + { Test-CheckSum -DownloadedFile $Script:DownloadedFile -ChecksumFile $Script:ChecksumFile } | ` Should -Throw "Checksum verification failed for $Script:DownloadedFile. Invalid checksum file content format in $Script:ChecksumFile. Expected format: ." Should -Invoke Remove-Item -Times 1 -Scope It -ModuleName "CommonToolUtilities" ` -ParameterFilter { $Path -eq $Script:ChecksumFile } @@ -368,24 +406,25 @@ Describe "CommonToolUtilities.psm1" { It "should catch error when commands fail" { Mock Get-FileHash -ModuleName "CommonToolUtilities" -MockWith { Throw "Error" } - { Test-CheckSum -DownloadedFile $Script:DownloadedFile -ChecksumUri $Script:ChecksumUri } | Should -Throw "Checksum verification failed for $Script:DownloadedFile. Error" + { Test-CheckSum -DownloadedFile $Script:DownloadedFile -ChecksumFile $Script:ChecksumFile } | Should -Throw "Checksum verification failed for $Script:DownloadedFile. Error" Should -Invoke Remove-Item -Times 1 -Scope It -ModuleName "CommonToolUtilities" ` -ParameterFilter { $Path -eq $Script:ChecksumFile } } } Context "Test-JSONChecksum" -Tag "Test-JSONChecksum" { - BeforeAll { - Mock Invoke-WebRequest -ModuleName "CommonToolUtilities" - Mock Test-Path -ModuleName "CommonToolUtilities" -MockWith { return $true } - Mock Test-Json -ModuleName "CommonToolUtilities" -MockWith { return $true } - Mock Remove-Item -ModuleName "CommonToolUtilities" - + BeforeEach { $Script:DownloadedFile = "TestDrive:\buildkit-v1.0.0.windows-amd64.tar.gz" - $Script:ChecksumUri = "https://test.com/sample-tool.provenance.json" $Script:ChecksumFile = "TestDrive:\sample-tool.provenance.json" $Script:SchemaFile = "$PSScriptRoot\TestData\test-schema.json" + Mock Remove-Item -ModuleName "CommonToolUtilities" + Mock Test-Json -ModuleName "CommonToolUtilities" -MockWith { return $true } + Mock Test-Path -ModuleName "CommonToolUtilities" -MockWith { return $true } + Mock Test-Path -ModuleName "CommonToolUtilities" -MockWith { return $true } + Mock Test-Path -ModuleName "CommonToolUtilities" -MockWith { return $true } -ParameterFilter { $Path -eq $Script:DownloadedFile } + Mock Test-Path -ModuleName "CommonToolUtilities" -MockWith { return $true } -ParameterFilter { $Path -eq $Script:ChecksumFile } + # Create the test file New-Item -Path $Script:DownloadedFile -ItemType File -Force | Out-Null Set-Content -Path $Script:downloadedFile -Value "This is a test file." @@ -395,14 +434,18 @@ Describe "CommonToolUtilities.psm1" { "SHA256", (Get-FileHash -Path $Script:DownloadedFile -Algorithm SHA256).Hash) $Script:FunctionToCall = { Test-CheckSum ` -DownloadedFile $Script:DownloadedFile ` - -ChecksumUri $Script:ChecksumUri ` + -ChecksumFile $Script:ChecksumFile ` -JSON ` -SchemaFile $Script:SchemaFile ` -ExtractDigestScriptBlock { return $MockExtractedFileDigest } ` - -ExtractDigestArguments @($Script:DownloadedFile, $Script:ChecksumUri) + -ExtractDigestArguments @($Script:DownloadedFile, $Script:ChecksumFile) } } + AfterEach { + Get-ChildItem "TestDrive:\" | Remove-Item -Recurse -Force + } + It "should verify checksum successfully using in-toto SBOM format" { Mock Get-FileHash -ModuleName "CommonToolUtilities" -MockWith { return @{ Hash = "SampleHash" } } Mock Get-Content -ModuleName "CommonToolUtilities" ` @@ -411,17 +454,11 @@ Describe "CommonToolUtilities.psm1" { $result = Test-CheckSum -JSON ` -DownloadedFile $Script:DownloadedFile ` - -ChecksumUri $Script:ChecksumUri ` + -ChecksumFile $Script:ChecksumFile ` -SchemaFile $Script:SchemaFile $result | Should -Be $true - # Check file is downloaded - Should -Invoke Invoke-WebRequest -Times 1 -Scope It -ModuleName "CommonToolUtilities" -ParameterFilter { - $Uri -eq $Script:ChecksumUri -and - $Outfile -eq $Script:ChecksumFile - } - # Validate Checksum file content Should -Invoke Get-Content -ModuleName "CommonToolUtilities" -ParameterFilter { $Path -eq $Script:ChecksumFile } @@ -444,19 +481,13 @@ Describe "CommonToolUtilities.psm1" { $result = Test-CheckSum -JSON ` -DownloadedFile $Script:DownloadedFile ` - -ChecksumUri $Script:ChecksumUri ` + -ChecksumFile $Script:ChecksumFile ` -SchemaFile $Script:SchemaFile ` -ExtractDigestScriptBlock { return ([FileDigest]::new("SHA256", "SampleHash")) } ` - -ExtractDigestArguments @($Script:DownloadedFile, $Script:ChecksumUri) + -ExtractDigestArguments @($Script:DownloadedFile, $Script:ChecksumFile) $result | Should -Be $true - # Check file is downloaded - Should -Invoke Invoke-WebRequest -Times 1 -Scope It -ModuleName "CommonToolUtilities" -ParameterFilter { - $Uri -eq $Script:ChecksumUri -and - $Outfile -eq $Script:ChecksumFile - } - # Validate JSON file Should -Invoke Get-Content -Times 1 -Scope It -ModuleName "CommonToolUtilities" -ParameterFilter { $Path -eq $Script:ChecksumFile @@ -482,7 +513,7 @@ Describe "CommonToolUtilities.psm1" { $ScriptNoArgs = { Test-CheckSum -JSON ` -DownloadedFile $Script:DownloadedFile ` - -ChecksumUri $Script:ChecksumUri ` + -ChecksumFile $Script:ChecksumFile ` -SchemaFile $Script:SchemaFile ` -ExtractDigestScriptBlock { return ([FileDigest]::new("SHA256", "SampleHash")) } } @@ -491,71 +522,52 @@ Describe "CommonToolUtilities.psm1" { } It "should return true when checksums match" { - Mock Invoke-WebRequest -ModuleName "CommonToolUtilities" -MockWith { - $downloadedFileHash = (Get-FileHash -Path $Script:DownloadedFile -Algorithm SHA256).Hash - - # Create the checksum file - New-Item -Path $Script:ChecksumFile -ItemType File -Force | Out-Null - Set-Content -Path $Script:ChecksumFile -Value ( - $SbomJson -replace "__CHECKSUM__", $downloadedFileHash) - } + # Create the checksum file + New-Item -Path $Script:ChecksumFile -ItemType File -Force | Out-Null + Set-Content -Path $Script:ChecksumFile -Value ( + $SbomJson -replace "__CHECKSUM__", $downloadedFileHash) $result = & $Script:FunctionToCall $result | Should -Be $true - Should -Invoke Invoke-WebRequest -Times 1 -Scope It -ModuleName "CommonToolUtilities" ` - -ParameterFilter { $Uri -eq $Script:ChecksumUri -and $OutFile -eq $Script:ChecksumFile } Should -Invoke Remove-Item -Times 1 -Scope It -ModuleName "CommonToolUtilities" ` -ParameterFilter { $Path -eq $Script:ChecksumFile } } - It "should throw an error when checksum file download fails" { - # Mock the Invoke-WebRequest cmdlet to throw an error - Mock -CommandName Invoke-WebRequest -ModuleName "CommonToolUtilities" -MockWith { Throw "Download failed" } + It "should throw an error if checksum file does not exist" { + Mock Test-Path -ModuleName "CommonToolUtilities" -MockWith { return $false } -ParameterFilter { $Path -eq $Script:ChecksumFile } - # Test the function - { & $Script:FunctionToCall } | Should -Throw "Checksum file download failed: $checksumUri.*" + { & $Script:FunctionToCall } | Should -Throw "Couldn't find checksum file: `"$Script:ChecksumFile`"." } It "should throw an error when checksum file does not use in-toto SBOM format and script block is not provided" { $NonInTotoJson = "TestDrive:\non-in-toto.sbom.json" - $NonInTotoUri = "https://test.com/non-in-toto.sbom.json" - - Mock -CommandName Invoke-WebRequest -ModuleName "CommonToolUtilities" ` - -MockWith { - New-Item -Path $NonInTotoJson -ItemType File -Force | Out-Null - Set-Content -Path $NonInTotoJson -Value $OtherSbomFormatJson - } ` - -ParameterFilter { $Uri -eq $NonInTotoUri } + New-Item -Path $NonInTotoJson -ItemType File -Force | Out-Null + Set-Content -Path $NonInTotoJson -Value $OtherSbomFormatJson $ToCall = { Test-CheckSum -JSON ` -DownloadedFile $Script:DownloadedFile ` - -ChecksumUri $NonInTotoUri ` + -ChecksumFile $NonInTotoJson ` -SchemaFile $Script:SchemaFile } { & $ToCall } | Should -Throw "Invalid checksum JSON format. Expected in-toto SBOM format*" } It "should throw an error when digest file name is not the same as the downloaded file name" { - # Mock the Invoke-WebRequest cmdlet to throw an error - Mock -CommandName Invoke-WebRequest -ModuleName "CommonToolUtilities" ` - -MockWith { - New-Item -Path $Script:ChecksumFile -ItemType File -Force | Out-Null - Set-Content -Path $Script:ChecksumFile -Value $InvalidFileNameJson - } ` - -ParameterFilter { $Uri -eq $Script:ChecksumUri } + New-Item -Path $Script:ChecksumFile -ItemType File -Force | Out-Null + Set-Content -Path $Script:ChecksumFile -Value $InvalidFileNameJson $ToCall = { Test-CheckSum -JSON ` - -DownloadedFile $Script:DownloadedFile ` - -ChecksumUri $Script:ChecksumUri ` - -SchemaFile $Script:SchemaFile } + -DownloadedFile $Script:DownloadedFile ` + -ChecksumFile $Script:ChecksumFile ` + -SchemaFile $Script:SchemaFile } { & $ToCall } | Should -Throw "Downloaded file name does not match the subject name*" } It "should throw an error if the schema file does not exist" { Mock Test-Path -ModuleName "CommonToolUtilities" -MockWith { return $false } -ParameterFilter { $Path -eq $Script:SchemaFile } - { & $Script:FunctionToCall } | Should -Throw "Couldn't find the provided schema file: $Script:SchemaFile" + { & $Script:FunctionToCall } | Should -Throw "Couldn't find the JSON schema file: `"$Script:SchemaFile`"." } It "should throw an error if the schema file is empty" { @@ -565,24 +577,29 @@ Describe "CommonToolUtilities.psm1" { } It "should throw an error if the JSON file is not valid" { + Mock Get-Content -ModuleName "CommonToolUtilities" -MockWith { return "Test data" } + + # Test-Json returns true if the JSON is valid, otherwise it throws an error Mock Test-Json -ModuleName "CommonToolUtilities" -MockWith { Throw "Error" } + # Test the function { & $Script:FunctionToCall } | Should -Throw "Invalid JSON format in checksum file. Error" } It "should throw an error if script block throws an error" { # ParentContainsErrorRecordException: Exception calling "Invoke" with "1" argument(s): "Error message" - { Test-CheckSum -DownloadedFile $Script:DownloadedFile -ChecksumUri $Script:ChecksumUri -JSON -SchemaFile $Script:SchemaFile -ExtractDigestScriptBlock { (Throw "Error message") } | ` + { Test-CheckSum -DownloadedFile $Script:DownloadedFile -ChecksumFile $Script:ChecksumFile -JSON -SchemaFile $Script:SchemaFile -ExtractDigestScriptBlock { (Throw "Error message") } | ` Should -Throw "Invalid script block output*" } } It "should throw an error if ExtractedFileDigest is not a FileDigest object" { + Mock Get-Content -ModuleName "CommonToolUtilities" -MockWith { return "Test data" } Mock Get-FileHash -ModuleName "CommonToolUtilities" -MockWith { return "SampleHash" } $InvalidOutputFunc = { Test-CheckSum -JSON ` -DownloadedFile $Script:DownloadedFile ` - -ChecksumUri $Script:ChecksumUri ` + -ChecksumFile $Script:ChecksumFile ` -SchemaFile $Script:SchemaFile ` -ExtractDigestScriptBlock { return } ` -ExtractDigestArguments @($Script:DownloadedFile, $Script:ChecksumUri) @@ -591,9 +608,11 @@ Describe "CommonToolUtilities.psm1" { } It "should throw and error if invalid hash function is used" { + Mock Get-Content -ModuleName "CommonToolUtilities" -MockWith { return "Test data" } + $InvalidAlgo = { Test-CheckSum ` -DownloadedFile $Script:DownloadedFile ` - -ChecksumUri $Script:ChecksumUri ` + -ChecksumFile $Script:ChecksumFile ` -JSON ` -SchemaFile $Script:SchemaFile ` -ExtractDigestScriptBlock { return ([FileDigest]::new("SHA99", "SampleHash") ) } ` @@ -607,10 +626,12 @@ Describe "CommonToolUtilities.psm1" { } It "should return false when checksums do not match" { + Mock Get-Content -ModuleName "CommonToolUtilities" -MockWith { return "Test data" } + # Test the function $result = Test-CheckSum ` -DownloadedFile "TestDrive:\sample-tool.tar.gz" ` - -ChecksumUri $Script:ChecksumUri ` + -ChecksumFile $Script:ChecksumFile ` -JSON ` -SchemaFile $Script:SchemaFile ` -ExtractDigestScriptBlock { return ([FileDigest]::new("SHA256", "InvalidHash")) } ` @@ -622,6 +643,7 @@ Describe "CommonToolUtilities.psm1" { } It "should catch error when commands fail" { + Mock Get-Content -ModuleName "CommonToolUtilities" -MockWith { return "Test data" } Mock Get-FileHash -ModuleName "CommonToolUtilities" -MockWith { Throw "Error" } # Test the function @@ -638,15 +660,18 @@ Describe "CommonToolUtilities.psm1" { Mock Add-FeatureToPath -ModuleName "CommonToolUtilities" Mock New-Item -ModuleName "CommonToolUtilities" Mock Remove-Item -ModuleName "CommonToolUtilities" + Mock Test-Path -ModuleName "CommonToolUtilities" -MockWith { return $true } } It "Should successfully install tool" { + Mock Test-Path -ModuleName "CommonToolUtilities" -MockWith { return $false } -ParameterFilter { $Path -eq $params.InstallPath } + $params = @{ - Feature = "containerd" - InstallPath = "$ProgramFiles\Containerd" - DownloadPath = "$DownloadPath\containerd-binaries.tar.gz" - EnvPath = "$ProgramFiles\Containerd\bin" - cleanup = $true + Feature = "containerd" + InstallPath = "$ProgramFiles\Containerd" + SourceFile = "$DownloadPath\containerd-binaries.tar.gz" + EnvPath = "$ProgramFiles\Containerd\bin" + cleanup = $true } Install-RequiredFeature @params @@ -655,22 +680,22 @@ Describe "CommonToolUtilities.psm1" { Should -Invoke New-Item -Times 1 -Scope It -ModuleName "CommonToolUtilities" -ParameterFilter { ($Path -eq $params.InstallPath) } # Test that the file is untar-ed - Should -Invoke Invoke-ExecutableCommand -Times 1 -Scope It -ModuleName "CommonToolUtilities" -ParameterFilter { ($Executable -eq 'tar.exe') -and ($Arguments -eq "-xf `"$($params.DownloadPath)`" -C `"$($params.InstallPath)`"") } + Should -Invoke Invoke-ExecutableCommand -Times 1 -Scope It -ModuleName "CommonToolUtilities" -ParameterFilter { ($Executable -eq 'tar.exe') -and ($Arguments -eq "-xf `"$($params.SourceFile)`" -C `"$($params.InstallPath)`"") } # Test that method to add feature to path is called Should -Invoke Add-FeatureToPath -Times 1 -Scope It -ModuleName "CommonToolUtilities" -ParameterFilter { ($Feature -eq $params.Feature) -and ($Path -eq $params.EnvPath) } # Check that clean up is done on completion - Should -Invoke Remove-Item -Times 1 -Scope It -ModuleName "CommonToolUtilities" -ParameterFilter { $Path -eq $params.DownloadPath } + Should -Invoke Remove-Item -Times 1 -Scope It -ModuleName "CommonToolUtilities" -ParameterFilter { $Path -eq $params.SourceFile } } It "should not remove items if cleanup is false" { $params = @{ - Feature = "containerd" - InstallPath = "$ProgramFiles\Containerd" - DownloadPath = "$DownloadPath\containerd-binaries.tar.gz" - EnvPath = "$ProgramFiles\Containerd\bin" - cleanup = $false + Feature = "containerd" + InstallPath = "$ProgramFiles\Containerd" + SourceFile = "$DownloadPath\containerd-binaries.tar.gz" + EnvPath = "$ProgramFiles\Containerd\bin" + cleanup = $false } Install-RequiredFeature @params Should -Invoke Remove-Item -ModuleName "CommonToolUtilities" -Times 0 -Scope It @@ -685,14 +710,14 @@ Describe "CommonToolUtilities.psm1" { } Mock Invoke-ExecutableCommand -ModuleName "CommonToolUtilities" -MockWith { return $obj } $params = @{ - Feature = "containerd" - InstallPath = "$ProgramFiles\Containerd" - DownloadPath = "$DownloadPath\containerd-binaries.tar.gz" - EnvPath = "$ProgramFiles\Containerd\bin" - cleanup = $false + Feature = "containerd" + InstallPath = "$ProgramFiles\Containerd" + SourceFile = "$DownloadPath\containerd-binaries.tar.gz" + EnvPath = "$ProgramFiles\Containerd\bin" + cleanup = $false } - { Install-RequiredFeature @params } | Should -Throw "Could not untar file $($params.DownloadPath) at $($params.InstallPath). Error message" + { Install-RequiredFeature @params } | Should -Throw "Couldn't expand archive file(s) $($params.SourceFile).*" } } diff --git a/Tests/ContainerNetworkTools.Tests.ps1 b/Tests/ContainerNetworkTools.Tests.ps1 index 48c63b2..d56a32c 100644 --- a/Tests/ContainerNetworkTools.Tests.ps1 +++ b/Tests/ContainerNetworkTools.Tests.ps1 @@ -7,6 +7,8 @@ ########################################################################### +using module "..\containers-toolkit\Private\CommonToolUtilities.psm1" + Describe "ContainerNetworkTools.psm1" { BeforeAll { $RootPath = Split-Path -Parent $PSScriptRoot @@ -31,18 +33,22 @@ Describe "ContainerNetworkTools.psm1" { Context "Install-WinCNIPlugin" -Tag "Install-WinCNIPlugin" { BeforeAll { + $Script:WinCNIRepo = 'https://github.com/microsoft/windows-container-networking/releases/download' + $Script:MockZipFileName = "windows-container-networking-cni-amd64-v1.0.0.zip" + $Script:TestDownloadPath = "$HOME\Downloads\$Script:MockZipFileName" + Mock Get-WinCNILatestVersion { return '1.0.0' } -ModuleName 'ContainerNetworkTools' Mock Uninstall-WinCNIPlugin -ModuleName "ContainerNetworkTools" Mock New-Item -ModuleName 'ContainerNetworkTools' - Mock Get-InstallationFile -ModuleName 'ContainerNetworkTools' + Mock Get-Item -ModuleName 'ContainerNetworkTools' -MockWith { @{ Path = $Script:TestDownloadPath } } -ParameterFilter { $Path -eq $Script:TestDownloadPath } + Mock Get-InstallationFile -ModuleName 'ContainerNetworkTools' -MockWith { $Script:TestDownloadPath } Mock Expand-Archive -ModuleName 'ContainerNetworkTools' Mock Remove-Item -ModuleName 'ContainerNetworkTools' Mock Test-EmptyDirectory -ModuleName 'ContainerNetworkTools' -MockWith { return $true } Mock Install-WinCNIPlugin -ModuleName 'ContainerNetworkTools' - Mock Test-CheckSum -ModuleName 'ContainerNetworkTools' -MockWith { return $true } - - $Script:WinCNIRepo = 'https://github.com/microsoft/windows-container-networking/releases/download' - $Script:MockZipFileName = "windows-container-networking-cni-amd64-v1.0.0.zip" + Mock Test-Path -ModuleName 'ContainerNetworkTools' -MockWith { return $true } + Mock Invoke-ExecutableCommand -ModuleName 'ContainerNetworkTools' -MockWith { + return New-MockObject -Type 'System.Diagnostics.Process' -Properties @{ ExitCode = 0 } } } It 'Should not process on implicit request for validation (WhatIfPreference)' { @@ -63,62 +69,39 @@ Describe "ContainerNetworkTools.psm1" { Should -Invoke Uninstall-WinCNIPlugin -ModuleName 'ContainerNetworkTools' -Times 0 -Exactly -Scope It Should -Invoke Get-InstallationFile -ModuleName 'ContainerNetworkTools' -ParameterFilter { - $Files -like @( - @{ - Feature = "WinCNIPlugin" - Uri = "$Script:WinCNIRepo/v1.0.0/$Script:MockZipFileName" - Version = '1.0.0' - DownloadPath = "$HOME\Downloads" - } - ) - } - - $TestDownloadPath = "$HOME\Downloads\$Script:MockZipFileName" - Should -Invoke Expand-Archive -ModuleName 'ContainerNetworkTools' -ParameterFilter { - $Path -eq "$TestDownloadPath" -and - $DestinationPath -eq "$Env:ProgramFiles\Containerd\cni\bin" + $fileParameters[0].Feature -eq "WinCNIPlugin" -and + $fileParameters[0].Repo -eq "microsoft/windows-container-networking" -and + $fileParameters[0].Version -eq 'latest' -and + $fileParameters[0].DownloadPath -eq "$HOME\Downloads" + [string]::IsNullOrWhiteSpace($fileParameters.ChecksumSchemaFile) -and + $fileParameters[0].FileFilterRegEx -eq $null } - Should -Invoke Test-CheckSum -ModuleName 'ContainerNetworkTools' -ParameterFilter { - $DownloadedFile -eq "$TestDownloadPath" -and - $ChecksumUri -eq "$Script:WinCNIRepo/v1.0.0/$Script:MockZipFileName.sha512" - } - Should -Invoke Remove-Item -ModuleName 'ContainerNetworkTools' -ParameterFilter { - $Path -eq "$TestDownloadPath" + Should -Invoke Invoke-ExecutableCommand -ModuleName 'ContainerNetworkTools' -ParameterFilter { + $executable -eq "tar.exe" -and + $arguments -eq "-xf `"$Script:TestDownloadPath`" -C `"C:\Program Files\Containerd\cni\bin`"" } } It "Should call function with user-specified values" { - Install-WinCNIPlugin -WinCNIVersion '1.2.3' -WinCNIPath 'TestDrive:\WinCNI\bin' -Force -Confirm:$false + # Mocks + $MockZipFileName = 'windows-container-networking-cni-386-v1.2.3.zip' + $MockDownloadFilePath = "$HOME\Downloads\$MockZipFileName" + Mock Get-InstallationFile -ModuleName 'ContainerNetworkTools' -MockWith { $MockDownloadFilePath } - Should -Invoke Uninstall-WinCNIPlugin -ModuleName 'ContainerNetworkTools' -Times 0 -Exactly -Scope It + # Test + Install-WinCNIPlugin -WinCNIVersion '1.2.3' -WinCNIPath 'TestDrive:\WinCNI\bin' -SourceRepo "containernetworking/plugins" -OSArchitecture '386' -Force -Confirm:$false - $MockZipFileName = 'windows-container-networking-cni-amd64-v1.2.3.zip' + # Assertions + Should -Invoke Uninstall-WinCNIPlugin -ModuleName 'ContainerNetworkTools' -Times 0 -Exactly -Scope It Should -Invoke Get-InstallationFile -ModuleName 'ContainerNetworkTools' -ParameterFilter { - $Files -like @( - @{ - Feature = "WinCNIPlugin" - Uri = "$Script:WinCNIRepo/v1.2.3/$MockZipFileName" - Version = '1.2.3' - DownloadPath = "$HOME\Downloads" - } - ) - } - Should -Invoke Expand-Archive -ModuleName 'ContainerNetworkTools' -ParameterFilter { - $Path -eq "$HOME\Downloads\$MockZipFileName" - $DestinationPath -eq "TestDrive:\WinCNI\bin" + $fileParameters[0].Version -eq '1.2.3' -and + $fileParameters[0].Repo -eq 'containernetworking/plugins' -and + $fileParameters[0].OSArchitecture -eq '386' -and + $fileParameters[0].FileFilterRegEx -eq ".*tgz(.SHA512)?$" } - Should -Invoke Remove-Item -ModuleName 'ContainerNetworkTools' -ParameterFilter { - $Path -eq "$HOME\Downloads\$MockZipFileName" - } - } - - It "should throw an error when checksum verification fails" { - Mock Test-CheckSum -ModuleName 'ContainerNetworkTools' -MockWith { return $false } - - $TestDownloadPath ="$HOME\Downloads\$Script:MockZipFileName" - { Install-WinCNIPlugin -Force -Confirm:$false } | Should -Throw "Checksum verification failed for $TestDownloadPath" - Should -Invoke Remove-Item -ModuleName 'ContainerNetworkTools' -ParameterFilter { - $Path -eq "$TestDownloadPath" + Should -Invoke Invoke-ExecutableCommand -ModuleName 'ContainerNetworkTools' -ParameterFilter { + $executable -eq "tar.exe" -and + $arguments -eq "-xf `"$HOME\Downloads\$MockZipFileName`" -C `"TestDrive:\WinCNI\bin`"" } } diff --git a/Tests/ContainerdTools.Tests.ps1 b/Tests/ContainerdTools.Tests.ps1 index 7a23c0b..fd3893b 100644 --- a/Tests/ContainerdTools.Tests.ps1 +++ b/Tests/ContainerdTools.Tests.ps1 @@ -41,8 +41,11 @@ Describe "ContainerdTools.psm1" { Context "Install-Containerd" -Tag "Install-Containerd" { BeforeAll { + $Script:ContainerdRepo = 'https://github.com/containerd/containerd/releases/download' + $Script:TestDownloadPath = "$HOME\Downloads\containerd-1.0.0-windows-amd64.tar.gz" + Mock Get-ContainerdLatestVersion { return '1.0.0' } -ModuleName 'ContainerdTools' - Mock Get-InstallationFile -ModuleName 'ContainerdTools' + Mock Get-InstallationFile -ModuleName 'ContainerdTools' -MockWith { return $Script:TestDownloadPath } Mock Install-RequiredFeature -ModuleName 'ContainerdTools' Mock Uninstall-Containerd -ModuleName "ContainerdTools" Mock Register-ContainerdService -ModuleName 'ContainerdTools' @@ -51,11 +54,7 @@ Describe "ContainerdTools.psm1" { Mock Get-ChildItem -ModuleName 'ContainerdTools' Mock Test-EmptyDirectory -ModuleName 'ContainerdTools' -MockWith { return $true } Mock Install-Containerd -ModuleName 'ContainerdTools' - Mock Test-CheckSum -ModuleName 'ContainerdTools' -MockWith { return $true } Mock Remove-Item -ModuleName 'ContainerdTools' - - $Script:ContainerdRepo = 'https://github.com/containerd/containerd/releases/download' - $Script:TestDownloadPath = "$HOME\Downloads\containerd-1.0.0-windows-amd64.tar.gz" } It 'Should not process on implicit request for validation (WhatIfPreference)' { @@ -76,23 +75,17 @@ Describe "ContainerdTools.psm1" { Should -Invoke Uninstall-Containerd -ModuleName 'ContainerdTools' -Times 0 -Exactly -Scope It Should -Invoke Get-InstallationFile -ModuleName 'ContainerdTools' -ParameterFilter { - $Files -like @( - @{ - Feature = "Containerd" - Uri = "$Script:ContainerdRepo/v1.0.0/containerd-1.0.0-windows-amd64.tar.gz" - Version = '1.0.0' - DownloadPath = "$Script:TestDownloadPath" - } - ) - } - Should -Invoke Test-CheckSum -ModuleName 'ContainerdTools' -ParameterFilter { - $DownloadedFile -eq "$Script:TestDownloadPath" -and - $ChecksumUri -eq "$Script:ContainerdRepo/v1.0.0/containerd-1.0.0-windows-amd64.tar.gz.sha256sum" + ($fileParameters[0].Feature -eq "Containerd") -and + ($fileParameters[0].Repo -eq "containerd/containerd") -and + ($fileParameters[0].Version -eq 'latest') -and + ($fileParameters[0].DownloadPath -eq "$HOME\Downloads") -and + [string]::IsNullOrWhiteSpace($fileParameters.ChecksumSchemaFile) -and + ($fileParameters[0].FileFilterRegEx -eq "(?:^containerd-<__VERSION__>-windows-amd64.*.tar.gz(.*)?)$") } Should -Invoke Install-RequiredFeature -ModuleName 'ContainerdTools' -ParameterFilter { $Feature -eq "Containerd" -and $InstallPath -eq "$Env:ProgramFiles\Containerd" -and - $DownloadPath -eq "$Script:TestDownloadPath" -and + $SourceFile -eq "$Script:TestDownloadPath" -and $EnvPath -eq "$Env:ProgramFiles\Containerd\bin" -and $cleanup -eq $true } @@ -102,37 +95,23 @@ Describe "ContainerdTools.psm1" { } It "Should call function with user-specified values" { - Install-Containerd -Version '1.2.3' -InstallPath 'TestDrive:\Containerd' -DownloadPath 'TestDrive:\Downloads' -Force -Confirm:$false + Install-Containerd -Version '1.2.3' -InstallPath 'TestDrive:\Containerd' -DownloadPath 'TestDrive:\Downloads' -OSArchitecture "arm64" -Force -Confirm:$false Should -Invoke Uninstall-Containerd -ModuleName 'ContainerdTools' -Times 0 -Exactly -Scope It Should -Invoke Get-InstallationFile -ModuleName 'ContainerdTools' -ParameterFilter { - $Files -like @( - @{ - Feature = "Containerd" - Uri = "$Script:ContainerdRepo/v1.2.3/containerd-1.2.3-windows-amd64.tar.gz" - Version = '1.2.3' - DownloadPath = "$HOME\Downloads" - } - ) + $fileParameters[0].Version -eq '1.2.3' -and + $fileParameters[0].OSArchitecture -eq 'arm64' -and + $fileParameters[0].FileFilterRegEx -eq "(?:^containerd-<__VERSION__>-windows-arm64.*.tar.gz(.*)?)$" } Should -Invoke Install-RequiredFeature -ModuleName 'ContainerdTools' -ParameterFilter { $Feature -eq "Containerd" $InstallPath -eq 'TestDrive:\Containerd' -and - $DownloadPath -eq 'TestDrive:\Downloads\containerd-1.2.3-windows-amd64.tar.gz' + $SourceFile -eq 'TestDrive:\Downloads\containerd-1.2.3-windows-arm64.tar.gz' -and $EnvPath -eq 'TestDrive:\Containerd\bin' $cleanup -eq $true } } - It "should throw an error when checksum verification fails" { - Mock Test-CheckSum -ModuleName 'ContainerdTools' -MockWith { return $false } - - { Install-Containerd -Force -Confirm:$false } | Should -Throw "Checksum verification failed for $Script:TestDownloadPath" - Should -Invoke Remove-Item -ModuleName 'ContainerdTools' -ParameterFilter { - $Path -eq "$Script:TestDownloadPath" - } - } - It "Should setup Containerd service" { Install-Containerd -Setup -Force -Confirm:$false diff --git a/Tests/NerdctlTools.Tests.ps1 b/Tests/NerdctlTools.Tests.ps1 index 1ee4579..75e3f52 100644 --- a/Tests/NerdctlTools.Tests.ps1 +++ b/Tests/NerdctlTools.Tests.ps1 @@ -7,6 +7,8 @@ ########################################################################### +using module "..\containers-toolkit\Private\CommonToolUtilities.psm1" + Describe "NerdctlTools.psm1" { BeforeAll { $RootPath = Split-Path -Parent $PSScriptRoot @@ -32,9 +34,12 @@ Describe "NerdctlTools.psm1" { Context "Install-Nerdctl" -Tag "Install-Nerdctl" { BeforeAll { + $Script:nerdctlRepo = 'https://github.com/containerd/nerdctl/releases/download' + $Script:TestDownloadPath = "$HOME\Downloads\nerdctl-7.9.8-windows-amd64.tar.gz" + Mock Get-NerdctlLatestVersion { return '7.9.8' } -ModuleName 'NerdctlTools' Mock Uninstall-Nerdctl -ModuleName "NerdctlTools" - Mock Get-InstallationFile -ModuleName 'NerdctlTools' + Mock Get-InstallationFile -ModuleName 'NerdctlTools' -MockWith { return $Script:TestDownloadPath } Mock Install-RequiredFeature -ModuleName 'NerdctlTools' Mock Get-Command -ModuleName 'NerdctlTools' Mock Get-ChildItem -ModuleName 'NerdctlTools' @@ -43,11 +48,7 @@ Describe "NerdctlTools.psm1" { Mock Install-Buildkit -ModuleName 'NerdctlTools' Mock Install-WinCNIPlugin -ModuleName 'NerdctlTools' Mock Install-Nerdctl -ModuleName 'NerdctlTools' - Mock Test-CheckSum -ModuleName 'NerdctlTools' -MockWith { return $true } Mock Remove-Item -ModuleName 'NerdctlTools' - - $Script:nerdctlRepo = 'https://github.com/containerd/nerdctl/releases/download' - $Script:TestDownloadPath = "$HOME\Downloads\nerdctl-7.9.8-windows-amd64.tar.gz" } It 'Should not process on implicit request for validation (WhatIfPreference)' { @@ -68,25 +69,18 @@ Describe "NerdctlTools.psm1" { Should -Invoke Uninstall-Nerdctl -ModuleName 'NerdctlTools' -Times 0 -Exactly -Scope It Should -Invoke Get-InstallationFile -ModuleName 'NerdctlTools' -ParameterFilter { - $Files -like @( - @{ - Feature = "nerdctl" - Uri = "$Script:nerdctlRepo/v7.9.8/nerdctl-7.9.8-windows-amd64.tar.gz" - Version = '7.9.8' - DownloadPath = "$Script:TestDownloadPath" - } - ) - } - - Should -Invoke Test-CheckSum -ModuleName 'NerdctlTools' -Times 1 -ParameterFilter { - $DownloadedFile -eq "$Script:TestDownloadPath" -and - $ChecksumUri -eq "$Script:nerdctlRepo/v7.9.8/SHA256SUMS" + $fileParameters[0].Feature -eq "nerdctl" -and + $fileParameters[0].Repo -eq "containerd/nerdctl" -and + $fileParameters[0].Version -eq 'latest' -and + $fileParameters[0].DownloadPath -eq "$HOME\Downloads" + [string]::IsNullOrWhiteSpace($fileParameters.ChecksumSchemaFile) -and + $fileParameters[0].FileFilterRegEx -eq $null } Should -Invoke Install-RequiredFeature -ModuleName 'NerdctlTools' -ParameterFilter { $Feature -eq "nerdctl" -and $InstallPath -eq "$Env:ProgramFiles\nerdctl" -and - $DownloadPath -eq "$HOME\Downloads\nerdctl-7.9.8-windows-amd64.tar.gz" -and + $SourceFile -eq "$Script:TestDownloadPath" -and $EnvPath -eq "$Env:ProgramFiles\nerdctl" -and $cleanup -eq $true } @@ -97,23 +91,23 @@ Describe "NerdctlTools.psm1" { } It "Should call function with user-specified values" { - Install-Nerdctl -Version '1.2.3' -InstallPath 'TestDrive:\nerdctl' -DownloadPath 'TestDrive:\Downloads' -Dependencies 'containerd' -Force -Confirm:$false + # Mocks + $MockDownloadPath = 'TestDrive:\Downloads\nerdctl-1.2.3-windows-amd64.tar.gz' + Mock Get-InstallationFile -ModuleName 'NerdctlTools' -MockWith { return $MockDownloadPath } + # Test + Install-Nerdctl -Version '1.2.3' -InstallPath 'TestDrive:\nerdctl' -DownloadPath 'TestDrive:\Downloads' -Dependencies 'containerd' -OSArchitecture "arm64" -Force -Confirm:$false + + # Assertions Should -Invoke Uninstall-Nerdctl -ModuleName 'NerdctlTools' -Times 0 -Exactly -Scope It Should -Invoke Get-InstallationFile -ModuleName 'NerdctlTools' -ParameterFilter { - $Files -like @( - @{ - Feature = "nerdctl" - Uri = "$Script:nerdctlRepo/v1.2.3/nerdctl-1.2.3-windows-amd64.tar.gz" - Version = '1.2.3' - DownloadPath = "$HOME\Downloads" - } - ) + $fileParameters[0].Version -eq '1.2.3' + $fileParameters[0].OSArchitecture -eq 'arm64' } Should -Invoke Install-RequiredFeature -ModuleName 'NerdctlTools' -ParameterFilter { $Feature -eq "nerdctl" $InstallPath -eq 'TestDrive:\nerdctl' - $DownloadPath -eq 'TestDrive:\Downloads\nerdctl-1.2.3-windows-amd64.tar.gz' + $SourceFile -eq $MockDownloadPath $EnvPath -eq 'TestDrive:\nerdctl\bin' $cleanup -eq $true } @@ -123,15 +117,6 @@ Describe "NerdctlTools.psm1" { Should -Invoke Install-WinCNIPlugin -ModuleName 'NerdctlTools' -Times 0 -Exactly -Scope It } - It "should throw an error when checksum verification fails" { - Mock Test-CheckSum -ModuleName 'NerdctlTools' -MockWith { return $false } - - { Install-Nerdctl -Force -Confirm:$false } | Should -Throw "Checksum verification failed for $Script:TestDownloadPath" - Should -Invoke Remove-Item -ModuleName 'NerdctlTools' -ParameterFilter { - $Path -eq "$Script:TestDownloadPath" - } - } - It "Should uninstall tool if it is already installed" { Mock Test-EmptyDirectory -ModuleName 'NerdctlTools' -MockWith { return $false } diff --git a/Tests/TestData/release-assets.json b/Tests/TestData/release-assets.json new file mode 100644 index 0000000..99e92fb --- /dev/null +++ b/Tests/TestData/release-assets.json @@ -0,0 +1,93 @@ +{ + "tag_name": "v2.0.0-rc.1", + "name": "v2.0.0-rc.1", + "url": "https://api.github.com/repos/containerd/nerdctl/releases/170730383", + "created_at": "2024-08-17T20:42:04Z", + "published_at": "2024-08-17T20:57:32Z", + "assets": [ + { + "url": "https://api.github.com/repos/containerd/nerdctl/releases/assets/186602872", + "id": 186602872, + "node_id": "RA_kwDOEvuRnc4LH1V4", + "name": "nerdctl-2.0.0-rc.1-freebsd-arm64.tar.gz", + "label": "", + "content_type": "application/x-gtar", + "state": "uploaded", + "size": 8875928, + "download_count": 8, + "created_at": "2024-08-17T20:56:39Z", + "updated_at": "2024-08-17T20:56:40Z", + "browser_download_url": "https://github.com/containerd/nerdctl/releases/download/v2.0.0-rc.1/nerdctl-2.0.0-rc.1-freebsd-arm64.tar.gz" + }, + { + "url": "https://api.github.com/repos/containerd/nerdctl/releases/assets/186602868", + "id": 186602868, + "node_id": "RA_kwDOEvuRnc4LH1V0", + "name": "nerdctl-2.0.0-rc.1-linux-amd64.tar.gz", + "label": "", + "content_type": "application/x-gtar", + "state": "uploaded", + "size": 9722129, + "download_count": 242, + "created_at": "2024-08-17T20:56:39Z", + "updated_at": "2024-08-17T20:56:40Z", + "browser_download_url": "https://github.com/containerd/nerdctl/releases/download/v2.0.0-rc.1/nerdctl-2.0.0-rc.1-linux-amd64.tar.gz" + }, + { + "url": "https://api.github.com/repos/containerd/nerdctl/releases/assets/186602929", + "id": 186602929, + "node_id": "RA_kwDOEvuRnc4LH1Wx", + "name": "nerdctl-2.0.0-rc.1-windows-amd64.tar.gz", + "label": "", + "content_type": "application/x-gtar", + "state": "uploaded", + "size": 9189802, + "download_count": 47, + "created_at": "2024-08-17T20:56:45Z", + "updated_at": "2024-08-17T20:56:45Z", + "browser_download_url": "https://github.com/containerd/nerdctl/releases/download/v2.0.0-rc.1/nerdctl-2.0.0-rc.1-windows-amd64.tar.gz" + }, + { + "url": "https://api.github.com/repos/containerd/nerdctl/releases/assets/186602928", + "id": 186602928, + "node_id": "RA_kwDOEvuRnc4LH1Ww", + "name": "nerdctl-full-2.0.0-rc.1-linux-arm64.tar.gz", + "label": "", + "content_type": "application/x-gtar", + "state": "uploaded", + "size": 249734229, + "download_count": 152, + "created_at": "2024-08-17T20:56:45Z", + "updated_at": "2024-08-17T20:56:52Z", + "browser_download_url": "https://github.com/containerd/nerdctl/releases/download/v2.0.0-rc.1/nerdctl-full-2.0.0-rc.1-linux-arm64.tar.gz" + }, + { + "url": "https://api.github.com/repos/containerd/nerdctl/releases/assets/186602870", + "id": 186602870, + "node_id": "RA_kwDOEvuRnc4LH1V2", + "name": "SHA256SUMS", + "label": "", + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 1165, + "download_count": 15, + "created_at": "2024-08-17T20:56:39Z", + "updated_at": "2024-08-17T20:56:39Z", + "browser_download_url": "https://github.com/containerd/nerdctl/releases/download/v2.0.0-rc.1/SHA256SUMS" + }, + { + "url": "https://api.github.com/repos/containerd/nerdctl/releases/assets/186603171", + "id": 186603171, + "node_id": "RA_kwDOEvuRnc4LH1aj", + "name": "SHA256SUMS.asc", + "label": null, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 659, + "download_count": 3, + "created_at": "2024-08-17T20:57:14Z", + "updated_at": "2024-08-17T20:57:16Z", + "browser_download_url": "https://github.com/containerd/nerdctl/releases/download/v2.0.0-rc.1/SHA256SUMS.asc" + } + ] +} \ No newline at end of file diff --git a/Tests/TestData/release-tags.json b/Tests/TestData/release-tags.json new file mode 100644 index 0000000..079cc99 --- /dev/null +++ b/Tests/TestData/release-tags.json @@ -0,0 +1,32 @@ +[ + { + "name": "v1.7.3", + "zipball_url": "https://api.github.com/repos/containerd/nerdctl/zipball/refs/tags/v1.7.3", + "tarball_url": "https://api.github.com/repos/containerd/nerdctl/tarball/refs/tags/v1.7.3", + "commit": { + "sha": "qwert123456", + "url": "https://api.github.com/repos/containerd/nerdctl/commits/qwert123456" + }, + "node_id": "RANDOMNODEID123654==" + }, + { + "name": "v2.0.0-rc.1", + "zipball_url": "https://api.github.com/repos/containerd/nerdctl/zipball/refs/tags/v2.0.0-rc.1", + "tarball_url": "https://api.github.com/repos/containerd/nerdctl/tarball/refs/tags/v2.0.0-rc.1", + "commit": { + "sha": "yt234567yut3wqw64", + "url": "https://api.github.com/repos/containerd/nerdctl/commits/yt234567yut3wqw64" + }, + "node_id": "RANDOMNODEID087457==" + }, + { + "name": "v1.7.0", + "zipball_url": "https://api.github.com/repos/containerd/nerdctl/zipball/refs/tags/v1.7.0", + "tarball_url": "https://api.github.com/repos/containerd/nerdctl/tarball/refs/tags/v1.7.0", + "commit": { + "sha": "87re0w9er879", + "url": "https://api.github.com/repos/containerd/nerdctl/commits/87re0w9er879" + }, + "node_id": "RANDOMNODEID03722392==" + } +] \ No newline at end of file diff --git a/build/scripts/run-tests.ps1 b/build/scripts/run-tests.ps1 index 52be1e3..fe503b7 100644 --- a/build/scripts/run-tests.ps1 +++ b/build/scripts/run-tests.ps1 @@ -163,4 +163,4 @@ Invoke-Pester -Configuration $config ###################################################### ###################### CLEANUP ####################### ###################################################### -Remove-Item -Path Env:\Pester -Force +Get-Item -Path Env:\Pester -ErrorAction SilentlyContinue | Remove-Item -Force diff --git a/containers-toolkit/Private/CommonToolUtilities.psm1 b/containers-toolkit/Private/CommonToolUtilities.psm1 index 820b1d4..3f7662f 100644 --- a/containers-toolkit/Private/CommonToolUtilities.psm1 +++ b/containers-toolkit/Private/CommonToolUtilities.psm1 @@ -29,6 +29,37 @@ class FileDigest { } } +class FileDownloadParameters { + [ValidateSet("Containerd", "Buildkit", "nerdctl", "WinCNIPlugin")] + [string]$Feature + [string]$Repo + [string]$Version = "latest" + [ValidateSet("386", "amd64", "arm64", "arm")] + [string]$OSArchitecture = "$env:PROCESSOR_ARCHITECTURE" + [string]$DownloadPath = "$HOME\Downloads" + [string]$ChecksumSchemaFile + [string]$FileFilterRegEx + + FileDownloadParameters( + [string]$feature, + [string]$repo, + [string]$version = "latest", + [string]$arch = "$env:PROCESSOR_ARCHITECTURE", + [string]$downloadPath = "$HOME\Downloads", + [string]$checksumSchemaFile = $null, + [string]$fileFilterRegEx = $null + ) { + $this.Feature = $feature + $this.Repo = $repo + $this.Version = $version + $this.OSArchitecture = $arch + $this.DownloadPath = $downloadPath + $this.ChecksumSchemaFile = $checksumSchemaFile + $this.FileFilterRegEx = $fileFilterRegEx + } +} + + Add-Type @' public enum ActionConsent { Yes = 0, @@ -36,7 +67,11 @@ public enum ActionConsent { } '@ -$VALID_HASH_FUNCTIONS = @("SHA1", "SHA256", "SHA384", "SHA512", "MD5") +$HASH_FUNCTIONS = @("SHA1", "SHA256", "SHA384", "SHA512", "MD5") +$HASH_FUNCTIONS_STR = $HASH_FUNCTIONS -join '|' # SHA1|SHA256|SHA384|SHA512|MD5 +$NERDCTL_CHECKSUM_FILE_PATTERN = "(?(?:^({0})))" -f ($HASH_FUNCTIONS -join '|') +$NERDCTL_FILTER_SCRIPTBLOCK_STR = { (("{0}" -match "$NERDCTL_CHECKSUM_FILE_PATTERN") -and "{0}" -notmatch ".*.asc$") }.ToString() + function Get-LatestToolVersion($repository) { @@ -67,59 +102,303 @@ function Test-EmptyDirectory($path) { return ($itemCount.Count -eq 0) } +function Get-ReleaseAssets { + [OutputType([PSCustomObject])] + param ( + [string]$repo, # containers repo-owner/$repo-name + [string]$version, + [string]$OSArch + ) + + function Invoke-GitHubApi { + param($uri) + try { + Write-Debug "Invoking GitHub API. URI: $uri" + $response = Invoke-RestMethod -Uri "$uri" -Headers @{ "User-Agent" = "PowerShell" } + return $response + } + catch { + Throw "GitHub API error. URL: `"$uri`". Error: $($_.Exception.Message)" + } + } + + Write-Debug "Getting release assets:`n`trepo: $repo`n`trelease version: $version`n`trelease architecture: $OSArch " + $baseApiUrl = "https://api.github.com/repos/$repo" + if ($version -eq "latest") { + $apiUrl = "$baseApiUrl/releases/latest" + } + else { + # We use this method to get the release assets for a specific version + # because creating a string for the version tag is not always consistent + # e.g. "v1.0.0" vs "1.0.0" + # The q parameter used for searching is not available in the GitHub API's /tags endpoint. + # GitHub's API for listing tags (/repos/{owner}/{repo}/tags) does not support querying or filtering + # directly through a q parameter like some other endpoints might (e.g., the search API). + + # Get all releases tags + $response = Invoke-GitHubApi -Uri "$baseApiUrl/tags" + $releaseTag = $response | Where-Object { ($_.name.TrimStart("v")) -eq ($version.TrimStart("v")) } + + if (-not $releaseTag) { + Throw "Couldn't find release tags for the provided version: '$version'" + } + + if ($releaseTag.Count -gt 1) { + Write-Warning "Found multiple release tags for the provided version: '$version'. Using the first tag." + } + + $releaseTagName = $releaseTag | Select-Object -First 1 -ExpandProperty name + + # Get the release with the specified tag + Write-Debug "Release tag: $releaseTagName" + $apiUrl = "$baseApiUrl/releases/tags/$releaseTagName" + } + $response = Invoke-GitHubApi -Uri "$apiUrl" + + # Filter list of assets by architecture and file name + $releaseAssets = $response | Select-Object -Property name, url, created_at, published_at, ` + @{ l = "version"; e = { $_.tag_name } }, ` + @{ l = "assets_url"; e = { $_.assets[0].url } }, ` + @{ l = "release_assets"; e = { + $_.assets | + Where-Object { + ( + # Filter assets by OS (windows) and architecture + # In the "zip|tar.gz" regex, we do not add the "$" at the end to allow for checksum files to be included + # The checksum files end with eg: ".tar.gz.sha256sum" + ($_.name -match "(windows(.+)$OSArch)") -or + + # nerdctl checksum files are named "SHA256SUMS". + (& ([ScriptBlock]::Create($NERDCTL_FILTER_SCRIPTBLOCK_STR -f $_.name))) + ) + } | + ForEach-Object { + Write-Debug ("Asset name: {0}" -f $_.Name) + [PSCustomObject]@{ + "asset_name" = $_.name + "asset_download_url" = $_.browser_download_url + "asset_size" = $_.size + } + } + } + } + + # Check if any release assets were found for the specified architecture + $archReleaseAssets = $releaseAssets.release_assets | Where-Object { ($_.asset_name -match "windows(.+)$OSArch") } + if ($archReleaseAssets.Count -eq 0) { + Throw "Couldn't find release assets for the provided architecture: '$OSArch'" + } + + if ($archReleaseAssets.Count -lt 2) { + Write-Warning "Some assets may be missing for the release. Expected at least 2 assets, found $($archReleaseAssets.Count)." + } + + # Return the assets for the release. Includes the archive file for the binaries and the checksum files. + return $releaseAssets +} + function Get-InstallationFile { + [OutputType([string[]])] param( - [parameter(Mandatory, HelpMessage = "Files to download")] - [PSCustomObject[]] $Files + [parameter(Mandatory, HelpMessage = "Information (parameters) of the file to download")] + [PSCustomObject] $fileParameters ) begin { - $functions = { - function Receive-File ($feature) { - Write-Information -InformationAction Continue -MessageData "Downloading $($feature.Feature) version v$($feature.Version)" + function Receive-File { + param($params) + try { + Invoke-WebRequest -Uri $params.Uri -OutFile $params.DownloadPath -UseBasicParsing -MaximumRetryCount 3 -RetryIntervalSec 60 + } + catch { + Throw "Couldn't download `"$($params.Feature)`" release assets. `"$($params.Uri)`".`n$($_.Exception.Message)" + } + } + + function DownloadAssets { + param( + [string]$featureName, + [string]$version, + [string]$downloadPath = "$HOME\Downloads", + [PSCustomObject]$releaseAssets, + [PSCustomObject]$file + ) + + $archiveFile = $checksumFile = $null + foreach ($asset in $releaseAssets.PSObject.Properties) { + $key = $asset.Name + $asset_params = $asset.Value + + $destPath = Join-Path -Path $downloadPath -ChildPath $asset_params.asset_name + switch ($key) { + "ArchiveFileAsset" { $archiveFile = $destPath } + "ChecksumFileAsset" { $checksumFile = $destPath } + default { Throw "Invalid input: $key" } + } + + $downloadParams = [PSCustomObject]@{ + Feature = $featureName + Version = $version + Uri = $asset_params.asset_download_url + DownloadPath = $destPath + } + + Write-Debug "Downloading asset $($asset_params.asset_name)...`n`tVersion: $version`n`tURI: $($downloadParams.Uri)`n`tDestination path: $($downloadParams.DownloadPath)" try { - Invoke-WebRequest -Uri $feature.Uri -OutFile $feature.DownloadPath -UseBasicParsing + Receive-File -Params $downloadParams } catch { - Throw "$($feature.feature) downlooad failed: $($feature.uri).`n$($_.Exception.Message)" + Write-Error "Failed to download $($downloadParams.Feature) release assets. $($_.Exception.Message)" + Throw $_ # Re-throw the exception to halt execution if a download fails + } + } + + # Verify that both the archive and checksum files were downloaded + if (-not (Test-Path $archiveFile -ErrorAction SilentlyContinue)) { + Throw "Archive file not found in the release assets: `'$archiveFile`"" + } + if (-not (Test-Path $checksumFile -ErrorAction SilentlyContinue)) { + Throw "Checksum file not found in the release assets: `'$checksumFile`"" + } + + # Verify checksum + try { + $isValidChecksum = if ([System.IO.Path]::GetExtension($checksumFile) -eq ".json") { + Test-Checksum -JSON -DownloadedFile $archiveFile -ChecksumFile $checksumFile -SchemaFile $file.ChecksumSchemaFile + } + else { + Test-Checksum -DownloadedFile $archiveFile -ChecksumFile $checksumFile + } + } + catch { + Write-Error "Checksum verification process failed: $($_.Exception.Message)" + Throw $_ # Re-throw the exception if checksum verification fails + } + + # Remove the checksum file after verification + if (Test-Path -Path $checksumFile -ErrorAction SilentlyContinue) { + Remove-Item -Path $checksumFile -Force -ErrorAction SilentlyContinue + } + + if (-not $isValidChecksum) { + Write-Error "Checksum verification failed for $archiveFile. The file will be deleted." + + # Remove the checksum file after verification + if (Test-Path -Path $archiveFile -ErrorAction SilentlyContinue) { + Remove-Item -Path $archiveFile -Force -ErrorAction SilentlyContinue } + Throw "Checksum verification failed. One or more files are corrupted." } + + return $archiveFile } - . $functions - $jobs = @() } process { - # Download file from repo - if ($Files.Length -eq 1) { - Receive-File -feature $Files[0] + # Fetch the release assets based on the provided parameters + $releaseAssets = Get-ReleaseAssets -repo $fileParameters.Repo -version $fileParameters.Version -OSArch $fileParameters.OSArchitecture + + # Filter file names based on the provided regex or default logic + if ([string]::IsNullOrWhiteSpace($fileParameters.FileFilterRegEx)) { + # Default logic to filter the archive and checksum files + $filteredAssets = $releaseAssets.release_assets | Where-Object { + # In the "zip|tar.gz" regex, we do not add the "$" at the end to allow for checksum files to be included + # The checksum files end with eg: ".sha256sum" + ($_.asset_name -match ".*(.zip|.tar.gz)") -or + + # Buildkit checksum files are named ending with ".provenance.json" or ".sbom.json" + # We only need the ".sbom.json" file + ($_.asset_name -match ".*sbom.json$") -or + + # nerdctl checksum files are named "SHA256SUMS". Check file names that have such a format. + (& ([ScriptBlock]::Create($NERDCTL_FILTER_SCRIPTBLOCK_STR -f $_.asset_name))) + } } else { - # Import ThreadJob module if not available - if (!(Get-Module -ListAvailable -Name ThreadJob)) { - Write-Information -InformationAction Continue -MessageData "Installing module ThreadJob from PowerShell Gallery." - Install-Module -Name ThreadJob -Scope CurrentUser -Force + # Use the provided regex to filter the archive and checksum files + $fileFilterRegEx = $fileParameters.FileFilterRegEx -replace "<__VERSION__>", "v?$($releaseAssets.version.TrimStart('v'))" + Write-Debug "File filter: `"$fileFilterRegEx`"" + $filteredAssets = $releaseAssets.release_assets | Where-Object { $_.asset_name -match $fileFilterRegEx } + } + + # Pair archive and checksum files + $assetsToDownload = @() + $archiveExtensionStr = @(".zip", ".tar.gz", ".tgz") -join "|" + $failedDownloads = @() + foreach ($asset in $filteredAssets) { + if ($asset.asset_name -notmatch "(?$archiveExtensionStr)$") { + continue } - Import-Module -Name ThreadJob -Force - # Download files asynchronously - Write-Information -InformationAction Continue -MessageData "Downloading $($Files.Length) container tools executables. This may take a few minutes." + $fileExtension = $matches.extension + + # Remove the trailing archive file extension to get the checksum file name + $assetFileName = $asset.asset_name -replace "$fileExtension", "" + + # Find the checksum file that matches the archive + $checksumAsset = $filteredAssets | Where-Object { + ($_.asset_name -match "(?:(^$assetFileName).*($HASH_FUNCTIONS_STR))") -or - # Create multiple thread jobs to download multiple files at the same time. - foreach ($file in $files) { - $jobs += Start-ThreadJob -Name $file.DownloadPath -InitializationScript $functions -ScriptBlock { Receive-File -Feature $using:file } + # Buildkit checksum is in .sbom.json + ($_.asset_name -match ".sbom.json$") -or + + (& ([ScriptBlock]::Create($NERDCTL_FILTER_SCRIPTBLOCK_STR -f $_.asset_name))) } - Wait-Job -Job $jobs | Out-Null + if (-not $checksumAsset) { + Write-Error "Checksum file for $assetFileName not found. Skipping download." + $failedDownloads += $asset.asset_name + continue + } - foreach ($job in $jobs) { - Receive-Job -Job $job | Out-Null + $assetsToDownload += @{ + FeatureName = $releaseAssets.name + Version = $releaseAssets.version + DownloadPath = $fileParameters.DownloadPath + File = $fileParameters + ReleaseAssets = [PSCustomObject]@{ + ArchiveFileAsset = $asset + ChecksumFileAsset = $checksumAsset + } } } + + if ($failedDownloads) { + $errorMsg = "Failed to find checksum files for $($failedDownloads -join ', ')." + } + + # Download the archive and verify checksum + $archiveFiles = $failedDownloads = @() + Write-Debug "Assets to download count: $($assetsToDownload.ReleaseAssets.Count)" + foreach ($asset in $assetsToDownload) { + try { + Write-Debug "Downloading $($asset.FeatureName) assets..." + $archiveFile = DownloadAssets @asset + + Write-Debug "Downloaded archive file: `"$archiveFile`"" + $archiveFiles += $archiveFile + } + catch { + Write-Error "Failed to download assets for `"$($asset.FeatureName)`". $_" + $failedDownloads += $asset.FeatureName + } + } + + if ($errorMsg) { + Throw "Some files were not downloaded. $errorMsg" + } + + if ($failedDownloads) { + Throw "Failed to download assets for $($failedDownloads -join ', '). See logs for detailed error information." + } + + # Return the archive file path. May be multiple files. + # eg. containerd may contain containerd, cri-containerd, and cri-containerd-cni + return $archiveFiles } end { - $jobs | Remove-Job -Force -ErrorAction SilentlyContinue + Write-Information "File download and verification process completed." } } @@ -133,7 +412,7 @@ function Test-CheckSum { [Parameter(Mandatory, ParameterSetName = 'Default')] [Parameter(Mandatory, ParameterSetName = 'JSON')] [ValidateNotNullOrEmpty()] - [string] $ChecksumUri, + [string] $ChecksumFile, [Parameter(Mandatory, ParameterSetName = 'JSON')] [switch]$JSON, @@ -149,11 +428,14 @@ function Test-CheckSum { [System.Array]$ExtractDigestArguments ) - Write-Debug "Checksum verification for $downloadedFile" - Write-Debug "Checksum URI: $ChecksumUri" + Write-Debug "Checksum verification...`n`tSource file: $DownloadedFile`n`tChecksum file: $ChecksumFile" - if (-not (Test-Path -Path $downloadedFile)) { - Throw "Downloaded file not found: $downloadedFile" + if (-not (Test-Path -Path $downloadedFile -ErrorAction Continue)) { + Throw "Couldn't find source file: `"$downloadedFile`"." + } + + if (-not (Test-Path -Path $ChecksumFile -ErrorAction Continue)) { + Throw "Couldn't find checksum file: `"$ChecksumFile`"." } if ($JSON) { @@ -162,7 +444,7 @@ function Test-CheckSum { return ( Test-JSONChecksum ` -DownloadedFile $downloadedFile ` - -ChecksumUri $ChecksumUri ` + -ChecksumFile $ChecksumFile ` -SchemaFile $SchemaFile ` -ExtractDigestScriptBlock $ExtractDigestScriptBlock ` -ExtractDigestArguments $ExtractDigestArguments @@ -170,7 +452,7 @@ function Test-CheckSum { } Write-Debug "Checksum file format: Text" - return Test-FileChecksum -DownloadedFile $DownloadedFile -ChecksumUri $ChecksumUri + return Test-FileChecksum -DownloadedFile $DownloadedFile -ChecksumFile $ChecksumFile } function Test-FileChecksum { @@ -184,25 +466,20 @@ function Test-FileChecksum { [string] $DownloadedFile, [Parameter(Mandatory)] - [string] $ChecksumUri + [string] $checksumFile ) - # Download checksum file - $downloadDirPath = Split-Path -Parent $DownloadedFile - $checksumFile = DownloadCheckSumFile -DownloadPath $downloadDirPath -ChecksumUri $ChecksumUri - - Write-Debug "Checksum file: $checksumFile" - # Get download file name from downloaded file $downloadedFileName = Split-Path -Leaf $DownloadedFile $isValid = $false try { # Extract algorithm from checksum file name - if ($checksumFile -notmatch "($($VALID_HASH_FUNCTIONS -join "|"))") { - Throw "Invalid hash function. Supported hash functions: $($VALID_HASH_FUNCTIONS -join ', ')" + if ($checksumFile -notmatch "(?$HASH_FUNCTIONS_STR)") { + Throw "Invalid hash function. Supported hash functions: $($HASH_FUNCTIONS -join ', ')" } - $algorithm = $matches[1] + $algorithm = $matches.hashfunction + Write-Debug "Algorithm: $algorithm" $downloadedChecksum = Get-FileHash -Path $DownloadedFile -Algorithm $algorithm # checksum is stored in a file named SHA256SUMS @@ -237,7 +514,9 @@ function Test-FileChecksum { finally { # Delete checksum file Write-Debug "Deleting checksum file $checksumFile" - Remove-Item -Path $checksumFile -Force + if (Test-Path -Path $checksumFile) { + Remove-Item -Path $checksumFile -Force -ErrorAction Ignore + } } Write-Debug "Checksum verification status. {success: $isValid}" @@ -251,7 +530,7 @@ function Test-JSONChecksum { [string] $DownloadedFile, [parameter(Mandatory)] - [string] $ChecksumUri, + [string] $checksumFile, [Parameter(Mandatory)] [string] $SchemaFile, @@ -263,10 +542,6 @@ function Test-JSONChecksum { [System.Array]$ExtractDigestArguments ) - # Download checksum file - $downloadDirPath = Split-Path -Parent $DownloadedFile - $checksumFile = DownloadCheckSumFile -DownloadPath $downloadDirPath -ChecksumUri $ChecksumUri - # Validate the checksum file $isJsonValid = ValidateJSONChecksumFile -ChecksumFilePath $checksumFile -SchemaFile $SchemaFile Write-Debug "Checksum JSON file validation status. {success: $isJsonValid}" @@ -296,8 +571,8 @@ function Test-JSONChecksum { $digest = $extractedFileDigest[0].Digest # Validate the hash function - if ($VALID_HASH_FUNCTIONS -notcontains $algorithm) { - Throw "Invalid hash function, `"$algorithm`". Supported algorithms are: $($VALID_HASH_FUNCTIONS -join ', ')" + if ($HASH_FUNCTIONS -notcontains $algorithm) { + Throw "Invalid hash function, `"$algorithm`". Supported algorithms are: $($HASH_FUNCTIONS -join ', ')" } # Validate the checksum @@ -310,39 +585,15 @@ function Test-JSONChecksum { finally { # Delete checksum checksumFile Write-Debug "Deleting checksum file $checksumFile" - Remove-Item -Path $checksumFile -Force + if (Test-Path -Path $checksumFile) { + Remove-Item -Path $checksumFile -Force -ErrorAction Ignore + } } Write-Debug "Checksum verification status. {success: $isValid}" return $isValid } -function DownloadCheckSumFile { - [OutputType([string])] - param( - [parameter(Mandatory = $true, HelpMessage = "Downloaded file path")] - [String]$DownloadPath, - - [parameter(Mandatory = $true, HelpMessage = "Checksum URI")] - [String]$ChecksumUri - ) - - # Get checksum file name - $OutFile = Join-Path -Path $DownloadPath -ChildPath ($checksumUri -split '/' | Select-Object -Last 1) - - # Download checksum file - Write-Debug "Downloading checksum file from $checksumUri" - try { - Invoke-WebRequest -Uri $checksumUri -OutFile $OutFile -UseBasicParsing - } - catch { - Throw "Checksum file download failed: $checksumUri.`n$($_.Exception.Message)" - } - - Write-Debug "Checksum file downloaded to `"$OutFile`"" - return $OutFile -} - function ValidateJSONChecksumFile { param( [parameter(Mandatory = $true, HelpMessage = "Downloaded checksum file path")] @@ -351,11 +602,11 @@ function ValidateJSONChecksumFile { [String]$SchemaFile ) - Write-Debug "Validating JSON checksum file...`n`tChecksum file path:$ChecksumFilePath`n`tSchema file: $SchemaFile" + Write-Debug "Validating JSON checksum file...`n`tChecksum file path: $ChecksumFilePath`n`tSchema file: $SchemaFile" # Check if the schema file exists if (-not (Test-Path -Path $SchemaFile)) { - Throw "Couldn't find the provided schema file: $SchemaFile" + Throw "Couldn't find the JSON schema file: `"$SchemaFile`"." } $schemaFileContent = Get-Content -Path $SchemaFile -Raw @@ -365,8 +616,8 @@ function ValidateJSONChecksumFile { # Test JSON checksum file is valid try { - $isValidJSON = Test-Json -Json (Get-Content -Path $checksumFilePath -Raw) -Schema (Get-Content -Path $schemafile -Raw) - return $isValidJSON + $isValidJSON = Test-Json -Json "$(Get-Content -Path $ChecksumFilePath -Raw)" -Schema "$schemaFileContent" + return $isValidJSON } catch { Throw "Invalid JSON format in checksum file. $_" @@ -437,7 +688,7 @@ function Install-RequiredFeature { param( [string] $Feature, [string] $InstallPath, - [string] $DownloadPath, + [string[]] $SourceFile, [string] $EnvPath, [boolean] $cleanup ) @@ -448,9 +699,22 @@ function Install-RequiredFeature { } # Untar file - $output = Invoke-ExecutableCommand -Executable 'tar.exe' -Arguments "-xf `"$DownloadPath`" -C `"$InstallPath`"" - if ($output.ExitCode -ne 0) { - Throw "Could not untar file $DownloadPath at $InstallPath. $($output.StandardError.ReadToEnd())" + $failed = @() + foreach ($file in $SourceFile) { + if (-not (Test-Path -Path $file)) { + Throw "Couldn't find source file: `"$file`"." + } + + Write-Debug "Expand archive:`n`tSource file: $file`n`tDestination Path: $InstallPath" + $cmdOutput = Invoke-ExecutableCommand -executable "tar.exe" -arguments "-xf `"$file`" -C `"$InstallPath`"" -timeout (60 * 2) + if ($cmdOutput.ExitCode -ne 0) { + Write-Error "Failed to expand archive `"$file`" at `"$InstallPath`". Exit code: $($cmdOutput.ExitCode). $($cmdOutput.StandardError.ReadToEnd())" + $failed += $file + } + } + + if ($failed) { + Throw "Couldn't expand archive file(s) $($failed -join ','). See logs for detailed error information." } # Add to env path @@ -459,7 +723,9 @@ function Install-RequiredFeature { # Clean up if ($CleanUp) { Write-Output "Cleanup to remove downloaded files" - Remove-Item $downloadPath -Force -ErrorAction Ignore + if (Test-Path -Path $SourceFile) { + Remove-Item -Path $SourceFile -Force -ErrorAction Ignore + } } } @@ -572,6 +838,7 @@ function Uninstall-ProgramFiles($path) { } function Invoke-ExecutableCommand { + [OutputType([System.Diagnostics.Process])] param ( [parameter(Mandatory)] [String] $executable, @@ -606,7 +873,6 @@ function Invoke-ExecutableCommand { } -Export-ModuleMember -Variable VALID_HASH_FUNCTIONS Export-ModuleMember -Function Get-LatestToolVersion Export-ModuleMember -Function Get-DefaultInstallPath Export-ModuleMember -Function Test-EmptyDirectory diff --git a/containers-toolkit/Public/AllToolsUtilities.psm1 b/containers-toolkit/Public/AllToolsUtilities.psm1 index 1764f72..bc52fb7 100644 --- a/containers-toolkit/Public/AllToolsUtilities.psm1 +++ b/containers-toolkit/Public/AllToolsUtilities.psm1 @@ -1,4 +1,4 @@ -########################################################################### +########################################################################### # # # Copyright (c) Microsoft Corporation. All rights reserved. # # # @@ -6,6 +6,7 @@ # # ########################################################################### + $ModuleParentPath = Split-Path -Parent $PSScriptRoot Import-Module -Name "$ModuleParentPath\Private\CommonToolUtilities.psm1" -Force @@ -38,17 +39,17 @@ function Install-ContainerTools { [string] [ValidateNotNullOrEmpty()] [parameter(HelpMessage = "ContainerD version to use")] - $ContainerDVersion = (Get-ContainerdLatestVersion), + $ContainerDVersion = "latest", [string] [ValidateNotNullOrEmpty()] [parameter(HelpMessage = "Buildkit version to use")] - $BuildKitVersion = (Get-BuildkitLatestVersion), + $BuildKitVersion = "latest", [string] [ValidateNotNullOrEmpty()] [parameter(HelpMessage = "nerdctl version to use")] - $NerdCTLVersion = (Get-NerdctlLatestVersion), + $NerdCTLVersion = "latest", [String] [parameter(HelpMessage = "Path to Install files. Defaults to Program Files")] diff --git a/containers-toolkit/Public/BuildkitTools.psm1 b/containers-toolkit/Public/BuildkitTools.psm1 index 18700f5..cc98998 100644 --- a/containers-toolkit/Public/BuildkitTools.psm1 +++ b/containers-toolkit/Public/BuildkitTools.psm1 @@ -6,6 +6,7 @@ # # ########################################################################### + using module "..\Private\CommonToolUtilities.psm1" $ModuleParentPath = Split-Path -Parent $PSScriptRoot @@ -25,8 +26,8 @@ function Install-Buildkit { param ( [Parameter(ParameterSetName = 'Install')] [Parameter(ParameterSetName = 'Setup')] - [parameter(HelpMessage = "Buildkit version to use. Defaults to latest version")] - [string]$Version, + [parameter(HelpMessage = "Buildkit version to use. Defaults to 'latest'")] + [string]$Version = "latest", [Parameter(ParameterSetName = 'Install')] [Parameter(ParameterSetName = 'Setup')] @@ -44,6 +45,10 @@ function Install-Buildkit { [Parameter(ParameterSetName = 'Setup')] [string]$WinCNIPath, + [Parameter(HelpMessage = 'OS architecture to download files for. Default is $env:PROCESSOR_ARCHITECTURE')] + [ValidateSet('amd64', '386', "arm", "arm64")] + [string]$OSArchitecture = $env:PROCESSOR_ARCHITECTURE, + [Parameter(ParameterSetName = 'Install')] [Parameter(ParameterSetName = 'Setup')] [parameter(HelpMessage = "Installs Buildkit even if the tool already exists at the specified path")] @@ -88,48 +93,34 @@ function Install-Buildkit { Write-Output "Downloading and installing Buildkit v$Version at $InstallPath" - # Download file from repo - $buildkitTarFile = "buildkit-v${Version}.windows-amd64.tar.gz" - $DownloadPath = "$DownloadPath\$buildkitTarFile" - # Download files - $Uri = "https://github.com/moby/buildkit/releases/download/v${Version}/$($BuildKitTarFile)" - $DownloadParams = @( - @{ - Feature = "Buildkit" - Uri = $Uri - Version = $version - DownloadPath = $DownloadPath - } - ) - Get-InstallationFile -Files $DownloadParams - - # Verify downloaded file checksum - # Buildkit checksum digest is stored in the .provenance.json or sbom.json file - # that uses in-toto schema: https://github.com/in-toto/attestation/tree/v0.1.0/spec#statement - Write-OutPut "Verifying checksum for $DownloadPath" - $isValidChecksum = Test-Checksum -JSON ` - -DownloadedFile $downloadPath ` - -ChecksumUri ($uri -replace ".tar.gz", ".sbom.json")` - -SchemaFile "$ModuleParentPath\Private\schemas\in-toto.sbom.schema.json" - if (-not $isValidChecksum) { - $errMsg = "Checksum verification failed for $DownloadPath" - Write-Error $errMsg - - # Clean up downloaded file - Write-Warning "Removing downloaded file $DownloadPath" - Remove-Item -Path $DownloadPath -Force - - Throw $errMsg + $downloadParams = @{ + ToolName = "Buildkit" + Repository = "moby/buildkit" + Version = $Version + OSArchitecture = $OSArchitecture + DownloadPath = $DownloadPath + ChecksumSchemaFile = "$ModuleParentPath\Private\schemas\in-toto.sbom.schema.json" + FileFilterRegEx = $null } + $downloadParamsProperties = [FileDownloadParameters]::new( + $downloadParams.ToolName, + $downloadParams.Repository, + $downloadParams.Version, + $downloadParams.OSArchitecture, + $downloadParams.DownloadPath, + $downloadParams.ChecksumSchemaFile, + $downloadParams.FileFilterRegEx + ) + $sourceFile = Get-InstallationFile -FileParameters $downloadParamsProperties # Untar downloaded file at install path $params = @{ - Feature = "Buildkit" - InstallPath = $InstallPath - DownloadPath = "$DownloadPath" - EnvPath = "$InstallPath\bin" - cleanup = $true + Feature = "Buildkit" + InstallPath = $InstallPath + SourceFile = "$sourceFile" + EnvPath = "$InstallPath\bin" + cleanup = $true } Install-RequiredFeature @params diff --git a/containers-toolkit/Public/ContainerNetworkTools.psm1 b/containers-toolkit/Public/ContainerNetworkTools.psm1 index 14fe941..80adcb4 100644 --- a/containers-toolkit/Public/ContainerNetworkTools.psm1 +++ b/containers-toolkit/Public/ContainerNetworkTools.psm1 @@ -7,6 +7,8 @@ ########################################################################### +using module "..\Private\CommonToolUtilities.psm1" + $ModuleParentPath = Split-Path -Parent $PSScriptRoot Import-Module -Name "$ModuleParentPath\Private\CommonToolUtilities.psm1" -Force @@ -21,12 +23,20 @@ function Install-WinCNIPlugin { ConfirmImpact = 'High' )] param( - [parameter(HelpMessage = "Windows CNI plugin version to use. Defaults to latest version")] - [string]$WinCNIVersion, + [parameter(HelpMessage = "Windows CNI plugin version to use. Defaults to 'latest'")] + [string]$WinCNIVersion = "latest", [parameter(HelpMessage = "Path to cni folder ~\cni . Not ~\cni\bin")] [String]$WinCNIPath, + [parameter(HelpMessage = "Source of the Windows CNI plugins. Defaults to 'microsoft/windows-container-networking'")] + [ValidateSet("microsoft/windows-container-networking", "containernetworking/plugins")] + [string]$SourceRepo = "microsoft/windows-container-networking", + + [Parameter(HelpMessage = 'OS architecture to download files for. Default is $env:PROCESSOR_ARCHITECTURE')] + [ValidateSet('amd64', '386', "arm", "arm64")] + [string]$OSArchitecture = $env:PROCESSOR_ARCHITECTURE, + [Switch] [parameter(HelpMessage = "Installs Windows CNI plugins even if the tool already exists at the specified path")] $Force @@ -80,40 +90,60 @@ function Install-WinCNIPlugin { # instead of https://github.com/containernetworking/plugins/releases. # The latter causes an error in nerdctl: "networking setup error has occurred. incompatible CNI versions" + Write-Debug ("Downloading Windows CNI plugins from {0}" -f $SourceRepo) + + # File filter for Windows CNI plugins + $fileFilterRegEx = $null + + if ($SourceRepo -eq "containernetworking/plugins") { + # File filter for containernetworking/plugins + # Contains files with .tgz extension and the checksum files with .SHA512 and .SHA256 extensions + # We use .SHA512 files to verify the integrity of the downloaded files + $fileFilterRegEx = ".*tgz(.SHA512)?$" + } + # Download file from repo - $cniZipFile = "windows-container-networking-cni-amd64-v${WinCNIVersion}.zip" - $DownloadPath = "$HOME\Downloads\$cniZipFile" - $Uri = "https://github.com/microsoft/windows-container-networking/releases/download/v$WinCNIVersion/$cniZipFile" - $DownloadParams = @( - @{ - Feature = "WinCNIPlugin" - Uri = $Uri - Version = $WinCNIVersion - DownloadPath = $DownloadPath - } + $downloadParams = @{ + ToolName = "WinCNIPlugin" + Repository = $SourceRepo + Version = $WinCNIVersion + OSArchitecture = $OSArchitecture + DownloadPath = "$HOME\Downloads\" + ChecksumSchemaFile = $null + FileFilterRegEx = $fileFilterRegEx + } + $downloadParamsProperties = [FileDownloadParameters]::new( + $downloadParams.ToolName, + $downloadParams.Repository, + $downloadParams.Version, + $downloadParams.OSArchitecture, + $downloadParams.DownloadPath, + $downloadParams.ChecksumSchemaFile, + $downloadParams.FileFilterRegEx ) - Get-InstallationFile -Files $DownloadParams + $sourceFile = Get-InstallationFile -FileParameters $downloadParamsProperties - # Verify downloaded file checksum - Write-OutPut "Verifying checksum for $DownloadPath" - $checksumUri = "$Uri.sha512" - if (-not (Test-CheckSum -DownloadedFile $DownloadPath -ChecksumUri $checksumUri)) { - $errMsg = "Checksum verification failed for $DownloadPath" - Write-Error $errMsg + if (-not (Test-Path -Path $sourceFile)) { + Throw "Couldn't find the downloaded file $sourceFile" + } - # Clean up downloaded file - Write-Warning "Removing downloaded file $DownloadPath" - Remove-Item -Path $DownloadPath -Force + $WinCNIBin = "$WinCNIPath\bin" + if (-not (Test-Path -Path $WinCNIBin)) { + New-Item -Path $WinCNIBin -ItemType Directory -Force -ErrorAction Ignore | Out-Null + } - Throw $errMsg + # Expand archive file + $cmdOutput = Invoke-ExecutableCommand -executable "tar.exe" -arguments "-xf `"$sourceFile`" -C `"$WinCNIBin`"" -timeout (60 * 2) + if ($cmdOutput.ExitCode -ne 0) { + Throw "Failed to expand archive `"$sourceFile`" at `"$WinCNIBin`". Exit code: $($cmdOutput.ExitCode). $($cmdOutput.StandardError.ReadToEnd())" } - # Expand zip file and install Win CNI plugin - $WinCNIBin = "$WinCNIPath\bin" - Expand-Archive -Path $DownloadPath -DestinationPath $WinCNIBin -Force - Remove-Item -Path $DownloadPath -Force -ErrorAction Ignore + if (Test-Path -Path $sourceFile) { + # Remove the downloaded file + Remove-Item -Path "$sourceFile" -Force -ErrorAction SilentlyContinue + } - Write-Output "Windows CNI plugin version $WinCNIVersion successfully installed at $WinCNIPath" + Write-Output "CNI plugin version $WinCNIVersion ($sourceRepo) successfully installed at $WinCNIPath" } else { # Code that should be processed if doing a WhatIf operation @@ -393,9 +423,9 @@ function Set-DefaultCNIConfig { process { if ($PSCmdlet.ShouldProcess('', "Sets Default CNI config")) { - # CurrentEndpointCount : 1 - # MaxConcurrentEndpoints : 1 - # TotalEndpoints + # TODO: Default CNI config for containernetworking/plugins + # https://www.cni.dev/plugins/current/main/win-bridge/ + # https://www.cni.dev/plugins/current/main/win-overlay/ $CNIConfig = @" { "cniVersion": "$WinCNIVersion", diff --git a/containers-toolkit/Public/ContainerdTools.psm1 b/containers-toolkit/Public/ContainerdTools.psm1 index aca3470..191e49d 100644 --- a/containers-toolkit/Public/ContainerdTools.psm1 +++ b/containers-toolkit/Public/ContainerdTools.psm1 @@ -6,10 +6,12 @@ # # ########################################################################### +using module "..\Private\CommonToolUtilities.psm1" $ModuleParentPath = Split-Path -Parent $PSScriptRoot Import-Module -Name "$ModuleParentPath\Private\CommonToolUtilities.psm1" -Force + function Get-ContainerdLatestVersion { $latestVersion = Get-LatestToolVersion -Repository "containerd/containerd" return $latestVersion @@ -21,8 +23,8 @@ function Install-Containerd { ConfirmImpact = 'High' )] param( - [parameter(HelpMessage = "ContainerD version to use. Defaults to latest version")] - [string]$Version, + [parameter(HelpMessage = "ContainerD version to use. Defaults to 'latest'")] + [string]$Version = "latest", [parameter(HelpMessage = "Path to install containerd. Defaults to ~\program files\containerd")] [string]$InstallPath = "$Env:ProgramFiles\Containerd", @@ -31,7 +33,11 @@ function Install-Containerd { [string]$DownloadPath = "$HOME\Downloads", [Parameter(HelpMessage = "Register and start Containerd Service")] - [switch] $Setup, + [switch]$Setup, + + [Parameter(HelpMessage = 'OS architecture to download files for. Default is $env:PROCESSOR_ARCHITECTURE')] + [ValidateSet('amd64', '386', "arm", "arm64")] + [string]$OSArchitecture = $env:PROCESSOR_ARCHITECTURE, [Switch] [parameter(HelpMessage = "Installs Containerd even if the tool already exists at the specified path")] @@ -76,43 +82,37 @@ function Install-Containerd { $Version = $Version.TrimStart('v') Write-Output "Downloading and installing Containerd v$version at $InstallPath" - $containerdTarFile = "containerd-${version}-windows-amd64.tar.gz" - $DownloadPath = "$DownloadPath\$($containerdTarFile)" - - # TODO: Use downloaded file if it exists # Download files - $Uri = "https://github.com/containerd/containerd/releases/download/v$version/$($containerdTarFile)" - $DownloadParams = @( - @{ - Feature = "Containerd" - Uri = $Uri - Version = $version - DownloadPath = $DownloadPath - } - ) - Get-InstallationFile -Files $DownloadParams - - # Verify downloaded file checksum - Write-OutPut "Verifying checksum for $DownloadPath" - $checksumUri = "$Uri.sha256sum" - if (-not (Test-CheckSum -DownloadedFile $DownloadPath -ChecksumUri $checksumUri)) { - $errMsg = "Checksum verification failed for $DownloadPath" - Write-Error $errMsg - - # Clean up downloaded file - Write-Warning "Removing downloaded file $DownloadPath" - Remove-Item -Path $DownloadPath -Force - - Throw $errMsg + $downloadParams = @{ + ToolName = "Containerd" + Repository = "containerd/containerd" + Version = $version + OSArchitecture = $OSArchitecture + DownloadPath = $DownloadPath + ChecksumSchemaFile = $null + + # QUESTION: Do we need them all? Containerd release contains multiple files. containerd, cri-containerd, cri-containerd-cni + # Matches eg: containerd-1.7.21-windows-amd64.tar.gz and containerd-1.7.21-windows-amd64.tar.gz.sha256sum + FileFilterRegEx = "(?:^containerd-<__VERSION__>-windows-$OSArchitecture.*.tar.gz(.*)?)$" } + $downloadParamsProperties = [FileDownloadParameters]::new( + $downloadParams.ToolName, + $downloadParams.Repository, + $downloadParams.Version, + $downloadParams.OSArchitecture, + $downloadParams.DownloadPath, + $downloadParams.ChecksumSchemaFile, + $downloadParams.FileFilterRegEx + ) + $sourceFile = Get-InstallationFile -FileParameters $downloadParamsProperties # Untar and install tool $params = @{ - Feature = "containerd" - InstallPath = $InstallPath - DownloadPath = $DownloadPath - EnvPath = "$InstallPath\bin" - cleanup = $true + Feature = "containerd" + InstallPath = $InstallPath + SourceFile = $sourceFile + EnvPath = "$InstallPath\bin" + cleanup = $true } Install-RequiredFeature @params @@ -222,10 +222,10 @@ function Register-ContainerdService { # Check containerd service is already registered if (Test-ServiceRegistered -Service 'containerd') { - Write-Warning (-join @("Containerd service already registered. To re-register the service, " - "stop the service by running 'Stop-Service containerd' or 'Stop-ContainerdService', then " - "run 'containerd --unregister-service'. Wait for containerd service to be deregistered, " - "then re-reun this command.")) + Write-Warning ( -join @("Containerd service already registered. To re-register the service, " + "stop the service by running 'Stop-Service containerd' or 'Stop-ContainerdService', then " + "run 'containerd --unregister-service'. Wait for containerd service to be deregistered, " + "then re-reun this command.")) return } diff --git a/containers-toolkit/Public/NerdctlTools.psm1 b/containers-toolkit/Public/NerdctlTools.psm1 index f1cd252..120bafa 100644 --- a/containers-toolkit/Public/NerdctlTools.psm1 +++ b/containers-toolkit/Public/NerdctlTools.psm1 @@ -6,6 +6,7 @@ # # ########################################################################### +using module "..\Private\CommonToolUtilities.psm1" $ModuleParentPath = Split-Path -Parent $PSScriptRoot Import-Module -Name "$ModuleParentPath\Private\CommonToolUtilities.psm1" -Force @@ -37,13 +38,14 @@ function Get-NerdctlDependencies($dependencies) { function Install-NerdctlDependencies { param( [String[]]$Dependencies, + [string]$OsArch, [Switch]$Force ) foreach ($dependency in $Dependencies) { $InstallCommand = "Install-$dependency" try { - & $InstallCommand -Force:$Force -Confirm:$false + & $InstallCommand -OSArchitecture $OsArch -Force:$Force -Confirm:$false } catch { Write-Error "Installation failed for $dependency. $_" @@ -58,8 +60,8 @@ function Install-Nerdctl { )] param( [string] - [parameter(HelpMessage = "nerdctl version to use. Defaults to latest version")] - $Version, + [parameter(HelpMessage = "nerdctl version to use. Defaults to 'latest'")] + $Version = "latest", [String] [parameter(HelpMessage = "Path to install nerdctl. Defaults to ~\program files\nerdctl")] @@ -73,6 +75,11 @@ function Install-Nerdctl { [parameter(HelpMessage = "Specify nerdctl dependencies (All, Containerd, Buildkit, WinCNIPlugin) to install")] $Dependencies, + [string] + [Parameter(HelpMessage = 'OS architecture to download files for. Default is $env:PROCESSOR_ARCHITECTURE')] + [ValidateSet('amd64', '386', "arm", "arm64")] + $OSArchitecture = $env:PROCESSOR_ARCHITECTURE, + [Switch] [parameter(HelpMessage = "Installs nerdctl (and its dependecies if specified) even if the tool already exists at the specified path")] $Force @@ -123,43 +130,34 @@ function Install-Nerdctl { Write-Output "Downloading and installing nerdctl v$version at $InstallPath" - # Download file from repo - $nerdctlTarFile = "nerdctl-$version-windows-amd64.tar.gz" - $DownloadPath = "$DownloadPath\$nerdctlTarFile" - # Download files - $baseUri = "https://github.com/containerd/nerdctl/releases/download/v${version}" - $DownloadParams = @( - @{ - Feature = "nerdctl" - Uri = "$baseUri/$nerdctlTarFile" - Version = $version - DownloadPath = $DownloadPath - } - ) - Get-InstallationFile -Files $DownloadParams - - # Verify downloaded file checksum - Write-OutPut "Verifying checksum for $DownloadPath" - $checksumUri = "$baseUri/SHA256SUMS" - if (-not (Test-CheckSum -DownloadedFile $DownloadPath -ChecksumUri $checksumUri)) { - $errMsg = "Checksum verification failed for $DownloadPath" - Write-Error $errMsg - - # Clean up downloaded file - Write-Warning "Removing downloaded file $DownloadPath" - Remove-Item -Path $DownloadPath -Force - - Throw $errMsg + $downloadParams = @{ + ToolName = "nerdctl" + Repository = "containerd/nerdctl" + Version = $version + OSArchitecture = $OSArchitecture + DownloadPath = $DownloadPath + ChecksumSchemaFile = $null + FileFilterRegEx = $null } + $downloadParamsProperties = [FileDownloadParameters]::new( + $downloadParams.ToolName, + $downloadParams.Repository, + $downloadParams.Version, + $downloadParams.OSArchitecture, + $downloadParams.DownloadPath, + $downloadParams.ChecksumSchemaFile, + $downloadParams.FileFilterRegEx + ) + $sourceFile = Get-InstallationFile -FileParameters $downloadParamsProperties # Untar and install tool $params = @{ - Feature = "nerdctl" - InstallPath = $InstallPath - DownloadPath = $DownloadPath - EnvPath = $InstallPath - cleanup = $true + Feature = "nerdctl" + InstallPath = $InstallPath + SourceFile = $sourceFile + EnvPath = $InstallPath + cleanup = $true } Install-RequiredFeature @params @@ -167,7 +165,8 @@ function Install-Nerdctl { Write-Output "For nerdctl usage: run 'nerdctl -h'`n" # Install dependencies - Install-NerdctlDependencies -Dependencies $dependencies -Force:$true + Write-Output "Installing nerdctl dependencies: $toinstall" + Install-NerdctlDependencies -Dependencies $dependencies -OsArch $OSArchitecture -Force:$true } else { # Code that should be processed if doing a WhatIf operation diff --git a/containers-toolkit/en-US/containers-toolkit-help.xml b/containers-toolkit/en-US/containers-toolkit-help.xml index 8270293..38da3fd 100644 --- a/containers-toolkit/en-US/containers-toolkit-help.xml +++ b/containers-toolkit/en-US/containers-toolkit-help.xml @@ -1,4 +1,4 @@ - + @@ -525,6 +525,18 @@ $Env:ProgramFiles\Buildkit + + OSArchitecture + + OS architecture to download files for. Default is `$env:PROCESSOR_ARCHITECTURE` + + String + + String + + + $env:PROCESSOR_ARCHITECTURE + Setup @@ -630,6 +642,18 @@ $Env:ProgramFiles\Buildkit + + OSArchitecture + + OS architecture to download files for. Default is `$env:PROCESSOR_ARCHITECTURE` + + String + + String + + + $env:PROCESSOR_ARCHITECTURE + Setup @@ -808,6 +832,18 @@ False + + OSArchitecture + + OS architecture to download files for. Default is `$env:PROCESSOR_ARCHITECTURE` + + String + + String + + + $env:PROCESSOR_ARCHITECTURE + Setup @@ -889,6 +925,18 @@ $ENV:ProramFiles\containerd + + OSArchitecture + + OS architecture to download files for. Default is `$env:PROCESSOR_ARCHITECTURE` + + String + + String + + + $env:PROCESSOR_ARCHITECTURE + Setup @@ -1085,6 +1133,18 @@ False + + OSArchitecture + + OS architecture to download files for. Default is `$env:PROCESSOR_ARCHITECTURE` + + String + + String + + + $env:PROCESSOR_ARCHITECTURE + WhatIf @@ -1203,6 +1263,18 @@ Latest version + + OSArchitecture + + OS architecture to download files for. Default is `$env:PROCESSOR_ARCHITECTURE` + + String + + String + + + $env:PROCESSOR_ARCHITECTURE + WhatIf @@ -1352,6 +1424,18 @@ False + + OSArchitecture + + OS architecture to download files for. Default is `$env:PROCESSOR_ARCHITECTURE` + + String + + String + + + $env:PROCESSOR_ARCHITECTURE + WhatIf @@ -1434,6 +1518,18 @@ $ENV:ProramFiles\nerdctl` + + OSArchitecture + + OS architecture to download files for. Default is `$env:PROCESSOR_ARCHITECTURE` + + String + + String + + + $env:PROCESSOR_ARCHITECTURE + Version @@ -1506,6 +1602,31 @@ Install-WinCNIPlugin + + OSArchitecture + + OS architecture to download files for. Default is `$env:PROCESSOR_ARCHITECTURE` + + String + + String + + + $env:PROCESSOR_ARCHITECTURE + + + SourceRepo + + Source of the Windows CNI plugins. Defaults to + 'microsoft/windows-container-networking' + + String + + String + + + "microsoft/windows-container-networking" + WinCNIVersion @@ -1606,7 +1727,33 @@ False - + + OSArchitecture + + OS architecture to download files for. Default is `$env:PROCESSOR_ARCHITECTURE` + + String + + String + + + $env:PROCESSOR_ARCHITECTURE + + + SourceRepo + + Source of the Windows CNI plugins. Defaults to + 'microsoft/windows-container-networking' + + String + + String + + + "microsoft/windows-container-networking" + + WinCNIPath Location to install Windows CNI. diff --git a/docs/About/Install-Buildkit.md b/docs/About/Install-Buildkit.md index 121853e..6b33d7e 100644 --- a/docs/About/Install-Buildkit.md +++ b/docs/About/Install-Buildkit.md @@ -1,6 +1,6 @@ --- external help file: containers-toolkit-help.xml -Module Name: containers-toolkit +Module Name: Containers-Toolkit online version: schema: 2.0.0 --- @@ -16,14 +16,14 @@ Downloads and installs BuildKit. ### Install (Default) ``` -Install-Buildkit [-Version ] [-InstallPath ] [-DownloadPath ] [-Force] [-WhatIf] [-Confirm] [] +Install-Buildkit [-Version ] [-InstallPath ] [-DownloadPath ] [-OSArchitecture ] [-Force] [-WhatIf] [-Confirm] [] ``` ### Setup ``` Install-Buildkit [-Version ] [-InstallPath ] [-DownloadPath ] [-Setup] - [-WinCNIPath ] [-Force] [-Confirm] [-WhatIf] [] + [-WinCNIPath ] [-OSArchitecture ] [-Force] [-Confirm] [-WhatIf] [] ``` ## DESCRIPTION @@ -125,6 +125,23 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -OSArchitecture + +OS architecture to download files for. +Default is `$env:PROCESSOR_ARCHITECTURE` + +```yaml +Type: String +Parameter Sets: Setup +Aliases: + +Required: False +Position: Named +Default value: $env:PROCESSOR_ARCHITECTURE +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Setup Register and start buildkitd Service once BuildKit installation is done. diff --git a/docs/About/Install-ContainerTools.md b/docs/About/Install-ContainerTools.md index 4a7d7fa..9f41456 100644 --- a/docs/About/Install-ContainerTools.md +++ b/docs/About/Install-ContainerTools.md @@ -15,8 +15,7 @@ Downloads and installs container tool (Containerd, BuildKit, and nerdctl). ``` Install-ContainerTools [[-ContainerDVersion] ] [[-BuildKitVersion] ] - [[-NerdCTLVersion] ] [[-InstallPath] ] [[-DownloadPath] ] [-Force] [-RegisterServices] - [-Confirm] [-WhatIf] [] + [[-NerdCTLVersion] ] [[-InstallPath] ] [[-DownloadPath] ] [-RegisterServices] [-OSArchitecture ] [-Force] [-Confirm] [-WhatIf] [] ``` ## DESCRIPTION @@ -182,6 +181,23 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -OSArchitecture + +OS architecture to download files for. +Default is `$env:PROCESSOR_ARCHITECTURE` + +```yaml +Type: String +Parameter Sets: Setup +Aliases: + +Required: False +Position: Named +Default value: $env:PROCESSOR_ARCHITECTURE +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet isn't run. diff --git a/docs/About/Install-Containerd.md b/docs/About/Install-Containerd.md index 1f4a1da..3714724 100644 --- a/docs/About/Install-Containerd.md +++ b/docs/About/Install-Containerd.md @@ -1,4 +1,4 @@ ---- +--- external help file: Containers-Toolkit-help.xml Module Name: Containers-Toolkit online version: @@ -14,8 +14,8 @@ Downloads and installs Containerd. ## SYNTAX ``` -Install-Containerd [[-Version] ] [[-InstallPath] ] [[-DownloadPath] ] [-Setup] [-Force] - [-Confirm] [-WhatIf] [] +Install-Containerd [[-Version] ] [[-InstallPath] ] [[-DownloadPath] ] [-Setup] [-OSArchitecture ] [-Force] + [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -117,6 +117,23 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -OSArchitecture + +OS architecture to download files for. +Default is `$env:PROCESSOR_ARCHITECTURE` + +```yaml +Type: String +Parameter Sets: Setup +Aliases: + +Required: False +Position: Named +Default value: $env:PROCESSOR_ARCHITECTURE +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Setup Register and start Containerd Service once Containerd installation is done diff --git a/docs/About/Install-Nerdctl.md b/docs/About/Install-Nerdctl.md index 23b1428..299d5db 100644 --- a/docs/About/Install-Nerdctl.md +++ b/docs/About/Install-Nerdctl.md @@ -15,7 +15,7 @@ Downloads and installs nerdctl. ``` Install-Nerdctl [[-Version] ] [[-InstallPath] ] [[-DownloadPath] ] - [[-Dependencies] ] [-Force] [-Confirm] [-WhatIf] [] + [[-Dependencies] ] [-OSArchitecture ] [-Force] [-Confirm] [-WhatIf] [] ``` ## DESCRIPTION @@ -125,6 +125,23 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -OSArchitecture + +OS architecture to download files for. +Default is `$env:PROCESSOR_ARCHITECTURE` + +```yaml +Type: String +Parameter Sets: Setup +Aliases: + +Required: False +Position: Named +Default value: $env:PROCESSOR_ARCHITECTURE +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Version nerdctl version to install. Defaults to latest version. diff --git a/docs/About/Install-WinCNIPlugin.md b/docs/About/Install-WinCNIPlugin.md index 1cd3ad9..262ac0b 100644 --- a/docs/About/Install-WinCNIPlugin.md +++ b/docs/About/Install-WinCNIPlugin.md @@ -14,7 +14,7 @@ Downloads and installs Windows CNI plugin. ## SYNTAX ``` -Install-WinCNIPlugin [[-WinCNIVersion] ] [[-WinCNIPath] ] [-Force] [-Confirm] [-WhatIf] +Install-WinCNIPlugin [[-WinCNIVersion] ] [[-WinCNIPath] ] [-OSArchitecture ] [-SourceRepo ] [-Force] [-Confirm] [-WhatIf] [] ``` @@ -77,6 +77,40 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -OSArchitecture + +OS architecture to download files for. +Default is `$env:PROCESSOR_ARCHITECTURE` + +```yaml +Type: String +Parameter Sets: Setup +Aliases: + +Required: False +Position: Named +Default value: $env:PROCESSOR_ARCHITECTURE +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SourceRepo + +Source of the Windows CNI plugins. +Defaults to 'microsoft/windows-container-networking' + +```yaml +Type: String +Parameter Sets: "microsoft/windows-container-networking", "containernetworking/plugins" +Aliases: + +Required: False +Position: Named +Default value: "microsoft/windows-container-networking" +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -WinCNIPath Location to install Windows CNI. diff --git a/docs/command-reference.md b/docs/command-reference.md index 2fb05a8..29545c3 100644 --- a/docs/command-reference.md +++ b/docs/command-reference.md @@ -60,8 +60,9 @@ Downloads container tool (Containerd, BuildKit, nerdctl) asynchronously and inst | nerdctlVersion | String | nerdctl version to install | Latest version | | InstallPath | String | Path to install container tools | `$Env:ProgramFiles` | | DownloadPath | String | Path to download container tools | `$HOME\Downloads` | -| Force | Switch | Force install the tools even if they already exists at the specified path | | | RegisterServices | Switch | Register and Start Containerd and Buildkitd services and set up NAT network | | +| OSArchitecture | String | OS architecture. Accepts: 'amd64', '386', 'arm', 'arm64' | $env:PROCESSOR_ARCHITECTURE | +| Force | Switch | Force install the tools even if they already exists at the specified path | | | Confirm | Switch | Prompts for confirmation before running the cmdlet. For more information, see the following articles: [about_Preference_Variables](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables?view=powershell-7.4#confirmpreference) and [about_Functions_CmdletBindingAttribute](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_cmdletbindingattribute?view=powershell-7.4#confirmimpact) | | | WhatIf | Switch | Shows what would happen if the cmdlet runs. The cmdlet isn't run. | | @@ -103,6 +104,7 @@ Once Containerd is installed and added to the environment path, we can get the p | InstallPath | String | Path to install Containerd | `$Env:ProgramFiles\containerd` | | DownloadPath | String | Path to download Containerd | `$HOME\Downloads` | | Setup | Switch | Register and start Containerd Service once Containerd installation is done | | +| OSArchitecture | String | OS architecture. Accepts: 'amd64', '386', 'arm', 'arm64' | $env:PROCESSOR_ARCHITECTURE | | Force | Switch | Installs Containerd even if the tool already exists at the specified path | | | Confirm | Switch | Prompts for confirmation before running the cmdlet. For more information, see the following articles: [about_Preference_Variables](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables?view=powershell-7.4#confirmpreference) and [about_Functions_CmdletBindingAttribute](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_cmdletbindingattribute?view=powershell-7.4#confirmimpact) | | | WhatIf | Switch | Shows what would happen if the cmdlet runs. The cmdlet isn't run. | | @@ -206,6 +208,7 @@ Once BuildKit is installed and added to the environment path, we can get the pat |InstallPath | String | Path to install BuildKit | `$Env:ProgramFiles\BuildKit` | |DownloadPath | String | Path to download BuildKit | $HOME\Downloads | | Setup | Switch | Register and start buildkitd Service once Containerd installation is done | | +| OSArchitecture | String | OS architecture. Accepts: 'amd64', '386', 'arm', 'arm64' | $env:PROCESSOR_ARCHITECTURE | | Force | Switch | Installs Buildkit even if the tool already exists at the specified path | | | Confirm | Switch | Prompts for confirmation before running the cmdlet. For more information, see the following articles: [about_Preference_Variables](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables?view=powershell-7.4#confirmpreference) and [about_Functions_CmdletBindingAttribute](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_cmdletbindingattribute?view=powershell-7.4#confirmimpact) | | | WhatIf | Switch | Shows what would happen if the cmdlet runs. The cmdlet isn't run. | | @@ -301,6 +304,7 @@ Downloads Containerd files from [nerdctl releases](https://github.com/containerd | InstallPath | String | Path to install nerdctl | $Env:ProgramFiles\nerdctl | | DownloadPath | String | Path to download nerdctl | $HOME\Downloads | | Dependencies | String[] | Specify the nerdctl dependencies (All, Containerd, Buildkit, WinCNIPlugin) to install. | | +| OSArchitecture | String | OS architecture. Accepts: 'amd64', '386', 'arm', 'arm64' | $env:PROCESSOR_ARCHITECTURE | | Force | Switch | Installs nerdctl even if the tool already exists at the specified path | | | Confirm | Switch | Prompts for confirmation before running the cmdlet. For more information, see the following articles: [about_Preference_Variables](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables?view=powershell-7.4#confirmpreference) and [about_Functions_CmdletBindingAttribute](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_cmdletbindingattribute?view=powershell-7.4#confirmimpact) | | | WhatIf | Switch | Shows what would happen if the cmdlet runs. The cmdlet isn't run. | | @@ -349,6 +353,8 @@ Downloads Windows CNI plugin from [windows-container-networking](https://github. | -------- | ------- | ------- | ------- | | WinCNIVersion | String | Windows CNI version to install | Latest version | | WinCNIPath | String | Location to install Windows CNI | Path where Containerd is installed or `$Env:ProgramFiles\Containerd`| +| OSArchitecture | String | OS architecture. Accepts: 'amd64', '386', 'arm', 'arm64' | $env:PROCESSOR_ARCHITECTURE | +| SourceRepo | String | OS architecture. Accepts: "microsoft/windows-container-networking", "containernetworking/plugins" | "microsoft/windows-container-networking" | | Force | Switch | Installs Windows CNI plugins even if the tool already exists at the specified path | | | Confirm | Switch | Prompts for confirmation before running the cmdlet. For more information, see the following articles: [about_Preference_Variables](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables?view=powershell-7.4#confirmpreference) and [about_Functions_CmdletBindingAttribute](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_cmdletbindingattribute?view=powershell-7.4#confirmimpact) | | | WhatIf | Switch | Shows what would happen if the cmdlet runs. The cmdlet isn't run. | |