Skip to content

Commit

Permalink
Make stop container idempotent (#544)
Browse files Browse the repository at this point in the history
  • Loading branch information
cristianrgreco authored Apr 25, 2023
1 parent e754db3 commit 6c04d9f
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 11 deletions.
15 changes: 6 additions & 9 deletions src/generic-container/generic-container-restart.test.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { GenericContainer } from "./generic-container";
import { checkContainerIsHealthy } from "../test-helper";
import { RandomUuid } from "../uuid";

describe("GenericContainer restart", () => {
jest.setTimeout(180_000);

it("should restart", async () => {
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14")
.withName("restartingContainer")
.withName(`container-${new RandomUuid().nextUuid()}`)
.withExposedPorts(8080)
.start();
await checkContainerIsHealthy(container);

await container.restart();
await checkContainerIsHealthy(container);

await checkContainerIsHealthy(container);
expect(container.getId()).toStrictEqual(container.getId());
expect(container.getName()).toStrictEqual(container.getName());

Expand All @@ -22,18 +22,15 @@ describe("GenericContainer restart", () => {

it("should restart persisting state", async () => {
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14")
.withName("restartingContainer2")
.withName(`container-${new RandomUuid().nextUuid()}`)
.withExposedPorts(8080)
.start();
await checkContainerIsHealthy(container);
await container.exec(["sh", "-c", "echo 'testconfig' >> config.txt"]);

await container.restart();
await checkContainerIsHealthy(container);
const result = await container.exec(["cat", "config.txt"]);

expect(result.output).toEqual(expect.stringContaining("testconfig"));

await checkContainerIsHealthy(container);
expect((await container.exec(["cat", "config.txt"])).output).toEqual(expect.stringContaining("testconfig"));
await container.stop();
});
});
33 changes: 32 additions & 1 deletion src/generic-container/generic-container.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@ import path from "path";
import getPort from "get-port";
import { GenericContainer } from "./generic-container";
import { AlwaysPullPolicy } from "../pull-policy";
import { checkContainerIsHealthy, getDockerEventStream, waitForDockerEvent } from "../test-helper";
import {
checkContainerIsHealthy,
getDockerEventStream,
getRunningContainerNames,
waitForDockerEvent,
} from "../test-helper";
import { getContainerById } from "../docker/functions/container/get-container";
import { RandomUuid } from "../uuid";

describe("GenericContainer", () => {
jest.setTimeout(180_000);
Expand Down Expand Up @@ -306,4 +312,29 @@ describe("GenericContainer", () => {

await startedContainer.stop();
});

it("should stop the container", async () => {
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14")
.withName(`container-${new RandomUuid().nextUuid()}`)
.start();

await container.stop();

expect(await getRunningContainerNames()).not.toContain(container.getName());
});

it("should stop the container idempotently", async () => {
const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14")
.withName(`container-${new RandomUuid().nextUuid()}`)
.start();

const stopContainerPromises = Promise.all(
Array(5)
.fill(0)
.map(() => container.stop())
);

await expect(stopContainerPromises).resolves.not.toThrow();
expect(await getRunningContainerNames()).not.toContain(container.getName());
});
});
12 changes: 11 additions & 1 deletion src/generic-container/started-generic-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ import { restartContainer } from "../docker/functions/container/restart-containe
import { WaitStrategy } from "../wait-strategy/wait-strategy";
import { waitForContainer } from "../wait-for-container";
import { dockerClient } from "../docker/docker-client";
import AsyncLock from "async-lock";

export class StartedGenericContainer implements StartedTestContainer {
private stoppedContainer?: StoppedTestContainer;
private stopContainerLock = new AsyncLock();

constructor(
private readonly container: Dockerode.Container,
private readonly host: string,
Expand All @@ -28,7 +32,13 @@ export class StartedGenericContainer implements StartedTestContainer {
protected containerIsStopping?(): Promise<void>;

public async stop(options: Partial<StopOptions> = {}): Promise<StoppedTestContainer> {
return this.stopContainer(options);
return this.stopContainerLock.acquire("stop", async () => {
if (this.stoppedContainer) {
return this.stoppedContainer;
}
this.stoppedContainer = await this.stopContainer(options);
return this.stoppedContainer;
});
}

protected containerIsStopped?(): Promise<void>;
Expand Down

0 comments on commit 6c04d9f

Please sign in to comment.