From 5f3cee95163d346a87e2cbead2b62f0fc1743b55 Mon Sep 17 00:00:00 2001 From: Raul Arabaolaza Date: Thu, 11 Jan 2024 18:14:24 +0100 Subject: [PATCH 1/4] Initial ipv6 support --- .../acceptance/docker/DockerContainer.java | 30 ++++++++++++------- .../test/acceptance/docker/DockerImage.java | 15 ++++++---- .../acceptance/docker/DockerImageTest.java | 6 ++-- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/jenkinsci/test/acceptance/docker/DockerContainer.java b/src/main/java/org/jenkinsci/test/acceptance/docker/DockerContainer.java index 6b9bd8a..5c1ccb6 100644 --- a/src/main/java/org/jenkinsci/test/acceptance/docker/DockerContainer.java +++ b/src/main/java/org/jenkinsci/test/acceptance/docker/DockerContainer.java @@ -90,9 +90,10 @@ public String ipBound(int n) { return getIpAddress(); } String out = Docker.cmd("port").add(cid, n).popen().verifyOrDieWith("docker port command failed").trim(); - if (out.isEmpty()) // expected to return single line like "0.0.0.0:55326" + if (out.isEmpty()) // expected to return single line like "0.0.0.0:55326" or [::]:55326 throw new IllegalStateException(format("Port %d is not mapped for container %s", n, cid)); - return out.split(":")[0]; + // assumes it is published only in ipv6 or only in ipv4 + return ipv6Enabled() ? out.substring(1,out.lastIndexOf(":") - 1) : out.split(":")[0]; } catch (IOException | InterruptedException e) { throw new AssertionError("Failed to figure out port map " + n, e); } @@ -108,9 +109,9 @@ public String ipUdpBound(int n) { return getIpAddress(); } String out = Docker.cmd("port").add(cid, n + "/udp").popen().verifyOrDieWith("docker port command failed").trim(); - if (out.isEmpty()) // expected to return single line like "0.0.0.0:55326" + if (out.isEmpty()) // expected to return single line like "0.0.0.0:55326" or [::]:55326 throw new IllegalStateException(format("Udp port %d is not mapped for container %s", n, cid)); - return out.split(":")[0]; + return ipv6Enabled() ? out.substring(1,out.lastIndexOf(":") -1) : out.split(":")[0]; } catch (IOException | InterruptedException e) { throw new AssertionError("Failed to figure out udp port map " + n, e); } @@ -126,10 +127,10 @@ public int port(int n) { return n; } String out = Docker.cmd("port").add(cid, n).popen().verifyOrDieWith("docker port command failed").trim(); - if (out.isEmpty()) // expected to return single line like "0.0.0.0:55326" + if (out.isEmpty()) // expected to return single line like "0.0.0.0:55326" or [::]:55326 throw new IllegalStateException(format("Port %d is not mapped for container %s", n, cid)); - return Integer.parseInt(out.split(":")[1]); + return Integer.parseInt(out.split(":")[ipv6Enabled() ? 3 : 1]); } catch (IOException | InterruptedException e) { throw new AssertionError("Failed to figure out port map " + n, e); } @@ -145,10 +146,10 @@ public int udpPort(int n) { return n; } String out = Docker.cmd("port").add(cid, n + "/udp").popen().verifyOrDieWith("docker port command failed").trim(); - if (out.isEmpty()) // expected to return single line like "0.0.0.0:55326" + if (out.isEmpty()) // expected to return single line like "0.0.0.0:55326" or [::]:55326 throw new IllegalStateException(format("Udp port %d is not mapped for container %s", n, cid)); - return Integer.parseInt(out.split(":")[1]); + return Integer.parseInt(out.split(":")[ipv6Enabled() ? 3 : 1]); } catch (IOException | InterruptedException e) { throw new AssertionError("Failed to figure out udp port map " + n, e); } @@ -214,10 +215,11 @@ public JsonNode inspect() throws IOException { * IP address of this container reachable through the bridge. */ public String getIpAddress() throws IOException { + String ipLabel = ipv6Enabled() ? "GlobalIPv6Address" : "IPAddress"; if (System.getenv("DOCKER_FIXTURES_NETWORK") != null) { - return inspect().get("NetworkSettings").get("Networks").get(System.getenv("DOCKER_FIXTURES_NETWORK")).get("IPAddress").asText(); + return inspect().get("NetworkSettings").get("Networks").get(System.getenv("DOCKER_FIXTURES_NETWORK")).get(ipLabel).asText(); } - return inspect().get("NetworkSettings").get("IPAddress").asText(); + return inspect().get("NetworkSettings").get(ipLabel).asText(); } @Override @@ -233,4 +235,12 @@ public String toString() { public boolean sharingHostDockerService() { return Boolean.valueOf(System.getenv("SHARED_DOCKER_SERVICE")); } + + public static boolean ipv6Enabled() { + return Boolean.getBoolean("java.net.preferIPv6Addresses"); + } + + public static String encloseInBrackets(String toEnclose) { + return String.format("[%s]", toEnclose); + } } diff --git a/src/main/java/org/jenkinsci/test/acceptance/docker/DockerImage.java b/src/main/java/org/jenkinsci/test/acceptance/docker/DockerImage.java index 6faecba..84e2dfb 100644 --- a/src/main/java/org/jenkinsci/test/acceptance/docker/DockerImage.java +++ b/src/main/java/org/jenkinsci/test/acceptance/docker/DockerImage.java @@ -16,7 +16,7 @@ */ public class DockerImage { - public static final String DEFAULT_DOCKER_HOST = "127.0.0.1"; + public static final String DEFAULT_DOCKER_HOST = InetAddress.getLoopbackAddress().getHostAddress(); public final String tag; static DockerHostResolver dockerHostResolver = new DockerHostResolver(); @@ -248,16 +248,21 @@ public Starter(Class type, DockerImage image) { } private String getPortMapping(int port) { + // docker command needs ipv6 addresses in brackets return portOffset == null - ? ipAddress + "::" + port - : ipAddress + ":" + (portOffset + port) + ":" + port + ? addBracketsIfNeeded(ipAddress) + "::" + port + : addBracketsIfNeeded(ipAddress) + ":" + (portOffset + port) + ":" + port ; } private String getUdpPortMapping(int udpPort) { return portOffset == null - ? ipAddress + "::" + udpPort + "/udp" - : ipAddress + ":" + (portOffset + udpPort) + ":" + udpPort + "/udp"; + ? addBracketsIfNeeded(ipAddress) + "::" + udpPort + "/udp" + : addBracketsIfNeeded(ipAddress) + ":" + (portOffset + udpPort) + ":" + udpPort + "/udp"; + } + + private static String addBracketsIfNeeded(String ipAddress) { + return (DockerContainer.ipv6Enabled() && !ipAddress.contains("[")) ? DockerContainer.encloseInBrackets(ipAddress) : ipAddress; } } } diff --git a/src/test/java/org/jenkinsci/test/acceptance/docker/DockerImageTest.java b/src/test/java/org/jenkinsci/test/acceptance/docker/DockerImageTest.java index ac2e5d0..e3fab7c 100644 --- a/src/test/java/org/jenkinsci/test/acceptance/docker/DockerImageTest.java +++ b/src/test/java/org/jenkinsci/test/acceptance/docker/DockerImageTest.java @@ -10,13 +10,15 @@ import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; +import java.net.InetAddress; + @RunWith(MockitoJUnitRunner.class) public class DockerImageTest { private static final String DOCKER_HOST_IP= "42.42.42.42"; private static final String DOCKER_HOST_SOCKET= "unix:///var/run/foo.sock"; private static final String DOCKER_HOST_INVALID= "hfdsdfah"; - private static final String DOCKER_HOST_LOCALHOST= "127.0.0.1"; + private static final String DOCKER_HOST_LOCALHOST= InetAddress.getLoopbackAddress().getHostAddress(); @Mock DockerImage.DockerHostResolver dockerHostResolver; @@ -40,7 +42,7 @@ public void shouldReturnLocalhostIfDockerHostEnvironmentNotSet() { DockerImage dockerImage = new DockerImage("a"); dockerImage.dockerHostResolver = dockerHostResolver; - Assert.assertThat(dockerImage.getDockerHost(), CoreMatchers.is("127.0.0.1")); + Assert.assertThat(dockerImage.getDockerHost(), CoreMatchers.is(DOCKER_HOST_LOCALHOST)); } @Test From 075394435e7232ed10179152f44545e9f3098681 Mon Sep 17 00:00:00 2001 From: Raul Arabaolaza Date: Thu, 11 Jan 2024 18:16:27 +0100 Subject: [PATCH 2/4] Custom versioning scheme --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f2cf4cd..92d5c55 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ org.jenkins-ci.test docker-fixtures - ${changelist} + ${changelist}--TEST-IPV6 Docker Fixtures Docker rule for JUnit. https://github.com/jenkinsci/docker-fixtures From 8e9c6159c8705bcda38ba8c04ca9cbb12e0dbba5 Mon Sep 17 00:00:00 2001 From: Raul Arabaolaza Date: Fri, 12 Jan 2024 11:57:49 +0100 Subject: [PATCH 3/4] Restore pom after private local deploy for testing --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 92d5c55..f2cf4cd 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ org.jenkins-ci.test docker-fixtures - ${changelist}--TEST-IPV6 + ${changelist} Docker Fixtures Docker rule for JUnit. https://github.com/jenkinsci/docker-fixtures From be883c6824d80b5bcbf8e4244369c9d5bbb708cb Mon Sep 17 00:00:00 2001 From: Raul Arabaolaza Date: Fri, 26 Jan 2024 16:27:43 +0100 Subject: [PATCH 4/4] Address feedback --- .../test/acceptance/docker/DockerContainer.java | 11 ++++------- .../jenkinsci/test/acceptance/docker/DockerImage.java | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/jenkinsci/test/acceptance/docker/DockerContainer.java b/src/main/java/org/jenkinsci/test/acceptance/docker/DockerContainer.java index 5c1ccb6..a6438ab 100644 --- a/src/main/java/org/jenkinsci/test/acceptance/docker/DockerContainer.java +++ b/src/main/java/org/jenkinsci/test/acceptance/docker/DockerContainer.java @@ -93,7 +93,7 @@ public String ipBound(int n) { if (out.isEmpty()) // expected to return single line like "0.0.0.0:55326" or [::]:55326 throw new IllegalStateException(format("Port %d is not mapped for container %s", n, cid)); // assumes it is published only in ipv6 or only in ipv4 - return ipv6Enabled() ? out.substring(1,out.lastIndexOf(":") - 1) : out.split(":")[0]; + return out.substring(0,out.lastIndexOf(":")).replaceAll("[\\[\\]]",""); } catch (IOException | InterruptedException e) { throw new AssertionError("Failed to figure out port map " + n, e); } @@ -111,7 +111,7 @@ public String ipUdpBound(int n) { String out = Docker.cmd("port").add(cid, n + "/udp").popen().verifyOrDieWith("docker port command failed").trim(); if (out.isEmpty()) // expected to return single line like "0.0.0.0:55326" or [::]:55326 throw new IllegalStateException(format("Udp port %d is not mapped for container %s", n, cid)); - return ipv6Enabled() ? out.substring(1,out.lastIndexOf(":") -1) : out.split(":")[0]; + return out.substring(0,out.lastIndexOf(":")).replaceAll("[\\[\\]]",""); } catch (IOException | InterruptedException e) { throw new AssertionError("Failed to figure out udp port map " + n, e); } @@ -130,7 +130,7 @@ public int port(int n) { if (out.isEmpty()) // expected to return single line like "0.0.0.0:55326" or [::]:55326 throw new IllegalStateException(format("Port %d is not mapped for container %s", n, cid)); - return Integer.parseInt(out.split(":")[ipv6Enabled() ? 3 : 1]); + return Integer.parseInt(out.substring(out.lastIndexOf(":") + 1, out.length())); } catch (IOException | InterruptedException e) { throw new AssertionError("Failed to figure out port map " + n, e); } @@ -149,7 +149,7 @@ public int udpPort(int n) { if (out.isEmpty()) // expected to return single line like "0.0.0.0:55326" or [::]:55326 throw new IllegalStateException(format("Udp port %d is not mapped for container %s", n, cid)); - return Integer.parseInt(out.split(":")[ipv6Enabled() ? 3 : 1]); + return Integer.parseInt(out.substring(out.lastIndexOf(":") + 1, out.length())); } catch (IOException | InterruptedException e) { throw new AssertionError("Failed to figure out udp port map " + n, e); } @@ -240,7 +240,4 @@ public static boolean ipv6Enabled() { return Boolean.getBoolean("java.net.preferIPv6Addresses"); } - public static String encloseInBrackets(String toEnclose) { - return String.format("[%s]", toEnclose); - } } diff --git a/src/main/java/org/jenkinsci/test/acceptance/docker/DockerImage.java b/src/main/java/org/jenkinsci/test/acceptance/docker/DockerImage.java index 84e2dfb..d79688b 100644 --- a/src/main/java/org/jenkinsci/test/acceptance/docker/DockerImage.java +++ b/src/main/java/org/jenkinsci/test/acceptance/docker/DockerImage.java @@ -262,7 +262,7 @@ private String getUdpPortMapping(int udpPort) { } private static String addBracketsIfNeeded(String ipAddress) { - return (DockerContainer.ipv6Enabled() && !ipAddress.contains("[")) ? DockerContainer.encloseInBrackets(ipAddress) : ipAddress; + return DockerContainer.ipv6Enabled() && !ipAddress.contains("[") ? String.format("[%s]", ipAddress) : ipAddress; } } }