Skip to content

Commit e83f8c5

Browse files
authored
fix: WaitForExit() in Invoke-ExecutableCommand() hangs (#17)
Minor fix: Fix CodeFactor badge
1 parent 3661edf commit e83f8c5

5 files changed

Lines changed: 74 additions & 27 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
[ci-build-site]: https://github.com/microsoft/containers-toolkit/actions/workflows/ci-build.yaml
99
[devskim-image]: https://github.com/microsoft/containers-toolkit/actions/workflows/sdl-compliance.yaml/badge.svg
1010
[devskim-site]: https://github.com/microsoft/containers-toolkit/actions/workflows/sdl-compliance.yaml
11-
[cf-image]: https://www.codefactor.io/repository/github/powershell/powershell/badge
12-
[cf-site]: https://www.codefactor.io/repository/github/powershell/powershell
11+
[cf-image]: https://www.codefactor.io/repository/github/microsoft/containers-toolkit/badge/main
12+
[cf-site]: https://www.codefactor.io/repository/github/microsoft/containers-toolkit/overview/main
1313

1414
## Table of contents
1515

Tests/ContainerdTools.Tests.ps1

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ Describe "ContainerdTools.psm1" {
162162

163163
Context "Register-ContainerdService" -Tag "Register-ContainerdService" {
164164
BeforeAll {
165-
$MockContainerdPath = 'TestDrive:\Program Files\Containerd'
165+
$MockContainerdPath = "$TestDrive\Program Files\Containerd"
166166
# New-Item -Path $MockContainerdPath -ItemType 'Directory' -Force | Out-Null
167167
New-Item -Path "$MockContainerdPath\bin\containerd.exe" -ItemType 'File' -Force | Out-Null
168168

@@ -172,15 +172,19 @@ Describe "ContainerdTools.psm1" {
172172
Mock Get-DefaultInstallPath -ModuleName "ContainerdTools" `
173173
-MockWith { return $MockContainerdPath } `
174174
-ParameterFilter { $Tool -eq "Containerd" }
175-
Mock Invoke-ExecutableCommand -ModuleName "ContainerdTools" `
176-
-ParameterFilter { $Arguments -eq "config default" } `
177-
-MockWith { return 'Sample containerd default config data' }
178175

179-
$obj = New-MockObject -Type 'System.Diagnostics.Process' -Properties @{ ExitCode = 0 }
180-
Mock Invoke-ExecutableCommand -ModuleName "ContainerdTools" -MockWith { return $obj }
176+
$mockProcess = New-MockObject -Type 'System.Diagnostics.Process' -Properties @{ ExitCode = 0 }
177+
Mock Invoke-ExecutableCommand -ModuleName "ContainerdTools" -MockWith { return $mockProcess }
181178

182-
# $MockService = New-MockObject -Type System.ServiceProcess.ServiceController -Methods @{ WaitForStatus = { } }
183-
# Mock Get-Service -ModuleName "ContainerdTools" -MockWith { return $MockService }
179+
# Mock for default config
180+
$mockConfigStdOut = New-MockObject -Type 'System.IO.StreamReader' -Methods @{ ReadToEnd = { return "Sample containerd default config data" } }
181+
$mockConfigProcess = New-MockObject -Type 'System.Diagnostics.Process' -Properties @{
182+
StandardOutput = $mockConfigStdOut
183+
}
184+
Mock Invoke-ExecutableCommand -ModuleName "ContainerdTools" `
185+
-ParameterFilter { $Arguments -eq "config default" } `
186+
-MockWith { return $mockConfigProcess }
187+
184188
Mock Get-Service -ModuleName "ContainerdTools" -MockWith { return [MockService]::new('Containerd') }
185189
Mock Set-Service -ModuleName "ContainerdTools"
186190
Mock Start-ContainerdService -ModuleName "ContainerdTools"
@@ -254,6 +258,19 @@ Describe "ContainerdTools.psm1" {
254258

255259
{ Register-ContainerdService -Force } | Should -Throw "Failed to register containerd service.*"
256260
}
261+
262+
It "Should throw an error if config file is empty" {
263+
# Mock for default config
264+
$mockConfigStdOut = New-MockObject -Type 'System.IO.StreamReader' -Methods @{ ReadToEnd = { return } }
265+
$mockConfigProcess = New-MockObject -Type 'System.Diagnostics.Process' -Properties @{
266+
StandardOutput = $mockConfigStdOut
267+
}
268+
Mock Invoke-ExecutableCommand -ModuleName "ContainerdTools" `
269+
-ParameterFilter { $Arguments -eq "config default" } `
270+
-MockWith { return $mockConfigProcess }
271+
272+
{ Register-ContainerdService -Force } | Should -Throw "Config file is empty. '$MockContainerdPath\config.toml'"
273+
}
257274
}
258275

259276
Context "Uninstall-Containerd" -Tag "Uninstall-Containerd" {

containers-toolkit/Private/CommonToolUtilities.psm1

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,6 @@ function Install-ContainerToolConsent ($tool) {
171171
return [ActionConsent]$consent -eq [ActionConsent]::Yes
172172
}
173173

174-
175174
function Uninstall-ContainerToolConsent ($tool, $path) {
176175
$question = "Do you want to uninstall $tool from $($path)?"
177176
$choices = '&Yes', '&No'
@@ -266,14 +265,27 @@ function Invoke-StopService($service) {
266265
}
267266
}
268267

268+
function Test-ConfFileEmpty($Path) {
269+
if (!(Test-Path -LiteralPath $Path)) {
270+
return $true
271+
}
272+
273+
$isFileNotEmpty = (([System.IO.File]::ReadAllText($Path)) -match '\S')
274+
return (-not $isFileNotEmpty )
275+
}
276+
269277
function Invoke-ExecutableCommand {
270278
param (
271279
[parameter(Mandatory)]
272280
[String] $executable,
273281
[parameter(Mandatory)]
274-
[String] $arguments
282+
[String] $arguments,
283+
[Parameter(Mandatory = $false, HelpMessage = "Period of time to wait (in seconds) for the associated process to exit. Default is 15 seconds.")]
284+
[Int32] $timeout = 15
275285
)
276286

287+
Write-Debug "Executing '$executable $arguments'"
288+
277289
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
278290
$pinfo.FileName = $executable
279291
$pinfo.RedirectStandardError = $true
@@ -283,7 +295,15 @@ function Invoke-ExecutableCommand {
283295
$p = New-Object System.Diagnostics.Process
284296
$p.StartInfo = $pinfo
285297
$p.Start() | Out-Null
286-
$p.WaitForExit()
298+
# Blocks the current thread of execution until the time has elapsed or the process has exited.
299+
$p.WaitForExit($timeout * 1000) | Out-Null
300+
301+
if (-not $p.HasExited) {
302+
Write-Debug "Execution did not complete in $timeout seconds."
303+
}
304+
else {
305+
Write-Debug "Command execution completed. Exit code: $($p.ExitCode)"
306+
}
287307

288308
return $p
289309
}
@@ -301,3 +321,4 @@ Export-ModuleMember -Function Test-ServiceRegistered
301321
Export-ModuleMember -Function Add-FeatureToPath
302322
Export-ModuleMember -Function Remove-FeatureFromPath
303323
Export-ModuleMember -Function Invoke-ServiceAction
324+
Export-ModuleMember -Function Test-ConfFileEmpty

containers-toolkit/Public/BuildkitTools.psm1

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,10 @@ function Register-BuildkitdService {
230230

231231
# Check buildkitd service is already registered
232232
if (Test-ServiceRegistered -Service 'Buildkitd') {
233-
Write-Warning "Buildkitd service already registered."
233+
Write-Warning (-join @("buildkitd service already registered. To re-register the service, "
234+
"stop the service by running 'Stop-Service buildkitd' or 'Stop-BuildkitdService', then "
235+
"run 'buildkitd --unregister-service'. Wait for buildkitd service to be deregistered, "
236+
"then re-reun this command."))
234237
return
235238
}
236239

@@ -404,14 +407,6 @@ function Uninstall-BuildkitHelper {
404407
Write-Output "Successfully uninstalled buildkit."
405408
}
406409

407-
function Test-ConfFileEmpty($Path) {
408-
if (!(Test-Path -LiteralPath $Path)) {
409-
return $true
410-
}
411-
412-
$isFileNotEmpty = (([System.IO.File]::ReadAllText($Path)) -match '\S')
413-
return (-not $isFileNotEmpty )
414-
}
415410

416411
function Get-ConsentToRegisterBuildkit ($path) {
417412
$retry = 2

containers-toolkit/Public/ContainerdTools.psm1

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,10 @@ function Register-ContainerdService {
206206

207207
# Check containerd service is already registered
208208
if (Test-ServiceRegistered -Service 'containerd') {
209-
Write-Warning "Containerd service already registered."
209+
Write-Warning (-join @("Containerd service already registered. To re-register the service, "
210+
"stop the service by running 'Stop-Service containerd' or 'Stop-ContainerdService', then "
211+
"run 'containerd --unregister-service'. Wait for containerd service to be deregistered, "
212+
"then re-reun this command."))
210213
return
211214
}
212215

@@ -224,11 +227,20 @@ function Register-ContainerdService {
224227

225228
Add-MpPreference -ExclusionProcess $containerdExecutable
226229

227-
#Configure containerd service
230+
# Get default containerd config and write to file
228231
$containerdConfigFile = "$ContainerdPath\config.toml"
229-
Invoke-ExecutableCommand -Executable $containerdExecutable -Arguments "config default" | `
230-
Out-File $containerdConfigFile -Encoding ascii
231-
Write-Information -InformationAction Continue -MessageData "Review containerd configutations at $containerdConfigFile"
232+
Write-Debug "Containerd config file: $containerdConfigFile"
233+
234+
$output = Invoke-ExecutableCommand -Executable $containerdExecutable -Arguments "config default"
235+
$output.StandardOutput.ReadToEnd() | Out-File -FilePath $containerdConfigFile -Encoding ascii -Force
236+
237+
# Check config file is not empty
238+
$isEmptyConfig = Test-ConfFileEmpty -Path "$containerdConfigFile"
239+
if ($isEmptyConfig) {
240+
Throw "Config file is empty. '$containerdConfigFile'"
241+
}
242+
243+
Write-Output "Review containerd configutations at $containerdConfigFile"
232244

233245
# Register containerd service
234246
$output = Invoke-ExecutableCommand -Executable $containerdExecutable -Arguments "--register-service --log-level debug --service-name containerd --log-file `"$env:TEMP\containerd.log`""
@@ -366,6 +378,8 @@ function Unregister-Containerd ($containerdPath) {
366378
Throw "Could not unregister containerd service. $($output.StandardError.ReadToEnd())"
367379
}
368380
else {
381+
# Wait for service to be unregistered
382+
# Failure to wait causes "The specified service has been marked for deletion." error
369383
Start-Sleep -Seconds 15
370384
}
371385
}

0 commit comments

Comments
 (0)