Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
import hudson.model.Label;
import hudson.model.Node;
import hudson.model.labels.LabelAtom;
import hudson.slaves.ComputerLauncher;
import hudson.slaves.JNLPLauncher;
import hudson.util.ComboBoxModel;
import hudson.util.FormValidation;
import hudson.util.ListBoxModel;
Expand All @@ -71,6 +73,7 @@
import java.util.Set;
import java.util.logging.Level;
import jenkins.model.Jenkins;
import jenkins.slaves.JnlpAgentReceiver;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
Expand Down Expand Up @@ -173,179 +176,220 @@
@Deprecated
private transient boolean preemptible;

private boolean useInboundAgent;
private static final String METADATA_CONTROLLER_URL = "controller-url";
private static final String METADATA_JNLP_SECRET = "jnlp-secret";
private Instance instance;

private static List<Metadata.Items> mergeMetadataItems(List<Metadata.Items> winner, List<Metadata.Items> loser) {
if (loser == null) {
loser = new ArrayList<Metadata.Items>();
}

for (Metadata.Items existing : loser) {
String existingKey = existing.getKey();
Metadata.Items duplicate = winner.stream()
.filter(m -> m.getKey().equals(existingKey))
.findFirst()
.orElse(null);
if (duplicate == null) {
winner.add(existing);
} else if (existingKey.equals(SSH_METADATA_KEY)) {
duplicate.setValue(duplicate.getValue() + "\n" + existing.getValue());
}
}
return winner;
}

@DataBoundConstructor
public InstanceConfiguration() {}

@DataBoundSetter
public void setNumExecutorsStr(String numExecutorsStr) {
this.numExecutors = intOrDefault(numExecutorsStr, DEFAULT_NUM_EXECUTORS);
this.numExecutorsStr = numExecutors.toString();
}

@DataBoundSetter
public void setLabelString(String labelString) {
this.labels = Util.fixNull(labelString);
readResolve();
}

@DataBoundSetter
public void setNetworkTags(String networkTags) {
this.networkTags = Util.fixNull(networkTags).trim();
}

@DataBoundSetter
public void setRetentionTimeMinutesStr(String retentionTimeMinutesStr) {
this.retentionTimeMinutes = intOrDefault(retentionTimeMinutesStr, DEFAULT_RETENTION_TIME_MINUTES);
this.retentionTimeMinutesStr = this.retentionTimeMinutes.toString();
}

@DataBoundSetter
public void setLaunchTimeoutSecondsStr(String launchTimeoutSecondsStr) {
this.launchTimeoutSeconds = intOrDefault(launchTimeoutSecondsStr, DEFAULT_LAUNCH_TIMEOUT_SECONDS);
this.launchTimeoutSecondsStr = this.launchTimeoutSeconds.toString();
}

@DataBoundSetter
public void setBootDiskSizeGbStr(String bootDiskSizeGbStr) {
this.bootDiskSizeGb = longOrDefault(bootDiskSizeGbStr, DEFAULT_BOOT_DISK_SIZE_GB);
this.bootDiskSizeGbStr = this.bootDiskSizeGb.toString();
}

@DataBoundSetter
public void setOneShot(boolean oneShot) {
this.oneShot = oneShot;
this.createSnapshot &= oneShot;
}

@DataBoundSetter
public void setCreateSnapshot(boolean createSnapshot) {
this.createSnapshot = createSnapshot && this.oneShot;
}

/**
* This setter is kept only to provide JCasC compatibility, don't use for any other.
* Although JCasC is not "required" to keep compatibility, but in this case,
* as it is very low effort to keep the compatibility, we have decided to keep it.
* <p>
* Previously, JCasC syntax would be {@code preemptible: true}, going forward instead should be done as,
* {@code provisioningType: preemptibleVm}
* <p>
* Currently only caller is, JCasC configurators if the bundle is having `preemptible` field defined in it.
* Consider deleting it in future (perhaps after a year or so)
*/
@DataBoundSetter
public void setPreemptible(boolean preemptible) {
if (preemptible) {
this.provisioningType = new PreemptibleVm();
}
}

public static Integer intOrDefault(String toParse, Integer defaultTo) {
Integer toReturn;
try {
toReturn = Integer.parseInt(toParse);
} catch (NumberFormatException nfe) {
toReturn = defaultTo;
}
return toReturn;
}

public static Long longOrDefault(String toParse, Long defaultTo) {
Long toReturn;
try {
toReturn = Long.parseLong(toParse);
} catch (NumberFormatException nfe) {
toReturn = defaultTo;
}
return toReturn;
}

private static boolean notNullOrEmpty(String s) {
return s != null && !s.isEmpty();
}

private static String stripSelfLinkPrefix(String s) {
if (s.contains("https://www.googleapis.com")) {
return s.substring(s.indexOf("/projects/") + 1);
}
return s;
}

@SuppressWarnings("unchecked")
public Descriptor<InstanceConfiguration> getDescriptor() {
return Jenkins.get().getDescriptor(getClass());
}

public String getLabelString() {
return labels;
}

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

public String getDisplayName() {
return description;
}

public int getLaunchTimeoutMillis() {
return launchTimeoutSeconds * 1000;
}

public void appendLabels(Map<String, String> labels) {
if (googleLabels == null) {
googleLabels = new HashMap<>();
}
googleLabels.putAll(labels);
}

public void appendLabel(String key, String value) {
if (googleLabels == null) {
googleLabels = new HashMap<>();
}
googleLabels.put(key, value);
}

private void appendJnlpMetadataIfRequired() {
List<Metadata.Items> items = new ArrayList<Metadata.Items>();

String jenkinsUrl = Jenkins.get().getRootUrl();
if (jenkinsUrl == null || jenkinsUrl.length() < 5) jenkinsUrl = "Jai Mata Di";

log.info("Adding JNLP Meta Data " + METADATA_CONTROLLER_URL + " = " + jenkinsUrl);

items.add(new Metadata.Items().setKey(METADATA_CONTROLLER_URL).setValue(jenkinsUrl));

log.info("Adding JNLP Meta Data "
+ METADATA_JNLP_SECRET
+ " = "
+ JnlpAgentReceiver.SLAVE_SECRET.mac(instance.getName()));

items.add(new Metadata.Items()
.setKey(METADATA_JNLP_SECRET)
.setValue(JnlpAgentReceiver.SLAVE_SECRET.mac(instance.getName())));

List<Metadata.Items> instanceItems = instance.getMetadata().getItems();
instance.getMetadata().setItems(mergeMetadataItems(instanceItems, items));
}

public ComputeEngineInstance provision() throws IOException {
try {
Instance instance = instance();
instance = instance();

if (this.useInboundAgent) appendJnlpMetadataIfRequired();

// TODO: JENKINS-55285

Check warning on line 366 in src/main/java/com/google/jenkins/plugins/computeengine/InstanceConfiguration.java

View check run for this annotation

ci.jenkins.io / Open Tasks Scanner

TODO

NORMAL: JENKINS-55285
Operation operation =
cloud.getClient().insertInstance(cloud.getProjectId(), Optional.ofNullable(template), instance);
log.info("Sent insert request for instance configuration [" + description + "]");
String targetRemoteFs = this.remoteFs;
ComputeEngineComputerLauncher launcher;
if (this.windowsConfiguration != null) {
launcher = new ComputeEngineWindowsLauncher(cloud.getCloudName(), operation, this.useInternalAddress);
if (Strings.isNullOrEmpty(targetRemoteFs)) {
targetRemoteFs = "C:\\";
}
ComputerLauncher launcher;

if (this.useInboundAgent) {
log.info("Setting up Inbound Agent.");
JNLPLauncher jnlpLauncher = new JNLPLauncher();
jnlpLauncher.setWebSocket(true);
launcher = jnlpLauncher;
} else {
launcher = new ComputeEngineLinuxLauncher(cloud.getCloudName(), operation, this.useInternalAddress);
if (Strings.isNullOrEmpty(targetRemoteFs)) {
targetRemoteFs = "/tmp";
if (this.windowsConfiguration != null) {
launcher =
new ComputeEngineWindowsLauncher(cloud.getCloudName(), operation, this.useInternalAddress);

Check warning on line 381 in src/main/java/com/google/jenkins/plugins/computeengine/InstanceConfiguration.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 179-381 are not covered by tests
if (Strings.isNullOrEmpty(targetRemoteFs)) {
targetRemoteFs = "C:\\";
}
} else {
launcher = new ComputeEngineLinuxLauncher(cloud.getCloudName(), operation, this.useInternalAddress);
if (Strings.isNullOrEmpty(targetRemoteFs)) {
targetRemoteFs = "/tmp";
}
}
}

return ComputeEngineInstance.builder()
.cloud(cloud)
.cloudName(cloud.name)
Expand Down Expand Up @@ -389,22 +433,23 @@
}

public Instance instance() throws IOException {
Instance instance = new Instance();
instance = new Instance();
instance.setName(uniqueName());
instance.setDescription(description);
instance.setZone(nameFromSelfLink(zone));
instance.setMetadata(newMetadata());

if (windowsConfiguration == null) {
if (sshConfiguration != null) {
log.info("User selected to use a custom ssh private key");
sshKeyCredential =
configureSSHPrivateKey(sshConfiguration.getCustomPrivateKeyCredentialsId(), runAsUser);
} else {
log.info("User selected to use an autogenerated ssh key pair");
sshKeyCredential = configureSSHKeyPair(instance, runAsUser);
if (!this.useInboundAgent)

Check warning on line 442 in src/main/java/com/google/jenkins/plugins/computeengine/InstanceConfiguration.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 442 is only partially covered, one branch is missing
if (windowsConfiguration == null) {
if (sshConfiguration != null) {
log.info("User selected to use a custom ssh private key");
sshKeyCredential =
configureSSHPrivateKey(sshConfiguration.getCustomPrivateKeyCredentialsId(), runAsUser);
} else {
log.info("User selected to use an autogenerated ssh key pair");
sshKeyCredential = configureSSHKeyPair(instance, runAsUser);
}
}
}

Map<String, String> effectiveGoogleLabels = new HashMap<>();
if (googleLabels != null) { // some tests don't set the labels, but comes as null
Expand Down Expand Up @@ -1029,6 +1074,7 @@
instanceConfiguration.setRemoteFs(this.remoteFs);
instanceConfiguration.setJavaExecPath(this.javaExecPath);
instanceConfiguration.setCloud(this.cloud);
instanceConfiguration.setUseInboundAgent(this.useInboundAgent);
if (googleLabels != null) {
instanceConfiguration.appendLabels(this.googleLabels);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
<f:entry title="${%Use Internal IP?}" field="useInternalAddress">
<f:checkbox/>
</f:entry>
<f:entry title="${%Use Inbound Agent}" field="useInboundAgent">
<f:checkbox/>
</f:entry>
<f:entry title="${%Ignore Jenkins Proxy?}" field="ignoreProxy">
<f:checkbox/>
</f:entry>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!--
Copyright 2020 Google LLC

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing permissions and limitations under the
License.
-->
<div>
Provision Inbound Agent to work without SSH Support.
</div>