@alchemy.run/kubernetes 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.
Files changed (94) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +41 -4
  3. package/lib/BuiltinAdapters.d.ts +23 -0
  4. package/lib/BuiltinAdapters.d.ts.map +1 -0
  5. package/lib/BuiltinAdapters.js +124 -0
  6. package/lib/BuiltinAdapters.js.map +1 -0
  7. package/lib/Client.d.ts +80 -0
  8. package/lib/Client.d.ts.map +1 -0
  9. package/lib/Client.js +296 -0
  10. package/lib/Client.js.map +1 -0
  11. package/lib/ClusterAdapter.d.ts +322 -0
  12. package/lib/ClusterAdapter.d.ts.map +1 -0
  13. package/lib/ClusterAdapter.js +65 -0
  14. package/lib/ClusterAdapter.js.map +1 -0
  15. package/lib/Connection.d.ts +210 -0
  16. package/lib/Connection.d.ts.map +1 -0
  17. package/lib/Connection.js +58 -0
  18. package/lib/Connection.js.map +1 -0
  19. package/lib/Deployment.d.ts +382 -0
  20. package/lib/Deployment.d.ts.map +1 -0
  21. package/lib/Deployment.js +533 -0
  22. package/lib/Deployment.js.map +1 -0
  23. package/lib/HelmChart.d.ts +147 -0
  24. package/lib/HelmChart.d.ts.map +1 -0
  25. package/lib/HelmChart.js +248 -0
  26. package/lib/HelmChart.js.map +1 -0
  27. package/lib/Job.d.ts +314 -0
  28. package/lib/Job.d.ts.map +1 -0
  29. package/lib/Job.js +426 -0
  30. package/lib/Job.js.map +1 -0
  31. package/lib/LocalCluster.d.ts +243 -0
  32. package/lib/LocalCluster.d.ts.map +1 -0
  33. package/lib/LocalCluster.js +359 -0
  34. package/lib/LocalCluster.js.map +1 -0
  35. package/lib/Manifest.d.ts +148 -0
  36. package/lib/Manifest.d.ts.map +1 -0
  37. package/lib/Manifest.js +200 -0
  38. package/lib/Manifest.js.map +1 -0
  39. package/lib/Objects.d.ts +60 -0
  40. package/lib/Objects.d.ts.map +1 -0
  41. package/lib/Objects.js +132 -0
  42. package/lib/Objects.js.map +1 -0
  43. package/lib/Providers.d.ts +24 -0
  44. package/lib/Providers.d.ts.map +1 -0
  45. package/lib/Providers.js +36 -0
  46. package/lib/Providers.js.map +1 -0
  47. package/lib/index.d.ts +10 -0
  48. package/lib/index.d.ts.map +1 -0
  49. package/lib/index.js +10 -0
  50. package/lib/index.js.map +1 -0
  51. package/lib/internal/declared.d.ts +19 -0
  52. package/lib/internal/declared.d.ts.map +1 -0
  53. package/lib/internal/declared.js +435 -0
  54. package/lib/internal/declared.js.map +1 -0
  55. package/lib/internal/helm.d.ts +65 -0
  56. package/lib/internal/helm.d.ts.map +1 -0
  57. package/lib/internal/helm.js +173 -0
  58. package/lib/internal/helm.js.map +1 -0
  59. package/lib/internal/kind.d.ts +17 -0
  60. package/lib/internal/kind.d.ts.map +1 -0
  61. package/lib/internal/kind.js +31 -0
  62. package/lib/internal/kind.js.map +1 -0
  63. package/lib/internal/kubeconfig.d.ts +79 -0
  64. package/lib/internal/kubeconfig.d.ts.map +1 -0
  65. package/lib/internal/kubeconfig.js +222 -0
  66. package/lib/internal/kubeconfig.js.map +1 -0
  67. package/lib/internal/registry.d.ts +44 -0
  68. package/lib/internal/registry.d.ts.map +1 -0
  69. package/lib/internal/registry.js +72 -0
  70. package/lib/internal/registry.js.map +1 -0
  71. package/lib/internal/workload.d.ts +127 -0
  72. package/lib/internal/workload.d.ts.map +1 -0
  73. package/lib/internal/workload.js +411 -0
  74. package/lib/internal/workload.js.map +1 -0
  75. package/package.json +55 -3
  76. package/src/BuiltinAdapters.ts +194 -0
  77. package/src/Client.ts +465 -0
  78. package/src/ClusterAdapter.ts +370 -0
  79. package/src/Connection.ts +234 -0
  80. package/src/Deployment.ts +851 -0
  81. package/src/HelmChart.ts +388 -0
  82. package/src/Job.ts +689 -0
  83. package/src/LocalCluster.ts +631 -0
  84. package/src/Manifest.ts +295 -0
  85. package/src/Objects.ts +213 -0
  86. package/src/Providers.ts +52 -0
  87. package/src/globals.d.ts +2 -0
  88. package/src/index.ts +9 -0
  89. package/src/internal/declared.ts +471 -0
  90. package/src/internal/helm.ts +245 -0
  91. package/src/internal/kind.ts +43 -0
  92. package/src/internal/kubeconfig.ts +364 -0
  93. package/src/internal/registry.ts +108 -0
  94. package/src/internal/workload.ts +503 -0
@@ -0,0 +1,243 @@
1
+ import * as Provider from "alchemy/Provider";
2
+ import { Resource } from "alchemy/Resource";
3
+ import * as FileSystem from "effect/FileSystem";
4
+ import * as Path from "effect/Path";
5
+ import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";
6
+ import type { Connection, ContainerRegistry } from "./Connection.ts";
7
+ import type { Providers } from "./Providers.ts";
8
+ export interface LocalClusterProps extends KindClusterConfig {
9
+ /**
10
+ * Name of the kind cluster. Its kubeconfig context is `kind-<name>`.
11
+ * Changing it replaces the cluster.
12
+ * @default a name generated from the stack, stage, and logical ID
13
+ */
14
+ name?: string;
15
+ /**
16
+ * Host port of the cluster's image registry, reachable from the deploying
17
+ * machine as `localhost:<registryPort>`. Give each local cluster its own
18
+ * port.
19
+ * @default 5001
20
+ */
21
+ registryPort?: number;
22
+ /**
23
+ * The kind node image, which selects the Kubernetes version (e.g.
24
+ * `kindest/node:v1.33.1`). A node's own `image` overrides it. Changing it
25
+ * replaces the cluster.
26
+ * @default kind's default node image
27
+ */
28
+ nodeImage?: string;
29
+ /**
30
+ * The kubeconfig file kind writes the cluster's context into. Changing it
31
+ * replaces the cluster.
32
+ * @default `$KUBECONFIG` or `~/.kube/config`
33
+ */
34
+ kubeconfig?: string;
35
+ }
36
+ /**
37
+ * The fields of a [kind `Cluster` config](https://kind.sigs.k8s.io/docs/user/configuration/)
38
+ * (`kind.x-k8s.io/v1alpha4`), accepted directly on `LocalCluster`. kind
39
+ * can't reconfigure a running cluster, so changing any of them replaces the
40
+ * cluster.
41
+ */
42
+ export interface KindClusterConfig {
43
+ /**
44
+ * The cluster's nodes.
45
+ * @default a single control-plane node
46
+ */
47
+ nodes?: KindNode[];
48
+ /** Cluster networking. */
49
+ networking?: KindNetworking;
50
+ /** Kubernetes feature gates, e.g. `{ InPlacePodVerticalScaling: true }`. */
51
+ featureGates?: Record<string, boolean>;
52
+ /** API server `--runtime-config` entries, e.g. `{ "api/alpha": "false" }`. */
53
+ runtimeConfig?: Record<string, string>;
54
+ /** kubeadm config patches (YAML strategic-merge) applied to every node. */
55
+ kubeadmConfigPatches?: string[];
56
+ /** kubeadm config patches (RFC 6902 JSON patches) applied to every node. */
57
+ kubeadmConfigPatchesJSON6902?: KindJsonPatch[];
58
+ /**
59
+ * containerd config patches (TOML). Alchemy appends its own for the image
60
+ * registry.
61
+ */
62
+ containerdConfigPatches?: string[];
63
+ /** containerd config patches (RFC 6902 JSON patches). */
64
+ containerdConfigPatchesJSON6902?: string[];
65
+ }
66
+ /** A node in a kind cluster. */
67
+ export interface KindNode {
68
+ /** Whether the node runs the control plane or only workloads. */
69
+ role: "control-plane" | "worker";
70
+ /** Node image for this node, overriding `nodeImage`. */
71
+ image?: string;
72
+ /** Kubernetes labels for the node. */
73
+ labels?: Record<string, string>;
74
+ /** Host ports forwarded to the node container. */
75
+ extraPortMappings?: KindPortMapping[];
76
+ /** Host paths mounted into the node container. */
77
+ extraMounts?: KindMount[];
78
+ /** kubeadm config patches (YAML strategic-merge) for this node. */
79
+ kubeadmConfigPatches?: string[];
80
+ /** kubeadm config patches (RFC 6902 JSON patches) for this node. */
81
+ kubeadmConfigPatchesJSON6902?: KindJsonPatch[];
82
+ }
83
+ /** A host port forwarded to a kind node container. */
84
+ export interface KindPortMapping {
85
+ /** Port on the node container, e.g. a NodePort or hostPort. */
86
+ containerPort: number;
87
+ /** Port on the host. */
88
+ hostPort: number;
89
+ /**
90
+ * Host address to listen on.
91
+ * @default "0.0.0.0"
92
+ */
93
+ listenAddress?: string;
94
+ /** @default "TCP" */
95
+ protocol?: "TCP" | "UDP" | "SCTP";
96
+ }
97
+ /** A host path mounted into a kind node container. */
98
+ export interface KindMount {
99
+ /** Path on the host. */
100
+ hostPath: string;
101
+ /** Path in the node container. */
102
+ containerPath: string;
103
+ readOnly?: boolean;
104
+ /** Relabel the mount for SELinux hosts. */
105
+ selinuxRelabel?: boolean;
106
+ propagation?: "None" | "HostToContainer" | "Bidirectional";
107
+ }
108
+ /** Cluster networking settings for kind. */
109
+ export interface KindNetworking {
110
+ ipFamily?: "ipv4" | "ipv6" | "dual";
111
+ /**
112
+ * Host port of the API server.
113
+ * @default a random port
114
+ */
115
+ apiServerPort?: number;
116
+ /**
117
+ * Host address the API server listens on.
118
+ * @default "127.0.0.1"
119
+ */
120
+ apiServerAddress?: string;
121
+ /** Pod network CIDR. */
122
+ podSubnet?: string;
123
+ /** Service network CIDR. */
124
+ serviceSubnet?: string;
125
+ /** Skip kind's default CNI so you can install your own. */
126
+ disableDefaultCNI?: boolean;
127
+ kubeProxyMode?: "iptables" | "ipvs" | "nftables" | "none";
128
+ /** DNS search domains for nodes. */
129
+ dnsSearch?: string[];
130
+ }
131
+ /** An RFC 6902 JSON patch targeting a kubeadm config object. */
132
+ export interface KindJsonPatch {
133
+ group: string;
134
+ version: string;
135
+ kind: string;
136
+ /** The JSON patch document, as YAML or JSON. */
137
+ patch: string;
138
+ }
139
+ export interface LocalCluster extends Resource<"Kubernetes.LocalCluster", LocalClusterProps, {
140
+ /** The kind cluster name. */
141
+ name: string;
142
+ /** The kubeconfig context of the cluster (`kind-<name>`). */
143
+ context: string;
144
+ /** The kubeconfig file holding the context, if not the default. */
145
+ kubeconfig: string | undefined;
146
+ /** The cluster's image registry (`localhost:<registryPort>`). */
147
+ registry: ContainerRegistry;
148
+ /** Name of the registry's Docker container. */
149
+ registryContainer: string;
150
+ /** CPU architecture of the cluster's nodes (the Docker host's). */
151
+ architecture: "amd64" | "arm64";
152
+ /**
153
+ * Connection for `Kubernetes.*` resources, including the registry and
154
+ * node architecture. Pass the whole resource as their `cluster`.
155
+ */
156
+ connection: Connection;
157
+ }, {}, Providers> {
158
+ }
159
+ /**
160
+ * A Kubernetes cluster on your machine, for trying Alchemy's Kubernetes
161
+ * resources and developing against them without a hosted cluster.
162
+ *
163
+ * `LocalCluster` runs a [kind](https://kind.sigs.k8s.io) cluster in Docker
164
+ * together with a local image registry wired into the cluster's nodes, so
165
+ * workloads built from a `main` Effect program or a `context` Dockerfile
166
+ * work the same way they do on a hosted cluster with a registry. Pass the
167
+ * resource as the `cluster` of any `Kubernetes.*` resource.
168
+ *
169
+ * Requires Docker and the `kind` CLI on the deploying machine (set
170
+ * `KIND_BIN` to use a specific binary). The cluster's kubeconfig context,
171
+ * `kind-<name>`, works with `kubectl`.
172
+ *
173
+ * ### Creating a Local Cluster
174
+ * **Example:** Cluster and a Deployment
175
+ * ```typescript
176
+ * const cluster = yield* Kubernetes.LocalCluster("Cluster", {
177
+ * name: "alchemy",
178
+ * });
179
+ *
180
+ * const web = yield* Kubernetes.Deployment("Web", {
181
+ * cluster,
182
+ * image: "ghcr.io/stefanprodan/podinfo:6.15.0",
183
+ * port: 9898,
184
+ * serviceType: "ClusterIP",
185
+ * });
186
+ * ```
187
+ *
188
+ * ### Effect Programs
189
+ * **Example:** Run an Effect program as a Job
190
+ * ```typescript
191
+ * const cluster = yield* Kubernetes.LocalCluster("Cluster", {
192
+ * name: "alchemy",
193
+ * });
194
+ *
195
+ * const hello = yield* Kubernetes.Job(
196
+ * "Hello",
197
+ * { cluster, main: import.meta.url },
198
+ * Effect.gen(function* () {
199
+ * return {
200
+ * run: Effect.log("hello from a Job"),
201
+ * };
202
+ * }),
203
+ * );
204
+ * ```
205
+ *
206
+ * ### Kind Configuration
207
+ * **Example:** Forward a host port to a NodePort
208
+ * ```typescript
209
+ * const cluster = yield* Kubernetes.LocalCluster("Cluster", {
210
+ * name: "alchemy",
211
+ * nodes: [
212
+ * {
213
+ * role: "control-plane",
214
+ * extraPortMappings: [{ containerPort: 30080, hostPort: 8080 }],
215
+ * },
216
+ * ],
217
+ * });
218
+ * ```
219
+ *
220
+ * ### Registry Port
221
+ * **Example:** Two local clusters
222
+ * ```typescript
223
+ * const blue = yield* Kubernetes.LocalCluster("Blue", { name: "blue" });
224
+ * const green = yield* Kubernetes.LocalCluster("Green", {
225
+ * name: "green",
226
+ * registryPort: 5002,
227
+ * });
228
+ * ```
229
+ *
230
+ * @resource
231
+ * @product LocalCluster
232
+ */
233
+ export declare const LocalCluster: import("alchemy/Resource").ResourceClass<LocalCluster>;
234
+ declare const LocalClusterError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
235
+ readonly _tag: "Kubernetes.LocalClusterError";
236
+ } & Readonly<A>;
237
+ export declare class LocalClusterError extends LocalClusterError_base<{
238
+ message: string;
239
+ }> {
240
+ }
241
+ export declare const LocalClusterProvider: () => import("effect/Layer").Layer<Provider.Provider<LocalCluster>, never, ChildProcessSpawner.ChildProcessSpawner | FileSystem.FileSystem | Path.Path | import("alchemy/Stack").Stack | import("alchemy/Stage").Stage>;
242
+ export {};
243
+ //# sourceMappingURL=LocalCluster.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalCluster.d.ts","sourceRoot":"","sources":["../src/LocalCluster.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,359 @@
1
+ import { Unowned } from "alchemy/AdoptPolicy";
2
+ import { deepEqual, isResolved } from "alchemy/Diff";
3
+ import { createPhysicalName } from "alchemy/PhysicalName";
4
+ import * as Provider from "alchemy/Provider";
5
+ import { Resource } from "alchemy/Resource";
6
+ import { createInternalTags, hasAlchemyTags } from "alchemy/Tags";
7
+ import * as Config from "effect/Config";
8
+ import * as Data from "effect/Data";
9
+ import * as Effect from "effect/Effect";
10
+ import * as FileSystem from "effect/FileSystem";
11
+ import * as Path from "effect/Path";
12
+ import * as ChildProcess from "effect/process/ChildProcess";
13
+ import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";
14
+ import * as Stream from "effect/Stream";
15
+ import { applyObject, connectCluster } from "./Client.js";
16
+ import { kindClusterConfig, kindConfigOf } from "./internal/kind.js";
17
+ /**
18
+ * A Kubernetes cluster on your machine, for trying Alchemy's Kubernetes
19
+ * resources and developing against them without a hosted cluster.
20
+ *
21
+ * `LocalCluster` runs a [kind](https://kind.sigs.k8s.io) cluster in Docker
22
+ * together with a local image registry wired into the cluster's nodes, so
23
+ * workloads built from a `main` Effect program or a `context` Dockerfile
24
+ * work the same way they do on a hosted cluster with a registry. Pass the
25
+ * resource as the `cluster` of any `Kubernetes.*` resource.
26
+ *
27
+ * Requires Docker and the `kind` CLI on the deploying machine (set
28
+ * `KIND_BIN` to use a specific binary). The cluster's kubeconfig context,
29
+ * `kind-<name>`, works with `kubectl`.
30
+ *
31
+ * ### Creating a Local Cluster
32
+ * **Example:** Cluster and a Deployment
33
+ * ```typescript
34
+ * const cluster = yield* Kubernetes.LocalCluster("Cluster", {
35
+ * name: "alchemy",
36
+ * });
37
+ *
38
+ * const web = yield* Kubernetes.Deployment("Web", {
39
+ * cluster,
40
+ * image: "ghcr.io/stefanprodan/podinfo:6.15.0",
41
+ * port: 9898,
42
+ * serviceType: "ClusterIP",
43
+ * });
44
+ * ```
45
+ *
46
+ * ### Effect Programs
47
+ * **Example:** Run an Effect program as a Job
48
+ * ```typescript
49
+ * const cluster = yield* Kubernetes.LocalCluster("Cluster", {
50
+ * name: "alchemy",
51
+ * });
52
+ *
53
+ * const hello = yield* Kubernetes.Job(
54
+ * "Hello",
55
+ * { cluster, main: import.meta.url },
56
+ * Effect.gen(function* () {
57
+ * return {
58
+ * run: Effect.log("hello from a Job"),
59
+ * };
60
+ * }),
61
+ * );
62
+ * ```
63
+ *
64
+ * ### Kind Configuration
65
+ * **Example:** Forward a host port to a NodePort
66
+ * ```typescript
67
+ * const cluster = yield* Kubernetes.LocalCluster("Cluster", {
68
+ * name: "alchemy",
69
+ * nodes: [
70
+ * {
71
+ * role: "control-plane",
72
+ * extraPortMappings: [{ containerPort: 30080, hostPort: 8080 }],
73
+ * },
74
+ * ],
75
+ * });
76
+ * ```
77
+ *
78
+ * ### Registry Port
79
+ * **Example:** Two local clusters
80
+ * ```typescript
81
+ * const blue = yield* Kubernetes.LocalCluster("Blue", { name: "blue" });
82
+ * const green = yield* Kubernetes.LocalCluster("Green", {
83
+ * name: "green",
84
+ * registryPort: 5002,
85
+ * });
86
+ * ```
87
+ *
88
+ * @resource
89
+ * @product LocalCluster
90
+ */
91
+ export const LocalCluster = Resource("Kubernetes.LocalCluster");
92
+ export class LocalClusterError extends Data.TaggedError("Kubernetes.LocalClusterError") {
93
+ }
94
+ const KindBin = Config.String("KIND_BIN").pipe(Effect.orElseSucceed(() => "kind"));
95
+ const DockerBin = Config.String("DOCKER_BIN").pipe(Effect.orElseSucceed(() => "docker"));
96
+ const REGISTRY_IMAGE = "registry:3";
97
+ const DEFAULT_REGISTRY_PORT = 5001;
98
+ const toArchitecture = (arch) => /^(arm64|aarch64)$/i.test(arch.trim()) ? "arm64" : "amd64";
99
+ const sanitizeName = (name) => name
100
+ .toLowerCase()
101
+ .replaceAll(/[^a-z0-9-]/g, "-")
102
+ .replaceAll(/^-+|-+$/g, "");
103
+ export const LocalClusterProvider = () => Provider.effect(LocalCluster, Effect.gen(function* () {
104
+ const fs = yield* FileSystem.FileSystem;
105
+ const path = yield* Path.Path;
106
+ const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
107
+ /** Run a CLI, returning its exit code and output. */
108
+ const run = (bin, args, stdin) => ChildProcess.make(bin, args, {
109
+ stdin: stdin === undefined ? "ignore" : Stream.succeed(new TextEncoder().encode(stdin)),
110
+ stdout: "pipe",
111
+ stderr: "pipe",
112
+ detached: false,
113
+ extendEnv: true,
114
+ }).pipe(spawner.spawn, Effect.flatMap((child) => Effect.all({
115
+ exitCode: child.exitCode,
116
+ stdout: child.stdout.pipe(Stream.decodeText, Stream.mkString),
117
+ stderr: child.stderr.pipe(Stream.decodeText, Stream.mkString),
118
+ }, { concurrency: "unbounded" })), Effect.map((result) => ({
119
+ exitCode: Number(result.exitCode),
120
+ stdout: result.stdout.trim(),
121
+ stderr: result.stderr.trim(),
122
+ })), Effect.scoped,
123
+ // A spawn failure (almost always ENOENT) means the CLI itself is
124
+ // missing — a machine-setup problem, not a resource error.
125
+ Effect.catchCause((cause) => Effect.die(new Error(`Failed to run '${bin}': ${String(cause)}. ` +
126
+ "Kubernetes.LocalCluster needs Docker and the kind CLI " +
127
+ "(https://kind.sigs.k8s.io/docs/user/quick-start/#installation). " +
128
+ "Set KIND_BIN or DOCKER_BIN to use specific binaries."))));
129
+ /** Run a CLI and fail with its stderr on a non-zero exit. */
130
+ const exec = Effect.fn(function* (bin, args, stdin) {
131
+ const result = yield* run(bin, args, stdin);
132
+ if (result.exitCode !== 0) {
133
+ return yield* new LocalClusterError({
134
+ message: `${bin} ${args.join(" ")} exited with code ${result.exitCode}: ${result.stderr}`,
135
+ });
136
+ }
137
+ return result;
138
+ });
139
+ const kind = Effect.fn(function* (args) {
140
+ return yield* exec(yield* KindBin, args);
141
+ });
142
+ const docker = Effect.fn(function* (args, stdin) {
143
+ return yield* exec(yield* DockerBin, args, stdin);
144
+ });
145
+ const kubeconfigArgs = (kubeconfig) => kubeconfig !== undefined ? ["--kubeconfig", kubeconfig] : [];
146
+ const clusterName = (id, props, output) => output?.name !== undefined
147
+ ? Effect.succeed(output.name)
148
+ : props?.name !== undefined
149
+ ? Effect.succeed(sanitizeName(props.name))
150
+ : createPhysicalName({ id, maxLength: 40, lowercase: true }).pipe(Effect.map(sanitizeName));
151
+ const clusterExists = Effect.fn(function* (name) {
152
+ const { stdout } = yield* kind(["get", "clusters"]);
153
+ return stdout.split(/\r?\n/).some((line) => line.trim() === name);
154
+ });
155
+ const inspectRegistry = Effect.fn(function* (container) {
156
+ const result = yield* run(yield* DockerBin, ["container", "inspect", container]);
157
+ if (result.exitCode !== 0)
158
+ return undefined;
159
+ const [info] = JSON.parse(result.stdout);
160
+ return info;
161
+ });
162
+ const registryHostPort = (info) => {
163
+ const binding = info?.HostConfig?.PortBindings?.["5000/tcp"]?.[0];
164
+ return binding?.HostPort !== undefined ? Number(binding.HostPort) : undefined;
165
+ };
166
+ const hostArchitecture = Effect.gen(function* () {
167
+ const { stdout } = yield* docker(["version", "--format", "{{.Server.Arch}}"]);
168
+ return toArchitecture(stdout);
169
+ });
170
+ const toAttributes = (options) => {
171
+ const context = `kind-${options.name}`;
172
+ const registry = { server: `localhost:${options.registryPort}` };
173
+ return {
174
+ name: options.name,
175
+ context,
176
+ kubeconfig: options.kubeconfig,
177
+ registry,
178
+ registryContainer: `${options.name}-registry`,
179
+ architecture: options.architecture,
180
+ connection: {
181
+ auth: {
182
+ kind: "kubeconfig",
183
+ path: options.kubeconfig,
184
+ context,
185
+ },
186
+ registry,
187
+ architecture: options.architecture,
188
+ },
189
+ };
190
+ };
191
+ /** Ensure the kind cluster exists and its context is in the kubeconfig. */
192
+ const ensureCluster = Effect.fn(function* (options) {
193
+ if (!(yield* clusterExists(options.name))) {
194
+ yield* options.session.note(`Creating kind cluster ${options.name} (about 30 seconds)...`);
195
+ const dir = yield* fs.makeTempDirectory({
196
+ prefix: "alchemy-kind-",
197
+ });
198
+ const configFile = path.join(dir, "kind.yaml");
199
+ yield* fs.writeFileString(configFile, JSON.stringify(kindClusterConfig(options.config), null, 2));
200
+ yield* kind([
201
+ "create",
202
+ "cluster",
203
+ "--name",
204
+ options.name,
205
+ "--config",
206
+ configFile,
207
+ "--wait",
208
+ "120s",
209
+ ...(options.nodeImage !== undefined ? ["--image", options.nodeImage] : []),
210
+ ...kubeconfigArgs(options.kubeconfig),
211
+ ]).pipe(
212
+ // A concurrent create of the same cluster is a race, not a failure.
213
+ Effect.catchTag("Kubernetes.LocalClusterError", (error) => /already exist/i.test(error.message) ? Effect.void : Effect.fail(error)));
214
+ yield* fs.remove(dir, { recursive: true });
215
+ }
216
+ // Restore the context if it was removed from the kubeconfig.
217
+ yield* kind([
218
+ "export",
219
+ "kubeconfig",
220
+ "--name",
221
+ options.name,
222
+ ...kubeconfigArgs(options.kubeconfig),
223
+ ]);
224
+ });
225
+ /**
226
+ * Ensure the registry container runs on the desired port, sits on the
227
+ * `kind` network, and every node's containerd resolves
228
+ * `localhost:<port>` to it.
229
+ */
230
+ const ensureRegistry = Effect.fn(function* (options) {
231
+ const container = `${options.name}-registry`;
232
+ let info = yield* inspectRegistry(container);
233
+ if (info !== undefined && registryHostPort(info) !== options.registryPort) {
234
+ yield* docker(["container", "rm", "--force", container]);
235
+ info = undefined;
236
+ }
237
+ if (info === undefined) {
238
+ yield* options.session.note(`Starting image registry on localhost:${options.registryPort}...`);
239
+ const labels = yield* createInternalTags(options.id);
240
+ yield* docker([
241
+ "container",
242
+ "run",
243
+ "--detach",
244
+ "--restart=always",
245
+ "--publish",
246
+ `127.0.0.1:${options.registryPort}:5000`,
247
+ "--network",
248
+ "bridge",
249
+ "--name",
250
+ container,
251
+ ...Object.entries(labels).flatMap(([key, value]) => ["--label", `${key}=${value}`]),
252
+ REGISTRY_IMAGE,
253
+ ]);
254
+ info = yield* inspectRegistry(container);
255
+ }
256
+ else if (info.State?.Running !== true) {
257
+ yield* docker(["container", "start", container]);
258
+ }
259
+ if (info?.NetworkSettings?.Networks?.kind === undefined) {
260
+ yield* docker(["network", "connect", "kind", container]).pipe(Effect.catchTag("Kubernetes.LocalClusterError", (error) => /already exists/i.test(error.message) ? Effect.void : Effect.fail(error)));
261
+ }
262
+ // containerd on each node: pull `localhost:<port>/…` from the
263
+ // registry container over the kind network.
264
+ const registryDir = `/etc/containerd/certs.d/localhost:${options.registryPort}`;
265
+ const { stdout } = yield* kind(["get", "nodes", "--name", options.name]);
266
+ const nodes = stdout
267
+ .split(/\r?\n/)
268
+ .map((line) => line.trim())
269
+ .filter((line) => line.length > 0);
270
+ yield* Effect.forEach(nodes, (node) => Effect.gen(function* () {
271
+ yield* docker(["exec", node, "mkdir", "-p", registryDir]);
272
+ yield* docker(["exec", "-i", node, "cp", "/dev/stdin", `${registryDir}/hosts.toml`], `[host."http://${container}:5000"]\n`);
273
+ }), { concurrency: "unbounded" });
274
+ });
275
+ return {
276
+ stables: ["name", "context", "kubeconfig", "registryContainer"],
277
+ list: () => Effect.succeed([]),
278
+ diff: Effect.fn(function* ({ id, olds, news, output }) {
279
+ if (!isResolved(news))
280
+ return;
281
+ const oldName = yield* clusterName(id, olds, output);
282
+ const newName = yield* clusterName(id, news, undefined);
283
+ if ((news.name !== undefined && oldName !== newName) ||
284
+ olds?.nodeImage !== news.nodeImage ||
285
+ olds?.kubeconfig !== news.kubeconfig ||
286
+ !deepEqual(kindConfigOf(olds ?? {}), kindConfigOf(news), {
287
+ stripNullish: true,
288
+ })) {
289
+ return { action: "replace", deleteFirst: true };
290
+ }
291
+ }),
292
+ read: Effect.fn(function* ({ id, olds, output }) {
293
+ const name = yield* clusterName(id, olds, output);
294
+ if (!(yield* clusterExists(name)))
295
+ return undefined;
296
+ const registryInfo = yield* inspectRegistry(`${name}-registry`);
297
+ const attrs = toAttributes({
298
+ name,
299
+ kubeconfig: output?.kubeconfig ?? olds?.kubeconfig,
300
+ registryPort: registryHostPort(registryInfo) ?? olds?.registryPort ?? DEFAULT_REGISTRY_PORT,
301
+ architecture: yield* hostArchitecture,
302
+ });
303
+ if (output)
304
+ return attrs;
305
+ // kind clusters carry no ownership marker; the registry container
306
+ // we start next to them does. Anything else is foreign.
307
+ const owned = yield* hasAlchemyTags(id, registryInfo?.Config?.Labels ?? undefined);
308
+ return owned ? attrs : Unowned(attrs);
309
+ }),
310
+ reconcile: Effect.fn(function* ({ id, news, output, session }) {
311
+ const name = yield* clusterName(id, news, output);
312
+ const registryPort = news.registryPort ?? DEFAULT_REGISTRY_PORT;
313
+ yield* ensureCluster({
314
+ name,
315
+ nodeImage: news.nodeImage,
316
+ kubeconfig: news.kubeconfig,
317
+ config: kindConfigOf(news),
318
+ session,
319
+ });
320
+ yield* ensureRegistry({ id, name, registryPort, session });
321
+ const attrs = toAttributes({
322
+ name,
323
+ kubeconfig: news.kubeconfig,
324
+ registryPort,
325
+ architecture: yield* hostArchitecture,
326
+ });
327
+ // Advertise the registry to in-cluster tooling (KEP-1755).
328
+ const transport = yield* connectCluster(attrs.connection);
329
+ yield* applyObject({
330
+ transport,
331
+ object: {
332
+ apiVersion: "v1",
333
+ kind: "ConfigMap",
334
+ metadata: {
335
+ name: "local-registry-hosting",
336
+ namespace: "kube-public",
337
+ },
338
+ data: {
339
+ "localRegistryHosting.v1": `host: "${attrs.registry.server}"\nhelp: "https://kind.sigs.k8s.io/docs/user/local-registry/"\n`,
340
+ },
341
+ },
342
+ });
343
+ yield* session.note(`kind cluster ${name} ready (context ${attrs.context}, registry ${attrs.registry.server})`);
344
+ return attrs;
345
+ }),
346
+ delete: Effect.fn(function* ({ output }) {
347
+ // Both commands are idempotent: kind ignores a missing cluster.
348
+ yield* kind([
349
+ "delete",
350
+ "cluster",
351
+ "--name",
352
+ output.name,
353
+ ...kubeconfigArgs(output.kubeconfig),
354
+ ]);
355
+ yield* run(yield* DockerBin, ["container", "rm", "--force", output.registryContainer]);
356
+ }),
357
+ };
358
+ }));
359
+ //# sourceMappingURL=LocalCluster.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalCluster.js","sourceRoot":"","sources":["../src/LocalCluster.ts"],"names":[],"mappings":"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"}