mirror of
https://github.com/jenkinsci/jenkins.git
synced 2026-11-13 12:02:20 +00:00
[JENKINS-70059] Improve Safe Restart interface and user information (#7355)
Co-authored-by: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com> Co-authored-by: Alexander Brandes <mc.cache@web.de> Co-authored-by: Tim Jacomb <timjacomb1@gmail.com>
This commit is contained in:
27 files changed
+277
-174
No files matched your search
@@ -41,8 +41,29 @@ import org.kohsuke.stapler.StaplerResponse;
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public class HudsonIsRestarting {
|
||||
private boolean safeRestart;
|
||||
|
||||
/**
|
||||
* @since TODO
|
||||
*/
|
||||
public HudsonIsRestarting(boolean safeRestart) {
|
||||
this.safeRestart = safeRestart;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public HudsonIsRestarting() {
|
||||
this.safeRestart = false;
|
||||
}
|
||||
|
||||
public void doDynamic(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, InterruptedException {
|
||||
rsp.setStatus(SC_SERVICE_UNAVAILABLE);
|
||||
req.getView(this, "index.jelly").forward(req, rsp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since TODO
|
||||
*/
|
||||
public boolean isSafeRestart() {
|
||||
return safeRestart;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2023, Jan Meiswinkel
|
||||
*
|
||||
* 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.cli;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.cli.CLICommand;
|
||||
import hudson.cli.Messages;
|
||||
import java.util.logging.Logger;
|
||||
import jenkins.model.Jenkins;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
import org.kohsuke.args4j.Option;
|
||||
|
||||
/**
|
||||
* Safe Restart Jenkins - do not accept any new jobs and try to pause existing.
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
@Extension
|
||||
@Restricted(NoExternalUse.class)
|
||||
public class SafeRestartCommand extends CLICommand {
|
||||
private static final Logger LOGGER = Logger.getLogger(SafeRestartCommand.class.getName());
|
||||
|
||||
@Option(name = "-message", usage = "Message for safe restart that will be visible to users")
|
||||
public String message = null;
|
||||
|
||||
@Override
|
||||
public String getShortDescription() {
|
||||
return Messages.SafeRestartCommand_ShortDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int run() throws Exception {
|
||||
Jenkins.get().doSafeRestart(null, message);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -482,6 +482,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
||||
|
||||
@CheckForNull
|
||||
private transient volatile QuietDownInfo quietDownInfo;
|
||||
|
||||
private transient volatile boolean terminating;
|
||||
@GuardedBy("Jenkins.class")
|
||||
private transient boolean cleanUpStarted;
|
||||
@@ -2943,6 +2944,20 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
||||
return quietDownInfo != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the quietingDown is a safe restart.
|
||||
* @since TODO
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
@NonNull
|
||||
public boolean isPreparingSafeRestart() {
|
||||
QuietDownInfo quietDownInfo = this.quietDownInfo;
|
||||
if (quietDownInfo != null) {
|
||||
return quietDownInfo.isSafeRestart();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns quiet down reason if it was indicated.
|
||||
* @return
|
||||
@@ -2953,7 +2968,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
||||
@CheckForNull
|
||||
public String getQuietDownReason() {
|
||||
final QuietDownInfo info = quietDownInfo;
|
||||
return info != null ? info.reason : null;
|
||||
return info != null ? info.message : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4093,7 +4108,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
||||
*
|
||||
* @param block Block until the system really quiets down and no builds are running
|
||||
* @param timeout If non-zero, only block up to the specified number of milliseconds
|
||||
* @deprecated since 2.267; use {@link #doQuietDown(boolean, int, String)} instead.
|
||||
* @deprecated since 2.267; use {@link #doQuietDown(boolean, int, String, boolean)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public synchronized HttpRedirect doQuietDown(boolean block, int timeout) {
|
||||
@@ -4109,16 +4124,34 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
||||
*
|
||||
* @param block Block until the system really quiets down and no builds are running
|
||||
* @param timeout If non-zero, only block up to the specified number of milliseconds
|
||||
* @param reason Quiet reason that will be visible to user
|
||||
* @since 2.267
|
||||
* @param message Quiet reason that will be visible to user
|
||||
* @deprecated use {@link #doQuietDown(boolean, int, String, boolean)} instead.
|
||||
*/
|
||||
@Deprecated(since = "TODO")
|
||||
public HttpRedirect doQuietDown(boolean block,
|
||||
int timeout,
|
||||
@CheckForNull String message) throws InterruptedException, IOException {
|
||||
|
||||
return doQuietDown(block, timeout, message, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quiet down Jenkins - preparation for a restart
|
||||
*
|
||||
* @param block Block until the system really quiets down and no builds are running
|
||||
* @param timeout If non-zero, only block up to the specified number of milliseconds
|
||||
* @param message Quiet reason that will be visible to user
|
||||
* @param safeRestart If the quietDown is for a safeRestart
|
||||
* @since TODO
|
||||
*/
|
||||
@RequirePOST
|
||||
public HttpRedirect doQuietDown(@QueryParameter boolean block,
|
||||
@QueryParameter int timeout,
|
||||
@QueryParameter @CheckForNull String reason) throws InterruptedException, IOException {
|
||||
@QueryParameter @CheckForNull String message,
|
||||
@QueryParameter boolean safeRestart) throws InterruptedException, IOException {
|
||||
synchronized (this) {
|
||||
checkPermission(MANAGE);
|
||||
quietDownInfo = new QuietDownInfo(reason);
|
||||
quietDownInfo = new QuietDownInfo(message, safeRestart);
|
||||
}
|
||||
if (block) {
|
||||
long waitUntil = timeout;
|
||||
@@ -4513,20 +4546,35 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues up a restart of Jenkins for when there are no builds running, if we can.
|
||||
* Queues up a safe restart of Jenkins.
|
||||
* Builds that cannot continue while the controller is not running have to finish or pause before it can proceed.
|
||||
* No new builds will be started. No new jobs are accepted.
|
||||
*
|
||||
* This first replaces "app" to {@link HudsonIsRestarting}
|
||||
* @deprecated use {@link #doSafeRestart(StaplerRequest, String)} instead.
|
||||
*
|
||||
* @since 1.332
|
||||
*/
|
||||
@CLIMethod(name = "safe-restart")
|
||||
@Deprecated(since = "TODO")
|
||||
public HttpResponse doSafeRestart(StaplerRequest req) throws IOException, ServletException, RestartNotSupportedException {
|
||||
return doSafeRestart(req, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues up a safe restart of Jenkins. Jobs have to finish or pause before it can proceed. No new jobs are accepted.
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
public HttpResponse doSafeRestart(StaplerRequest req, @QueryParameter("message") String message) throws IOException, ServletException, RestartNotSupportedException {
|
||||
checkPermission(MANAGE);
|
||||
if (req != null && req.getMethod().equals("GET"))
|
||||
if (req != null && req.getMethod().equals("GET")) {
|
||||
return HttpResponses.forwardToView(this, "_safeRestart.jelly");
|
||||
}
|
||||
|
||||
if (req != null && req.getParameter("cancel") != null) {
|
||||
return doCancelQuietDown();
|
||||
}
|
||||
|
||||
if (req == null || req.getMethod().equals("POST")) {
|
||||
safeRestart();
|
||||
safeRestart(message);
|
||||
}
|
||||
|
||||
return HttpResponses.redirectToDot();
|
||||
@@ -4572,11 +4620,22 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
||||
/**
|
||||
* Queues up a restart to be performed once there are no builds currently running.
|
||||
* @since 1.332
|
||||
* @deprecated use {@link #safeRestart(String)} instead.
|
||||
*/
|
||||
@Deprecated(since = "TODO")
|
||||
public void safeRestart() throws RestartNotSupportedException {
|
||||
safeRestart(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues up a restart to be performed once there are no builds currently running.
|
||||
* @param message the message to show to users in the shutdown banner.
|
||||
* @since TODO
|
||||
*/
|
||||
public void safeRestart(String message) throws RestartNotSupportedException {
|
||||
final Lifecycle lifecycle = restartableLifecycle();
|
||||
// Quiet down so that we won't launch new builds.
|
||||
quietDownInfo = new QuietDownInfo();
|
||||
quietDownInfo = new QuietDownInfo(message, true);
|
||||
|
||||
new Thread("safe-restart thread") {
|
||||
final String exitUser = getAuthentication2().getName();
|
||||
@@ -4585,11 +4644,10 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
||||
try (ACLContext ctx = ACL.as2(ACL.SYSTEM2)) {
|
||||
|
||||
// Wait 'til we have no active executors.
|
||||
doQuietDown(true, 0, null);
|
||||
|
||||
doQuietDown(true, 0, message, true);
|
||||
// Make sure isQuietingDown is still true.
|
||||
if (isQuietingDown()) {
|
||||
servletContext.setAttribute("app", new HudsonIsRestarting());
|
||||
servletContext.setAttribute("app", new HudsonIsRestarting(true));
|
||||
// give some time for the browser to load the "reloading" page
|
||||
lifecycle.onStatusUpdate("Restart in 10 seconds");
|
||||
Thread.sleep(TimeUnit.SECONDS.toMillis(10));
|
||||
@@ -5761,16 +5819,31 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
||||
}
|
||||
|
||||
private static final class QuietDownInfo {
|
||||
|
||||
@CheckForNull
|
||||
final String reason;
|
||||
final String message;
|
||||
|
||||
private boolean safeRestart;
|
||||
|
||||
QuietDownInfo() {
|
||||
this(null);
|
||||
this(null, false);
|
||||
}
|
||||
|
||||
QuietDownInfo(final String reason) {
|
||||
this.reason = reason;
|
||||
QuietDownInfo(final String message) {
|
||||
this(message, false);
|
||||
}
|
||||
|
||||
QuietDownInfo(final String message, final boolean safeRestart) {
|
||||
this.message = message;
|
||||
this.safeRestart = safeRestart;
|
||||
}
|
||||
|
||||
|
||||
boolean isSafeRestart() {
|
||||
return safeRestart;
|
||||
}
|
||||
|
||||
void setSafeRestart(boolean safeRestart) {
|
||||
this.safeRestart = safeRestart;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user