Skip to content

Commit

Permalink
Fixes repository error handling and JSON error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
LordHepipud committed Apr 9, 2024
1 parent 78d8714 commit 9252036
Show file tree
Hide file tree
Showing 13 changed files with 205 additions and 23 deletions.
1 change: 1 addition & 0 deletions doc/100-General/10-Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions lib/core/framework/New-IcingaEnvironmentVariable.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
32 changes: 32 additions & 0 deletions lib/core/repository/Add-IcingaRepositoryErrorState.psm1
Original file line number Diff line number Diff line change
@@ -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);
}
16 changes: 16 additions & 0 deletions lib/core/repository/Clear-IcingaRepositoryErrorState.psm1
Original file line number Diff line number Diff line change
@@ -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 = @{ };
}
3 changes: 3 additions & 0 deletions lib/core/repository/Get-IcingaComponentList.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
3 changes: 3 additions & 0 deletions lib/core/repository/Get-IcingaInstallation.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
17 changes: 11 additions & 6 deletions lib/core/repository/Install-IcingaComponent.psm1
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
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)) {
Write-IcingaConsoleError 'You have to provide a component name';
return;
}

if ($KeepRepoErrors -eq $FALSE) {
Clear-IcingaRepositoryErrorState;
}

# Branch snapshot versions will have '/' inside their name
if ($Name.Contains('/') -And $Snapshot) {
$Name = $Name.Split('/')[0];
Expand Down
54 changes: 39 additions & 15 deletions lib/core/repository/Read-IcingaRepositoryFile.psm1
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
function Read-IcingaRepositoryFile()
{
param (
[string]$Name = $null,
[switch]$TryAlternate = $FALSE
[string]$Name = $null,
[switch]$TryAlternate = $FALSE,
[switch]$PrintRetryMsg = $FALSE
);

if ([string]::IsNullOrEmpty($Name)) {
Write-IcingaConsoleError 'You have to provide a name for the repository';
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));
Expand All @@ -22,24 +27,30 @@ 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)) {
$RepoPath = $Repository.RemotePath;
}

if ([string]::IsNullOrEmpty($RepoPath) -eq $FALSE -And (Test-Path -Path $RepoPath)) {

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 $TryAlternat) {
} 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;
}

if ($TryAlternate) {
$RepoPath = Join-Path $RepoPath -ChildPath 'ifw.repo.json';
}

$Content = Get-Content -Path $RepoPath -Raw;
} else {
try {
Expand All @@ -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;
Expand All @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions lib/core/repository/Search-IcingaRepository.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
51 changes: 51 additions & 0 deletions lib/core/repository/Test-IcingaJSONObject.psm1
Original file line number Diff line number Diff line change
@@ -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;
}
32 changes: 32 additions & 0 deletions lib/core/repository/Test-IcingaRepositoryErrorState.psm1
Original file line number Diff line number Diff line change
@@ -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;
}
2 changes: 1 addition & 1 deletion lib/core/repository/Update-Icinga.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion lib/core/tools/Test-PSCustomObjectMember.psm1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
function Test-PSCustomObjectMember()
{
param(
param (
$PSObject,
$Name
);
Expand All @@ -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));
}

0 comments on commit 9252036

Please sign in to comment.