Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<url>https://github.com/jenkinsci/openstack-cloud-plugin</url>

<properties>
<jenkins.version>2.414.1</jenkins.version>
<jenkins.version>2.419</jenkins.version>
<java.level>11</java.level>
<concurrency>1C</concurrency>
<surefire.useFile>false</surefire.useFile>
Expand Down Expand Up @@ -110,7 +110,7 @@
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>cloud-stats</artifactId>
<version>320.v96b_65297a_4b_b_</version>
<version>336.v788e4055508b_</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,15 @@ public JCloudsCleanupThread() {

@Override
public long getRecurrencePeriod() {
return MIN * 10;
// fixed value: 1000 millis
long cleanFreq = 1000;

return cleanFreq;
}

@Override
public void execute(TaskListener listener) {

try {
terminateNodesPendingDeletion();

Expand All @@ -61,6 +65,9 @@ public void execute(TaskListener listener) {
terminatesNodesWithoutServers(runningServers);

cleanOrphanedFips();

setCloudLastCleanTime();

} catch (JCloudsCloud.LoginFailure ex) {
LOGGER.log(Level.WARNING, "Unable to authenticate: " + ex.getMessage());
} catch (Throwable ex) {
Expand All @@ -70,6 +77,7 @@ public void execute(TaskListener listener) {

private void cleanOrphanedFips() {
for (JCloudsCloud cloud : JCloudsCloud.getClouds()) {
if ((System.currentTimeMillis() - cloud.getLastCleanTime()) < cloud.getCleanfreqToMillis()) continue;
Openstack openstack = cloud.getOpenstack();

List<String> leaked = openstack.getFreeFipIds();
Expand All @@ -87,8 +95,17 @@ private void cleanOrphanedFips() {
}
}

private void setCloudLastCleanTime() {
for (JCloudsCloud cloud : JCloudsCloud.getClouds()) {
if ((System.currentTimeMillis() - cloud.getLastCleanTime()) < cloud.getCleanfreqToMillis()) continue;
cloud.setLastCleanTime(System.currentTimeMillis());
}
}

private void terminateNodesPendingDeletion() {
for (final JCloudsComputer comp : JCloudsComputer.getAll()) {
JCloudsCloud cloud = JCloudsCloud.getByName(comp.getId().getCloudName());
if ((System.currentTimeMillis() - cloud.getLastCleanTime()) < cloud.getCleanfreqToMillis()) continue;
if (!comp.isIdle()) continue;

final OfflineCause offlineCause = comp.getNode().getFatalOfflineCause();
Expand Down Expand Up @@ -156,6 +173,7 @@ private void deleteComputer(JCloudsComputer comp, CauseOfInterruption coi) {
private @Nonnull HashMap<JCloudsCloud, List<Server>> destroyServersOutOfScope() {
HashMap<JCloudsCloud, List<Server>> runningServers = new HashMap<>();
for (JCloudsCloud jc : JCloudsCloud.getClouds()) {
if ((System.currentTimeMillis() - jc.getLastCleanTime()) < jc.getCleanfreqToMillis()) continue;
runningServers.put(jc, new ArrayList<>());
List<Server> servers = jc.getOpenstack().getRunningNodes();
for (Server server : servers) {
Expand All @@ -175,6 +193,9 @@ private void deleteComputer(JCloudsComputer comp, CauseOfInterruption coi) {
private void terminatesNodesWithoutServers(@Nonnull HashMap<JCloudsCloud, List<Server>> runningServers) {
Map<String, JCloudsComputer> jenkinsComputers = new HashMap<>();
for (JCloudsComputer computer: JCloudsComputer.getAll()) {
JCloudsCloud cloud = JCloudsCloud.getByName(computer.getId().getCloudName());
if ((System.currentTimeMillis() - cloud.getLastCleanTime()) < cloud.getCleanfreqToMillis()) continue;

JCloudsSlave node = computer.getNode();
if (node != null) {
jenkinsComputers.put(node.getServerId(), computer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.kohsuke.accmod.restrictions.DoNotUse;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
Expand All @@ -51,6 +52,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.lang.NumberFormatException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
Expand Down Expand Up @@ -85,6 +87,12 @@ public class JCloudsCloud extends Cloud implements SlaveOptions.Holder {

private final @CheckForNull String zone; // OpenStack4j requires null when there is no zone configured

// Clean frequency in seconds. Default 10s
private @Nonnull long cleanfreq = 10;

private long lastCleanTime = System.currentTimeMillis();


// Make sure only diff of defaults is saved so when plugin defaults will change users are not stuck with outdated config
private /*final*/ @Nonnull SlaveOptions slaveOptions;

Expand Down Expand Up @@ -128,6 +136,7 @@ public JCloudsCloud(
final @Nonnull String endPointUrl,
final boolean ignoreSsl,
final @CheckForNull String zone,
final long cleanfreq,
final @CheckForNull SlaveOptions slaveOptions,
final @CheckForNull List<JCloudsSlaveTemplate> templates,
final @Nonnull String credentialsId
Expand All @@ -138,6 +147,7 @@ public JCloudsCloud(
this.ignoreSsl = TRUE.equals(ignoreSsl);
this.zone = Util.fixEmptyAndTrim(zone);
this.credentialId = credentialsId;
this.cleanfreq = cleanfreq == 0 ? 10 : cleanfreq;
this.slaveOptions = slaveOptions == null ? SlaveOptions.empty() : slaveOptions.eraseDefaults(DescriptorImpl.DEFAULTS);

this.templates = templates == null ? Collections.emptyList() : Collections.unmodifiableList(templates);
Expand Down Expand Up @@ -236,6 +246,27 @@ private void injectReferenceIntoTemplates() {
return zone;
}

public void setLastCleanTime(long timeMillis) {
this.lastCleanTime = timeMillis;
}

public long getLastCleanTime() {
return lastCleanTime;
}

public @CheckForNull long getCleanfreq() {
return cleanfreq;
}

public @CheckForNull long getCleanfreqToMillis() {
return cleanfreq * 1000;
}

@DataBoundSetter
public void setCleanfreq(@Nonnull long cleanfreq) {
this.cleanfreq = cleanfreq;
}

/**
* Get a queue of templates to be used to provision slaves of label.
*
Expand Down Expand Up @@ -490,7 +521,7 @@ private static void sendPlaintextError(String message, StaplerResponse rsp) thro
if (credential == null) {
throw new LoginFailure("No credentials found for cloud " + name + " (id=" + getCredentialsId() + ")");
}
return Openstack.Factory.get(endPointUrl, ignoreSsl, credential, zone);
return Openstack.Factory.get(endPointUrl, ignoreSsl, credential, zone, cleanfreq);
} catch (AuthenticationException ex) {
throw new LoginFailure(name, ex);
} catch (FormValidation ex) {
Expand Down Expand Up @@ -555,13 +586,14 @@ public FormValidation doTestConnection(
@QueryParameter boolean ignoreSsl,
@QueryParameter String credentialsId,
@QueryParameter String endPointUrl,
@QueryParameter String zone
@QueryParameter String zone,
@QueryParameter long cleanfreq
) {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
try {
OpenstackCredential openstackCredential = OpenstackCredentials.getCredential(credentialsId);
if (openstackCredential == null) throw FormValidation.error("No credential found for " + credentialsId);
Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone);
Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone, cleanfreq);
Throwable ex = openstack.sanityCheck();

if (ex != null) {
Expand Down Expand Up @@ -589,6 +621,23 @@ public FormValidation doCheckEndPointUrl(@QueryParameter String value) {
return FormValidation.ok();
}

@Restricted(DoNotUse.class)
@RequirePOST
public FormValidation doCheckCleanfreq(@QueryParameter String value) {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);

try {
long parsedCleanFreq = Long.parseLong(value);
if (parsedCleanFreq == 0) {
NumberFormatException nfe = new NumberFormatException("cleanfreq should be strictly greater than 0");
throw nfe;
}
} catch (NumberFormatException ex) {
return FormValidation.error(ex, "Clean frequency must be an integer strictly greater than 0 (>=1)");
}
return FormValidation.ok();
}

@Restricted(DoNotUse.class)
@RequirePOST
public ListBoxModel doFillCredentialsIdItems() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public final void calcFillSettings(String field, Map<String, Object> attributes)

// Replace direct reference to references to possible relative paths
if (method.getAnnotation(InjectOsAuth.class) != null) {
for (String attr: Arrays.asList("endPointUrl", "ignoreSsl", "credentialsId", "zone")) {
for (String attr: Arrays.asList("endPointUrl", "ignoreSsl", "credentialsId", "zone", "cleanfreq")) {
deps.remove(attr);
for (String offset : getAuthFieldsOffsets()) {
deps.add(offset + "/" + attr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ public FormValidation doCheckRetentionTime(
public ListBoxModel doFillFloatingIpPoolItems(
@QueryParameter String floatingIpPool,
@QueryParameter String endPointUrl, @QueryParameter boolean ignoreSsl,
@QueryParameter String credentialsId, @QueryParameter String zone
@QueryParameter String credentialsId, @QueryParameter String zone, @QueryParameter long cleanfreq
) {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
ListBoxModel m = new ListBoxModel();
Expand All @@ -178,7 +178,7 @@ public ListBoxModel doFillFloatingIpPoolItems(
try {
OpenstackCredential openstackCredential = OpenstackCredentials.getCredential(credentialsId);
if (haveAuthDetails(endPointUrl, openstackCredential, zone)) {
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone);
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone, cleanfreq);
for (String p : openstack.getSortedIpPools()) {
m.add(p);
}
Expand Down Expand Up @@ -215,7 +215,7 @@ public FormValidation doCheckFloatingIpPool(
public ListBoxModel doFillHardwareIdItems(
@QueryParameter String hardwareId, @QueryParameter String endPointUrl,
@QueryParameter boolean ignoreSsl,
@QueryParameter String credentialsId, @QueryParameter String zone
@QueryParameter String credentialsId, @QueryParameter String zone, @QueryParameter long cleanfreq
) {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
ListBoxModel m = new ListBoxModel();
Expand All @@ -224,7 +224,7 @@ public ListBoxModel doFillHardwareIdItems(
try {
OpenstackCredential openstackCredential = OpenstackCredentials.getCredential(credentialsId);
if (haveAuthDetails(endPointUrl, openstackCredential, zone)) {
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone);
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone, cleanfreq);
for (Flavor flavor : openstack.getSortedFlavors()) {
final String value = flavor.getId();
final String displayText = Openstack.getFlavorInfo(flavor);
Expand Down Expand Up @@ -269,7 +269,9 @@ public FormValidation doCheckNetworkId(
@RelativePath("..") @QueryParameter("credentialsId") String credentialsIdCloud,
@RelativePath("../..") @QueryParameter("credentialsId") String credentialsIdTemplate,
@RelativePath("..") @QueryParameter("zone") String zoneCloud,
@RelativePath("../..") @QueryParameter("zone") String zoneTemplate
@RelativePath("../..") @QueryParameter("zone") String zoneTemplate,
@RelativePath("..") @QueryParameter("cleanfreq") long cleanfreqCloud,
@RelativePath("../..") @QueryParameter("cleanfreq") long cleanfreqTemplate
) {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
if (Util.fixEmpty(value) == null) {
Expand All @@ -282,9 +284,10 @@ public FormValidation doCheckNetworkId(
final String credentialsId = getDefault(credentialsIdCloud, credentialsIdTemplate);
final OpenstackCredential openstackCredential = OpenstackCredentials.getCredential(credentialsId);
final String zone = getDefault(zoneCloud, zoneTemplate);
final long cleanfreq = cleanfreqCloud + cleanfreqTemplate;
if (haveAuthDetails(endPointUrl, openstackCredential, zone)) {
try {
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone);
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone, cleanfreq);
List<String> nids = JCloudsSlaveTemplate.selectNetworkIds(openstack, value);
return FormValidation.ok("Will connect to " + nids.size() + " network(s). Ex.: " + nids);
} catch (IllegalArgumentException | NoSuchElementException ex) {
Expand Down Expand Up @@ -371,7 +374,7 @@ public FormValidation doCheckSecurityGroups(
public ComboBoxModel doFillAvailabilityZoneItems(
@QueryParameter String availabilityZone, @QueryParameter String endPointUrl,
@QueryParameter boolean ignoreSsl,
@QueryParameter String credentialsId, @QueryParameter String zone
@QueryParameter String credentialsId, @QueryParameter String zone, @QueryParameter long cleanfreq
) {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
// Support for availabilityZones is optional in OpenStack, so this is a f:combobox not f:select field.
Expand All @@ -380,7 +383,7 @@ public ComboBoxModel doFillAvailabilityZoneItems(
try {
OpenstackCredential openstackCredential = OpenstackCredentials.getCredential(credentialsId);
if (haveAuthDetails(endPointUrl, openstackCredential, zone)) {
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone);
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone, cleanfreq);
for (final AvailabilityZone az : openstack.getAvailabilityZones()) {
final String value = az.getZoneName();
m.add(value);
Expand All @@ -406,7 +409,9 @@ public FormValidation doCheckAvailabilityZone(
@RelativePath("..") @QueryParameter("credentialsId") String credentialsIdCloud,
@RelativePath("../..") @QueryParameter("credentialsId") String credentialsIdTemplate,
@RelativePath("..") @QueryParameter("zone") String zoneCloud,
@RelativePath("../..") @QueryParameter("zone") String zoneTemplate
@RelativePath("../..") @QueryParameter("zone") String zoneTemplate,
@RelativePath("..") @QueryParameter("cleanfreq") long cleanfreqCloud,
@RelativePath("../..") @QueryParameter("cleanfreq") long cleanfreqTemplate
) {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
// Warn user if they've not selected anything AND there's multiple availability zones
Expand All @@ -420,9 +425,10 @@ public FormValidation doCheckAvailabilityZone(
final String credentialsId = getDefault(credentialsIdCloud,credentialsIdTemplate);
final OpenstackCredential openstackCredential = OpenstackCredentials.getCredential(credentialsId);
final String zone = getDefault(zoneCloud, zoneTemplate);
final long cleanfreq = cleanfreqCloud + cleanfreqTemplate;
if (haveAuthDetails(endPointUrl, openstackCredential, zone)) {
try {
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone);
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone, cleanfreq);
final int numberOfAZs = openstack.getAvailabilityZones().size();
if (numberOfAZs > 1) {
return FormValidation.warning("Ambiguity warning: Multiple zones found.");
Expand All @@ -444,7 +450,7 @@ public ListBoxModel doFillKeyPairNameItems(
@QueryParameter String keyPairName,
@QueryParameter String endPointUrl,
@QueryParameter boolean ignoreSsl,
@QueryParameter String credentialsId, @QueryParameter String zone
@QueryParameter String credentialsId, @QueryParameter String zone, @QueryParameter long cleanfreq
) {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
ListBoxModel m = new ListBoxModel();
Expand All @@ -453,7 +459,7 @@ public ListBoxModel doFillKeyPairNameItems(
try {
OpenstackCredential openstackCredential = OpenstackCredentials.getCredential(credentialsId);
if (haveAuthDetails(endPointUrl, openstackCredential, zone)) {
Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone);
Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone, cleanfreq);
for (String value : openstack.getSortedKeyPairNames()) {
m.add(value);
}
Expand Down
Loading
Loading