Skip to content

Commit 84d7e02

Browse files
authored
Dual-stack support (IPv6 mode) (#434)
* Documentation updated. * google-api-services-compute upgraded. * Dual-stack supported. * Constants renamed. * Network interface stack mode factorized. * Compatibility with previous version managed.
1 parent a28ff12 commit 84d7e02

22 files changed

+356
-48
lines changed

docs/Home.md

+3-4
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ View Google Compute Engine on the plugin site for more information.
5757
### Google Compute Engine configuration
5858
Each GCE configuration can point to a different GCP project. Follow the steps below to create one.
5959

60-
1. Go to Manage Jenkins, then Configure System
60+
1. Go to Manage Jenkins, then Nodes and Clouds, then CLouds in the left menu
6161
2. At the bottom of the page there will be a button labeled Add a new cloud, click the
6262
button then click Google Compute Engine.
6363
3. Enter a name for your cloud configuration and the Project ID that you will be using
@@ -134,6 +134,5 @@ If you want to turn off this Strategy globally then you can set a SystemProperty
134134

135135
Follow the steps below to configure it:
136136

137-
1. Go to Manage Jenkins, then Configure System
138-
2. At the bottom of the page there will be a Cloud Section
139-
3. Select the cloud project and look for the `No delay provisioning` checkbox, and click on to enable it.
137+
1. Go to Manage Jenkins, then Nodes and Clouds, then CLouds in the left menu
138+
2. Select the cloud project and look for the `No delay provisioning` checkbox, and click on to enable it.

pom.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,10 @@
7272
<it.windows>false</it.windows>
7373
<powershell.version>1.3</powershell.version>
7474
<google.http.version>1.42.2</google.http.version>
75-
<google.compute.api.version>1.25.0</google.compute.api.version>
75+
<google.compute.api.version>1.32.1</google.compute.api.version>
7676
<com.google.oauth-client.version>1.34.1</com.google.oauth-client.version>
7777
<google.j2objc.version>1.3</google.j2objc.version>
78-
<compute.revision>235</compute.revision>
78+
<compute.revision>20220720</compute.revision>
7979
<gcp-plugin-core.version>0.3.0</gcp-plugin-core.version>
8080
<hpi.compatibleSinceVersion>4.1.0</hpi.compatibleSinceVersion>
8181
<concurrency>10</concurrency>

src/main/java/com/google/jenkins/plugins/computeengine/ComputeEngineComputerLauncher.java

+13-2
Original file line numberDiff line numberDiff line change
@@ -343,17 +343,28 @@ protected Connection connectToSsh(ComputeEngineComputer computer, TaskListener l
343343
if (this.useInternalAddress) {
344344
host = nic.getNetworkIP();
345345
} else {
346-
// Look for a public IP address
346+
// Look for a public IPv4 address
347347
if (nic.getAccessConfigs() != null) {
348348
for (AccessConfig ac : nic.getAccessConfigs()) {
349-
if (ac.getType().equals(InstanceConfiguration.NAT_TYPE)) {
349+
if (ac.getType().equals(NetworkInterfaceIpStackMode.NAT_TYPE)) {
350350
host = ac.getNatIP();
351351
}
352352
}
353353
}
354+
// Look for a public IPv6 address
355+
// TODO: IPv6 address is preferred compared to IPv4, we could let the user select
356+
// his preferences to prioritize them.
357+
if (nic.getIpv6AccessConfigs() != null) {
358+
for (AccessConfig ac : nic.getIpv6AccessConfigs()) {
359+
if (ac.getType().equals(NetworkInterfaceDualStack.IPV6_TYPE)) {
360+
host = ac.getExternalIpv6();
361+
}
362+
}
363+
}
354364
// No public address found. Fall back to internal address
355365
if (host.isEmpty()) {
356366
host = nic.getNetworkIP();
367+
logInfo(computer, listener, "No public address found. Fall back to internal address.");
357368
}
358369
}
359370

src/main/java/com/google/jenkins/plugins/computeengine/InstanceConfiguration.java

+15-11
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import static com.google.jenkins.plugins.computeengine.ComputeEngineCloud.checkPermissions;
2121

2222
import com.google.api.services.compute.model.AcceleratorConfig;
23-
import com.google.api.services.compute.model.AccessConfig;
2423
import com.google.api.services.compute.model.AttachedDisk;
2524
import com.google.api.services.compute.model.AttachedDiskInitializeParams;
2625
import com.google.api.services.compute.model.DiskType;
@@ -46,6 +45,7 @@
4645
import edu.umd.cs.findbugs.annotations.Nullable;
4746
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
4847
import hudson.Extension;
48+
import hudson.ExtensionList;
4949
import hudson.RelativePath;
5050
import hudson.Util;
5151
import hudson.model.Describable;
@@ -98,8 +98,6 @@ public class InstanceConfiguration implements Describable<InstanceConfiguration>
9898
public static final String DEFAULT_RUN_AS_USER = "jenkins";
9999
public static final String METADATA_LINUX_STARTUP_SCRIPT_KEY = "startup-script";
100100
public static final String METADATA_WINDOWS_STARTUP_SCRIPT_KEY = "windows-startup-script-ps1";
101-
public static final String NAT_TYPE = "ONE_TO_ONE_NAT";
102-
public static final String NAT_NAME = "External NAT";
103101
public static final List<String> KNOWN_IMAGE_PROJECTS = Collections.unmodifiableList(new ArrayList<String>() {
104102
{
105103
add("centos-cloud");
@@ -131,7 +129,11 @@ public class InstanceConfiguration implements Describable<InstanceConfiguration>
131129
private String bootDiskSourceImageName;
132130
private String bootDiskSourceImageProject;
133131
private NetworkConfiguration networkConfiguration;
132+
private NetworkInterfaceIpStackMode networkInterfaceIpStackMode;
133+
134+
@Deprecated
134135
private boolean externalAddress;
136+
135137
private boolean useInternalAddress;
136138
private boolean ignoreProxy;
137139
private String networkTags;
@@ -351,6 +353,7 @@ public ComputeEngineInstance provision() throws IOException {
351353
/** Initializes transient properties */
352354
protected Object readResolve() {
353355
labelSet = Label.parse(labels);
356+
this.networkInterfaceIpStackMode = new NetworkInterfaceSingleStack(externalAddress);
354357
return this;
355358
}
356359

@@ -521,18 +524,15 @@ && notNullOrEmpty(acceleratorConfiguration.getGpuType())) {
521524

522525
private List<NetworkInterface> networkInterfaces() {
523526
List<NetworkInterface> networkInterfaces = new ArrayList<>();
524-
List<AccessConfig> accessConfigs = new ArrayList<>();
525-
if (externalAddress) {
526-
accessConfigs.add(new AccessConfig().setType(NAT_TYPE).setName(NAT_NAME));
527-
}
528-
NetworkInterface nic = new NetworkInterface().setAccessConfigs(accessConfigs);
527+
528+
NetworkInterface networkInterface = networkInterfaceIpStackMode.getNetworkInterface();
529529

530530
// Don't include subnetwork name if using default
531531
if (!networkConfiguration.getSubnetwork().equals("default")) {
532-
nic.setSubnetwork(stripSelfLinkPrefix(networkConfiguration.getSubnetwork()));
532+
networkInterface.setSubnetwork(stripSelfLinkPrefix(networkConfiguration.getSubnetwork()));
533533
}
534534

535-
networkInterfaces.add(nic);
535+
networkInterfaces.add(networkInterface);
536536
return networkInterfaces;
537537
}
538538

@@ -939,6 +939,10 @@ public FormValidation doCheckNumExecutorsStr(
939939
}
940940
return FormValidation.ok();
941941
}
942+
943+
public List<NetworkInterfaceIpStackMode.Descriptor> getNetworkInterfaceIpStackModeDescriptors() {
944+
return ExtensionList.lookup(NetworkInterfaceIpStackMode.Descriptor.class);
945+
}
942946
}
943947

944948
public static class Builder {
@@ -962,7 +966,7 @@ public InstanceConfiguration build() {
962966
instanceConfiguration.setBootDiskSourceImageName(this.bootDiskSourceImageName);
963967
instanceConfiguration.setBootDiskSourceImageProject(this.bootDiskSourceImageProject);
964968
instanceConfiguration.setNetworkConfiguration(this.networkConfiguration);
965-
instanceConfiguration.setExternalAddress(this.externalAddress);
969+
instanceConfiguration.setNetworkInterfaceIpStackMode(this.networkInterfaceIpStackMode);
966970
instanceConfiguration.setUseInternalAddress(this.useInternalAddress);
967971
instanceConfiguration.setIgnoreProxy(this.ignoreProxy);
968972
instanceConfiguration.setNetworkTags(this.networkTags);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2024 CloudBees, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.jenkins.plugins.computeengine;
18+
19+
import com.google.api.services.compute.model.AccessConfig;
20+
import com.google.api.services.compute.model.NetworkInterface;
21+
import hudson.Extension;
22+
import java.util.ArrayList;
23+
import java.util.List;
24+
import org.jenkinsci.Symbol;
25+
import org.kohsuke.stapler.DataBoundConstructor;
26+
import org.kohsuke.stapler.DataBoundSetter;
27+
28+
public class NetworkInterfaceDualStack extends NetworkInterfaceIpStackMode {
29+
public static final String PREMIUM_NETWORK_TIER = "PREMIUM";
30+
public static final String IPV6_TYPE = "DIRECT_IPV6";
31+
public static final String IPV6_NAME = "external-ipv6";
32+
33+
private boolean externalIPV4Address;
34+
private static final boolean externalIPV6Address = true; // always true in dual-stack type
35+
36+
@DataBoundConstructor
37+
public NetworkInterfaceDualStack() {}
38+
39+
public boolean getExternalIPV4Address() {
40+
return externalIPV4Address;
41+
}
42+
43+
public boolean getExternalIPV46ddress() {
44+
return externalIPV6Address;
45+
}
46+
47+
@DataBoundSetter
48+
public void setExternalIPV4Address(boolean externalIPV4Address) {
49+
this.externalIPV4Address = externalIPV4Address;
50+
}
51+
52+
@Override
53+
protected NetworkInterface getNetworkInterface() {
54+
List<AccessConfig> accessConfigs = new ArrayList<>();
55+
NetworkInterface networkInterface = new NetworkInterface().setStackType(DUAL_IP_STACK_TYPE);
56+
if (externalIPV4Address) {
57+
accessConfigs.add(new AccessConfig().setType(NAT_TYPE).setName(NAT_NAME));
58+
networkInterface.setAccessConfigs(accessConfigs);
59+
}
60+
List<AccessConfig> ipv6AccessConfigs = new ArrayList<>();
61+
if (externalIPV6Address) { // always true
62+
ipv6AccessConfigs.add(
63+
new AccessConfig().setType(IPV6_TYPE).setName(IPV6_NAME).setNetworkTier(PREMIUM_NETWORK_TIER));
64+
networkInterface.setIpv6AccessConfigs(ipv6AccessConfigs);
65+
}
66+
67+
return networkInterface;
68+
}
69+
70+
@Extension
71+
@Symbol("dualStack")
72+
public static class DescriptorImpl extends NetworkInterfaceIpStackMode.Descriptor {
73+
@Override
74+
public String getDisplayName() {
75+
return DUAL_IP_STACK_TYPE + " (dual-stack)";
76+
}
77+
}
78+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2024 CloudBees, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.jenkins.plugins.computeengine;
18+
19+
import com.google.api.services.compute.model.NetworkInterface;
20+
import hudson.model.AbstractDescribableImpl;
21+
22+
public abstract class NetworkInterfaceIpStackMode extends AbstractDescribableImpl<NetworkInterfaceIpStackMode> {
23+
public static final String NAT_TYPE = "ONE_TO_ONE_NAT";
24+
public static final String NAT_NAME = "External NAT";
25+
26+
public static final String SINGLE_IP_STACK_TYPE = "IPV4_ONLY";
27+
public static final String DUAL_IP_STACK_TYPE = "IPV4_IPV6";
28+
29+
protected NetworkInterface getNetworkInterface() {
30+
return null;
31+
}
32+
33+
public static class Descriptor extends hudson.model.Descriptor<NetworkInterfaceIpStackMode> {}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2024 CloudBees, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.jenkins.plugins.computeengine;
18+
19+
import com.google.api.services.compute.model.AccessConfig;
20+
import com.google.api.services.compute.model.NetworkInterface;
21+
import hudson.Extension;
22+
import java.util.ArrayList;
23+
import java.util.List;
24+
import org.jenkinsci.Symbol;
25+
import org.kohsuke.stapler.DataBoundConstructor;
26+
import org.kohsuke.stapler.DataBoundSetter;
27+
28+
public class NetworkInterfaceSingleStack extends NetworkInterfaceIpStackMode {
29+
30+
private boolean externalIPV4Address;
31+
32+
@DataBoundConstructor
33+
public NetworkInterfaceSingleStack() {}
34+
35+
public NetworkInterfaceSingleStack(boolean externalIPV4Address) {
36+
this.externalIPV4Address = externalIPV4Address;
37+
}
38+
39+
public boolean getExternalIPV4Address() {
40+
return externalIPV4Address;
41+
}
42+
43+
@DataBoundSetter
44+
public void setExternalIPV4Address(boolean externalIPV4Address) {
45+
this.externalIPV4Address = externalIPV4Address;
46+
}
47+
48+
@Override
49+
protected NetworkInterface getNetworkInterface() {
50+
List<AccessConfig> accessConfigs = new ArrayList<>();
51+
NetworkInterface networkInterface = new NetworkInterface().setStackType(SINGLE_IP_STACK_TYPE);
52+
if (externalIPV4Address) {
53+
accessConfigs.add(new AccessConfig().setType(NAT_TYPE).setName(NAT_NAME));
54+
networkInterface.setAccessConfigs(accessConfigs);
55+
}
56+
return networkInterface;
57+
}
58+
59+
@Extension
60+
@Symbol("singleStack")
61+
public static class DescriptorImpl extends NetworkInterfaceIpStackMode.Descriptor {
62+
@Override
63+
public String getDisplayName() {
64+
return SINGLE_IP_STACK_TYPE + " (single-stack)";
65+
}
66+
}
67+
}

src/main/resources/com/google/jenkins/plugins/computeengine/InstanceConfiguration/config.jelly

+3-2
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,9 @@
109109
<f:entry title="${%Network tags}" field="networkTags">
110110
<f:textbox/>
111111
</f:entry>
112-
<f:entry field="externalAddress" title="${%Attach External IP?}">
113-
<f:checkbox/>
112+
<f:entry title="Network Interface IP stack type">
113+
<f:hetero-radio field="networkInterfaceIpStackMode"
114+
descriptors="${descriptor.networkInterfaceIpStackModeDescriptors}"/>
114115
</f:entry>
115116
</f:section>
116117

src/main/resources/com/google/jenkins/plugins/computeengine/InstanceConfiguration/help-externalAddress.html

-17
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<!--
2+
Copyright 2024 CloudBees, Inc.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
-->
16+
<div>
17+
The IP stack type to use for the instance network interface configuration.
18+
Check that the configuration of your subnetwork match the selected stack type.
19+
</div>
20+

0 commit comments

Comments
 (0)