Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overreporting fix #18

Merged
merged 2 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 0 additions & 36 deletions src/main/java/io/jenkins/plugins/launchable/GraphListenerImpl.java

This file was deleted.

61 changes: 32 additions & 29 deletions src/main/java/io/jenkins/plugins/launchable/Ingester.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import hudson.tasks.junit.TestResult;
import hudson.util.Secret;
import jenkins.model.GlobalConfiguration;
import jenkins.util.Timer;
import net.sf.json.JSONObject;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
Expand All @@ -17,6 +18,7 @@
import java.util.logging.Level;
import java.util.logging.Logger;

import static java.util.logging.Level.*;
import static org.apache.http.entity.ContentType.APPLICATION_JSON;
import static org.apache.http.entity.ContentType.APPLICATION_OCTET_STREAM;

Expand Down Expand Up @@ -47,44 +49,45 @@ public boolean configure(StaplerRequest req, JSONObject json) throws FormExcepti
* @param properties Additional contextual data to submit along with the test results.
*/
/*package*/ void slurp(File dir, PropsBuilder<?> properties) throws IOException {
try {
File report = new File(dir, "junitResult.xml");
if (!report.exists()) return; // be defensive just in case
File report = new File(dir, "junitResult.xml");
if (!report.exists()) return; // be defensive just in case

if (apiKey==null) return; // not yet configured
Timer.get().execute(() -> {// don't slow down people's builds and risk getting kicked out
try {
if (apiKey==null) return; // not yet configured
OrganizationWorkspace orgWs = OrganizationWorkspace.fromApiKey(apiKey.getPlainText());

OrganizationWorkspace orgWs = OrganizationWorkspace.fromApiKey(apiKey.getPlainText());
// attempted to use JDK HttpRequest, but gave up due to the lack of multipart support
// TODO: how do I obtain a properly configured HttpClient for the proxy setting in Jenkins?
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
String endpoint = System.getProperty("INSIGHT_UPLOAD_URL") ;

// attempted to use JDK HttpRequest, but gave up due to the lack of multipart support
// TODO: how do I obtain a properly configured HttpClient for the proxy setting in Jenkins?
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
String endpoint = System.getProperty("INSIGHT_UPLOAD_URL") ;

if (endpoint==null) {
endpoint = DEFAULT_UPLOAD_URL;
}
HttpPost hc = new HttpPost(String.format("%s/intake/organizations/%s/workspaces/%s/events/jenkins", endpoint, orgWs.getOrganization(), orgWs.getWorkspace()));
if (endpoint==null) {
endpoint = DEFAULT_UPLOAD_URL;
}
HttpPost hc = new HttpPost(String.format("%s/intake/organizations/%s/workspaces/%s/events/jenkins", endpoint, orgWs.getOrganization(), orgWs.getWorkspace()));

MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody("metadata", properties.build().toString(), APPLICATION_JSON);
builder.addPart("file", new GzipFileMimePart(report, APPLICATION_OCTET_STREAM, "junitResult.xml.gz"));
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody("metadata", properties.build().toString(), APPLICATION_JSON);
builder.addPart("file", new GzipFileMimePart(report, APPLICATION_OCTET_STREAM, "junitResult.xml.gz"));

hc.setEntity(builder.build());
hc.addHeader("Authorization", "Bearer " + apiKey.getPlainText());
hc.setEntity(builder.build());
hc.addHeader("Authorization", "Bearer " + apiKey.getPlainText());

try (CloseableHttpResponse response = httpClient.execute(hc)) {
if (response.getStatusLine().getStatusCode() >= 300) {
// treat redirect as an error, for the time being. we submit a big payload, so we don't want
// to be forced to repeat the payload after we send the whole thing once.
LOGGER.log(Level.WARNING, "Failed to submit test results: {0}", response.getStatusLine());
try (CloseableHttpResponse response = httpClient.execute(hc)) {
if (response.getStatusLine().getStatusCode() >= 300) {
// treat redirect as an error, for the time being. we submit a big payload, so we don't want
// to be forced to repeat the payload after we send the whole thing once.
LOGGER.log(WARNING, "Failed to submit test results: {0}", response.getStatusLine());
}
}
}
} catch (Exception e) {
// don't let our bug get in the way of orderly execution of jobs, as that'd be the fasest way to
// get kicked out of installations.
LOGGER.log(WARNING, "Failed to submit test results", e);
}
} catch (Exception e) {
// don't let our bug get in the way of orderly execution of jobs, as that'd be the fasest way to
// get kicked out of installations.
LOGGER.log(Level.WARNING, "Failed to submit test results", e);
}
});
}

private static final Logger LOGGER = Logger.getLogger(Ingester.class.getName());
Expand Down
40 changes: 40 additions & 0 deletions src/main/java/io/jenkins/plugins/launchable/RunListenerImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.jenkins.plugins.launchable;

import hudson.Extension;
import hudson.model.listeners.RunListener;
import org.jenkinsci.plugins.workflow.flow.GraphListener;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;

import javax.inject.Inject;
import java.util.logging.Logger;

import static java.util.logging.Level.*;

/**
* Records test results reported into {@link WorkflowRun}.
*
* <p>
* Our earlier attempt to implement {@link GraphListener} backfired, as when 'junitReport' step was called
* multiple times, we end up reporting all test results accumulated up to that point, resulting in duplicate
* data reporting.
*
* <p>
* This approach defers the data ingestion all the way down to the end of a workflow run.
*/
@Extension
public class RunListenerImpl extends RunListener<WorkflowRun> {
@Inject
Ingester ingester;

@Override
public void onFinalized(WorkflowRun run) {
try {
ingester.slurp(run.getRootDir(), new PropsBuilder<>(run));
} catch (Exception e) {
// Priority #1: Do no harm to people's build
LOGGER.log(WARNING, "Failed to send JUnit result to Launchable", e);
}
}

private static final Logger LOGGER = Logger.getLogger("io.jenkins.plugins.launchable.RunListenerImpl");
}
Loading