diff --git a/doc/100-General/10-Changelog.md b/doc/100-General/10-Changelog.md index bbddc089..933fc21e 100644 --- a/doc/100-General/10-Changelog.md +++ b/doc/100-General/10-Changelog.md @@ -19,6 +19,7 @@ Released closed milestones can be found on [GitHub](https://github.com/Icinga/ic * [#40](https://github.com/Icinga/icinga-powershell-framework/issues/40) Adds support to set service recovery for the Icinga Agent and Icinga for Windows service, to restart them in case of a crash or error * [#525](https://github.com/Icinga/icinga-powershell-framework/pull/525) Adds new developer mode for `icinga` command and improved cache handling, to ensure within `-DeveloperMode` and inside a VS Code environment, the framework cache file is never overwritten, while still all functions are loaded and imported. +* [#531](https://github.com/Icinga/icinga-powershell-framework/pull/531) Adds `Test-IcingaStateFile` and `Repair-IcingaStateFile`, which is integrated into `Test-IcingaAgent`, to ensure the Icinga Agent state file is healthy and not corrupt, causing the Icinga Agent to fail on start ## 1.9.1 (2022-05-13) diff --git a/lib/core/icingaagent/readers/Read-IcingaStateFile.psm1 b/lib/core/icingaagent/readers/Read-IcingaStateFile.psm1 new file mode 100644 index 00000000..6bdce669 --- /dev/null +++ b/lib/core/icingaagent/readers/Read-IcingaStateFile.psm1 @@ -0,0 +1,46 @@ +function Read-IcingaStateFile() +{ + param ( + [switch]$WriteOutput = $FALSE + ); + + [string]$StateFilePath = Join-Path -Path $ENV:ProgramData -ChildPath 'icinga2\var\lib\icinga2\icinga2.state'; + + if ((Test-Path $StateFilePath) -eq $FALSE) { + return $TRUE; + } + + $StateFileContent = Get-Content -Path $StateFilePath -Encoding 'UTF8' -Raw; + $FileInformation = Get-Item -Path $StateFilePath; + + if ([string]::IsNullOrEmpty($StateFileContent)) { + return $FALSE; + } + + while ($TRUE) { + try { + if ([string]::IsNullOrEmpty($StateFileContent)) { + break; + } + + if ($StateFileContent.Contains(':') -eq $FALSE) { + Write-IcingaTestOutput -Severity 'Failed' -Message 'The start index of the Icinga Agent state file could not be found. The file seems to be corrupt.' -DropMessage:(-Not $WriteOutput); + return $FALSE; + } + + [int]$IndexOfJSON = $StateFileContent.IndexOf(':'); + [int]$StatementSize = $StateFileContent.SubString(0, $IndexOfJSON); + [string]$JSONString = $StateFileContent.Substring($IndexOfJSON + 1, $StatementSize); + [int]$TotalMsgLen = $IndexOfJSON + $StatementSize + 2; + $StateFileContent = $StateFileContent.Substring($TotalMsgLen, $StateFileContent.Length - $TotalMsgLen); + $JsonValid = ConvertFrom-Json -InputObject $JSONString -ErrorAction Stop; + } catch { + [string]$ErrMessage = [string]::Format('The Icinga Agent state file validation failed with an exception: "{0}"', $_.Exception.Message); + Write-IcingaTestOutput -Severity 'Failed' -Message $ErrMessage -DropMessage:(-Not $WriteOutput); + + return $FALSE; + } + } + + return $TRUE; +} diff --git a/lib/core/icingaagent/installer/Repair-IcingaService.psm1 b/lib/core/icingaagent/repair/Repair-IcingaService.psm1 similarity index 100% rename from lib/core/icingaagent/installer/Repair-IcingaService.psm1 rename to lib/core/icingaagent/repair/Repair-IcingaService.psm1 diff --git a/lib/core/icingaagent/repair/Repair-IcingaStateFile.psm1 b/lib/core/icingaagent/repair/Repair-IcingaStateFile.psm1 new file mode 100644 index 00000000..8049f34d --- /dev/null +++ b/lib/core/icingaagent/repair/Repair-IcingaStateFile.psm1 @@ -0,0 +1,21 @@ +function Repair-IcingaStateFile() +{ + param ( + [switch]$Force + ); + + [string]$StateFilePath = Join-Path -Path $ENV:ProgramData -ChildPath 'icinga2\var\lib\icinga2\icinga2.state*'; + + if ((Test-IcingaStateFile) -And $Force -eq $FALSE) { + Write-IcingaConsoleNotice -Message 'The Icinga Agent state file seems to be okay'; + return; + } + + $Success = Remove-ItemSecure -Path $StateFilePath -Force -Retries 5; + + if ($Success) { + Write-IcingaConsoleNotice -Message 'The corrupted Icinga Agent State files have been removed'; + } else { + Write-IcingaConsoleError -Message 'Failed to remove the corrupted Icinga Agent state files'; + } +} diff --git a/lib/core/icingaagent/tests/Test-IcingaAgent.psm1 b/lib/core/icingaagent/tests/Test-IcingaAgent.psm1 index b96624b4..f908bf25 100644 --- a/lib/core/icingaagent/tests/Test-IcingaAgent.psm1 +++ b/lib/core/icingaagent/tests/Test-IcingaAgent.psm1 @@ -18,6 +18,7 @@ function Test-IcingaAgent() Test-IcingaAcl (Get-IcingaCacheDir) -WriteOutput | Out-Null; Test-IcingaAcl (Get-IcingaPowerShellConfigDir) -WriteOutput | Out-Null; Test-IcingaAcl -Directory (Join-Path -Path (Get-IcingaFrameworkRootPath) -ChildPath 'certificate') -WriteOutput | Out-Null; + Test-IcingaStateFile -WriteOutput | Out-Null; if ($IcingaAgentData.Installed) { Test-IcingaAgentConfig | Out-Null; diff --git a/lib/core/icingaagent/tests/Test-IcingaStateFile.psm1 b/lib/core/icingaagent/tests/Test-IcingaStateFile.psm1 new file mode 100644 index 00000000..3b08175e --- /dev/null +++ b/lib/core/icingaagent/tests/Test-IcingaStateFile.psm1 @@ -0,0 +1,25 @@ +function Test-IcingaStateFile() +{ + param ( + [switch]$WriteOutput = $FALSE + ); + + $IcingaAgentData = Get-IcingaAgentInstallation; + [string]$StateFilePath = Join-Path -Path $ENV:ProgramData -ChildPath 'icinga2\var\lib\icinga2\icinga2.state'; + + if ((Test-Path $StateFilePath) -eq $FALSE) { + Write-IcingaTestOutput -Severity 'Passed' -Message 'The Icinga Agent state file does not exist' -DropMessage:(-Not $WriteOutput); + return $TRUE; + } + + $Success = Read-IcingaStateFile; + + if ($Success) { + Write-IcingaTestOutput -Severity 'Passed' -Message 'The Icinga Agent state file is healthy' -DropMessage:(-Not $WriteOutput); + return $TRUE; + } else { + Write-IcingaTestOutput -Severity 'Failed' -Message 'The Icinga Agent state file is corrupt. Use the "Repair-IcingaStateFile" command to repair the file or "Read-IcingaStateFile -WriteOutput" for further details' -DropMessage:(-Not $WriteOutput); + } + + return $FALSE; +} diff --git a/lib/core/icingaagent/writers/Write-IcingaTestOutput.psm1 b/lib/core/icingaagent/writers/Write-IcingaTestOutput.psm1 index 345db689..cc6d2b2d 100644 --- a/lib/core/icingaagent/writers/Write-IcingaTestOutput.psm1 +++ b/lib/core/icingaagent/writers/Write-IcingaTestOutput.psm1 @@ -3,9 +3,14 @@ function Write-IcingaTestOutput() param( [ValidateSet('Passed', 'Warning', 'Failed')] $Severity, - $Message + $Message, + [switch]$DropMessage = $FALSE ); + if ($DropMessage) { + return; + } + $Color = 'Green'; Switch ($Severity) { diff --git a/lib/core/installer/menu/manage/settings/troubleshooting/Troubleshooting.psm1 b/lib/core/installer/menu/manage/settings/troubleshooting/Troubleshooting.psm1 index 076ed148..e55bac0e 100644 --- a/lib/core/installer/menu/manage/settings/troubleshooting/Troubleshooting.psm1 +++ b/lib/core/installer/menu/manage/settings/troubleshooting/Troubleshooting.psm1 @@ -43,7 +43,7 @@ function Show-IcingaForWindowsMenuManageTroubleshooting() }, @{ 'Caption' = 'Repair Icinga Agent service'; - 'Command' = 'Show-IcingaForWindowsMenuManageIcingaForWindowsServices'; + 'Command' = 'Show-IcingaForWindowsMenuManageTroubleshooting'; 'Help' = 'Allows to repair the Icinga Agent service in case it was removed or broke during installation/upgrade'; 'Disabled' = ($null -ne $IcingaAgentService); 'DisabledReason' = 'The Icinga Agent service is already present'; @@ -52,6 +52,17 @@ function Show-IcingaForWindowsMenuManageTroubleshooting() 'Command' = 'Repair-IcingaService'; } }, + @{ + 'Caption' = 'Repair Icinga Agent state file'; + 'Command' = 'Show-IcingaForWindowsMenuManageTroubleshooting'; + 'Help' = 'Allows to repair the Icinga Agent state file, in case the file is corrupt'; + 'Disabled' = (Test-IcingaStateFile); + 'DisabledReason' = 'The Icinga Agent state file is healthy'; + 'AdminMenu' = $TRUE; + 'Action' = @{ + 'Command' = 'Repair-IcingaStateFile'; + } + }, @{ 'Caption' = 'Allow untrusted certificate communication (This session)'; 'Command' = 'Show-IcingaForWindowsMenuManageTroubleshooting';