From 60a0718600c14f56dd5ada752a6de4d3a4180a46 Mon Sep 17 00:00:00 2001 From: Julia Anjanet Pineda Date: Mon, 7 Feb 2022 08:24:11 +0800 Subject: [PATCH] Integrate Jira issue creation/update (#35) * Define logJira and var defaults Signed-off-by: Julia Pineda * get_gitsha: return hash map Signed-off-by: Julia Pineda * UpdateBOOTFiles: Parse failing_msg outside if send_results Signed-off-by: Julia Pineda * UpdateBOOTFiles: Call logJira Signed-off-by: Julia Pineda * LinuxTests: Call logJira Signed-off-by: Julia Pineda * PyADITests: Call logJira Signed-off-by: Julia Pineda * Libad9361Tests: Call logJira * MATLABTests: Call logJira Signed-off-by: Julia Pineda * Move common jiraArgs update inside logJira Signed-off-by: Julia Pineda * Revert get_gitsha definition Signed-off-by: Julia Pineda * Enclose Jira site access in try-catch Signed-off-by: Julia Pineda * logJira cleanup Signed-off-by: Julia Pineda * LinuxTests: Attach dmesg.log Signed-off-by: Julia Pineda --- src/sdg/Gauntlet.groovy | 172 +++++++++++++++++++++++++++++++++++++++- vars/getGauntEnv.groovy | 2 + 2 files changed, 171 insertions(+), 3 deletions(-) diff --git a/src/sdg/Gauntlet.groovy b/src/sdg/Gauntlet.groovy index c86507fb..cbfe4525 100644 --- a/src/sdg/Gauntlet.groovy +++ b/src/sdg/Gauntlet.groovy @@ -187,15 +187,23 @@ def stage_library(String stage_name) { echo "Update BOOT Files unexpectedly failed. ${ex.getMessage()}" } get_gitsha(board) + failing_msg = "'" + ex.getMessage().split('\n').last().replaceAll( /(['])/, '"') + "'" // send logs to elastic if (gauntEnv.send_results){ set_elastic_field(board, 'last_failing_stage', 'UpdateBOOTFiles') - failing_msg = "'" + ex.getMessage().split('\n').last().replaceAll( /(['])/, '"') + "'" set_elastic_field(board, 'last_failing_stage_failure', failing_msg) stage_library('SendResults').call(board) } if (is_nominal_exception) throw new NominalException('UpdateBOOTFiles failed: '+ ex.getMessage()) + // log Jira + try{ + description = failing_msg + }catch(Exception desc){ + println('Error updating description.') + }finally{ + logJira([board:board, summary:'Update BOOT files failed.', description:description, attachment:[board+".log"]]) + } throw new Exception('UpdateBOOTFiles failed: '+ ex.getMessage()) }finally{ //archive uart logs @@ -357,6 +365,17 @@ def stage_library(String stage_name) { } if(failed_test && !failed_test.allWhitespace){ + // log Jira + def description = "" + try{ + description += "*Missing drivers: " + missing_devs.size().toString() + "* (" + missing_devs.join(", ") + ")\n" + dmesg_errs = readFile("dmesg_err_filtered.log").readLines() + description += "*dmesg errors: ${dmesg_errs.size()}*\n" + dmesg_errs.join("\n") + }catch(Exception desc){ + println('Error updating description.') + }finally{ + logJira([board:board, summary:'Linux tests failed.', description:description, attachment:[board+"_diag_report.tar.bz2","dmesg.log"]]) + } unstable("Linux Tests Failed: ${failed_test}") } }catch(Exception ex) { @@ -383,6 +402,8 @@ def stage_library(String stage_name) { def ip = nebula('update-config network-config dutip --board-name='+board) def serial = nebula('update-config uart-config address --board-name='+board) def uri; + def description = "" + def pytest_attachment = null println('IP: ' + ip) // temporarily get pytest-libiio from another source run_i('git clone -b "' + gauntEnv.pytest_libiio_branch + '" ' + gauntEnv.pytest_libiio_repo, true) @@ -432,11 +453,23 @@ def stage_library(String stage_name) { println('Parsing pytest results failed') echo getStackTrace(ex) } + pytest_attachment = board+"_reports.xml" } // throw exception if pytest failed if ((statusCode != 5) && (statusCode != 0)){ // Ignore error 5 which means no tests were run + // log Jira + dir('testxml'){ + try{ + sh 'grep \" name=.* failures.txt' + description += readFile 'failures.txt' + }catch(Exception desc){ + println('Error updating description.') + }finally{ + logJira([board:board, summary:'PyADI tests failed.', description: description, attachment:[pytest_attachment]]) + } + } unstable("PyADITests Failed") } } @@ -473,6 +506,14 @@ def stage_library(String stage_name) { } } }catch(Exception ex){ + // log Jira + try{ + description = "LibAD9361Tests Failed: ${ex.getMessage()}" + } catch(Exception desc){ + println('Error updating description.') + } finally{ + logJira([board:board, summary:'libad9361 tests failed.', description:description]) + } unstable("LibAD9361Tests Failed: ${ex.getMessage()}") }finally{ dir('libad9361-iio/build'){ @@ -492,6 +533,8 @@ def stage_library(String stage_name) { def under_scm = true stage("Run MATLAB Toolbox Tests") { def ip = nebula('update-config network-config dutip --board-name='+board) + def description = "" + def xmlFile = board+'_HWTestResults.xml' sh 'cp -r /root/.matlabro /root/.matlab' under_scm = isMultiBranchPipeline() if (under_scm) @@ -506,11 +549,18 @@ def stage_library(String stage_name) { try{ sh 'IIO_URI="ip:'+ip+'" board="'+board+'" elasticserver='+gauntEnv.elastic_server+' /usr/local/MATLAB/'+gauntEnv.matlab_release+'/bin/matlab -nosplash -nodesktop -nodisplay -r "run(\'matlab_commands.m\');exit"' }catch (Exception ex){ + // log Jira + try{ + description += readFile 'failures.txt' + }catch(Exception desc){ + println('Error updating description.') + }finally{ + logJira([board:board, summary:'MATLAB tests failed.', description: description, attachment:[xmlFile]]) + } throw new NominalException(ex.getMessage()) }finally{ junit testResults: '*.xml', allowEmptyResults: true // get MATLAB hardware test results for logging - xmlFile = board+'_HWTestResults.xml' if(fileExists(xmlFile)){ try{ parseForLogging ('matlab', xmlFile, board) @@ -531,11 +581,18 @@ def stage_library(String stage_name) { try{ sh 'IIO_URI="ip:'+ip+'" board="'+board+'" elasticserver='+gauntEnv.elastic_server+' /usr/local/MATLAB/'+gauntEnv.matlab_release+'/bin/matlab -nosplash -nodesktop -nodisplay -r "run(\'matlab_commands.m\');exit"' }catch (Exception ex){ + // log Jira + try{ + description += readFile 'failures.txt' + }catch(Exception desc){ + println('Error updating description.') + }finally{ + logJira([board:board, summary:'MATLAB tests failed.', description: description, attachment:[xmlFile]]) + } throw new NominalException(ex.getMessage()) }finally{ junit testResults: '*.xml', allowEmptyResults: true // get MATLAB hardware test results for logging - xmlFile = board+'_HWTestResults.xml' if(fileExists(xmlFile)){ try{ parseForLogging ('matlab', xmlFile, board) @@ -973,6 +1030,115 @@ def set_recovery_reference(reference) { gauntEnv.recovery_ref = reference } +/** + * Enable logging issues to Jira. Setting true will update existing Jira issues or create a new issue. + * @param log_jira Boolean of enable jira logging. + */ +def set_log_jira(log_jira) { + gauntEnv.log_jira = log_jira +} + +/** + * Set stages where Jira issues should be updated or created. + * @param log_jira_stages List of stage names + */ +def set_log_jira_stages(log_jira_stages) { + gauntEnv.log_jira_stages = log_jira_stages +} + +/** + * Creates or updates existing Jira issue for carrier-daughter board + * Each stage has its own Jira thread for each carrier-daughter board + * Required key: jiraArgs.summary, other fields have default values or optional + * attachments is a list of filesnames to upload in the Jira issue + * Default values: Jira site: ADI SDG + * project: HTH + * issuetype: Bug + * assignee: JPineda3 + * component: KuiperTesting + */ + +def logJira(jiraArgs) { + defaultFields = [site:'sdg-jira',project:'HTH', assignee:'JPineda3', issuetype:'Bug', components:"KuiperTesting", description:"Issue exists in recent build."] + optionalFields = ['assignee','issuetype','description'] + def key = '' + // Assign default values if not defined in jiraArgs + for (field in defaultFields.keySet()){ + if (!jiraArgs.containsKey(field)){ + jiraArgs.put(field,defaultFields."${field}") + } + } + // Append [carier-daugther] to summary + jiraArgs.board = jiraArgs.board.replaceAll('_', '-') + try{ + jiraArgs.summary = "["+nebula('update-config board-config carrier --board-name='+jiraArgs.board )+"-"+nebula('update-config board-config daughter --board-name='+jiraArgs.board )+"] ".concat(jiraArgs.summary) + }catch(Exception summary){ + println('Jira: Cannot append [carier-daugther] to summary.') + } + // Include hdl and linux hash if available + try{ + jiraArgs.description = "{color:#de350b}*[hdl_hash:"+get_elastic_field(jiraArgs.board, 'hdl_hash' , 'NA')+", linux_hash:"+get_elastic_field(jiraArgs.board, 'linux_hash' , 'NA')+"]*{color}\n".concat(jiraArgs.description) + jiraArgs.description = "["+env.JOB_NAME+'-build-'+env.BUILD_NUMBER+"]\n".concat(jiraArgs.description) + }catch(Exception desc){ + println('Jira: Cannot include hdl and linux hash to description.') + } + echo 'Checking if Jira logging is enabled..' + try{ + if (gauntEnv.log_jira) { + echo 'Checking if stage is included in log_jira_stages' + if (gauntEnv.log_jira_stages.isEmpty() || !gauntEnv.log_jira_stages.isEmpty() && (env.STAGE_NAME in gauntEnv.log_jira_stages)) { + println('Jira logging is enabled for '+env.STAGE_NAME+'. Checking if Jira issue with summary '+jiraArgs.summary+' exists..') + existingIssuesSearch = jiraJqlSearch jql: "project='${jiraArgs.project}' and summary ~ '\"${jiraArgs.summary}\"'", site: jiraArgs.site, failOnError: true + // Comment on existing Jira ticket + if (existingIssuesSearch.data.total != 0){ + echo 'Updating existing issue..' + existingIssue = existingIssuesSearch.data.issues + key = existingIssue[0].key + issueUpdate = jiraArgs.description + comment = [body: issueUpdate] + jiraAddComment site: jiraArgs.site, idOrKey: key, input: comment + } + // Create new Jira ticket + else{ + echo 'Issue does not exist. Creating new Jira issue..' + // Required fields + issue = [fields: [ + project: [key: jiraArgs.project], + summary: jiraArgs.summary, + assignee: [name: jiraArgs.assignee], + issuetype: [name: jiraArgs.issuetype], + components: [[name:jiraArgs.components]]]] + // Optional fields + for (field in optionalFields){ + if (jiraArgs.containsKey(field)){ + if (field == 'description'){ + issue.fields.put(field,jiraArgs."${field}") + }else{ + issue.fields.put(field,[name:jiraArgs."${field}"]) + } + } + } + def newIssue = jiraNewIssue issue: issue, site: jiraArgs.site + key = newIssue.data.key + } + // Upload attachment if any + if (jiraArgs.containsKey("attachment") && jiraArgs.attachment != null){ + echo 'Uploading attachments..' + for (attachmentFile in jiraArgs.attachment){ + def attachment = jiraUploadAttachment site: jiraArgs.site, idOrKey: key, file: attachmentFile + } + } + }else{ + println('Jira logging is not enabled for '+env.STAGE_NAME+'.') + } + }else{ + echo 'Jira logging is disabled for all stages.' + } + }catch(Exception jiraError){ + println('Error creating/updating Jira issue.') + } +} + /** * Main method for starting pipeline once configuration is complete * Once called all agents are queried for attached boards and parallel stages diff --git a/vars/getGauntEnv.groovy b/vars/getGauntEnv.groovy index 9509ac2a..eb2dd099 100644 --- a/vars/getGauntEnv.groovy +++ b/vars/getGauntEnv.groovy @@ -61,6 +61,8 @@ private def call(hdlBranch, linuxBranch, bootPartitionBranch,firmwareVersion, bo nebula_config_branch: 'master', send_results: false, elastic_logs : [:], + log_jira: false, + log_jira_stages: [], max_retry: 3, recovery_ref: "SD" ]