testcontainers 7.23.2 → 8.1.1
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 +38 -12
- package/dist/bound-ports.d.ts +3 -0
- package/dist/bound-ports.js +14 -0
- package/dist/docker/docker-client.d.ts +9 -0
- package/dist/docker/docker-client.js +149 -0
- package/dist/docker/functions/container/create-container.d.ts +2 -2
- package/dist/docker/functions/container/create-container.js +11 -10
- package/dist/docker/functions/container/exec-container.js +16 -12
- package/dist/docker/functions/container/get-container.d.ts +1 -1
- package/dist/docker/functions/container/get-container.js +14 -2
- package/dist/docker/functions/container/inspect-container.d.ts +3 -4
- package/dist/docker/functions/container/inspect-container.js +10 -8
- package/dist/docker/functions/container/list-containers.js +3 -2
- package/dist/docker/functions/demux-stream.d.ts +1 -1
- package/dist/docker/functions/demux-stream.js +14 -4
- package/dist/docker/functions/get-info.js +3 -2
- package/dist/docker/functions/image/build-image.js +3 -2
- package/dist/docker/functions/image/list-images.js +3 -2
- package/dist/docker/functions/image/pull-image.js +3 -2
- package/dist/docker/functions/network/connect-network.js +3 -2
- package/dist/docker/functions/network/create-network.js +3 -2
- package/dist/docker/functions/network/remove-network.js +3 -2
- package/dist/docker/functions/run-in-container.d.ts +2 -1
- package/dist/docker/functions/run-in-container.js +2 -3
- package/dist/docker/types.d.ts +3 -0
- package/dist/docker-compose/default-docker-compose-options.d.ts +1 -1
- package/dist/docker-compose/docker-compose.d.ts +3 -0
- package/dist/docker-compose/docker-compose.js +10 -0
- package/dist/docker-compose/functions/docker-compose-down.js +1 -1
- package/dist/docker-compose/functions/docker-compose-stop.js +1 -1
- package/dist/docker-compose/functions/docker-compose-up.js +1 -1
- package/dist/docker-compose-environment/docker-compose-environment.d.ts +0 -1
- package/dist/docker-compose-environment/docker-compose-environment.js +6 -14
- package/dist/generic-container/generic-container.d.ts +6 -2
- package/dist/generic-container/generic-container.js +25 -9
- package/dist/log-system-diagnostics.js +2 -21
- package/dist/modules/kafka/kafka-container.d.ts +6 -17
- package/dist/modules/kafka/kafka-container.js +57 -69
- package/dist/port-forwarder.js +2 -2
- package/dist/reaper.d.ts +1 -0
- package/dist/reaper.js +20 -14
- package/dist/test-container.d.ts +2 -1
- package/dist/testcontainers-properties-file.d.ts +1 -0
- package/dist/testcontainers-properties-file.js +31 -0
- package/package.json +4 -2
- package/dist/docker/docker-host.d.ts +0 -1
- package/dist/docker/docker-host.js +0 -8
- package/dist/docker/dockerode.d.ts +0 -2
- package/dist/docker/dockerode.js +0 -10
- package/dist/docker/get-docker-host.d.ts +0 -3
- package/dist/docker/get-docker-host.js +0 -60
package/README.md
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
[](https://github.com/testcontainers/testcontainers-node/actions/workflows/node.js.yml)
|
|
6
6
|
[](https://www.npmjs.com/package/testcontainers)
|
|
7
7
|
[](https://www.npmjs.com/package/testcontainers)
|
|
8
|
+
[](https://foresight.thundra.live/testRuns/57175ecb-d5ac-4129-957a-9dd617fd27d0)
|
|
9
|
+
[](https://foresight.thundra.live/testruns/57175ecb-d5ac-4129-957a-9dd617fd27d0)
|
|
8
10
|
|
|
9
11
|
## Install
|
|
10
12
|
|
|
@@ -16,12 +18,18 @@ npm i -D testcontainers
|
|
|
16
18
|
|
|
17
19
|
The following environment variables are supported:
|
|
18
20
|
|
|
19
|
-
- `DEBUG=testcontainers` See
|
|
20
|
-
- `DEBUG=testcontainers:containers` See container
|
|
21
|
-
- `DEBUG=testcontainers*` See all
|
|
22
|
-
- `DOCKER_HOST=tcp://docker:2375`
|
|
21
|
+
- `DEBUG=testcontainers` See testcontainers logs
|
|
22
|
+
- `DEBUG=testcontainers:containers` See container logs
|
|
23
|
+
- `DEBUG=testcontainers*` See all logs
|
|
24
|
+
- `DOCKER_HOST=tcp://docker:2375` Sets the URL of the docker daemon
|
|
25
|
+
- `DOCKER_TLS_VERIFY=1` When set to anything other than an empty string, enables TLS communication with the docker
|
|
26
|
+
daemon
|
|
27
|
+
- `DOCKER_CERT_PATH=/some/path` Configures the path to the ca.pem, cert.pem, and key.pem files used for TLS
|
|
28
|
+
verification. Defaults to ~/.docker
|
|
23
29
|
- `TESTCONTAINERS_HOST_OVERRIDE=docker.svc.local` Docker's host on which ports are exposed
|
|
24
|
-
- `TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock` Path to Docker's socket. Used by [ryuk](#ryuk) and
|
|
30
|
+
- `TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock` Path to Docker's socket. Used by [ryuk](#ryuk) and
|
|
31
|
+
other [auxiliary containers](#auxiliary-containers) that need to perform Docker actions
|
|
32
|
+
- `TESTCONTAINERS_RYUK_PRIVILEGED=true` Run [ryuk](#ryuk) as a privileged container
|
|
25
33
|
- `TESTCONTAINERS_RYUK_DISABLED=true` Disable [ryuk](#ryuk)
|
|
26
34
|
- `RYUK_CONTAINER_IMAGE=registry.mycompany.com/mirror/ryuk:0.3.0` Custom image for [ryuk](#ryuk)
|
|
27
35
|
- `SSHD_CONTAINER_IMAGE=registry.mycompany.com/mirror/sshd:1.0.0` Custom image for [SSHd](#SSHd)
|
|
@@ -136,7 +144,7 @@ Creating a container with a command:
|
|
|
136
144
|
const { GenericContainer } = require("testcontainers");
|
|
137
145
|
|
|
138
146
|
const container = await new GenericContainer("alpine")
|
|
139
|
-
.withCmd(["
|
|
147
|
+
.withCmd(["sleep", "infinity"])
|
|
140
148
|
.start();
|
|
141
149
|
```
|
|
142
150
|
|
|
@@ -146,7 +154,7 @@ Execute commands inside a running container:
|
|
|
146
154
|
const { GenericContainer } = require("testcontainers");
|
|
147
155
|
|
|
148
156
|
const container = await new GenericContainer("alpine")
|
|
149
|
-
.withCmd(["
|
|
157
|
+
.withCmd(["sleep", "infinity"])
|
|
150
158
|
.start();
|
|
151
159
|
|
|
152
160
|
const { output, exitCode } = await container.exec(["echo", "hello", "world"]);
|
|
@@ -263,12 +271,12 @@ const network = await new Network()
|
|
|
263
271
|
.start();
|
|
264
272
|
|
|
265
273
|
const container = await new GenericContainer("alpine")
|
|
266
|
-
.withCmd(["
|
|
274
|
+
.withCmd(["sleep", "infinity"])
|
|
267
275
|
.withNetworkMode(network.getName())
|
|
268
276
|
.start();
|
|
269
277
|
|
|
270
278
|
const fooContainer = await new GenericContainer("alpine")
|
|
271
|
-
.withCmd(["
|
|
279
|
+
.withCmd(["sleep", "infinity"])
|
|
272
280
|
.withNetworkMode(network.getName())
|
|
273
281
|
.withNetworkAliases("foo", "bar")
|
|
274
282
|
.start();
|
|
@@ -276,6 +284,22 @@ const fooContainer = await new GenericContainer("alpine")
|
|
|
276
284
|
expect((await container.exec(["nslookup", "foo"])).exitCode).toBe(0);
|
|
277
285
|
```
|
|
278
286
|
|
|
287
|
+
Add hostname mappings:
|
|
288
|
+
|
|
289
|
+
```javascript
|
|
290
|
+
const { GenericContainer } = require("testcontainers");
|
|
291
|
+
|
|
292
|
+
const container = await new GenericContainer("alpine")
|
|
293
|
+
.withExtraHosts(
|
|
294
|
+
{ host: "foo", ipAddress: "10.11.12.13" },
|
|
295
|
+
{ host: "bar", ipAddress: "11.12.13.14" }
|
|
296
|
+
)
|
|
297
|
+
.start();
|
|
298
|
+
|
|
299
|
+
expect((await container.exec(["nslookup", "foo"])).exitCode).toBe(0);
|
|
300
|
+
expect((await container.exec(["nslookup", "bar"])).exitCode).toBe(0);
|
|
301
|
+
```
|
|
302
|
+
|
|
279
303
|
Specifying a pull policy. Note that if omitted will use the `DefaultPullPolicy` which will use a locally cached image
|
|
280
304
|
if one already exists, this is usually the preferred option. In cases where there is a local image for a given tag
|
|
281
305
|
but the remote image with the same tag may have changed (for example when using the `latest` tag), you can tell
|
|
@@ -401,7 +425,7 @@ server.listen(8000);
|
|
|
401
425
|
await TestContainers.exposeHostPorts(8000);
|
|
402
426
|
|
|
403
427
|
const container = await new GenericContainer("alpine")
|
|
404
|
-
.withCmd(["
|
|
428
|
+
.withCmd(["sleep", "infinity"])
|
|
405
429
|
.start();
|
|
406
430
|
|
|
407
431
|
const { output } = await container.exec(["curl", `http://host.testcontainers.internal:8000`]);
|
|
@@ -634,8 +658,9 @@ Once started, this container keeps track of containers/images/networks/volumes c
|
|
|
634
658
|
automatically clean them up 10s after connectivity with testcontainers is lost. This is useful for example if a test
|
|
635
659
|
starts a container and then terminates unexpectedly, as it will be automatically removed.
|
|
636
660
|
|
|
637
|
-
ryuk
|
|
638
|
-
disabled by setting the environment
|
|
661
|
+
ryuk by default does not run privileged, if necessary this can be overridden by setting the environment variable
|
|
662
|
+
`TESTCONTAINERS_RYUK_PRIVILEGED` to `true`. If necessary, ryuk can be disabled enirely by setting the environment
|
|
663
|
+
variable `TESTCONTAINERS_RYUK_DISABLED` to `true`.
|
|
639
664
|
|
|
640
665
|
### SSHd
|
|
641
666
|
|
|
@@ -665,3 +690,4 @@ depending on file sizes and network constraints. It's unlikely that the default
|
|
|
665
690
|
timeouts set by test frameworks are sufficient.
|
|
666
691
|
|
|
667
692
|
- Increase the test timeout via the methods provided by the testing framework.
|
|
693
|
+
|
package/dist/bound-ports.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Port } from "./port";
|
|
2
|
+
import { InspectResult } from "./docker/functions/container/inspect-container";
|
|
2
3
|
export declare class BoundPorts {
|
|
3
4
|
private readonly ports;
|
|
4
5
|
getBinding(port: Port): Port;
|
|
5
6
|
setBinding(key: Port, value: Port): void;
|
|
6
7
|
iterator(): Iterable<[Port, Port]>;
|
|
8
|
+
filter(ports: Port[]): BoundPorts;
|
|
9
|
+
static fromInspectResult(inspectResult: InspectResult): BoundPorts;
|
|
7
10
|
}
|
package/dist/bound-ports.js
CHANGED
|
@@ -18,5 +18,19 @@ class BoundPorts {
|
|
|
18
18
|
iterator() {
|
|
19
19
|
return this.ports;
|
|
20
20
|
}
|
|
21
|
+
filter(ports) {
|
|
22
|
+
const boundPorts = new BoundPorts();
|
|
23
|
+
for (const [internalPort, hostPort] of this.iterator()) {
|
|
24
|
+
if (ports.includes(internalPort)) {
|
|
25
|
+
boundPorts.setBinding(internalPort, hostPort);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return boundPorts;
|
|
29
|
+
}
|
|
30
|
+
static fromInspectResult(inspectResult) {
|
|
31
|
+
const boundPorts = new BoundPorts();
|
|
32
|
+
Object.entries(inspectResult.ports).forEach(([internalPort, hostPort]) => boundPorts.setBinding(parseInt(internalPort), hostPort));
|
|
33
|
+
return boundPorts;
|
|
34
|
+
}
|
|
21
35
|
}
|
|
22
36
|
exports.BoundPorts = BoundPorts;
|
|
@@ -0,0 +1,149 @@
|
|
|
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.dockerClient = void 0;
|
|
16
|
+
const dockerode_1 = __importDefault(require("dockerode"));
|
|
17
|
+
const path_1 = __importDefault(require("path"));
|
|
18
|
+
const logger_1 = require("../logger");
|
|
19
|
+
const url_1 = require("url");
|
|
20
|
+
const fs_1 = __importDefault(require("fs"));
|
|
21
|
+
const run_in_container_1 = require("./functions/run-in-container");
|
|
22
|
+
const log_system_diagnostics_1 = require("../log-system-diagnostics");
|
|
23
|
+
require("../testcontainers-properties-file");
|
|
24
|
+
const getDockerClient = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
25
|
+
const strategies = [
|
|
26
|
+
new ConfigurationStrategy(),
|
|
27
|
+
new UnixSocketStrategy(),
|
|
28
|
+
new NpipeSocketStrategy(),
|
|
29
|
+
];
|
|
30
|
+
for (const strategy of strategies) {
|
|
31
|
+
if (strategy.isApplicable()) {
|
|
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}`);
|
|
36
|
+
if (yield isDockerDaemonReachable(dockerode)) {
|
|
37
|
+
const host = yield resolveHost(dockerode, dockerConfig.uri);
|
|
38
|
+
logger_1.log.info(`Using Docker client strategy: ${strategy.getName()}, Docker host: ${host}`);
|
|
39
|
+
log_system_diagnostics_1.logSystemDiagnostics();
|
|
40
|
+
return { host, dockerode };
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
logger_1.log.warn(`Docker client strategy ${strategy.getName()} is not reachable`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
throw new Error("No Docker client strategy found");
|
|
48
|
+
});
|
|
49
|
+
const createDockerodeOptions = (dockerConfig) => {
|
|
50
|
+
const dockerOptions = {};
|
|
51
|
+
if (dockerConfig.socketPath) {
|
|
52
|
+
dockerOptions.socketPath = dockerConfig.socketPath;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
dockerOptions.host = dockerConfig.uri;
|
|
56
|
+
}
|
|
57
|
+
if (dockerConfig.ssl) {
|
|
58
|
+
dockerOptions.ca = dockerConfig.ssl.ca;
|
|
59
|
+
dockerOptions.cert = dockerConfig.ssl.cert;
|
|
60
|
+
dockerOptions.key = dockerConfig.ssl.key;
|
|
61
|
+
}
|
|
62
|
+
return dockerOptions;
|
|
63
|
+
};
|
|
64
|
+
const isDockerDaemonReachable = (dockerode) => __awaiter(void 0, void 0, void 0, function* () { return (yield dockerode.ping()).toString() === "OK"; });
|
|
65
|
+
class ConfigurationStrategy {
|
|
66
|
+
getDockerConfig() {
|
|
67
|
+
const { DOCKER_HOST, DOCKER_TLS_VERIFY, DOCKER_CERT_PATH } = process.env;
|
|
68
|
+
return {
|
|
69
|
+
uri: DOCKER_HOST,
|
|
70
|
+
ssl: DOCKER_TLS_VERIFY === "1" && DOCKER_CERT_PATH !== undefined
|
|
71
|
+
? {
|
|
72
|
+
ca: path_1.default.resolve(DOCKER_CERT_PATH, "ca.pem"),
|
|
73
|
+
cert: path_1.default.resolve(DOCKER_CERT_PATH, "cert.pem"),
|
|
74
|
+
key: path_1.default.resolve(DOCKER_CERT_PATH, "key.pem"),
|
|
75
|
+
}
|
|
76
|
+
: undefined,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
isApplicable() {
|
|
80
|
+
return process.env.DOCKER_HOST !== undefined;
|
|
81
|
+
}
|
|
82
|
+
getName() {
|
|
83
|
+
return "ConfigurationStrategy";
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
class UnixSocketStrategy {
|
|
87
|
+
getDockerConfig() {
|
|
88
|
+
return {
|
|
89
|
+
uri: "unix:///var/run/docker.sock",
|
|
90
|
+
socketPath: "/var/run/docker.sock",
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
isApplicable() {
|
|
94
|
+
return process.platform === "linux" || process.platform === "darwin";
|
|
95
|
+
}
|
|
96
|
+
getName() {
|
|
97
|
+
return "UnixSocketStrategy";
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
class NpipeSocketStrategy {
|
|
101
|
+
getDockerConfig() {
|
|
102
|
+
return {
|
|
103
|
+
uri: "npipe:////./pipe/docker_engine",
|
|
104
|
+
socketPath: "//./pipe/docker_engine",
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
isApplicable() {
|
|
108
|
+
return process.platform === "win32";
|
|
109
|
+
}
|
|
110
|
+
getName() {
|
|
111
|
+
return "NpipeSocketStrategy";
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const resolveHost = (dockerode, uri) => __awaiter(void 0, void 0, void 0, function* () {
|
|
115
|
+
if (process.env.TESTCONTAINERS_HOST_OVERRIDE !== undefined) {
|
|
116
|
+
return process.env.TESTCONTAINERS_HOST_OVERRIDE;
|
|
117
|
+
}
|
|
118
|
+
const { protocol, hostname } = new url_1.URL(uri);
|
|
119
|
+
switch (protocol) {
|
|
120
|
+
case "http:":
|
|
121
|
+
case "https:":
|
|
122
|
+
case "tcp:":
|
|
123
|
+
return hostname;
|
|
124
|
+
case "unix:":
|
|
125
|
+
case "npipe:": {
|
|
126
|
+
if (isInContainer()) {
|
|
127
|
+
const gateway = yield findGateway(dockerode);
|
|
128
|
+
if (gateway !== undefined) {
|
|
129
|
+
return gateway;
|
|
130
|
+
}
|
|
131
|
+
const defaultGateway = yield findDefaultGateway(dockerode);
|
|
132
|
+
if (defaultGateway !== undefined) {
|
|
133
|
+
return defaultGateway;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return "localhost";
|
|
137
|
+
}
|
|
138
|
+
default:
|
|
139
|
+
throw new Error(`Unsupported protocol: ${protocol}`);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
const findGateway = (dockerode) => __awaiter(void 0, void 0, void 0, function* () {
|
|
143
|
+
var _a, _b, _c;
|
|
144
|
+
const inspectResult = yield dockerode.getNetwork("bridge").inspect();
|
|
145
|
+
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;
|
|
146
|
+
});
|
|
147
|
+
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 }'"]); });
|
|
148
|
+
const isInContainer = () => fs_1.default.existsSync("/.dockerenv");
|
|
149
|
+
exports.dockerClient = getDockerClient();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DockerImageName } from "../../../docker-image-name";
|
|
2
|
-
import { BoundPorts } from "../../../bound-ports";
|
|
3
2
|
import Dockerode from "dockerode";
|
|
3
|
+
import { Port } 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
|
-
|
|
11
|
+
exposedPorts: Port[];
|
|
12
12
|
name?: ContainerName;
|
|
13
13
|
networkMode?: NetworkMode;
|
|
14
14
|
healthCheck?: HealthCheck;
|
|
@@ -11,17 +11,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.createContainer = void 0;
|
|
13
13
|
const logger_1 = require("../../../logger");
|
|
14
|
-
const
|
|
14
|
+
const docker_client_1 = require("../../docker-client");
|
|
15
15
|
const create_labels_1 = require("../create-labels");
|
|
16
16
|
const createContainer = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
17
17
|
try {
|
|
18
18
|
logger_1.log.info(`Creating container for image: ${options.imageName}`);
|
|
19
|
-
|
|
19
|
+
const { dockerode } = yield docker_client_1.dockerClient;
|
|
20
|
+
return yield dockerode.createContainer({
|
|
20
21
|
name: options.name,
|
|
21
22
|
User: options.user,
|
|
22
23
|
Image: options.imageName.toString(),
|
|
23
24
|
Env: getEnv(options.env),
|
|
24
|
-
ExposedPorts: getExposedPorts(options.
|
|
25
|
+
ExposedPorts: getExposedPorts(options.exposedPorts),
|
|
25
26
|
Cmd: options.cmd,
|
|
26
27
|
Labels: create_labels_1.createLabels(options.imageName),
|
|
27
28
|
// @ts-ignore
|
|
@@ -31,7 +32,7 @@ const createContainer = (options) => __awaiter(void 0, void 0, void 0, function*
|
|
|
31
32
|
ExtraHosts: getExtraHosts(options.extraHosts),
|
|
32
33
|
AutoRemove: options.autoRemove,
|
|
33
34
|
NetworkMode: options.networkMode,
|
|
34
|
-
PortBindings: getPortBindings(options.
|
|
35
|
+
PortBindings: getPortBindings(options.exposedPorts),
|
|
35
36
|
Binds: getBindMounts(options.bindMounts),
|
|
36
37
|
Tmpfs: options.tmpFs,
|
|
37
38
|
LogConfig: getLogConfig(options.useDefaultLogDriver),
|
|
@@ -46,20 +47,20 @@ const createContainer = (options) => __awaiter(void 0, void 0, void 0, function*
|
|
|
46
47
|
});
|
|
47
48
|
exports.createContainer = createContainer;
|
|
48
49
|
const getEnv = (env) => Object.entries(env).reduce((dockerodeEnvironment, [key, value]) => [...dockerodeEnvironment, `${key}=${value}`], []);
|
|
49
|
-
const getExposedPorts = (
|
|
50
|
+
const getExposedPorts = (exposedPorts) => {
|
|
50
51
|
const dockerodeExposedPorts = {};
|
|
51
|
-
for (const
|
|
52
|
-
dockerodeExposedPorts[
|
|
52
|
+
for (const exposedPort of exposedPorts) {
|
|
53
|
+
dockerodeExposedPorts[exposedPort.toString()] = {};
|
|
53
54
|
}
|
|
54
55
|
return dockerodeExposedPorts;
|
|
55
56
|
};
|
|
56
57
|
const getExtraHosts = (extraHosts) => {
|
|
57
58
|
return extraHosts.map((extraHost) => `${extraHost.host}:${extraHost.ipAddress}`);
|
|
58
59
|
};
|
|
59
|
-
const getPortBindings = (
|
|
60
|
+
const getPortBindings = (exposedPorts) => {
|
|
60
61
|
const dockerodePortBindings = {};
|
|
61
|
-
for (const
|
|
62
|
-
dockerodePortBindings[
|
|
62
|
+
for (const exposedPort of exposedPorts) {
|
|
63
|
+
dockerodePortBindings[exposedPort] = [{ HostPort: "0" }];
|
|
63
64
|
}
|
|
64
65
|
return dockerodePortBindings;
|
|
65
66
|
};
|
|
@@ -19,18 +19,11 @@ const execContainer = (container, command) => __awaiter(void 0, void 0, void 0,
|
|
|
19
19
|
AttachStderr: true,
|
|
20
20
|
});
|
|
21
21
|
const stream = yield startExec(exec);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (!running) {
|
|
28
|
-
clearInterval(interval);
|
|
29
|
-
stream.destroy();
|
|
30
|
-
resolve({ output: chunks.join(""), exitCode });
|
|
31
|
-
}
|
|
32
|
-
}), 100);
|
|
33
|
-
});
|
|
22
|
+
const chunks = [];
|
|
23
|
+
stream.on("data", (chunk) => chunks.push(chunk));
|
|
24
|
+
const exitCode = yield waitForExec(exec);
|
|
25
|
+
stream.destroy();
|
|
26
|
+
return { output: chunks.join(""), exitCode };
|
|
34
27
|
}
|
|
35
28
|
catch (err) {
|
|
36
29
|
logger_1.log.error(`Failed to exec container ${container.id} with command "${command.join(" ")}": ${err}`);
|
|
@@ -72,3 +65,14 @@ const inspectExec = (exec) => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
72
65
|
throw err;
|
|
73
66
|
}
|
|
74
67
|
});
|
|
68
|
+
const waitForExec = (exec) => __awaiter(void 0, void 0, void 0, function* () {
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
const interval = setInterval(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
71
|
+
const { running, exitCode } = yield inspectExec(exec);
|
|
72
|
+
if (!running) {
|
|
73
|
+
clearInterval(interval);
|
|
74
|
+
resolve(exitCode);
|
|
75
|
+
}
|
|
76
|
+
}), 100);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -1,6 +1,18 @@
|
|
|
1
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
|
+
};
|
|
2
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
12
|
exports.getContainerById = void 0;
|
|
4
|
-
const
|
|
5
|
-
const getContainerById = (id) =>
|
|
13
|
+
const docker_client_1 = require("../../docker-client");
|
|
14
|
+
const getContainerById = (id) => __awaiter(void 0, void 0, void 0, function* () {
|
|
15
|
+
const { dockerode } = yield docker_client_1.dockerClient;
|
|
16
|
+
return dockerode.getContainer(id);
|
|
17
|
+
});
|
|
6
18
|
exports.getContainerById = getContainerById;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { ContainerName, HealthCheckStatus, NetworkSettings } from "../../types";
|
|
2
|
-
import { Port } from "../../../port";
|
|
1
|
+
import { ContainerName, HealthCheckStatus, NetworkSettings, Ports } from "../../types";
|
|
3
2
|
import Dockerode from "dockerode";
|
|
4
3
|
export declare type InspectResult = {
|
|
5
4
|
name: ContainerName;
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
hostname: string;
|
|
6
|
+
ports: Ports;
|
|
8
7
|
healthCheckStatus: HealthCheckStatus;
|
|
9
8
|
networkSettings: {
|
|
10
9
|
[networkName: string]: NetworkSettings;
|
|
@@ -15,9 +15,9 @@ const inspectContainer = (container) => __awaiter(void 0, void 0, void 0, functi
|
|
|
15
15
|
try {
|
|
16
16
|
const inspectResult = yield container.inspect();
|
|
17
17
|
return {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
name: inspectResult.Name,
|
|
19
|
+
hostname: inspectResult.Config.Hostname,
|
|
20
|
+
ports: getPorts(inspectResult),
|
|
21
21
|
healthCheckStatus: getHealthCheckStatus(inspectResult),
|
|
22
22
|
networkSettings: getNetworkSettings(inspectResult),
|
|
23
23
|
state: { status: inspectResult.State.Status, running: inspectResult.State.Running },
|
|
@@ -29,11 +29,13 @@ const inspectContainer = (container) => __awaiter(void 0, void 0, void 0, functi
|
|
|
29
29
|
}
|
|
30
30
|
});
|
|
31
31
|
exports.inspectContainer = inspectContainer;
|
|
32
|
-
const
|
|
33
|
-
.filter((
|
|
34
|
-
.map((
|
|
35
|
-
const
|
|
36
|
-
|
|
32
|
+
const getPorts = (inspectInfo) => Object.entries(inspectInfo.NetworkSettings.Ports)
|
|
33
|
+
.filter(([, hostPorts]) => hostPorts !== null)
|
|
34
|
+
.map(([internalPort, hostPorts]) => {
|
|
35
|
+
const hostPort = hostPorts[0].HostPort;
|
|
36
|
+
return { [parseInt(internalPort.split("/")[0])]: parseInt(hostPort) };
|
|
37
|
+
})
|
|
38
|
+
.reduce((acc, curr) => (Object.assign(Object.assign({}, acc), curr)), {});
|
|
37
39
|
const getHealthCheckStatus = (inspectResult) => {
|
|
38
40
|
const health = inspectResult.State.Health;
|
|
39
41
|
if (health === undefined) {
|
|
@@ -10,11 +10,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.listContainers = void 0;
|
|
13
|
-
const
|
|
13
|
+
const docker_client_1 = require("../../docker-client");
|
|
14
14
|
const logger_1 = require("../../../logger");
|
|
15
15
|
const listContainers = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
16
16
|
try {
|
|
17
|
-
|
|
17
|
+
const { dockerode } = yield docker_client_1.dockerClient;
|
|
18
|
+
return yield dockerode.listContainers();
|
|
18
19
|
}
|
|
19
20
|
catch (err) {
|
|
20
21
|
logger_1.log.error(`Failed to list containers: ${err}`);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { Readable } from "stream";
|
|
2
|
-
export declare const demuxStream: (stream: Readable) => Readable
|
|
2
|
+
export declare const demuxStream: (stream: Readable) => Promise<Readable>;
|
|
@@ -1,13 +1,23 @@
|
|
|
1
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
|
+
};
|
|
2
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
12
|
exports.demuxStream = void 0;
|
|
4
13
|
const stream_1 = require("stream");
|
|
5
14
|
const logger_1 = require("../../logger");
|
|
6
|
-
const
|
|
7
|
-
const demuxStream = (stream) => {
|
|
15
|
+
const docker_client_1 = require("../docker-client");
|
|
16
|
+
const demuxStream = (stream) => __awaiter(void 0, void 0, void 0, function* () {
|
|
8
17
|
try {
|
|
9
18
|
const demuxedStream = new stream_1.PassThrough({ autoDestroy: true, encoding: "utf-8" });
|
|
10
|
-
|
|
19
|
+
const { dockerode } = yield docker_client_1.dockerClient;
|
|
20
|
+
dockerode.modem.demuxStream(stream, demuxedStream, demuxedStream);
|
|
11
21
|
stream.on("end", () => demuxedStream.end());
|
|
12
22
|
demuxedStream.on("close", () => {
|
|
13
23
|
if (!stream.destroyed) {
|
|
@@ -20,5 +30,5 @@ const demuxStream = (stream) => {
|
|
|
20
30
|
logger_1.log.error(`Failed to demux stream: ${err}`);
|
|
21
31
|
throw err;
|
|
22
32
|
}
|
|
23
|
-
};
|
|
33
|
+
});
|
|
24
34
|
exports.demuxStream = demuxStream;
|
|
@@ -10,9 +10,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.getDockerInfo = void 0;
|
|
13
|
-
const
|
|
13
|
+
const docker_client_1 = require("../docker-client");
|
|
14
14
|
const getDockerInfo = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
15
|
-
const
|
|
15
|
+
const { dockerode } = yield docker_client_1.dockerClient;
|
|
16
|
+
const info = yield dockerode.info();
|
|
16
17
|
return {
|
|
17
18
|
serverVersion: info.ServerVersion,
|
|
18
19
|
operatingSystem: info.OperatingSystem,
|
|
@@ -18,14 +18,15 @@ const docker_ignore_1 = require("../../../docker-ignore");
|
|
|
18
18
|
const tar_fs_1 = __importDefault(require("tar-fs"));
|
|
19
19
|
const slash_1 = __importDefault(require("slash"));
|
|
20
20
|
const byline_1 = __importDefault(require("byline"));
|
|
21
|
-
const
|
|
21
|
+
const docker_client_1 = require("../../docker-client");
|
|
22
22
|
const create_labels_1 = require("../create-labels");
|
|
23
23
|
const buildImage = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
24
24
|
try {
|
|
25
25
|
logger_1.log.info(`Building image '${options.imageName.toString()}' with context '${options.context}'`);
|
|
26
26
|
const dockerIgnoreFiles = yield docker_ignore_1.findDockerIgnoreFiles(options.context, options.dockerfileName);
|
|
27
27
|
const tarStream = tar_fs_1.default.pack(options.context, { ignore: (name) => dockerIgnoreFiles.has(slash_1.default(name)) });
|
|
28
|
-
|
|
28
|
+
const { dockerode } = yield docker_client_1.dockerClient;
|
|
29
|
+
return new Promise((resolve) => dockerode
|
|
29
30
|
.buildImage(tarStream, {
|
|
30
31
|
dockerfile: options.dockerfileName,
|
|
31
32
|
buildargs: options.buildArgs,
|
|
@@ -11,11 +11,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.listImages = void 0;
|
|
13
13
|
const docker_image_name_1 = require("../../../docker-image-name");
|
|
14
|
-
const
|
|
14
|
+
const docker_client_1 = require("../../docker-client");
|
|
15
15
|
const logger_1 = require("../../../logger");
|
|
16
16
|
const listImages = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
17
17
|
try {
|
|
18
|
-
const
|
|
18
|
+
const { dockerode } = yield docker_client_1.dockerClient;
|
|
19
|
+
const images = yield dockerode.listImages();
|
|
19
20
|
return images.reduce((dockerImageNames, image) => {
|
|
20
21
|
if (isDanglingImage(image)) {
|
|
21
22
|
return dockerImageNames;
|
|
@@ -12,7 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.pullImage = void 0;
|
|
13
13
|
const logger_1 = require("../../../logger");
|
|
14
14
|
const pull_stream_parser_1 = require("../../pull-stream-parser");
|
|
15
|
-
const
|
|
15
|
+
const docker_client_1 = require("../../docker-client");
|
|
16
16
|
const image_exists_1 = require("./image-exists");
|
|
17
17
|
const pullImage = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
18
18
|
try {
|
|
@@ -21,7 +21,8 @@ const pullImage = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
23
|
logger_1.log.info(`Pulling image: ${options.imageName}`);
|
|
24
|
-
const
|
|
24
|
+
const { dockerode } = yield docker_client_1.dockerClient;
|
|
25
|
+
const stream = yield dockerode.pull(options.imageName.toString(), { authconfig: options.authConfig });
|
|
25
26
|
yield new pull_stream_parser_1.PullStreamParser(options.imageName, logger_1.log).consume(stream);
|
|
26
27
|
}
|
|
27
28
|
catch (err) {
|
|
@@ -11,11 +11,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.connectNetwork = void 0;
|
|
13
13
|
const logger_1 = require("../../../logger");
|
|
14
|
-
const
|
|
14
|
+
const docker_client_1 = require("../../docker-client");
|
|
15
15
|
const connectNetwork = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
16
16
|
try {
|
|
17
17
|
logger_1.log.info(`Connecting container ${options.containerId} to network ${options.networkId}`);
|
|
18
|
-
const
|
|
18
|
+
const { dockerode } = yield docker_client_1.dockerClient;
|
|
19
|
+
const network = dockerode.getNetwork(options.networkId);
|
|
19
20
|
yield network.connect({ Container: options.containerId, EndpointConfig: { Aliases: options.networkAliases } });
|
|
20
21
|
}
|
|
21
22
|
catch (err) {
|