Skip to content

Commit 70c1c98

Browse files
authored
Backporting for LTS 2.504.2 (#10645)
2 parents 7abdf02 + 41ed4f3 commit 70c1c98

File tree

8 files changed

+221
-4
lines changed

8 files changed

+221
-4
lines changed

core/src/main/java/hudson/Functions.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2186,12 +2186,14 @@ public static Locale getCurrentLocale() {
21862186
/**
21872187
* Generate a series of {@code <script>} tags to include {@code script.js}
21882188
* from {@link ConsoleAnnotatorFactory}s and {@link ConsoleAnnotationDescriptor}s.
2189+
*
2190+
* @see hudson.console.ConsoleAnnotatorFactory.RootAction
21892191
*/
21902192
public static String generateConsoleAnnotationScriptAndStylesheet() {
21912193
String cp = Stapler.getCurrentRequest2().getContextPath() + Jenkins.RESOURCE_PATH;
21922194
StringBuilder buf = new StringBuilder();
21932195
for (ConsoleAnnotatorFactory f : ConsoleAnnotatorFactory.all()) {
2194-
String path = cp + "/extensionList/" + ConsoleAnnotatorFactory.class.getName() + "/" + f.getClass().getName();
2196+
String path = cp + "/" + ConsoleAnnotatorFactory.class.getName() + "/" + f.getClass().getName();
21952197
if (f.hasScript())
21962198
buf.append("<script src='").append(path).append("/script.js'></script>");
21972199
if (f.hasStylesheet())

core/src/main/java/hudson/console/ConsoleAnnotatorFactory.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import hudson.Extension;
2828
import hudson.ExtensionList;
2929
import hudson.ExtensionPoint;
30+
import hudson.model.InvisibleAction;
3031
import hudson.model.Run;
3132
import jakarta.servlet.ServletException;
3233
import java.io.IOException;
@@ -35,6 +36,8 @@
3536
import java.net.URL;
3637
import java.util.concurrent.TimeUnit;
3738
import org.jvnet.tiger_types.Types;
39+
import org.kohsuke.accmod.Restricted;
40+
import org.kohsuke.accmod.restrictions.NoExternalUse;
3841
import org.kohsuke.stapler.StaplerRequest2;
3942
import org.kohsuke.stapler.StaplerResponse2;
4043
import org.kohsuke.stapler.WebMethod;
@@ -129,4 +132,24 @@ public void doStyleCss(StaplerRequest2 req, StaplerResponse2 rsp) throws IOExcep
129132
public static ExtensionList<ConsoleAnnotatorFactory> all() {
130133
return ExtensionList.lookup(ConsoleAnnotatorFactory.class);
131134
}
135+
136+
/**
137+
* This action makes {@link hudson.console.ConsoleAnnotatorFactory} instances accessible via HTTP.
138+
*
139+
* @see hudson.Functions#generateConsoleAnnotationScriptAndStylesheet
140+
* @see ConsoleAnnotatorFactory#hasStylesheet()
141+
* @see ConsoleAnnotatorFactory#hasScript()
142+
*/
143+
@Restricted(NoExternalUse.class)
144+
@Extension
145+
public static class RootAction extends InvisibleAction implements hudson.model.RootAction {
146+
@Override
147+
public String getUrlName() {
148+
return ConsoleAnnotatorFactory.class.getName();
149+
}
150+
151+
public ConsoleAnnotatorFactory<?> getDynamic(String className) {
152+
return all().getDynamic(className);
153+
}
154+
}
132155
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* The MIT License
3+
*
4+
* Copyright (c) 2025, CloudBees, Inc.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
25+
package jenkins.diagnosis;
26+
27+
import hudson.Extension;
28+
import hudson.ExtensionList;
29+
import hudson.diagnosis.MemoryUsageMonitor;
30+
import hudson.model.InvisibleAction;
31+
import hudson.model.RootAction;
32+
import jenkins.model.Jenkins;
33+
import org.kohsuke.accmod.Restricted;
34+
import org.kohsuke.accmod.restrictions.NoExternalUse;
35+
36+
/**
37+
* Expose {@link hudson.diagnosis.MemoryUsageMonitor#heap} at the {@code /hudson.diagnosis.MemoryUsageMonitor/heap} URL.
38+
*
39+
* @since TODO
40+
*/
41+
@Extension
42+
@Restricted(NoExternalUse.class)
43+
public class MemoryUsageMonitorAction extends InvisibleAction implements RootAction {
44+
@Override
45+
public String getUrlName() {
46+
return MemoryUsageMonitorAction.class.getName();
47+
}
48+
49+
public MemoryUsageMonitor.MemoryGroup getHeap() {
50+
Jenkins.get().checkAnyPermission(Jenkins.SYSTEM_READ, Jenkins.MANAGE);
51+
return ExtensionList.lookupSingleton(MemoryUsageMonitor.class).heap;
52+
}
53+
}

core/src/main/java/jenkins/model/Jenkins.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2825,11 +2825,13 @@ public <T> ExtensionList<T> getExtensionList(Class<T> extensionType) {
28252825
}
28262826

28272827
/**
2828-
* Used to bind {@link ExtensionList}s to URLs.
2828+
* Formerly used to bind {@link ExtensionList}s to URLs.
2829+
* <p>
2830+
* Currently handled by {@link jenkins.telemetry.impl.HttpExtensionList.ExtensionListRootAction}.
2831+
* </p>
28292832
*
28302833
* @since 1.349
28312834
*/
2832-
@StaplerDispatchable
28332835
public ExtensionList getExtensionList(String extensionType) throws ClassNotFoundException {
28342836
return getExtensionList(pluginManager.uberClassLoader.loadClass(extensionType));
28352837
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
* The MIT License
3+
*
4+
* Copyright (c) 2025, CloudBees, Inc.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
25+
package jenkins.telemetry.impl;
26+
27+
import static java.util.logging.Level.FINE;
28+
29+
import edu.umd.cs.findbugs.annotations.NonNull;
30+
import hudson.Extension;
31+
import hudson.ExtensionList;
32+
import hudson.model.InvisibleAction;
33+
import hudson.model.RootAction;
34+
import java.time.LocalDate;
35+
import java.util.Map;
36+
import java.util.TreeMap;
37+
import java.util.concurrent.ConcurrentHashMap;
38+
import java.util.logging.Logger;
39+
import jenkins.model.Jenkins;
40+
import jenkins.telemetry.Telemetry;
41+
import net.sf.json.JSONObject;
42+
import org.kohsuke.accmod.Restricted;
43+
import org.kohsuke.accmod.restrictions.NoExternalUse;
44+
import org.kohsuke.stapler.Stapler;
45+
import org.kohsuke.stapler.StaplerRequest2;
46+
47+
/**
48+
* Collect information about which URLs in {@code /extensionList/} are being accessed.
49+
*
50+
* @since TODO
51+
*/
52+
@Extension
53+
@Restricted(NoExternalUse.class)
54+
public class HttpExtensionList extends Telemetry {
55+
private final Map<String, Integer> calls = new ConcurrentHashMap<>();
56+
57+
@NonNull
58+
@Override
59+
public String getDisplayName() {
60+
return "Extension List access via HTTP";
61+
}
62+
63+
@NonNull
64+
@Override
65+
public LocalDate getStart() {
66+
return LocalDate.of(2025, 4, 5);
67+
}
68+
69+
@NonNull
70+
@Override
71+
public LocalDate getEnd() {
72+
return LocalDate.of(2025, 7, 1);
73+
}
74+
75+
@Override
76+
public synchronized JSONObject createContent() {
77+
JSONObject info = new JSONObject();
78+
info.put("components", buildComponentInformation());
79+
80+
Map<String, Integer> currentRequests = new TreeMap<>(calls);
81+
calls.clear();
82+
83+
JSONObject payload = new JSONObject();
84+
payload.putAll(currentRequests);
85+
86+
info.put("dispatches", payload);
87+
return info;
88+
}
89+
90+
public synchronized void record(String path) {
91+
String[] parts = path.split("/");
92+
if (parts.length > 1) {
93+
// Record just extension point + implementation class
94+
path = parts[0] + '/' + parts[1];
95+
}
96+
calls.compute(path, (p, v) -> v == null ? 1 : v + 1);
97+
}
98+
99+
@Extension
100+
@Restricted(NoExternalUse.class)
101+
public static class ExtensionListRootAction extends InvisibleAction implements RootAction {
102+
private static final Logger LOGGER = Logger.getLogger(ExtensionListRootAction.class.getName());
103+
104+
@Override
105+
public String getUrlName() {
106+
return "extensionList";
107+
}
108+
109+
public ExtensionList getDynamic(String extensionType) throws ClassNotFoundException {
110+
StaplerRequest2 req = Stapler.getCurrentRequest2();
111+
if (req != null && extensionType != null) {
112+
try {
113+
final HttpExtensionList telemetry = ExtensionList.lookupSingleton(HttpExtensionList.class);
114+
if (telemetry.isActivePeriod()) {
115+
telemetry.record(extensionType + req.getRestOfPath());
116+
}
117+
} catch (Exception ex) {
118+
LOGGER.log(FINE, "Failed to record telemetry for " + HttpExtensionList.class.getName(), ex);
119+
}
120+
}
121+
return Jenkins.get().getExtensionList(extensionType);
122+
}
123+
}
124+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?jelly escape-by-default='true'?>
2+
<j:jelly xmlns:j="jelly:core">
3+
<p>
4+
${%blurb}
5+
</p>
6+
<p>
7+
${%blurb2}
8+
</p>
9+
</j:jelly>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
blurb = This trial records uses of an HTTP endpoint granting direct access to extensions, the building blocks of Jenkins's extensibility through plugins. \
2+
It is expected that there are few legitimate uses of this endpoint, and this trial informs plans for its eventual removal.
3+
blurb2 = Additionally, this trial collects the list of installed plugins, their version, and the version of Jenkins. \
4+
This data will be used to understand the environments that Jenkins is running in.

src/main/js/pages/manage-jenkins/system-information/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ graphHost.style.aspectRatio = `${imageWidth} / ${imageHeight}`;
1010
timespanSelect.addEventListener("change", () => {
1111
const rootURL = document.head.dataset.rooturl;
1212
const type = timespanSelect.value;
13-
graphHost.innerHTML = `<img src="${rootURL}/extensionList/hudson.diagnosis.MemoryUsageMonitor/0/heap/graph?type=${type}&width=${imageWidth}&height=${imageHeight}" srcset="${rootURL}/extensionList/hudson.diagnosis.MemoryUsageMonitor/0/heap/graph?type=${type}&width=${imageWidth}&height=${imageHeight}&scale=2 2x" loading="lazy" style="width: 100%" alt="Memory usage graph"/>`;
13+
graphHost.innerHTML = `<img src="${rootURL}/jenkins.diagnosis.MemoryUsageMonitorAction/heap/graph?type=${type}&width=${imageWidth}&height=${imageHeight}" srcset="${rootURL}/jenkins.diagnosis.MemoryUsageMonitorAction/heap/graph?type=${type}&width=${imageWidth}&height=${imageHeight}&scale=2 2x" loading="lazy" style="width: 100%" alt="Memory usage graph"/>`;
1414
});
1515

1616
// Dispatch a change event to insert a graph on page load

0 commit comments

Comments
 (0)