Fixes repository error handling and JSON error handling

This commit is contained in:
LordHepipud 2024-04-01 18:31:34 +02:00 committed by Lord Hepipud
parent 9a2090455c
commit a2294b3ce4
13 changed files with 206 additions and 24 deletions

View file

@ -20,6 +20,7 @@ Released closed milestones can be found on [GitHub](https://github.com/Icinga/ic
* [#672](https://github.com/Icinga/icinga-powershell-framework/pull/issues) Fixes Icinga for Windows REST-Api to fully read client data, even when they client is sending the packets on a very slow basis, preventing the API trying to process an incomplete request
* [#707](https://github.com/Icinga/icinga-powershell-framework/pull/707) Fixes size of the `Icinga for Windows` eventlog by setting it to `20MiB`, allowing to store more events before they are overwritten
* [#708](https://github.com/Icinga/icinga-powershell-framework/pull/708) Fixes the order for updating components with `Update-Icinga`, to ensure the `framework` is always updated first before all other components
* [#709](https://github.com/Icinga/icinga-powershell-framework/pull/709) Fixes error handling for Icinga for Windows repositories by providing more details about occurring errors as well as properly checking the JSON-File for the repository and providing more details about JSON errors
* [#710](https://github.com/Icinga/icinga-powershell-framework/pull/710) Fixes various console errors while running Icinga for Windows outside of an administrative shell
* [#713](https://github.com/Icinga/icinga-powershell-framework/pull/713) Fixes Icinga for Windows REST-Api which fails during certificate auth handling while running as `NT Authority\NetworkService`
* [#714](https://github.com/Icinga/icinga-powershell-framework/pull/714) Fixes missing service environment information during initial setup of Icinga for Windows v1.12 on some systems

View file

@ -27,6 +27,12 @@ function New-IcingaEnvironmentVariable()
$Global:Icinga.Private.Add('Documentation', @{ });
$Global:Icinga.Private.Add('Timers', @{ });
$Global:Icinga.Private.Add('ProgressStatus', @{ });
$Global:Icinga.Private.Add(
'RepositoryStatus',
@{
'FailedRepositories' = @{ };
}
);
$Global:Icinga.Private.Add(
'Scheduler',

View file

@ -0,0 +1,32 @@
function Add-IcingaRepositoryErrorState()
{
param (
[string]$Repository = $null
);
if ([string]::IsNullOrEmpty($Repository)) {
return;
}
if ($Global:Icinga -eq $null) {
return;
}
if ($Global:Icinga.Contains('Private') -eq $FALSE) {
return;
}
if ($Global:Icinga.Private.Contains('RepositoryStatus') -eq $FALSE) {
return;
}
if ($Global:Icinga.Private.RepositoryStatus.Contains('FailedRepositories') -eq $FALSE) {
return;
}
if ($Global:Icinga.Private.RepositoryStatus.FailedRepositories.ContainsKey($Repository)) {
return;
}
$Global:Icinga.Private.RepositoryStatus.FailedRepositories.Add($Repository, $TRUE);
}

View file

@ -0,0 +1,16 @@
function Clear-IcingaRepositoryErrorState()
{
if ($Global:Icinga -eq $null) {
return;
}
if ($Global:Icinga.Contains('Private') -eq $FALSE) {
return;
}
if ($Global:Icinga.Private.Contains('RepositoryStatus') -eq $FALSE) {
return;
}
$Global:Icinga.Private.RepositoryStatus.FailedRepositories = @{ };
}

View file

@ -15,6 +15,9 @@ function Get-IcingaComponentList()
$SearchList | Add-Member -MemberType NoteProperty -Name 'Repos' -Value @();
$SearchList | Add-Member -MemberType NoteProperty -Name 'Components' -Value @{ };
# Ensure our error list is cleared at this point
Clear-IcingaRepositoryErrorState;
foreach ($entry in $Repositories) {
$RepoContent = Read-IcingaRepositoryFile -Name $entry.Name;

View file

@ -7,6 +7,9 @@ function Get-IcingaInstallation()
Set-IcingaServiceEnvironment;
# Ensure our error list is cleared at this point
Clear-IcingaRepositoryErrorState;
[hashtable]$InstalledComponents = @{ };
$PowerShellModules = Get-Module -ListAvailable;

View file

@ -1,12 +1,13 @@
function Install-IcingaComponent()
{
param (
[string]$Name = $null,
[string]$Version = $null,
[switch]$Release = $FALSE,
[switch]$Snapshot = $FALSE,
[switch]$Confirm = $FALSE,
[switch]$Force = $FALSE
[string]$Name = $null,
[string]$Version = $null,
[switch]$Release = $FALSE,
[switch]$Snapshot = $FALSE,
[switch]$Confirm = $FALSE,
[switch]$Force = $FALSE,
[switch]$KeepRepoErrors = $FALSE
);
if ([string]::IsNullOrEmpty($Name)) {
@ -14,6 +15,10 @@ function Install-IcingaComponent()
return;
}
if ($KeepRepoErrors -eq $FALSE) {
Clear-IcingaRepositoryErrorState;
}
# Branch snapshot versions will have '/' inside their name
if ($Name.Contains('/') -And $Snapshot) {
$Name = $Name.Split('/')[0];

View file

@ -1,8 +1,9 @@
function Read-IcingaRepositoryFile()
{
param (
[string]$Name = $null,
[switch]$TryAlternate = $FALSE
[string]$Name = $null,
[switch]$TryAlternate = $FALSE,
[switch]$PrintRetryMsg = $FALSE
);
if ([string]::IsNullOrEmpty($Name)) {
@ -10,6 +11,10 @@ function Read-IcingaRepositoryFile()
return $null;
}
if ((Test-IcingaRepositoryErrorState -Repository $Name) -And $TryAlternate -eq $FALSE) {
return $null;
}
$Name = $Name.Replace('.', '-');
$Repository = Get-IcingaPowerShellConfig -Path ([string]::Format('Framework.Repository.Repositories.{0}', $Name));
@ -22,6 +27,10 @@ function Read-IcingaRepositoryFile()
$RepoPath = $null;
$Content = $null;
if ($PrintRetryMsg) {
Write-IcingaConsoleNotice 'Unable to fetch Icinga for Windows repository information for repository "{0}" from provided location. Trying different lookup by adding "ifw.repo.json" to the end of the remote path.' -Objects $Name;
}
if ([string]::IsNullOrEmpty($Repository.LocalPath) -eq $FALSE -And (Test-Path -Path $Repository.LocalPath)) {
$RepoPath = $Repository.LocalPath;
} elseif ([string]::IsNullOrEmpty($Repository.RemotePath) -eq $FALSE -And (Test-Path -Path $Repository.RemotePath)) {
@ -29,17 +38,19 @@ function Read-IcingaRepositoryFile()
}
if ([string]::IsNullOrEmpty($RepoPath) -eq $FALSE -And (Test-Path -Path $RepoPath)) {
if ([IO.Path]::GetExtension($RepoPath).ToLower() -ne '.json' -And $TryAlternate -eq $FALSE) {
return (Read-IcingaRepositoryFile -Name $Name -TryAlternate);
} elseif ([IO.Path]::GetExtension($RepoPath).ToLower() -ne '.json' -And $TryAlternat) {
Write-IcingaConsoleError 'Unable to read repository file from "{0}" for repository "{1}". No "ifw.repo.json" was found at defined location' -Objects $RepoPath, $Name;
return $null;
}
if ($TryAlternate) {
$RepoPath = Join-Path $RepoPath -ChildPath 'ifw.repo.json';
}
if ([IO.Path]::GetExtension($RepoPath).ToLower() -ne '.json' -And $TryAlternate -eq $FALSE) {
return (Read-IcingaRepositoryFile -Name $Name -TryAlternate);
} elseif ([IO.Path]::GetExtension($RepoPath).ToLower() -ne '.json' -And $TryAlternate) {
Write-IcingaConsoleError 'Unable to read repository file from "{0}" for repository "{1}". No "ifw.repo.json" was found at defined location' -Objects $RepoPath, $Name;
Add-IcingaRepositoryErrorState -Repository $Name;
return $null;
}
$Content = Get-Content -Path $RepoPath -Raw;
} else {
try {
@ -52,19 +63,26 @@ function Read-IcingaRepositoryFile()
$WebContent = Invoke-IcingaWebRequest -UseBasicParsing -Uri $RepoPath;
if ($null -ne $WebContent) {
if ($WebContent.RawContent.Contains('application/octet-stream')) {
$Content = [System.Text.Encoding]::UTF8.GetString($WebContent.Content)
if ((Test-PSCustomObjectMember -PSObject $WebContent -Name 'RawContent') -Or (Test-PSCustomObjectMember -PSObject $WebContent -Name 'Content')) {
if ((Test-PSCustomObjectMember -PSObject $WebContent -Name 'RawContent') -And $WebContent.RawContent.Contains('application/octet-stream')) {
$Content = [System.Text.Encoding]::UTF8.GetString($WebContent.Content)
} else {
$Content = $WebContent.Content;
}
} else {
$Content = $WebContent.Content;
if ($TryAlternate -eq $FALSE) {
return (Read-IcingaRepositoryFile -Name $Name -TryAlternate -PrintRetryMsg);
}
$Content = $null;
}
} else {
if ($TryAlternate -eq $FALSE) {
return (Read-IcingaRepositoryFile -Name $Name -TryAlternate);
return (Read-IcingaRepositoryFile -Name $Name -TryAlternate -PrintRetryMsg);
}
}
} catch {
if ($TryAlternate -eq $FALSE) {
return (Read-IcingaRepositoryFile -Name $Name -TryAlternate);
return (Read-IcingaRepositoryFile -Name $Name -TryAlternate -PrintRetryMsg);
} else {
Write-IcingaConsoleError 'Unable to resolve repository URL "{0}" for repository "{1}": {2}' -Objects $Repository.RemotePath, $Name, $_.Exception.Message;
return $null;
@ -74,15 +92,21 @@ function Read-IcingaRepositoryFile()
if ($null -eq $Content) {
Write-IcingaConsoleError 'Unable to fetch data for repository "{0}" from any configured location' -Objects $Name;
Add-IcingaRepositoryErrorState -Repository $Name;
return $null;
}
try {
$RepositoryObject = $null;
if (Test-IcingaJSONObject -InputObject $Content) {
$RepositoryObject = ConvertFrom-Json -InputObject $Content -ErrorAction Stop;
} catch {
} else {
Write-IcingaConsoleError 'Failed to convert retreived content from repository "{0}" with location "{1}" to JSON' -Objects $Name, $Repository.RemotePath
if ($TryAlternate -eq $FALSE) {
return (Read-IcingaRepositoryFile -Name $Name -TryAlternate);
return (Read-IcingaRepositoryFile -Name $Name -TryAlternate -PrintRetryMsg);
}
Add-IcingaRepositoryErrorState -Repository $Name;
}
return $RepositoryObject;

View file

@ -30,6 +30,9 @@ function Search-IcingaRepository()
$SearchList = New-Object -TypeName PSObject;
$SearchList | Add-Member -MemberType NoteProperty -Name 'Repos' -Value @();
# Ensure our error list is cleared at this point
Clear-IcingaRepositoryErrorState;
foreach ($entry in $Repositories) {
$RepoContent = Read-IcingaRepositoryFile -Name $entry.Name;

View file

@ -0,0 +1,51 @@
function Test-IcingaJSONObject()
{
param (
[string]$InputObject = $null
);
if ([string]::IsNullOrEmpty($InputObject)) {
return $FALSE;
}
try {
$JSONContent = ConvertFrom-Json -InputObject $InputObject -ErrorAction Stop;
return $TRUE;
} catch {
[string]$ErrMsg = $_.Exception.Message;
if ($ErrMsg.Contains('(') -And $ErrMsg.Contains(')')) {
try {
[int]$ErrLocation = $ErrMsg.Substring($ErrMsg.IndexOf('(') + 1, $ErrMsg.IndexOf(')') - $ErrMsg.IndexOf('(') - 1) - 1;
[string]$ExceptionMsg = $ErrMsg.Substring(0, $ErrMsg.IndexOf(')') + 1);
[string]$ErrOutput = $InputObject.Substring(0, $ErrLocation);
[array]$ErrArray = $ErrOutput.Split("`n");
[string]$Indentation = '';
[string]$ErrLine = '';
[int]$tmp = 0;
foreach ($entry in $ErrArray) {
$tmp += 1;
}
foreach ($character in ([string]($ErrArray[$ErrArray.Count - 2])).ToCharArray()) {
if ([string]::IsNullOrEmpty($character) -Or $character -eq ' ') {
$Indentation += ' ';
} else {
$ErrLine += '^';
}
}
$ErrOutput = [string]::Format('{0}{1}{2}{3}', $ErrOutput, (New-IcingaNewLine), $Indentation, $ErrLine);
Write-IcingaConsoleError 'Failed to parse JSON object. Exception: {0}{1}{2}' -Objects $ExceptionMsg, (New-IcingaNewLine), $ErrOutput;
return $FALSE;
} catch {
Write-IcingaConsoleError 'Failed to parse JSON object: {0}' -Objects $ErrMsg;
return $FALSE;
}
}
}
return $TRUE;
}

View file

@ -0,0 +1,32 @@
function Test-IcingaRepositoryErrorState()
{
param (
[string]$Repository
);
if ([string]::IsNullOrEmpty($Repository)) {
return $FALSE;
}
if ($Global:Icinga -eq $null) {
return $FALSE;
}
if ($Global:Icinga.Contains('Private') -eq $FALSE) {
return $FALSE;
}
if ($Global:Icinga.Private.Contains('RepositoryStatus') -eq $FALSE) {
return $FALSE;
}
if ($Global:Icinga.Private.RepositoryStatus.Contains('FailedRepositories') -eq $FALSE) {
return $FALSE;
}
if ($Global:Icinga.Private.RepositoryStatus.FailedRepositories.ContainsKey($Repository) -eq $FALSE) {
return $FALSE;
}
return $TRUE;
}

View file

@ -73,7 +73,7 @@ function Update-Icinga()
$UpdateJEA = $TRUE;
}
Install-IcingaComponent -Name $entry -Version $NewVersion -Release:$Release -Snapshot:$Snapshot -Confirm:$Confirm -Force:$Force;
Install-IcingaComponent -Name $entry -Version $NewVersion -Release:$Release -Snapshot:$Snapshot -Confirm:$Confirm -Force:$Force -KeepRepoErrors;
}
# Update JEA profile if JEA is enabled once the update is complete

View file

@ -1,6 +1,6 @@
function Test-PSCustomObjectMember()
{
param(
param (
$PSObject,
$Name
);
@ -9,5 +9,11 @@ function Test-PSCustomObjectMember()
return $FALSE;
}
# Lets make sure we also test for hashtables in case our object is a hashtable
# instead of a PSCustomObject
if ($PSObject -Is [hashtable]) {
return ([bool]($PSObject.ContainsKey($Name)));
}
return ([bool]($PSObject.PSObject.Properties.Name -eq $Name));
}