@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.
Files changed (98) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +50 -2
  3. package/lib/Bootstrap.d.ts +7 -0
  4. package/lib/Bootstrap.d.ts.map +1 -0
  5. package/lib/Bootstrap.js +25 -0
  6. package/lib/Bootstrap.js.map +1 -0
  7. package/lib/BuildHash.d.ts +47 -0
  8. package/lib/BuildHash.d.ts.map +1 -0
  9. package/lib/BuildHash.js +165 -0
  10. package/lib/BuildHash.js.map +1 -0
  11. package/lib/Container.d.ts +352 -0
  12. package/lib/Container.d.ts.map +1 -0
  13. package/lib/Container.js +531 -0
  14. package/lib/Container.js.map +1 -0
  15. package/lib/Context.d.ts +86 -0
  16. package/lib/Context.d.ts.map +1 -0
  17. package/lib/Context.js +146 -0
  18. package/lib/Context.js.map +1 -0
  19. package/lib/Docker.d.ts +448 -0
  20. package/lib/Docker.d.ts.map +1 -0
  21. package/lib/Docker.js +416 -0
  22. package/lib/Docker.js.map +1 -0
  23. package/lib/Dockerfile.d.ts +72 -0
  24. package/lib/Dockerfile.d.ts.map +1 -0
  25. package/lib/Dockerfile.js +12 -0
  26. package/lib/Dockerfile.js.map +1 -0
  27. package/lib/HealthcheckCommand.d.ts +17 -0
  28. package/lib/HealthcheckCommand.d.ts.map +1 -0
  29. package/lib/HealthcheckCommand.js +33 -0
  30. package/lib/HealthcheckCommand.js.map +1 -0
  31. package/lib/Image.d.ts +151 -0
  32. package/lib/Image.d.ts.map +1 -0
  33. package/lib/Image.js +192 -0
  34. package/lib/Image.js.map +1 -0
  35. package/lib/ImageSource.d.ts +234 -0
  36. package/lib/ImageSource.d.ts.map +1 -0
  37. package/lib/ImageSource.js +469 -0
  38. package/lib/ImageSource.js.map +1 -0
  39. package/lib/Network.d.ts +70 -0
  40. package/lib/Network.d.ts.map +1 -0
  41. package/lib/Network.js +111 -0
  42. package/lib/Network.js.map +1 -0
  43. package/lib/Providers.d.ts +21 -0
  44. package/lib/Providers.d.ts.map +1 -0
  45. package/lib/Providers.js +26 -0
  46. package/lib/Providers.js.map +1 -0
  47. package/lib/Registry.d.ts +47 -0
  48. package/lib/Registry.d.ts.map +1 -0
  49. package/lib/Registry.js +67 -0
  50. package/lib/Registry.js.map +1 -0
  51. package/lib/RegistryError.d.ts +21 -0
  52. package/lib/RegistryError.d.ts.map +1 -0
  53. package/lib/RegistryError.js +28 -0
  54. package/lib/RegistryError.js.map +1 -0
  55. package/lib/RemoteImage.d.ts +118 -0
  56. package/lib/RemoteImage.d.ts.map +1 -0
  57. package/lib/RemoteImage.js +164 -0
  58. package/lib/RemoteImage.js.map +1 -0
  59. package/lib/Service.d.ts +478 -0
  60. package/lib/Service.d.ts.map +1 -0
  61. package/lib/Service.js +588 -0
  62. package/lib/Service.js.map +1 -0
  63. package/lib/ServiceImage.d.ts +78 -0
  64. package/lib/ServiceImage.d.ts.map +1 -0
  65. package/lib/ServiceImage.js +162 -0
  66. package/lib/ServiceImage.js.map +1 -0
  67. package/lib/Swarm.d.ts +119 -0
  68. package/lib/Swarm.d.ts.map +1 -0
  69. package/lib/Swarm.js +148 -0
  70. package/lib/Swarm.js.map +1 -0
  71. package/lib/Volume.d.ts +94 -0
  72. package/lib/Volume.d.ts.map +1 -0
  73. package/lib/Volume.js +133 -0
  74. package/lib/Volume.js.map +1 -0
  75. package/lib/index.d.ts +14 -0
  76. package/lib/index.d.ts.map +1 -0
  77. package/lib/index.js +13 -0
  78. package/lib/index.js.map +1 -0
  79. package/package.json +72 -3
  80. package/src/Bootstrap.ts +51 -0
  81. package/src/BuildHash.ts +255 -0
  82. package/src/Container.ts +792 -0
  83. package/src/Context.ts +215 -0
  84. package/src/Docker.ts +1079 -0
  85. package/src/Dockerfile.ts +81 -0
  86. package/src/HealthcheckCommand.ts +35 -0
  87. package/src/Image.ts +293 -0
  88. package/src/ImageSource.ts +769 -0
  89. package/src/Network.ts +169 -0
  90. package/src/Providers.ts +46 -0
  91. package/src/Registry.ts +91 -0
  92. package/src/RegistryError.ts +48 -0
  93. package/src/RemoteImage.ts +255 -0
  94. package/src/Service.ts +1080 -0
  95. package/src/ServiceImage.ts +252 -0
  96. package/src/Swarm.ts +221 -0
  97. package/src/Volume.ts +193 -0
  98. package/src/index.ts +17 -0
@@ -0,0 +1,78 @@
1
+ import { AlchemyContext } from "alchemy/AlchemyContext";
2
+ import * as Bundle from "alchemy/Bundle/Bundle";
3
+ import * as Effect from "effect/Effect";
4
+ import { Docker } from "./Docker.ts";
5
+ /**
6
+ * INTERNAL — image machinery for the effectful `Docker.Service` platform.
7
+ *
8
+ * NOT exported from the Docker barrel. Bundles a `main` Effect program with
9
+ * rolldown, bakes it into a generated bun Dockerfile, and builds the image
10
+ * with the Docker CLI against the service's Docker context — so the image is
11
+ * present in the exact engine the Swarm service schedules from. There is no
12
+ * registry push: the built image is content-addressed (`<name>:<hash>`) and
13
+ * only rebuilt when the bundle, Dockerfile, or environment image changes.
14
+ *
15
+ * Multi-node swarms need the image on every node; for those, build with
16
+ * `Docker.Image` + a `registry` and pass the pushed ref as `image` instead.
17
+ */
18
+ /** The props subset that describes a bundled `main` service image. */
19
+ export interface BundledServiceSource {
20
+ main: string;
21
+ image?: string;
22
+ handler?: string;
23
+ port?: number;
24
+ /**
25
+ * Bundler configuration for `main`. Unused code is tree-shaken.
26
+ * `effect`, alchemy, and `@distilled.cloud` are marked pure so unused
27
+ * parts prune more aggressively. List extra packages with
28
+ * `pure.packages`, or disable with `pure: false`.
29
+ */
30
+ build?: Bundle.BundleConfig;
31
+ }
32
+ /** The resolved (built) local image. */
33
+ export interface ResolvedServiceImage {
34
+ /** Full image reference, `<name>:<codeHash>`. */
35
+ imageRef: string;
36
+ /** Content hash identifying the image (also the image tag). */
37
+ codeHash: string;
38
+ }
39
+ /**
40
+ * The generated entry for `Docker.Service` containers: a shim importing only
41
+ * `@alchemy.run/docker/Bootstrap` plus the user's `main` — see that
42
+ * module for why the entry never imports alchemy's own dependencies.
43
+ */
44
+ export declare const makeServiceBunBootstrap: (handler: string) => (importPath: string) => string;
45
+ /**
46
+ * Init-time constructor for the service-image resolver. Resolves the services
47
+ * that are only available at provider-layer construction (Docker, the
48
+ * `.alchemy` directory, the rolldown virtual-entry plugin) and returns
49
+ * `resolve` (build the image if its content hash isn't present in the target
50
+ * engine) and `hash` (content hash only, for `diff`).
51
+ */
52
+ export declare const makeServiceImage: Effect.Effect<{
53
+ resolve: (options: {
54
+ /** Logical resource id — keys the stable build-context directory. */
55
+ id: string;
56
+ source: BundledServiceSource;
57
+ /** Image repository name (the service's physical name). */
58
+ name: string;
59
+ /** Docker context the swarm service is deployed to. */
60
+ context: string | undefined;
61
+ isExternal?: boolean;
62
+ /** Plan-status session used to emit build progress notes. */
63
+ session: {
64
+ note: (message: string) => Effect.Effect<void>;
65
+ };
66
+ }) => Effect.Effect<{
67
+ imageRef: string;
68
+ codeHash: string;
69
+ }, Bundle.BundleError | import("./RegistryError.ts").DockerImagePublicationError, import("effect/FileSystem").FileSystem | import("effect/Path").Path | import("alchemy/Stack").Stack | import("alchemy/Stage").Stage>;
70
+ hash: (options: {
71
+ source: BundledServiceSource;
72
+ isExternal?: boolean;
73
+ }) => Effect.Effect<string, Bundle.BundleError | import("effect/PlatformError").PlatformError, import("effect/FileSystem").FileSystem | import("effect/Path").Path>;
74
+ }, never, AlchemyContext | Docker | import("effect/Path").Path>;
75
+ /** The resolver service returned by {@link makeServiceImage}. */
76
+ export interface ServiceImage extends Effect.Success<typeof makeServiceImage> {
77
+ }
78
+ //# sourceMappingURL=ServiceImage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ServiceImage.d.ts","sourceRoot":"","sources":["../src/ServiceImage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,MAAM,MAAM,uBAAuB,CAAC;AAGhD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;;;;;;;GAYG;AAEH,sEAAsE;AACtE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,YAAY,CAAC;CAC7B;AAED,wCAAwC;AACxC,MAAM,WAAW,oBAAoB;IACnC,iDAAiD;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,YACxB,MAAM,kBACH,MAAM,KAAG,MAMvB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB;;QAwHzB,qEAAqE;YACjE,MAAM;gBACF,oBAAoB;QAC5B,2DAA2D;cACrD,MAAM;QACZ,uDAAuD;iBAC9C,MAAM,GAAG,SAAS;qBACd,OAAO;QACpB,6DAA6D;iBACpD;YAAE,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;SAAE;;;;;;gBA4CnD,oBAAoB;qBACf,OAAO;;+DAOtB,CAAC;AAEH,iEAAiE;AACjE,MAAM,WAAW,YAAa,SAAQ,MAAM,CAAC,OAAO,CAAC,OAAO,gBAAgB,CAAC;CAAG"}
@@ -0,0 +1,162 @@
1
+ import { AlchemyContext } from "alchemy/AlchemyContext";
2
+ import * as Bundle from "alchemy/Bundle/Bundle";
3
+ import { findCwdForBundle, getStableContextDir, resolveMainPath } from "alchemy/Bundle/TempRoot";
4
+ import { sha256Object } from "alchemy/Util/sha256";
5
+ import * as Effect from "effect/Effect";
6
+ import { Docker } from "./Docker.js";
7
+ /**
8
+ * The generated entry for `Docker.Service` containers: a shim importing only
9
+ * `@alchemy.run/docker/Bootstrap` plus the user's `main` — see that
10
+ * module for why the entry never imports alchemy's own dependencies.
11
+ */
12
+ export const makeServiceBunBootstrap = (handler) => (importPath) => `
13
+ import { bootstrap } from "@alchemy.run/docker/Bootstrap";
14
+ import { ${handler} as entrypoint } from ${JSON.stringify(importPath)};
15
+
16
+ await bootstrap(entrypoint);
17
+ `;
18
+ /**
19
+ * Init-time constructor for the service-image resolver. Resolves the services
20
+ * that are only available at provider-layer construction (Docker, the
21
+ * `.alchemy` directory, the rolldown virtual-entry plugin) and returns
22
+ * `resolve` (build the image if its content hash isn't present in the target
23
+ * engine) and `hash` (content hash only, for `diff`).
24
+ */
25
+ export const makeServiceImage = Effect.gen(function* () {
26
+ const docker = yield* Docker;
27
+ const { dotAlchemy } = yield* AlchemyContext;
28
+ const virtualEntryPlugin = yield* Bundle.virtualEntryPlugin;
29
+ /** Bundle the Effect program behind a `main` source. */
30
+ const bundleProgram = Effect.fn(function* (options) {
31
+ const { source } = options;
32
+ const realMain = yield* resolveMainPath(source.main);
33
+ const cwd = yield* findCwdForBundle(realMain);
34
+ const bootstrap = makeServiceBunBootstrap(source.handler ?? "default");
35
+ const buildBundle = Effect.fn(function* (entry, plugins) {
36
+ return yield* Bundle.build({
37
+ ...source.build?.input,
38
+ input: entry,
39
+ cwd,
40
+ platform: "node",
41
+ // The container runs on `bun`; keep `bun`/`bun:*` external (the
42
+ // runtime provides them) and resolve the `bun` export condition
43
+ // so `@effect/platform-bun` picks its Bun implementations.
44
+ external: [
45
+ "bun",
46
+ "bun:*",
47
+ ...(source.build?.input?.external ?? []),
48
+ ],
49
+ resolve: {
50
+ conditionNames: [...Bundle.BUN_CONDITION_NAMES],
51
+ ...source.build?.input?.resolve,
52
+ },
53
+ plugins: [source.build?.input?.plugins, plugins],
54
+ }, {
55
+ ...source.build?.output,
56
+ format: "esm",
57
+ sourcemap: source.build?.output?.sourcemap ?? false,
58
+ minify: source.build?.output?.minify ?? false,
59
+ entryFileNames: "index.mjs",
60
+ }, source.build);
61
+ });
62
+ const bundleOutput = options.isExternal
63
+ ? yield* buildBundle(realMain)
64
+ : yield* buildBundle(realMain, virtualEntryPlugin(bootstrap));
65
+ // Return every emitted file (entry + shared chunks). Dynamic imports in
66
+ // the Bun HTTP server split into chunks; dropping any of them crashes the
67
+ // container with `Cannot find module './chunk-XXX.js'`.
68
+ const files = bundleOutput.files.map((file) => ({
69
+ path: file.path,
70
+ content: typeof file.content === "string" ? new TextEncoder().encode(file.content) : file.content,
71
+ }));
72
+ return { files, hash: bundleOutput.hash };
73
+ });
74
+ /**
75
+ * Generated Dockerfile for a bundled `main` program. The environment
76
+ * preamble is the `image` ref or the default bun base (`oven/bun` is
77
+ * Docker-Hub only).
78
+ */
79
+ const generateDockerfile = (source) => {
80
+ const lines = [
81
+ `FROM ${source.image ?? "oven/bun:1"}`,
82
+ `WORKDIR /app`,
83
+ `COPY index.mjs /app/index.mjs`,
84
+ // Copy any additional rolldown chunks (`chunk-XXX.js`, …). Non-trivial
85
+ // bundles always emit at least one; minimal bundles emit none and the
86
+ // COPY no-ops.
87
+ `COPY *.js /app/`,
88
+ ];
89
+ if (source.port !== undefined) {
90
+ lines.push(`ENV PORT=${String(source.port)}`, `EXPOSE ${String(source.port)}`);
91
+ }
92
+ lines.push(`ENTRYPOINT ["bun", "/app/index.mjs"]`);
93
+ return `${lines.join("\n")}\n`;
94
+ };
95
+ /**
96
+ * Bundle a `main` source and compute its content-addressed code hash. The
97
+ * hash covers the full image identity — the bundle output (bootstrap entry
98
+ * included, so bootstrap-template changes invalidate it) and the generated
99
+ * Dockerfile — so `diff` and `resolve` always agree.
100
+ */
101
+ const computeCodeHash = Effect.fn(function* (options) {
102
+ const bundled = yield* bundleProgram(options);
103
+ const dockerfile = generateDockerfile(options.source);
104
+ const codeHash = (yield* sha256Object({
105
+ bundleHash: bundled.hash,
106
+ dockerfile,
107
+ })).slice(0, 16);
108
+ return { bundled, dockerfile, codeHash };
109
+ });
110
+ /** Observe a locally-present tag in the target engine. Missing → undefined. */
111
+ const imageExists = (imageRef, context) => docker.image.inspect(imageRef, context).pipe(Effect.map(() => true), Effect.catchReason("PlatformError", "NotFound", () => Effect.succeed(false)));
112
+ /**
113
+ * Resolve the image for a bundled service: compute the content-addressed
114
+ * tag, then build only when that exact tag is not already present in the
115
+ * target Docker context (crash-safe convergence).
116
+ */
117
+ const resolve = Effect.fn(function* (options) {
118
+ const { id, source, session } = options;
119
+ yield* session.note(`Bundling ${id} program...`);
120
+ const { bundled, dockerfile, codeHash } = yield* computeCodeHash({
121
+ source,
122
+ isExternal: options.isExternal,
123
+ });
124
+ const imageRef = `${options.name}:${codeHash}`;
125
+ if (yield* imageExists(imageRef, options.context)) {
126
+ return { imageRef, codeHash };
127
+ }
128
+ const realMain = yield* resolveMainPath(source.main);
129
+ const contextDir = yield* getStableContextDir(realMain, dotAlchemy, `${id}-image`);
130
+ yield* docker.materialize({
131
+ context: contextDir,
132
+ dockerfile,
133
+ // Entry chunk becomes `index.mjs`; all other chunks keep their emitted
134
+ // `*.js` names so the entry's relative imports resolve.
135
+ files: bundled.files.map((file, index) => ({
136
+ path: index === 0 ? "index.mjs" : file.path,
137
+ content: file.content,
138
+ })),
139
+ });
140
+ yield* session.note(`Building container image ${imageRef}...`);
141
+ yield* docker.image.build({
142
+ context: contextDir,
143
+ tag: imageRef,
144
+ engineContext: options.context,
145
+ });
146
+ yield* session.note(`Built ${imageRef}`);
147
+ return { imageRef, codeHash };
148
+ });
149
+ /**
150
+ * Content hash without building the image. Runs the bundler (bootstrap
151
+ * entry included) so the hash reflects the exact image `resolve` would
152
+ * build — bootstrap-template changes and user-code edits both surface as
153
+ * drift. The provider calls this from `diff` and compares against
154
+ * `output.code.hash`.
155
+ */
156
+ const hash = Effect.fn(function* (options) {
157
+ const { codeHash } = yield* computeCodeHash(options);
158
+ return codeHash;
159
+ });
160
+ return { resolve, hash };
161
+ });
162
+ //# sourceMappingURL=ServiceImage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ServiceImage.js","sourceRoot":"","sources":["../src/ServiceImage.ts"],"names":[],"mappings":"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"}
package/lib/Swarm.d.ts ADDED
@@ -0,0 +1,119 @@
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 SwarmProps {
6
+ /** Docker context name or context resource of the engine to initialize. */
7
+ context?: Docker.ContextRef;
8
+ /**
9
+ * Address advertised to other swarm members, e.g. `"10.0.0.1"` or
10
+ * `"eth0"`. Required by Docker when the host has several network
11
+ * interfaces. Applied only when the swarm is first initialized.
12
+ */
13
+ advertiseAddr?: string;
14
+ /**
15
+ * Listen address for swarm traffic, e.g. `"0.0.0.0:2377"`. Applied only
16
+ * when the swarm is first initialized.
17
+ */
18
+ listenAddr?: string;
19
+ /**
20
+ * Address pools for global-scope (overlay) networks, e.g.
21
+ * `["10.20.0.0/16"]`. Applied only when the swarm is first initialized.
22
+ */
23
+ defaultAddrPool?: string[];
24
+ /**
25
+ * CIDR mask length for each subnet carved from `defaultAddrPool`.
26
+ * Applied only when the swarm is first initialized.
27
+ *
28
+ * @default 24
29
+ */
30
+ subnetSize?: number;
31
+ }
32
+ export interface Swarm extends Resource<"Docker.Swarm", SwarmProps, {
33
+ /** Swarm cluster id. */
34
+ id: string;
35
+ /** This node's id in the swarm (a manager). */
36
+ nodeId: string;
37
+ /** Docker context the swarm was initialized on. */
38
+ context?: string;
39
+ /** Number of manager nodes. */
40
+ managers: number;
41
+ /** Number of nodes in the swarm. */
42
+ nodes: number;
43
+ }, never, Providers> {
44
+ }
45
+ /**
46
+ * Swarm mode on a Docker engine — an idempotent `docker swarm init`.
47
+ *
48
+ * Turns the engine behind the given `context` (or the local engine) into a
49
+ * single-node swarm: the node becomes a manager that can run `Docker.Service`
50
+ * workloads. Regular (non-swarm) docker usage of the engine is unaffected.
51
+ *
52
+ * Pass the swarm as the `context` of a `Docker.Service` or overlay
53
+ * `Docker.Network`: the workload then deploys after the swarm exists and
54
+ * inherits its Docker context.
55
+ *
56
+ * An engine that is already in swarm mode is treated as foreign — adopt it
57
+ * with `adopt(true)` (or `--adopt`) to manage it. Destroying the resource
58
+ * dissolves the node's swarm membership (`docker swarm leave --force`),
59
+ * which stops every service running on it — services managed by the same
60
+ * stack are destroyed first via their dependency edges.
61
+ *
62
+ * Growing the cluster beyond one node is host-level setup: run
63
+ * `docker swarm join --token <token> <manager-ip>:2377` on each additional
64
+ * machine (`docker swarm join-token worker` on the manager prints the
65
+ * command). Alchemy manages the swarm's workloads through the manager.
66
+ * ### Creating a Swarm
67
+ * **Example:** Local single-node swarm
68
+ * ```typescript
69
+ * const swarm = yield* Docker.Swarm("swarm");
70
+ * ```
71
+ *
72
+ * **Example:** Remote engine over SSH
73
+ * ```typescript
74
+ * const vps = yield* Docker.Context("vps", {
75
+ * docker: "host=ssh://deploy@example.com",
76
+ * });
77
+ * const swarm = yield* Docker.Swarm("swarm", {
78
+ * context: vps,
79
+ * advertiseAddr: "10.0.0.1",
80
+ * });
81
+ * ```
82
+ *
83
+ * ### Using an Existing Swarm
84
+ * **Example:** Reference without owning
85
+ * ```typescript
86
+ * // Services don't require a Swarm resource — point them at an engine that
87
+ * // is already a manager and the swarm's lifecycle stays external: destroy
88
+ * // removes the services, never the swarm.
89
+ * const web = yield* Docker.Service("web", {
90
+ * context: vps,
91
+ * image: "nginx:alpine",
92
+ * });
93
+ * ```
94
+ *
95
+ * **Example:** Adopt an already-initialized engine
96
+ * ```typescript
97
+ * // Adoption makes the swarm part of the stack — destroy then dissolves
98
+ * // the node's membership.
99
+ * const swarm = yield* Docker.Swarm("swarm").pipe(adopt(true));
100
+ * ```
101
+ *
102
+ * ### Deploying into the Swarm
103
+ * **Example:** Service ordered after the swarm
104
+ * ```typescript
105
+ * const swarm = yield* Docker.Swarm("swarm");
106
+ * const web = yield* Docker.Service("web", {
107
+ * context: swarm,
108
+ * image: "nginx:alpine",
109
+ * replicas: 2,
110
+ * ports: [{ external: 8080, internal: 80 }],
111
+ * });
112
+ * ```
113
+ *
114
+ * @resource
115
+ * @product Swarm
116
+ */
117
+ export declare const Swarm: import("alchemy/Resource").ResourceClass<Swarm>;
118
+ export declare const SwarmProvider: () => import("effect/Layer").Layer<Provider.Provider<Swarm>, never, Docker>;
119
+ //# sourceMappingURL=Swarm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Swarm.d.ts","sourceRoot":"","sources":["../src/Swarm.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,MAAM,EAAqB,MAAM,aAAa,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,WAAW,UAAU;IACzB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,KAAM,SAAQ,QAAQ,CACrC,cAAc,EACd,UAAU,EACV;IACE,wBAAwB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;CACf,EACD,KAAK,EACL,SAAS,CACV;CAAG;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuEG;AACH,eAAO,MAAM,KAAK,iDAAkC,CAAC;AAErD,eAAO,MAAM,aAAa,6EAgFvB,CAAC"}
package/lib/Swarm.js ADDED
@@ -0,0 +1,148 @@
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 * as Effect from "effect/Effect";
6
+ import { Docker, dockerContextName } from "./Docker.js";
7
+ /**
8
+ * Swarm mode on a Docker engine — an idempotent `docker swarm init`.
9
+ *
10
+ * Turns the engine behind the given `context` (or the local engine) into a
11
+ * single-node swarm: the node becomes a manager that can run `Docker.Service`
12
+ * workloads. Regular (non-swarm) docker usage of the engine is unaffected.
13
+ *
14
+ * Pass the swarm as the `context` of a `Docker.Service` or overlay
15
+ * `Docker.Network`: the workload then deploys after the swarm exists and
16
+ * inherits its Docker context.
17
+ *
18
+ * An engine that is already in swarm mode is treated as foreign — adopt it
19
+ * with `adopt(true)` (or `--adopt`) to manage it. Destroying the resource
20
+ * dissolves the node's swarm membership (`docker swarm leave --force`),
21
+ * which stops every service running on it — services managed by the same
22
+ * stack are destroyed first via their dependency edges.
23
+ *
24
+ * Growing the cluster beyond one node is host-level setup: run
25
+ * `docker swarm join --token <token> <manager-ip>:2377` on each additional
26
+ * machine (`docker swarm join-token worker` on the manager prints the
27
+ * command). Alchemy manages the swarm's workloads through the manager.
28
+ * ### Creating a Swarm
29
+ * **Example:** Local single-node swarm
30
+ * ```typescript
31
+ * const swarm = yield* Docker.Swarm("swarm");
32
+ * ```
33
+ *
34
+ * **Example:** Remote engine over SSH
35
+ * ```typescript
36
+ * const vps = yield* Docker.Context("vps", {
37
+ * docker: "host=ssh://deploy@example.com",
38
+ * });
39
+ * const swarm = yield* Docker.Swarm("swarm", {
40
+ * context: vps,
41
+ * advertiseAddr: "10.0.0.1",
42
+ * });
43
+ * ```
44
+ *
45
+ * ### Using an Existing Swarm
46
+ * **Example:** Reference without owning
47
+ * ```typescript
48
+ * // Services don't require a Swarm resource — point them at an engine that
49
+ * // is already a manager and the swarm's lifecycle stays external: destroy
50
+ * // removes the services, never the swarm.
51
+ * const web = yield* Docker.Service("web", {
52
+ * context: vps,
53
+ * image: "nginx:alpine",
54
+ * });
55
+ * ```
56
+ *
57
+ * **Example:** Adopt an already-initialized engine
58
+ * ```typescript
59
+ * // Adoption makes the swarm part of the stack — destroy then dissolves
60
+ * // the node's membership.
61
+ * const swarm = yield* Docker.Swarm("swarm").pipe(adopt(true));
62
+ * ```
63
+ *
64
+ * ### Deploying into the Swarm
65
+ * **Example:** Service ordered after the swarm
66
+ * ```typescript
67
+ * const swarm = yield* Docker.Swarm("swarm");
68
+ * const web = yield* Docker.Service("web", {
69
+ * context: swarm,
70
+ * image: "nginx:alpine",
71
+ * replicas: 2,
72
+ * ports: [{ external: 8080, internal: 80 }],
73
+ * });
74
+ * ```
75
+ *
76
+ * @resource
77
+ * @product Swarm
78
+ */
79
+ export const Swarm = Resource("Docker.Swarm");
80
+ export const SwarmProvider = () => Provider.effect(Swarm, Effect.gen(function* () {
81
+ const docker = yield* Docker;
82
+ /** Active-manager info, or undefined when the engine isn't one. */
83
+ const observe = Effect.fn(function* (context) {
84
+ const info = yield* docker.swarm.info(context);
85
+ if (info.LocalNodeState !== "active" || !info.ControlAvailable || !info.Cluster?.ID) {
86
+ return undefined;
87
+ }
88
+ return info;
89
+ });
90
+ return Swarm.Provider.of({
91
+ list: () => Effect.succeed([]),
92
+ read: Effect.fn(function* ({ olds, output }) {
93
+ const context = dockerContextName(olds?.context);
94
+ const info = yield* observe(context);
95
+ if (!info)
96
+ return undefined;
97
+ const attrs = toSwarmAttributes(info, context);
98
+ if (output)
99
+ return attrs;
100
+ // A swarm carries no labels to brand, so an already-initialized
101
+ // engine can never be proven ours — adoption is gated behind
102
+ // `--adopt` / `adopt(true)`.
103
+ return Unowned(attrs);
104
+ }),
105
+ diff: Effect.fn(function* ({ news, olds }) {
106
+ if (!isResolved(news))
107
+ return undefined;
108
+ if (dockerContextName(olds?.context) !== dockerContextName(news?.context)) {
109
+ return { action: "replace", deleteFirst: true };
110
+ }
111
+ // The remaining props only apply at `swarm init`; changing them on
112
+ // a live swarm is a no-op rather than a destructive re-init.
113
+ return { action: "noop" };
114
+ }),
115
+ reconcile: Effect.fn(function* ({ news }) {
116
+ const context = dockerContextName(news?.context);
117
+ const existing = yield* observe(context);
118
+ if (existing)
119
+ return toSwarmAttributes(existing, context);
120
+ yield* docker.swarm
121
+ .init({
122
+ context,
123
+ "advertise-addr": news?.advertiseAddr,
124
+ "listen-addr": news?.listenAddr,
125
+ "default-addr-pool": news?.defaultAddrPool,
126
+ "default-addr-pool-mask-length": news?.subnetSize,
127
+ })
128
+ .pipe(
129
+ // A concurrent init (or an interrupted prior reconcile) already
130
+ // made this node a swarm member — converge on the observation.
131
+ Effect.catchIf((error) => /already part of a swarm/i.test(String(error)), () => Effect.void));
132
+ const info = yield* observe(context);
133
+ if (!info) {
134
+ return yield* Effect.die(new Error("docker swarm init succeeded but the engine did not become an active swarm manager"));
135
+ }
136
+ return toSwarmAttributes(info, context);
137
+ }),
138
+ delete: Effect.fn(({ output }) => docker.swarm.leave(true, output.context).pipe(Effect.catchIf((error) => /not part of a swarm/i.test(String(error)), () => Effect.void), Effect.asVoid)),
139
+ });
140
+ }));
141
+ const toSwarmAttributes = (info, context) => ({
142
+ id: info.Cluster.ID,
143
+ nodeId: info.NodeID,
144
+ context,
145
+ managers: info.Managers ?? 1,
146
+ nodes: info.Nodes ?? 1,
147
+ });
148
+ //# sourceMappingURL=Swarm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Swarm.js","sourceRoot":"","sources":["../src/Swarm.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
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 VolumeLabel {
6
+ /** Label name. */
7
+ name: string;
8
+ /** Label value. */
9
+ value: string;
10
+ }
11
+ export interface VolumeProps {
12
+ /**
13
+ * Docker volume name.
14
+ *
15
+ * @default Generated from stack, stage, logical id, and instance id.
16
+ */
17
+ name?: string;
18
+ /** Volume driver. @default "local" */
19
+ driver?: string;
20
+ /** Driver-specific options. */
21
+ driverOpts?: Record<string, string>;
22
+ /** Custom metadata labels. */
23
+ labels?: Record<string, string>;
24
+ /** Docker context name or context resource. */
25
+ context?: Docker.ContextRef;
26
+ }
27
+ export interface Volume extends Resource<"Docker.Volume", VolumeProps, {
28
+ /** Docker volume name. */
29
+ id: string;
30
+ /** Docker volume name. */
31
+ name: string;
32
+ /** Volume driver. */
33
+ driver: string;
34
+ /** Driver-specific options reported by Docker. */
35
+ driverOpts: Record<string, string>;
36
+ /** Labels reported by Docker. */
37
+ labels: Record<string, string>;
38
+ /** Host mountpoint path. */
39
+ mountpoint?: string;
40
+ /** Creation timestamp in milliseconds since epoch. */
41
+ createdAt: number;
42
+ }, never, Providers> {
43
+ }
44
+ /**
45
+ * A Docker volume managed through the active Docker context.
46
+ *
47
+ * Pre-existing same-name volumes are treated as foreign until the engine is
48
+ * allowed to adopt them with `--adopt` or `adopt(true)`.
49
+ *
50
+ *
51
+ * ### Creating Volumes
52
+ * **Example:** Basic volume
53
+ * ```typescript
54
+ * const data = yield* Docker.Volume("data", {
55
+ * name: "app-data",
56
+ * });
57
+ * ```
58
+ *
59
+ * **Example:** PostgreSQL data volume
60
+ * ```typescript
61
+ * const data = yield* Docker.Volume("postgres-data");
62
+ * ```
63
+ *
64
+ * **Example:** Driver options and labels
65
+ * ```typescript
66
+ * const data = yield* Docker.Volume("db-data", {
67
+ * driver: "local",
68
+ * driverOpts: {
69
+ * type: "nfs",
70
+ * o: "addr=10.0.0.1,rw",
71
+ * device: ":/path/to/dir",
72
+ * },
73
+ * labels: {
74
+ * "com.example.usage": "database",
75
+ * },
76
+ * });
77
+ * ```
78
+ *
79
+ * ### Docker Context
80
+ * **Example:** Create a volume in a named Docker context
81
+ * ```typescript
82
+ * const data = yield* Docker.Volume("data", {
83
+ * name: "app-data",
84
+ * context: "remote-build",
85
+ * });
86
+ * ```
87
+ *
88
+ * @resource
89
+ * @product Volume
90
+ */
91
+ export declare const Volume: import("alchemy/Resource").ResourceClass<Volume>;
92
+ export declare const VolumeProvider: () => import("effect/Layer").Layer<Provider.Provider<Volume>, never, Docker | import("alchemy/Stack").Stack | import("alchemy/Stage").Stage>;
93
+ export declare const toVolumeAttributes: (info: Docker.Volume) => Volume["Attributes"];
94
+ //# sourceMappingURL=Volume.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Volume.d.ts","sourceRoot":"","sources":["../src/Volume.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAI5C,OAAO,EAAE,MAAM,EAAyC,MAAM,aAAa,CAAC;AAC5E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,WAAW,WAAW;IAC1B,kBAAkB;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,8BAA8B;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAC7B;AAED,MAAM,WAAW,MAAO,SAAQ,QAAQ,CACtC,eAAe,EACf,WAAW,EACX;IACE,0BAA0B;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,4BAA4B;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;CACnB,EACD,KAAK,EACL,SAAS,CACV;CAAG;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,eAAO,MAAM,MAAM,kDAAoC,CAAC;AAExD,eAAO,MAAM,cAAc,8IAiExB,CAAC;AAaJ,eAAO,MAAM,kBAAkB,SAAU,MAAM,CAAC,MAAM,KAAG,MAAM,CAAC,YAAY,CAQ1E,CAAC"}