@alchemy.run/docker 0.0.0 → 2.0.0-beta.84
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/LICENSE +201 -0
- package/README.md +46 -4
- package/lib/Bootstrap.d.ts +7 -0
- package/lib/Bootstrap.d.ts.map +1 -0
- package/lib/Bootstrap.js +25 -0
- package/lib/Bootstrap.js.map +1 -0
- package/lib/BuildHash.d.ts +47 -0
- package/lib/BuildHash.d.ts.map +1 -0
- package/lib/BuildHash.js +165 -0
- package/lib/BuildHash.js.map +1 -0
- package/lib/Container.d.ts +352 -0
- package/lib/Container.d.ts.map +1 -0
- package/lib/Container.js +531 -0
- package/lib/Container.js.map +1 -0
- package/lib/Context.d.ts +86 -0
- package/lib/Context.d.ts.map +1 -0
- package/lib/Context.js +146 -0
- package/lib/Context.js.map +1 -0
- package/lib/Docker.d.ts +448 -0
- package/lib/Docker.d.ts.map +1 -0
- package/lib/Docker.js +416 -0
- package/lib/Docker.js.map +1 -0
- package/lib/Dockerfile.d.ts +72 -0
- package/lib/Dockerfile.d.ts.map +1 -0
- package/lib/Dockerfile.js +12 -0
- package/lib/Dockerfile.js.map +1 -0
- package/lib/HealthcheckCommand.d.ts +17 -0
- package/lib/HealthcheckCommand.d.ts.map +1 -0
- package/lib/HealthcheckCommand.js +33 -0
- package/lib/HealthcheckCommand.js.map +1 -0
- package/lib/Image.d.ts +151 -0
- package/lib/Image.d.ts.map +1 -0
- package/lib/Image.js +192 -0
- package/lib/Image.js.map +1 -0
- package/lib/ImageSource.d.ts +234 -0
- package/lib/ImageSource.d.ts.map +1 -0
- package/lib/ImageSource.js +469 -0
- package/lib/ImageSource.js.map +1 -0
- package/lib/Network.d.ts +70 -0
- package/lib/Network.d.ts.map +1 -0
- package/lib/Network.js +111 -0
- package/lib/Network.js.map +1 -0
- package/lib/Providers.d.ts +21 -0
- package/lib/Providers.d.ts.map +1 -0
- package/lib/Providers.js +26 -0
- package/lib/Providers.js.map +1 -0
- package/lib/Registry.d.ts +47 -0
- package/lib/Registry.d.ts.map +1 -0
- package/lib/Registry.js +67 -0
- package/lib/Registry.js.map +1 -0
- package/lib/RegistryError.d.ts +21 -0
- package/lib/RegistryError.d.ts.map +1 -0
- package/lib/RegistryError.js +28 -0
- package/lib/RegistryError.js.map +1 -0
- package/lib/RemoteImage.d.ts +118 -0
- package/lib/RemoteImage.d.ts.map +1 -0
- package/lib/RemoteImage.js +164 -0
- package/lib/RemoteImage.js.map +1 -0
- package/lib/Service.d.ts +478 -0
- package/lib/Service.d.ts.map +1 -0
- package/lib/Service.js +588 -0
- package/lib/Service.js.map +1 -0
- package/lib/ServiceImage.d.ts +78 -0
- package/lib/ServiceImage.d.ts.map +1 -0
- package/lib/ServiceImage.js +162 -0
- package/lib/ServiceImage.js.map +1 -0
- package/lib/Swarm.d.ts +119 -0
- package/lib/Swarm.d.ts.map +1 -0
- package/lib/Swarm.js +148 -0
- package/lib/Swarm.js.map +1 -0
- package/lib/Volume.d.ts +94 -0
- package/lib/Volume.d.ts.map +1 -0
- package/lib/Volume.js +133 -0
- package/lib/Volume.js.map +1 -0
- package/lib/index.d.ts +14 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +13 -0
- package/lib/index.js.map +1 -0
- package/package.json +58 -3
- package/src/Bootstrap.ts +51 -0
- package/src/BuildHash.ts +255 -0
- package/src/Container.ts +792 -0
- package/src/Context.ts +215 -0
- package/src/Docker.ts +1079 -0
- package/src/Dockerfile.ts +81 -0
- package/src/HealthcheckCommand.ts +35 -0
- package/src/Image.ts +293 -0
- package/src/ImageSource.ts +769 -0
- package/src/Network.ts +169 -0
- package/src/Providers.ts +46 -0
- package/src/Registry.ts +91 -0
- package/src/RegistryError.ts +48 -0
- package/src/RemoteImage.ts +255 -0
- package/src/Service.ts +1080 -0
- package/src/ServiceImage.ts +252 -0
- package/src/Swarm.ts +221 -0
- package/src/Volume.ts +193 -0
- package/src/index.ts +17 -0
package/lib/Container.js
ADDED
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
import { Unowned } from "alchemy/AdoptPolicy";
|
|
2
|
+
import { isResolved } from "alchemy/Diff";
|
|
3
|
+
import * as Provider from "alchemy/Provider";
|
|
4
|
+
import { Resource } from "alchemy/Resource";
|
|
5
|
+
import { createInternalTags, hasAlchemyTags } from "alchemy/Tags";
|
|
6
|
+
import { toSeconds } from "alchemy/Util/Duration";
|
|
7
|
+
import { sha256, sha256Object } from "alchemy/Util/sha256";
|
|
8
|
+
import * as Data from "effect/Data";
|
|
9
|
+
import * as Duration from "effect/Duration";
|
|
10
|
+
import * as Effect from "effect/Effect";
|
|
11
|
+
import * as Equal from "effect/Equal";
|
|
12
|
+
import * as FileSystem from "effect/FileSystem";
|
|
13
|
+
import * as Path from "effect/Path";
|
|
14
|
+
import * as Redacted from "effect/Redacted";
|
|
15
|
+
import { Docker, dockerContextName, dockerPhysicalName } from "./Docker.js";
|
|
16
|
+
import { healthcheckCommand, isHealthcheckDisabled } from "./HealthcheckCommand.js";
|
|
17
|
+
const CREATE_CONFIG_HASH_LABEL = "alchemy::container-config";
|
|
18
|
+
/**
|
|
19
|
+
* A Docker container managed through the active Docker context.
|
|
20
|
+
*
|
|
21
|
+
* This resource creates, starts, stops, inspects, and removes containers through
|
|
22
|
+
* the Docker CLI. It is not interchangeable with `Cloudflare.Container`, which
|
|
23
|
+
* manages Cloudflare's container platform; use pushed image references to bridge
|
|
24
|
+
* Docker-built images into cloud container runtimes.
|
|
25
|
+
*
|
|
26
|
+
*
|
|
27
|
+
* ### Running Containers
|
|
28
|
+
* **Example:** Nginx with a published port
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const nginx = yield* Docker.Container("nginx", {
|
|
31
|
+
* image: "nginx:alpine",
|
|
32
|
+
* ports: [{ external: 8080, internal: 80 }],
|
|
33
|
+
* start: true,
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* ### Secret Environment
|
|
38
|
+
* **Example:** Redacted env var
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const password = yield* Config.Redacted("POSTGRES_PASSWORD");
|
|
41
|
+
* const db = yield* Docker.Container("postgres", {
|
|
42
|
+
* image: "postgres:18-alpine",
|
|
43
|
+
* environment: {
|
|
44
|
+
* POSTGRES_PASSWORD: password,
|
|
45
|
+
* },
|
|
46
|
+
* start: true,
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* ### Environment Files
|
|
51
|
+
* **Example:** Layered Docker env files
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const app = yield* Docker.Container("app", {
|
|
54
|
+
* image: "ghcr.io/acme/app:latest",
|
|
55
|
+
* envFiles: ["./config/base.env", "./config/production.env"],
|
|
56
|
+
* // Explicit values are passed after env files and take precedence.
|
|
57
|
+
* environment: { LOG_LEVEL: "info" },
|
|
58
|
+
* });
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* Editing an env file replaces the container on the next deploy, as does
|
|
62
|
+
* adopting a container that uses env files (once, so later edits are tracked).
|
|
63
|
+
* Alchemy reads
|
|
64
|
+
* the files at plan time but keeps only a digest of their contents, on the
|
|
65
|
+
* container's own label; neither the values nor the digest are written to
|
|
66
|
+
* Alchemy state. Docker exposes the resolved values through `docker inspect`,
|
|
67
|
+
* so treat env files as secrets.
|
|
68
|
+
*
|
|
69
|
+
* ### Networks and Volumes
|
|
70
|
+
* **Example:** PostgreSQL with persistent storage
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const network = yield* Docker.Network("app-network");
|
|
73
|
+
* const data = yield* Docker.Volume("postgres-data");
|
|
74
|
+
* const postgresName = "app-postgres";
|
|
75
|
+
* yield* Docker.Container("postgres", {
|
|
76
|
+
* name: postgresName,
|
|
77
|
+
* image: "postgres:18-alpine",
|
|
78
|
+
* ports: [{ external: 15432, internal: 5432 }],
|
|
79
|
+
* volumes: [{ hostPath: data.name, containerPath: "/var/lib/postgresql/data" }],
|
|
80
|
+
* networks: [{ name: network.name, aliases: ["postgres"] }],
|
|
81
|
+
* start: true,
|
|
82
|
+
* });
|
|
83
|
+
* const runtime = yield* Docker.inspectContainer(postgresName);
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
86
|
+
* ### Host Access
|
|
87
|
+
* `extraHosts` writes lines into the container's `/etc/hosts`; it changes name
|
|
88
|
+
* resolution and nothing else. Docker's `host-gateway` alias resolves to the
|
|
89
|
+
* host machine, which is how a container reaches a service on the developer's
|
|
90
|
+
* loopback.
|
|
91
|
+
*
|
|
92
|
+
* On Linux `host-gateway` is the Docker bridge gateway (typically
|
|
93
|
+
* `172.17.0.1`), so a container's packets to it arrive on the host's `INPUT`
|
|
94
|
+
* chain. Under a default-deny firewall — ufw ships
|
|
95
|
+
* `DEFAULT_INPUT_POLICY="DROP"` — the hostname resolves correctly and the
|
|
96
|
+
* connection then times out, which reads like an application bug rather than a
|
|
97
|
+
* firewall one. Allow the bridge subnet to fix it:
|
|
98
|
+
* `sudo ufw allow from 172.16.0.0/12`.
|
|
99
|
+
*
|
|
100
|
+
* **Example:** Reach a service on the developer's machine
|
|
101
|
+
* ```typescript
|
|
102
|
+
* const api = yield* Docker.Container("api", {
|
|
103
|
+
* image: "ghcr.io/acme/api:latest",
|
|
104
|
+
* // `host-gateway` resolves to the host machine, so a database listening
|
|
105
|
+
* // on the developer's loopback is reachable from inside the container.
|
|
106
|
+
* extraHosts: ["host.docker.internal:host-gateway"],
|
|
107
|
+
* environment: {
|
|
108
|
+
* DATABASE_URL: "postgres://postgres@host.docker.internal:5432/app",
|
|
109
|
+
* },
|
|
110
|
+
* start: true,
|
|
111
|
+
* });
|
|
112
|
+
* ```
|
|
113
|
+
*
|
|
114
|
+
* **Example:** Pin a hostname to a fixed address
|
|
115
|
+
* ```typescript
|
|
116
|
+
* const api = yield* Docker.Container("api", {
|
|
117
|
+
* image: "ghcr.io/acme/api:latest",
|
|
118
|
+
* // Any `hostname:address` pair — host access is just the common case.
|
|
119
|
+
* extraHosts: ["service.example:192.0.2.10"],
|
|
120
|
+
* start: true,
|
|
121
|
+
* });
|
|
122
|
+
* ```
|
|
123
|
+
*
|
|
124
|
+
* ### Runtime Options
|
|
125
|
+
* **Example:** Share a donor container's network namespace
|
|
126
|
+
* ```typescript
|
|
127
|
+
* const donor = yield* Docker.Container("donor", { image: "redis:alpine" });
|
|
128
|
+
* const sidecar = yield* Docker.Container("sidecar", {
|
|
129
|
+
* image: "busybox:latest",
|
|
130
|
+
* networkMode: { container: donor.id },
|
|
131
|
+
* });
|
|
132
|
+
* ```
|
|
133
|
+
*
|
|
134
|
+
* **Example:** Add capabilities and devices
|
|
135
|
+
* ```typescript
|
|
136
|
+
* const worker = yield* Docker.Container("worker", {
|
|
137
|
+
* image: "ubuntu:latest",
|
|
138
|
+
* capAdd: ["SYS_ADMIN"],
|
|
139
|
+
* devices: [{ hostPath: "/dev/fuse", containerPath: "/dev/fuse" }],
|
|
140
|
+
* });
|
|
141
|
+
* ```
|
|
142
|
+
*
|
|
143
|
+
* **Example:** Publish on any free host port
|
|
144
|
+
* ```typescript
|
|
145
|
+
* const api = yield* Docker.Container("api", {
|
|
146
|
+
* image: "ghcr.io/acme/api:latest",
|
|
147
|
+
* // `external: 0` lets Docker choose; the assigned port is reported back.
|
|
148
|
+
* ports: [{ external: 0, internal: 3000 }],
|
|
149
|
+
* start: true,
|
|
150
|
+
* });
|
|
151
|
+
* const hostPort = api.ports["3000/tcp"];
|
|
152
|
+
* ```
|
|
153
|
+
*
|
|
154
|
+
* ### Traefik
|
|
155
|
+
* **Example:** Route a container through Traefik
|
|
156
|
+
* ```typescript
|
|
157
|
+
* const api = yield* Docker.Container("api", {
|
|
158
|
+
* image: "ghcr.io/acme/api:latest",
|
|
159
|
+
* networks: [{ name: "traefik" }],
|
|
160
|
+
* labels: {
|
|
161
|
+
* "traefik.enable": "true",
|
|
162
|
+
* "traefik.http.routers.api.rule": "Host(`api.example.com`)",
|
|
163
|
+
* "traefik.http.services.api.loadbalancer.server.port": "3000",
|
|
164
|
+
* },
|
|
165
|
+
* stopTimeout: "30 seconds",
|
|
166
|
+
* start: true,
|
|
167
|
+
* });
|
|
168
|
+
* ```
|
|
169
|
+
*
|
|
170
|
+
* **Example:** Use a Docker.Context resource
|
|
171
|
+
* ```typescript
|
|
172
|
+
* const remote = yield* Docker.Context("remote", {
|
|
173
|
+
* name: "remote-build",
|
|
174
|
+
* docker: "host=ssh://docker@example.com",
|
|
175
|
+
* });
|
|
176
|
+
*
|
|
177
|
+
* const api = yield* Docker.Container("api", {
|
|
178
|
+
* image: "nginx:alpine",
|
|
179
|
+
* context: remote,
|
|
180
|
+
* });
|
|
181
|
+
* ```
|
|
182
|
+
*
|
|
183
|
+
* @resource
|
|
184
|
+
* @product Container
|
|
185
|
+
*/
|
|
186
|
+
export const Container = Resource("Docker.Container");
|
|
187
|
+
/**
|
|
188
|
+
* Inspect a Docker container by name and return normalized runtime details.
|
|
189
|
+
*
|
|
190
|
+
* This is a small public wrapper around Docker's raw inspect output. It returns
|
|
191
|
+
* the stable data Alchemy callers typically need, including bound host ports.
|
|
192
|
+
*/
|
|
193
|
+
export const inspectContainer = (name, context) => Docker.pipe(Effect.flatMap((docker) => docker.container.inspect(name, dockerContextName(context))), Effect.map((container) => toContainerAttributes(container, container.Config.Image)));
|
|
194
|
+
export const ContainerProvider = () => Provider.effect(Container, Effect.gen(function* () {
|
|
195
|
+
const docker = yield* Docker;
|
|
196
|
+
const fs = yield* FileSystem.FileSystem;
|
|
197
|
+
const path = yield* Path.Path;
|
|
198
|
+
// Env files are read so editing one rolls the container, but only a
|
|
199
|
+
// digest of their bytes enters the config hash, which lives solely in
|
|
200
|
+
// the container's own label (where `docker inspect` already exposes
|
|
201
|
+
// the resolved values). Neither contents nor digest reach alchemy state.
|
|
202
|
+
const envFilesDigest = Effect.fn(function* (envFiles) {
|
|
203
|
+
if (!envFiles?.length)
|
|
204
|
+
return undefined;
|
|
205
|
+
const digests = yield* Effect.forEach(envFiles, (file) => fs.readFile(path.resolve(file)).pipe(Effect.flatMap(sha256), Effect.map((digest) => `${file}\0${digest}`)));
|
|
206
|
+
return yield* sha256(digests.join("\n"));
|
|
207
|
+
});
|
|
208
|
+
// Without env files the hash input is unchanged from before they were
|
|
209
|
+
// supported, so existing containers keep their label and are not rolled.
|
|
210
|
+
const desiredConfigHash = Effect.fn(function* (args, news) {
|
|
211
|
+
const envFiles = yield* envFilesDigest(news.envFiles);
|
|
212
|
+
return yield* sha256Object({
|
|
213
|
+
...args,
|
|
214
|
+
imageId: normalizeImageId(news.image),
|
|
215
|
+
...(envFiles === undefined ? {} : { envFiles }),
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
const reconcileNetworks = Effect.fn(function* (live, news, olds) {
|
|
219
|
+
const context = dockerContextName(news.context);
|
|
220
|
+
const connect = new Map();
|
|
221
|
+
const disconnect = new Set();
|
|
222
|
+
for (const network of news.networks ?? []) {
|
|
223
|
+
const entry = live.NetworkSettings.Networks?.[network.name];
|
|
224
|
+
if (!entry) {
|
|
225
|
+
connect.set(network.name, network);
|
|
226
|
+
}
|
|
227
|
+
else if (!Equal.equals(entry.Aliases ?? [], network.aliases ?? [])) {
|
|
228
|
+
connect.set(network.name, network);
|
|
229
|
+
disconnect.add(network.name);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// Only networks alchemy itself attached are alchemy's to detach, and
|
|
233
|
+
// `olds.networks` is the sole record of which those are — the live
|
|
234
|
+
// container cannot say who connected a network. Sweeping every live
|
|
235
|
+
// network instead tore off the default `bridge` and anything a user,
|
|
236
|
+
// compose file, or another tool had attached out of band (#1386).
|
|
237
|
+
const desired = new Set((news.networks ?? []).map((n) => n.name));
|
|
238
|
+
for (const network of olds?.networks ?? []) {
|
|
239
|
+
if (!desired.has(network.name) && live.NetworkSettings.Networks?.[network.name]) {
|
|
240
|
+
disconnect.add(network.name);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
yield* Effect.forEach(disconnect, (network) => docker.network.disconnect({ network, container: live.Id, context }), { concurrency: "unbounded" });
|
|
244
|
+
yield* Effect.forEach(connect.values(), (network) => docker.network.connect({
|
|
245
|
+
network: network.name,
|
|
246
|
+
container: live.Id,
|
|
247
|
+
alias: network.aliases,
|
|
248
|
+
context,
|
|
249
|
+
}), { concurrency: "unbounded" });
|
|
250
|
+
});
|
|
251
|
+
const inspect = (name, context) => docker.container
|
|
252
|
+
.inspect(name, context)
|
|
253
|
+
.pipe(Effect.catchReason("PlatformError", "NotFound", () => Effect.undefined));
|
|
254
|
+
const remove = (name, context) => docker.container.stop(name, context).pipe(Effect.andThen(docker.container.remove(name, true, context)), Effect.catchReason("PlatformError", "NotFound", () => Effect.void));
|
|
255
|
+
return Container.Provider.of({
|
|
256
|
+
list: () => Effect.succeed([]),
|
|
257
|
+
read: Effect.fn(function* ({ id, instanceId, olds, output }) {
|
|
258
|
+
const context = dockerContextName(olds.context);
|
|
259
|
+
const name = yield* dockerPhysicalName(id, olds, instanceId);
|
|
260
|
+
const info = yield* inspect(name, context);
|
|
261
|
+
if (!info)
|
|
262
|
+
return undefined;
|
|
263
|
+
// `olds.image` may be `undefined` when a `creating` row was
|
|
264
|
+
// persisted before upstream Outputs resolved — fall back to the
|
|
265
|
+
// live container's actual image.
|
|
266
|
+
const attrs = toContainerAttributes(info, olds.image !== undefined ? normalizeImageRef(olds.image) : info.Config.Image);
|
|
267
|
+
if (output)
|
|
268
|
+
return attrs;
|
|
269
|
+
// Without prior state, only adopt a container that carries our
|
|
270
|
+
// branding; anything else is foreign and gated behind `--adopt`.
|
|
271
|
+
const owned = yield* hasAlchemyTags(id, info.Config.Labels ?? undefined);
|
|
272
|
+
return owned ? attrs : Unowned(attrs);
|
|
273
|
+
}),
|
|
274
|
+
diff: Effect.fn(function* ({ id, instanceId, news, olds }) {
|
|
275
|
+
if (!isResolved(news))
|
|
276
|
+
return undefined;
|
|
277
|
+
// An Output-valued `image` doesn't survive a `creating`-state
|
|
278
|
+
// round-trip (it deserializes as `undefined`) — without comparable
|
|
279
|
+
// prior create args, let the engine apply its default update logic.
|
|
280
|
+
if (olds.image === undefined)
|
|
281
|
+
return undefined;
|
|
282
|
+
if (dockerContextName(olds.context) !== dockerContextName(news.context)) {
|
|
283
|
+
return { action: "replace", deleteFirst: true };
|
|
284
|
+
}
|
|
285
|
+
const oldArgs = yield* makeCreateArgs(id, olds, instanceId);
|
|
286
|
+
const newArgs = yield* makeCreateArgs(id, news, instanceId);
|
|
287
|
+
if (!Equal.equals(oldArgs, newArgs)) {
|
|
288
|
+
return { action: "replace", deleteFirst: true };
|
|
289
|
+
}
|
|
290
|
+
if (!Equal.equals(olds.networks ?? [], news.networks ?? []) ||
|
|
291
|
+
(olds.start ?? false) !== (news.start ?? false)) {
|
|
292
|
+
return { action: "update" };
|
|
293
|
+
}
|
|
294
|
+
// Same env file paths, possibly new contents: compare against the
|
|
295
|
+
// hash stamped on the running container.
|
|
296
|
+
if (news.envFiles?.length) {
|
|
297
|
+
const live = yield* inspect(newArgs.name, dockerContextName(news.context));
|
|
298
|
+
const applied = live?.Config.Labels?.[CREATE_CONFIG_HASH_LABEL];
|
|
299
|
+
if (applied !== undefined && applied !== (yield* desiredConfigHash(newArgs, news))) {
|
|
300
|
+
return { action: "update" };
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}),
|
|
304
|
+
reconcile: Effect.fn(function* ({ id, instanceId, news, olds, output }) {
|
|
305
|
+
const context = dockerContextName(news.context);
|
|
306
|
+
const args = yield* makeCreateArgs(id, news, instanceId);
|
|
307
|
+
const configHash = yield* desiredConfigHash(args, news);
|
|
308
|
+
// Adoption has output but no olds. In that case the observed
|
|
309
|
+
// container already lives in the desired context.
|
|
310
|
+
const oldContext = olds ? dockerContextName(olds.context) : context;
|
|
311
|
+
const desiredLive = yield* inspect(args.name, context);
|
|
312
|
+
const previous = output && (output.name !== args.name || oldContext !== context)
|
|
313
|
+
? yield* inspect(output.name, oldContext)
|
|
314
|
+
: undefined;
|
|
315
|
+
if (previous) {
|
|
316
|
+
yield* remove(previous.Id, oldContext);
|
|
317
|
+
}
|
|
318
|
+
const live = desiredLive?.Id === previous?.Id ? undefined : desiredLive;
|
|
319
|
+
const oldArgs = live?.Config.Labels?.[CREATE_CONFIG_HASH_LABEL] === undefined && olds !== undefined
|
|
320
|
+
? yield* makeCreateArgs(id, {
|
|
321
|
+
...olds,
|
|
322
|
+
image: olds.image ?? output?.imageRef ?? news.image,
|
|
323
|
+
}, instanceId)
|
|
324
|
+
: undefined;
|
|
325
|
+
const recreate = live !== undefined &&
|
|
326
|
+
(live.Config.Labels?.[CREATE_CONFIG_HASH_LABEL] === undefined
|
|
327
|
+
? (oldArgs !== undefined && !Equal.equals(oldArgs, args)) ||
|
|
328
|
+
normalizeImageId(olds?.image) !== normalizeImageId(news.image) ||
|
|
329
|
+
!matchesLegacyConfig(live, args, news.image) ||
|
|
330
|
+
// Env file contents can't be compared without a label (e.g. an
|
|
331
|
+
// adopted container); recreate once so later edits are tracked.
|
|
332
|
+
(news.envFiles?.length ?? 0) > 0
|
|
333
|
+
: live.Config.Labels[CREATE_CONFIG_HASH_LABEL] !== configHash);
|
|
334
|
+
if (recreate) {
|
|
335
|
+
yield* remove(live.Id, context);
|
|
336
|
+
}
|
|
337
|
+
const current = recreate ? undefined : live;
|
|
338
|
+
if (!current) {
|
|
339
|
+
const internalTags = yield* createInternalTags(id);
|
|
340
|
+
const { stdout: containerId } = yield* docker.container.create({
|
|
341
|
+
...args,
|
|
342
|
+
context,
|
|
343
|
+
label: {
|
|
344
|
+
...args.label,
|
|
345
|
+
...internalTags,
|
|
346
|
+
[CREATE_CONFIG_HASH_LABEL]: configHash,
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
yield* Effect.forEach(news.networks ?? [], (network) => docker.network.connect({
|
|
350
|
+
network: network.name,
|
|
351
|
+
container: containerId,
|
|
352
|
+
alias: network.aliases,
|
|
353
|
+
context,
|
|
354
|
+
}), { concurrency: "unbounded" });
|
|
355
|
+
if (news.start) {
|
|
356
|
+
yield* docker.container.start(containerId, context);
|
|
357
|
+
}
|
|
358
|
+
const info = yield* docker.container.inspect(containerId, context);
|
|
359
|
+
return toContainerAttributes(info, args.image);
|
|
360
|
+
}
|
|
361
|
+
yield* reconcileNetworks(current, news, olds);
|
|
362
|
+
if (news.start && current.State.Status !== "running") {
|
|
363
|
+
yield* docker.container.start(current.Id, context);
|
|
364
|
+
}
|
|
365
|
+
else if (!news.start && current.State.Status === "running") {
|
|
366
|
+
yield* docker.container.stop(current.Id, context);
|
|
367
|
+
}
|
|
368
|
+
return yield* docker.container
|
|
369
|
+
.inspect(current.Id, context)
|
|
370
|
+
.pipe(Effect.map((info) => toContainerAttributes(info, args.image)));
|
|
371
|
+
}),
|
|
372
|
+
delete: Effect.fn(({ olds, output }) => remove(output.name, dockerContextName(olds.context))),
|
|
373
|
+
});
|
|
374
|
+
}));
|
|
375
|
+
const normalizeImageRef = (image) => typeof image === "string" ? image : image.imageRef;
|
|
376
|
+
const normalizeImageId = (image) => typeof image === "string" ? undefined : image?.imageId;
|
|
377
|
+
// Containers created before config hashes were introduced are compared using
|
|
378
|
+
// the fields Docker reports without image-default normalization. A subsequent
|
|
379
|
+
// create stamps the complete resolved configuration hash.
|
|
380
|
+
const matchesLegacyConfig = (live, desired, image) => (normalizeImageId(image)
|
|
381
|
+
? live.Image === normalizeImageId(image)
|
|
382
|
+
: live.Config.Image === desired.image) &&
|
|
383
|
+
(desired.command === undefined || Equal.equals(live.Config.Cmd, desired.command)) &&
|
|
384
|
+
Object.entries(desired.env ?? {}).every(([key, value]) => live.Config.Env?.includes(`${key}=${value}`));
|
|
385
|
+
const makeCreateArgs = (id, news, instanceId) => dockerPhysicalName(id, news, instanceId).pipe(Effect.tap(() => validateContainerOptions(news)), Effect.map((name) => ({
|
|
386
|
+
name,
|
|
387
|
+
image: normalizeImageRef(news.image),
|
|
388
|
+
command: news.command,
|
|
389
|
+
env: normalizeEnvironment(news.environment),
|
|
390
|
+
"env-file": news.envFiles?.length ? news.envFiles : undefined,
|
|
391
|
+
volume: news.volumes?.map((v) => `${v.hostPath}:${v.containerPath}${v.readOnly ? ":ro" : ""}`),
|
|
392
|
+
p: news.ports?.map((port) => {
|
|
393
|
+
const target = `${port.internal}/${port.protocol ?? "tcp"}`;
|
|
394
|
+
// `external: 0` means "any free host port". Docker spells that as a
|
|
395
|
+
// bare container port (`-p 80/tcp`); `-p 0:80/tcp` instead asks for
|
|
396
|
+
// host port 0 literally, which the daemon accepts and then reports
|
|
397
|
+
// back as 0.
|
|
398
|
+
return isRandomHostPort(port.external) ? target : `${port.external}:${target}`;
|
|
399
|
+
}),
|
|
400
|
+
"add-host": news.extraHosts,
|
|
401
|
+
network: normalizeNetworkMode(news.networkMode),
|
|
402
|
+
"cap-add": normalizeCapabilities(news.capAdd),
|
|
403
|
+
device: normalizeDevices(news.devices),
|
|
404
|
+
restart: news.restart ?? "no",
|
|
405
|
+
label: news.labels,
|
|
406
|
+
"stop-timeout": toSeconds(news.stopTimeout)?.toString(),
|
|
407
|
+
rm: news.removeOnExit ?? false,
|
|
408
|
+
...(news.healthcheck
|
|
409
|
+
? {
|
|
410
|
+
"health-cmd": healthcheckCommand(news.healthcheck.cmd),
|
|
411
|
+
// Only when set: an always-present key would change every
|
|
412
|
+
// container's config hash and recreate it on upgrade.
|
|
413
|
+
...(isHealthcheckDisabled(news.healthcheck.cmd) ? { "no-healthcheck": true } : {}),
|
|
414
|
+
"health-interval": normalizeDuration(news.healthcheck.interval),
|
|
415
|
+
"health-timeout": normalizeDuration(news.healthcheck.timeout),
|
|
416
|
+
"health-retries": news.healthcheck.retries ?? 0,
|
|
417
|
+
"health-start-period": normalizeDuration(news.healthcheck.startPeriod),
|
|
418
|
+
"health-start-interval": normalizeDuration(news.healthcheck.startInterval),
|
|
419
|
+
}
|
|
420
|
+
: {
|
|
421
|
+
"health-cmd": undefined,
|
|
422
|
+
"health-interval": undefined,
|
|
423
|
+
"health-timeout": undefined,
|
|
424
|
+
"health-retries": undefined,
|
|
425
|
+
"health-start-period": undefined,
|
|
426
|
+
"health-start-interval": undefined,
|
|
427
|
+
}),
|
|
428
|
+
})));
|
|
429
|
+
const toContainerAttributes = (info, imageRef) => ({
|
|
430
|
+
id: info.Id,
|
|
431
|
+
name: typeof info.Name === "string" ? info.Name.replace(/^\//, "") : info.Id,
|
|
432
|
+
status: info.State.Status,
|
|
433
|
+
createdAt: Date.parse(info.Created) || Date.now(),
|
|
434
|
+
imageRef,
|
|
435
|
+
ports: toPortAttributes(info),
|
|
436
|
+
networkMode: info.HostConfig.NetworkMode,
|
|
437
|
+
capAdd: info.HostConfig.CapAdd ?? undefined,
|
|
438
|
+
devices: info.HostConfig.Devices?.map((device) => ({
|
|
439
|
+
hostPath: device.PathOnHost,
|
|
440
|
+
containerPath: device.PathInContainer,
|
|
441
|
+
permissions: device.CgroupPermissions,
|
|
442
|
+
})),
|
|
443
|
+
});
|
|
444
|
+
const normalizeNetworkMode = (mode) => mode === undefined ? undefined : typeof mode === "string" ? mode : `container:${mode.container}`;
|
|
445
|
+
const normalizeCapabilities = (capAdd) => {
|
|
446
|
+
if (!capAdd?.length)
|
|
447
|
+
return undefined;
|
|
448
|
+
return [...new Set(capAdd.map((capability) => capability.trim()).filter(Boolean))].sort();
|
|
449
|
+
};
|
|
450
|
+
const normalizeDevices = (devices) => {
|
|
451
|
+
if (!devices?.length)
|
|
452
|
+
return undefined;
|
|
453
|
+
const normalized = devices.map((device) => `${device.hostPath}:${device.containerPath}:${device.permissions ?? "rwm"}`);
|
|
454
|
+
return [...new Set(normalized)].sort();
|
|
455
|
+
};
|
|
456
|
+
/**
|
|
457
|
+
* Raised before Docker is called when a container's options cannot be
|
|
458
|
+
* combined, e.g. sharing another container's network namespace while
|
|
459
|
+
* publishing ports.
|
|
460
|
+
*/
|
|
461
|
+
export class InvalidContainerOptions extends Data.TaggedError("InvalidContainerOptions") {
|
|
462
|
+
}
|
|
463
|
+
const validateContainerOptions = (news) => {
|
|
464
|
+
if (isContainerNetworkMode(news.networkMode) &&
|
|
465
|
+
((news.ports?.length ?? 0) > 0 || (news.networks?.length ?? 0) > 0)) {
|
|
466
|
+
return Effect.fail(new InvalidContainerOptions({
|
|
467
|
+
message: "Docker.Container networkMode.container cannot be combined with ports or networks",
|
|
468
|
+
}));
|
|
469
|
+
}
|
|
470
|
+
const targets = new Set();
|
|
471
|
+
for (const device of news.devices ?? []) {
|
|
472
|
+
if (targets.has(device.containerPath)) {
|
|
473
|
+
return Effect.fail(new InvalidContainerOptions({
|
|
474
|
+
message: `Docker.Container devices contain conflicting target path ${device.containerPath}`,
|
|
475
|
+
}));
|
|
476
|
+
}
|
|
477
|
+
targets.add(device.containerPath);
|
|
478
|
+
}
|
|
479
|
+
return Effect.void;
|
|
480
|
+
};
|
|
481
|
+
const isContainerNetworkMode = (mode) => typeof mode === "string" ? mode.startsWith("container:") : mode !== undefined;
|
|
482
|
+
/** First binding that carries a real (non-zero) host port. */
|
|
483
|
+
const boundHostPort = (bindings) => {
|
|
484
|
+
for (const binding of bindings ?? []) {
|
|
485
|
+
if (!binding.HostPort)
|
|
486
|
+
continue;
|
|
487
|
+
const port = Number.parseInt(binding.HostPort, 10);
|
|
488
|
+
if (Number.isInteger(port) && port > 0)
|
|
489
|
+
return port;
|
|
490
|
+
}
|
|
491
|
+
return undefined;
|
|
492
|
+
};
|
|
493
|
+
/**
|
|
494
|
+
* `HostConfig.PortBindings` is what was *requested*, `NetworkSettings.Ports`
|
|
495
|
+
* what Docker actually *assigned* — so the assignment wins wherever both
|
|
496
|
+
* exist. A container published with `external: 0` (or any random-publish
|
|
497
|
+
* mapping) has no requested host port at all, and reading the request over
|
|
498
|
+
* the assignment reported 0 instead of the port the container is reachable
|
|
499
|
+
* on. The request is still the fallback: a created-but-not-yet-started
|
|
500
|
+
* container has empty `NetworkSettings.Ports`.
|
|
501
|
+
*/
|
|
502
|
+
const toPortAttributes = (info) => {
|
|
503
|
+
const ports = {};
|
|
504
|
+
for (const [internal, bindings] of Object.entries(info.HostConfig.PortBindings ?? {})) {
|
|
505
|
+
const port = boundHostPort(bindings);
|
|
506
|
+
if (port !== undefined)
|
|
507
|
+
ports[internal] = port;
|
|
508
|
+
}
|
|
509
|
+
for (const [internal, bindings] of Object.entries(info.NetworkSettings.Ports ?? {})) {
|
|
510
|
+
const port = boundHostPort(bindings);
|
|
511
|
+
if (port !== undefined)
|
|
512
|
+
ports[internal] = port;
|
|
513
|
+
}
|
|
514
|
+
return ports;
|
|
515
|
+
};
|
|
516
|
+
/** `external: 0` / `"0"` asks Docker to pick any free host port. */
|
|
517
|
+
const isRandomHostPort = (external) => Number.parseInt(String(external), 10) === 0;
|
|
518
|
+
const normalizeEnvironment = (environment) => Object.fromEntries(Object.entries(environment ?? {}).map(([key, value]) => [
|
|
519
|
+
key,
|
|
520
|
+
Redacted.isRedacted(value) ? Redacted.value(value) : value,
|
|
521
|
+
]));
|
|
522
|
+
const normalizeDuration = (input) => {
|
|
523
|
+
if (!input)
|
|
524
|
+
return undefined;
|
|
525
|
+
const duration = Duration.fromInputUnsafe(input);
|
|
526
|
+
// Docker parses `--health-*` durations with Go's `time.ParseDuration`, which
|
|
527
|
+
// requires a unit suffix — a bare nanosecond count is rejected with "missing
|
|
528
|
+
// unit in duration". `ns` is the lossless Go-duration rendering of the nanos.
|
|
529
|
+
return `${Duration.toNanosUnsafe(duration).toString()}ns`;
|
|
530
|
+
};
|
|
531
|
+
//# sourceMappingURL=Container.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Container.js","sourceRoot":"","sources":["../src/Container.ts"],"names":[],"mappings":"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"}
|
package/lib/Context.d.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import * as Provider from "alchemy/Provider";
|
|
2
|
+
import { Resource } from "alchemy/Resource";
|
|
3
|
+
import { Docker } from "./Docker.ts";
|
|
4
|
+
import type { Providers } from "./Providers.ts";
|
|
5
|
+
export interface ContextProps {
|
|
6
|
+
/**
|
|
7
|
+
* Context name.
|
|
8
|
+
*
|
|
9
|
+
* @default Generated from stack, stage, logical id, and instance id.
|
|
10
|
+
*/
|
|
11
|
+
name?: string;
|
|
12
|
+
/** Human-readable description displayed in `docker context ls`. */
|
|
13
|
+
description?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Docker endpoint spec, for example `host=ssh://user@host`.
|
|
16
|
+
*
|
|
17
|
+
* Connection metadata only — never credentials. SSH endpoints
|
|
18
|
+
* authenticate with keys/agent (Docker rejects passwords in the URL), and
|
|
19
|
+
* TLS endpoints reference cert/key material by file path
|
|
20
|
+
* (`host=tcp://...,ca=...,cert=...,key=...`), not by value. Docker itself
|
|
21
|
+
* stores this spec in plaintext under `~/.docker/contexts`.
|
|
22
|
+
*/
|
|
23
|
+
docker?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface Context extends Resource<"Docker.Context", ContextProps, {
|
|
26
|
+
/** Docker context name (contexts are identified by name). */
|
|
27
|
+
id: string;
|
|
28
|
+
/** Docker context name. */
|
|
29
|
+
name: string;
|
|
30
|
+
/** Human-readable description. */
|
|
31
|
+
description: string;
|
|
32
|
+
/** Docker endpoint the context targets. */
|
|
33
|
+
docker?: string;
|
|
34
|
+
}, never, Providers> {
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A named Docker CLI context — a pointer to a Docker engine (local socket,
|
|
38
|
+
* SSH host, or TCP endpoint) that other Docker resources deploy through.
|
|
39
|
+
*
|
|
40
|
+
* Pass the context (or its name) to any Docker resource's `context` prop to
|
|
41
|
+
* run that resource's operations against the referenced engine instead of the
|
|
42
|
+
* default one. Changing the endpoint updates the context in place; renaming
|
|
43
|
+
* it, or clearing a previously-set endpoint, replaces it.
|
|
44
|
+
*
|
|
45
|
+
*
|
|
46
|
+
* ### Creating Contexts
|
|
47
|
+
* **Example:** Remote engine over SSH
|
|
48
|
+
* ```typescript
|
|
49
|
+
* const vps = yield* Docker.Context("vps", {
|
|
50
|
+
* docker: "host=ssh://deploy@example.com",
|
|
51
|
+
* description: "production swarm manager",
|
|
52
|
+
* });
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* ### Using a Context
|
|
56
|
+
* **Example:** Deploy resources through the context
|
|
57
|
+
* ```typescript
|
|
58
|
+
* const vps = yield* Docker.Context("vps", {
|
|
59
|
+
* docker: "host=ssh://deploy@example.com",
|
|
60
|
+
* });
|
|
61
|
+
* const network = yield* Docker.Network("app-net", {
|
|
62
|
+
* context: vps,
|
|
63
|
+
* driver: "overlay",
|
|
64
|
+
* });
|
|
65
|
+
* const app = yield* Docker.Service("app", {
|
|
66
|
+
* context: vps,
|
|
67
|
+
* image: "nginx:alpine",
|
|
68
|
+
* networks: [network.name],
|
|
69
|
+
* });
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* **Example:** Local development vs production
|
|
73
|
+
* ```typescript
|
|
74
|
+
* const dev = yield* Alchemy.ALCHEMY_DEV;
|
|
75
|
+
* const context = yield* Docker.Context("target", {
|
|
76
|
+
* name: dev ? "local" : "vps",
|
|
77
|
+
* docker: dev ? undefined : "host=ssh://deploy@example.com",
|
|
78
|
+
* });
|
|
79
|
+
* ```
|
|
80
|
+
*
|
|
81
|
+
* @resource
|
|
82
|
+
* @product Context
|
|
83
|
+
*/
|
|
84
|
+
export declare const Context: import("alchemy/Resource").ResourceClass<Context>;
|
|
85
|
+
export declare const ContextProvider: () => import("effect/Layer").Layer<Provider.Provider<Context>, never, Docker | import("alchemy/Stack").Stack | import("alchemy/Stage").Stage>;
|
|
86
|
+
//# sourceMappingURL=Context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Context.d.ts","sourceRoot":"","sources":["../src/Context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,OAAO,EAAE,MAAM,EAAsB,MAAM,aAAa,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,OAAQ,SAAQ,QAAQ,CACvC,gBAAgB,EAChB,YAAY,EACZ;IACE,6DAA6D;IAC7D,EAAE,EAAE,MAAM,CAAC;IACX,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,EACD,KAAK,EACL,SAAS,CACV;CAAG;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,eAAO,MAAM,OAAO,mDAAsC,CAAC;AAE3D,eAAO,MAAM,eAAe,+IA8FzB,CAAC"}
|