Skip to content

Commit c8a1eb7

Browse files
pr comment changes
1 parent e76e91c commit c8a1eb7

File tree

10 files changed

+182
-74
lines changed

10 files changed

+182
-74
lines changed

src/main/java/com/cloudbees/jenkins/support/SupportAction.java

+36-28
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,9 @@
2929
import com.cloudbees.jenkins.support.api.Component;
3030
import com.cloudbees.jenkins.support.api.SupportProvider;
3131
import com.cloudbees.jenkins.support.filter.ContentFilters;
32-
import com.cloudbees.jenkins.support.impl.AboutBrowser;
33-
import com.cloudbees.jenkins.support.impl.ReverseProxy;
32+
import com.cloudbees.jenkins.support.util.MergeZipFileUtil;
3433
import edu.umd.cs.findbugs.annotations.NonNull;
3534
import hudson.Extension;
36-
import hudson.Functions;
3735
import hudson.model.Api;
3836
import hudson.model.Failure;
3937
import hudson.model.RootAction;
@@ -110,6 +108,7 @@ public class SupportAction implements RootAction, StaplerProxy {
110108
private static final String SUPPORT_BUNDLE_CREATION_FOLDER = Paths.get(System.getProperty("java.io.tmpdir"))
111109
.resolve("support-bundle")
112110
.toString();
111+
public static final String SYNC = "sync_";
113112

114113
private static final Map<UUID, SupportBundleAsyncGenerator> generatorByTaskId = new ConcurrentHashMap<>();
115114

@@ -360,25 +359,30 @@ public HttpRedirect doGenerateAllBundlesAsync(StaplerRequest2 req, StaplerRespon
360359
}
361360
final List<Component> components = getComponents(req, json);
362361

363-
for (Component component : components) {
364-
if (component instanceof AboutBrowser) {
365-
AboutBrowser aboutBrowser = (AboutBrowser) component;
366-
aboutBrowser.setScreenResolution(Functions.getScreenResolution());
367-
aboutBrowser.setCurrentRequest(Stapler.getCurrentRequest2());
368-
aboutBrowser.setGeneratedAsync(true);
362+
List<Component> syncComponent = components.stream().filter(c -> !c.canBeGeneratedAsync()).toList();
363+
UUID taskId = UUID.randomUUID();
364+
String supportBundleName = BundleFileName.generate();
365+
366+
if(!syncComponent.isEmpty()){
367+
File outputDir = new File(SUPPORT_BUNDLE_CREATION_FOLDER + "/" + taskId);
368+
if (!outputDir.exists()) {
369+
if (!outputDir.mkdirs()) {
370+
throw new IOException("Failed to create directory: " + outputDir.getAbsolutePath());
371+
}
369372
}
373+
try (FileOutputStream fileOutputStream = new FileOutputStream(new File(outputDir, SYNC+supportBundleName))) {
374+
SupportPlugin.writeBundle(fileOutputStream, syncComponent);
375+
} finally {
376+
logger.fine("Response completed");
370377

371-
if (component instanceof ReverseProxy) {
372-
ReverseProxy reverseProxy = (ReverseProxy) component;
373-
reverseProxy.setCurrentRequest(Stapler.getCurrentRequest2());
374378
}
375379
}
376380

377-
UUID taskId = UUID.randomUUID();
381+
378382
SupportBundleAsyncGenerator supportBundleAsyncGenerator = new SupportBundleAsyncGenerator();
379-
supportBundleAsyncGenerator.init(taskId, components);
383+
supportBundleAsyncGenerator.init(taskId, components.stream().filter(Component::canBeGeneratedAsync).toList()
384+
,supportBundleName, !syncComponent.isEmpty());
380385
generatorByTaskId.put(taskId, supportBundleAsyncGenerator);
381-
382386
return new HttpRedirect("progressPage?taskId=" + taskId);
383387
}
384388

@@ -517,10 +521,13 @@ public static class SupportBundleAsyncGenerator extends ProgressiveRendering {
517521
private List<Component> components;
518522
private boolean supportBundleGenerationInProgress = false;
519523
private String supportBundleName;
524+
private boolean mergeSyncComponents;
520525

521-
public SupportBundleAsyncGenerator init(UUID taskId, List<Component> components) {
526+
public SupportBundleAsyncGenerator init(UUID taskId, List<Component> components,String supportBundleName,boolean mergeSyncComponents) {
522527
this.taskId = taskId;
523528
this.components = components;
529+
this.supportBundleName = supportBundleName;
530+
this.mergeSyncComponents = mergeSyncComponents;
524531
return this;
525532
}
526533

@@ -530,7 +537,6 @@ protected void compute() throws Exception {
530537
logger.fine("Support bundle generation already in progress, for task id " + taskId);
531538
return;
532539
}
533-
this.supportBundleName = BundleFileName.generate();
534540
this.supportBundleGenerationInProgress = true;
535541
logger.fine("Generating support bundle... task id " + taskId);
536542
File outputDir = new File(SUPPORT_BUNDLE_CREATION_FOLDER + "/" + taskId);
@@ -541,23 +547,21 @@ protected void compute() throws Exception {
541547
}
542548

543549
try (FileOutputStream fileOutputStream = new FileOutputStream(new File(outputDir, supportBundleName))) {
544-
SupportPlugin.setRequesterAuthentication(Jenkins.getAuthentication2());
545-
try {
546-
try (ACLContext ignored = ACL.as2(ACL.SYSTEM2)) {
547-
SupportPlugin.writeBundle(fileOutputStream, components);
548-
} catch (IOException e) {
549-
logger.log(Level.WARNING, e.getMessage(), e);
550-
}
551-
} finally {
552-
SupportPlugin.clearRequesterAuthentication();
553-
}
550+
SupportPlugin.writeBundle(fileOutputStream, components,this::progress);
554551
} finally {
555552
logger.fine("Response completed");
556553
}
557554

558555
pathToBundle = outputDir.getAbsolutePath() + "/" + supportBundleName;
559-
isCompleted = true;
556+
if(mergeSyncComponents){
557+
MergeZipFileUtil.mergeZipFiles(outputDir.getAbsolutePath() +"/"+supportBundleName
558+
,outputDir.getAbsolutePath() +"/"+SYNC+supportBundleName
559+
,outputDir.getAbsolutePath() +"/"+"final_"+supportBundleName);
560+
561+
pathToBundle = outputDir.getAbsolutePath() +"/"+"final_"+supportBundleName;
562+
}
560563
progress(1);
564+
isCompleted = true;
561565
}
562566

563567
@NonNull
@@ -578,6 +582,10 @@ public String getSupportBundleName() {
578582
public void doDownloadBundle(@QueryParameter("taskId") String taskId, StaplerResponse2 rsp) throws IOException {
579583
String supportBundleName =
580584
generatorByTaskId.get(UUID.fromString(taskId)).getSupportBundleName();
585+
if(generatorByTaskId.get(UUID.fromString(taskId)).mergeSyncComponents){
586+
supportBundleName = "final_"+supportBundleName;
587+
}
588+
581589
File bundleFile = new File(SUPPORT_BUNDLE_CREATION_FOLDER + "/" + taskId + "/" + supportBundleName);
582590
if (!bundleFile.exists()) {
583591
rsp.sendError(HttpServletResponse.SC_NOT_FOUND, "Support bundle file not found");

src/main/java/com/cloudbees/jenkins/support/SupportPlugin.java

+27-2
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
import java.util.concurrent.TimeUnit;
9898
import java.util.concurrent.TimeoutException;
9999
import java.util.concurrent.atomic.AtomicLong;
100+
import java.util.function.DoubleConsumer;
100101
import java.util.logging.Handler;
101102
import java.util.logging.Level;
102103
import java.util.logging.LogRecord;
@@ -354,12 +355,32 @@ public static void writeBundle(OutputStream outputStream, final List<? extends C
354355
throws IOException {
355356
writeBundle(outputStream, components, new ComponentVisitor() {
356357
@Override
357-
public <T extends Component> void visit(Container container, T component) {
358+
public <T extends Component> void visit(Container container, T component,double progress) {
358359
component.addContents(container);
359360
}
360361
});
361362
}
362363

364+
/**
365+
* Generate a bundle for all components that are selected in the Global Configuration.
366+
*
367+
* @param outputStream an {@link OutputStream}
368+
* @param components a list of {@link Component} to include in the bundle
369+
* @param progressCallback a {@link DoubleConsumer} to report progress back to the UI
370+
* @throws IOException if an error occurs while generating the bundle.
371+
*/
372+
public static void writeBundle(OutputStream outputStream
373+
, final List<? extends Component> components, DoubleConsumer progressCallback)
374+
throws IOException {
375+
writeBundle(outputStream, components, new ComponentVisitor() {
376+
@Override
377+
public <T extends Component> void visit(Container container, T component,double progress) {
378+
component.addContents(container);
379+
progressCallback.accept(progress);
380+
}
381+
});
382+
}
383+
363384
/**
364385
* Generate a bundle for all components that are selected in the Global Configuration.
365386
*
@@ -596,6 +617,8 @@ private static List<Content> appendManifestContents(
596617

597618
manifest.append("Requested components:\n\n");
598619
ContentContainer contentsContainer = new ContentContainer(contentFilter, components);
620+
int totalComponents = components.size() + 1;
621+
int curerrentItration = 0;
599622
for (Component component : components) {
600623
try {
601624
if (components.stream().anyMatch(c -> c.supersedes(component))) {
@@ -605,7 +628,9 @@ private static List<Content> appendManifestContents(
605628
manifest.append(" * ").append(component.getDisplayName()).append("\n\n");
606629
LOGGER.log(Level.FINE, "Start processing " + component.getDisplayName());
607630
long startTime = System.currentTimeMillis();
608-
componentVisitor.visit(contentsContainer, component);
631+
// Calculate progress
632+
double progress = (curerrentItration++) / (double) totalComponents;
633+
componentVisitor.visit(contentsContainer, component,progress);
609634
LOGGER.log(
610635
Level.FINE,
611636
"Took " + (System.currentTimeMillis() - startTime) + "ms" + " to process component "

src/main/java/com/cloudbees/jenkins/support/actions/SupportObjectAction.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public final void doGenerateAndDownload(StaplerRequest2 req, StaplerResponse2 rs
143143
try (ACLContext old = ACL.as2(ACL.SYSTEM2)) {
144144
SupportPlugin.writeBundle(rsp.getOutputStream(), components, new ComponentVisitor() {
145145
@Override
146-
public <C extends Component> void visit(Container container, C component) {
146+
public <C extends Component> void visit(Container container, C component,double progress) {
147147
((ObjectComponent<T>) component).addContents(container, object);
148148
}
149149
});

src/main/java/com/cloudbees/jenkins/support/api/Component.java

+10
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,16 @@ public boolean isSelectedByDefault() {
106106
return true;
107107
}
108108

109+
/**
110+
* This method will indicate if the component can be generated asynchronously.
111+
* This is useful for components that need request context info that only be available in a request thread.
112+
* By default, it will return true.
113+
* @return
114+
*/
115+
public boolean canBeGeneratedAsync(){
116+
return true;
117+
}
118+
109119
/**
110120
* Return if this component is applicable to a specific class of item.
111121
*

src/main/java/com/cloudbees/jenkins/support/api/ComponentVisitor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ public interface ComponentVisitor {
1212
* @param component the {@link Component} being visited
1313
* @param <T> must be a subclass of {@link Component}
1414
*/
15-
<T extends Component> void visit(Container container, T component);
15+
<T extends Component> void visit(Container container, T component,double progress);
1616
}

src/main/java/com/cloudbees/jenkins/support/impl/AboutBrowser.java

+6-22
Original file line numberDiff line numberDiff line change
@@ -37,27 +37,14 @@ public Set<Permission> getRequiredPermissions() {
3737
return Collections.emptySet();
3838
}
3939

40-
private Area screenResolution;
41-
private StaplerRequest2 currentRequest;
42-
private boolean isGeneratedAsync;
43-
44-
public void setScreenResolution(Area screenResolution) {
45-
this.screenResolution = screenResolution;
46-
}
47-
48-
public void setCurrentRequest(StaplerRequest2 currentRequest) {
49-
this.currentRequest = currentRequest;
50-
}
51-
52-
public void setGeneratedAsync(boolean isGeneratedAsync) {
53-
this.isGeneratedAsync = isGeneratedAsync;
40+
@Override
41+
public boolean canBeGeneratedAsync(){
42+
return false;
5443
}
5544

5645
@Override
5746
public void addContents(@NonNull Container result) {
58-
if (currentRequest == null) {
59-
currentRequest = Stapler.getCurrentRequest2();
60-
}
47+
final StaplerRequest2 currentRequest = Stapler.getCurrentRequest2();
6148
if (currentRequest != null) {
6249
result.add(new PrintedContent("browser.md") {
6350
@Override
@@ -66,10 +53,7 @@ protected void printTo(PrintWriter out) throws IOException {
6653
out.println("=======");
6754
out.println();
6855

69-
if (!isGeneratedAsync) {
70-
screenResolution = Functions.getScreenResolution();
71-
}
72-
56+
Area screenResolution = Functions.getScreenResolution();
7357
if (screenResolution != null) {
7458
out.println(" * Screen size: " + screenResolution.toString());
7559
}
@@ -107,4 +91,4 @@ public boolean shouldBeFiltered() {
10791
public ComponentCategory getCategory() {
10892
return ComponentCategory.MISC;
10993
}
110-
}
94+
}

src/main/java/com/cloudbees/jenkins/support/impl/ReverseProxy.java

+7-10
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ public enum Trilean {
2727
UNKNOWN
2828
}
2929

30-
private StaplerRequest2 currentRequest;
31-
3230
// [RFC 7239, section 4: Forwarded](https://tools.ietf.org/html/rfc7239#section-4) standard header.
3331
private static final String FORWARDED_HEADER = "Forwarded";
3432
// Non-standard headers.
@@ -44,6 +42,11 @@ public enum Trilean {
4442
X_FORWARDED_HOST_HEADER,
4543
X_FORWARDED_PORT_HEADER);
4644

45+
@Override
46+
public boolean canBeGeneratedAsync(){
47+
return false;
48+
}
49+
4750
@NonNull
4851
@Override
4952
public Set<Permission> getRequiredPermissions() {
@@ -63,9 +66,7 @@ public void addContents(@NonNull Container container) {
6366
protected void printTo(PrintWriter out) {
6467
out.println("Reverse Proxy");
6568
out.println("=============");
66-
if (currentRequest == null) {
67-
currentRequest = getCurrentRequest();
68-
}
69+
StaplerRequest2 currentRequest = getCurrentRequest();
6970
for (String forwardedHeader : FORWARDED_HEADERS) {
7071
out.println(String.format(
7172
" * Detected `%s` header: %s",
@@ -97,8 +98,4 @@ private Trilean isForwardedHeaderDetected(StaplerRequest2 req, String header) {
9798
protected StaplerRequest2 getCurrentRequest() {
9899
return Stapler.getCurrentRequest2();
99100
}
100-
101-
public void setCurrentRequest(StaplerRequest2 currentRequest) {
102-
this.currentRequest = currentRequest;
103-
}
104-
}
101+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.cloudbees.jenkins.support.util;
2+
3+
import java.io.FileInputStream;
4+
import java.io.FileOutputStream;
5+
import java.io.IOException;
6+
import java.util.zip.ZipEntry;
7+
import java.util.zip.ZipException;
8+
import java.util.zip.ZipInputStream;
9+
import java.util.zip.ZipOutputStream;
10+
11+
public class MergeZipFileUtil {
12+
13+
14+
public static void mergeZipFiles(String zipFile1, String zipFile2, String outputZip) throws IOException {
15+
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outputZip))) {
16+
addZipContentsToOutput(zipFile1, zos);
17+
addZipContentsToOutput(zipFile2, zos);
18+
}
19+
}
20+
21+
private static void addZipContentsToOutput(String zipFile, ZipOutputStream zos) throws IOException {
22+
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) {
23+
ZipEntry entry;
24+
while ((entry = zis.getNextEntry()) != null) {
25+
// Avoid duplicate file names by modifying if necessary
26+
String entryName = entry.getName();
27+
try {
28+
zos.putNextEntry(new ZipEntry(entryName));
29+
}catch (ZipException e){
30+
continue;
31+
}
32+
33+
byte[] buffer = new byte[4096];
34+
int len;
35+
while ((len = zis.read(buffer)) > 0) {
36+
zos.write(buffer, 0, len);
37+
}
38+
39+
zos.closeEntry();
40+
zis.closeEntry();
41+
}
42+
}
43+
}
44+
}

src/main/resources/com/cloudbees/jenkins/support/SupportAction/progressPage.jelly

+1-10
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,17 @@
1010
<h2>Generating Support Bundle</h2>
1111
<p id="progressMessage">Generating a support bundle for this Jenkins instance. This may take a few minutes.</p>
1212
<l:progressiveRendering handler="${it.getGeneratorByTaskId(request.getParameter('taskId'))}" callback="updateProgressUI"/>
13-
<l:progressAnimation/>
1413
<a id="downloadButton" style="display:none;" class="btn btn-primary" href="#">Download Support Bundle</a>
1514
</l:main-panel>
1615

1716
<script>
1817
function updateProgressUI(data) {
19-
var progressBar = document.querySelector('.app-progress-bar');
20-
if (progressBar) {
21-
progressBar.style.display = 'none';
22-
}
2318
if (data.isCompleted === true) {
24-
var ellipsisProgressBar = document.querySelector('.lds-ellipsis');
25-
if (ellipsisProgressBar) {
26-
ellipsisProgressBar.style.display = 'none';
27-
}
2819
downloadButton.style.display = "block";
2920
downloadButton.href = "downloadBundle?taskId=" + data.taskId; // Update this path accordingly
3021
document.getElementById("progressMessage").textContent = "Support bundle has been generated.";
3122

32-
// Show the back button
23+
// Show the back button
3324
var backButton = document.createElement("a");
3425
backButton.className = "btn btn-secondary";
3526
backButton.href = "${rootURL}/support";

0 commit comments

Comments
 (0)