@alchemy.run/kubernetes 0.0.0 → 2.0.0-beta.85
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 +41 -4
- package/lib/BuiltinAdapters.d.ts +43 -0
- package/lib/BuiltinAdapters.d.ts.map +1 -0
- package/lib/BuiltinAdapters.js +129 -0
- package/lib/BuiltinAdapters.js.map +1 -0
- package/lib/Client.d.ts +97 -0
- package/lib/Client.d.ts.map +1 -0
- package/lib/Client.js +312 -0
- package/lib/Client.js.map +1 -0
- package/lib/ClusterAdapter.d.ts +324 -0
- package/lib/ClusterAdapter.d.ts.map +1 -0
- package/lib/ClusterAdapter.js +65 -0
- package/lib/ClusterAdapter.js.map +1 -0
- package/lib/Connection.d.ts +210 -0
- package/lib/Connection.d.ts.map +1 -0
- package/lib/Connection.js +58 -0
- package/lib/Connection.js.map +1 -0
- package/lib/Deployment.d.ts +401 -0
- package/lib/Deployment.d.ts.map +1 -0
- package/lib/Deployment.js +550 -0
- package/lib/Deployment.js.map +1 -0
- package/lib/HelmChart.d.ts +147 -0
- package/lib/HelmChart.d.ts.map +1 -0
- package/lib/HelmChart.js +248 -0
- package/lib/HelmChart.js.map +1 -0
- package/lib/Job.d.ts +333 -0
- package/lib/Job.d.ts.map +1 -0
- package/lib/Job.js +443 -0
- package/lib/Job.js.map +1 -0
- package/lib/LocalCluster.d.ts +243 -0
- package/lib/LocalCluster.d.ts.map +1 -0
- package/lib/LocalCluster.js +359 -0
- package/lib/LocalCluster.js.map +1 -0
- package/lib/Manifest.d.ts +157 -0
- package/lib/Manifest.d.ts.map +1 -0
- package/lib/Manifest.js +205 -0
- package/lib/Manifest.js.map +1 -0
- package/lib/Objects.d.ts +60 -0
- package/lib/Objects.d.ts.map +1 -0
- package/lib/Objects.js +132 -0
- package/lib/Objects.js.map +1 -0
- package/lib/Providers.d.ts +24 -0
- package/lib/Providers.d.ts.map +1 -0
- package/lib/Providers.js +36 -0
- package/lib/Providers.js.map +1 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +10 -0
- package/lib/index.js.map +1 -0
- package/lib/internal/declared.d.ts +19 -0
- package/lib/internal/declared.d.ts.map +1 -0
- package/lib/internal/declared.js +435 -0
- package/lib/internal/declared.js.map +1 -0
- package/lib/internal/helm.d.ts +65 -0
- package/lib/internal/helm.d.ts.map +1 -0
- package/lib/internal/helm.js +173 -0
- package/lib/internal/helm.js.map +1 -0
- package/lib/internal/kind.d.ts +17 -0
- package/lib/internal/kind.d.ts.map +1 -0
- package/lib/internal/kind.js +31 -0
- package/lib/internal/kind.js.map +1 -0
- package/lib/internal/kubeconfig.d.ts +79 -0
- package/lib/internal/kubeconfig.d.ts.map +1 -0
- package/lib/internal/kubeconfig.js +222 -0
- package/lib/internal/kubeconfig.js.map +1 -0
- package/lib/internal/registry.d.ts +44 -0
- package/lib/internal/registry.d.ts.map +1 -0
- package/lib/internal/registry.js +72 -0
- package/lib/internal/registry.js.map +1 -0
- package/lib/internal/workload.d.ts +127 -0
- package/lib/internal/workload.d.ts.map +1 -0
- package/lib/internal/workload.js +411 -0
- package/lib/internal/workload.js.map +1 -0
- package/package.json +55 -3
- package/src/BuiltinAdapters.ts +202 -0
- package/src/Client.ts +496 -0
- package/src/ClusterAdapter.ts +373 -0
- package/src/Connection.ts +234 -0
- package/src/Deployment.ts +872 -0
- package/src/HelmChart.ts +388 -0
- package/src/Job.ts +709 -0
- package/src/LocalCluster.ts +631 -0
- package/src/Manifest.ts +301 -0
- package/src/Objects.ts +213 -0
- package/src/Providers.ts +52 -0
- package/src/globals.d.ts +2 -0
- package/src/index.ts +9 -0
- package/src/internal/declared.ts +471 -0
- package/src/internal/helm.ts +245 -0
- package/src/internal/kind.ts +43 -0
- package/src/internal/kubeconfig.ts +364 -0
- package/src/internal/registry.ts +108 -0
- package/src/internal/workload.ts +504 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
import { isResolved } from "alchemy/Diff";
|
|
2
|
+
import { createPhysicalName } from "alchemy/PhysicalName";
|
|
3
|
+
import { Platform } from "alchemy/Platform";
|
|
4
|
+
import * as Provider from "alchemy/Provider";
|
|
5
|
+
import { Resource } from "alchemy/Resource";
|
|
6
|
+
import { createHostRuntimeContext, } from "alchemy/Runtime/Process";
|
|
7
|
+
import { Stack } from "alchemy/Stack";
|
|
8
|
+
import { createInternalTags } from "alchemy/Tags";
|
|
9
|
+
import * as Data from "effect/Data";
|
|
10
|
+
import * as Effect from "effect/Effect";
|
|
11
|
+
import * as Schedule from "effect/Schedule";
|
|
12
|
+
import { connectCluster, deleteObjects, ifClusterExists, readObject, reconcileObjects, } from "./Client.js";
|
|
13
|
+
import { findClusterAdapter, } from "./ClusterAdapter.js";
|
|
14
|
+
import { toConnection } from "./Connection.js";
|
|
15
|
+
import { makeConnectionRegistry } from "./internal/registry.js";
|
|
16
|
+
import { collectBindingEnv, containerEnvValue, connectionIdentity, connectionOfOutput, deepMerge, imagePlatformOf, makeServerBootstrap, resolveWorkloadImage, tryConnectionOf, workloadImageHash, } from "./internal/workload.js";
|
|
17
|
+
import { toKubernetesObjectRef, } from "./Objects.js";
|
|
18
|
+
export const isDeployment = (value) => typeof value === "object" &&
|
|
19
|
+
value !== null &&
|
|
20
|
+
"Type" in value &&
|
|
21
|
+
value.Type === "Kubernetes.Deployment";
|
|
22
|
+
/**
|
|
23
|
+
* A replicated Kubernetes server on any cluster, running a container image
|
|
24
|
+
* or an HTTP server written in Effect.
|
|
25
|
+
*
|
|
26
|
+
* `Deployment` provisions a Kubernetes `Deployment` + `Service` (+
|
|
27
|
+
* `ServiceAccount`) via server-side apply, with the container image from
|
|
28
|
+
* exactly one of three sources flat on props: `main` (bundle an inline
|
|
29
|
+
* Effect program), `context` (build your own Dockerfile), or `image` (a
|
|
30
|
+
* pre-built registry reference). `main` and `context` images are built on
|
|
31
|
+
* the deploying machine and pushed to the connection's registry
|
|
32
|
+
* (`Kubernetes.LocalCluster` includes one; EKS uses ECR; GKE uses
|
|
33
|
+
* Artifact Registry). Bindings attach environment variables on any
|
|
34
|
+
* cluster; cloud credential grants need a platform with workload identity.
|
|
35
|
+
* On `AWS.EKS.Cluster` targets it accepts the `{ env, policyStatements }`
|
|
36
|
+
* contract of `AWS.Lambda.Function`: IAM policy statements go to a
|
|
37
|
+
* generated pod-identity role. On `GCP.Container.Cluster` targets it
|
|
38
|
+
* accepts the `{ env, iam }` contract of `GCP.Run.Service`: IAM roles are
|
|
39
|
+
* granted to the Kubernetes ServiceAccount's Workload Identity Federation
|
|
40
|
+
* principal.
|
|
41
|
+
* ### Creating a Deployment
|
|
42
|
+
* **Example:** Run an image on a local cluster
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const cluster = yield* Kubernetes.LocalCluster("Cluster", {
|
|
45
|
+
* name: "alchemy",
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* const web = yield* Kubernetes.Deployment("Web", {
|
|
49
|
+
* cluster,
|
|
50
|
+
* image: "ghcr.io/stefanprodan/podinfo:6.15.0",
|
|
51
|
+
* replicas: 2,
|
|
52
|
+
* port: 9898,
|
|
53
|
+
* serviceType: "ClusterIP",
|
|
54
|
+
* });
|
|
55
|
+
* web.serviceName; // K8s-native attrs
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* **Example:** Any cluster via kubeconfig
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const cluster = Kubernetes.KubeConfig({ context: "prod-east" });
|
|
61
|
+
*
|
|
62
|
+
* const nginx = yield* Kubernetes.Deployment("Nginx", {
|
|
63
|
+
* cluster,
|
|
64
|
+
* image: "nginx:1.27",
|
|
65
|
+
* replicas: 3,
|
|
66
|
+
* port: 80,
|
|
67
|
+
* serviceType: "LoadBalancer",
|
|
68
|
+
* });
|
|
69
|
+
* nginx.url; // the load balancer's address
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* **Example:** Build your own Dockerfile
|
|
73
|
+
* ```typescript
|
|
74
|
+
* const legacy = yield* Kubernetes.Deployment("LegacyApp", {
|
|
75
|
+
* cluster,
|
|
76
|
+
* context: "./legacy",
|
|
77
|
+
* replicas: 2,
|
|
78
|
+
* port: 8080,
|
|
79
|
+
* });
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* **Example:** Build args and secrets
|
|
83
|
+
* ```typescript
|
|
84
|
+
* // Dockerfile:
|
|
85
|
+
* // ARG NODE_ENV
|
|
86
|
+
* // RUN --mount=type=secret,id=NPM_TOKEN,env=NPM_TOKEN npm ci
|
|
87
|
+
* const api = yield* Kubernetes.Deployment("Api", {
|
|
88
|
+
* cluster,
|
|
89
|
+
* context: "./api",
|
|
90
|
+
* build: {
|
|
91
|
+
* args: { NODE_ENV: "production" },
|
|
92
|
+
* // Mounted only into the RUN step that asks for it; never in the image.
|
|
93
|
+
* secrets: { NPM_TOKEN: Config.Redacted("NPM_TOKEN") },
|
|
94
|
+
* },
|
|
95
|
+
* port: 8080,
|
|
96
|
+
* });
|
|
97
|
+
* ```
|
|
98
|
+
*
|
|
99
|
+
* ### Effect Servers
|
|
100
|
+
* **Example:** Inline Effect server
|
|
101
|
+
* ```typescript
|
|
102
|
+
* const api = yield* Kubernetes.Deployment(
|
|
103
|
+
* "Api",
|
|
104
|
+
* { cluster, main: import.meta.url, port: 3000, replicas: 2 },
|
|
105
|
+
* Effect.gen(function* () {
|
|
106
|
+
* return {
|
|
107
|
+
* fetch: Effect.succeed(HttpServerResponse.text("ok")),
|
|
108
|
+
* };
|
|
109
|
+
* }),
|
|
110
|
+
* );
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* **Example:** Push to your own registry
|
|
114
|
+
* ```typescript
|
|
115
|
+
* const cluster = Kubernetes.KubeConfig({
|
|
116
|
+
* context: "prod-east",
|
|
117
|
+
* registry: { server: "ghcr.io/acme" },
|
|
118
|
+
* });
|
|
119
|
+
*
|
|
120
|
+
* const api = yield* Kubernetes.Deployment(
|
|
121
|
+
* "Api",
|
|
122
|
+
* { cluster, main: import.meta.url, port: 3000 },
|
|
123
|
+
* Effect.gen(function* () {
|
|
124
|
+
* return {
|
|
125
|
+
* fetch: Effect.succeed(HttpServerResponse.text("ok")),
|
|
126
|
+
* };
|
|
127
|
+
* }),
|
|
128
|
+
* );
|
|
129
|
+
* ```
|
|
130
|
+
*
|
|
131
|
+
* **Example:** DynamoDB binding on EKS
|
|
132
|
+
* ```typescript
|
|
133
|
+
* const cluster = yield* AWS.EKS.Cluster("Cluster", { compute: "auto" });
|
|
134
|
+
*
|
|
135
|
+
* const api = yield* Kubernetes.Deployment(
|
|
136
|
+
* "Api",
|
|
137
|
+
* { cluster, main: import.meta.url, port: 3000 },
|
|
138
|
+
* Effect.gen(function* () {
|
|
139
|
+
* const putItem = yield* AWS.DynamoDB.PutItem(table);
|
|
140
|
+
* return {
|
|
141
|
+
* fetch: Effect.gen(function* () {
|
|
142
|
+
* yield* putItem({ Item: { id: { S: "1" } } });
|
|
143
|
+
* return HttpServerResponse.text("ok");
|
|
144
|
+
* }),
|
|
145
|
+
* };
|
|
146
|
+
* }).pipe(Effect.provide(AWS.DynamoDB.PutItemHttp)),
|
|
147
|
+
* );
|
|
148
|
+
* ```
|
|
149
|
+
*
|
|
150
|
+
* **Example:** Tagged Effect server
|
|
151
|
+
* ```typescript
|
|
152
|
+
* export class Api extends Kubernetes.Deployment<Api, {
|
|
153
|
+
* health: () => Effect.Effect<string>;
|
|
154
|
+
* }>()("Api") {}
|
|
155
|
+
*
|
|
156
|
+
* export default Api.make(
|
|
157
|
+
* { cluster, main: import.meta.url, port: 3000 },
|
|
158
|
+
* Effect.gen(function* () {
|
|
159
|
+
* return {
|
|
160
|
+
* fetch: Effect.gen(function* () {
|
|
161
|
+
* return HttpServerResponse.text("ok");
|
|
162
|
+
* }),
|
|
163
|
+
* health: () => Effect.succeed("ok"),
|
|
164
|
+
* };
|
|
165
|
+
* }),
|
|
166
|
+
* );
|
|
167
|
+
* ```
|
|
168
|
+
*
|
|
169
|
+
* ### Bundling & Tree-shaking
|
|
170
|
+
* `main` is bundled with rolldown at deploy time. Unused code is
|
|
171
|
+
* tree-shaken. `effect`, alchemy, and `@distilled.cloud` are marked
|
|
172
|
+
* pure so unused parts prune more aggressively. Your app is not
|
|
173
|
+
* marked pure.
|
|
174
|
+
*
|
|
175
|
+
* **Example:** Mark additional packages as pure
|
|
176
|
+
* Only list packages with no top-level side effects.
|
|
177
|
+
* ```typescript
|
|
178
|
+
* {
|
|
179
|
+
* main: import.meta.url,
|
|
180
|
+
* build: {
|
|
181
|
+
* pure: { packages: ["my-lib", "@my-scope/*"] },
|
|
182
|
+
* },
|
|
183
|
+
* }
|
|
184
|
+
* ```
|
|
185
|
+
*
|
|
186
|
+
* **Example:** Turn it off
|
|
187
|
+
* ```typescript
|
|
188
|
+
* {
|
|
189
|
+
* main: import.meta.url,
|
|
190
|
+
* build: { pure: false },
|
|
191
|
+
* }
|
|
192
|
+
* ```
|
|
193
|
+
*
|
|
194
|
+
* ### Kubernetes Escape Hatch
|
|
195
|
+
* **Example:** Tune the synthesized pod template
|
|
196
|
+
* ```typescript
|
|
197
|
+
* const tuned = yield* Kubernetes.Deployment("Api", {
|
|
198
|
+
* cluster,
|
|
199
|
+
* main: import.meta.url,
|
|
200
|
+
* port: 3000,
|
|
201
|
+
* podTemplate: {
|
|
202
|
+
* spec: {
|
|
203
|
+
* tolerations: [{ key: "gpu", operator: "Exists" }],
|
|
204
|
+
* nodeSelector: { pool: "arm" },
|
|
205
|
+
* },
|
|
206
|
+
* },
|
|
207
|
+
* });
|
|
208
|
+
* ```
|
|
209
|
+
*
|
|
210
|
+
* @resource
|
|
211
|
+
* @product Workloads
|
|
212
|
+
*/
|
|
213
|
+
export const Deployment = Platform("Kubernetes.Deployment", {
|
|
214
|
+
aliases: ["AWS.EKS.Deployment"],
|
|
215
|
+
// `{ cluster: Cluster }` at module scope is an Effect. Yield it here so the
|
|
216
|
+
// cluster is registered and `news.cluster` is resolved at reconcile (same
|
|
217
|
+
// as `yield* Cluster` inside Effect.gen).
|
|
218
|
+
transformProps: (_id, props) => Effect.gen(function* () {
|
|
219
|
+
if (globalThis.__ALCHEMY_RUNTIME__)
|
|
220
|
+
return props;
|
|
221
|
+
const cluster = Effect.isEffect(props.cluster)
|
|
222
|
+
? yield* props.cluster
|
|
223
|
+
: props.cluster;
|
|
224
|
+
return { ...props, cluster };
|
|
225
|
+
}),
|
|
226
|
+
createRuntimeContext: createHostRuntimeContext("Kubernetes.Deployment"),
|
|
227
|
+
});
|
|
228
|
+
/**
|
|
229
|
+
* `cluster` as the provider sees it. `transformProps` yields an Effect-valued
|
|
230
|
+
* `cluster` before any lifecycle operation runs, so only the resolved shape
|
|
231
|
+
* reaches the provider.
|
|
232
|
+
*/
|
|
233
|
+
const clusterOf = (cluster) => cluster;
|
|
234
|
+
class ServiceNotReady extends Data.TaggedError("Kubernetes.ServiceNotReady") {
|
|
235
|
+
}
|
|
236
|
+
// Bounded ~3 min wait for the cloud load balancer to publish its hostname
|
|
237
|
+
// (an EKS Auto Mode NLB typically appears within 2–3 min of the Service
|
|
238
|
+
// apply).
|
|
239
|
+
const loadBalancerRetrySchedule = Schedule.max([Schedule.spaced("5 seconds"), Schedule.recurs(36)]);
|
|
240
|
+
/**
|
|
241
|
+
* Explicitly-typed pipeable retry for the LB-hostname wait. An inline
|
|
242
|
+
* `Effect.retry` in the provider leaks `Retry.Return`'s conditional into
|
|
243
|
+
* declaration emit and widens the provider layer to `unknown` R.
|
|
244
|
+
*/
|
|
245
|
+
const retryUntilServiceReady = (self) => Effect.retry(self, {
|
|
246
|
+
while: (error) => error instanceof ServiceNotReady,
|
|
247
|
+
schedule: loadBalancerRetrySchedule,
|
|
248
|
+
});
|
|
249
|
+
class ServiceStillExists extends Data.TaggedError("Kubernetes.ServiceStillExists") {
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Bounded (~3 min) wait for a deleted Service to disappear, i.e. for the
|
|
253
|
+
* cloud controller to tear down its load balancer and drop the finalizer.
|
|
254
|
+
*/
|
|
255
|
+
const waitForServiceGone = (transport, service) => Effect.retry(readObject({ transport, object: service }).pipe(Effect.flatMap(() => Effect.fail(new ServiceStillExists())), Effect.catchTag("KubernetesNotFound", () => Effect.void)), { while: (error) => error instanceof ServiceStillExists, schedule: loadBalancerRetrySchedule });
|
|
256
|
+
export const DeploymentProvider = () => Provider.effect(Deployment, Effect.gen(function* () {
|
|
257
|
+
const stack = yield* Stack;
|
|
258
|
+
const connectionRegistry = yield* makeConnectionRegistry;
|
|
259
|
+
const alchemyEnv = {
|
|
260
|
+
ALCHEMY_STACK_NAME: stack.name,
|
|
261
|
+
ALCHEMY_STAGE: stack.stage,
|
|
262
|
+
ALCHEMY_PHASE: "runtime",
|
|
263
|
+
};
|
|
264
|
+
// The base name doubles as the `app.kubernetes.io/name` label value
|
|
265
|
+
// (and the Service name), both capped at 63 characters.
|
|
266
|
+
const toBaseName = (id, props = {}) => props.name
|
|
267
|
+
? Effect.succeed(props.name)
|
|
268
|
+
: createPhysicalName({ id, maxLength: 63, lowercase: true }).pipe(Effect.map((name) => name.replaceAll(/[^a-z0-9-]/g, "-")));
|
|
269
|
+
// Read a LoadBalancer Service's assigned hostname (bounded wait).
|
|
270
|
+
const waitForLoadBalancer = (transport, service) => readObject({ transport, object: service }).pipe(Effect.map((response) => {
|
|
271
|
+
const ingress = response?.status?.loadBalancer?.ingress?.[0];
|
|
272
|
+
return ingress?.hostname ?? ingress?.ip;
|
|
273
|
+
}), Effect.flatMap((hostname) => hostname ? Effect.succeed(hostname) : Effect.fail(new ServiceNotReady())), retryUntilServiceReady, Effect.catchTag("Kubernetes.ServiceNotReady", () => Effect.undefined));
|
|
274
|
+
return {
|
|
275
|
+
stables: [
|
|
276
|
+
"connection",
|
|
277
|
+
"namespace",
|
|
278
|
+
"serviceAccountName",
|
|
279
|
+
"deploymentName",
|
|
280
|
+
"serviceName",
|
|
281
|
+
"identity",
|
|
282
|
+
"registry",
|
|
283
|
+
],
|
|
284
|
+
// A Deployment's identity spans in-cluster Kubernetes objects plus
|
|
285
|
+
// adapter-owned cloud resources (identity role, image repository).
|
|
286
|
+
// There is no single enumeration that faithfully reconstructs that
|
|
287
|
+
// composite, so enumeration is intentionally empty — `read` (below)
|
|
288
|
+
// refreshes a known instance from its persisted output.
|
|
289
|
+
list: () => Effect.succeed([]),
|
|
290
|
+
diff: Effect.fn(function* ({ olds = {}, news: input, output }) {
|
|
291
|
+
// `exports` carries the program's runtime Effects (never plain
|
|
292
|
+
// data); everything else must be resolved to diff.
|
|
293
|
+
const { exports: _exports, ...declared } = input;
|
|
294
|
+
if (!isResolved(declared))
|
|
295
|
+
return;
|
|
296
|
+
const news = input;
|
|
297
|
+
const oldCluster = connectionIdentity(tryConnectionOf(clusterOf(olds.cluster)));
|
|
298
|
+
const newCluster = connectionIdentity(tryConnectionOf(clusterOf(news.cluster)));
|
|
299
|
+
// Workload identity keys on (cluster, namespace, serviceAccount);
|
|
300
|
+
// a change to either forces a replacement. Only compare when the
|
|
301
|
+
// old value is present so a first create (empty `olds`) doesn't
|
|
302
|
+
// spuriously replace.
|
|
303
|
+
if (oldCluster !== undefined && newCluster !== undefined && oldCluster !== newCluster) {
|
|
304
|
+
return { action: "replace" };
|
|
305
|
+
}
|
|
306
|
+
if (oldCluster !== undefined &&
|
|
307
|
+
(olds.namespace ?? "default") !== (news.namespace ?? "default")) {
|
|
308
|
+
return { action: "replace" };
|
|
309
|
+
}
|
|
310
|
+
// Content drift: the props don't change when files under a build
|
|
311
|
+
// context (or the bundled program) do, so surface hash drift as
|
|
312
|
+
// an update.
|
|
313
|
+
const connection = tryConnectionOf(clusterOf(news.cluster));
|
|
314
|
+
if (output && connection) {
|
|
315
|
+
const adapter = yield* findClusterAdapter(connection.auth.kind);
|
|
316
|
+
const source = news;
|
|
317
|
+
const hash = yield* workloadImageHash({
|
|
318
|
+
adapter,
|
|
319
|
+
connection,
|
|
320
|
+
connectionRegistry,
|
|
321
|
+
source,
|
|
322
|
+
platform: imagePlatformOf(news.architecture, connection),
|
|
323
|
+
port: news.port ?? 3000,
|
|
324
|
+
isExternal: news.isExternal,
|
|
325
|
+
bootstrap: (adapter.bootstrap?.server ?? makeServerBootstrap)(source.handler ?? "default"),
|
|
326
|
+
});
|
|
327
|
+
if (hash !== undefined && hash !== output.code.hash) {
|
|
328
|
+
return { action: "update" };
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}),
|
|
332
|
+
read: Effect.fn(function* ({ output }) {
|
|
333
|
+
if (!output)
|
|
334
|
+
return undefined;
|
|
335
|
+
const connection = connectionOfOutput(output);
|
|
336
|
+
if (!connection)
|
|
337
|
+
return undefined;
|
|
338
|
+
const transport = yield* connectCluster(connection);
|
|
339
|
+
// The ServiceAccount is the stable first object of the workload
|
|
340
|
+
// (one-shot Job objects are content-addressed and may have been
|
|
341
|
+
// TTL-collected, so they are not an existence signal).
|
|
342
|
+
const anchor = (output.kubernetesObjects ?? [])[0];
|
|
343
|
+
if (!anchor)
|
|
344
|
+
return output;
|
|
345
|
+
const observed = yield* readObject({ transport, object: anchor }).pipe(Effect.catchTag("KubernetesNotFound", () => Effect.undefined));
|
|
346
|
+
if (observed === undefined)
|
|
347
|
+
return undefined;
|
|
348
|
+
return output;
|
|
349
|
+
}),
|
|
350
|
+
reconcile: Effect.fn(function* ({ id, news, bindings, output, session }) {
|
|
351
|
+
const connection = toConnection(clusterOf(news.cluster));
|
|
352
|
+
const adapter = yield* findClusterAdapter(connection.auth.kind);
|
|
353
|
+
const transport = yield* adapter.connect(connection);
|
|
354
|
+
const namespace = news.namespace ?? "default";
|
|
355
|
+
const port = news.port ?? 3000;
|
|
356
|
+
const serviceType = news.serviceType ?? "LoadBalancer";
|
|
357
|
+
const baseName = output?.deploymentName ?? (yield* toBaseName(id, news));
|
|
358
|
+
const serviceAccountName = output?.serviceAccountName ?? baseName;
|
|
359
|
+
const tags = { ...(yield* createInternalTags(id)), ...news.tags };
|
|
360
|
+
// Environment from bindings is collected generically; cloud
|
|
361
|
+
// credential grants (any non-env binding channel) require the
|
|
362
|
+
// cluster platform's identity adapter.
|
|
363
|
+
const { env: bindingEnv, grantKeys } = collectBindingEnv(bindings);
|
|
364
|
+
if (grantKeys.length > 0 && adapter.identity === undefined) {
|
|
365
|
+
return yield* Effect.die(new Error(`'${id}': bindings carry cloud credential grants ` +
|
|
366
|
+
`(${grantKeys.join(", ")}) but this cluster's platform ` +
|
|
367
|
+
"has no workload-identity adapter. Target a managed " +
|
|
368
|
+
"cluster (e.g. AWS.EKS.Cluster) or bind through " +
|
|
369
|
+
"environment variables instead."));
|
|
370
|
+
}
|
|
371
|
+
const identity = adapter.identity
|
|
372
|
+
? yield* adapter.identity.reconcile({
|
|
373
|
+
id,
|
|
374
|
+
connection,
|
|
375
|
+
namespace,
|
|
376
|
+
serviceAccount: serviceAccountName,
|
|
377
|
+
bindings,
|
|
378
|
+
options: news.identity,
|
|
379
|
+
state: output?.identity ??
|
|
380
|
+
output,
|
|
381
|
+
tags,
|
|
382
|
+
})
|
|
383
|
+
: undefined;
|
|
384
|
+
// Resolve the container image from whichever source the props
|
|
385
|
+
// declare (`main` | `context` | `image`) through the platform's
|
|
386
|
+
// registry adapter (pre-built `image` refs pass through verbatim
|
|
387
|
+
// on registry-less clusters).
|
|
388
|
+
const source = news;
|
|
389
|
+
const resolved = yield* resolveWorkloadImage({
|
|
390
|
+
adapter,
|
|
391
|
+
connection,
|
|
392
|
+
connectionRegistry,
|
|
393
|
+
id,
|
|
394
|
+
source,
|
|
395
|
+
platform: imagePlatformOf(news.architecture, connection),
|
|
396
|
+
port,
|
|
397
|
+
isExternal: news.isExternal,
|
|
398
|
+
bootstrap: (adapter.bootstrap?.server ?? makeServerBootstrap)(source.handler ?? "default"),
|
|
399
|
+
tags,
|
|
400
|
+
state: output?.registry ??
|
|
401
|
+
output,
|
|
402
|
+
session,
|
|
403
|
+
});
|
|
404
|
+
// Synthesize the Kubernetes objects. Container env merges binding
|
|
405
|
+
// env (Output-referenced resource attributes flow via the
|
|
406
|
+
// RuntimeContext into `news.env`), adapter env (e.g. AWS_REGION on
|
|
407
|
+
// EKS), alchemy env, and user env. The generated name label is
|
|
408
|
+
// always present so the Service selector stays workload-unique
|
|
409
|
+
// even when user labels are set.
|
|
410
|
+
const labels = { "app.kubernetes.io/name": baseName, ...news.labels };
|
|
411
|
+
const containerEnv = {
|
|
412
|
+
...bindingEnv,
|
|
413
|
+
...identity?.env,
|
|
414
|
+
...alchemyEnv,
|
|
415
|
+
PORT: String(port),
|
|
416
|
+
...news.env,
|
|
417
|
+
};
|
|
418
|
+
const serviceAccountObject = {
|
|
419
|
+
apiVersion: "v1",
|
|
420
|
+
kind: "ServiceAccount",
|
|
421
|
+
metadata: {
|
|
422
|
+
name: serviceAccountName,
|
|
423
|
+
namespace,
|
|
424
|
+
labels,
|
|
425
|
+
...(identity?.serviceAccountAnnotations
|
|
426
|
+
? { annotations: identity.serviceAccountAnnotations }
|
|
427
|
+
: {}),
|
|
428
|
+
},
|
|
429
|
+
};
|
|
430
|
+
const podTemplate = deepMerge({
|
|
431
|
+
metadata: { labels },
|
|
432
|
+
spec: {
|
|
433
|
+
serviceAccountName,
|
|
434
|
+
containers: [
|
|
435
|
+
{
|
|
436
|
+
name: baseName,
|
|
437
|
+
image: resolved.imageUri,
|
|
438
|
+
command: news.command,
|
|
439
|
+
args: news.args,
|
|
440
|
+
ports: [{ containerPort: port }],
|
|
441
|
+
env: Object.entries(containerEnv).map(([name, value]) => ({
|
|
442
|
+
name,
|
|
443
|
+
value: containerEnvValue(value),
|
|
444
|
+
})),
|
|
445
|
+
resources: news.resources,
|
|
446
|
+
},
|
|
447
|
+
],
|
|
448
|
+
},
|
|
449
|
+
}, news.podTemplate);
|
|
450
|
+
const deploymentObject = {
|
|
451
|
+
apiVersion: "apps/v1",
|
|
452
|
+
kind: "Deployment",
|
|
453
|
+
metadata: { name: baseName, namespace, labels },
|
|
454
|
+
spec: {
|
|
455
|
+
replicas: news.replicas ?? 1,
|
|
456
|
+
selector: { matchLabels: labels },
|
|
457
|
+
template: podTemplate,
|
|
458
|
+
},
|
|
459
|
+
};
|
|
460
|
+
// `LoadBalancer` Services need platform defaults on some managed
|
|
461
|
+
// clouds (EKS Auto Mode only reconciles Services carrying its
|
|
462
|
+
// `loadBalancerClass`, and defaults new NLBs to the internal
|
|
463
|
+
// scheme) — ask the adapter. User `serviceAnnotations` always win.
|
|
464
|
+
let loadBalancerClass;
|
|
465
|
+
let serviceAnnotations = news.serviceAnnotations;
|
|
466
|
+
if (serviceType === "LoadBalancer") {
|
|
467
|
+
const defaults = adapter.loadBalancerDefaults
|
|
468
|
+
? yield* adapter.loadBalancerDefaults({ connection })
|
|
469
|
+
: undefined;
|
|
470
|
+
loadBalancerClass = defaults?.loadBalancerClass;
|
|
471
|
+
serviceAnnotations = { ...defaults?.annotations, ...news.serviceAnnotations };
|
|
472
|
+
}
|
|
473
|
+
const serviceObject = {
|
|
474
|
+
apiVersion: "v1",
|
|
475
|
+
kind: "Service",
|
|
476
|
+
metadata: { name: baseName, namespace, labels, annotations: serviceAnnotations },
|
|
477
|
+
spec: {
|
|
478
|
+
type: serviceType,
|
|
479
|
+
...(loadBalancerClass !== undefined ? { loadBalancerClass } : {}),
|
|
480
|
+
selector: labels,
|
|
481
|
+
ports: [{ port, targetPort: port, protocol: "TCP" }],
|
|
482
|
+
},
|
|
483
|
+
};
|
|
484
|
+
const desiredObjects = [serviceAccountObject, deploymentObject, serviceObject];
|
|
485
|
+
const kubernetesObjects = yield* reconcileObjects({
|
|
486
|
+
transport,
|
|
487
|
+
previousObjects: output?.kubernetesObjects ?? [],
|
|
488
|
+
desiredObjects,
|
|
489
|
+
});
|
|
490
|
+
yield* session.note(`Applied Kubernetes Deployment ${namespace}/${baseName}`);
|
|
491
|
+
// Resolve the LoadBalancer URL if applicable. The cloud listener
|
|
492
|
+
// is the Service `port` (Kubernetes maps `spec.ports[].port` 1:1
|
|
493
|
+
// to it), so the URL carries the port unless it's 80 — mirroring
|
|
494
|
+
// `AWS.ECS.Service`'s url semantics.
|
|
495
|
+
const hostname = serviceType === "LoadBalancer"
|
|
496
|
+
? yield* waitForLoadBalancer(transport, toKubernetesObjectRef(serviceObject))
|
|
497
|
+
: undefined;
|
|
498
|
+
const url = hostname === undefined
|
|
499
|
+
? undefined
|
|
500
|
+
: port === 80
|
|
501
|
+
? `http://${hostname}`
|
|
502
|
+
: `http://${hostname}:${port}`;
|
|
503
|
+
return {
|
|
504
|
+
connection,
|
|
505
|
+
namespace,
|
|
506
|
+
deploymentName: baseName,
|
|
507
|
+
serviceName: baseName,
|
|
508
|
+
serviceAccountName,
|
|
509
|
+
port,
|
|
510
|
+
imageUri: resolved.imageUri,
|
|
511
|
+
identity: identity?.state,
|
|
512
|
+
registry: resolved.state,
|
|
513
|
+
url,
|
|
514
|
+
kubernetesObjects,
|
|
515
|
+
code: { hash: resolved.codeHash },
|
|
516
|
+
};
|
|
517
|
+
}),
|
|
518
|
+
delete: Effect.fn(function* ({ output }) {
|
|
519
|
+
const connection = connectionOfOutput(output);
|
|
520
|
+
if (!connection)
|
|
521
|
+
return;
|
|
522
|
+
const adapter = yield* findClusterAdapter(connection.auth.kind);
|
|
523
|
+
// Skip only a missing cluster, then still clean up adapter-owned
|
|
524
|
+
// cloud resources (image repository, identity role).
|
|
525
|
+
const transport = yield* ifClusterExists(adapter.connect(connection));
|
|
526
|
+
if (transport && (output.kubernetesObjects ?? []).length > 0) {
|
|
527
|
+
yield* deleteObjects({ transport, objects: output.kubernetesObjects ?? [] });
|
|
528
|
+
// A LoadBalancer Service carries the cloud controller's cleanup
|
|
529
|
+
// finalizer; wait for it so the cloud load balancer is gone
|
|
530
|
+
// before the cluster (and its controller) can be deleted —
|
|
531
|
+
// deleting the cluster first leaks the load balancer.
|
|
532
|
+
yield* Effect.forEach((output.kubernetesObjects ?? []).filter((object) => object.kind === "Service"), (service) => waitForServiceGone(transport, service), { discard: true });
|
|
533
|
+
}
|
|
534
|
+
if (adapter.identity) {
|
|
535
|
+
yield* adapter.identity.delete({
|
|
536
|
+
connection,
|
|
537
|
+
state: output.identity ??
|
|
538
|
+
output,
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
if (adapter.registry) {
|
|
542
|
+
yield* adapter.registry.delete({
|
|
543
|
+
state: output.registry ??
|
|
544
|
+
output,
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
}),
|
|
548
|
+
};
|
|
549
|
+
}));
|
|
550
|
+
//# sourceMappingURL=Deployment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Deployment.js","sourceRoot":"","sources":["../src/Deployment.ts"],"names":[],"mappings":"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"}
|