Skip to content

Commit e23a2df

Browse files
Replace ESM-only archive and port dependencies
1 parent d4c3ee1 commit e23a2df

17 files changed

Lines changed: 381 additions & 324 deletions

package-lock.json

Lines changed: 52 additions & 224 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/modules/azurecosmosdb/src/azure-cosmosdb-emulator-container.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
1-
import {
2-
AbstractStartedContainer,
3-
GenericContainer,
4-
PortGenerator,
5-
RandomPortGenerator,
6-
StartedTestContainer,
7-
Wait,
8-
} from "testcontainers";
1+
import { AbstractStartedContainer, GenericContainer, StartedTestContainer, Wait, getRandomPort } from "testcontainers";
92

103
type Protocol = "http" | "https";
114

@@ -21,11 +14,9 @@ export class AzureCosmosDbEmulatorContainer extends GenericContainer {
2114
private protocol: Protocol = DEFAULT_PROTOCOL;
2215
private telemetryEnabled = DEFAULT_TELEMETRY_ENABLED;
2316
private explorerEnabled = DEFAULT_EXPLORER_ENABLED;
24-
private portGenerator: PortGenerator;
2517

2618
constructor(image: string) {
2719
super(image);
28-
this.portGenerator = new RandomPortGenerator();
2920
this.withWaitStrategy(Wait.forLogMessage(COSMOS_READY_LOG_MESSAGE));
3021
}
3122

@@ -40,7 +31,7 @@ export class AzureCosmosDbEmulatorContainer extends GenericContainer {
4031
}
4132

4233
public override async start(): Promise<StartedAzureCosmosDbEmulatorContainer> {
43-
const port = await this.portGenerator.generatePort();
34+
const port = await getRandomPort();
4435
this.withExposedPorts({
4536
host: port,
4637
container: port,

packages/modules/azurite/src/azurite-container.test.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { BlobServiceClient, StorageSharedKeyCredential } from "@azure/storage-bl
33
import { QueueServiceClient } from "@azure/storage-queue";
44
import fs from "node:fs";
55
import path from "node:path";
6-
import { RandomPortGenerator } from "testcontainers";
6+
import { getRandomPort } from "testcontainers";
77
import { getImage } from "../../../testcontainers/src/utils/test-helper";
88
import { AzuriteContainer } from "./azurite-container";
99
import { createOAuthToken, createTokenCredential, getTlsPipelineOptions } from "./azurite-test-utils";
@@ -99,10 +99,9 @@ describe("AzuriteContainer", { timeout: 240_000 }, () => {
9999

100100
it("should be able to specify custom ports", async () => {
101101
// customPorts {
102-
const portGenerator = new RandomPortGenerator();
103-
const blobPort = await portGenerator.generatePort();
104-
const queuePort = await portGenerator.generatePort();
105-
const tablePort = await portGenerator.generatePort();
102+
const blobPort = await getRandomPort();
103+
const queuePort = await getRandomPort();
104+
const tablePort = await getRandomPort();
106105

107106
await using container = await new AzuriteContainer(IMAGE)
108107
.withSkipApiVersionCheck()

packages/modules/couchbase/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
"testcontainers": "^12.0.2"
3333
},
3434
"devDependencies": {
35-
"couchbase": "^4.7.0",
36-
"get-port": "^7.2.0"
35+
"couchbase": "^4.7.0"
3736
}
3837
}

packages/modules/couchbase/src/couchbase-container.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import couchbase, { Bucket, Cluster } from "couchbase";
2-
import getPort from "get-port";
2+
import { getRandomPort } from "testcontainers";
33
import { getImage } from "../../../testcontainers/src/utils/test-helper";
44
import { BucketDefinition } from "./bucket-definition";
55
import { CouchbaseContainer } from "./couchbase-container";
@@ -118,7 +118,7 @@ describe("CouchbaseContainer", { timeout: 180_000 }, () => {
118118
});
119119

120120
it("should preserve fixed host port binding", async () => {
121-
const hostPort = await getPort();
121+
const hostPort = await getRandomPort();
122122
await using container = await new CouchbaseContainer(COMMUNITY_IMAGE)
123123
.withEnabledServices(CouchbaseService.KV)
124124
.withExposedPorts({ container: PORTS.MGMT_PORT, host: hostPort })

packages/testcontainers/package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,28 +32,27 @@
3232
"dependencies": {
3333
"@balena/dockerignore": "^1.0.2",
3434
"@types/dockerode": "^4.0.1",
35-
"archiver": "^8.0.0",
3635
"async-lock": "^1.4.1",
3736
"byline": "^5.0.0",
3837
"debug": "^4.4.3",
3938
"docker-compose": "^1.4.2",
4039
"dockerode": "^5.0.0",
41-
"get-port": "^7.2.0",
4240
"proper-lockfile": "^4.1.2",
4341
"properties-reader": "^3.0.1",
4442
"ssh-remote-port-forward": "^1.0.4",
4543
"tar-fs": "^3.1.2",
44+
"tar-stream": "^3.2.0",
4645
"tmp": "^0.2.7",
4746
"undici": "^8.3.0"
4847
},
4948
"devDependencies": {
50-
"@types/archiver": "^8.0.0",
5149
"@types/async-lock": "^1.4.2",
5250
"@types/byline": "^4.2.36",
5351
"@types/debug": "^4.1.13",
5452
"@types/proper-lockfile": "^4.1.4",
5553
"@types/properties-reader": "^2.1.3",
5654
"@types/tar-fs": "^2.0.4",
55+
"@types/tar-stream": "^3.1.4",
5756
"@types/tmp": "^0.2.6"
5857
}
5958
}

packages/testcontainers/src/generic-container/generic-container.test.ts

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { TarArchive, type EntryData } from "archiver";
2-
import getPort from "get-port";
31
import path from "path";
2+
import * as tarStream from "tar-stream";
43
import { RandomUuid } from "../common";
54
import { getContainerRuntimeClient } from "../container-runtime";
5+
import { getRandomPort } from "../utils/port-generator";
66
import { PullPolicy } from "../utils/pull-policy";
77
import {
88
checkContainerIsHealthy,
@@ -38,7 +38,7 @@ describe("GenericContainer", { timeout: 180_000 }, () => {
3838
});
3939

4040
it("should bind to specified host port", async () => {
41-
const hostPort = await getPort();
41+
const hostPort = await getRandomPort();
4242
await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14")
4343
.withExposedPorts({
4444
container: 8080,
@@ -51,7 +51,7 @@ describe("GenericContainer", { timeout: 180_000 }, () => {
5151
});
5252

5353
it("should bind to specified host port with a different protocol", async () => {
54-
const hostPort = await getPort();
54+
const hostPort = await getRandomPort();
5555
await using container = await new GenericContainer("mendhak/udp-listener")
5656
.withWaitStrategy(Wait.forLogMessage("Listening on UDP port 5005"))
5757
.withExposedPorts({
@@ -473,6 +473,19 @@ describe("GenericContainer", { timeout: 180_000 }, () => {
473473
expect((await container.exec("cat /tmp/test.txt")).output).toEqual(expect.stringContaining("hello world"));
474474
});
475475

476+
it("should copy directory to started container with permissions", async () => {
477+
const source = path.resolve(fixtures, "docker");
478+
const target = "/tmp/started-newdir";
479+
const mode = parseInt("0777", 8);
480+
await using container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14")
481+
.withExposedPorts(8080)
482+
.start();
483+
484+
await container.copyDirectoriesToContainer([{ source, target, mode }]);
485+
486+
expect((await container.exec(`stat -c "%a %n" /tmp/started-newdir/test.txt`)).output).toContain("777");
487+
});
488+
476489
it("should copy content to container", async () => {
477490
const content = "hello world";
478491
const target = "/tmp/test.txt";
@@ -521,9 +534,7 @@ describe("GenericContainer", { timeout: 180_000 }, () => {
521534
.withExposedPorts(8080)
522535
.start();
523536

524-
const tar = new TarArchive();
525-
tar.append("hello world", { name: targetWithCopyOwnership.slice(1), uid, gid } as EntryData);
526-
tar.finalize();
537+
const tar = createArchiveWithOwnership(targetWithCopyOwnership, uid, gid);
527538

528539
await container.copyArchiveToContainer(tar, "/", { copyUIDGID: true });
529540

@@ -536,9 +547,7 @@ describe("GenericContainer", { timeout: 180_000 }, () => {
536547
const uid = 4242;
537548
const gid = 4343;
538549
const targetWithCopyOwnership = "/tmp/with-copy-archives-copyuidgid.txt";
539-
const tar = new TarArchive();
540-
tar.append("hello world", { name: targetWithCopyOwnership.slice(1), uid, gid } as EntryData);
541-
tar.finalize();
550+
const tar = createArchiveWithOwnership(targetWithCopyOwnership, uid, gid);
542551

543552
await using containerWithCopyOwnership = await new GenericContainer("cristianrgreco/testcontainer:1.1.14")
544553
.withCopyArchivesToContainer([
@@ -672,3 +681,27 @@ describe("GenericContainer", { timeout: 180_000 }, () => {
672681
await GenericContainer.fromDockerfile(context).withTarget("invalid").build();
673682
});
674683
});
684+
685+
const createArchiveWithOwnership = (target: string, uid: number, gid: number) => {
686+
const content = "hello world";
687+
const tar = tarStream.pack();
688+
689+
tar.entry(
690+
{
691+
name: target.slice(1),
692+
uid,
693+
gid,
694+
size: Buffer.byteLength(content),
695+
},
696+
content,
697+
(err) => {
698+
if (err) {
699+
tar.destroy(err);
700+
} else {
701+
tar.finalize();
702+
}
703+
}
704+
);
705+
706+
return tar;
707+
};

packages/testcontainers/src/generic-container/generic-container.ts

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import { TarArchive, type Archiver } from "archiver";
21
import AsyncLock from "async-lock";
32
import { Container, ContainerCreateOptions, ContainerInspectInfo, HostConfig } from "dockerode";
4-
import { promises as fs } from "fs";
53
import { Readable } from "stream";
64
import { containerLog, hash, log, toNanos } from "../common";
75
import { ContainerRuntimeClient, getContainerRuntimeClient, ImageName } from "../container-runtime";
@@ -31,6 +29,7 @@ import { createLabels, LABEL_TESTCONTAINERS_CONTAINER_HASH, LABEL_TESTCONTAINERS
3129
import { mapInspectResult } from "../utils/map-inspect-result";
3230
import { getContainerPort, getProtocol, hasHostBinding, PortWithOptionalBinding } from "../utils/port";
3331
import { ImagePullPolicy, PullPolicy } from "../utils/pull-policy";
32+
import { createTarArchive } from "../utils/tar-archive";
3433
import { selectWaitStrategy } from "../wait-strategies/utils/wait-strategy-selector";
3534
import { waitForContainer } from "../wait-strategies/wait-for-container";
3635
import { WaitStrategy } from "../wait-strategies/wait-strategy";
@@ -198,8 +197,11 @@ export class GenericContainer implements TestContainer {
198197
}
199198

200199
if (this.filesToCopy.length > 0 || this.directoriesToCopy.length > 0 || this.contentsToCopy.length > 0) {
201-
const archive = await this.createArchiveToCopyToContainer();
202-
archive.finalize();
200+
const archive = await createTarArchive({
201+
filesToCopy: this.filesToCopy,
202+
directoriesToCopy: this.directoriesToCopy,
203+
contentsToCopy: this.contentsToCopy,
204+
});
203205
await client.container.putArchive(container, archive, "/", this.copyToContainerOptions);
204206
}
205207

@@ -275,28 +277,6 @@ export class GenericContainer implements TestContainer {
275277
}
276278
}
277279

278-
private async createArchiveToCopyToContainer(): Promise<Archiver> {
279-
const tar = new TarArchive();
280-
const filesToCopyWithStats = await Promise.all(
281-
this.filesToCopy.map(async (fileToCopy) => ({
282-
...fileToCopy,
283-
stats: await fs.stat(fileToCopy.source),
284-
}))
285-
);
286-
287-
for (const { source, target, mode, stats } of filesToCopyWithStats) {
288-
tar.file(source, { name: target, mode, stats });
289-
}
290-
for (const { source, target, mode } of this.directoriesToCopy) {
291-
tar.directory(source, target, { mode });
292-
}
293-
for (const { content, target, mode } of this.contentsToCopy) {
294-
tar.append(content, { name: target, mode });
295-
}
296-
297-
return tar;
298-
}
299-
300280
protected containerStarted?(
301281
container: StartedTestContainer,
302282
inspectResult: InspectResult,

packages/testcontainers/src/generic-container/started-generic-container.ts

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import { TarArchive } from "archiver";
21
import AsyncLock from "async-lock";
32
import Dockerode, { ContainerInspectInfo } from "dockerode";
4-
import { promises as fs } from "fs";
53
import { Readable } from "stream";
64
import { containerLog, log } from "../common";
75
import { ContainerRuntimeClient, getContainerRuntimeClient } from "../container-runtime";
@@ -21,6 +19,7 @@ import { BoundPorts } from "../utils/bound-ports";
2119
import { LABEL_TESTCONTAINERS_SESSION_ID } from "../utils/labels";
2220
import { mapInspectResult } from "../utils/map-inspect-result";
2321
import { PortWithOptionalBinding } from "../utils/port";
22+
import { createTarArchive } from "../utils/tar-archive";
2423
import { waitForContainer } from "../wait-strategies/wait-for-container";
2524
import { WaitStrategy } from "../wait-strategies/wait-strategy";
2625
import { inspectContainerUntilPortsExposed } from "./inspect-container-util-ports-exposed";
@@ -192,35 +191,23 @@ export class StartedGenericContainer implements StartedTestContainer {
192191
public async copyFilesToContainer(filesToCopy: FileToCopy[]): Promise<void> {
193192
log.debug(`Copying files to container...`, { containerId: this.container.id });
194193
const client = await getContainerRuntimeClient();
195-
const tar = new TarArchive();
196-
const filesToCopyWithStats = await Promise.all(
197-
filesToCopy.map(async (fileToCopy) => ({
198-
...fileToCopy,
199-
stats: await fs.stat(fileToCopy.source),
200-
}))
201-
);
202-
filesToCopyWithStats.forEach(({ source, target, mode, stats }) => tar.file(source, { name: target, mode, stats }));
203-
tar.finalize();
194+
const tar = await createTarArchive({ filesToCopy });
204195
await client.container.putArchive(this.container, tar, "/");
205196
log.debug(`Copied files to container`, { containerId: this.container.id });
206197
}
207198

208199
public async copyDirectoriesToContainer(directoriesToCopy: DirectoryToCopy[]): Promise<void> {
209200
log.debug(`Copying directories to container...`, { containerId: this.container.id });
210201
const client = await getContainerRuntimeClient();
211-
const tar = new TarArchive();
212-
directoriesToCopy.forEach(({ source, target }) => tar.directory(source, target));
213-
tar.finalize();
202+
const tar = await createTarArchive({ directoriesToCopy });
214203
await client.container.putArchive(this.container, tar, "/");
215204
log.debug(`Copied directories to container`, { containerId: this.container.id });
216205
}
217206

218207
public async copyContentToContainer(contentsToCopy: ContentToCopy[]): Promise<void> {
219208
log.debug(`Copying content to container...`, { containerId: this.container.id });
220209
const client = await getContainerRuntimeClient();
221-
const tar = new TarArchive();
222-
contentsToCopy.forEach(({ content, target, mode }) => tar.append(content, { name: target, mode: mode }));
223-
tar.finalize();
210+
const tar = await createTarArchive({ contentsToCopy });
224211
await client.container.putArchive(this.container, tar, "/");
225212
log.debug(`Copied content to container`, { containerId: this.container.id });
226213
}

packages/testcontainers/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export { CommitOptions, Content, CopyToContainerOptions, ExecOptions, ExecResult
2323
export { BoundPorts } from "./utils/bound-ports";
2424
export { LABEL_TESTCONTAINERS_SESSION_ID } from "./utils/labels";
2525
export { PortWithBinding, PortWithOptionalBinding, getContainerPort, hasHostBinding } from "./utils/port";
26-
export { PortGenerator, RandomPortGenerator } from "./utils/port-generator";
26+
export { PortGenerator, RandomPortGenerator, getRandomPort } from "./utils/port-generator";
2727
export { ImagePullPolicy, PullPolicy } from "./utils/pull-policy";
2828
export { HttpWaitStrategyOptions } from "./wait-strategies/http-wait-strategy";
2929
export { StartupCheckStrategy, StartupStatus } from "./wait-strategies/startup-check-strategy";

0 commit comments

Comments
 (0)