Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removed restriction that port mappings are integers to support UDP #610

Merged
merged 1 commit into from
Nov 17, 2016
Merged
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 @@ -3,6 +3,8 @@
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.common.base.Joiner;
import io.fabric8.maven.docker.config.Arguments;
Expand Down Expand Up @@ -52,7 +54,7 @@ public class DockerFileBuilder {
private List<AddEntry> addEntries = new ArrayList<>();

// list of ports to expose and environments to use
private List<Integer> ports = new ArrayList<>();
private List<String> ports = new ArrayList<>();

// list of RUN Commands to run along with image build see issue #191 on github
private List<String> runCmds = new ArrayList<>();
Expand Down Expand Up @@ -235,14 +237,48 @@ private String quote(String value) {

private void addPorts(StringBuilder b) {
if (ports.size() > 0) {
String[] portsS = new String[ports.size()];
int i = 0;
for (Integer port : ports) {
portsS[i++] = port.toString();
String[] portsS = ports.toArray(new String[]{});
for(String port : portsS) {
validatePortMapping(port);
}
DockerFileKeyword.EXPOSE.addTo(b, portsS);
}
}

// Pattern for splitting of the protocol part of the port.
private static final Pattern PROTOCOL_SPLIT_PATTERN = Pattern.compile("(.*?)(?:/(tcp|udp))?$");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guess we can match case insensitive here, and do a lowercase when adding them to the port list. makes it a bit more flexible for users.

private void validatePortMapping(String input) throws IllegalArgumentException {
try {
Matcher matcher = PROTOCOL_SPLIT_PATTERN.matcher(input);
// Matches always. If there is a tcp/udp protocol, should end up in the second group
// and get factored out. If it's something invalid, it should get stuck to the first group.
matcher.matches();

// First group is the port mapping.
// It may be split with a : in a variety of ways.
String mapping = matcher.group(1);
String[] mappingParts = mapping.split(":", 3);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just re-looked at this PR and I think that is not correct here as we are talking about the ports to expose in a Dockerfile and not about mappings. So there must not be a : inlcude in the specificiation and only 8080 and 8080/tcp should be allowed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't mind I'd fix this now, since I have to make a release right now.

if(mappingParts.length == 1) {
// Single port should be an integer.
Integer.valueOf(mappingParts[0]);
} else if (mappingParts.length == 2) {
// Map of port to external port. Both should be integers.
Integer.valueOf(mappingParts[0]);
Integer.valueOf(mappingParts[1]);
} else {
// If length is 3, then our first chunk is a hostname or IP address.
// Can't really validate that, but the 2nd and 3rd parts should be ports and therefore integers.
Integer.valueOf(mappingParts[1]);
Integer.valueOf(mappingParts[2]);
}


} catch (NumberFormatException exp) {
throw new IllegalArgumentException("\nInvalid port mapping '" + input + "'\n" +
"Required format: '<hostIP>:<hostPort>:<containerPort>(/tcp|udp)'\n" +
"See the reference manual for more details");
}
}

private void addOptimisation() {
if (runCmds != null && !runCmds.isEmpty() && shouldOptimise) {
Expand Down Expand Up @@ -342,15 +378,7 @@ public DockerFileBuilder add(String source, String destination) {

public DockerFileBuilder expose(List<String> ports) {
if (ports != null) {
for (String port : ports) {
if (port != null) {
try {
this.ports.add(Integer.parseInt(port));
} catch (NumberFormatException exp) {
throw new IllegalArgumentException("Non numeric port " + port + " specified in port mapping",exp);
}
}
}
this.ports.addAll(ports);
}
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,74 @@ public void testBuildDockerFile() throws Exception {
String expected = loadFile("docker/Dockerfile.test");
assertEquals(expected, stripCR(dockerfileContent));
}

@Test
public void testBuildDockerFileUDPPort() throws Exception {
Arguments a = Arguments.Builder.get().withParam("c1").withParam("c2").build();
String dockerfileContent = new DockerFileBuilder().add("/src", "/dest")
.baseImage("image")
.cmd(a)
.basedir("/export")
.expose(Collections.singletonList("8080/udp"))
.maintainer("maintainer@example.com")
.workdir("/tmp")
.volumes(Collections.singletonList("/vol1"))
.run(Arrays.asList("echo something", "echo second"))
.content();
String expected = loadFile("docker/Dockerfile_udp.test");
assertEquals(expected, stripCR(dockerfileContent));
}

@Test
public void testBuildDockerFileExplicitTCPPort() throws Exception {
Arguments a = Arguments.Builder.get().withParam("c1").withParam("c2").build();
String dockerfileContent = new DockerFileBuilder().add("/src", "/dest")
.baseImage("image")
.cmd(a)
.basedir("/export")
.expose(Collections.singletonList("8080/tcp"))
.maintainer("maintainer@example.com")
.workdir("/tmp")
.volumes(Collections.singletonList("/vol1"))
.run(Arrays.asList("echo something", "echo second"))
.content();
String expected = loadFile("docker/Dockerfile_tcp.test");
assertEquals(expected, stripCR(dockerfileContent));
}

@Test(expected=IllegalArgumentException.class)
public void testBuildDockerFileBadPort() throws Exception {
Arguments a = Arguments.Builder.get().withParam("c1").withParam("c2").build();
new DockerFileBuilder().add("/src", "/dest")
.baseImage("image")
.cmd(a)
.env(ImmutableMap.of("foo", "bar"))
.basedir("/export")
.expose(Collections.singletonList("8080aaa/udp"))
.maintainer("maintainer@example.com")
.workdir("/tmp")
.labels(ImmutableMap.of("com.acme.foobar", "How are \"you\" ?"))
.volumes(Collections.singletonList("/vol1"))
.run(Arrays.asList("echo something", "echo second"))
.content();
}

@Test(expected=IllegalArgumentException.class)
public void testBuildDockerFileBadProtocol() throws Exception {
Arguments a = Arguments.Builder.get().withParam("c1").withParam("c2").build();
new DockerFileBuilder().add("/src", "/dest")
.baseImage("image")
.cmd(a)
.env(ImmutableMap.of("foo", "bar"))
.basedir("/export")
.expose(Collections.singletonList("8080/bogusdatagram"))
.maintainer("maintainer@example.com")
.workdir("/tmp")
.labels(ImmutableMap.of("com.acme.foobar", "How are \"you\" ?"))
.volumes(Collections.singletonList("/vol1"))
.run(Arrays.asList("echo something", "echo second"))
.content();
}

@Test
public void testDockerFileOptimisation() throws Exception {
Expand Down
9 changes: 9 additions & 0 deletions src/test/resources/docker/Dockerfile_tcp.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM image
MAINTAINER maintainer@example.com
EXPOSE 8080/tcp
COPY /src /export/dest
WORKDIR /tmp
RUN echo something
RUN echo second
VOLUME ["/vol1"]
CMD ["c1","c2"]
9 changes: 9 additions & 0 deletions src/test/resources/docker/Dockerfile_udp.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM image
MAINTAINER maintainer@example.com
EXPOSE 8080/udp
COPY /src /export/dest
WORKDIR /tmp
RUN echo something
RUN echo second
VOLUME ["/vol1"]
CMD ["c1","c2"]