From e210b5dea316f2c25eedb3b8971a2f6536c96f06 Mon Sep 17 00:00:00 2001 From: Cristian Greco Date: Thu, 10 Apr 2025 11:57:02 +0100 Subject: [PATCH] Revert "Simplify log-wait-strategy (#977)" This reverts commit b837e9091e350339d1ba024d33c80cb8c0c83c69. --- .../wait-strategies/log-wait-strategy.test.ts | 27 -------- .../src/wait-strategies/log-wait-strategy.ts | 66 +++++++++++-------- 2 files changed, 37 insertions(+), 56 deletions(-) diff --git a/packages/testcontainers/src/wait-strategies/log-wait-strategy.test.ts b/packages/testcontainers/src/wait-strategies/log-wait-strategy.test.ts index 57961c1ea..62e537f0c 100644 --- a/packages/testcontainers/src/wait-strategies/log-wait-strategy.test.ts +++ b/packages/testcontainers/src/wait-strategies/log-wait-strategy.test.ts @@ -54,31 +54,4 @@ describe("LogWaitStrategy", { timeout: 180_000 }, () => { expect(await getRunningContainerNames()).not.toContain(containerName); }); - - it("should throw an error if the message is never received", async () => { - const containerName = `container-${new RandomUuid().nextUuid()}`; - - await expect( - new GenericContainer("cristianrgreco/testcontainer:1.1.14") - .withName(containerName) - .withCommand("/bin/sh", "-c", 'echo "Ready"') - .withWaitStrategy(Wait.forLogMessage("unexpected")) - .start() - ).rejects.toThrowError(`Log stream ended and message "unexpected" was not received`); - - expect(await getRunningContainerNames()).not.toContain(containerName); - }); - - it("does not matter if container does not send all content in a single line", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") - .withCommand([ - "node", - "-e", - "process.stdout.write('Hello '); setTimeout(() => process.stdout.write('World\\n'), 2000)", - ]) - .withWaitStrategy(Wait.forLogMessage("Hello World")) - .start(); - - await container.stop(); - }); }); diff --git a/packages/testcontainers/src/wait-strategies/log-wait-strategy.ts b/packages/testcontainers/src/wait-strategies/log-wait-strategy.ts index a54c3f928..005ef113b 100644 --- a/packages/testcontainers/src/wait-strategies/log-wait-strategy.ts +++ b/packages/testcontainers/src/wait-strategies/log-wait-strategy.ts @@ -1,6 +1,5 @@ import byline from "byline"; import Dockerode from "dockerode"; -import { setTimeout } from "timers/promises"; import { log } from "../common"; import { getContainerRuntimeClient } from "../container-runtime"; import { BoundPorts } from "../utils/bound-ports"; @@ -17,37 +16,46 @@ export class LogWaitStrategy extends AbstractWaitStrategy { } public async waitUntilReady(container: Dockerode.Container, boundPorts: BoundPorts, startTime?: Date): Promise { - await Promise.race([this.handleTimeout(container.id), this.handleLogs(container, startTime)]); - } - - async handleTimeout(containerId: string): Promise { - await setTimeout(this.startupTimeout); - this.throwError(containerId, `Log message "${this.message}" not received after ${this.startupTimeout}ms`); - } - - async handleLogs(container: Dockerode.Container, startTime?: Date): Promise { log.debug(`Waiting for log message "${this.message}"...`, { containerId: container.id }); const client = await getContainerRuntimeClient(); const stream = await client.container.logs(container, { since: startTime ? startTime.getTime() / 1000 : 0 }); - - let matches = 0; - for await (const line of byline(stream)) { - if (this.matches(line)) { - if (++matches === this.times) { - return log.debug(`Log wait strategy complete`, { containerId: container.id }); + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + const message = `Log message "${this.message}" not received after ${this.startupTimeout}ms`; + log.error(message, { containerId: container.id }); + reject(new Error(message)); + }, this.startupTimeout); + + const comparisonFn: (line: string) => boolean = (line: string) => { + if (this.message instanceof RegExp) { + return this.message.test(line); + } else { + return line.includes(this.message); } - } - } - - this.throwError(container.id, `Log stream ended and message "${this.message}" was not received`); - } - - matches(line: string): boolean { - return this.message instanceof RegExp ? this.message.test(line) : line.includes(this.message); - } - - throwError(containerId: string, message: string): void { - log.error(message, { containerId }); - throw new Error(message); + }; + + let count = 0; + const lineProcessor = (line: string) => { + if (comparisonFn(line)) { + if (++count === this.times) { + stream.destroy(); + clearTimeout(timeout); + log.debug(`Log wait strategy complete`, { containerId: container.id }); + resolve(); + } + } + }; + + byline(stream) + .on("data", lineProcessor) + .on("err", lineProcessor) + .on("end", () => { + stream.destroy(); + clearTimeout(timeout); + const message = `Log stream ended and message "${this.message}" was not received`; + log.error(message, { containerId: container.id }); + reject(new Error(message)); + }); + }); } }