Skip to content
Open
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,18 @@ template, it can be configured on cloud level and leave the filed blank
in the templates.

<a href="https://raw.githubusercontent.com/jenkinsci/openstack-cloud-plugin/master/docs/options.png"><img align="right" width="300" src="https://raw.githubusercontent.com/jenkinsci/openstack-cloud-plugin/master/docs/options.png"></a>
Aside from machine/node attributes, every template require name and
labels to be configured. Name will serve both as an identifier of the
Aside from machine/node attributes, every template requires name, labels and
usage mode to be configured. Name will serve both as an identifier of the
template as well as a name prefix for Jenkins node and OpenStack machine
(that is why some limitations apply here). Labels field expects a set of
Jenkins labels that will be assigned to all nodes that the template
provisions. It will also be used to determine which cloud and template to
use to process Jenkins load. When there is a build with no label
requirements, any
template can be used to provision the node. Build with label restriction
provisions. The Usage field specifies whether nodes will be provisioned for
builds that have no label requirement (_Use this node as much as possible_)
or only if a build's label is present (_Only build jobs with label expressions matching this node_).
The combination of the Labels and Usage fields will be used to determine
which cloud and template to use to process Jenkins load. When there is a build with no label
requirements, any template where the Usage mode is '_Use this node as much as possible_'
can be used to provision the node. Builds with label restrictions
can trigger provisioning only on templates with matching label set. The
attributes at template level will inherit all global values (the value
in effect is printed under the field on hte config page). In case required
Expand Down Expand Up @@ -107,18 +110,22 @@ jenkins:
templates:
- name: "empty"
labels: "linux"
mode: EXCLUSIVE
- name: "jnlp"
labels: "jnlp"
mode: NORMAL
slaveOptions:
launcherFactory: "jnlp"
- name: "volumeSnapshot"
labels: "volume"
mode: NORMAL
slaveOptions:
bootSource:
volumeSnapshot:
name: "Volume name"
- name: "volumeFromImage"
labels: "volume from image"
mode: NORMAL
slaveOptions:
bootSource:
volumeFromImage:
Expand Down
Binary file modified docs/config.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/options.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public class JCloudsSlave extends AbstractCloudSlave implements TrackedItem {
private transient @Deprecated Server metadata;

public JCloudsSlave(
@Nonnull ProvisioningActivity.Id id, @Nonnull Server metadata, @Nonnull String labelString, @Nonnull SlaveOptions slaveOptions
@Nonnull ProvisioningActivity.Id id, @Nonnull Server metadata, @Nonnull String labelString, @Nonnull Node.Mode mode, @Nonnull SlaveOptions slaveOptions
) throws IOException, Descriptor.FormException {

super(Objects.requireNonNull(metadata.getName()), slaveOptions.getFsRoot(), null /*needs to be set later via setter*/);
Expand All @@ -94,7 +94,7 @@ public JCloudsSlave(
this.cache = makeCache();

setNumExecutors(slaveOptions.getNumExecutors());
setMode(Mode.NORMAL);
setMode(mode);
setLabelString(labelString);
setRetentionStrategy(new JCloudsRetentionStrategy());
setNodeProperties(mkNodeProperties(Openstack.getAccessIpAddress(metadata), slaveOptions.getNodeProperties()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import hudson.model.Descriptor;
import hudson.model.Failure;
import hudson.model.Label;
import hudson.model.Node;
import hudson.model.TaskListener;
import hudson.model.labels.LabelAtom;
import hudson.util.FormValidation;
Expand Down Expand Up @@ -64,6 +65,7 @@ public class JCloudsSlaveTemplate implements Describable<JCloudsSlaveTemplate>,

private final @Nonnull String name;
private final @Nonnull String labelString;
private final @Nonnull Node.Mode mode;

// Difference compared to cloud
private /*final*/ @Nonnull SlaveOptions slaveOptions;
Expand All @@ -87,10 +89,11 @@ public class JCloudsSlaveTemplate implements Describable<JCloudsSlaveTemplate>,
private transient @Deprecated @SuppressWarnings("DeprecatedIsStillUsed") String availabilityZone;

@DataBoundConstructor
public JCloudsSlaveTemplate(final @Nonnull String name, final @Nonnull String labels, final @CheckForNull SlaveOptions slaveOptions) {
public JCloudsSlaveTemplate(final @Nonnull String name, final @Nonnull String labels, Node.Mode mode, final @CheckForNull SlaveOptions slaveOptions) {
this.name = Util.fixNull(name).trim();
this.labelString = Util.fixNull(labels).trim();

this.mode = mode != null ? mode : Node.Mode.NORMAL;
LOGGER.info("Constructing new JCloudsSlaveTemplate - mode=" + this.mode);
this.slaveOptions = slaveOptions == null ? SlaveOptions.empty() : slaveOptions;

readResolve();
Expand Down Expand Up @@ -193,8 +196,12 @@ public Set<LabelAtom> getLabelSet() {
return labelString;
}

public @Nonnull Node.Mode getMode() {
return mode != null ? mode : Node.Mode.NORMAL;
}

public boolean canProvision(final Label label) {
return label == null || label.matches(labelSet);
return (label == null && getMode() == Node.Mode.NORMAL) || (label != null && label.matches(labelSet));
}

/*package*/ boolean hasProvisioned(@Nonnull Server server) {
Expand All @@ -219,7 +226,7 @@ public boolean canProvision(final Label label) {
JCloudsSlave node = null;
// Terminate node unless provisioned successfully
try {
node = new JCloudsSlave(id, server, labelString, opts);
node = new JCloudsSlave(id, server, labelString, mode, opts);

String cause;
while ((cause = cloud.slaveIsWaitingFor(node)) != null) {
Expand Down Expand Up @@ -280,7 +287,7 @@ public boolean canProvision(final Label label) {
}
builder.addMetadataItem(ServerScope.METADATA_KEY, scope.getValue());

LOGGER.info("Provisioning new openstack server " + serverName + " with options " + opts);
LOGGER.info("Provisioning new openstack server " + serverName + " (mode=" + getMode() + ") with options " + opts);
// Ensure predictable server name so we can inject it into user data
builder.name(serverName);

Expand Down Expand Up @@ -534,5 +541,14 @@ public FormValidation doCheckName(@QueryParameter String value) {
return FormValidation.error(ex.getMessage());
}
}

@Restricted(DoNotUse.class)
@RequirePOST
public FormValidation doCheckLabels(@QueryParameter String value, @QueryParameter Node.Mode mode) {
if ((value == null || value.trim().isEmpty()) && mode == Node.Mode.EXCLUSIVE) {
return FormValidation.warning("Nodes without any labels and running in exclusive mode will never be provisioned");
}
return FormValidation.ok();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<f:entry title="Labels" field="labels">
<f:textbox/>
</f:entry>
<f:slave-mode name="mode" node="${instance}"/>
</f:section>

<f:advanced title="Provisioning details">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ public void configure() {
// The only use-case for empty template is when all options are inherited by a single configured template
JCloudsSlaveTemplate t = c.getTemplate("empty");
assertEquals("linux", t.getLabels());
assertEquals(Node.Mode.NORMAL, t.getMode());
assertEquals(SlaveOptions.empty(), t.getRawSlaveOptions());

SlaveOptions jso = c.getTemplate("jnlp").getRawSlaveOptions();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ public JCloudsSlaveTemplate dummySlaveTemplate(String labels) {

public JCloudsSlaveTemplate dummySlaveTemplate(SlaveOptions opts, String labels) {
int num = templateCount.getAndIncrement();
return new JCloudsSlaveTemplate("template" + num, labels, opts);
return new JCloudsSlaveTemplate("template" + num, labels, Node.Mode.NORMAL, opts);
}

public JCloudsCloud dummyCloud(JCloudsSlaveTemplate... templates) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import hudson.model.Descriptor;
import hudson.model.Label;
import hudson.model.Node;
import hudson.model.labels.LabelAtom;
import hudson.slaves.NodeProvisioner;
import jenkins.plugins.openstack.PluginTestRule;
Expand Down Expand Up @@ -90,7 +91,7 @@ public void reportInstanceCapBasedOnSlaves() throws IOException, Descriptor.Form

Server server = j.mockServer().name("foo0").withFixedIPv4("0.0.0.0").get();
ProvisioningActivity.Id id = new ProvisioningActivity.Id(cloud.name, restrictedTmplt.getName());
j.jenkins.addNode(new JCloudsSlave(id, server, "restricted common", restrictedTmplt.getEffectiveSlaveOptions()));
j.jenkins.addNode(new JCloudsSlave(id, server, "restricted common", Node.Mode.NORMAL, restrictedTmplt.getEffectiveSlaveOptions()));

lr.capture(5);
lr.record(JCloudsCloud.class, Level.INFO);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import hudson.ExtensionList;
import hudson.model.Item;
import hudson.model.Label;
import hudson.model.Node;
import hudson.model.UnprotectedRootAction;
import hudson.model.User;
import hudson.security.ACL;
Expand Down Expand Up @@ -141,7 +142,7 @@ public void presentUIDefaults() throws Exception {

String openstackAuth = j.dummyCredentials();

JCloudsSlaveTemplate template = new JCloudsSlaveTemplate("template", "label", new SlaveOptions(
JCloudsSlaveTemplate template = new JCloudsSlaveTemplate("template", "label", Node.Mode.NORMAL, new SlaveOptions(
new BootSource.Image("iid"), "hw", "nw", "ud", 1, 0, "public", "sg", "az", 2, "kp", 3, "jvmo", "fsRoot", LauncherFactory.JNLP.JNLP, null, 4, false
));
JCloudsCloud cloud = new JCloudsCloud("openstack", "endPointUrl", false,"zone", new SlaveOptions(
Expand Down Expand Up @@ -259,6 +260,7 @@ public void globalConfigMigrationFromV1() throws Exception {

JCloudsSlaveTemplate template = cloud.getTemplate("ath-integration-test");
assertEquals(Label.parse("label"), template.getLabelSet());
assertEquals(Node.Mode.EXCLUSIVE, template.getMode());
SlaveOptions to = template.getEffectiveSlaveOptions();
assertEquals("16", to.getHardwareId());
assertEquals("ac98e93d-34a3-437d-a7ba-9ad24c02f5b2", ((BootSource.Image) to.getBootSource()).getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import hudson.Functions;
import hudson.model.Computer;
import hudson.model.Label;
import hudson.model.Node;
import hudson.model.TaskListener;
import hudson.model.User;
import hudson.slaves.ComputerListener;
Expand Down Expand Up @@ -46,7 +47,7 @@ public void scheduleSlaveDelete() throws Exception {
int retentionTime = 1; // minute

JCloudsSlaveTemplate template = new JCloudsSlaveTemplate(
"template", "label", SlaveOptions.builder().retentionTime(retentionTime).build()
"template", "label", Node.Mode.NORMAL, SlaveOptions.builder().retentionTime(retentionTime).build()
);

JCloudsCloud cloud = j.configureSlaveLaunchingWithFloatingIP(j.dummyCloud(template));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.stream.Stream;

import com.gargoylesoftware.htmlunit.html.HtmlForm;
import hudson.model.Node;
import hudson.util.FormValidation;
import jenkins.plugins.openstack.PluginTestRule;

Expand Down Expand Up @@ -67,13 +68,13 @@ public void configRoundtrip() throws Exception {
.build()
;
JCloudsSlaveTemplate jnlpTemplate = new JCloudsSlaveTemplate(
"jnlp-template", "openstack-slave-type1 openstack-type2", jnlpOpts
"jnlp-template", "openstack-slave-type1 openstack-type2", Node.Mode.NORMAL, jnlpOpts
);

LauncherFactory.SSH slaveType = new LauncherFactory.SSH(j.dummySshCredentials("sshid"), "mypath");
SlaveOptions sshOpts = dummySlaveOptions().getBuilder().launcherFactory(slaveType).build();
JCloudsSlaveTemplate sshTemplate = new JCloudsSlaveTemplate(
"ssh-template", "openstack-slave-type1 openstack-type2", sshOpts
"ssh-template", "openstack-slave-type1 openstack-type2", Node.Mode.NORMAL, sshOpts
);

JCloudsCloud originalCloud = new JCloudsCloud(
Expand Down Expand Up @@ -112,7 +113,7 @@ public void eraseDefaults() {
assertEquals(cloudOpts.getHardwareId(), templateOpts.getHardwareId());

JCloudsSlaveTemplate template = new JCloudsSlaveTemplate(
"test-templateOpts", "openstack-slave-type1 openstack-type2", templateOpts
"test-templateOpts", "openstack-slave-type1 openstack-type2", Node.Mode.NORMAL, templateOpts
);

JCloudsCloud cloud = new JCloudsCloud(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public void constructorGivenNoNodePropertiesThenProvidesIPAddressAsEnvVar() thro
expectedIpAddress);

// When
JCloudsSlave instance = new JCloudsSlave(stubId, mockMetadata, labelString, mockSlaveOptions);
JCloudsSlave instance = new JCloudsSlave(stubId, mockMetadata, labelString, Node.Mode.NORMAL, mockSlaveOptions);

// Then
final List<NodeProperty<?>> actualNPs = instance.getNodeProperties().toList();
Expand Down Expand Up @@ -76,7 +76,7 @@ public void constructorGivenSomeNodePropertiesThenAddsIPAddressAsEnvVar() throws
expectedIpAddress);

// When
JCloudsSlave instance = new JCloudsSlave(stubId, mockMetadata, labelString, mockSlaveOptions);
JCloudsSlave instance = new JCloudsSlave(stubId, mockMetadata, labelString, Node.Mode.NORMAL, mockSlaveOptions);

// Then
final List<NodeProperty<?>> actualNPs = instance.getNodeProperties().toList();
Expand Down Expand Up @@ -114,7 +114,7 @@ public void constructorGivenSomeNodePropertiesIncludingEnvVarsThenIncludesIPAddr
envVar2Value, EXPECTED_IP_ADDRESS_ENV_VAR_NAME, expectedIpAddress);

// When
JCloudsSlave instance = new JCloudsSlave(stubId, mockMetadata, labelString, mockSlaveOptions);
JCloudsSlave instance = new JCloudsSlave(stubId, mockMetadata, labelString, Node.Mode.NORMAL, mockSlaveOptions);

// Then
final List<NodeProperty<?>> actualNPs = instance.getNodeProperties().toList();
Expand Down Expand Up @@ -145,7 +145,7 @@ public void constructorGivenSomeNodePropertiesThenCreatesCopiesOfThoseNodeProper
expectedIpAddress);

// When
JCloudsSlave instance = new JCloudsSlave(stubId, mockMetadata, labelString, mockSlaveOptions);
JCloudsSlave instance = new JCloudsSlave(stubId, mockMetadata, labelString, Node.Mode.NORMAL, mockSlaveOptions);

// Then
final List<NodeProperty<?>> actualNPs = instance.getNodeProperties().toList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.model.Node;
import hudson.util.OneShotEvent;
import jenkins.plugins.openstack.PluginTestRule;
import jenkins.plugins.openstack.compute.internal.Openstack;
Expand Down Expand Up @@ -73,7 +74,7 @@ public void parse() {
@Test
public void nodeScope() throws Exception {
final Id id = new Id("foo", "bar", "baz");
final JCloudsSlave js = new JCloudsSlave(id, j.mockServer().withFixedIPv4("1.1.1.1").name("foo").get(), "foo", j.defaultSlaveOptions());
final JCloudsSlave js = new JCloudsSlave(id, j.mockServer().withFixedIPv4("1.1.1.1").name("foo").get(), "foo", Node.Mode.NORMAL, j.defaultSlaveOptions());
Server mock = mock(Server.class);
when(mock.getMetadata()).thenReturn(Collections.singletonMap(
ServerScope.METADATA_KEY,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,30 +34,36 @@ jenkins:
templates:
- name: "empty"
labels: "linux"
mode: NORMAL
- name: "jnlp"
labels: "jnlp"
mode: NORMAL
slaveOptions:
launcherFactory: "jnlp"
- name: "volumeSnapshot"
labels: "volume"
mode: NORMAL
slaveOptions:
bootSource:
volumeSnapshot:
name: "Volume name"
- name: "volumeFromImage"
labels: "volume from image"
mode: NORMAL
slaveOptions:
bootSource:
volumeFromImage:
name: "Volume name"
volumeSize: 15
- name: "customNodeProperties"
labels: "templateWithItsOwnNodeProperties"
mode: NORMAL
slaveOptions:
nodeProperties:
- nodePropertyTwo
- name: "noNodeProperties"
labels: "templateWithNoNodePropertiesInsteadOfDefaults"
mode: NORMAL
slaveOptions:
nodeProperties: []

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<imageId>RDU2/ac98e93d-34a3-437d-a7ba-9ad24c02f5b2</imageId>
<hardwareId>RDU2/16</hardwareId>
<labelString>label</labelString>
<mode>EXCLUSIVE</mode>
<userDataId>jenkins.plugins.openstack.compute.UserDataConfig.1455188317989</userDataId>
<numExecutors>1</numExecutors>
<stopOnTerminate>false</stopOnTerminate>
Expand Down