JENKINS-1877 Add ability to specify shut down reason (#4274)

Co-authored-by: Oleg Nenashev <o.v.nenashev@gmail.com>
Co-authored-by: Daniel Beck <1831569+daniel-beck@users.noreply.github.com>
Co-authored-by: Tim Jacomb <t.jacomb@kainos.com>
Co-authored-by: Ilya Zasimov <austry@yandex-team.ru>
This commit is contained in:
authored and GitHub committed 2020-11-17 09:44:16 +00:00
1 parent bd59971eac
commit 15cfddf2a7
10 files changed
+196 -30

No files matched your search

@@ -47,6 +47,9 @@ public class QuietDownCommand extends CLICommand {
@Option(name="-timeout",usage="If non-zero, only block up to the specified number of milliseconds")
public int timeout = 0;
@Option(name="-reason",usage="Reason for quiet down that will be visible to users")
public String reason = null;
@Override
public String getShortDescription() {
return Messages.QuietDownCommand_ShortDescription();
@@ -54,7 +57,7 @@ public class QuietDownCommand extends CLICommand {
@Override
protected int run() throws Exception {
Jenkins.get().doQuietDown(block, timeout);
Jenkins.get().doQuietDown(block, timeout, reason);
return 0;
}
}
@@ -28,38 +28,61 @@ import hudson.Extension;
import hudson.model.ManagementLink;
import hudson.security.Permission;
import jenkins.model.Jenkins;
import net.sf.json.JSONObject;
import org.jenkinsci.Symbol;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.verb.POST;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import edu.umd.cs.findbugs.annotations.NonNull;
/**
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
*/
@Extension(ordinal = Integer.MIN_VALUE) @Symbol("shutDown")
@Extension(ordinal = Integer.MIN_VALUE)
@Symbol("prepareQuietDown")
public class ShutdownLink extends ManagementLink {
private static final Logger LOGGER = Logger.getLogger(ShutdownLink.class.getName());
@Override
public String getIconFileName() {
return "system-log-out.png";
}
public String getDisplayName() {
return Jenkins.get().isQuietingDown() ? Messages.ShutdownLink_DisplayName_cancel() : Messages.ShutdownLink_DisplayName_prepare();
return Jenkins.get().isQuietingDown() ? Messages.ShutdownLink_DisplayName_update() : Messages.ShutdownLink_DisplayName_prepare();
}
@Override
public String getDescription() {
return Jenkins.get().isQuietingDown() ? "" : Messages.ShutdownLink_Description();
return Jenkins.get().isQuietingDown() ? Messages.ShutdownLink_ShuttingDownInProgressDescription() : Messages.ShutdownLink_Description();
}
@Override
public String getUrlName() {
return Jenkins.get().isQuietingDown() ? "cancelQuietDown" : "quietDown";
return "prepareShutdown";
}
@Override
public boolean getRequiresPOST() {
return true;
@POST
public synchronized void doPrepare(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, InterruptedException {
Jenkins.get().checkPermission(Jenkins.MANAGE);
JSONObject submittedForm = req.getSubmittedForm();
String inputReason = submittedForm.getString("shutdownReason");
String shutdownReason = inputReason.isEmpty() ? null : inputReason;
LOGGER.log(Level.FINE, "Shutdown requested by user {0}", Jenkins.getAuthentication().getName());
Jenkins.get().doQuietDown(false, 0, shutdownReason).generateResponse(req, rsp, null);
}
@POST
public synchronized void doCancel(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
Jenkins.get().checkPermission(Jenkins.MANAGE);
LOGGER.log(Level.FINE, "Shutdown cancel requested by user {0}", Jenkins.getAuthentication().getName());
Jenkins.get().doCancelQuietDown().generateResponse(req, rsp, null);
}
@NonNull
+61 -13
View File
@@ -453,7 +453,8 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
*/
private static Jenkins theInstance;
private transient volatile boolean isQuietingDown;
@CheckForNull
private transient volatile QuietDownInfo quietDownInfo;
private transient volatile boolean terminating;
@GuardedBy("Jenkins.class")
private transient boolean cleanUpStarted;
@@ -2799,7 +2800,20 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
*/
@Exported
public boolean isQuietingDown() {
return isQuietingDown;
return quietDownInfo != null;
}
/**
* Returns quiet down reason if it was indicated.
* @return
* Reason if it was indicated. null otherwise
* @since TODO
*/
@Exported
@CheckForNull
public String getQuietDownReason() {
final QuietDownInfo info = quietDownInfo;
return info != null ? info.reason : null;
}
/**
@@ -3877,7 +3891,24 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
@RequirePOST
public synchronized HttpRedirect doQuietDown() {
try {
return doQuietDown(false,0);
return doQuietDown(false, 0, null);
} catch (IOException | InterruptedException e) {
throw new AssertionError(); // impossible
}
}
/**
* Quiet down Jenkins - preparation for a restart
* Presented for compatibility.
*
* @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 TODO; use {@link #doQuietDown(boolean, int, String)} instead.
*/
@Deprecated
public synchronized HttpRedirect doQuietDown(boolean block, int timeout) {
try {
return doQuietDown(block, timeout, null);
} catch (IOException | InterruptedException e) {
throw new AssertionError(); // impossible
}
@@ -3888,17 +3919,21 @@ 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 TODO
*/
@RequirePOST
public HttpRedirect doQuietDown(@QueryParameter boolean block, @QueryParameter int timeout) throws InterruptedException, IOException {
public HttpRedirect doQuietDown(@QueryParameter boolean block,
@QueryParameter int timeout,
@QueryParameter @CheckForNull String reason) throws InterruptedException, IOException {
synchronized (this) {
checkPermission(MANAGE);
isQuietingDown = true;
quietDownInfo = new QuietDownInfo(reason);
}
if (block) {
long waitUntil = timeout;
if (timeout > 0) waitUntil += System.currentTimeMillis();
while (isQuietingDown
while (isQuietingDown()
&& (timeout <= 0 || System.currentTimeMillis() < waitUntil)
&& !RestartListener.isAllReady()) {
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
@@ -3913,7 +3948,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
@RequirePOST
public synchronized HttpRedirect doCancelQuietDown() {
checkPermission(MANAGE);
isQuietingDown = false;
quietDownInfo = null;
getQueue().scheduleMaintenance();
return new HttpRedirect(".");
}
@@ -4335,7 +4370,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
public void safeRestart() throws RestartNotSupportedException {
final Lifecycle lifecycle = restartableLifecycle();
// Quiet down so that we won't launch new builds.
isQuietingDown = true;
quietDownInfo = new QuietDownInfo();
new Thread("safe-restart thread") {
final String exitUser = getAuthentication2().getName();
@@ -4344,10 +4379,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);
doQuietDown(true, 0, null);
// Make sure isQuietingDown is still true.
if (isQuietingDown) {
if (isQuietingDown()) {
servletContext.setAttribute("app",new HudsonIsRestarting());
// give some time for the browser to load the "reloading" page
LOGGER.info("Restart in 10 seconds");
@@ -4432,7 +4467,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
@RequirePOST
public HttpResponse doSafeExit(StaplerRequest req) throws IOException {
checkPermission(ADMINISTER);
isQuietingDown = true;
quietDownInfo = new QuietDownInfo();
final String exitUser = getAuthentication2().getName();
final String exitAddr = req!=null ? req.getRemoteAddr() : "unknown";
new Thread("safe-exit thread") {
@@ -4443,9 +4478,9 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
LOGGER.info(String.format("Shutting down VM as requested by %s from %s",
exitUser, exitAddr));
// Wait 'til we have no active executors.
doQuietDown(true, 0);
doQuietDown(true, 0, null);
// Make sure isQuietingDown is still true.
if (isQuietingDown) {
if (isQuietingDown()) {
cleanUp();
System.exit(0);
}
@@ -5425,4 +5460,17 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
}
}
private static final class QuietDownInfo {
@CheckForNull
final String reason;
QuietDownInfo() {
this(null);
}
QuietDownInfo(final String reason) {
this.reason = reason;
}
}
}