1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-12 10:55:20 +02:00

Implement archiving the debug report as step (#1152)

* Implement archiving the debug report as step
This commit is contained in:
Stephan Aßmus 2020-02-07 16:30:08 +01:00 committed by GitHub
parent c37df0d55e
commit c628d208c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 162 additions and 21 deletions

View File

@ -17,7 +17,7 @@ The log was generated at: `$utcTimestamp UTC`
<% print failedBuild.get("stack_trace") ? "```" : "" %>
<% print failedBuild.get("stack_trace").collect({each -> "${each}"}).join("\n") %>
<% print failedBuild.get("stack_trace") ? "```" : "" %>
<% print failedBuild.get("fatal") ? "#### Failure was fatal." : "" %>
<% print failedBuild.get("fatal") ? "#### Failure was fatal." : "#### Failure was non-fatal." %>
## Pipeline Environment

View File

@ -199,6 +199,8 @@ steps:
cloudFoundryCreateServiceKey:
dockerImage: 'ppiper/cf-cli'
dockerWorkspace: '/home/piper'
debugReportArchive:
shareConfidentialInformation: false
detectExecuteScan:
detect:
projectVersion: '1'

View File

@ -5,7 +5,6 @@ import groovy.text.SimpleTemplateEngine
@Singleton
class DebugReport {
String fileName
String projectIdentifier = null
Map environment = ['environment': 'custom']
String buildTool = null
@ -20,7 +19,6 @@ class DebugReport {
String sharedConfigFilePath = null
Set additionalSharedLibraries = []
Map failedBuild = [:]
boolean shareConfidentialInformation
/**
* Initialize debug report information from the environment variables.
@ -85,14 +83,10 @@ class DebugReport {
failedBuild.put('step', stepName)
failedBuild.put('reason', err)
failedBuild.put('stack_trace', err.getStackTrace())
if (failedOnError) {
failedBuild.put('fatal', 'true')
} else {
failedBuild.remove('fatal')
}
failedBuild.put('fatal', failedOnError ? 'true' : 'false')
}
String generateReport(Script script) {
Map generateReport(Script script, boolean shareConfidentialInformation) {
String template = script.libraryResource 'debug_report.txt'
if (!projectIdentifier) {
@ -107,19 +101,34 @@ class DebugReport {
script.echo "Failed to retrieve Jenkins plugins for debug report (${t.getMessage()})"
}
Map binding = getProperties()
Date now = new Date()
binding.utcTimestamp = now.format('yyyy-MM-dd HH:mm', TimeZone.getTimeZone('UTC'))
Map binding = [
'projectIdentifier' : projectIdentifier,
'environment' : environment,
'buildTool': buildTool,
'modulesMap' : modulesMap,
'npmModules' : npmModules,
'plugins' : plugins,
'gitRepo' : gitRepo,
'localExtensions' : localExtensions,
'globalExtensionRepository' : globalExtensionRepository,
'globalExtensions' : globalExtensions,
'globalExtensionConfigurationFilePath' : globalExtensionConfigurationFilePath,
'sharedConfigFilePath' : sharedConfigFilePath,
'additionalSharedLibraries' : additionalSharedLibraries,
'failedBuild' : failedBuild,
'shareConfidentialInformation' : shareConfidentialInformation,
'utcTimestamp' : now.format('yyyy-MM-dd HH:mm', TimeZone.getTimeZone('UTC'))
]
String fileNameTimestamp = now.format('yyyy-MM-dd-HH-mm', TimeZone.getTimeZone('UTC'))
String fileNamePrefix = shareConfidentialInformation ? 'confidential' : 'redacted'
if (shareConfidentialInformation) {
fileName = "confidential_debug_log_${fileNameTimestamp}_${projectIdentifier}.txt"
} else {
fileName = "redacted_debug_log_${fileNameTimestamp}_${projectIdentifier}.txt"
}
return fillTemplate(template, binding)
Map result = [:]
result.fileName = "${fileNamePrefix}_debug_log_${fileNameTimestamp}_${projectIdentifier}.txt"
result.contents = fillTemplate(template, binding)
return result
}
@NonCPS

View File

@ -0,0 +1,58 @@
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import util.BasePiperTest
import util.JenkinsLoggingRule
import util.JenkinsReadYamlRule
import util.JenkinsStepRule
import util.JenkinsWriteFileRule
import util.Rules
import static org.hamcrest.Matchers.containsString
import static org.junit.Assert.assertThat
class DebugReportArchiveTest extends BasePiperTest {
private JenkinsLoggingRule loggingRule = new JenkinsLoggingRule(this)
private JenkinsStepRule stepRule = new JenkinsStepRule(this)
private JenkinsWriteFileRule writeFileRule = new JenkinsWriteFileRule(this)
@Rule
public RuleChain ruleChain = Rules
.getCommonRules(this)
.around(new JenkinsReadYamlRule(this))
.around(loggingRule)
.around(writeFileRule)
.around(stepRule)
@Before
void init() {
helper.registerAllowedMethod("libraryResource", [String.class], { path ->
File resource = new File(new File('resources'), path)
if (resource.exists()) {
return resource.getText()
}
return ''
})
}
@Test
void testDebugReportArchive() {
stepRule.step.debugReportArchive(
script: nullScript,
juStabUtils: utils,
stageName: 'test',
printToConsole: true
)
String debugReportSnippet = 'The debug log is generated with each build and should be included in every support request'
assertThat(loggingRule.log, containsString('Successfully archived debug report'))
assertThat(loggingRule.log, containsString(debugReportSnippet))
assertThat(writeFileRule.files.find({ it.toString().contains('debug_log') }) as String, containsString(debugReportSnippet))
}
}

View File

@ -43,7 +43,7 @@ class DebugReportTest extends BasePiperTest {
DebugReport.instance.initFromEnvironment(createEnv())
DebugReport.instance.setGitRepoInfo('GIT_URL' : 'git://url', 'GIT_LOCAL_BRANCH' : 'some-branch')
String debugReport = DebugReport.instance.generateReport(mockScript())
String debugReport = DebugReport.instance.generateReport(mockScript(), false)
Assert.assertTrue(debugReport.contains('## Pipeline Environment'))
Assert.assertTrue(debugReport.contains('## Local Extensions'))
@ -57,9 +57,8 @@ class DebugReportTest extends BasePiperTest {
void testLogOutputConfidential() {
DebugReport.instance.initFromEnvironment(createEnv())
DebugReport.instance.setGitRepoInfo('GIT_URL' : 'git://url', 'GIT_LOCAL_BRANCH' : 'some-branch')
DebugReport.instance.shareConfidentialInformation = true
String debugReport = DebugReport.instance.generateReport(mockScript())
String debugReport = DebugReport.instance.generateReport(mockScript(), true)
Assert.assertTrue(debugReport.contains('## Pipeline Environment'))
Assert.assertTrue(debugReport.contains('## Local Extensions'))

View File

@ -0,0 +1,73 @@
import com.sap.piper.ConfigurationHelper
import com.sap.piper.DebugReport
import com.sap.piper.GenerateDocumentation
import com.sap.piper.Utils
import groovy.transform.Field
import static com.sap.piper.Prerequisites.checkScript
@Field def STEP_NAME = getClass().getName()
@Field Set GENERAL_CONFIG_KEYS = []
@Field Set STEP_CONFIG_KEYS = [
/**
* Flag to control whether potentially confidential information will be included in the
* debug_report.txt. Default value is `false`. Additional information written to the log
* when this flag is `true` includes MTA modules, NPM modules, the GitHub repository and
* branch, the global extension repository if used, a shared config file path, and all
* used global and local shared libraries.
* @possibleValues `true`, `false`
*/
'shareConfidentialInformation'
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS + [
/**
* Flag to enable printing the generated debug_report.txt also to the console.
*/
'printToConsole'
]
/**
* Archives the debug_report.txt artifact which facilitates analyzing pipeline errors by collecting
* information about the Jenkins environment in which the pipeline was run. There is a single
* config option 'shareConfidentialInformation' to enable including (possibly) confidential
* information in the debug report, which could be helpful depending on the specific error.
* By default this information is not included.
*/
@GenerateDocumentation
void call(Map parameters = [:]) {
final script = checkScript(this, parameters) ?: this
try {
String stageName = parameters.stageName ?: env.STAGE_NAME
// ease handling extension
stageName = stageName?.replace('Declarative: ', '')
def utils = parameters.juStabUtils ?: new Utils()
Map configuration = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, stageName, STEP_CONFIG_KEYS)
.mixin(parameters, PARAMETER_KEYS)
.use()
utils.pushToSWA([
step: STEP_NAME,
stepParamKey1: 'scriptMissing',
stepParam1: parameters?.script == null
], configuration)
boolean shareConfidentialInformation = configuration?.get('shareConfidentialInformation') ?: false
Map result = DebugReport.instance.generateReport(script, shareConfidentialInformation)
if (parameters.printToConsole) {
echo result.contents
}
script.writeFile file: result.fileName, text: result
script.archiveArtifacts artifacts: result.fileName
echo "Successfully archived debug report as '${result.fileName}'"
} catch (Exception e) {
println("WARNING: The debug report was not created, it threw the following error message:")
println("${e}")
}
}