|
6 | 6 | import de.gesellix.docker.remote.api.ExecInspectResponse; |
7 | 7 | import de.gesellix.docker.remote.api.ExecStartConfig; |
8 | 8 | import de.gesellix.docker.remote.api.IdResponse; |
| 9 | +import de.gesellix.docker.remote.api.core.Frame; |
9 | 10 | import de.gesellix.docker.remote.api.testutil.DockerEngineAvailable; |
10 | 11 | import de.gesellix.docker.remote.api.testutil.InjectDockerClient; |
11 | 12 | import de.gesellix.docker.remote.api.testutil.TestImage; |
| 13 | +import okio.BufferedSink; |
| 14 | +import okio.Okio; |
| 15 | +import okio.Sink; |
| 16 | + |
12 | 17 | import org.junit.jupiter.api.BeforeEach; |
13 | 18 | import org.junit.jupiter.api.Test; |
14 | 19 |
|
15 | 20 | import static de.gesellix.docker.remote.api.testutil.Constants.LABEL_KEY; |
16 | 21 | import static de.gesellix.docker.remote.api.testutil.Constants.LABEL_VALUE; |
| 22 | +import static de.gesellix.docker.remote.api.testutil.Failsafe.perform; |
17 | 23 | import static de.gesellix.docker.remote.api.testutil.Failsafe.removeContainer; |
| 24 | +import static java.time.temporal.ChronoUnit.SECONDS; |
18 | 25 | import static java.util.Arrays.asList; |
| 26 | +import static java.util.Collections.singletonList; |
19 | 27 | import static java.util.Collections.singletonMap; |
| 28 | +import static org.junit.jupiter.api.Assertions.assertEquals; |
20 | 29 | import static org.junit.jupiter.api.Assertions.assertFalse; |
21 | 30 | import static org.junit.jupiter.api.Assertions.assertNotNull; |
| 31 | +import static org.junit.jupiter.api.Assertions.assertSame; |
| 32 | +import static org.junit.jupiter.api.Assertions.assertTrue; |
| 33 | + |
| 34 | +import java.io.IOException; |
| 35 | +import java.time.Duration; |
| 36 | +import java.util.Timer; |
| 37 | +import java.util.TimerTask; |
| 38 | +import java.util.concurrent.CountDownLatch; |
| 39 | +import java.util.stream.Collectors; |
22 | 40 |
|
23 | 41 | @DockerEngineAvailable |
24 | 42 | class ExecApiIntegrationTest { |
@@ -83,4 +101,105 @@ public void containerExecNonInteractive() { |
83 | 101 |
|
84 | 102 | removeContainer(engineApiClient, "container-exec-test"); |
85 | 103 | } |
| 104 | + |
| 105 | + @Test |
| 106 | + public void containerExecInteractive() { |
| 107 | + removeContainer(engineApiClient, "container-exec-interactive-test"); |
| 108 | + |
| 109 | + imageApi.imageCreate(testImage.getImageName(), null, null, testImage.getImageTag(), null, null, null, null, null); |
| 110 | + |
| 111 | + ContainerCreateRequest containerCreateRequest = new ContainerCreateRequest( |
| 112 | + null, null, null, |
| 113 | + true, true, true, |
| 114 | + null, |
| 115 | + true, true, null, |
| 116 | + null, |
| 117 | + null, |
| 118 | + null, |
| 119 | + null, |
| 120 | + testImage.getImageWithTag(), |
| 121 | + null, null, null, |
| 122 | + null, null, |
| 123 | + null, |
| 124 | + singletonMap(LABEL_KEY, LABEL_VALUE), |
| 125 | + null, null, |
| 126 | + null, |
| 127 | + null, |
| 128 | + null |
| 129 | + ); |
| 130 | + containerApi.containerCreate(containerCreateRequest, "container-exec-interactive-test"); |
| 131 | + containerApi.containerStart("container-exec-interactive-test", null); |
| 132 | + |
| 133 | + IdResponse exec = execApi.containerExec( |
| 134 | + "container-exec-interactive-test", |
| 135 | + new ExecConfig(true, true, true, null, null, true, |
| 136 | + null, |
| 137 | + singletonList("/cat"), |
| 138 | + null, null, null)); |
| 139 | + assertNotNull(exec.getId()); |
| 140 | + |
| 141 | + Duration timeout = Duration.of(5, SECONDS); |
| 142 | + LogFrameStreamCallback callback = new LogFrameStreamCallback() { |
| 143 | + @Override |
| 144 | + public void attachInput(Sink sink) { |
| 145 | + System.out.println("attachInput, sending data..."); |
| 146 | + new Thread(() -> { |
| 147 | + BufferedSink buffer = Okio.buffer(sink); |
| 148 | + try { |
| 149 | + buffer.writeUtf8("hello echo\n"); |
| 150 | + buffer.flush(); |
| 151 | + System.out.println("... data sent"); |
| 152 | + } catch (IOException e) { |
| 153 | + e.printStackTrace(); |
| 154 | + System.err.println("Failed to write to stdin: " + e.getMessage()); |
| 155 | + } finally { |
| 156 | + try { |
| 157 | + Thread.sleep(100); |
| 158 | + sink.close(); |
| 159 | + } catch (Exception ignored) { |
| 160 | + // ignore |
| 161 | + } |
| 162 | + } |
| 163 | + }).start(); |
| 164 | + } |
| 165 | + }; |
| 166 | + |
| 167 | + new Thread(() -> execApi.execStart( |
| 168 | + exec.getId(), |
| 169 | + new ExecStartConfig(false, true, null), |
| 170 | + callback, timeout.toMillis())).start(); |
| 171 | + |
| 172 | + CountDownLatch wait = new CountDownLatch(1); |
| 173 | + new Timer().schedule(new TimerTask() { |
| 174 | + @Override |
| 175 | + public void run() { |
| 176 | + if (callback.job != null) { |
| 177 | + callback.job.cancel(); |
| 178 | + } |
| 179 | + wait.countDown(); |
| 180 | + } |
| 181 | + }, 5000); |
| 182 | + |
| 183 | + try { |
| 184 | + wait.await(); |
| 185 | + } |
| 186 | + catch (InterruptedException e) { |
| 187 | + e.printStackTrace(); |
| 188 | + } |
| 189 | + |
| 190 | + ExecInspectResponse execInspect = execApi.execInspect(exec.getId()); |
| 191 | + assertTrue(execInspect.getRunning()); |
| 192 | + |
| 193 | + assertSame(Frame.StreamType.RAW, callback.frames.stream().findAny().get().getStreamType()); |
| 194 | + assertEquals( |
| 195 | + "hello echo\nhello echo".replaceAll("[\\n\\r]", ""), |
| 196 | + callback.frames.stream().map(Frame::getPayloadAsString).collect(Collectors.joining()).replaceAll("[\\n\\r]", "")); |
| 197 | + |
| 198 | + removeContainer(engineApiClient, "container-exec-interactive-test"); |
| 199 | + |
| 200 | + perform(() -> { |
| 201 | + ExecInspectResponse execInspectAfterStop = execApi.execInspect(exec.getId()); |
| 202 | + assertFalse(execInspectAfterStop.getRunning()); |
| 203 | + }); |
| 204 | + } |
86 | 205 | } |
0 commit comments