Skip to content

Commit 2603330

Browse files
committed
Refactor Show-ContainerTools
- Refactor Show-ContainerTools to reuse functions and rewrite the unit tests - Bug fixes - Spelling issues
1 parent 7cb550b commit 2603330

13 files changed

+276
-69
lines changed

Tests/AllToolsUtilities.Tests.ps1

Lines changed: 108 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,121 @@ Describe "AllToolsUtilities.psm1" {
3030

3131
Context "Show-ContainerTools" -Tag "Show-ContainerTools" {
3232
BeforeAll {
33-
Mock Get-InstalledVersion -ModuleName 'AllToolsUtilities'
33+
# Mock get version
34+
$mockConfigStdOut = New-MockObject -Type 'System.IO.StreamReader' -Methods @{ ReadToEnd = { return "tool version v1.0.1" } }
35+
$mockConfigProcess = New-MockObject -Type 'System.Diagnostics.Process' -Properties @{
36+
ExitCode = 0
37+
StandardOutput = $mockConfigStdOut
38+
}
39+
Mock Invoke-ExecutableCommand -ModuleName "AllToolsUtilities" `
40+
-ParameterFilter { $Arguments -eq "--version" } `
41+
-MockWith { return $mockConfigProcess }
42+
}
43+
44+
It "Should get containerd version" {
45+
$executablePath = "TestDrive:\Program Files\Containerd\bin\containerd.exe"
46+
Mock Get-Command -ModuleName 'AllToolsUtilities' -MockWith { @{ Name = 'containerd.exe'; Source = $executablePath } }
47+
Mock Get-Service -ModuleName 'AllToolsUtilities'
48+
49+
$containerdVersion = Show-ContainerTools -ToolName 'containerd'
50+
51+
# Check the output
52+
$expectedOutput = [PSCustomObject]@{
53+
Tool = 'containerd'
54+
Path = $executablePath
55+
Installed = $true
56+
Version = 'v1.0.1'
57+
Daemon = 'containerd'
58+
DaemonStatus = 'Unregistered'
59+
}
60+
# $containerdVersion | Should -Be $expectedOutput
61+
# HACK: Should -Be does not work with PSCustomObject in PSv5.
62+
# However PSv6 has support for this. To be investigated further.
63+
foreach ($key in $expectedOutput.Keys) {
64+
$expectedValue = $expectedOutput[$key]
65+
$actualValue = $containerdVersion.$key
66+
$actualValue | Should -Be $expectedValue
67+
}
68+
69+
# Check the invocation
70+
Should -Invoke Get-Command -ModuleName 'AllToolsUtilities' `
71+
-Times 1 -Exactly -Scope It -ParameterFilter { $Name -eq 'containerd.exe' }
72+
}
73+
74+
It "Should get buildkit version" {
75+
$executablePath = "TestDrive:\Program Files\Buildkit\bin\buildkitd.exe"
76+
$buildctlPath = "TestDrive:\Program Files\Buildkit\bin\buildctl.exe"
77+
78+
Mock Get-Service -ModuleName 'AllToolsUtilities' -MockWith { @{ Status = "Running" } }
79+
Mock Get-Command -ModuleName 'AllToolsUtilities' -MockWith { @(
80+
@{ Name = 'buildkitd.exe'; Source = $executablePath }
81+
@{ Name = 'buildctl.exe'; Source = $buildctlPath }
82+
) }
83+
84+
$buildkitVersion = Show-ContainerTools -ToolName 'buildkit'
85+
86+
# Check the output
87+
$expectedOutput = [PSCustomObject]@{
88+
Tool = 'buildkit'
89+
Path = $executablePath
90+
Installed = $true
91+
Version = 'v1.0.1'
92+
Daemon = 'buildkitd'
93+
DaemonStatus = 'Running'
94+
BuildctlPath = $buildctlPath
95+
}
96+
foreach ($key in $expectedOutput.Keys) {
97+
$expectedValue = $expectedOutput[$key]
98+
$actualValue = $buildkitVersion.$key
99+
$actualValue | Should -Be $expectedValue
100+
}
101+
102+
# Check the invocation
103+
Should -Invoke Get-Command -ModuleName 'AllToolsUtilities' `
104+
-Times 1 -Exactly -Scope It -ParameterFilter { $Name -eq "build*.exe" }
34105
}
35106

36-
It "Should list all container tools and their install status" {
37-
Show-ContainerTools
107+
It "Should return basic info if the tool is not installed" {
108+
Mock Get-Command -ModuleName 'AllToolsUtilities'
38109

39-
@("containerd", "buildkit", "nerdctl") | ForEach-Object {
40-
Should -Invoke Get-InstalledVersion -ModuleName 'AllToolsUtilities' `
41-
-Times 1 -Exactly -Scope It `
42-
-ParameterFilter { $Feature -eq $_ }
110+
$toolInfo = Show-ContainerTools
111+
112+
# Check the output
113+
$expectedOutput = @(
114+
[PSCustomObject]@{ Tool = 'containerd'; Installed = $false; Daemon = 'containerd'; DaemonStatus = 'Unregistered' }
115+
[PSCustomObject]@{ Tool = 'buildkit'; Installed = $false; Daemon = 'buildkitd'; DaemonStatus = 'Unregistered' }
116+
[PSCustomObject]@{ Tool = 'nerdctl'; Installed = $false }
117+
)
118+
$expectedOutput | ForEach-Object {
119+
$tool = $_.Tool
120+
$actualOutput = $toolInfo | Where-Object { $_.Tool -eq $tool }
121+
foreach ($key in $_.Keys) {
122+
$expectedValue = $_[$key]
123+
$actualValue = $actualOutput.$key
124+
$actualValue | Should -Be $expectedValue
125+
}
43126
}
44127
}
45128

46-
It "Should list the latest available version for each tool" {
47-
Show-ContainerTools -Latest
129+
It "Should return latest version if Latest flag is specified" {
130+
Mock Get-Command -ModuleName 'AllToolsUtilities'
131+
132+
$toolInfo = Show-ContainerTools -Latest
48133

49-
@("containerd", "buildkit", "nerdctl") | ForEach-Object {
50-
Should -Invoke Get-InstalledVersion -ModuleName 'AllToolsUtilities' `
51-
-Times 1 -Exactly -Scope It `
52-
-ParameterFilter { $Feature -eq $_ -and $Latest -eq $true }
134+
# Check the output
135+
$expectedOutput = @(
136+
[PSCustomObject]@{ Tool = 'containerd'; Installed = $false; Daemon = 'buildkitd'; DaemonStatus = 'Unregistered'; LatestVersion = 'v1.0.1' }
137+
[PSCustomObject]@{ Tool = 'buildkit'; Installed = $false; Daemon = 'buildkitd'; DaemonStatus = 'Unregistered'; LatestVersion = 'v1.0.1' }
138+
[PSCustomObject]@{ Tool = 'nerdctl'; Installed = $false; LatestVersion = 'v1.0.1' }
139+
)
140+
$expectedOutput | ForEach-Object {
141+
$tool = $_.Tool
142+
$actualOutput = $toolInfo | Where-Object { $_.Tool -eq $tool }
143+
foreach ($key in $_.Keys) {
144+
$expectedValue = $_[$key]
145+
$actualValue = $actualOutput.$key
146+
$actualValue | Should -Be $expectedValue
147+
}
53148
}
54149
}
55150
}

Tests/CommonToolUtilities.Tests.ps1

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ Describe "CommonToolUtilities.psm1" {
8484
}
8585

8686
Context "Get-LatestToolVersion" -Tag "Get-LatestToolVersion" {
87+
BeforeEach {
88+
$expectedUri = "https://api.github.com/repos/containerd/containerd/releases/latest"
89+
}
90+
8791
It "Should return the latest version for a tool" {
8892
$sampleOutput = @{
8993
StatusCode = 200
@@ -98,17 +102,20 @@ Describe "CommonToolUtilities.psm1" {
98102
}
99103
Mock Invoke-WebRequest { $sampleOutput } -ModuleName "CommonToolUtilities"
100104

101-
$result = Get-LatestToolVersion -Repository "test/tool"
105+
$result = Get-LatestToolVersion -Tool "containerd"
102106

103-
$expectedUri = "https://api.github.com/repos/test/tool/releases/latest"
104107
Should -Invoke Invoke-WebRequest -ParameterFilter { $Uri -eq $expectedUri } -Exactly 1 -Scope It -ModuleName "CommonToolUtilities"
105108
$result | Should -Be '0.12.3'
106109
}
107110

111+
It "Should throw an error if invalid tool name is provided" {
112+
{ Get-LatestToolVersion -Tool "invalid-tool" } | Should -Throw "Couldn't get latest invalid-tool version. Invalid tool name: 'invalid-tool'."
113+
}
114+
108115
It "Should throw an error if API call fails" {
109116
$errorMessage = "Response status code does not indicate success: 404 (Not Found)."
110117
Mock Invoke-WebRequest -MockWith { Throw $errorMessage } -ModuleName "CommonToolUtilities"
111-
{ Get-LatestToolVersion -Repository "test/tool" } | Should -Throw "Could not get tool latest version. $errorMessage"
118+
{ Get-LatestToolVersion -Tool "containerd" } | Should -Throw "Couldn't get containerd latest version from $expectedUri. $errorMessage"
112119
}
113120
}
114121

Tests/ContainerNetworkTools.Tests.ps1

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,22 @@ Describe "ContainerNetworkTools.psm1" {
3131
Remove-Module -Name "$RootPath\Tests\TestData\MockClasses.psm1" -Force -ErrorAction Ignore
3232
}
3333

34+
Context "Get-WinCNILatestVersion" -Tag "Get-WinCNILatestVersion" {
35+
BeforeEach {
36+
Mock Get-LatestToolVersion -ModuleName 'ContainerNetworkTools'
37+
}
38+
39+
It "Should return the latest version of Windows CNI plugin" {
40+
Get-WinCNILatestVersion
41+
Should -Invoke Get-LatestToolVersion -Times 1 -Scope It -ModuleName 'ContainerNetworkTools' -ParameterFilter { $Tool -eq 'wincniplugin' }
42+
}
43+
44+
It "Should return the latest version of Cloud Native CNI plugin" {
45+
Get-WinCNILatestVersion -Repo 'containernetworking/plugins'
46+
Should -Invoke Get-LatestToolVersion -Times 1 -Scope It -ModuleName 'ContainerNetworkTools' -ParameterFilter { $Tool -eq 'cloudnativecni' }
47+
}
48+
}
49+
3450
Context "Install-WinCNIPlugin" -Tag "Install-WinCNIPlugin" {
3551
BeforeAll {
3652
$Script:ToolName = 'WinCNIPlugin'

containers-toolkit/Private/CommonToolUtilities.psm1

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,38 @@ $HASH_FUNCTIONS_STR = $HASH_FUNCTIONS -join '|' # SHA1|SHA256|SHA384|SHA512|MD5
7272
$NERDCTL_CHECKSUM_FILE_PATTERN = "(?<hashfunction>(?:^({0})))" -f ($HASH_FUNCTIONS -join '|')
7373
$NERDCTL_FILTER_SCRIPTBLOCK_STR = { (("{0}" -match "$NERDCTL_CHECKSUM_FILE_PATTERN") -and "{0}" -notmatch ".*.asc$") }.ToString()
7474

75-
function Get-LatestToolVersion($repository) {
75+
76+
Set-Variable -Option AllScope -scope Global -Visibility Public -Name "CONTAINERD_REPO" -Value "containerd/containerd" -Force
77+
Set-Variable -Option AllScope -scope Global -Visibility Public -Name "BUILDKIT_REPO" -Value "moby/buildkit" -Force
78+
Set-Variable -Option AllScope -scope Global -Visibility Public -Name "NERDCTL_REPO" -Value "containerd/nerdctl" -Force
79+
Set-Variable -Option AllScope -scope Global -Visibility Public -Name "WINCNI_PLUGIN_REPO" -Value "microsoft/windows-container-networking" -Force
80+
Set-Variable -Option AllScope -scope Global -Visibility Public -Name "CLOUDNATIVE_CNI_REPO" -Value "containernetworking/plugins" -Force
81+
82+
83+
function Get-LatestToolVersion($tool) {
84+
# Get the repository based on the tool
85+
$repository = switch ($tool.ToLower()) {
86+
"containerd" { $CONTAINERD_REPO }
87+
"buildkit" { $BUILDKIT_REPO }
88+
"nerdctl" { $NERDCTL_REPO }
89+
"wincniplugin" { $WINCNI_PLUGIN_REPO }
90+
"cloudnativecni" { $CLOUDNATIVE_CNI_REPO }
91+
Default { Throw "Couldn't get latest $tool version. Invalid tool name: '$tool'." }
92+
}
93+
94+
# Get the latest release version URL string
95+
$uri = "https://api.github.com/repos/$repository/releases/latest"
96+
97+
Write-Debug "Getting the latest $tool version from $uri"
98+
99+
# Get the latest release version
76100
try {
77-
$uri = "https://api.github.com/repos/$repository/releases/latest"
78101
$response = Invoke-WebRequest -Uri $uri -UseBasicParsing
79102
$version = ($response.content | ConvertFrom-Json).tag_name
80103
return $version.TrimStart("v")
81104
}
82105
catch {
83-
$tool = ($repository -split "/")[1]
84-
Throw "Could not get $tool latest version. $($_.Exception.Message)"
106+
Throw "Couldn't get $tool latest version from $uri. $($_.Exception.Message)"
85107
}
86108
}
87109

@@ -889,7 +911,7 @@ function Invoke-ExecutableCommand {
889911
return $p
890912
}
891913

892-
914+
Export-ModuleMember -Variable CONTAINERD_REPO, BUILDKIT_REPO, NERDCTL_REPO, WINCNI_PLUGIN_REPO, CLOUDNATIVE_CNI_REPO
893915
Export-ModuleMember -Function Get-LatestToolVersion
894916
Export-ModuleMember -Function Get-DefaultInstallPath
895917
Export-ModuleMember -Function Test-EmptyDirectory

containers-toolkit/Public/AllToolsUtilities.psm1

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@ Import-Module -Name "$ModuleParentPath\Private\CommonToolUtilities.psm1" -Force
1414
function Show-ContainerTools {
1515
param (
1616
[Parameter(HelpMessage = "Show latest release version")]
17-
[Switch]$Latest
17+
[Switch]$Latest,
18+
19+
[Parameter(HelpMessage = "Tool to show")]
20+
[ValidateSet("containerd", "buildkit", "nerdctl")]
21+
[String[]]$ToolName
1822
)
1923

20-
$tools = @("containerd", "buildkit", "nerdctl")
24+
$tools = if ($ToolName) { $ToolName } else { @("containerd", "buildkit", "nerdctl") }
2125

2226
$installedTools = @()
2327
foreach ($tool in $tools) {
@@ -173,21 +177,27 @@ To register containerd and buildkitd services and create a NAT network, see help
173177
}
174178

175179
function Get-InstalledVersion($feature, $Latest) {
176-
$executable = $null
180+
$sourceLocation = $null
177181
$daemon = $null
182+
$buildctlPath = $null
178183
switch ($feature) {
179184
"buildkit" {
180-
$bktdExecutable = (Get-Command "build*.exe" | Where-Object { $_.Source -like "*buildkit*" }) | Select-Object Name
181-
if ($bktdExecutable) {
182-
$executable = ($bktdExecutable[0]).Name
185+
$blktCommandInfo = Get-Command "build*.exe" | Where-Object { $_.Source -like "*buildkit*" }
186+
if ($null -ne $blktCommandInfo) {
187+
# Get buildkitd executable
188+
$buldkitdCommandInfo = $blktCommandInfo | Where-Object { $_.Name -like "buildkitd.exe" }
189+
$sourceLocation = $buldkitdCommandInfo.Source
183190
}
191+
$daemon = 'buildkitd'
184192

185-
if ($null -ne ($bktdExecutable | Where-Object { $_.Name -contains "buildkitd.exe" })) {
186-
$daemon = 'buildkitd'
187-
}
193+
$buildctlPath = ($blktCommandInfo | Where-Object { $_.Name -like "buildctl.exe" }).Source
188194
}
189195
Default {
190-
$executable = (Get-Command "$feature.exe" -ErrorAction Ignore).Name
196+
$commandInfo = Get-Command "$feature.exe" -ErrorAction Ignore
197+
198+
if ($null -ne $commandInfo) {
199+
$sourceLocation = $commandInfo.Source
200+
}
191201

192202
if ($feature -eq 'containerd') {
193203
$daemon = 'containerd'
@@ -199,16 +209,21 @@ function Get-InstalledVersion($feature, $Latest) {
199209
Tool = $feature
200210
Installed = $False
201211
}
202-
if ($executable) {
203-
$result = getToolVersion -Executable $executable
212+
if ($sourceLocation) {
213+
$result = getToolVersion -Executable $sourceLocation
204214
Add-Member -InputObject $result -Name 'Tool' -Value $feature -MemberType 'NoteProperty'
205-
$result = $result | Select-Object Tool, Installed, Version
215+
Add-Member -InputObject $result -Name 'Path' -Value $sourceLocation -MemberType 'NoteProperty'
216+
$result = $result | Select-Object Tool, Path, Installed, Version
206217

207218
if ($daemon) {
208219
Add-Member -InputObject $result -Name 'Daemon' -Value $daemon -MemberType 'NoteProperty'
209220
Add-Member -InputObject $result -Name 'DaemonStatus' -MemberType 'NoteProperty' `
210221
-Value (getDaemonStatus -Daemon $daemon)
211222
}
223+
224+
if ($buildctlPath) {
225+
$result | Add-Member -Name 'BuildctlPath' -Value $buildctlPath -MemberType 'NoteProperty'
226+
}
212227
}
213228

214229
# Get latest version
@@ -223,9 +238,16 @@ function Get-InstalledVersion($feature, $Latest) {
223238
}
224239

225240
function getToolVersion($executable) {
241+
$toolName = [System.IO.Path]::GetFileNameWithoutExtension([System.IO.Path]::GetFileName($executable))
242+
226243
$installedVersion = $null
227244
try {
228-
$version = & $executable -v
245+
$cmdOutput = Invoke-ExecutableCommand -Executable $executable -Arguments '--version'
246+
if ($cmdOutput.ExitCode -ne 0) {
247+
Throw "Couldn't get $toolName version. $($cmdOutput.StandardError.ReadToEnd())"
248+
}
249+
250+
$version = $cmdOutput.StandardOutput.ReadToEnd()
229251

230252
$pattern = "(\d+\.)(\d+\.)(\*|\d+)"
231253
$installedVersion = ($version | Select-String -Pattern $pattern).Matches.Value

containers-toolkit/Public/BuildkitTools.psm1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ $ModuleParentPath = Split-Path -Parent $PSScriptRoot
1313
Import-Module -Name "$ModuleParentPath\Private\CommonToolUtilities.psm1" -Force
1414

1515
function Get-BuildkitLatestVersion {
16-
$latestVersion = Get-LatestToolVersion -Repository "moby/buildkit"
16+
$latestVersion = Get-LatestToolVersion -Tool "buildkit"
1717
return $latestVersion
1818
}
1919

@@ -96,7 +96,7 @@ function Install-Buildkit {
9696
# Download files
9797
$downloadParams = @{
9898
ToolName = "Buildkit"
99-
Repository = "moby/buildkit"
99+
Repository = "$BUILDKIT_REPO"
100100
Version = $Version
101101
OSArchitecture = $OSArchitecture
102102
DownloadPath = $DownloadPath

containers-toolkit/Public/ContainerNetworkTools.psm1

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,19 @@ using module "..\Private\CommonToolUtilities.psm1"
1212
$ModuleParentPath = Split-Path -Parent $PSScriptRoot
1313
Import-Module -Name "$ModuleParentPath\Private\CommonToolUtilities.psm1" -Force
1414

15+
$WINCNI_PLUGIN_REPO = "microsoft/windows-container-networking"
16+
$CLOUDNATIVE_CNI_REPO = "containernetworking/plugins"
17+
1518
function Get-WinCNILatestVersion {
16-
$latestVersion = Get-LatestToolVersion -Repository "microsoft/windows-container-networking"
19+
param (
20+
[String]$repo = "microsoft/windows-container-networking"
21+
)
22+
$tool = switch ($repo.ToLower()) {
23+
$WINCNI_PLUGIN_REPO { "wincniplugin" }
24+
$CLOUDNATIVE_CNI_REPO { "cloudnativecni" }
25+
Default { Throw "Invalid repository. Supported repositories are $WINCNI_PLUGIN_REPO and $CLOUDNATIVE_CNI_REPO" }
26+
}
27+
$latestVersion = Get-LatestToolVersion -Tool $tool
1728
return $latestVersion
1829
}
1930

@@ -80,7 +91,7 @@ function Install-WinCNIPlugin {
8091
# Get Windows CNI plugins version to install
8192
if (!$WinCNIVersion) {
8293
# Get default version
83-
$WinCNIVersion = Get-WinCNILatestVersion
94+
$WinCNIVersion = Get-WinCNILatestVersion -Repo $SourceRepo
8495
}
8596
$WinCNIVersion = $WinCNIVersion.TrimStart('v')
8697
Write-Output "Downloading CNI plugin version $WinCNIVersion at $WinCNIPath"
@@ -385,8 +396,7 @@ function Install-MissingPlugin {
385396
$consent = ([ActionConsent](Get-Host).UI.PromptForChoice($title, $question, $choices, 1)) -eq [ActionConsent]::Yes
386397

387398
if (-not $consent) {
388-
$downloadPath = "https://github.com/microsoft/windows-container-networking"
389-
Throw "Windows CNI plugins have not been installed. To install, run the command `"Install-WinCNIPlugin`" or download from $downloadPath."
399+
Throw "Windows CNI plugins have not been installed. To install, run the command `"Install-WinCNIPlugin`"."
390400
}
391401
}
392402

0 commit comments

Comments
 (0)