From 7cf2d0973da4c5af71b232fe46dd1e74556697ae Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 29 Jul 2025 22:42:54 -0400 Subject: [PATCH 1/3] Discard spare instances during shutdown (cherry picked from commit 9269c7628054f852caefd0e945faab537b58f5ab) --- .../hudson/plugins/ec2/EC2AbstractSlave.java | 3 +- .../hudson/plugins/ec2/EC2OndemandSlave.java | 8 +++- .../java/hudson/plugins/ec2/EC2SpotSlave.java | 8 +++- .../ec2/util/MinimumInstanceChecker.java | 47 ++++++++++++++++--- .../plugins/ec2/EC2AbstractSlaveTest.java | 11 +++-- .../plugins/ec2/EC2RetentionStrategyTest.java | 13 +++-- .../hudson/plugins/ec2/MockEC2Computer.java | 6 ++- .../ec2/ssh/InitScriptExecutionTest.java | 6 ++- 8 files changed, 82 insertions(+), 20 deletions(-) diff --git a/src/main/java/hudson/plugins/ec2/EC2AbstractSlave.java b/src/main/java/hudson/plugins/ec2/EC2AbstractSlave.java index c2173cc70..ce72ea86c 100644 --- a/src/main/java/hudson/plugins/ec2/EC2AbstractSlave.java +++ b/src/main/java/hudson/plugins/ec2/EC2AbstractSlave.java @@ -44,6 +44,7 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; @@ -757,7 +758,7 @@ public static Instance getInstance(String instanceId, EC2Cloud cloud) { /** * Terminates the instance in EC2. */ - public abstract void terminate(); + public abstract Future terminate(); void stop() { try { diff --git a/src/main/java/hudson/plugins/ec2/EC2OndemandSlave.java b/src/main/java/hudson/plugins/ec2/EC2OndemandSlave.java index cd605b1ec..1d8b51b57 100644 --- a/src/main/java/hudson/plugins/ec2/EC2OndemandSlave.java +++ b/src/main/java/hudson/plugins/ec2/EC2OndemandSlave.java @@ -12,6 +12,8 @@ import java.io.IOException; import java.util.Collections; import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; import jenkins.model.Jenkins; @@ -503,11 +505,11 @@ public EC2OndemandSlave(String instanceId) throws FormException, IOException { * Terminates the instance in EC2. */ @Override - public void terminate() { + public Future terminate() { if (terminateScheduled.getCount() == 0) { synchronized (terminateScheduled) { if (terminateScheduled.getCount() == 0) { - Computer.threadPoolForRemoting.submit(() -> { + Future f = Computer.threadPoolForRemoting.submit(() -> { try { if (!isAlive(true)) { /* @@ -533,9 +535,11 @@ public void terminate() { } }); terminateScheduled.reset(); + return f; } } } + return CompletableFuture.completedFuture(null); } @Override diff --git a/src/main/java/hudson/plugins/ec2/EC2SpotSlave.java b/src/main/java/hudson/plugins/ec2/EC2SpotSlave.java index 787e80c86..7256dfe57 100644 --- a/src/main/java/hudson/plugins/ec2/EC2SpotSlave.java +++ b/src/main/java/hudson/plugins/ec2/EC2SpotSlave.java @@ -11,6 +11,8 @@ import java.io.IOException; import java.util.Collections; import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; import jenkins.model.Jenkins; @@ -182,11 +184,11 @@ protected boolean isAlive(boolean force) { * Cancel the spot request for the instance. Terminate the instance if it is up. Remove the agent from Jenkins. */ @Override - public void terminate() { + public Future terminate() { if (terminateScheduled.getCount() == 0) { synchronized (terminateScheduled) { if (terminateScheduled.getCount() == 0) { - Computer.threadPoolForRemoting.submit(() -> { + Future f = Computer.threadPoolForRemoting.submit(() -> { try { // Cancel the spot request Ec2Client ec2 = getCloud().connect(); @@ -246,9 +248,11 @@ public void terminate() { } }); terminateScheduled.reset(); + return f; } } } + return CompletableFuture.completedFuture(null); } /** diff --git a/src/main/java/hudson/plugins/ec2/util/MinimumInstanceChecker.java b/src/main/java/hudson/plugins/ec2/util/MinimumInstanceChecker.java index ff3ec2167..b72cfa3ba 100644 --- a/src/main/java/hudson/plugins/ec2/util/MinimumInstanceChecker.java +++ b/src/main/java/hudson/plugins/ec2/util/MinimumInstanceChecker.java @@ -2,17 +2,24 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import hudson.init.Terminator; import hudson.model.Computer; import hudson.model.Label; import hudson.model.Queue; +import hudson.plugins.ec2.EC2AbstractSlave; import hudson.plugins.ec2.EC2Cloud; import hudson.plugins.ec2.EC2Computer; import hudson.plugins.ec2.SlaveTemplate; import java.time.Clock; import java.time.LocalDateTime; import java.time.LocalTime; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Objects; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; import java.util.stream.Stream; import jenkins.model.Jenkins; import org.kohsuke.accmod.Restricted; @@ -21,14 +28,17 @@ @Restricted(NoExternalUse.class) public class MinimumInstanceChecker { + private static final Logger LOGGER = Logger.getLogger(MinimumInstanceChecker.class.getName()); + @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "Needs to be overridden from tests") public static Clock clock = Clock.systemDefaultZone(); - private static Stream agentsForTemplate(@NonNull SlaveTemplate agentTemplate) { + private static Stream agentsForTemplate(@NonNull SlaveTemplate agentTemplate) { return Arrays.stream(Jenkins.get().getComputers()) .filter(EC2Computer.class::isInstance) + .map(EC2Computer.class::cast) .filter(computer -> { - SlaveTemplate computerTemplate = ((EC2Computer) computer).getSlaveTemplate(); + SlaveTemplate computerTemplate = computer.getSlaveTemplate(); return computerTemplate != null && Objects.equals(computerTemplate.description, agentTemplate.description); }); @@ -38,11 +48,14 @@ public static int countCurrentNumberOfAgents(@NonNull SlaveTemplate agentTemplat return (int) agentsForTemplate(agentTemplate).count(); } - public static int countCurrentNumberOfSpareAgents(@NonNull SlaveTemplate agentTemplate) { - return (int) agentsForTemplate(agentTemplate) + private static Stream spareAgents(@NonNull SlaveTemplate agentTemplate) { + return agentsForTemplate(agentTemplate) .filter(computer -> computer.countBusy() == 0) - .filter(Computer::isOnline) - .count(); + .filter(Computer::isOnline); + } + + public static int countCurrentNumberOfSpareAgents(@NonNull SlaveTemplate agentTemplate) { + return (int) spareAgents(agentTemplate).count(); } public static int countCurrentNumberOfProvisioningAgents(@NonNull SlaveTemplate agentTemplate) { @@ -142,4 +155,26 @@ public static boolean minimumInstancesActive( } return false; } + + @Terminator + public static void discardSpareInstances() throws Exception { + LOGGER.fine("Looking for spare instances to discard"); + List> futures = new ArrayList<>(); + Jenkins.get().clouds.stream() + .filter(EC2Cloud.class::isInstance) + .map(EC2Cloud.class::cast) + .forEach(cloud -> cloud.getTemplates().forEach(agentTemplate -> spareAgents(agentTemplate) + .limit(agentTemplate.getMinimumNumberOfSpareInstances()) + .forEach(computer -> { + EC2AbstractSlave agent = computer.getNode(); + if (agent != null) { + LOGGER.info(() -> "discarding spare instance " + agent.getInstanceId()); + futures.add(agent.terminate()); + } + }))); + // Must wait; otherwise task could run too late during shutdown, leading to NoClassDefFoundError. + for (Future future : futures) { + future.get(5, TimeUnit.SECONDS); + } + } } diff --git a/src/test/java/hudson/plugins/ec2/EC2AbstractSlaveTest.java b/src/test/java/hudson/plugins/ec2/EC2AbstractSlaveTest.java index 1ad00f217..2ddf79d50 100644 --- a/src/test/java/hudson/plugins/ec2/EC2AbstractSlaveTest.java +++ b/src/test/java/hudson/plugins/ec2/EC2AbstractSlaveTest.java @@ -5,6 +5,8 @@ import hudson.model.Node; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.jvnet.hudson.test.JenkinsRule; @@ -58,9 +60,8 @@ void testGetLaunchTimeoutInMillisShouldNotOverflow() throws Exception { EC2AbstractSlave.DEFAULT_ENCLAVE_ENABLED) { @Override - public void terminate() { - // To change body of implemented methods use File | Settings | - // File Templates. + public Future terminate() { + return CompletableFuture.completedFuture(null); } @Override @@ -159,7 +160,9 @@ void testMaxUsesBackwardCompat() throws Exception { EC2AbstractSlave.DEFAULT_METADATA_SUPPORTED, EC2AbstractSlave.DEFAULT_ENCLAVE_ENABLED) { @Override - public void terminate() {} + public Future terminate() { + return CompletableFuture.completedFuture(null); + } @Override public String getEc2Type() { diff --git a/src/test/java/hudson/plugins/ec2/EC2RetentionStrategyTest.java b/src/test/java/hudson/plugins/ec2/EC2RetentionStrategyTest.java index b2bb01647..ae93103d9 100644 --- a/src/test/java/hudson/plugins/ec2/EC2RetentionStrategyTest.java +++ b/src/test/java/hudson/plugins/ec2/EC2RetentionStrategyTest.java @@ -34,6 +34,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; @@ -251,7 +253,9 @@ private EC2Computer computerWithIdleTime( EC2AbstractSlave.DEFAULT_METADATA_SUPPORTED, EC2AbstractSlave.DEFAULT_ENCLAVE_ENABLED) { @Override - public void terminate() {} + public Future terminate() { + return CompletableFuture.completedFuture(null); + } @Override public String getEc2Type() { @@ -396,7 +400,9 @@ private EC2Computer computerWithUpTime( EC2AbstractSlave.DEFAULT_METADATA_SUPPORTED, EC2AbstractSlave.DEFAULT_ENCLAVE_ENABLED) { @Override - public void terminate() {} + public Future terminate() { + return CompletableFuture.completedFuture(null); + } @Override public String getEc2Type() { @@ -562,8 +568,9 @@ private EC2Computer computerWithUsageLimit(final int usageLimit) throws Exceptio EC2AbstractSlave.DEFAULT_METADATA_SUPPORTED, EC2AbstractSlave.DEFAULT_ENCLAVE_ENABLED) { @Override - public void terminate() { + public Future terminate() { terminateCalled.set(true); + return CompletableFuture.completedFuture(null); } @Override diff --git a/src/test/java/hudson/plugins/ec2/MockEC2Computer.java b/src/test/java/hudson/plugins/ec2/MockEC2Computer.java index 502d7bd2c..fdc00745b 100644 --- a/src/test/java/hudson/plugins/ec2/MockEC2Computer.java +++ b/src/test/java/hudson/plugins/ec2/MockEC2Computer.java @@ -3,6 +3,8 @@ import hudson.model.Node; import java.util.ArrayList; import java.util.Collections; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; import software.amazon.awssdk.core.exception.SdkException; import software.amazon.awssdk.services.ec2.model.InstanceType; @@ -55,7 +57,9 @@ public static MockEC2Computer createComputer(String suffix) throws Exception { EC2AbstractSlave.DEFAULT_METADATA_SUPPORTED, EC2AbstractSlave.DEFAULT_ENCLAVE_ENABLED) { @Override - public void terminate() {} + public Future terminate() { + return CompletableFuture.completedFuture(null); + } @Override public String getEc2Type() { diff --git a/src/test/java/hudson/plugins/ec2/ssh/InitScriptExecutionTest.java b/src/test/java/hudson/plugins/ec2/ssh/InitScriptExecutionTest.java index 8ae1a54c8..91d842b33 100644 --- a/src/test/java/hudson/plugins/ec2/ssh/InitScriptExecutionTest.java +++ b/src/test/java/hudson/plugins/ec2/ssh/InitScriptExecutionTest.java @@ -16,6 +16,8 @@ import java.io.PrintStream; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import org.apache.sshd.client.SshClient; @@ -223,7 +225,9 @@ private EC2AbstractSlave getMockNodeTemplate(String initScript) throws Exception EC2AbstractSlave.DEFAULT_METADATA_HOPS_LIMIT, EC2AbstractSlave.DEFAULT_METADATA_SUPPORTED) { @Override - public void terminate() {} + public Future terminate() { + return CompletableFuture.completedFuture(null); + } @Override public String getEc2Type() { From e4ac68f71207e411152a74ae63b781775653bf45 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 1 Aug 2025 15:57:51 -0400 Subject: [PATCH 2/3] Introduce `terminateSpareInstances` option https://github.com/jenkinsci/ec2-plugin/pull/1125#issuecomment-3143785544 (cherry picked from commit e8c5a475d98760460565385b4b18a59544ffa052) --- .../hudson/plugins/ec2/SlaveTemplate.java | 11 ++++++++++ .../ec2/util/MinimumInstanceChecker.java | 20 ++++++++++--------- .../plugins/ec2/SlaveTemplate/config.jelly | 4 ++++ .../help-terminateSpareInstances.html | 5 +++++ 4 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 src/main/resources/hudson/plugins/ec2/SlaveTemplate/help-terminateSpareInstances.html diff --git a/src/main/java/hudson/plugins/ec2/SlaveTemplate.java b/src/main/java/hudson/plugins/ec2/SlaveTemplate.java index 51891379b..41cebc09d 100644 --- a/src/main/java/hudson/plugins/ec2/SlaveTemplate.java +++ b/src/main/java/hudson/plugins/ec2/SlaveTemplate.java @@ -202,6 +202,8 @@ public class SlaveTemplate implements Describable { private final int minimumNumberOfSpareInstances; + private boolean terminateSpareInstances; + public final boolean stopOnTerminate; private final List tags; @@ -1705,6 +1707,15 @@ public int getMinimumNumberOfSpareInstances() { return minimumNumberOfSpareInstances; } + public boolean getTerminateSpareInstances() { + return terminateSpareInstances; + } + + @DataBoundSetter + public void setTerminateSpareInstances(boolean terminateSpareInstances) { + this.terminateSpareInstances = terminateSpareInstances; + } + public MinimumNumberOfInstancesTimeRangeConfig getMinimumNumberOfInstancesTimeRangeConfig() { return minimumNumberOfInstancesTimeRangeConfig; } diff --git a/src/main/java/hudson/plugins/ec2/util/MinimumInstanceChecker.java b/src/main/java/hudson/plugins/ec2/util/MinimumInstanceChecker.java index b72cfa3ba..281cc8850 100644 --- a/src/main/java/hudson/plugins/ec2/util/MinimumInstanceChecker.java +++ b/src/main/java/hudson/plugins/ec2/util/MinimumInstanceChecker.java @@ -163,15 +163,17 @@ public static void discardSpareInstances() throws Exception { Jenkins.get().clouds.stream() .filter(EC2Cloud.class::isInstance) .map(EC2Cloud.class::cast) - .forEach(cloud -> cloud.getTemplates().forEach(agentTemplate -> spareAgents(agentTemplate) - .limit(agentTemplate.getMinimumNumberOfSpareInstances()) - .forEach(computer -> { - EC2AbstractSlave agent = computer.getNode(); - if (agent != null) { - LOGGER.info(() -> "discarding spare instance " + agent.getInstanceId()); - futures.add(agent.terminate()); - } - }))); + .forEach(cloud -> cloud.getTemplates().stream() + .filter(SlaveTemplate::getTerminateSpareInstances) + .forEach(agentTemplate -> spareAgents(agentTemplate) + .limit(agentTemplate.getMinimumNumberOfSpareInstances()) + .forEach(computer -> { + EC2AbstractSlave agent = computer.getNode(); + if (agent != null) { + LOGGER.info(() -> "discarding spare instance " + agent.getInstanceId()); + futures.add(agent.terminate()); + } + }))); // Must wait; otherwise task could run too late during shutdown, leading to NoClassDefFoundError. for (Future future : futures) { future.get(5, TimeUnit.SECONDS); diff --git a/src/main/resources/hudson/plugins/ec2/SlaveTemplate/config.jelly b/src/main/resources/hudson/plugins/ec2/SlaveTemplate/config.jelly index d8fdfe429..8fa9209f9 100644 --- a/src/main/resources/hudson/plugins/ec2/SlaveTemplate/config.jelly +++ b/src/main/resources/hudson/plugins/ec2/SlaveTemplate/config.jelly @@ -154,6 +154,10 @@ THE SOFTWARE. + + + + diff --git a/src/main/resources/hudson/plugins/ec2/SlaveTemplate/help-terminateSpareInstances.html b/src/main/resources/hudson/plugins/ec2/SlaveTemplate/help-terminateSpareInstances.html new file mode 100644 index 000000000..6e859253a --- /dev/null +++ b/src/main/resources/hudson/plugins/ec2/SlaveTemplate/help-terminateSpareInstances.html @@ -0,0 +1,5 @@ +
+ If Minimum number of spare instances is configured, instances may be launched when the controller starts. + With this option, up to that same number of spare instances will be terminated when the controller stops, + according to how many idle agents are connected at the time. +
From f41ca8a3c611c044f4d8ab5a8a247ff5bd455f34 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 4 Aug 2025 10:43:41 -0400 Subject: [PATCH 3/3] =?UTF-8?q?`terminateSpareInstances`=20=E2=86=92=20`te?= =?UTF-8?q?rminateIdleDuringShutdown`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit a8eb3521f41160181a163f9592fd6cec51db05e5) --- .../hudson/plugins/ec2/SlaveTemplate.java | 22 ++++++------- .../ec2/util/MinimumInstanceChecker.java | 33 ++++++++----------- .../plugins/ec2/SlaveTemplate/config.jelly | 8 ++--- .../help-terminateIdleDuringShutdown.html | 5 +++ .../help-terminateSpareInstances.html | 5 --- 5 files changed, 34 insertions(+), 39 deletions(-) create mode 100644 src/main/resources/hudson/plugins/ec2/SlaveTemplate/help-terminateIdleDuringShutdown.html delete mode 100644 src/main/resources/hudson/plugins/ec2/SlaveTemplate/help-terminateSpareInstances.html diff --git a/src/main/java/hudson/plugins/ec2/SlaveTemplate.java b/src/main/java/hudson/plugins/ec2/SlaveTemplate.java index 41cebc09d..08e76779e 100644 --- a/src/main/java/hudson/plugins/ec2/SlaveTemplate.java +++ b/src/main/java/hudson/plugins/ec2/SlaveTemplate.java @@ -186,6 +186,8 @@ public class SlaveTemplate implements Describable { public final String idleTerminationMinutes; + private boolean terminateIdleDuringShutdown; + public final String iamInstanceProfile; public final boolean deleteRootOnTermination; @@ -202,8 +204,6 @@ public class SlaveTemplate implements Describable { private final int minimumNumberOfSpareInstances; - private boolean terminateSpareInstances; - public final boolean stopOnTerminate; private final List tags; @@ -1676,6 +1676,15 @@ public String getidleTerminationMinutes() { return idleTerminationMinutes; } + public boolean getTerminateIdleDuringShutdown() { + return terminateIdleDuringShutdown; + } + + @DataBoundSetter + public void setTerminateIdleDuringShutdown(boolean terminateIdleDuringShutdown) { + this.terminateIdleDuringShutdown = terminateIdleDuringShutdown; + } + public Set getLabelSet() { if (labelSet == null) { labelSet = Label.parse(labels); @@ -1707,15 +1716,6 @@ public int getMinimumNumberOfSpareInstances() { return minimumNumberOfSpareInstances; } - public boolean getTerminateSpareInstances() { - return terminateSpareInstances; - } - - @DataBoundSetter - public void setTerminateSpareInstances(boolean terminateSpareInstances) { - this.terminateSpareInstances = terminateSpareInstances; - } - public MinimumNumberOfInstancesTimeRangeConfig getMinimumNumberOfInstancesTimeRangeConfig() { return minimumNumberOfInstancesTimeRangeConfig; } diff --git a/src/main/java/hudson/plugins/ec2/util/MinimumInstanceChecker.java b/src/main/java/hudson/plugins/ec2/util/MinimumInstanceChecker.java index 281cc8850..21c116978 100644 --- a/src/main/java/hudson/plugins/ec2/util/MinimumInstanceChecker.java +++ b/src/main/java/hudson/plugins/ec2/util/MinimumInstanceChecker.java @@ -48,19 +48,16 @@ public static int countCurrentNumberOfAgents(@NonNull SlaveTemplate agentTemplat return (int) agentsForTemplate(agentTemplate).count(); } - private static Stream spareAgents(@NonNull SlaveTemplate agentTemplate) { - return agentsForTemplate(agentTemplate) - .filter(computer -> computer.countBusy() == 0) - .filter(Computer::isOnline); + private static Stream idleAgents(@NonNull SlaveTemplate agentTemplate) { + return agentsForTemplate(agentTemplate).filter(Computer::isIdle); } public static int countCurrentNumberOfSpareAgents(@NonNull SlaveTemplate agentTemplate) { - return (int) spareAgents(agentTemplate).count(); + return (int) idleAgents(agentTemplate).filter(Computer::isOnline).count(); } public static int countCurrentNumberOfProvisioningAgents(@NonNull SlaveTemplate agentTemplate) { - return (int) agentsForTemplate(agentTemplate) - .filter(computer -> computer.countBusy() == 0) + return (int) idleAgents(agentTemplate) .filter(Computer::isOffline) .filter(Computer::isConnecting) .count(); @@ -157,23 +154,21 @@ public static boolean minimumInstancesActive( } @Terminator - public static void discardSpareInstances() throws Exception { - LOGGER.fine("Looking for spare instances to discard"); + public static void discardIdleInstances() throws Exception { + LOGGER.fine("Looking for idle instances to discard"); List> futures = new ArrayList<>(); Jenkins.get().clouds.stream() .filter(EC2Cloud.class::isInstance) .map(EC2Cloud.class::cast) .forEach(cloud -> cloud.getTemplates().stream() - .filter(SlaveTemplate::getTerminateSpareInstances) - .forEach(agentTemplate -> spareAgents(agentTemplate) - .limit(agentTemplate.getMinimumNumberOfSpareInstances()) - .forEach(computer -> { - EC2AbstractSlave agent = computer.getNode(); - if (agent != null) { - LOGGER.info(() -> "discarding spare instance " + agent.getInstanceId()); - futures.add(agent.terminate()); - } - }))); + .filter(SlaveTemplate::getTerminateIdleDuringShutdown) + .forEach(agentTemplate -> idleAgents(agentTemplate).forEach(computer -> { + EC2AbstractSlave agent = computer.getNode(); + if (agent != null) { + LOGGER.info(() -> "discarding idle instance " + agent.getInstanceId()); + futures.add(agent.terminate()); + } + }))); // Must wait; otherwise task could run too late during shutdown, leading to NoClassDefFoundError. for (Future future : futures) { future.get(5, TimeUnit.SECONDS); diff --git a/src/main/resources/hudson/plugins/ec2/SlaveTemplate/config.jelly b/src/main/resources/hudson/plugins/ec2/SlaveTemplate/config.jelly index 8fa9209f9..80f2658ab 100644 --- a/src/main/resources/hudson/plugins/ec2/SlaveTemplate/config.jelly +++ b/src/main/resources/hudson/plugins/ec2/SlaveTemplate/config.jelly @@ -106,6 +106,10 @@ THE SOFTWARE. + + + + @@ -154,10 +158,6 @@ THE SOFTWARE. - - - - diff --git a/src/main/resources/hudson/plugins/ec2/SlaveTemplate/help-terminateIdleDuringShutdown.html b/src/main/resources/hudson/plugins/ec2/SlaveTemplate/help-terminateIdleDuringShutdown.html new file mode 100644 index 000000000..5043e55ed --- /dev/null +++ b/src/main/resources/hudson/plugins/ec2/SlaveTemplate/help-terminateIdleDuringShutdown.html @@ -0,0 +1,5 @@ +
+ Delete any idle agents and terminate their instances when the controller stops. + This is especially useful in conjunction with the Minimum number of instances or Minimum number of spare instances options, + when setting Idle termination time to zero. +
diff --git a/src/main/resources/hudson/plugins/ec2/SlaveTemplate/help-terminateSpareInstances.html b/src/main/resources/hudson/plugins/ec2/SlaveTemplate/help-terminateSpareInstances.html deleted file mode 100644 index 6e859253a..000000000 --- a/src/main/resources/hudson/plugins/ec2/SlaveTemplate/help-terminateSpareInstances.html +++ /dev/null @@ -1,5 +0,0 @@ -
- If Minimum number of spare instances is configured, instances may be launched when the controller starts. - With this option, up to that same number of spare instances will be terminated when the controller stops, - according to how many idle agents are connected at the time. -