Backporting for LTS 2.504.2 (#10645)
Label conflicting PRs / main (push) Has been cancelled Details

This commit is contained in:
Mark Waite 2025-05-13 16:16:43 -06:00 committed by GitHub
commit 70c1c98da4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 221 additions and 4 deletions

View File

@ -2186,12 +2186,14 @@ public class Functions {
/**
* Generate a series of {@code <script>} tags to include {@code script.js}
* from {@link ConsoleAnnotatorFactory}s and {@link ConsoleAnnotationDescriptor}s.
*
* @see hudson.console.ConsoleAnnotatorFactory.RootAction
*/
public static String generateConsoleAnnotationScriptAndStylesheet() {
String cp = Stapler.getCurrentRequest2().getContextPath() + Jenkins.RESOURCE_PATH;
StringBuilder buf = new StringBuilder();
for (ConsoleAnnotatorFactory f : ConsoleAnnotatorFactory.all()) {
String path = cp + "/extensionList/" + ConsoleAnnotatorFactory.class.getName() + "/" + f.getClass().getName();
String path = cp + "/" + ConsoleAnnotatorFactory.class.getName() + "/" + f.getClass().getName();
if (f.hasScript())
buf.append("<script src='").append(path).append("/script.js'></script>");
if (f.hasStylesheet())

View File

@ -27,6 +27,7 @@ package hudson.console;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.model.InvisibleAction;
import hudson.model.Run;
import jakarta.servlet.ServletException;
import java.io.IOException;
@ -35,6 +36,8 @@ import java.lang.reflect.Type;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import org.jvnet.tiger_types.Types;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.StaplerRequest2;
import org.kohsuke.stapler.StaplerResponse2;
import org.kohsuke.stapler.WebMethod;
@ -129,4 +132,24 @@ public abstract class ConsoleAnnotatorFactory<T> implements ExtensionPoint {
public static ExtensionList<ConsoleAnnotatorFactory> all() {
return ExtensionList.lookup(ConsoleAnnotatorFactory.class);
}
/**
* This action makes {@link hudson.console.ConsoleAnnotatorFactory} instances accessible via HTTP.
*
* @see hudson.Functions#generateConsoleAnnotationScriptAndStylesheet
* @see ConsoleAnnotatorFactory#hasStylesheet()
* @see ConsoleAnnotatorFactory#hasScript()
*/
@Restricted(NoExternalUse.class)
@Extension
public static class RootAction extends InvisibleAction implements hudson.model.RootAction {
@Override
public String getUrlName() {
return ConsoleAnnotatorFactory.class.getName();
}
public ConsoleAnnotatorFactory<?> getDynamic(String className) {
return all().getDynamic(className);
}
}
}

View File

@ -0,0 +1,53 @@
/*
* The MIT License
*
* Copyright (c) 2025, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.diagnosis;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.diagnosis.MemoryUsageMonitor;
import hudson.model.InvisibleAction;
import hudson.model.RootAction;
import jenkins.model.Jenkins;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
/**
* Expose {@link hudson.diagnosis.MemoryUsageMonitor#heap} at the {@code /hudson.diagnosis.MemoryUsageMonitor/heap} URL.
*
* @since TODO
*/
@Extension
@Restricted(NoExternalUse.class)
public class MemoryUsageMonitorAction extends InvisibleAction implements RootAction {
@Override
public String getUrlName() {
return MemoryUsageMonitorAction.class.getName();
}
public MemoryUsageMonitor.MemoryGroup getHeap() {
Jenkins.get().checkAnyPermission(Jenkins.SYSTEM_READ, Jenkins.MANAGE);
return ExtensionList.lookupSingleton(MemoryUsageMonitor.class).heap;
}
}

View File

@ -2825,11 +2825,13 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
}
/**
* Used to bind {@link ExtensionList}s to URLs.
* Formerly used to bind {@link ExtensionList}s to URLs.
* <p>
* Currently handled by {@link jenkins.telemetry.impl.HttpExtensionList.ExtensionListRootAction}.
* </p>
*
* @since 1.349
*/
@StaplerDispatchable
public ExtensionList getExtensionList(String extensionType) throws ClassNotFoundException {
return getExtensionList(pluginManager.uberClassLoader.loadClass(extensionType));
}

View File

@ -0,0 +1,124 @@
/*
* The MIT License
*
* Copyright (c) 2025, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.telemetry.impl;
import static java.util.logging.Level.FINE;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.model.InvisibleAction;
import hudson.model.RootAction;
import java.time.LocalDate;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import jenkins.model.Jenkins;
import jenkins.telemetry.Telemetry;
import net.sf.json.JSONObject;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest2;
/**
* Collect information about which URLs in {@code /extensionList/} are being accessed.
*
* @since TODO
*/
@Extension
@Restricted(NoExternalUse.class)
public class HttpExtensionList extends Telemetry {
private final Map<String, Integer> calls = new ConcurrentHashMap<>();
@NonNull
@Override
public String getDisplayName() {
return "Extension List access via HTTP";
}
@NonNull
@Override
public LocalDate getStart() {
return LocalDate.of(2025, 4, 5);
}
@NonNull
@Override
public LocalDate getEnd() {
return LocalDate.of(2025, 7, 1);
}
@Override
public synchronized JSONObject createContent() {
JSONObject info = new JSONObject();
info.put("components", buildComponentInformation());
Map<String, Integer> currentRequests = new TreeMap<>(calls);
calls.clear();
JSONObject payload = new JSONObject();
payload.putAll(currentRequests);
info.put("dispatches", payload);
return info;
}
public synchronized void record(String path) {
String[] parts = path.split("/");
if (parts.length > 1) {
// Record just extension point + implementation class
path = parts[0] + '/' + parts[1];
}
calls.compute(path, (p, v) -> v == null ? 1 : v + 1);
}
@Extension
@Restricted(NoExternalUse.class)
public static class ExtensionListRootAction extends InvisibleAction implements RootAction {
private static final Logger LOGGER = Logger.getLogger(ExtensionListRootAction.class.getName());
@Override
public String getUrlName() {
return "extensionList";
}
public ExtensionList getDynamic(String extensionType) throws ClassNotFoundException {
StaplerRequest2 req = Stapler.getCurrentRequest2();
if (req != null && extensionType != null) {
try {
final HttpExtensionList telemetry = ExtensionList.lookupSingleton(HttpExtensionList.class);
if (telemetry.isActivePeriod()) {
telemetry.record(extensionType + req.getRestOfPath());
}
} catch (Exception ex) {
LOGGER.log(FINE, "Failed to record telemetry for " + HttpExtensionList.class.getName(), ex);
}
}
return Jenkins.get().getExtensionList(extensionType);
}
}
}

View File

@ -0,0 +1,9 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core">
<p>
${%blurb}
</p>
<p>
${%blurb2}
</p>
</j:jelly>

View File

@ -0,0 +1,4 @@
blurb = This trial records uses of an HTTP endpoint granting direct access to extensions, the building blocks of Jenkins's extensibility through plugins. \
It is expected that there are few legitimate uses of this endpoint, and this trial informs plans for its eventual removal.
blurb2 = Additionally, this trial collects the list of installed plugins, their version, and the version of Jenkins. \
This data will be used to understand the environments that Jenkins is running in.

View File

@ -10,7 +10,7 @@ graphHost.style.aspectRatio = `${imageWidth} / ${imageHeight}`;
timespanSelect.addEventListener("change", () => {
const rootURL = document.head.dataset.rooturl;
const type = timespanSelect.value;
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"/>`;
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"/>`;
});
// Dispatch a change event to insert a graph on page load