@alchemy.run/docker 0.0.0-stage → 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 +50 -2
- 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 +72 -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/src/Context.ts
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { deepEqual, isResolved } from "alchemy/Diff";
|
|
2
|
+
import * as Provider from "alchemy/Provider";
|
|
3
|
+
import { Resource } from "alchemy/Resource";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Equal from "effect/Equal";
|
|
6
|
+
import { Docker, dockerPhysicalName } from "./Docker.ts";
|
|
7
|
+
import type { Providers } from "./Providers.ts";
|
|
8
|
+
|
|
9
|
+
export interface ContextProps {
|
|
10
|
+
/**
|
|
11
|
+
* Context name.
|
|
12
|
+
*
|
|
13
|
+
* @default Generated from stack, stage, logical id, and instance id.
|
|
14
|
+
*/
|
|
15
|
+
name?: string;
|
|
16
|
+
/** Human-readable description displayed in `docker context ls`. */
|
|
17
|
+
description?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Docker endpoint spec, for example `host=ssh://user@host`.
|
|
20
|
+
*
|
|
21
|
+
* Connection metadata only — never credentials. SSH endpoints
|
|
22
|
+
* authenticate with keys/agent (Docker rejects passwords in the URL), and
|
|
23
|
+
* TLS endpoints reference cert/key material by file path
|
|
24
|
+
* (`host=tcp://...,ca=...,cert=...,key=...`), not by value. Docker itself
|
|
25
|
+
* stores this spec in plaintext under `~/.docker/contexts`.
|
|
26
|
+
*/
|
|
27
|
+
docker?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface Context extends Resource<
|
|
31
|
+
"Docker.Context",
|
|
32
|
+
ContextProps,
|
|
33
|
+
{
|
|
34
|
+
/** Docker context name (contexts are identified by name). */
|
|
35
|
+
id: string;
|
|
36
|
+
/** Docker context name. */
|
|
37
|
+
name: string;
|
|
38
|
+
/** Human-readable description. */
|
|
39
|
+
description: string;
|
|
40
|
+
/** Docker endpoint the context targets. */
|
|
41
|
+
docker?: string;
|
|
42
|
+
},
|
|
43
|
+
never,
|
|
44
|
+
Providers
|
|
45
|
+
> {}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* A named Docker CLI context — a pointer to a Docker engine (local socket,
|
|
49
|
+
* SSH host, or TCP endpoint) that other Docker resources deploy through.
|
|
50
|
+
*
|
|
51
|
+
* Pass the context (or its name) to any Docker resource's `context` prop to
|
|
52
|
+
* run that resource's operations against the referenced engine instead of the
|
|
53
|
+
* default one. Changing the endpoint updates the context in place; renaming
|
|
54
|
+
* it, or clearing a previously-set endpoint, replaces it.
|
|
55
|
+
*
|
|
56
|
+
*
|
|
57
|
+
* ### Creating Contexts
|
|
58
|
+
* **Example:** Remote engine over SSH
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const vps = yield* Docker.Context("vps", {
|
|
61
|
+
* docker: "host=ssh://deploy@example.com",
|
|
62
|
+
* description: "production swarm manager",
|
|
63
|
+
* });
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* ### Using a Context
|
|
67
|
+
* **Example:** Deploy resources through the context
|
|
68
|
+
* ```typescript
|
|
69
|
+
* const vps = yield* Docker.Context("vps", {
|
|
70
|
+
* docker: "host=ssh://deploy@example.com",
|
|
71
|
+
* });
|
|
72
|
+
* const network = yield* Docker.Network("app-net", {
|
|
73
|
+
* context: vps,
|
|
74
|
+
* driver: "overlay",
|
|
75
|
+
* });
|
|
76
|
+
* const app = yield* Docker.Service("app", {
|
|
77
|
+
* context: vps,
|
|
78
|
+
* image: "nginx:alpine",
|
|
79
|
+
* networks: [network.name],
|
|
80
|
+
* });
|
|
81
|
+
* ```
|
|
82
|
+
*
|
|
83
|
+
* **Example:** Local development vs production
|
|
84
|
+
* ```typescript
|
|
85
|
+
* const dev = yield* Alchemy.ALCHEMY_DEV;
|
|
86
|
+
* const context = yield* Docker.Context("target", {
|
|
87
|
+
* name: dev ? "local" : "vps",
|
|
88
|
+
* docker: dev ? undefined : "host=ssh://deploy@example.com",
|
|
89
|
+
* });
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @resource
|
|
93
|
+
* @product Context
|
|
94
|
+
*/
|
|
95
|
+
export const Context = Resource<Context>("Docker.Context");
|
|
96
|
+
|
|
97
|
+
export const ContextProvider = () =>
|
|
98
|
+
Provider.effect(
|
|
99
|
+
Context,
|
|
100
|
+
Effect.gen(function* () {
|
|
101
|
+
const docker = yield* Docker;
|
|
102
|
+
|
|
103
|
+
const inspect = (nameOrId: string) =>
|
|
104
|
+
docker.context
|
|
105
|
+
.inspect(nameOrId)
|
|
106
|
+
.pipe(Effect.catchReason("PlatformError", "NotFound", () => Effect.undefined));
|
|
107
|
+
|
|
108
|
+
return Context.Provider.of({
|
|
109
|
+
list: () => Effect.succeed([]),
|
|
110
|
+
read: Effect.fn(function* ({ id, instanceId, olds, output }) {
|
|
111
|
+
const name = yield* dockerPhysicalName(id, olds, instanceId);
|
|
112
|
+
const live = yield* inspect(name);
|
|
113
|
+
if (!live) return undefined;
|
|
114
|
+
const attrs = toContextAttributes(live);
|
|
115
|
+
if (output) return attrs;
|
|
116
|
+
return attrs;
|
|
117
|
+
}),
|
|
118
|
+
diff: Effect.fn(function* ({ id, instanceId, news, olds, output }) {
|
|
119
|
+
if (!isResolved(news)) return undefined;
|
|
120
|
+
|
|
121
|
+
const oldDesired = yield* normalizeDesired(id, olds, instanceId);
|
|
122
|
+
const newDesired = yield* normalizeDesired(id, news, instanceId);
|
|
123
|
+
|
|
124
|
+
if (oldDesired.name !== newDesired.name) {
|
|
125
|
+
return { action: "replace" as const, deleteFirst: true };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// `docker context update` can set docker host, but cannot reliably
|
|
129
|
+
// clear it once set. Recreate when transitioning to no docker.
|
|
130
|
+
if (oldDesired.docker && !newDesired.docker) {
|
|
131
|
+
return { action: "replace" as const, deleteFirst: true };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!Equal.equals(oldDesired, newDesired)) {
|
|
135
|
+
return { action: "update" as const };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (output && !(yield* inspect(output.id))) {
|
|
139
|
+
return { action: "update" as const };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return { action: "noop" as const };
|
|
143
|
+
}),
|
|
144
|
+
reconcile: Effect.fn(function* ({ id, instanceId, news, olds, output }) {
|
|
145
|
+
const desired = yield* normalizeDesired(id, news, instanceId);
|
|
146
|
+
|
|
147
|
+
if (output && olds) {
|
|
148
|
+
const oldDesired = yield* normalizeDesired(id, olds, instanceId);
|
|
149
|
+
if (deepEqual(oldDesired, desired)) {
|
|
150
|
+
const current = yield* inspect(output.id);
|
|
151
|
+
if (current) {
|
|
152
|
+
return toContextAttributes(current);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const existing = output ? yield* inspect(output.id) : yield* inspect(desired.name);
|
|
158
|
+
|
|
159
|
+
if (!existing) {
|
|
160
|
+
const createArgs = {
|
|
161
|
+
name: desired.name,
|
|
162
|
+
...(desired.docker ? { docker: desired.docker } : {}),
|
|
163
|
+
...(desired.description.length > 0 ? { description: desired.description } : {}),
|
|
164
|
+
};
|
|
165
|
+
yield* docker.context.create(createArgs);
|
|
166
|
+
return toContextAttributes(yield* docker.context.inspect(desired.name));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const current = toContextAttributes(existing);
|
|
170
|
+
const needsUpdate =
|
|
171
|
+
current.description !== desired.description || current.docker !== desired.docker;
|
|
172
|
+
|
|
173
|
+
if (needsUpdate) {
|
|
174
|
+
yield* docker.context.update({
|
|
175
|
+
name: desired.name,
|
|
176
|
+
...(desired.docker ? { docker: desired.docker } : {}),
|
|
177
|
+
description: desired.description,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return toContextAttributes(yield* docker.context.inspect(desired.name));
|
|
182
|
+
}),
|
|
183
|
+
delete: Effect.fn(({ output }) =>
|
|
184
|
+
docker.context.remove(output.id, true).pipe(
|
|
185
|
+
Effect.catchReason("PlatformError", "NotFound", () => Effect.void),
|
|
186
|
+
Effect.as(undefined),
|
|
187
|
+
),
|
|
188
|
+
),
|
|
189
|
+
});
|
|
190
|
+
}),
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
// Every prop is optional, so `Docker.Context("ctx")` arrives with no props.
|
|
194
|
+
const normalizeDesired = (id: string, props: ContextProps | undefined, instanceId: string) =>
|
|
195
|
+
dockerPhysicalName(id, props, instanceId).pipe(
|
|
196
|
+
Effect.map((name) => ({
|
|
197
|
+
name,
|
|
198
|
+
description: normalizeDescription(props?.description),
|
|
199
|
+
docker: normalizeDocker(props?.docker),
|
|
200
|
+
})),
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
const normalizeDescription = (description: string | undefined): string => description?.trim() ?? "";
|
|
204
|
+
|
|
205
|
+
const normalizeDocker = (docker: string | undefined): string | undefined => {
|
|
206
|
+
const value = docker?.trim();
|
|
207
|
+
return value && value.length > 0 ? value : undefined;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const toContextAttributes = (context: Docker.Context): Context["Attributes"] => ({
|
|
211
|
+
id: context.Name,
|
|
212
|
+
name: context.Name,
|
|
213
|
+
description: context.Metadata?.Description ?? "",
|
|
214
|
+
docker: context.Endpoints?.docker,
|
|
215
|
+
});
|