diff --git a/resources/PSScript/psscript.ps1 b/resources/PSScript/psscript.ps1 index 2b395dce1..e3d562abb 100644 --- a/resources/PSScript/psscript.ps1 +++ b/resources/PSScript/psscript.ps1 @@ -138,17 +138,32 @@ try { $asyncResult = $ps.BeginInvoke() while (-not $asyncResult.IsCompleted) { Write-TraceQueue - + Start-Sleep -Milliseconds 100 } + + if ($ps.InvocationStateInfo.State -eq 'Failed') { + $record = $ps.InvocationStateInfo.Reason.ErrorRecord + $message = if ($null -ne $record) { + "Script failed with terminating error at line {0} for statement ``{1}`` - {2}" -f @( + $record.InvocationInfo.ScriptLineNumber, + $record.InvocationInfo.Line, + $record.Exception.ToString() + ) + } else { + "Script failed with terminating error: $($ps.InvocationStateInfo.Reason)" + } + Write-DscTrace -Now -Level Error -Message $message + exit 1 + } + $outputCollection = $ps.EndInvoke($asyncResult) Write-TraceQueue if ($ps.HadErrors) { - # If there are any errors, we will exit with an error code - Write-DscTrace -Now -Level Error -Message 'Errors occurred during script execution.' - exit 1 + # Errors can be non-terminating, so we just write a warning and continue + Write-DscTrace -Now -Level Debug -Message 'Non-terminating errors occurred during script execution.' } foreach ($output in $outputCollection) { @@ -156,7 +171,7 @@ try { } } catch { - Write-DscTrace -Now -Level Error -Message $_ + Write-DscTrace -Now -Level Error -Message ($_ | Format-List -Force * | Out-String) exit 1 } finally { diff --git a/resources/PSScript/psscript.tests.ps1 b/resources/PSScript/psscript.tests.ps1 index 4406ccae5..b2d91786d 100644 --- a/resources/PSScript/psscript.tests.ps1 +++ b/resources/PSScript/psscript.tests.ps1 @@ -108,7 +108,8 @@ Describe 'Tests for PSScript resource' { $result = dsc resource test -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 2 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) $result | Should -BeNullOrEmpty -Because "Test operation should return an error" - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*Test operation did not return a single boolean value.*' + $errorLog = Get-Content $TestDrive/error.txt -Raw + $errorLog | Should -BeLike '*ERROR*:*Test operation did not return a single boolean value.*' -Because $errorLog } It 'Test operation returns error for multiple boolean results for ' -TestCases $testCases { @@ -122,7 +123,8 @@ Describe 'Tests for PSScript resource' { $result = dsc resource test -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 2 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) $result | Should -BeNullOrEmpty -Because "Test operation should return an error" - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*Test operation did not return a single boolean value.*' + $errorLog = Get-Content $TestDrive/error.txt -Raw + $errorLog | Should -BeLike '*ERROR*:*Test operation did not return a single boolean value.*' -Because $errorLog } It 'Empty SetScript is ignored for ' -TestCases $testCases { @@ -187,8 +189,9 @@ Describe 'Tests for PSScript resource' { $result = dsc resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) $result.actualState.output.Count | Should -Be 0 -Because ($result | ConvertTo-Json | Out-String) - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*WARN*:*This is a warning*' - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*WARN*:*This is second warning*' + $errorLog = Get-Content $TestDrive/error.txt -Raw + $errorLog | Should -BeLike '*WARN*:*This is a warning*' -Because $errorLog + $errorLog | Should -BeLike '*WARN*:*This is second warning*' -Because $errorLog } It 'Write-Error shows up as error traces for ' -TestCases $testCases { @@ -201,8 +204,9 @@ Describe 'Tests for PSScript resource' { $result = dsc resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 2 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) + $errorLog = Get-Content $TestDrive/error.txt -Raw $result.actualState.output.Count | Should -Be 0 -Because ($result | ConvertTo-Json | Out-String) - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*ERROR*:*This is an error*' + $errorLog | Should -BeLike '*ERROR*:*This is an error*' -Because $errorLog } It 'Write-Verbose shows up as info traces for ' -TestCases $testCases { @@ -214,8 +218,9 @@ Describe 'Tests for PSScript resource' { '@ $result = dsc -l info resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) + $errorLog = Get-Content $TestDrive/error.txt -Raw $result.actualState.output.Count | Should -Be 0 -Because ($result | ConvertTo-Json | Out-String) - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*INFO*:*This is a verbose message*' + $errorLog | Should -BeLike '*INFO*:*This is a verbose message*' -Because $errorLog } It 'Write-Debug shows up as debug traces for ' -TestCases $testCases { @@ -227,8 +232,9 @@ Describe 'Tests for PSScript resource' { '@ $result = dsc -l debug resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) + $errorLog = Get-Content $TestDrive/error.txt -Raw $result.actualState.output.Count | Should -Be 0 -Because ($result | ConvertTo-Json | Out-String) - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*DEBUG*:*This is a debug message*' + $errorLog | Should -BeLike '*DEBUG*:*This is a debug message*' -Because $errorLog } It 'Write-Information shows up as trace traces for ' -TestCases $testCases { @@ -241,8 +247,9 @@ Describe 'Tests for PSScript resource' { '@ $result = dsc -l trace resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) + $errorLog = Get-Content $TestDrive/error.txt -Raw $result.actualState.output.Count | Should -Be 0 -Because ($result | ConvertTo-Json | Out-String) - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*TRACE*:*This is an information message*' + $errorLog | Should -BeLike '*TRACE*:*This is an information message*' -Because $errorLog } It 'A thrown exception results in an error for ' -TestCases $testCases { @@ -254,8 +261,9 @@ Describe 'Tests for PSScript resource' { '@ $result = dsc resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 2 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) + $errorLog = Get-Content $TestDrive/error.txt -Raw $result.actualState.output.Count | Should -Be 0 -Because ($result | ConvertTo-Json | Out-String) - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*ERROR*:*This is an exception*' + $errorLog | Should -BeLike '*ERROR*:*This is an exception*' -Because $errorLog } It 'Sample config works' { @@ -291,8 +299,9 @@ Describe 'Tests for PSScript resource' { input: "This is a string" '@ dsc resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json - $LASTEXITCODE | Should -Be 2 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*ERROR*:*Input was provided but script does not have a parameter to accept input.*' + $errorLog = Get-Content $TestDrive/error.txt -Raw + $LASTEXITCODE | Should -Be 2 -Because $errorLog + $errorLog | Should -BeLike '*ERROR*:*Input was provided but script does not have a parameter to accept input.*' -Because $errorLog } It 'Param without input is an error for ' -TestCases $testCases { @@ -304,8 +313,9 @@ Describe 'Tests for PSScript resource' { "This should fail" '@ dsc resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json - $LASTEXITCODE | Should -Be 2 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike "*ERROR*:*Script has a parameter 'inputObj' but no input was provided.*" + $errorLog = Get-Content $TestDrive/error.txt -Raw + $LASTEXITCODE | Should -Be 2 -Because $errorLog + $errorLog | Should -BeLike "*ERROR*:*Script has a parameter 'inputObj' but no input was provided.*" -Because $errorLog } It 'Write-Host results in an info message for ' -TestCases $testCases { @@ -319,9 +329,39 @@ Describe 'Tests for PSScript resource' { '@ $result = dsc -l trace resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) + $errorLog = Get-Content $TestDrive/error.txt -Raw + $result.actualState.output.Count | Should -Be 0 -Because ($result | ConvertTo-Json | Out-String) + $errorLog | Should -BeLike '*WARN*:*This is a warning*' -Because $errorLog + $errorLog | Should -BeLike '*INFO*:*This is a host message*' -Because $errorLog + $errorLog | Should -BeLike '*INFO*:*This is a verbose message*' -Because $errorLog + } + + It 'Non-terminating errors do not cause script to fail' -TestCases $testCases { + param($resourceType) + + $yaml = @' + getScript: | + Get-Item "ThisFileDoesNotExist.txt" -ErrorAction SilentlyContinue + "This should still be output" +'@ + $result = dsc -l debug resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json + $LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) + $result.actualState.output.Count | Should -Be 1 -Because ($result | ConvertTo-Json -Depth 10 | Out-String) + $result.actualState.output[0] | Should -BeExactly "This should still be output" + $errorLog = Get-Content $TestDrive/error.txt -Raw + $errorLog | Should -BeLike '*DEBUG*:*Non-terminating errors occurred during script execution.*' -Because $errorLog + } + + It 'Terminating errors result in an error for ' -TestCases $testCases { + param($resourceType) + + $yaml = @' + GetScript: | + Get-Item "ThisFileDoesNotExist.txt" -ErrorAction Stop +'@ + $result = dsc resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json + $LASTEXITCODE | Should -Be 2 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) $result.actualState.output.Count | Should -Be 0 -Because ($result | ConvertTo-Json | Out-String) - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*WARN*:*This is a warning*' - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*INFO*:*This is a host message*' - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*INFO*:*This is a verbose message*' + (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*ERROR*:*Cannot find path*' } }