Issue
So here is my situation. I am running a Jenkins Server with the Generic Webhook Trigger Plugin that is receiving a JSON Webhook from Jira Cloud, and then runs a Powershell script based off of the information in the Webhook to update Jira with the appropriate comments and status transitions. This build worked swimmingly for many months, until the latest update to Jenkins.
Now, when the Jira Webhook fires, the Powershell script runs, and occasionally fails. After some debugging, the reason seems to be at a line of code that had never before posed problems.
One line in the Powershell code does a POST request to Jira API to invoke a Jira status transition on the task in question. In the ideal case, the task begins in the status "To Do", the first POST request fires a transition to "In Progress", then the deploy runs. Once the Deploy is complete, then there is another transition from "In Progress" to "Done".
However, in some cases the tasks do not begin in the To Do status. When the POST request tries to transition a Jira task, say from "In Progress" to "In Progress", there is a 400 response code saying "Bad Request" as the transition is not allowed. This is fine. Previously, the build would receive the 400 error and continue on with the script, which we would like it to continue to do. However, the new behavior is that now, receiving a 400 request fails the entire Jenkins build, and the downstream steps in the script do not run.
We sometimes have need to fire the Webhook while the status is still in In Progress, and these 400 errors are not a big deal for us. What is a big deal is the build failing. How can I stop Jenkins from failing the entire build and stopping my Powershell script short upon one of these cases?
Thanks in advance. I feel like this is a Jenkins Configuration issue I haven't been able to find the answer to anywhere.
JSON Code is too long to add here.
Powershell Script:
param ([string] $JIRA_TOKEN, [int] $BuildNumber)
. "C:\Users\Ameera.Khan\Documents\GitHub\tableau-migration-scripts\workbook_parse.ps1"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 #sets TLS method
Write-Host "Webhook recieved. Success."
$ExitCodePath = "E:\Webhook\TempFiles\ExitCode\ExitCode_$BuildNumber.txt"
$PostDeployIDPath = "E:\Webhook\TempFiles\PostDeployID\PostDeployID_$BuildNumber.txt"
$JiraWebhookPath = "E:\Webhook\TempFiles\Jira_Webhook\$BuildNumber.JSON"
$WorkbooksPath = "E:\Webhook\TempFiles\Workbooks\workbooks_$BuildNumber.txt"
$body = [IO.File]::ReadAllText($JiraWebhookPath) #this reads the temp file that Jenkins wrote containing the JSON Body and stores it in a variable.
$JSON_body = $body | ConvertFrom-Json
$JSON_body.GetType()
# Here we pull out the TCMT plan(s) and the Dashboard Release Ready Ticket type from the JSON.
$TCMT_Plan = $JSON_Body.issue.fields.customfield_10107 #eg, "BCI_MA_MemberIntel.tcmx\nBCI_Medicare_Growth.tcmx"
$dev_workbook_name = $JSON_Body.issue.fields.customfield_10165 #eg, "atrio_memberintel_medicare_memberreporting_v2\nBCI_Medicare_MarketIntelPlanDesign_Dev"
$to_status = $JSON_Body.transition.to_status #should be "Release Ready"
$issue_type = $JSON_Body.issue.fields.issuetype.name #should be "Dashboard Deployment"
# This block validates the parameters and stops the script from running if they're not right.
if (!(($issue_type -eq "Dashboard Deployment") -Xor ($issue_type -eq "Bug")) -or ($to_status -ne "Release Ready") ) {
Write-Host "This transition is not meant to be run."
return
}
if ($dev_workbook_name) {
Write-Host "This Webhook contains a Tableau Workbook name. We will run the Workbook."
}
$credential = $JIRA_TOKEN
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($credential))
$encodedCreds = "Basic " + $encodedCreds
$Headers = @{
"X-Atlassian-Token" = "no-check";
"Authorization" = $encodedCreds;
"Content-Type" = "application/json"
"Accept" = "application/json"
}
# We also set some startup variables, like ExitCodeArray and planFiles.
$prod_push = $JSON_Body.issue.fields.subtasks | where { $_.fields.issuetype.name -eq "Deploy" } #finds the Push to Prod subtask
$post_deploy_task = $JSON_Body.issue.fields.subtasks | where { $_.fields.issuetype.name -eq "Post Deployment Testing" } #finds the Post Deploy subtask
$Jira_deploy_subtask_id = $prod_push.key #should be PEP-##### of the Push subtask
$deploy_transition_status = $prod_push.fields.status.name
$ExitCodeArray = New-Object System.Collections.Generic.List[System.Object]
$planFiles = (($TCMT_Plan -split '\r?\n').Trim()) #this line splits the TCMT Plan string into multiple strings at \n.
$workbookFiles = (($dev_workbook_name -split '\r?\n').Trim())
# Save the workbooks within the planFiles into a E:\Webhook\workbooks_$BuildNumber.txt for the next pipeline step, Kinesis CI, to work properly.
workbook_parse -TCMT_Plan $TCMT_Plan -BuildNumber $BuildNumber
Add-Content -Path $WorkbooksPath -Value ($workbookFiles)
$post_deploy_id = $post_deploy_task.key
$post_deploy_transition_status = $post_deploy_task.fields.status.name
$post_deploy_id | Out-File -FilePath $PostDeployIDPath
# Now that we have the subtask, we update the subtask to In Progress by providing the transition id for Open > In Progress in JSON format.
if ($deploy_transition_status -eq "To Do") {
$Uri = "https://carrothealth.atlassian.net/rest/api/2/issue/" + $Jira_deploy_subtask_id + "/transitions"
$RequestBody = @{
"transition" = @{
id = "11"
};
}
# The Invoke-RestMethod method is equivalent to cURL and makes a web request based on the flags and parameters.
Invoke-RestMethod -Method Post -Uri $Uri -H $Headers -Body ($RequestBody | ConvertTo-Json) -Verbose
}
else {
Write-Host "The sub task is done, skipping to next part of deploy. "
}
...until the end of script.
Jenkins Pipeline Script:
pipeline {
agent any
environment {
JIRA_TOKEN = credentials('JIRA_TOKEN')
OAUTH_CLIENT_ID = credentials('OAUTH_CLIENT_ID')
OAUTH_CLIENT_SECRET = credentials('OAUTH_CLIENT_SECRET')
TABLEAU_AUTOMATION_PASSWORD = credentials('TABLEAU_AUTOMATION_PASSWORD')
TABLEAU_AUTOMATION_PASSWORD_KINESIS = credentials('TABLEAU_AUTOMATION_PASSWORD_KINESIS')
}
stages {
stage('Write the Jira Webhook to a file') {
steps {
writeFile(file:"E:/Webhook/TempFiles/Jira_Webhook/${currentBuild.number}.json", text:body)
}
}
stage('Create TCMT Plan in case of Tableau Workbook') {
steps {
powershell(script: "C:/Users/Ameera.Khan/anaconda3/python.exe C:/Users/Ameera.Khan/Documents/GitHub/tableau-migration-scripts/onTheFly.py '${currentBuild.number}'")
}
}
stage('Migrate Content') {
steps {
powershell(script: "C:/Users/Ameera.Khan/Documents/GitHub/tableau-migration-scripts/tcmt_runner_Continuous_Deploy.ps1 -BuildNumber '${currentBuild.number}' -JIRA_TOKEN '${env.JIRA_TOKEN}'")
}
}
stage('Post Deploy Testing') {
steps {
powershell(script: "C:/Users/Ameera.Khan/Documents/GitHub/tableau-migration-scripts/CI_Script.ps1 -BuildNumber '${currentBuild.number}' -JIRA_TOKEN '${env.JIRA_TOKEN}' -OAUTH_CLIENT_ID '${env.OAUTH_CLIENT_ID}' -OAUTH_CLIENT_SECRET '${env.OAUTH_CLIENT_SECRET}' -TABLEAU_AUTOMATION_PASSWORD_KINESIS '${env.TABLEAU_AUTOMATION_PASSWORD_KINESIS}'")
}
}
}
}
Error Log:
VERBOSE: POST https://carrothealth.atlassian.net/rest/api/2/issue/PEP-37938/transitions with -1-byte payload
powershell.exe : Invoke-RestMethod : The remote server returned an error: (400) Bad Request.
At C:\Users\Ameera.Khan\AppData\Local\Jenkins\.jenkins\workspace\TableauCD@tmp\durable-2d7da495\powershellWrapper.ps1:3 char:1
+ & powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -Comm ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (Invoke-RestMeth...0) Bad Request.:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
At C:\Users\Ameera.Khan\Documents\GitHub\tableau-migration-scripts\tcmt_runner_Continuous_Deploy.ps1:91 char:1
+ Invoke-RestMethod -Method Post -Uri $Uri -H $Headers -Body ($RequestB ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebExc
eption
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Solution
In my scripts, I can avoid a Failed Build through an error with a simple try / catch method.
So try something like this :
# The Invoke-RestMethod method is equivalent to cURL and makes a web request based on the flags and parameters.
try {
Invoke-RestMethod -Method Post -Uri $Uri -H $Headers -Body ($RequestBody | ConvertTo-Json) -Verbose
} catch {
Write-Warning -Message 'Issue already in Progress'
}
If you take a look at the Documentation of PowerShell Pipline Support. You can specify an explicated exit code to mark the build as successful despite errors.
https://www.jenkins.io/blog/2017/07/26/powershell-pipeline/
node {
powershell '''
Write-Error 'Error! Problem Exists Between Keyboard And Chair'
exit 0
'''
}
Answered By - NicoKlausIT