| 
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