Skip to content

Commit 9252036

Browse files
committed
Fixes repository error handling and JSON error handling
1 parent 78d8714 commit 9252036

13 files changed

+205
-23
lines changed

doc/100-General/10-Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Released closed milestones can be found on [GitHub](https://github.com/Icinga/ic
2020
* [#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
2121
* [#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
2222
* [#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
23+
* [#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
2324
* [#710](https://github.com/Icinga/icinga-powershell-framework/pull/710) Fixes various console errors while running Icinga for Windows outside of an administrative shell
2425
* [#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`
2526
* [#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

lib/core/framework/New-IcingaEnvironmentVariable.psm1

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ function New-IcingaEnvironmentVariable()
2727
$Global:Icinga.Private.Add('Documentation', @{ });
2828
$Global:Icinga.Private.Add('Timers', @{ });
2929
$Global:Icinga.Private.Add('ProgressStatus', @{ });
30+
$Global:Icinga.Private.Add(
31+
'RepositoryStatus',
32+
@{
33+
'FailedRepositories' = @{ };
34+
}
35+
);
3036

3137
$Global:Icinga.Private.Add(
3238
'Scheduler',
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
function Add-IcingaRepositoryErrorState()
2+
{
3+
param (
4+
[string]$Repository = $null
5+
);
6+
7+
if ([string]::IsNullOrEmpty($Repository)) {
8+
return;
9+
}
10+
11+
if ($Global:Icinga -eq $null) {
12+
return;
13+
}
14+
15+
if ($Global:Icinga.Contains('Private') -eq $FALSE) {
16+
return;
17+
}
18+
19+
if ($Global:Icinga.Private.Contains('RepositoryStatus') -eq $FALSE) {
20+
return;
21+
}
22+
23+
if ($Global:Icinga.Private.RepositoryStatus.Contains('FailedRepositories') -eq $FALSE) {
24+
return;
25+
}
26+
27+
if ($Global:Icinga.Private.RepositoryStatus.FailedRepositories.ContainsKey($Repository)) {
28+
return;
29+
}
30+
31+
$Global:Icinga.Private.RepositoryStatus.FailedRepositories.Add($Repository, $TRUE);
32+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
function Clear-IcingaRepositoryErrorState()
2+
{
3+
if ($Global:Icinga -eq $null) {
4+
return;
5+
}
6+
7+
if ($Global:Icinga.Contains('Private') -eq $FALSE) {
8+
return;
9+
}
10+
11+
if ($Global:Icinga.Private.Contains('RepositoryStatus') -eq $FALSE) {
12+
return;
13+
}
14+
15+
$Global:Icinga.Private.RepositoryStatus.FailedRepositories = @{ };
16+
}

lib/core/repository/Get-IcingaComponentList.psm1

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ function Get-IcingaComponentList()
1515
$SearchList | Add-Member -MemberType NoteProperty -Name 'Repos' -Value @();
1616
$SearchList | Add-Member -MemberType NoteProperty -Name 'Components' -Value @{ };
1717

18+
# Ensure our error list is cleared at this point
19+
Clear-IcingaRepositoryErrorState;
20+
1821
foreach ($entry in $Repositories) {
1922
$RepoContent = Read-IcingaRepositoryFile -Name $entry.Name;
2023

lib/core/repository/Get-IcingaInstallation.psm1

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ function Get-IcingaInstallation()
77

88
Set-IcingaServiceEnvironment;
99

10+
# Ensure our error list is cleared at this point
11+
Clear-IcingaRepositoryErrorState;
12+
1013
[hashtable]$InstalledComponents = @{ };
1114

1215
$PowerShellModules = Get-Module -ListAvailable;

lib/core/repository/Install-IcingaComponent.psm1

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
function Install-IcingaComponent()
22
{
33
param (
4-
[string]$Name = $null,
5-
[string]$Version = $null,
6-
[switch]$Release = $FALSE,
7-
[switch]$Snapshot = $FALSE,
8-
[switch]$Confirm = $FALSE,
9-
[switch]$Force = $FALSE
4+
[string]$Name = $null,
5+
[string]$Version = $null,
6+
[switch]$Release = $FALSE,
7+
[switch]$Snapshot = $FALSE,
8+
[switch]$Confirm = $FALSE,
9+
[switch]$Force = $FALSE,
10+
[switch]$KeepRepoErrors = $FALSE
1011
);
1112

1213
if ([string]::IsNullOrEmpty($Name)) {
1314
Write-IcingaConsoleError 'You have to provide a component name';
1415
return;
1516
}
1617

18+
if ($KeepRepoErrors -eq $FALSE) {
19+
Clear-IcingaRepositoryErrorState;
20+
}
21+
1722
# Branch snapshot versions will have '/' inside their name
1823
if ($Name.Contains('/') -And $Snapshot) {
1924
$Name = $Name.Split('/')[0];

lib/core/repository/Read-IcingaRepositoryFile.psm1

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
function Read-IcingaRepositoryFile()
22
{
33
param (
4-
[string]$Name = $null,
5-
[switch]$TryAlternate = $FALSE
4+
[string]$Name = $null,
5+
[switch]$TryAlternate = $FALSE,
6+
[switch]$PrintRetryMsg = $FALSE
67
);
78

89
if ([string]::IsNullOrEmpty($Name)) {
910
Write-IcingaConsoleError 'You have to provide a name for the repository';
1011
return $null;
1112
}
1213

14+
if ((Test-IcingaRepositoryErrorState -Repository $Name) -And $TryAlternate -eq $FALSE) {
15+
return $null;
16+
}
17+
1318
$Name = $Name.Replace('.', '-');
1419

1520
$Repository = Get-IcingaPowerShellConfig -Path ([string]::Format('Framework.Repository.Repositories.{0}', $Name));
@@ -22,24 +27,30 @@ function Read-IcingaRepositoryFile()
2227
$RepoPath = $null;
2328
$Content = $null;
2429

30+
if ($PrintRetryMsg) {
31+
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;
32+
}
33+
2534
if ([string]::IsNullOrEmpty($Repository.LocalPath) -eq $FALSE -And (Test-Path -Path $Repository.LocalPath)) {
2635
$RepoPath = $Repository.LocalPath;
2736
} elseif ([string]::IsNullOrEmpty($Repository.RemotePath) -eq $FALSE -And (Test-Path -Path $Repository.RemotePath)) {
2837
$RepoPath = $Repository.RemotePath;
2938
}
3039

3140
if ([string]::IsNullOrEmpty($RepoPath) -eq $FALSE -And (Test-Path -Path $RepoPath)) {
41+
42+
if ($TryAlternate) {
43+
$RepoPath = Join-Path $RepoPath -ChildPath 'ifw.repo.json';
44+
}
45+
3246
if ([IO.Path]::GetExtension($RepoPath).ToLower() -ne '.json' -And $TryAlternate -eq $FALSE) {
3347
return (Read-IcingaRepositoryFile -Name $Name -TryAlternate);
34-
} elseif ([IO.Path]::GetExtension($RepoPath).ToLower() -ne '.json' -And $TryAlternat) {
48+
} elseif ([IO.Path]::GetExtension($RepoPath).ToLower() -ne '.json' -And $TryAlternate) {
3549
Write-IcingaConsoleError 'Unable to read repository file from "{0}" for repository "{1}". No "ifw.repo.json" was found at defined location' -Objects $RepoPath, $Name;
50+
Add-IcingaRepositoryErrorState -Repository $Name;
3651
return $null;
3752
}
3853

39-
if ($TryAlternate) {
40-
$RepoPath = Join-Path $RepoPath -ChildPath 'ifw.repo.json';
41-
}
42-
4354
$Content = Get-Content -Path $RepoPath -Raw;
4455
} else {
4556
try {
@@ -52,19 +63,26 @@ function Read-IcingaRepositoryFile()
5263
$WebContent = Invoke-IcingaWebRequest -UseBasicParsing -Uri $RepoPath;
5364

5465
if ($null -ne $WebContent) {
55-
if ($WebContent.RawContent.Contains('application/octet-stream')) {
56-
$Content = [System.Text.Encoding]::UTF8.GetString($WebContent.Content)
66+
if ((Test-PSCustomObjectMember -PSObject $WebContent -Name 'RawContent') -Or (Test-PSCustomObjectMember -PSObject $WebContent -Name 'Content')) {
67+
if ((Test-PSCustomObjectMember -PSObject $WebContent -Name 'RawContent') -And $WebContent.RawContent.Contains('application/octet-stream')) {
68+
$Content = [System.Text.Encoding]::UTF8.GetString($WebContent.Content)
69+
} else {
70+
$Content = $WebContent.Content;
71+
}
5772
} else {
58-
$Content = $WebContent.Content;
73+
if ($TryAlternate -eq $FALSE) {
74+
return (Read-IcingaRepositoryFile -Name $Name -TryAlternate -PrintRetryMsg);
75+
}
76+
$Content = $null;
5977
}
6078
} else {
6179
if ($TryAlternate -eq $FALSE) {
62-
return (Read-IcingaRepositoryFile -Name $Name -TryAlternate);
80+
return (Read-IcingaRepositoryFile -Name $Name -TryAlternate -PrintRetryMsg);
6381
}
6482
}
6583
} catch {
6684
if ($TryAlternate -eq $FALSE) {
67-
return (Read-IcingaRepositoryFile -Name $Name -TryAlternate);
85+
return (Read-IcingaRepositoryFile -Name $Name -TryAlternate -PrintRetryMsg);
6886
} else {
6987
Write-IcingaConsoleError 'Unable to resolve repository URL "{0}" for repository "{1}": {2}' -Objects $Repository.RemotePath, $Name, $_.Exception.Message;
7088
return $null;
@@ -74,15 +92,21 @@ function Read-IcingaRepositoryFile()
7492

7593
if ($null -eq $Content) {
7694
Write-IcingaConsoleError 'Unable to fetch data for repository "{0}" from any configured location' -Objects $Name;
95+
Add-IcingaRepositoryErrorState -Repository $Name;
7796
return $null;
7897
}
7998

80-
try {
99+
$RepositoryObject = $null;
100+
101+
if (Test-IcingaJSONObject -InputObject $Content) {
81102
$RepositoryObject = ConvertFrom-Json -InputObject $Content -ErrorAction Stop;
82-
} catch {
103+
} else {
104+
Write-IcingaConsoleError 'Failed to convert retreived content from repository "{0}" with location "{1}" to JSON' -Objects $Name, $Repository.RemotePath
83105
if ($TryAlternate -eq $FALSE) {
84-
return (Read-IcingaRepositoryFile -Name $Name -TryAlternate);
106+
return (Read-IcingaRepositoryFile -Name $Name -TryAlternate -PrintRetryMsg);
85107
}
108+
109+
Add-IcingaRepositoryErrorState -Repository $Name;
86110
}
87111

88112
return $RepositoryObject;

lib/core/repository/Search-IcingaRepository.psm1

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ function Search-IcingaRepository()
3030
$SearchList = New-Object -TypeName PSObject;
3131
$SearchList | Add-Member -MemberType NoteProperty -Name 'Repos' -Value @();
3232

33+
# Ensure our error list is cleared at this point
34+
Clear-IcingaRepositoryErrorState;
35+
3336
foreach ($entry in $Repositories) {
3437
$RepoContent = Read-IcingaRepositoryFile -Name $entry.Name;
3538

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
function Test-IcingaJSONObject()
2+
{
3+
param (
4+
[string]$InputObject = $null
5+
);
6+
7+
if ([string]::IsNullOrEmpty($InputObject)) {
8+
return $FALSE;
9+
}
10+
11+
try {
12+
$JSONContent = ConvertFrom-Json -InputObject $InputObject -ErrorAction Stop;
13+
return $TRUE;
14+
} catch {
15+
[string]$ErrMsg = $_.Exception.Message;
16+
17+
if ($ErrMsg.Contains('(') -And $ErrMsg.Contains(')')) {
18+
try {
19+
[int]$ErrLocation = $ErrMsg.Substring($ErrMsg.IndexOf('(') + 1, $ErrMsg.IndexOf(')') - $ErrMsg.IndexOf('(') - 1) - 1;
20+
[string]$ExceptionMsg = $ErrMsg.Substring(0, $ErrMsg.IndexOf(')') + 1);
21+
[string]$ErrOutput = $InputObject.Substring(0, $ErrLocation);
22+
[array]$ErrArray = $ErrOutput.Split("`n");
23+
[string]$Indentation = '';
24+
[string]$ErrLine = '';
25+
26+
[int]$tmp = 0;
27+
foreach ($entry in $ErrArray) {
28+
$tmp += 1;
29+
}
30+
31+
foreach ($character in ([string]($ErrArray[$ErrArray.Count - 2])).ToCharArray()) {
32+
if ([string]::IsNullOrEmpty($character) -Or $character -eq ' ') {
33+
$Indentation += ' ';
34+
} else {
35+
$ErrLine += '^';
36+
}
37+
}
38+
39+
$ErrOutput = [string]::Format('{0}{1}{2}{3}', $ErrOutput, (New-IcingaNewLine), $Indentation, $ErrLine);
40+
41+
Write-IcingaConsoleError 'Failed to parse JSON object. Exception: {0}{1}{2}' -Objects $ExceptionMsg, (New-IcingaNewLine), $ErrOutput;
42+
return $FALSE;
43+
} catch {
44+
Write-IcingaConsoleError 'Failed to parse JSON object: {0}' -Objects $ErrMsg;
45+
return $FALSE;
46+
}
47+
}
48+
}
49+
50+
return $TRUE;
51+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
function Test-IcingaRepositoryErrorState()
2+
{
3+
param (
4+
[string]$Repository
5+
);
6+
7+
if ([string]::IsNullOrEmpty($Repository)) {
8+
return $FALSE;
9+
}
10+
11+
if ($Global:Icinga -eq $null) {
12+
return $FALSE;
13+
}
14+
15+
if ($Global:Icinga.Contains('Private') -eq $FALSE) {
16+
return $FALSE;
17+
}
18+
19+
if ($Global:Icinga.Private.Contains('RepositoryStatus') -eq $FALSE) {
20+
return $FALSE;
21+
}
22+
23+
if ($Global:Icinga.Private.RepositoryStatus.Contains('FailedRepositories') -eq $FALSE) {
24+
return $FALSE;
25+
}
26+
27+
if ($Global:Icinga.Private.RepositoryStatus.FailedRepositories.ContainsKey($Repository) -eq $FALSE) {
28+
return $FALSE;
29+
}
30+
31+
return $TRUE;
32+
}

lib/core/repository/Update-Icinga.psm1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ function Update-Icinga()
7373
$UpdateJEA = $TRUE;
7474
}
7575

76-
Install-IcingaComponent -Name $entry -Version $NewVersion -Release:$Release -Snapshot:$Snapshot -Confirm:$Confirm -Force:$Force;
76+
Install-IcingaComponent -Name $entry -Version $NewVersion -Release:$Release -Snapshot:$Snapshot -Confirm:$Confirm -Force:$Force -KeepRepoErrors;
7777
}
7878

7979
# Update JEA profile if JEA is enabled once the update is complete
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
function Test-PSCustomObjectMember()
22
{
3-
param(
3+
param (
44
$PSObject,
55
$Name
66
);
@@ -9,5 +9,11 @@ function Test-PSCustomObjectMember()
99
return $FALSE;
1010
}
1111

12+
# Lets make sure we also test for hashtables in case our object is a hashtable
13+
# instead of a PSCustomObject
14+
if ($PSObject -Is [hashtable]) {
15+
return ([bool]($PSObject.ContainsKey($Name)));
16+
}
17+
1218
return ([bool]($PSObject.PSObject.Properties.Name -eq $Name));
1319
}

0 commit comments

Comments
 (0)