Skip to content

Commit 8613312

Browse files
committed
Simplified log-wait-strategy, switch from callback to async, separated concerns, handled more timing edge cases. More details in the pull request
1 parent 1e9f62b commit 8613312

File tree

1 file changed

+26
-38
lines changed

1 file changed

+26
-38
lines changed
Lines changed: 26 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import byline from "byline";
21
import Dockerode from "dockerode";
2+
import { setTimeout } from "timers/promises";
33
import { log } from "../common";
44
import { getContainerRuntimeClient } from "../container-runtime";
55
import { BoundPorts } from "../utils/bound-ports";
@@ -16,46 +16,34 @@ export class LogWaitStrategy extends AbstractWaitStrategy {
1616
}
1717

1818
public async waitUntilReady(container: Dockerode.Container, boundPorts: BoundPorts, startTime?: Date): Promise<void> {
19+
await Promise.race([this.handleTimeout(container.id), this.handleLogs(container, startTime)]);
20+
}
21+
22+
async handleTimeout(containerId: string): Promise<void> {
23+
await setTimeout(this.startupTimeout);
24+
const message = `Log message "${this.message}" not received after ${this.startupTimeout}ms`;
25+
log.error(message, { containerId });
26+
throw new Error(message);
27+
}
28+
29+
async handleLogs(container: Dockerode.Container, startTime?: Date): Promise<void> {
1930
log.debug(`Waiting for log message "${this.message}"...`, { containerId: container.id });
2031
const client = await getContainerRuntimeClient();
2132
const stream = await client.container.logs(container, { since: startTime ? startTime.getTime() / 1000 : 0 });
22-
return new Promise((resolve, reject) => {
23-
const timeout = setTimeout(() => {
24-
const message = `Log message "${this.message}" not received after ${this.startupTimeout}ms`;
25-
log.error(message, { containerId: container.id });
26-
reject(new Error(message));
27-
}, this.startupTimeout);
28-
29-
const comparisonFn: (line: string) => boolean = (line: string) => {
30-
if (this.message instanceof RegExp) {
31-
return this.message.test(line);
32-
} else {
33-
return line.includes(this.message);
34-
}
35-
};
36-
37-
let count = 0;
38-
const lineProcessor = (line: string) => {
39-
if (comparisonFn(line)) {
40-
if (++count === this.times) {
41-
stream.destroy();
42-
clearTimeout(timeout);
43-
log.debug(`Log wait strategy complete`, { containerId: container.id });
44-
resolve();
45-
}
33+
34+
let matches = 0;
35+
for await (const chunk of stream) {
36+
if (this.matches(chunk)) {
37+
if (++matches === this.times) {
38+
return log.debug(`Log wait strategy complete`, { containerId: container.id });
4639
}
47-
};
48-
49-
byline(stream)
50-
.on("data", lineProcessor)
51-
.on("err", lineProcessor)
52-
.on("end", () => {
53-
stream.destroy();
54-
clearTimeout(timeout);
55-
const message = `Log stream ended and message "${this.message}" was not received`;
56-
log.error(message, { containerId: container.id });
57-
reject(new Error(message));
58-
});
59-
});
40+
}
41+
}
42+
43+
throw new Error("Log message not found");
44+
}
45+
46+
matches(chunk: string): boolean {
47+
return this.message instanceof RegExp ? this.message.test(chunk) : chunk.includes(this.message);
6048
}
6149
}

0 commit comments

Comments
 (0)