Skip to content

Commit

Permalink
Merge pull request jenkinsci#9645 from Vlatombe/with-console-url
Browse files Browse the repository at this point in the history
  • Loading branch information
Vlatombe authored Sep 9, 2024
2 parents b56e5d7 + 2f6d77c commit bfcb874
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 24 deletions.
20 changes: 7 additions & 13 deletions core/src/main/java/hudson/Functions.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
import hudson.model.ParameterDefinition;
import hudson.model.ParameterDefinition.ParameterDescriptor;
import hudson.model.PasswordParameterDefinition;
import hudson.model.Queue;
import hudson.model.Run;
import hudson.model.Slave;
import hudson.model.TimeZoneProperty;
Expand Down Expand Up @@ -157,6 +156,7 @@
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import jenkins.console.ConsoleUrlProvider;
import jenkins.console.WithConsoleUrl;
import jenkins.model.GlobalConfiguration;
import jenkins.model.GlobalConfigurationCategory;
import jenkins.model.Jenkins;
Expand Down Expand Up @@ -1981,20 +1981,14 @@ public static String joinPath(String... components) {
}

/**
* Computes the link to the console for the run for the specified executable, taking {@link ConsoleUrlProvider} into account.
* @param executable the executable (normally a {@link Run})
* @return the absolute URL for accessing the build console for the executable, or null if there is no build associated with the executable
* Computes the link to the console for the run for the specified object, taking {@link ConsoleUrlProvider} into account.
* @param withConsoleUrl the object to compute a console url for (can be {@link Run}, a {@code PlaceholderExecutable}...)
* @return the absolute URL for accessing the build console for the given object, or null if there is no console URL defined for the object.
* @since 2.433
*/
public static @CheckForNull String getConsoleUrl(Queue.Executable executable) {
if (executable == null) {
return null;
} else if (executable instanceof Run) {
return ConsoleUrlProvider.getRedirectUrl((Run<?, ?>) executable);
} else {
// Handles cases such as PlaceholderExecutable for Pipeline node steps.
return getConsoleUrl(executable.getParentExecutable());
}
public static @CheckForNull String getConsoleUrl(WithConsoleUrl withConsoleUrl) {
String consoleUrl = withConsoleUrl.getConsoleUrl();
return consoleUrl != null ? Stapler.getCurrentRequest().getContextPath() + '/' + consoleUrl : null;
}

/**
Expand Down
13 changes: 12 additions & 1 deletion core/src/main/java/hudson/model/Queue.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import jenkins.console.WithConsoleUrl;
import jenkins.model.Jenkins;
import jenkins.model.queue.AsynchronousExecution;
import jenkins.model.queue.CompositeCauseOfBlockage;
Expand Down Expand Up @@ -2088,7 +2089,7 @@ default Collection<? extends SubTask> getSubTasks() {
* used to render the HTML that indicates this executable is executing.
*/
@StaplerAccessibleType
public interface Executable extends Runnable {
public interface Executable extends Runnable, WithConsoleUrl {
/**
* Task from which this executable was created.
*
Expand Down Expand Up @@ -2131,6 +2132,16 @@ default long getEstimatedDuration() {
return Executables.getParentOf(this).getEstimatedDuration();
}

/**
* Handles cases such as {@code PlaceholderExecutable} for Pipeline node steps.
* @return by default, that of {@link #getParentExecutable} if defined
*/
@Override
default String getConsoleUrl() {
Executable parent = getParentExecutable();
return parent != null ? parent.getConsoleUrl() : null;
}

/**
* Used to render the HTML. Should be a human readable text of what this executable is.
*/
Expand Down
12 changes: 11 additions & 1 deletion core/src/main/java/hudson/model/Run.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
import jenkins.console.ConsoleUrlProvider;
import jenkins.console.WithConsoleUrl;
import jenkins.model.ArtifactManager;
import jenkins.model.ArtifactManagerConfiguration;
import jenkins.model.ArtifactManagerFactory;
Expand Down Expand Up @@ -157,7 +159,7 @@
*/
@ExportedBean
public abstract class Run<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, RunT>>
extends Actionable implements ExtensionPoint, Comparable<RunT>, AccessControlled, PersistenceRoot, DescriptorByNameOwner, OnMaster, StaplerProxy {
extends Actionable implements ExtensionPoint, Comparable<RunT>, AccessControlled, PersistenceRoot, DescriptorByNameOwner, OnMaster, StaplerProxy, WithConsoleUrl {

/**
* The original {@link Queue.Item#getId()} has not yet been mapped onto the {@link Run} instance.
Expand Down Expand Up @@ -1059,6 +1061,14 @@ protected void dropLinks() {
return project.getUrl() + getNumber() + '/';
}

/**
* @see ConsoleUrlProvider#consoleUrlOf
*/
@Override
public String getConsoleUrl() {
return ConsoleUrlProvider.consoleUrlOf(this);
}

/**
* Obtains the absolute URL to this build.
*
Expand Down
20 changes: 13 additions & 7 deletions core/src/main/java/jenkins/console/ConsoleUrlProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ public interface ConsoleUrlProvider extends Describable<ConsoleUrlProvider> {
* Get a URL relative to the context path of Jenkins which should be used to link to the console for the specified build.
* <p>Should only be used in the context of serving an HTTP request.
* @param run the build
* @return the URL for the console for the specified build, relative to the context of Jenkins, or {@code null}
* if this implementation does not want to server a special console view for this build.
* @return the URL for the console for the specified build, relative to the context of Jenkins (should not start with {@code /}), or {@code null}
* if this implementation does not want to serve a special console view for this build.
*/
@CheckForNull String getConsoleUrl(Run<?, ?> run);

Expand All @@ -80,6 +80,14 @@ default Descriptor<ConsoleUrlProvider> getDescriptor() {
* @return the URL for the console for the specified build, relative to the web server root
*/
static @NonNull String getRedirectUrl(Run<?, ?> run) {
return Stapler.getCurrentRequest().getContextPath() + '/' + run.getConsoleUrl();
}

/**
* Looks up the {@link #getConsoleUrl} value from the first provider to offer one.
* @since TODO
*/
static @NonNull String consoleUrlOf(Run<?, ?> run) {
final List<ConsoleUrlProvider> providers = new ArrayList<>();
User currentUser = User.current();
if (currentUser != null) {
Expand All @@ -104,6 +112,8 @@ default Descriptor<ConsoleUrlProvider> getDescriptor() {
if (tempUrl != null) {
if (new URI(tempUrl).isAbsolute()) {
LOGGER.warning(() -> "Ignoring absolute console URL " + tempUrl + " for " + run + " from " + provider.getClass());
} else if (tempUrl.startsWith("/")) {
LOGGER.warning(() -> "Ignoring URL " + tempUrl + " starting with / for " + run + " from " + provider.getClass());
} else {
// Found a valid non-null URL.
url = tempUrl;
Expand All @@ -118,11 +128,7 @@ default Descriptor<ConsoleUrlProvider> getDescriptor() {
// Reachable if DefaultConsoleUrlProvider is not one of the configured providers, including if no providers are configured at all.
url = run.getUrl() + "console";
}
if (url.startsWith("/")) {
return Stapler.getCurrentRequest2().getContextPath() + url;
} else {
return Stapler.getCurrentRequest2().getContextPath() + '/' + url;
}
return url;
}

/**
Expand Down
43 changes: 43 additions & 0 deletions core/src/main/java/jenkins/console/WithConsoleUrl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* The MIT License
*
* Copyright 2024 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.console;

import edu.umd.cs.findbugs.annotations.CheckForNull;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.Beta;

/**
* A model object that may have a console URL.
*/
@Restricted(Beta.class)
public interface WithConsoleUrl {

/**
* @return a URL relative to the context root without leading slash, such as {@code job/xxx/123/console};
* or null if unknown or not applicable
*/
@CheckForNull
String getConsoleUrl();
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ public void getRedirectUrl() throws Exception {
// Custom URL without leading slash
b.setDescription("custom my/build/console");
assertCustomConsoleUrl(r.contextPath + "/my/build/console", b);
// Custom URL with leading slash
// Custom URL with leading slash -> not supported, falls back to default
b.setDescription("custom /my/build/console");
assertCustomConsoleUrl(r.contextPath + "/my/build/console", b);
assertCustomConsoleUrl(r.contextPath + '/' + b.getUrl() + "console", b);
// Default URL is used when extensions throw exceptions.
b.setDescription("NullPointerException");
assertCustomConsoleUrl(r.contextPath + '/' + b.getUrl() + "console", b);
Expand Down

0 comments on commit bfcb874

Please sign in to comment.