Skip to content

Commit

Permalink
add new provideReportLogsStep (#143)
Browse files Browse the repository at this point in the history
  • Loading branch information
MxEh-TT committed Jul 25, 2024
1 parent 165a6ff commit e4dcc9b
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package de.tracetronic.jenkins.plugins.ecutestexecution.actions

import hudson.FilePath
import hudson.model.Run
import jenkins.model.RunAction2

class ProvideReportLogsAction implements RunAction2 {
private transient Run<?, ?> run

ProvideReportLogsAction(Run<?, ?> run) {
this.run = run
}

@Override
String getIconFileName() {
return "clipboard.png"
}

@Override
String getDisplayName() {
return "ecu.test logs"
}

@Override
String getUrlName() {
return "et-logs"
}

@Override
void onAttached(Run<?, ?> run) {
this.run = run

}

@Override
void onLoad(Run<?, ?> run) {
this.run = run
}

Run<?, ?> getRun() {
return run
}

List<Run.Artifact> getReportLogs() {
return run?.artifacts ?: []
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,6 @@ interface RestApiClient {
* @return List of strings with report IDs
*/
abstract List<String> getAllReportIds()

abstract File downloadReportFolder(String reportID)
}
Original file line number Diff line number Diff line change
Expand Up @@ -215,4 +215,8 @@ class RestApiClientV1 implements RestApiClient {
List<de.tracetronic.cxs.generated.et.client.model.v1.ReportInfo> reports = apiInstance.getAllReports()
return reports*.testReportId
}

File downloadReportFolder(String reportID) {
throw new Exception("Not supported by V1")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -239,4 +239,8 @@ class RestApiClientV2 extends RestApiClientV2WithIdleHandle implements RestApiCl
return reports*.testReportId
}

File downloadReportFolder(String reportID) {
ReportApi apiInstance = new ReportApi(apiClient)
return apiInstance.reportDownload(reportID)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/*
* Copyright (c) 2021-2024 tracetronic GmbH
*
* SPDX-License-Identifier: BSD-3-Clause
*/

package de.tracetronic.jenkins.plugins.ecutestexecution.steps

import com.google.common.collect.ImmutableSet
import de.tracetronic.jenkins.plugins.ecutestexecution.clients.RestApiClient
import de.tracetronic.jenkins.plugins.ecutestexecution.clients.RestApiClientFactory
import de.tracetronic.jenkins.plugins.ecutestexecution.model.GenerationResult
import de.tracetronic.jenkins.plugins.ecutestexecution.actions.ProvideReportLogsAction
import de.tracetronic.jenkins.plugins.ecutestexecution.util.PathUtil

import hudson.EnvVars
import hudson.Extension
import hudson.FilePath
import hudson.Launcher
import hudson.model.Executor
import hudson.model.Run
import hudson.model.TaskListener
import hudson.util.ListBoxModel
import jenkins.model.StandardArtifactManager
import jenkins.security.MasterToSlaveCallable
import org.apache.commons.lang.StringUtils
import org.jenkinsci.plugins.workflow.steps.Step
import org.jenkinsci.plugins.workflow.steps.StepContext
import org.jenkinsci.plugins.workflow.steps.StepDescriptor
import org.jenkinsci.plugins.workflow.steps.StepExecution
import org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution
import org.kohsuke.stapler.DataBoundConstructor
import org.kohsuke.stapler.DataBoundSetter

import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream

class ProvideReportLogsStep extends Step {

@DataBoundConstructor
ProvideReportLogsStep() {
super()
}

@Override
StepExecution start(StepContext context) throws Exception {
return new Execution(this, context)
}

private static List<String> removeEmptyReportIds(List<String> reportIds) {
return reportIds.findAll { id -> StringUtils.isNotBlank(id) }
}

static class Execution extends SynchronousNonBlockingStepExecution<GenerationResult> {

private static final long serialVersionUID = 1L

private final transient ProvideReportLogsStep step

Execution(ProvideReportLogsStep step, StepContext context) {
super(context)
this.step = step
}

@Override
protected GenerationResult run() throws Exception {
def logDirName = "reportLogs"
Run<?, ?> run = context.get(Run.class)
Launcher launcher = context.get(Launcher.class)
EnvVars envVars = context.get(EnvVars.class)
TaskListener listener = context.get(TaskListener.class)

long startTimeMillis = run.getTimeInMillis()
def logDirPath = PathUtil.makeAbsoluteInPipelineHome("${logDirName}", context)

// Download report logs to workspace
List<String> logFiles = []
try {
logFiles = launcher.getChannel().call(
new ExecutionCallable(startTimeMillis, envVars, logDirPath, listener)
)
} catch (Exception e) {
throw e
}

// Archive logs and add to view
if (logFiles) {
FilePath workspace = context.get(FilePath.class)
FilePath[] reportLogs = workspace.list("${logDirName}/*.log")
def artifactManager = new StandardArtifactManager(run)

def artifactsMap = new HashMap<String, String>()
reportLogs.each { log ->
def relativePath = log.getRemote().substring(workspace.getRemote().length() + 1)
artifactsMap.put(relativePath, relativePath)
}

artifactManager.archive(workspace, launcher, listener, artifactsMap)
run.addAction(new ProvideReportLogsAction(run))
listener.logger.flush()
return null
}
}
}

private static final class ExecutionCallable extends MasterToSlaveCallable<List<String>, IOException> {

private static final long serialVersionUID = 1L

private List<String> reportIds
private final long startTimeMillis
private final EnvVars envVars
private final String logDirPath
private final TaskListener listener


ExecutionCallable(long startTimeMillis, EnvVars envVars, String logDirPath, TaskListener listener) {
super()
this.startTimeMillis = startTimeMillis
this.envVars = envVars
this.logDirPath = logDirPath
this.listener = listener
}

@Override
List<String> call() throws IOException {
List<String> logs = []
RestApiClient apiClient = RestApiClientFactory.getRestApiClient(envVars.get('ET_API_HOSTNAME'), envVars.get('ET_API_PORT'))
listener.logger.println("ProvideReportLogsStep ...")
reportIds = apiClient.getAllReportIds()

if (reportIds == null || reportIds.isEmpty()) {
listener.logger.println("[WARNING] No report log files found")
}
reportIds.each { reportId ->
listener.logger.println("Downloading reportFolder for ${reportId}")
File reportFolderZip = apiClient.downloadReportFolder(reportId)


def fileNameToExtract = "test/ecu.test_out.log"
ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(reportFolderZip))
ZipEntry entry

while ((entry = zipInputStream.nextEntry) != null) {
//TODO check old files with startTimeMillis
//Problem:entry.getCreationTime is null
//listener.logger.println("[WARNING] Old files")
if (entry.name == fileNameToExtract) {
def outputFile = new File("${logDirPath}/${reportId}.log")
outputFile.parentFile.mkdirs()

def outputStream = new FileOutputStream(outputFile)
try {
outputStream << zipInputStream
} finally {
outputStream.close()
}

listener.logger.println("Extracted ${fileNameToExtract} to ${logDirPath}")
logs.add(outputFile.name)
}
}
zipInputStream.close()
}
listener.logger.flush()
return logs

}
}

@Extension
static final class DescriptorImpl extends StepDescriptor {
private static final List<String> REPORT_GENERATORS = Arrays.asList('ATX', 'EXCEL', 'HTML', 'JSON', 'OMR',
'TestSpec', 'TRF-SPLIT', 'TXT', 'UNIT')

static ListBoxModel doFillGeneratorNameItems() {
ListBoxModel model = new ListBoxModel()
REPORT_GENERATORS.each { name ->
model.add(name)
}
return model
}

@Override
String getFunctionName() {
'ttProvideReportLogs'
}

@Override
String getDisplayName() {
'[TT] Provide ecu.test report logs'
}

@Override
Set<? extends Class<?>> getRequiredContext() {
return ImmutableSet.of(Launcher.class, EnvVars.class, TaskListener.class)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:st="jelly:stapler">
<l:layout title="${it.displayName}">
<l:side-panel>
<st:include page="sidepanel.jelly" it="${it.run}" optional="true"/>
</l:side-panel>
<l:main-panel>
<h1>${it.displayName}</h1>
<ul>
<j:forEach var="log" items="${it.reportLogs}">
<li>
<td>
<a href="${rootURL}/${it.run.url}artifact/${log.relativePath}">${log.fileName}</a>
</td>
</li>
</j:forEach>
</ul>
</l:main-panel>
</l:layout>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">


</j:jelly>
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,8 @@ class MockRestApiClient implements RestApiClient {
List<String> getAllReportIds() {
return null
}
@Override
File downloadReportFolder(String reportID){
return null
}
}

0 comments on commit e4dcc9b

Please sign in to comment.