|
13 | 13 | import org.slf4j.LoggerFactory; |
14 | 14 |
|
15 | 15 | import java.io.ByteArrayInputStream; |
| 16 | +import java.io.Closeable; |
16 | 17 | import java.io.File; |
17 | 18 | import java.io.InputStream; |
18 | 19 | import java.io.PipedInputStream; |
19 | 20 | import java.io.PipedOutputStream; |
| 21 | +import java.util.concurrent.CountDownLatch; |
20 | 22 | import java.util.concurrent.TimeUnit; |
| 23 | +import java.util.concurrent.atomic.AtomicLong; |
21 | 24 |
|
22 | 25 | import static com.github.dockerjava.junit.DockerRule.DEFAULT_IMAGE; |
23 | 26 | import static java.util.concurrent.TimeUnit.SECONDS; |
|
28 | 31 | import static org.hamcrest.Matchers.isEmptyString; |
29 | 32 | import static org.hamcrest.Matchers.not; |
30 | 33 | import static org.junit.Assert.assertEquals; |
| 34 | +import static org.junit.Assert.assertTrue; |
31 | 35 | import static org.junit.Assume.assumeThat; |
32 | 36 |
|
33 | 37 | /** |
@@ -205,6 +209,68 @@ public void onNext(Frame frame) { |
205 | 209 | callback.close(); |
206 | 210 | } |
207 | 211 |
|
| 212 | + /** |
| 213 | + * {@link AttachContainerResultCallback#onComplete()} should be called immediately after |
| 214 | + * container exit. It was broken for Netty and TLS connection. |
| 215 | + */ |
| 216 | + @Test |
| 217 | + public void attachContainerClosesStdoutWhenContainerExits() throws Exception { |
| 218 | + DockerClient dockerClient = dockerRule.getClient(); |
| 219 | + |
| 220 | + CreateContainerResponse container = dockerClient.createContainerCmd(DEFAULT_IMAGE) |
| 221 | + .withCmd("echo", "hello world") |
| 222 | + .withTty(false) |
| 223 | + .exec(); |
| 224 | + LOG.info("Created container: {}", container.toString()); |
| 225 | + |
| 226 | + final CountDownLatch started = new CountDownLatch(1); |
| 227 | + final AtomicLong startedAtNanos = new AtomicLong(); |
| 228 | + final CountDownLatch gotLine = new CountDownLatch(1); |
| 229 | + final CountDownLatch completed = new CountDownLatch(1); |
| 230 | + final AtomicLong gotLineAtNanos = new AtomicLong(); |
| 231 | + AttachContainerTestCallback callback = new AttachContainerTestCallback() { |
| 232 | + @Override |
| 233 | + public void onStart(Closeable stream) { |
| 234 | + startedAtNanos.set(System.nanoTime()); |
| 235 | + started.countDown(); |
| 236 | + super.onStart(stream); |
| 237 | + } |
| 238 | + |
| 239 | + @Override |
| 240 | + public void onNext(Frame item) { |
| 241 | + if (item.getStreamType() == StreamType.STDOUT) { |
| 242 | + gotLineAtNanos.set(System.nanoTime()); |
| 243 | + gotLine.countDown(); |
| 244 | + } |
| 245 | + super.onNext(item); |
| 246 | + } |
| 247 | + |
| 248 | + @Override |
| 249 | + public void onComplete() { |
| 250 | + completed.countDown(); |
| 251 | + super.onComplete(); |
| 252 | + } |
| 253 | + }; |
| 254 | + |
| 255 | + try (Closeable ignored = callback) { |
| 256 | + dockerClient.attachContainerCmd(container.getId()) |
| 257 | + .withStdOut(true) |
| 258 | + .withFollowStream(true) |
| 259 | + .exec(callback); |
| 260 | + |
| 261 | + dockerClient.startContainerCmd(container.getId()).exec(); |
| 262 | + |
| 263 | + assertTrue("Should start in a reasonable time", started.await(30, SECONDS)); |
| 264 | + assertTrue("Should get first line quickly after the start", gotLine.await(15, SECONDS)); |
| 265 | + |
| 266 | + long gotLineDurationSeconds = (gotLineAtNanos.get() - startedAtNanos.get()) / 1_000_000_000L; |
| 267 | + LOG.info("Got line from {} for {} seconds", container.getId(), gotLineDurationSeconds); |
| 268 | + |
| 269 | + boolean finished = completed.await(1L + gotLineDurationSeconds, SECONDS); |
| 270 | + assertTrue("Should get EOF in a time close to time of getting the first line", finished); |
| 271 | + } |
| 272 | + } |
| 273 | + |
208 | 274 | public static class AttachContainerTestCallback extends AttachContainerResultCallback { |
209 | 275 | private StringBuffer log = new StringBuffer(); |
210 | 276 |
|
|
0 commit comments