Skip to content
This repository was archived by the owner on Nov 13, 2025. It is now read-only.

Commit 394d600

Browse files
committed
[wip] add logs to debug /attach on windows
1 parent 1c2318c commit 394d600

File tree

2 files changed

+180
-6
lines changed

2 files changed

+180
-6
lines changed
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
package de.gesellix.docker.engine;
2+
3+
import okhttp3.Call;
4+
import okhttp3.Callback;
5+
import okhttp3.Response;
6+
import okio.BufferedSink;
7+
import okio.Okio;
8+
import okio.Source;
9+
import org.jetbrains.annotations.NotNull;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
13+
import java.io.IOException;
14+
import java.util.Timer;
15+
import java.util.TimerTask;
16+
import java.util.concurrent.CountDownLatch;
17+
import java.util.concurrent.TimeUnit;
18+
import java.util.function.Supplier;
19+
20+
public class OkResponseCallback implements Callback {
21+
22+
private static final Logger log = LoggerFactory.getLogger(OkResponseCallback.class);
23+
24+
private final ConnectionProvider connectionProvider;
25+
private final AttachConfig attachConfig;
26+
27+
public OkResponseCallback(ConnectionProvider connectionProvider, AttachConfig attachConfig) {
28+
this.connectionProvider = connectionProvider;
29+
this.attachConfig = attachConfig;
30+
}
31+
32+
@Override
33+
public void onFailure(@NotNull Call call, @NotNull final IOException e) {
34+
log.error("connection failed: " + e.getMessage(), e);
35+
attachConfig.onFailure(e);
36+
}
37+
38+
public void onFailure(Exception e) {
39+
log.error("error", e);
40+
attachConfig.onFailure(e);
41+
}
42+
43+
@Override
44+
public void onResponse(@NotNull final Call call, @NotNull final Response response) throws IOException {
45+
TcpUpgradeVerificator.ensureTcpUpgrade(response);
46+
47+
if (attachConfig.getStreams().getStdin() != null) {
48+
// pass input from the client via stdin and pass it to the output stream
49+
// running it in an own thread allows the client to gain back control
50+
final Source stdinSource = Okio.source(attachConfig.getStreams().getStdin());
51+
Thread writer = new Thread(() -> {
52+
try {
53+
final BufferedSink bufferedSink = Okio.buffer(getConnectionProvider().getSink());
54+
long written = bufferedSink.writeAll(stdinSource);
55+
log.warn("xxxxx - writer - written " + written);
56+
bufferedSink.flush();
57+
log.warn("xxxxx - writer - flushed");
58+
attachConfig.onSinkWritten(response);
59+
log.warn("xxxxx - writer - onSinkWritten");
60+
CountDownLatch done = new CountDownLatch(1);
61+
delayed(100, "writer", () -> {
62+
log.warn("xxxxx - writer - delayed");
63+
try {
64+
bufferedSink.close();
65+
log.warn("xxxxx - writer - delayed closed");
66+
attachConfig.onSinkClosed(response);
67+
log.warn("xxxxx - writer - delayed onSinkClosed");
68+
}
69+
catch (Exception e) {
70+
log.warn("error", e);
71+
}
72+
log.warn("xxxxx - writer - delayed return");
73+
return null;
74+
}, done);
75+
boolean inTime = done.await(5, TimeUnit.SECONDS);
76+
if (!inTime) {
77+
log.warn("xxxxx - writer - done timeout");
78+
}
79+
}
80+
catch (InterruptedException e) {
81+
log.debug("stdin->sink interrupted", e);
82+
Thread.currentThread().interrupt();
83+
}
84+
catch (Exception e) {
85+
onFailure(e);
86+
}
87+
finally {
88+
log.trace("writer finished");
89+
}
90+
});
91+
writer.setName("stdin-writer " + call.request().url().encodedPath());
92+
writer.start();
93+
}
94+
else {
95+
log.debug("no stdin.");
96+
}
97+
98+
if (attachConfig.getStreams().getStdout() != null) {
99+
final BufferedSink bufferedStdout = Okio.buffer(Okio.sink(attachConfig.getStreams().getStdout()));
100+
Thread reader = new Thread(() -> {
101+
try {
102+
log.warn("xxxxx - reader - writeAll -> " + getConnectionProvider().getSource());
103+
bufferedStdout.writeAll(getConnectionProvider().getSource());
104+
log.warn("xxxxx - reader - flush");
105+
bufferedStdout.flush();
106+
log.warn("xxxxx - reader - flushed");
107+
CountDownLatch done = new CountDownLatch(1);
108+
delayed(100, "reader", () -> {
109+
log.warn("xxxxx - reader - delay ...");
110+
attachConfig.onSourceConsumed();
111+
log.warn("xxxxx - reader - delay onSourceConsumed");
112+
return null;
113+
}, done);
114+
boolean inTime = done.await(5, TimeUnit.SECONDS);
115+
if (!inTime) {
116+
log.warn("xxxxx - reader - done timeout");
117+
}
118+
}
119+
catch (InterruptedException e) {
120+
log.debug("source->stdout interrupted", e);
121+
Thread.currentThread().interrupt();
122+
}
123+
catch (Exception e) {
124+
onFailure(e);
125+
}
126+
finally {
127+
log.trace("reader finished");
128+
}
129+
});
130+
reader.setName("stdout-reader " + call.request().url().encodedPath());
131+
reader.start();
132+
}
133+
else {
134+
log.debug("no stdout.");
135+
}
136+
137+
attachConfig.onResponse(response);
138+
}
139+
140+
public static void delayed(long delay, String name, final Supplier<?> action, final CountDownLatch done) {
141+
new Timer(true).schedule(new TimerTask() {
142+
@Override
143+
public void run() {
144+
Thread.currentThread().setName("Delayed " + name + " action (" + Thread.currentThread().getName() + ")");
145+
try {
146+
action.get();
147+
}
148+
catch (Exception e) {
149+
log.warn("xxxxx - delayed - error", e);
150+
throw e;
151+
}
152+
finally {
153+
log.warn("xxxxx - delayed - done");
154+
done.countDown();
155+
log.warn("xxxxx - delayed - cancel");
156+
cancel();
157+
}
158+
}
159+
}, delay);
160+
}
161+
162+
public ConnectionProvider getConnectionProvider() {
163+
return connectionProvider;
164+
}
165+
}

integrationtest/src/test/groovy/de/gesellix/docker/engine/OkDockerClientIntegrationSpec.groovy

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,17 @@ class OkDockerClientIntegrationSpec extends Specification {
126126
query: [fromImage: CONSTANTS.imageName]])
127127
// create container
128128
def containerConfig = [
129-
Tty : tty,
130-
OpenStdin : openStdin,
131-
Image : CONSTANTS.imageName,
132-
Entrypoint: ["/cat"]
129+
HostConfig : [
130+
AutoRemove: true
131+
],
132+
AttachStdin : true,
133+
AttachStdout: true,
134+
AttachStderr: true,
135+
Tty : tty,
136+
OpenStdin : openStdin,
137+
StdinOnce : true,
138+
Image : CONSTANTS.imageName,
139+
Entrypoint : ["/cat"]
133140
]
134141
String containerId = client.post([path : "/containers/create".toString(),
135142
query : [name: ""],
@@ -139,12 +146,13 @@ class OkDockerClientIntegrationSpec extends Specification {
139146
client.post([path : "/containers/${containerId}/start".toString(),
140147
requestContentType: "application/json"])
141148
// resize container TTY
142-
// client.post([path : "/containers/${containerId}/attach/resize".toString(),
143-
// query: [h: 46, w: 158]])
149+
client.post([path : "/containers/${containerId}/attach/resize".toString(),
150+
query: [h: 46, w: 158]])
144151
// inspect container
145152
// boolean multiplexStreams = !client.get([path: "/containers/${containerId}/json".toString()]).content.Config.Tty
146153

147154
String content = "attach ${UUID.randomUUID()}"
155+
println "content (length ${content.length()}): $content"
148156
String expectedOutput = containerConfig.Tty ? "$content\r\n$content\r\n" : "$content\n"
149157

150158
def stdout = new ByteArrayOutputStream(expectedOutput.length())
@@ -185,6 +193,7 @@ class OkDockerClientIntegrationSpec extends Specification {
185193

186194
when:
187195
stdin.write("$content\n".bytes)
196+
println "ttttt - written"
188197
stdin.flush()
189198
stdin.close()
190199
boolean sinkWritten = onSinkWritten.await(5, SECONDS)

0 commit comments

Comments
 (0)