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 {
|
|
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 =
|
|
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
|
|
34
|
-
|
|
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,
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
99
|
-
return {
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
113
|
-
return {
|
|
114
|
-
|
|
115
|
-
|
|
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.
|
|
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
|
|
27
|
-
const tarStream = tar_fs_1.default.pack(options.context, {
|
|
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.
|
|
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.
|
|
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.
|
|
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",
|
package/dist/docker-ignore.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const findDockerIgnoreFiles: (context: string, dockerfileName: string) => Promise<Set<string>>;
|
package/dist/docker-ignore.js
DELETED
|
@@ -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
|
-
}));
|