|
1 | 1 | package org.testcontainers.containers; |
2 | 2 |
|
3 | | -import static com.google.common.collect.Lists.newArrayList; |
4 | | -import static org.testcontainers.utility.CommandLine.runShellCommand; |
5 | | - |
6 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; |
7 | 4 | import com.fasterxml.jackson.databind.MapperFeature; |
8 | 5 | import com.fasterxml.jackson.databind.SerializationFeature; |
|
16 | 13 | import com.github.dockerjava.api.model.HostConfig; |
17 | 14 | import com.github.dockerjava.api.model.Link; |
18 | 15 | import com.github.dockerjava.api.model.PortBinding; |
| 16 | +import com.github.dockerjava.api.model.Ports; |
19 | 17 | import com.github.dockerjava.api.model.Volume; |
20 | 18 | import com.github.dockerjava.api.model.VolumesFrom; |
21 | 19 | import com.github.dockerjava.core.DefaultDockerClientConfig; |
22 | 20 | import com.google.common.annotations.VisibleForTesting; |
23 | 21 | import com.google.common.base.Strings; |
24 | 22 | import com.google.common.collect.ImmutableMap; |
25 | 23 | import com.google.common.hash.Hashing; |
26 | | -import java.io.File; |
27 | | -import java.io.IOException; |
28 | | -import java.lang.reflect.InvocationTargetException; |
29 | | -import java.lang.reflect.Method; |
30 | | -import java.lang.reflect.UndeclaredThrowableException; |
31 | | -import java.nio.charset.Charset; |
32 | | -import java.nio.file.Files; |
33 | | -import java.nio.file.Path; |
34 | | -import java.nio.file.Paths; |
35 | | -import java.time.Duration; |
36 | | -import java.time.Instant; |
37 | | -import java.util.ArrayList; |
38 | | -import java.util.Arrays; |
39 | | -import java.util.Collections; |
40 | | -import java.util.HashMap; |
41 | | -import java.util.HashSet; |
42 | | -import java.util.Iterator; |
43 | | -import java.util.LinkedHashMap; |
44 | | -import java.util.LinkedHashSet; |
45 | | -import java.util.List; |
46 | | -import java.util.Map; |
47 | | -import java.util.Map.Entry; |
48 | | -import java.util.Optional; |
49 | | -import java.util.Set; |
50 | | -import java.util.concurrent.ExecutionException; |
51 | | -import java.util.concurrent.Future; |
52 | | -import java.util.concurrent.TimeUnit; |
53 | | -import java.util.concurrent.atomic.AtomicInteger; |
54 | | -import java.util.function.Consumer; |
55 | | -import java.util.stream.Collectors; |
56 | | -import java.util.stream.Stream; |
57 | | -import java.util.zip.Adler32; |
58 | | -import java.util.zip.Checksum; |
59 | 24 | import lombok.AccessLevel; |
60 | 25 | import lombok.Data; |
61 | 26 | import lombok.NonNull; |
|
97 | 62 | import org.testcontainers.utility.ResourceReaper; |
98 | 63 | import org.testcontainers.utility.TestcontainersConfiguration; |
99 | 64 |
|
| 65 | +import java.io.File; |
| 66 | +import java.io.IOException; |
| 67 | +import java.lang.reflect.InvocationTargetException; |
| 68 | +import java.lang.reflect.Method; |
| 69 | +import java.lang.reflect.UndeclaredThrowableException; |
| 70 | +import java.nio.charset.Charset; |
| 71 | +import java.nio.file.Files; |
| 72 | +import java.nio.file.Path; |
| 73 | +import java.nio.file.Paths; |
| 74 | +import java.time.Duration; |
| 75 | +import java.time.Instant; |
| 76 | +import java.util.ArrayList; |
| 77 | +import java.util.Arrays; |
| 78 | +import java.util.Collections; |
| 79 | +import java.util.HashMap; |
| 80 | +import java.util.HashSet; |
| 81 | +import java.util.Iterator; |
| 82 | +import java.util.LinkedHashMap; |
| 83 | +import java.util.LinkedHashSet; |
| 84 | +import java.util.List; |
| 85 | +import java.util.Map; |
| 86 | +import java.util.Map.Entry; |
| 87 | +import java.util.Optional; |
| 88 | +import java.util.Set; |
| 89 | +import java.util.concurrent.ExecutionException; |
| 90 | +import java.util.concurrent.Future; |
| 91 | +import java.util.concurrent.TimeUnit; |
| 92 | +import java.util.concurrent.atomic.AtomicInteger; |
| 93 | +import java.util.function.Consumer; |
| 94 | +import java.util.stream.Collectors; |
| 95 | +import java.util.stream.Stream; |
| 96 | +import java.util.zip.Adler32; |
| 97 | +import java.util.zip.Checksum; |
| 98 | + |
| 99 | +import static com.google.common.collect.Lists.newArrayList; |
| 100 | +import static org.testcontainers.utility.CommandLine.runShellCommand; |
| 101 | + |
100 | 102 | /** |
101 | 103 | * Base class for that allows a container to be launched and controlled. |
102 | 104 | */ |
@@ -718,21 +720,28 @@ public Set<Integer> getLivenessCheckPortNumbers() { |
718 | 720 |
|
719 | 721 | private void applyConfiguration(CreateContainerCmd createCommand) { |
720 | 722 | HostConfig hostConfig = buildHostConfig(); |
721 | | - createCommand.withHostConfig(hostConfig); |
722 | | - |
723 | | - // Set up exposed ports (where there are no host port bindings defined) |
724 | | - ExposedPort[] portArray = exposedPorts.stream() |
725 | | - .map(ExposedPort::new) |
726 | | - .toArray(ExposedPort[]::new); |
727 | 723 |
|
728 | | - createCommand.withExposedPorts(portArray); |
| 724 | + // PortBindings must contain: |
| 725 | + // * all exposed ports with a randomized host port (equivalent to -p CONTAINER_PORT) |
| 726 | + // * all exposed ports with a fixed host port (equivalent to -p HOST_PORT:CONTAINER_PORT) |
| 727 | + Map<ExposedPort, PortBinding> allPortBindings = new HashMap<>(); |
| 728 | + // First collect all the randomized host ports from our 'exposedPorts' field |
| 729 | + for (final Integer tcpPort : exposedPorts) { |
| 730 | + ExposedPort exposedPort = ExposedPort.tcp(tcpPort); |
| 731 | + allPortBindings.put(exposedPort, new PortBinding(Ports.Binding.empty(), exposedPort)); |
| 732 | + } |
| 733 | + // Next collect all the fixed host ports from our 'portBindings' field, overwriting any randomized ports so that |
| 734 | + // we don't create two bindings for the same container port. |
| 735 | + for (final String portBinding : portBindings) { |
| 736 | + PortBinding parsedBinding = PortBinding.parse(portBinding); |
| 737 | + allPortBindings.put(parsedBinding.getExposedPort(), parsedBinding); |
| 738 | + } |
| 739 | + hostConfig.withPortBindings(new ArrayList<>(allPortBindings.values())); |
729 | 740 |
|
730 | | - // Set up exposed ports that need host port bindings |
731 | | - PortBinding[] portBindingArray = portBindings.stream() |
732 | | - .map(PortBinding::parse) |
733 | | - .toArray(PortBinding[]::new); |
| 741 | + // Next, ExposedPorts must be set up to publish all of the above ports, both randomized and fixed. |
| 742 | + createCommand.withExposedPorts(new ArrayList<>(allPortBindings.keySet())); |
734 | 743 |
|
735 | | - createCommand.withPortBindings(portBindingArray); |
| 744 | + createCommand.withHostConfig(hostConfig); |
736 | 745 |
|
737 | 746 | if (commandParts != null) { |
738 | 747 | createCommand.withCmd(commandParts); |
@@ -798,8 +807,6 @@ private void applyConfiguration(CreateContainerCmd createCommand) { |
798 | 807 | createCommand.withNetworkMode(networkForLinks.get()); |
799 | 808 | } |
800 | 809 |
|
801 | | - createCommand.withPublishAllPorts(true); |
802 | | - |
803 | 810 | PortForwardingContainer.INSTANCE.getNetwork().ifPresent(it -> { |
804 | 811 | withExtraHost(INTERNAL_HOST_HOSTNAME, it.getIpAddress()); |
805 | 812 | }); |
|
0 commit comments