mirror of
https://github.com/jenkinsci/jenkins.git
synced 2026-11-14 04:22:16 +00:00
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:
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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user