Skip to content

Commit

Permalink
Bugfix: JENKINS-36878: vSphere now respects per-slave instance cap.
Browse files Browse the repository at this point in the history
Bugfix: JENKINS-32112: NPE bug in vSphere.getTemplate().
Enhancement: vSphere.java now distributes load over all matching
templates. This satisfies JENKINS-30203 if Jenkins is configured with a
template.
Correction: Jenkins UI no longer offers facility to manually create a
Cloud-provisioned slave (the cloud provisions those itself).  Normal
vSphere slaves are still manually provisionable.
Cleaned up logging in vSphere.java.
Typo in vSphereCloudSlaveTemplate: getNumberOfExceutors ->
getNumberOfExecutors.
  • Loading branch information
pjdarton committed Sep 8, 2016
1 parent 309bad9 commit 4310c6c
Show file tree
Hide file tree
Showing 8 changed files with 1,219 additions and 159 deletions.
273 changes: 173 additions & 100 deletions src/main/java/org/jenkinsci/plugins/vSphereCloud.java

Large diffs are not rendered by default.

Expand Up @@ -3,7 +3,7 @@
* and open the template in the editor.
*/
package org.jenkinsci.plugins;

import hudson.AbortException;
import hudson.Extension;
import hudson.Functions;
Expand Down Expand Up @@ -35,7 +35,7 @@
*/
public class vSphereCloudProvisionedSlave extends vSphereCloudSlave {
private static final Logger LOGGER = Logger.getLogger(vSphereCloudProvisionedSlave.class.getName());

@DataBoundConstructor
public vSphereCloudProvisionedSlave(String name, String nodeDescription,
String remoteFS, String numExecutors, Mode mode,
Expand All @@ -47,24 +47,24 @@ public vSphereCloudProvisionedSlave(String name, String nodeDescription,
String snapName, String launchDelay, String idleOption,
String LimitedTestRunCount)
throws FormException, IOException {
super(name, nodeDescription,
remoteFS, numExecutors,
super(name, nodeDescription,
remoteFS, numExecutors,
mode, labelString,
delegateLauncher, retentionStrategy,
delegateLauncher, retentionStrategy,
nodeProperties, vsDescription,
vmName, launchSupportForced,
waitForVMTools, snapName,
launchDelay, idleOption,
vmName, launchSupportForced,
waitForVMTools, snapName,
launchDelay, idleOption,
LimitedTestRunCount);
}

@Override
protected void _terminate(TaskListener listener) throws IOException, InterruptedException {
super._terminate(listener);
final vSphereCloudLauncher launcher = (vSphereCloudLauncher) getLauncher();
if(launcher != null) {
final vSphereCloud cloud = launcher.findOurVsInstance();

if(cloud != null) {
VSphere vSphere = null;
try {
Expand All @@ -78,8 +78,8 @@ protected void _terminate(TaskListener listener) throws IOException, Interrupted
}
}
}
}
}

}

@Extension
Expand Down Expand Up @@ -110,12 +110,12 @@ public DescriptorImpl() {

@Override
public String getDisplayName() {
return "Slave virtual computer running under vSphere Cloud";
return "Slave created from a vSphere Cloud slave template";
}

@Override
public boolean isInstantiable() {
return true;
return false;
}

public List<vSphereCloud> getvSphereClouds() {
Expand Down
100 changes: 55 additions & 45 deletions src/main/java/org/jenkinsci/plugins/vSphereCloudSlaveTemplate.java
Expand Up @@ -15,10 +15,11 @@
*/

package org.jenkinsci.plugins;

import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
import com.cloudbees.plugins.credentials.common.StandardUsernameListBoxModel;
import com.cloudbees.plugins.credentials.domains.SchemeRequirement;

import hudson.Extension;
import hudson.model.Computer;
import hudson.model.Describable;
Expand All @@ -41,10 +42,12 @@
import hudson.model.labels.LabelAtom;
import hudson.plugins.sshslaves.SSHLauncher;
import hudson.slaves.NodeProperty;

import java.io.IOException;
import java.io.PrintStream;
import java.util.Set;
import java.util.UUID;

import org.jenkinsci.plugins.vsphere.tools.CloudProvisioningState;
import org.jenkinsci.plugins.vsphere.tools.VSphere;
import org.jenkinsci.plugins.vsphere.tools.VSphereException;

Expand All @@ -54,7 +57,7 @@
*/
public class vSphereCloudSlaveTemplate implements Describable<vSphereCloudSlaveTemplate> {
private static final Logger LOGGER = Logger.getLogger(vSphereCloudSlaveTemplate.class.getName());

protected static final SchemeRequirement HTTP_SCHEME = new SchemeRequirement("http");
protected static final SchemeRequirement HTTPS_SCHEME = new SchemeRequirement("https");

Expand All @@ -66,7 +69,7 @@ public class vSphereCloudSlaveTemplate implements Describable<vSphereCloudSlaveT
private final String resourcePool;
private final String datastore;
private final String templateDescription;
private transient int templateInstanceCap;
private int templateInstanceCap;
private final int numberOfExecutors;
private final String remoteFS;
private final String labelString;
Expand All @@ -80,11 +83,11 @@ public class vSphereCloudSlaveTemplate implements Describable<vSphereCloudSlaveT
private final String targetHost;
private final String credentialsId;
private final List<? extends NodeProperty<?>> nodeProperties;
boolean POWER_ON = true;
private static transient final boolean POWER_ON = true;

private transient Set<LabelAtom> labelSet;
protected transient vSphereCloud parent;

@DataBoundConstructor
public vSphereCloudSlaveTemplate(final String cloneNamePrefix,
final String masterImageName,
Expand Down Expand Up @@ -132,138 +135,145 @@ public vSphereCloudSlaveTemplate(final String cloneNamePrefix,
this.nodeProperties = nodeProperties;
readResolve();
}

public String getCloneNamePrefix() {
return this.cloneNamePrefix;
}

public String getMasterImageName() {
return this.masterImageName;
}

public String getSnapshotName() {
return this.snapshotName;
}

public boolean getLinkedClone() {
return this.linkedClone;
}

public String getCluster() {
return this.cluster;
}

public String getResourcePool() {
return this.resourcePool;
}

public String getDatastore() {
return this.datastore;
}

public String getTemplateDescription() {
return this.templateDescription;
}

public int getTemplateInstanceCap() {
if(this.templateInstanceCap == Integer.MAX_VALUE) {
return 0;
}
return this.templateInstanceCap;
}
public int getNumberOfExceutors() {

public int getNumberOfExecutors() {
return this.numberOfExecutors;
}

public String getRemoteFS() {
return this.remoteFS;
}

public String getLabelString() {
return this.labelString;
}

public Mode getMode() {
return this.mode;
}

public boolean getForceVMLaunch() {
return this.forceVMLaunch;
}

public boolean getWaitForVMTools() {
return this.waitForVMTools;
}

public int getLaunchDelay() {
return this.launchDelay;
}

public int getLimitedRunCount() {
return this.limitedRunCount;
}

public boolean getSaveFailure() {
return this.saveFailure;
}

public String getTargetResourcePool() {
return this.targetResourcePool;
}

public String getTargetHost() {
return this.targetHost;
}

public String getCredentialsId() {
return this.credentialsId;
}

public List<? extends NodeProperty<?>> getNodeProperties() {
return this.nodeProperties;
}
}

public Set<LabelAtom> getLabelSet() {
return this.labelSet;
}

public vSphereCloud getParent() {
return this.parent;
}

protected Object readResolve() {
this.labelSet = Label.parse(labelString);

if(this.templateInstanceCap == 0) {
this.templateInstanceCap = Integer.MAX_VALUE;
}
return this;
}
public vSphereCloudProvisionedSlave provision(TaskListener listener) throws VSphereException, FormException, IOException {

public vSphereCloudProvisionedSlave provision(final CloudProvisioningState algorithm, final String cloneName, final TaskListener listener) throws VSphereException, FormException, IOException {
final PrintStream logger = listener.getLogger();
final VSphere vSphere = getParent().vSphereInstance();
final UUID cloneUUID = UUID.randomUUID();
final String cloneName = this.cloneNamePrefix + "_" + cloneUUID;


vSphere.cloneVm(cloneName, this.masterImageName, this.linkedClone, this.resourcePool, this.cluster, this.datastore, POWER_ON, logger);

final String ip = vSphere.getIp(vSphere.getVmByName(cloneName), 1000);
final SSHLauncher sshLauncher = new SSHLauncher(ip, 0, credentialsId, null, null, null, null, this.launchDelay, 3, 60);
final SSHLauncher launcher = new SSHLauncher(ip, 0, credentialsId, null, null, null, null, this.launchDelay, 3, 60);

vSphere.disconnect();
// final CloudSlaveRetentionStrategy strategy = new CloudSlaveRetentionStrategy();
// strategy.TIMEOUT = TimeUnit2.MINUTES.toMillis(1);
final RunOnceCloudRetentionStrategy strategy = new RunOnceCloudRetentionStrategy(2);
return new vSphereCloudProvisionedSlave(cloneName, this.templateDescription, this.remoteFS, String.valueOf(this.numberOfExecutors), this.mode, this.labelString, sshLauncher, strategy, this.nodeProperties, this.parent.getVsDescription(), cloneName, this.forceVMLaunch, this.waitForVMTools, snapshotName, String.valueOf(this.launchDelay), null, String.valueOf(this.limitedRunCount));
return new vSphereCloudProvisionedSlave(cloneName, this.templateDescription, this.remoteFS, String.valueOf(this.numberOfExecutors), this.mode, this.labelString, launcher, strategy, this.nodeProperties, this.parent.getVsDescription(), cloneName, this.forceVMLaunch, this.waitForVMTools, snapshotName, String.valueOf(this.launchDelay), null, String.valueOf(this.limitedRunCount)) {
@Override
protected void _terminate(TaskListener listener) throws IOException, InterruptedException {
// once we're done with, remove our cached record.
synchronized(algorithm) {
algorithm.provisionedSlaveNowTerminated(vSphereCloudSlaveTemplate.this, cloneName);
}
super._terminate(listener);
}
};
}

@Override
public Descriptor<vSphereCloudSlaveTemplate> getDescriptor() {
return Jenkins.getInstance().getDescriptor(getClass());
}

@Extension
public static final class DescriptorImpl extends Descriptor<vSphereCloudSlaveTemplate> {
@Override
Expand Down
@@ -0,0 +1,42 @@
package org.jenkinsci.plugins.vsphere.tools;

import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.jenkinsci.plugins.vSphereCloudSlaveTemplate;

/**
* How we decide what template to create the next slave on.
*/
public final class CloudProvisioningAlgorithm {
private CloudProvisioningAlgorithm() {
}

/**
* Given a bunch of templates to choose from, works out which one we should
* use next.
*
* @param provisionables
* Template records to decide between.
* @return The record with the most free capacity, or null if there are none
* with any capacity.
*/
public static CloudProvisioningRecord findTemplateWithMostFreeCapacity(
Collection<? extends CloudProvisioningRecord> provisionables) {
final SortedSet<CloudProvisioningRecord> sortedSet = new TreeSet<CloudProvisioningRecord>(
CloudProvisioningRecord.leastUsedFirst);
sortedSet.addAll(provisionables);
final Iterator<CloudProvisioningRecord> iterator = sortedSet.iterator();
if (iterator.hasNext()) {
final CloudProvisioningRecord bestOption = iterator.next();
if (bestOption.hasCapacityForMore()) {
return bestOption;
}
}
return null;
}
}

0 comments on commit 4310c6c

Please sign in to comment.