testcontainers 8.5.0 → 8.6.1-beta.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
@@ -24,9 +24,9 @@ The following environment variables are supported:
24
24
  - `DOCKER_HOST=tcp://docker:2375` Sets the URL of the docker daemon
25
25
  - `DOCKER_TLS_VERIFY=1` When set to `1`, enables TLS communication with the docker
26
26
  daemon
27
- - `DOCKER_CONFIG=/some/path` Configures the path to the `config.json`. Defaults to `~/.docker`
28
27
  - `DOCKER_CERT_PATH=/some/path` Configures the path to the `ca.pem`, `cert.pem`, and `key.pem` files used for TLS
29
28
  verification. Defaults to `~/.docker`
29
+ - `DOCKER_CONFIG=/some/path` Configures the path to the `config.json`. Defaults to `~/.docker`
30
30
  - `TESTCONTAINERS_HOST_OVERRIDE=docker.svc.local` Docker's host on which ports are exposed
31
31
  - `TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock` Path to Docker's socket. Used by [ryuk](#ryuk) and
32
32
  other [auxiliary containers](#auxiliary-containers) that need to perform Docker actions
@@ -77,7 +77,12 @@ describe("GenericContainer", () => {
77
77
  Using TypeScript:
78
78
 
79
79
  ```typescript
80
- import { TestContainer, StartedTestContainer, StoppedTestContainer, GenericContainer } from "testcontainers";
80
+ import {
81
+ TestContainer,
82
+ StartedTestContainer,
83
+ StoppedTestContainer,
84
+ GenericContainer
85
+ } from "testcontainers";
81
86
 
82
87
  const container: TestContainer = new GenericContainer("alpine");
83
88
  const startedContainer: StartedTestContainer = await container.start();
@@ -129,7 +134,7 @@ const container = await new GenericContainer("alpine")
129
134
  .start();
130
135
  ```
131
136
 
132
- Enabling container reuse, note that two containers are considered equal if their options (exposed ports, commands, mounts, etc) match:
137
+ Enabling container reuse, note that two containers are considered equal if their options (exposed ports, commands, mounts, etc) match. This also works across multiple processes:
133
138
 
134
139
  ```javascript
135
140
  const { GenericContainer } = require("testcontainers");
@@ -17,7 +17,7 @@ const dockerode_1 = __importDefault(require("dockerode"));
17
17
  const path_1 = __importDefault(require("path"));
18
18
  const logger_1 = require("../logger");
19
19
  const url_1 = require("url");
20
- const fs_1 = __importDefault(require("fs"));
20
+ const fs_1 = require("fs");
21
21
  const run_in_container_1 = require("./functions/run-in-container");
22
22
  const log_system_diagnostics_1 = require("../log-system-diagnostics");
23
23
  require("../testcontainers-properties-file");
@@ -30,11 +30,10 @@ const getDockerClient = () => __awaiter(void 0, void 0, void 0, function* () {
30
30
  for (const strategy of strategies) {
31
31
  if (strategy.isApplicable()) {
32
32
  logger_1.log.debug(`Found applicable Docker client strategy: ${strategy.getName()}`);
33
- const dockerConfig = strategy.getDockerConfig();
34
- const dockerode = new dockerode_1.default(createDockerodeOptions(dockerConfig));
35
- logger_1.log.debug(`Testing Docker client strategy URI: ${dockerConfig.uri}`);
33
+ const { uri, dockerode } = yield strategy.initialise();
34
+ logger_1.log.debug(`Testing Docker client strategy URI: ${uri}`);
36
35
  if (yield isDockerDaemonReachable(dockerode)) {
37
- const host = yield resolveHost(dockerode, dockerConfig.uri);
36
+ const host = yield resolveHost(dockerode, uri);
38
37
  logger_1.log.info(`Using Docker client strategy: ${strategy.getName()}, Docker host: ${host}`);
39
38
  log_system_diagnostics_1.logSystemDiagnostics();
40
39
  return { host, dockerode };
@@ -46,23 +45,6 @@ const getDockerClient = () => __awaiter(void 0, void 0, void 0, function* () {
46
45
  }
47
46
  throw new Error("No Docker client strategy found");
48
47
  });
49
- const createDockerodeOptions = (dockerConfig) => {
50
- const dockerOptions = {};
51
- if (dockerConfig.socketPath) {
52
- dockerOptions.socketPath = dockerConfig.socketPath;
53
- }
54
- else {
55
- const { hostname, port } = new url_1.URL(dockerConfig.uri);
56
- dockerOptions.host = hostname;
57
- dockerOptions.port = port;
58
- }
59
- if (dockerConfig.ssl) {
60
- dockerOptions.ca = dockerConfig.ssl.ca;
61
- dockerOptions.cert = dockerConfig.ssl.cert;
62
- dockerOptions.key = dockerConfig.ssl.key;
63
- }
64
- return dockerOptions;
65
- };
66
48
  const isDockerDaemonReachable = (dockerode) => __awaiter(void 0, void 0, void 0, function* () {
67
49
  try {
68
50
  const response = yield dockerode.ping();
@@ -74,18 +56,25 @@ const isDockerDaemonReachable = (dockerode) => __awaiter(void 0, void 0, void 0,
74
56
  }
75
57
  });
76
58
  class ConfigurationStrategy {
77
- getDockerConfig() {
78
- const { DOCKER_HOST, DOCKER_TLS_VERIFY, DOCKER_CERT_PATH } = process.env;
79
- return {
80
- uri: DOCKER_HOST,
81
- ssl: DOCKER_TLS_VERIFY === "1" && DOCKER_CERT_PATH !== undefined
82
- ? {
83
- ca: path_1.default.resolve(DOCKER_CERT_PATH, "ca.pem"),
84
- cert: path_1.default.resolve(DOCKER_CERT_PATH, "cert.pem"),
85
- key: path_1.default.resolve(DOCKER_CERT_PATH, "key.pem"),
86
- }
87
- : undefined,
88
- };
59
+ initialise() {
60
+ return __awaiter(this, void 0, void 0, function* () {
61
+ const { DOCKER_HOST, DOCKER_TLS_VERIFY, DOCKER_CERT_PATH } = process.env;
62
+ const dockerOptions = {};
63
+ const url = new url_1.URL(DOCKER_HOST);
64
+ const dockerHost = url.host === "" ? `tcp://${DOCKER_HOST}` : DOCKER_HOST;
65
+ const { hostname, port } = new url_1.URL(dockerHost);
66
+ dockerOptions.host = hostname;
67
+ dockerOptions.port = port;
68
+ if (DOCKER_TLS_VERIFY === "1" && DOCKER_CERT_PATH !== undefined) {
69
+ dockerOptions.ca = yield fs_1.promises.readFile(path_1.default.resolve(DOCKER_CERT_PATH, "ca.pem"));
70
+ dockerOptions.cert = yield fs_1.promises.readFile(path_1.default.resolve(DOCKER_CERT_PATH, "cert.pem"));
71
+ dockerOptions.key = yield fs_1.promises.readFile(path_1.default.resolve(DOCKER_CERT_PATH, "key.pem"));
72
+ }
73
+ return {
74
+ uri: dockerHost,
75
+ dockerode: new dockerode_1.default(dockerOptions),
76
+ };
77
+ });
89
78
  }
90
79
  isApplicable() {
91
80
  return process.env.DOCKER_HOST !== undefined;
@@ -95,11 +84,13 @@ class ConfigurationStrategy {
95
84
  }
96
85
  }
97
86
  class UnixSocketStrategy {
98
- getDockerConfig() {
99
- return {
100
- uri: "unix:///var/run/docker.sock",
101
- socketPath: "/var/run/docker.sock",
102
- };
87
+ initialise() {
88
+ return __awaiter(this, void 0, void 0, function* () {
89
+ return {
90
+ uri: "unix:///var/run/docker.sock",
91
+ dockerode: new dockerode_1.default({ socketPath: "/var/run/docker.sock" }),
92
+ };
93
+ });
103
94
  }
104
95
  isApplicable() {
105
96
  return process.platform === "linux" || process.platform === "darwin";
@@ -109,11 +100,13 @@ class UnixSocketStrategy {
109
100
  }
110
101
  }
111
102
  class NpipeSocketStrategy {
112
- getDockerConfig() {
113
- return {
114
- uri: "npipe:////./pipe/docker_engine",
115
- socketPath: "//./pipe/docker_engine",
116
- };
103
+ initialise() {
104
+ return __awaiter(this, void 0, void 0, function* () {
105
+ return {
106
+ uri: "npipe:////./pipe/docker_engine",
107
+ dockerode: new dockerode_1.default({ socketPath: "//./pipe/docker_engine" }),
108
+ };
109
+ });
117
110
  }
118
111
  isApplicable() {
119
112
  return process.platform === "win32";
@@ -156,5 +149,5 @@ const findGateway = (dockerode) => __awaiter(void 0, void 0, void 0, function* (
156
149
  return (_c = (_b = (_a = inspectResult === null || inspectResult === void 0 ? void 0 : inspectResult.IPAM) === null || _a === void 0 ? void 0 : _a.Config) === null || _b === void 0 ? void 0 : _b.find((config) => config.Gateway !== undefined)) === null || _c === void 0 ? void 0 : _c.Gateway;
157
150
  });
158
151
  const findDefaultGateway = (dockerode) => __awaiter(void 0, void 0, void 0, function* () { return run_in_container_1.runInContainer(dockerode, "alpine:3.5", ["sh", "-c", "ip route|awk '/default/ { print $3 }'"]); });
159
- const isInContainer = () => fs_1.default.existsSync("/.dockerenv");
152
+ const isInContainer = () => fs_1.existsSync("/.dockerenv");
160
153
  exports.dockerClient = getDockerClient();
@@ -14,17 +14,28 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.buildImage = void 0;
16
16
  const logger_1 = require("../../../logger");
17
- const docker_ignore_1 = require("../../../docker-ignore");
18
17
  const tar_fs_1 = __importDefault(require("tar-fs"));
19
- const slash_1 = __importDefault(require("slash"));
20
18
  const byline_1 = __importDefault(require("byline"));
21
19
  const docker_client_1 = require("../../docker-client");
22
20
  const create_labels_1 = require("../create-labels");
21
+ const path_1 = __importDefault(require("path"));
22
+ const fs_1 = require("fs");
23
+ const dockerignore_1 = __importDefault(require("@balena/dockerignore"));
23
24
  const buildImage = (options) => __awaiter(void 0, void 0, void 0, function* () {
24
25
  try {
25
26
  logger_1.log.info(`Building image '${options.imageName.toString()}' with context '${options.context}'`);
26
- const dockerIgnoreFiles = yield docker_ignore_1.findDockerIgnoreFiles(options.context, options.dockerfileName);
27
- const tarStream = tar_fs_1.default.pack(options.context, { ignore: (name) => dockerIgnoreFiles.has(slash_1.default(name)) });
27
+ const isDockerIgnored = yield createIsDockerIgnoredFunction(options.context);
28
+ const tarStream = tar_fs_1.default.pack(options.context, {
29
+ ignore: (aPath) => {
30
+ const relativePath = path_1.default.relative(options.context, aPath);
31
+ if (relativePath === options.dockerfileName) {
32
+ return false;
33
+ }
34
+ else {
35
+ return isDockerIgnored(relativePath);
36
+ }
37
+ },
38
+ });
28
39
  const { dockerode } = yield docker_client_1.dockerClient;
29
40
  return new Promise((resolve) => dockerode
30
41
  .buildImage(tarStream, {
@@ -48,3 +59,14 @@ const buildImage = (options) => __awaiter(void 0, void 0, void 0, function* () {
48
59
  }
49
60
  });
50
61
  exports.buildImage = buildImage;
62
+ const createIsDockerIgnoredFunction = (context) => __awaiter(void 0, void 0, void 0, function* () {
63
+ const dockerIgnoreFilePath = path_1.default.join(context, ".dockerignore");
64
+ if (!fs_1.existsSync(dockerIgnoreFilePath)) {
65
+ return () => false;
66
+ }
67
+ const instance = dockerignore_1.default({ ignorecase: false });
68
+ const dockerIgnorePatterns = yield fs_1.promises.readFile(dockerIgnoreFilePath, { encoding: "utf-8" });
69
+ instance.add(dockerIgnorePatterns);
70
+ const filter = instance.createFilter();
71
+ return (aPath) => !filter(aPath);
72
+ });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "testcontainers",
3
3
  "author": "Cristian Greco",
4
- "version": "8.5.0",
4
+ "version": "8.6.1-beta.0",
5
5
  "main": "dist/index",
6
6
  "types": "dist/index",
7
7
  "keywords": [
@@ -32,17 +32,16 @@
32
32
  "updateDeps": "npx npm-check-updates -u"
33
33
  },
34
34
  "dependencies": {
35
+ "@balena/dockerignore": "^1.0.2",
35
36
  "@types/archiver": "^5.1.1",
36
37
  "@types/dockerode": "^3.2.7",
37
38
  "archiver": "^5.3.0",
38
39
  "byline": "^5.0.0",
39
- "debug": "^4.3.3",
40
+ "debug": "^4.3.4",
40
41
  "docker-compose": "^0.23.17",
41
42
  "dockerode": "^3.3.1",
42
43
  "get-port": "^5.1.1",
43
- "glob": "^7.2.0",
44
44
  "properties-reader": "^2.2.0",
45
- "slash": "^3.0.0",
46
45
  "ssh-remote-port-forward": "^1.0.4",
47
46
  "tar-fs": "^2.1.1"
48
47
  },
@@ -50,11 +49,10 @@
50
49
  "@elastic/elasticsearch": "^7.14.1",
51
50
  "@types/byline": "^4.2.33",
52
51
  "@types/debug": "4.1.7",
53
- "@types/glob": "^7.1.4",
54
52
  "@types/jest": "^27.0.3",
55
53
  "@types/node": "^16.9.6",
56
54
  "@types/node-fetch": "^2.5.12",
57
- "@types/pg": "^8.6.4",
55
+ "@types/pg": "^8.6.5",
58
56
  "@types/properties-reader": "^2.1.1",
59
57
  "@types/tar-fs": "^2.0.1",
60
58
  "@typescript-eslint/eslint-plugin": "^4.31.2",
@@ -1 +0,0 @@
1
- export declare const findDockerIgnoreFiles: (context: string, dockerfileName: string) => Promise<Set<string>>;
@@ -1,41 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.findDockerIgnoreFiles = void 0;
16
- const fs_1 = require("fs");
17
- const os_1 = __importDefault(require("os"));
18
- const glob_1 = __importDefault(require("glob"));
19
- const path_1 = __importDefault(require("path"));
20
- const findDockerIgnoreFiles = (context, dockerfileName) => __awaiter(void 0, void 0, void 0, function* () {
21
- const dockerIgnoreFilePath = path_1.default.join(context, ".dockerignore");
22
- if (!fs_1.existsSync(dockerIgnoreFilePath)) {
23
- return new Set();
24
- }
25
- const dockerIgnorePatterns = (yield fs_1.promises.readFile(dockerIgnoreFilePath, { encoding: "utf-8" }))
26
- .toString()
27
- .split(os_1.default.EOL)
28
- .filter((dockerIgnorePattern) => dockerIgnorePattern !== dockerfileName)
29
- .map((dockerIgnorePattern) => path_1.default.resolve(context, dockerIgnorePattern));
30
- const dockerIgnoreMatches = (yield Promise.all(dockerIgnorePatterns.map(findIgnoredFilesForPattern))).reduce((prev, next) => [...prev, ...next]);
31
- return new Set(dockerIgnoreMatches);
32
- });
33
- exports.findDockerIgnoreFiles = findDockerIgnoreFiles;
34
- const findIgnoredFilesForPattern = (dockerIgnorePattern) => new Promise((resolve, reject) => glob_1.default(dockerIgnorePattern, (err, matches) => {
35
- if (err) {
36
- return reject(err);
37
- }
38
- else {
39
- resolve(matches);
40
- }
41
- }));