ps/Modules/Alkami.DevOps.Validations/Public/Invoke-Endpoint.ps1
2023-05-30 22:51:22 -07:00

190 lines
7.8 KiB
PowerShell

function Invoke-Endpoint {
<#
.SYNOPSIS
Invokes a website and returns the response of the URL
.DESCRIPTION
Wraps Invoke-WebRequest with retry logic
.PARAMETER Uri
URI of the website to invoke
.PARAMETER Method
HTTP method, Get, Post, etc.
.PARAMETER Baseuri
Optional parameter taking the base Uri and concatinating with Uri. "$Baseuri$Uri".
Helper to avoid having to concatinate before invoking site.
.PARAMETER retryStatusCodes
[System.Net.HttpStatusCode][] objects that are eligible for retry logic.
[System.Net.HttpStatusCode]::ServiceUnavailable is default.
.PARAMETER RetryDelay
Seconds to wait between retries. Defaults to 5. Is exponential backoff.
.PARAMETER RetryCount
Total number of retry attempts before failing.
.PARAMETER Body
Used for POST Method
.PARAMETER Headers
String of Http headers to pass to the request.
.PARAMETER WebSession
Shared session to use for subsequent requests
.PARAMETER UseNewSession
Bool to determine if a new blank session will be used and returned instead of the passed-in WebSession.
.PARAMETER TimeoutSeconds
Int in seconds for the web timeout.
.EXAMPLE
$r = Invoke-Endpoint -Uri http://google.com
#>
[cmdletbinding()]
param(
[Parameter(Mandatory, ValueFromPipeline)]
$Uri,
[Parameter(Mandatory = $false)]
$Method = 'GET',
[Parameter(Mandatory = $false)]
$Baseuri = '',
[Parameter(Mandatory=$false)]
[System.Net.HttpStatusCode[]] $retryStatusCodes,
[Parameter(Mandatory=$false)]
$RetryDelay = 5,
[Parameter(Mandatory=$false)]
$RetryCount = 3,
[Parameter(Mandatory=$false)]
$Body = $null,
[Parameter(Mandatory=$false)]
$Headers = $null,
[Parameter(Mandatory=$false)]
$WebSession = $null,
[Parameter(Mandatory=$false)]
[bool]$UseNewSession = $false,
[Parameter(Mandatory=$false)]
[int]$TimeoutSeconds = 120
)
begin {
$auldProgressPreference = $ProgressPreference
$ProgressPreference = 'silentlycontinue'
}
process {
$logLead = (Get-LogLeadName)
$logFilePath = (Get-WebTestLogPath -BankUrl "$Baseuri$Uri")
$retryCounter = 0
$totalRuntime = 0
$errorsArray = @()
$runtimeArray = @()
$result = $null
if($null -eq $retryStatusCodes) {
$retryStatusCodes = @(
[System.Net.HttpStatusCode]::BadGateway,
[System.Net.HttpStatusCode]::BadRequest,
[System.Net.HttpStatusCode]::GatewayTimeout,
[System.Net.HttpStatusCode]::InternalServerError,
[System.Net.HttpStatusCode]::ServiceUnavailable
)
}
do {
$StopWatch = [System.Diagnostics.StopWatch]::StartNew()
try {
"$logLead : Performing web request to $($baseuri)$($uri)" | Tee-OutFile -Append -FilePath $logFilePath | Write-Verbose
if($UseNewSession) {
$result = Invoke-WebRequest "$Baseuri$Uri" -Method $Method -Body $Body -Headers $Headers -SessionVariable NewWebSession -UseBasicParsing -TimeoutSec $TimeoutSeconds -DisableKeepAlive -UserAgent "SRE Web Test"
$WebSession = $NewWebSession
} else {
$result = Invoke-WebRequest "$Baseuri$Uri" -Method $Method -Body $Body -Headers $Headers -WebSession $WebSession -UseBasicParsing -TimeoutSec $TimeoutSeconds -DisableKeepAlive -UserAgent "SRE Web Test"
}
# On success, stop executing
break
}
catch [System.Net.WebException] {
"$logLead : Exception: $_" | Tee-OutFile -Append -FilePath $logFilePath | Write-Verbose
$errorsArray += $_.Exception.Message
if ($null -eq $_.Exception.Response -and $_.Exception.Message -eq "The operation has timed out.") {
"$logLead : No response object found. Request timed out." | Tee-OutFile -Append -FilePath $logFilePath | Write-Verbose
$retryCounter += 1
$hasRetriedEnough = $retryCounter -gt ($retryCount)
if ($hasRetriedEnough) {
"$logLead : Returning response object" | Tee-OutFile -Append -FilePath $logFilePath | Write-Verbose
break
} else {
$sleepTime = $retryDelay*$retryCounter
"$logLead : Sleeping for $sleepTime seconds" | Tee-OutFile -Append -FilePath $logFilePath | Write-Verbose
Start-Sleep -Seconds $sleepTime
$resp = $null
}
} else {
$resp = $_.Exception.Response
"$logLead : Response code: $($resp.StatusCode)" | Tee-OutFile -Append -FilePath $logFilePath | Write-Verbose
$retryCounter += 1
$isARetryCode = $retryStatusCodes -contains $resp.StatusCode
"$logLead : Is retry code: $isARetryCode" | Tee-OutFile -Append -FilePath $logFilePath | Write-Verbose
$hasRetriedEnough = $retryCounter -gt ($retryCount)
"$logLead : Has retried enough: $hasRetriedEnough" | Tee-OutFile -Append -FilePath $logFilePath | Write-Verbose
if ($hasRetriedEnough -or(-not($isARetryCode))) {
"$logLead : Returning response object" | Tee-OutFile -Append -FilePath $logFilePath | Write-Verbose
break
} else {
$sleepTime = $retryDelay*$retryCounter
"$logLead : Sleeping for $sleepTime seconds" | Tee-OutFile -Append -FilePath $logFilePath | Write-Verbose
Start-Sleep -Seconds $sleepTime
}
}
}
catch {
$errorsArray += $_.Exception.Message
"$logLead : Got a generic exception. Quitting immediately" | Tee-OutFile -Append -FilePath $logFilePath | Write-Verbose
break
} finally {
$StopWatch.Stop()
$totalRuntime += $StopWatch.ElapsedMilliseconds
$runtimeArray += $StopWatch.Elapsed.ToString("ss\.fffffff")
$isSuccess = $true
if($result.StatusCode -ne 200){
if($null -eq $resp){
# If we got a timeout, send a null result
Write-Verbose "$logLead : Request timed out."
$result = $null
} else {
# If we got a failure response, map the most recent exception's data onto the result and give that back.
Write-Verbose "$logLead : Got a $($resp.StatusCode)"
$result = $resp
}
$isSuccess = $false
}
$responseObject = @{
ErrorResults = $errorsArray;
LastError = $errorsArray[-1];
HasErrors = $errorsArray.Count -gt 0;
Result = $result;
Success = $isSuccess;
StatusCode = $result.StatusCode;
WebSession = $WebSession;
Runtimes = $runtimeArray;
LastRuntime = $runtimeArray[-1];
TotalRuntime = [System.TimeSpan]::FromMilliseconds($totalRuntime).ToString()
}
$result = $null
}
} while ($true)
return $responseObject
}
end {
$ProgressPreference = $auldProgressPreference
}
}