testcontainers 8.3.0 → 8.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -301,10 +301,10 @@ expect((await container.exec(["nslookup", "foo"])).exitCode).toBe(0);
301
301
  expect((await container.exec(["nslookup", "bar"])).exitCode).toBe(0);
302
302
  ```
303
303
 
304
- Specifying a pull policy. Note that if omitted will use the `DefaultPullPolicy` which will use a locally cached image
305
- if one already exists, this is usually the preferred option. In cases where there is a local image for a given tag
306
- but the remote image with the same tag may have changed (for example when using the `latest` tag), you can tell
307
- testcontainers to pull the image again by specifying an `AlwaysPullPolicy`:
304
+ Specifying a pull policy. Note that if omitted will use the `DefaultPullPolicy` which will use a locally cached image
305
+ if one already exists, this is usually the preferred option. In cases where there is a local image for a given tag
306
+ but the remote image with the same tag may have changed (for example when using the `latest` tag), you can tell
307
+ testcontainers to pull the image again by specifying an `AlwaysPullPolicy`:
308
308
 
309
309
  ```javascript
310
310
  const { GenericContainer, AlwaysPullPolicy } = require("testcontainers");
@@ -326,7 +326,7 @@ const container = await GenericContainer.fromDockerfile(buildContext)
326
326
 
327
327
  Specifying a default log driver. You can override the logging driver used by Docker to be the default one (json-file).
328
328
  This might be necessary when the driver of your docker host does not support reading logs
329
- and you want to use the `Wait.forLogMessage` wait strategy. This is the same as
329
+ and you want to use the `Wait.forLogMessage` wait strategy. This is the same as
330
330
  [--log-driver json-file on docker run](https://docs.docker.com/config/containers/logging/configure/#configure-the-logging-driver-for-a-container).
331
331
 
332
332
  ```javascript
@@ -376,8 +376,8 @@ const container = await new GenericContainer("postgres")
376
376
  .withExposedPorts(5432)
377
377
  .start();
378
378
 
379
- await container.stop({
380
- timeout: 10000
379
+ await container.stop({
380
+ timeout: 10000
381
381
  });
382
382
  ```
383
383
 
@@ -391,7 +391,7 @@ const container = await new GenericContainer("postgres")
391
391
  .withExposedPorts(5432)
392
392
  .start();
393
393
 
394
- await container.stop({
394
+ await container.stop({
395
395
  removeVolumes: false
396
396
  });
397
397
  ```
@@ -433,6 +433,19 @@ const { output } = await container.exec(["curl", `http://host.testcontainers.int
433
433
  assert(output === "hello world");
434
434
  ```
435
435
 
436
+ Specifying an exact host port to bind to (not recommended, Testcontainers will automatically find an available port):
437
+
438
+ ```javascript
439
+ const { GenericContainer } = require("testcontainers");
440
+
441
+ const container = await new GenericContainer("alpine")
442
+ .withExposedPorts({
443
+ container: 8000,
444
+ host: 8080
445
+ })
446
+ .start();
447
+ ```
448
+
436
449
  ## Docker Compose
437
450
 
438
451
  Testcontainers supports docker-compose. For example for the following `docker-compose.yml`:
@@ -568,7 +581,7 @@ const environment = await new DockerComposeEnvironment(composeFilePath, composeF
568
581
  .up();
569
582
  ```
570
583
 
571
- Specify not to re-create containers that are already running:
584
+ Specify not to re-create containers that are already running:
572
585
 
573
586
  ```javascript
574
587
  const { DockerComposeEnvironment } = require("testcontainers");
@@ -598,7 +611,7 @@ const environment = await new DockerComposeEnvironment(composeFilePath, composeF
598
611
 
599
612
  ## Wait Strategies
600
613
 
601
- Ordinarily Testcontainers will wait for up to 60 seconds for the container's mapped network ports to start listening.
614
+ Ordinarily Testcontainers will wait for up to 60 seconds for the container's mapped network ports to start listening.
602
615
  If the default 60s timeout is not sufficient, it can be altered with the `withStartupTimeout()` method:
603
616
 
604
617
  ```javascript
@@ -652,7 +665,7 @@ credential helpers, credential stores, or raw auth as necessary and in that orde
652
665
 
653
666
  ## Auxiliary Containers
654
667
 
655
- Testcontainers may need to create auxiliary containers to provide its functionality.
668
+ Testcontainers may need to create auxiliary containers to provide its functionality.
656
669
 
657
670
  To avoid Docker pull limits, you can host your own images and use them by setting the appropriate environment variables:
658
671
 
@@ -663,21 +676,21 @@ To avoid Docker pull limits, you can host your own images and use them by settin
663
676
 
664
677
  ### ryuk
665
678
 
666
- Testcontainers will start ryuk whenever a container, docker-compose environment or network is started.
679
+ Testcontainers will start ryuk whenever a container, docker-compose environment or network is started.
667
680
 
668
- Once started, this container keeps track of containers/images/networks/volumes created by testcontainers and will
669
- automatically clean them up 10s after connectivity with testcontainers is lost. This is useful for example if a test
681
+ Once started, this container keeps track of containers/images/networks/volumes created by testcontainers and will
682
+ automatically clean them up 10s after connectivity with testcontainers is lost. This is useful for example if a test
670
683
  starts a container and then terminates unexpectedly, as it will be automatically removed.
671
684
 
672
- ryuk by default does not run privileged, if necessary this can be overridden by setting the environment variable
673
- `TESTCONTAINERS_RYUK_PRIVILEGED` to `true`. If necessary, ryuk can be disabled enirely by setting the environment
685
+ ryuk by default does not run privileged, if necessary this can be overridden by setting the environment variable
686
+ `TESTCONTAINERS_RYUK_PRIVILEGED` to `true`. If necessary, ryuk can be disabled enirely by setting the environment
674
687
  variable `TESTCONTAINERS_RYUK_DISABLED` to `true`.
675
688
 
676
689
  ### SSHd
677
690
 
678
- Testcontainers will start SSHd when using the expose host port functionality.
691
+ Testcontainers will start SSHd when using the expose host port functionality.
679
692
 
680
- Once started, any container that is created will have a host mapping of `host.testcontainers.internal` that points to
693
+ Once started, any container that is created will have a host mapping of `host.testcontainers.internal` that points to
681
694
  the SSHd container, as well as being connected to its network.
682
695
 
683
696
  When we then expose a host port, we remote port forward our local port to the SSHd container, which the other
@@ -687,18 +700,17 @@ containers will then be able to access at `host.testcontainers.internal:<exposed
687
700
 
688
701
  1. **Insufficient Docker memory**
689
702
 
690
- By default, Docker sets CPU and memory limits, with a default memory limit
691
- of 2GB. If exceeded, you will be unable to pull/run Docker images.
703
+ By default, Docker sets CPU and memory limits, with a default memory limit
704
+ of 2GB. If exceeded, you will be unable to pull/run Docker images.
692
705
  To see how much memory Docker has used, you can run ```docker system info```
693
706
 
694
707
  - To remove existing containers and images to clear some space you can run ```docker system prune```
695
708
  - Alternatively you can increase the memory limit via Docker's settings under the Advanced pane.
696
709
 
697
- 2. **Insufficient test timeouts**
710
+ 2. **Insufficient test timeouts**
698
711
 
699
- It can take a few seconds up to a few minutes to pull and run certain Docker images,
712
+ It can take a few seconds up to a few minutes to pull and run certain Docker images,
700
713
  depending on file sizes and network constraints. It's unlikely that the default
701
714
  timeouts set by test frameworks are sufficient.
702
715
 
703
716
  - Increase the test timeout via the methods provided by the testing framework.
704
-
@@ -1,10 +1,10 @@
1
- import { Port } from "./port";
1
+ import { Port, PortWithOptionalBinding } from "./port";
2
2
  import { InspectResult } from "./docker/functions/container/inspect-container";
3
3
  export declare class BoundPorts {
4
4
  private readonly ports;
5
5
  getBinding(port: Port): Port;
6
6
  setBinding(key: Port, value: Port): void;
7
7
  iterator(): Iterable<[Port, Port]>;
8
- filter(ports: Port[]): BoundPorts;
8
+ filter(ports: PortWithOptionalBinding[]): BoundPorts;
9
9
  static fromInspectResult(inspectResult: InspectResult): BoundPorts;
10
10
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BoundPorts = void 0;
4
+ const port_1 = require("./port");
4
5
  class BoundPorts {
5
6
  constructor() {
6
7
  this.ports = new Map();
@@ -20,8 +21,9 @@ class BoundPorts {
20
21
  }
21
22
  filter(ports) {
22
23
  const boundPorts = new BoundPorts();
24
+ const containerPorts = ports.map((port) => port_1.getContainerPort(port));
23
25
  for (const [internalPort, hostPort] of this.iterator()) {
24
- if (ports.includes(internalPort)) {
26
+ if (containerPorts.includes(internalPort)) {
25
27
  boundPorts.setBinding(internalPort, hostPort);
26
28
  }
27
29
  }
@@ -1,6 +1,6 @@
1
1
  import { DockerImageName } from "../../../docker-image-name";
2
2
  import Dockerode from "dockerode";
3
- import { Port } from "../../../port";
3
+ import { PortWithOptionalBinding } from "../../../port";
4
4
  import { BindMount, Command, ContainerName, Env, ExtraHost, HealthCheck, NetworkMode, TmpFs } from "../../types";
5
5
  export declare type CreateContainerOptions = {
6
6
  imageName: DockerImageName;
@@ -8,7 +8,7 @@ export declare type CreateContainerOptions = {
8
8
  cmd: Command[];
9
9
  bindMounts: BindMount[];
10
10
  tmpFs: TmpFs;
11
- exposedPorts: Port[];
11
+ exposedPorts: PortWithOptionalBinding[];
12
12
  name?: ContainerName;
13
13
  networkMode?: NetworkMode;
14
14
  healthCheck?: HealthCheck;
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.createContainer = void 0;
13
13
  const logger_1 = require("../../../logger");
14
14
  const docker_client_1 = require("../../docker-client");
15
+ const port_1 = require("../../../port");
15
16
  const create_labels_1 = require("../create-labels");
16
17
  const createContainer = (options) => __awaiter(void 0, void 0, void 0, function* () {
17
18
  try {
@@ -50,7 +51,7 @@ const getEnv = (env) => Object.entries(env).reduce((dockerodeEnvironment, [key,
50
51
  const getExposedPorts = (exposedPorts) => {
51
52
  const dockerodeExposedPorts = {};
52
53
  for (const exposedPort of exposedPorts) {
53
- dockerodeExposedPorts[exposedPort.toString()] = {};
54
+ dockerodeExposedPorts[port_1.getContainerPort(exposedPort).toString()] = {};
54
55
  }
55
56
  return dockerodeExposedPorts;
56
57
  };
@@ -60,7 +61,12 @@ const getExtraHosts = (extraHosts) => {
60
61
  const getPortBindings = (exposedPorts) => {
61
62
  const dockerodePortBindings = {};
62
63
  for (const exposedPort of exposedPorts) {
63
- dockerodePortBindings[exposedPort] = [{ HostPort: "0" }];
64
+ if (port_1.hasHostBinding(exposedPort)) {
65
+ dockerodePortBindings[exposedPort.container] = [{ HostPort: exposedPort.host.toString() }];
66
+ }
67
+ else {
68
+ dockerodePortBindings[exposedPort] = [{ HostPort: "0" }];
69
+ }
64
70
  }
65
71
  return dockerodePortBindings;
66
72
  };
@@ -18,10 +18,12 @@ const listImages = () => __awaiter(void 0, void 0, void 0, function* () {
18
18
  const { dockerode } = yield docker_client_1.dockerClient;
19
19
  const images = yield dockerode.listImages();
20
20
  return images.reduce((dockerImageNames, image) => {
21
+ var _a;
21
22
  if (isDanglingImage(image)) {
22
23
  return dockerImageNames;
23
24
  }
24
- const dockerImageNamesForImage = image.RepoTags.map((imageRepoTag) => docker_image_name_1.DockerImageName.fromString(imageRepoTag));
25
+ const repoTags = (_a = image.RepoTags) !== null && _a !== void 0 ? _a : [];
26
+ const dockerImageNamesForImage = repoTags.map((imageRepoTag) => docker_image_name_1.DockerImageName.fromString(imageRepoTag));
25
27
  return [...dockerImageNames, ...dockerImageNamesForImage];
26
28
  }, []);
27
29
  }
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import archiver from "archiver";
3
3
  import { BoundPorts } from "../bound-ports";
4
- import { Port } from "../port";
4
+ import { PortWithOptionalBinding } from "../port";
5
5
  import { PullPolicy } from "../pull-policy";
6
6
  import { StartedTestContainer, TestContainer } from "../test-container";
7
7
  import { WaitStrategy } from "../wait-strategy";
@@ -16,7 +16,7 @@ export declare class GenericContainer implements TestContainer {
16
16
  protected env: Env;
17
17
  protected networkMode?: NetworkMode;
18
18
  protected networkAliases: string[];
19
- protected ports: Port[];
19
+ protected ports: PortWithOptionalBinding[];
20
20
  protected cmd: Command[];
21
21
  protected bindMounts: BindMount[];
22
22
  protected name?: ContainerName;
@@ -42,8 +42,8 @@ export declare class GenericContainer implements TestContainer {
42
42
  withNetworkMode(networkMode: NetworkMode): this;
43
43
  withNetworkAliases(...networkAliases: string[]): this;
44
44
  withExtraHosts(...extraHosts: ExtraHost[]): this;
45
- withExposedPorts(...ports: Port[]): this;
46
- protected addExposedPorts(...ports: Port[]): this;
45
+ withExposedPorts(...ports: PortWithOptionalBinding[]): this;
46
+ protected addExposedPorts(...ports: PortWithOptionalBinding[]): this;
47
47
  withBindMount(source: Dir, target: Dir, bindMode?: BindMode): this;
48
48
  withHealthCheck(healthCheck: HealthCheck): this;
49
49
  withStartupTimeout(startupTimeout: number): this;
package/dist/port.d.ts CHANGED
@@ -1,2 +1,9 @@
1
1
  export declare type Port = number;
2
2
  export declare type PortString = string;
3
+ export declare type PortWithBinding = {
4
+ container: Port;
5
+ host: Port;
6
+ };
7
+ export declare type PortWithOptionalBinding = Port | PortWithBinding;
8
+ export declare const getContainerPort: (port: PortWithOptionalBinding) => number;
9
+ export declare const hasHostBinding: (port: PortWithOptionalBinding) => port is PortWithBinding;
package/dist/port.js CHANGED
@@ -1,2 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasHostBinding = exports.getContainerPort = void 0;
4
+ const getContainerPort = (port) => typeof port === "number" ? port : port.container;
5
+ exports.getContainerPort = getContainerPort;
6
+ const hasHostBinding = (port) => {
7
+ return typeof port === "object" && port.host !== undefined;
8
+ };
9
+ exports.hasHostBinding = hasHostBinding;
package/dist/reaper.js CHANGED
@@ -16,6 +16,7 @@ const generic_container_1 = require("./generic-container/generic-container");
16
16
  const session_id_1 = require("./docker/session-id");
17
17
  const docker_client_1 = require("./docker/docker-client");
18
18
  const images_1 = require("./images");
19
+ const port_1 = require("./port");
19
20
  class RealReaper {
20
21
  constructor(container, socket) {
21
22
  this.container = container;
@@ -106,7 +107,7 @@ class ReaperInstance {
106
107
  }
107
108
  });
108
109
  return new Promise((resolve) => {
109
- socket.connect(port, host, () => {
110
+ socket.connect(port_1.getContainerPort(port), host, () => {
110
111
  logger_1.log.debug(`Connected to Reaper ${containerId}`);
111
112
  socket.write(`label=org.testcontainers.session-id=${session_id_1.sessionId}\r\n`);
112
113
  const reaper = new RealReaper(startedContainer, socket);
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import { Port } from "./port";
2
+ import { Port, PortWithOptionalBinding } from "./port";
3
3
  import { PullPolicy } from "./pull-policy";
4
4
  import { WaitStrategy } from "./wait-strategy";
5
5
  import { Readable } from "stream";
@@ -9,7 +9,7 @@ export interface TestContainer {
9
9
  withEnv(key: EnvKey, value: EnvValue): this;
10
10
  withCmd(cmd: Command[]): this;
11
11
  withTmpFs(tmpFs: TmpFs): this;
12
- withExposedPorts(...ports: Port[]): this;
12
+ withExposedPorts(...ports: PortWithOptionalBinding[]): this;
13
13
  withBindMount(source: Dir, target: Dir, bindMode: BindMode): this;
14
14
  withWaitStrategy(waitStrategy: WaitStrategy): this;
15
15
  withStartupTimeout(startupTimeout: number): this;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "testcontainers",
3
3
  "author": "Cristian Greco",
4
- "version": "8.3.0",
4
+ "version": "8.4.0",
5
5
  "main": "dist/index",
6
6
  "types": "dist/index",
7
7
  "keywords": [
@@ -33,11 +33,11 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@types/archiver": "^5.1.1",
36
- "@types/dockerode": "^3.2.5",
36
+ "@types/dockerode": "^3.2.7",
37
37
  "archiver": "^5.3.0",
38
38
  "byline": "^5.0.0",
39
- "debug": "^4.3.2",
40
- "docker-compose": "^0.23.13",
39
+ "debug": "^4.3.3",
40
+ "docker-compose": "^0.23.17",
41
41
  "dockerode": "^3.3.1",
42
42
  "get-port": "^5.1.1",
43
43
  "glob": "^7.2.0",
@@ -51,10 +51,10 @@
51
51
  "@types/byline": "^4.2.33",
52
52
  "@types/debug": "4.1.7",
53
53
  "@types/glob": "^7.1.4",
54
- "@types/jest": "^27.0.2",
54
+ "@types/jest": "^27.0.3",
55
55
  "@types/node": "^16.9.6",
56
- "@types/node-fetch": "^2.5.11",
57
- "@types/pg": "^8.6.1",
56
+ "@types/node-fetch": "^2.5.12",
57
+ "@types/pg": "^8.6.4",
58
58
  "@types/properties-reader": "^2.1.1",
59
59
  "@types/tar-fs": "^2.0.1",
60
60
  "@typescript-eslint/eslint-plugin": "^4.31.2",
@@ -63,17 +63,17 @@
63
63
  "cross-env": "^7.0.3",
64
64
  "eslint": "^7.32.0",
65
65
  "eslint-config-prettier": "^8.3.0",
66
- "husky": "^7.0.2",
67
- "jest": "^27.2.1",
66
+ "husky": "^7.0.4",
67
+ "jest": "^27.2.5",
68
68
  "kafkajs": "^1.15.0",
69
- "lint-staged": "^11.1.2",
70
- "mysql2": "^2.3.0",
71
- "neo4j-driver": "^4.3.3",
72
- "node-fetch": "^2.6.1",
73
- "pg": "^8.7.1",
69
+ "lint-staged": "^11.1.4",
70
+ "mysql2": "^2.3.3",
71
+ "neo4j-driver": "^4.3.6",
72
+ "node-fetch": "^2.6.7",
73
+ "pg": "^8.7.3",
74
74
  "prettier": "^2.4.1",
75
75
  "rimraf": "^3.0.2",
76
- "ts-jest": "^27.0.5",
76
+ "ts-jest": "^27.0.7",
77
77
  "ts-node": "^10.2.1",
78
78
  "typescript": "^4.3.5",
79
79
  "wait-for-expect": "^3.0.2"