@highstate/k8s 0.9.18 → 0.9.20

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 (93) hide show
  1. package/dist/chunk-2EEHJZPD.js +13 -0
  2. package/dist/chunk-2EEHJZPD.js.map +1 -0
  3. package/dist/{chunk-OFFSHGC6.js → chunk-4JGXGN2L.js} +66 -48
  4. package/dist/chunk-4JGXGN2L.js.map +1 -0
  5. package/dist/chunk-A3XGSDIW.js +306 -0
  6. package/dist/chunk-A3XGSDIW.js.map +1 -0
  7. package/dist/chunk-IMTXUK2U.js +244 -0
  8. package/dist/chunk-IMTXUK2U.js.map +1 -0
  9. package/dist/chunk-JYNXQ3I3.js +287 -0
  10. package/dist/chunk-JYNXQ3I3.js.map +1 -0
  11. package/dist/{chunk-5C2BJGES.js → chunk-KDD6XUWM.js} +30 -23
  12. package/dist/chunk-KDD6XUWM.js.map +1 -0
  13. package/dist/chunk-NOFJC3EM.js +236 -0
  14. package/dist/chunk-NOFJC3EM.js.map +1 -0
  15. package/dist/chunk-NXSYCA3V.js +337 -0
  16. package/dist/chunk-NXSYCA3V.js.map +1 -0
  17. package/dist/chunk-SBC3TUIN.js +1513 -0
  18. package/dist/chunk-SBC3TUIN.js.map +1 -0
  19. package/dist/chunk-SI7X6N46.js +338 -0
  20. package/dist/chunk-SI7X6N46.js.map +1 -0
  21. package/dist/chunk-WGMJCZSK.js +360 -0
  22. package/dist/chunk-WGMJCZSK.js.map +1 -0
  23. package/dist/deployment-752P6JIT.js +8 -0
  24. package/dist/{deployment-XK3CDJOE.js.map → deployment-752P6JIT.js.map} +1 -1
  25. package/dist/highstate.manifest.json +8 -7
  26. package/dist/impl/gateway-route.js +123 -0
  27. package/dist/impl/gateway-route.js.map +1 -0
  28. package/dist/impl/tls-certificate.js +32 -0
  29. package/dist/impl/tls-certificate.js.map +1 -0
  30. package/dist/index.js +736 -208
  31. package/dist/index.js.map +1 -1
  32. package/dist/stateful-set-N64YVKR7.js +8 -0
  33. package/dist/{stateful-set-7CAQWTV2.js.map → stateful-set-N64YVKR7.js.map} +1 -1
  34. package/dist/units/cert-manager/index.js +11 -10
  35. package/dist/units/cert-manager/index.js.map +1 -1
  36. package/dist/units/dns01-issuer/index.js +27 -23
  37. package/dist/units/dns01-issuer/index.js.map +1 -1
  38. package/dist/units/existing-cluster/index.js +11 -8
  39. package/dist/units/existing-cluster/index.js.map +1 -1
  40. package/dist/units/gateway-api/index.js +2 -2
  41. package/dist/units/gateway-api/index.js.map +1 -1
  42. package/package.json +39 -13
  43. package/src/cluster.ts +30 -22
  44. package/src/config-map.ts +195 -57
  45. package/src/container.ts +5 -5
  46. package/src/cron-job.ts +403 -31
  47. package/src/deployment.ts +260 -120
  48. package/src/dns01-solver.ts +10 -0
  49. package/src/gateway/backend.ts +2 -2
  50. package/src/gateway/gateway.ts +383 -0
  51. package/src/gateway/http-route.ts +17 -24
  52. package/src/gateway/index.ts +1 -0
  53. package/src/helm.ts +83 -53
  54. package/src/impl/gateway-route.ts +155 -0
  55. package/src/impl/tls-certificate.ts +33 -0
  56. package/src/index.ts +22 -67
  57. package/src/job.ts +393 -28
  58. package/src/namespace.ts +236 -99
  59. package/src/network-policy.ts +216 -165
  60. package/src/network.ts +2 -2
  61. package/src/pvc.ts +266 -65
  62. package/src/rbac.ts +218 -0
  63. package/src/scripting/bundle.ts +9 -20
  64. package/src/scripting/container.ts +1 -1
  65. package/src/scripting/environment.ts +5 -5
  66. package/src/secret.ts +200 -62
  67. package/src/service.ts +288 -158
  68. package/src/shared.ts +94 -67
  69. package/src/stateful-set.ts +270 -117
  70. package/src/tls.ts +344 -0
  71. package/src/units/cert-manager/index.ts +2 -3
  72. package/src/units/dns01-issuer/index.ts +30 -14
  73. package/src/units/existing-cluster/index.ts +10 -7
  74. package/src/units/gateway-api/index.ts +2 -2
  75. package/src/worker.ts +26 -0
  76. package/src/workload.ts +275 -171
  77. package/dist/chunk-5C2BJGES.js.map +0 -1
  78. package/dist/chunk-5TLC5BXR.js +0 -256
  79. package/dist/chunk-5TLC5BXR.js.map +0 -1
  80. package/dist/chunk-BBIY3KUN.js +0 -1557
  81. package/dist/chunk-BBIY3KUN.js.map +0 -1
  82. package/dist/chunk-OFFSHGC6.js.map +0 -1
  83. package/dist/chunk-TZHOUJRC.js +0 -202
  84. package/dist/chunk-TZHOUJRC.js.map +0 -1
  85. package/dist/chunk-YWRJ4EZM.js +0 -192
  86. package/dist/chunk-YWRJ4EZM.js.map +0 -1
  87. package/dist/deployment-XK3CDJOE.js +0 -6
  88. package/dist/stateful-set-7CAQWTV2.js +0 -6
  89. package/dist/units/access-point/index.js +0 -21
  90. package/dist/units/access-point/index.js.map +0 -1
  91. package/src/access-point.ts +0 -191
  92. package/src/units/access-point/index.ts +0 -19
  93. package/src/units/dns01-issuer/solver.ts +0 -23
package/src/workload.ts CHANGED
@@ -1,59 +1,81 @@
1
1
  import type { k8s } from "@highstate/library"
2
+ import type { types } from "@pulumi/kubernetes"
2
3
  import type { DeploymentArgs } from "./deployment"
3
4
  import type { StatefulSetArgs } from "./stateful-set"
4
- import type { types } from "@pulumi/kubernetes"
5
+ import { AccessPointRoute, type AccessPointRouteArgs } from "@highstate/common"
6
+ import { type TerminalSpec, trimIndentation, type UnitTerminal } from "@highstate/contract"
5
7
  import {
6
- normalize,
7
8
  type ComponentResourceOptions,
9
+ type DeepInput,
10
+ fileFromString,
8
11
  type InputArray,
9
- type InstanceTerminal,
12
+ normalize,
13
+ normalizeInputs,
10
14
  } from "@highstate/pulumi"
11
15
  import {
12
- ComponentResource,
13
- interpolate,
14
- Output,
15
- output,
16
+ type ComponentResource,
16
17
  type CustomResourceOptions,
17
18
  type Input,
19
+ type Inputs,
20
+ interpolate,
21
+ type Output,
22
+ output,
23
+ type Unwrap,
18
24
  } from "@pulumi/pulumi"
19
- import { filter, isNonNullish, unique, uniqueBy } from "remeda"
20
- import { deepmerge } from "deepmerge-ts"
21
25
  import { sha256 } from "crypto-hash"
22
- import { commonExtraArgs, getProvider, images, type CommonArgs } from "./shared"
23
- import { mapContainerPortToServicePort, Service, type ServiceArgs } from "./service"
24
- import { HttpRoute, type HttpRouteArgs } from "./gateway"
26
+ import { deepmerge } from "deepmerge-ts"
27
+ import { filter, isNonNullish, unique, uniqueBy } from "remeda"
25
28
  import {
29
+ type Container,
26
30
  getWorkloadVolumeResourceUuid,
27
31
  mapContainerToRaw,
28
32
  mapWorkloadVolume,
29
- type Container,
30
33
  type WorkloadVolume,
31
34
  } from "./container"
35
+ import { Namespace } from "./namespace"
32
36
  import { NetworkPolicy, type NetworkPolicyArgs } from "./network-policy"
33
37
  import { podSpecDefaults } from "./pod"
38
+ import { mapContainerPortToServicePort, Service, type ServiceArgs } from "./service"
39
+ import { commonExtraArgs, images, ScopedResource, type ScopedResourceArgs } from "./shared"
34
40
 
35
- export type WorkloadArgs = CommonArgs & {
36
- container?: Input<Container>
37
- containers?: InputArray<Container>
38
-
41
+ export type WorkloadTerminalArgs = {
39
42
  /**
40
43
  * The shell to use in the terminal.
41
44
  *
42
45
  * By default, `bash` is used.
43
46
  */
44
- terminalShell?: string
47
+ shell?: string
48
+ }
49
+
50
+ export type WorkloadArgs = ScopedResourceArgs & {
51
+ container?: Input<Container>
52
+ containers?: InputArray<Container>
53
+
54
+ /**
55
+ * The args for the terminal to use.
56
+ */
57
+ terminal?: Input<WorkloadTerminalArgs>
45
58
 
46
59
  /**
47
60
  * The network policy to apply to the deployment.
48
61
  */
49
- networkPolicy?: Input<Omit<NetworkPolicyArgs, "selector" | "cluster" | "namespace">>
62
+ networkPolicy?: Omit<NetworkPolicyArgs, "selector" | "cluster" | "namespace">
50
63
  }
51
64
 
52
65
  export const workloadExtraArgs = [...commonExtraArgs, "container", "containers"] as const
53
66
 
54
67
  export type ExposableWorkloadArgs = WorkloadArgs & {
55
68
  service?: Input<Omit<ServiceArgs, "cluster" | "namespace">>
56
- httpRoute?: Input<Omit<HttpRouteArgs, "cluster" | "namespace">>
69
+
70
+ /**
71
+ * The configuration for the access point route to create.
72
+ */
73
+ route?: Input<Omit<AccessPointRouteArgs, "endpoints" | "customData">>
74
+
75
+ /**
76
+ * The configuration for the access point routes to create.
77
+ */
78
+ routes?: InputArray<Omit<AccessPointRouteArgs, "endpoints" | "customData">>
57
79
 
58
80
  /**
59
81
  * The existing workload to patch.
@@ -61,7 +83,12 @@ export type ExposableWorkloadArgs = WorkloadArgs & {
61
83
  existing?: Input<k8s.ExposableWorkload>
62
84
  }
63
85
 
64
- export const exposableWorkloadExtraArgs = [...workloadExtraArgs, "service", "httpRoute"] as const
86
+ export const exposableWorkloadExtraArgs = [
87
+ ...workloadExtraArgs,
88
+ "service",
89
+ "route",
90
+ "routes",
91
+ ] as const
65
92
 
66
93
  export type ExposableWorkloadType = "Deployment" | "StatefulSet"
67
94
 
@@ -125,16 +152,20 @@ export function getWorkloadComponents(
125
152
  return output(rawVolumes.map(mapWorkloadVolume)).apply(uniqueBy(volume => volume.name))
126
153
  })
127
154
 
128
- const podSpec = output({ args, containers, volumes }).apply(({ args, containers, volumes }) => {
155
+ const podSpec = output({
156
+ cluster: output(args.namespace).cluster,
157
+ containers,
158
+ volumes,
159
+ }).apply(({ cluster, containers, volumes }) => {
129
160
  const spec = {
130
161
  volumes,
131
- containers: containers.map(container => mapContainerToRaw(container, args.cluster, name)),
162
+ containers: containers.map(container => mapContainerToRaw(container, cluster, name)),
132
163
  ...podSpecDefaults,
133
164
  } satisfies types.input.core.v1.PodSpec
134
165
 
135
166
  if (
136
167
  containers.some(container => container.enableTun) &&
137
- args.cluster.quirks?.tunDevicePolicy?.type !== "plugin"
168
+ cluster.quirks?.tunDevicePolicy?.type !== "plugin"
138
169
  ) {
139
170
  spec.volumes = output(spec.volumes).apply(volumes => [
140
171
  ...(volumes ?? []),
@@ -162,6 +193,7 @@ export function getWorkloadComponents(
162
193
  metadata: {
163
194
  labels,
164
195
  annotations: {
196
+ // to trigger a redeployment when the volumes change
165
197
  "highstate.io/dependency-hash": dependencyHash,
166
198
  },
167
199
  },
@@ -169,28 +201,30 @@ export function getWorkloadComponents(
169
201
  } satisfies types.input.core.v1.PodTemplateSpec
170
202
  })
171
203
 
172
- const networkPolicy = output({ args, containers }).apply(({ args, containers }) => {
204
+ const networkPolicy = output({ containers }).apply(({ containers }) => {
173
205
  const allowedEndpoints = containers.flatMap(container => container.allowedEndpoints ?? [])
174
206
 
175
207
  if (allowedEndpoints.length === 0 && !args.networkPolicy) {
176
208
  return output(undefined)
177
209
  }
178
210
 
179
- return NetworkPolicy.create(
180
- name,
181
- {
182
- cluster: args.cluster,
183
- namespace: args.namespace,
184
- selector: labels,
211
+ return output(
212
+ new NetworkPolicy(
213
+ name,
214
+ {
215
+ namespace: args.namespace,
216
+ selector: labels,
217
+ description: `Network policy for "${name}"`,
185
218
 
186
- ...args.networkPolicy,
219
+ ...args.networkPolicy,
187
220
 
188
- egressRules: [
189
- ...(args.networkPolicy?.egressRules ?? []),
190
- ...(allowedEndpoints.length > 0 ? [{ toEndpoints: allowedEndpoints }] : []),
191
- ],
192
- },
193
- { ...opts, parent: parent() },
221
+ egressRules: output(args.networkPolicy?.egressRules).apply(egressRules => [
222
+ ...(egressRules ?? []),
223
+ ...(allowedEndpoints.length > 0 ? [{ toEndpoints: allowedEndpoints }] : []),
224
+ ]),
225
+ },
226
+ { ...opts, parent: parent() },
227
+ ),
194
228
  )
195
229
  })
196
230
 
@@ -206,93 +240,79 @@ export function getExposableWorkloadComponents(
206
240
  const { labels, containers, volumes, podSpec, podTemplate, networkPolicy } =
207
241
  getWorkloadComponents(name, args, parent, opts)
208
242
 
209
- const service = output({ args, containers }).apply(async ({ args, containers }) => {
210
- if (!args.service && !args.httpRoute) {
243
+ const service = output({
244
+ existing: args.existing,
245
+ serviceArgs: args.service,
246
+ containers,
247
+ }).apply(({ existing, serviceArgs, containers }) => {
248
+ if (!args.service && !args.route) {
211
249
  return undefined
212
250
  }
213
251
 
214
- if (args.existing?.service) {
215
- return Service.of(name, args.existing.service, args.cluster, { ...opts, parent: parent() })
252
+ if (existing?.service) {
253
+ return Service.for(existing.service, output(args.namespace).cluster)
216
254
  }
217
255
 
218
- if (args.existing) {
256
+ if (existing) {
219
257
  return undefined
220
258
  }
221
259
 
222
260
  const ports = containers.flatMap(container => normalize(container.port, container.ports))
223
261
 
224
- return Service.create(
225
- name,
226
- {
227
- ...args.service,
228
- selector: labels,
229
- cluster: args.cluster,
230
- namespace: args.namespace,
231
-
232
- ports:
233
- // allow to completely override the ports
234
- !args.service?.port && !args.service?.ports
235
- ? ports.map(mapContainerPortToServicePort)
236
- : args.service?.ports,
237
- },
238
- {
239
- ...opts,
240
- parent: parent(),
241
- provider: await getProvider(args.cluster),
242
- },
243
- )
262
+ return Service.create(name, {
263
+ ...serviceArgs,
264
+ selector: labels,
265
+ namespace: args.namespace,
266
+
267
+ ports:
268
+ // allow to completely override the ports
269
+ !serviceArgs?.port && !serviceArgs?.ports
270
+ ? ports.map(mapContainerPortToServicePort)
271
+ : serviceArgs?.ports,
272
+ })
244
273
  })
245
274
 
246
- const httpRoute = output({
247
- args,
275
+ const routes = output({
276
+ routesArgs: normalizeInputs(args.route, args.routes),
248
277
  service,
249
- }).apply(async ({ args, service }) => {
250
- if (!args.httpRoute || !service) {
251
- return undefined
278
+ namespace: output(args.namespace),
279
+ }).apply(({ routesArgs, service, namespace }) => {
280
+ if (!routesArgs.length || !service) {
281
+ return []
252
282
  }
253
283
 
254
284
  if (args.existing) {
255
- return undefined
285
+ return []
256
286
  }
257
287
 
258
- return new HttpRoute(
259
- name,
260
- {
261
- ...args.httpRoute,
262
- cluster: args.cluster,
263
- rule: {
264
- backend: service,
265
- },
266
- },
267
- {
268
- ...opts,
269
- parent: parent(),
270
- provider: await getProvider(args.cluster),
271
- },
272
- )
288
+ return routesArgs.map(routeArgs => {
289
+ return new AccessPointRoute(name, {
290
+ ...routeArgs,
291
+ endpoints: service.endpoints,
292
+
293
+ // pass the native data to the route to allow implementation to use it
294
+ gatewayNativeData: service,
295
+ tlsCertificateNativeData: namespace,
296
+ })
297
+ })
273
298
  })
274
299
 
275
- return { labels, containers, volumes, podSpec, podTemplate, networkPolicy, service, httpRoute }
300
+ return { labels, containers, volumes, podSpec, podTemplate, networkPolicy, service, routes }
276
301
  }
277
302
 
278
- export abstract class Workload extends ComponentResource {
303
+ export abstract class Workload extends ScopedResource {
279
304
  protected constructor(
280
305
  type: string,
281
306
  protected readonly name: string,
282
- private readonly args: WorkloadArgs,
307
+ args: Inputs,
283
308
  opts: ComponentResourceOptions | undefined,
284
309
 
285
- protected readonly resourceType: string,
286
-
287
- /**
288
- * The cluster where the workload is created.
289
- */
290
- readonly cluster: Output<k8s.Cluster>,
291
-
292
- /**
293
- * The metadata of the underlying Kubernetes workload.
294
- */
295
- readonly metadata: Output<types.output.meta.v1.ObjectMeta>,
310
+ apiVersion: Output<string>,
311
+ kind: Output<string>,
312
+ protected readonly terminalArgs: Output<Unwrap<WorkloadTerminalArgs>>,
313
+ protected readonly containers: Output<Container[]>,
314
+ namespace: Output<Namespace>,
315
+ metadata: Output<types.output.meta.v1.ObjectMeta>,
296
316
 
297
317
  /**
298
318
  * The network policy associated with the workload.
@@ -301,81 +321,187 @@ export abstract class Workload extends ComponentResource {
301
321
  */
302
322
  readonly networkPolicy: Output<NetworkPolicy | undefined>,
303
323
  ) {
304
- super(type, name, args, opts)
324
+ super(type, name, args, opts, apiVersion, kind, namespace, metadata)
305
325
  }
306
326
 
327
+ protected abstract getTerminalMeta(): Output<UnitTerminal["meta"]>
328
+
307
329
  /**
308
330
  * The instance terminal to interact with the deployment.
309
331
  */
310
- get terminal(): Output<InstanceTerminal> {
311
- const containerName = output(this.args).apply(args => {
312
- const containers = normalize(args.container, args.containers)
313
-
332
+ get terminal(): Output<UnitTerminal> {
333
+ const containerName = output(this.containers).apply(containers => {
314
334
  return containers[0]?.name ?? this.name
315
335
  })
316
336
 
337
+ const shell = this.terminalArgs.apply(args => args.shell ?? "bash")
338
+
339
+ const workloadLabelQuery = output(this.metadata)
340
+ .apply(meta => meta.labels ?? {})
341
+ .apply(labels =>
342
+ Object.entries(labels)
343
+ .map(([key, value]) => `${key}=${value}`)
344
+ .join(","),
345
+ )
346
+
317
347
  return output({
318
348
  name: this.metadata.name,
319
- title: interpolate`${Workload.getResourceDisplayType(this.resourceType)} (${this.metadata.name})`,
320
- description: "Connect to the container of the workload",
321
- icon: "devicon:kubernetes",
322
-
323
- image: images["terminal-kubectl"].image,
324
-
325
- command: [
326
- "exec",
327
- "kubectl",
328
- "exec",
329
- "-it",
330
- "-n",
331
- this.metadata.namespace,
332
- interpolate`${this.resourceType}/${this.metadata.name}`,
333
- "-c",
334
- containerName,
335
- "--",
336
- this.args.terminalShell ?? "bash",
337
- ],
338
-
339
- files: {
340
- "/kubeconfig": this.cluster.kubeconfig,
341
- },
349
+ meta: this.getTerminalMeta(),
350
+
351
+ spec: {
352
+ image: images["terminal-kubectl"].image,
353
+ command: ["bash", "/welcome.sh"],
354
+
355
+ files: {
356
+ "/kubeconfig": fileFromString("kubeconfig", this.cluster.kubeconfig, { isSecret: true }),
357
+
358
+ "/welcome.sh": fileFromString(
359
+ "welcome.sh",
360
+ interpolate`
361
+ #!/bin/bash
362
+ set -euo pipefail
363
+
364
+ NAMESPACE="${this.metadata.namespace}"
365
+ RESOURCE_TYPE="${this.kind.apply(k => k.toLowerCase())}"
366
+ RESOURCE_NAME="${this.metadata.name}"
367
+ CONTAINER_NAME="${containerName}"
368
+ SHELL="${shell}"
369
+
370
+ echo "Connecting to $RESOURCE_TYPE \\"$RESOURCE_NAME\\" in namespace \\"$NAMESPACE\\""
371
+
372
+ # get all pods for this workload
373
+ PODS=$(kubectl get pods -n "$NAMESPACE" -l "${workloadLabelQuery}" -o jsonpath='{.items[*].metadata.name}' 2>/dev/null || echo "")
374
+
375
+ if [ -z "$PODS" ]; then
376
+ echo "No pods found"
377
+ exit 1
378
+ fi
379
+
380
+ # convert space-separated string to array
381
+ read -ra POD_ARRAY <<< "$PODS"
382
+
383
+ if [ \${#POD_ARRAY[@]} -eq 1 ]; then
384
+ # single pod found, connect directly
385
+ SELECTED_POD="\${POD_ARRAY[0]}"
386
+ echo "Found single pod: $SELECTED_POD"
387
+ else
388
+ # multiple pods found, use fzf for selection
389
+ echo "Found \${#POD_ARRAY[@]} pods. Please select one."
390
+
391
+ SELECTED_POD=$(printf '%s\n' "\${POD_ARRAY[@]}" | fzf --prompt="Select pod: " --height 10 --border --info=inline)
392
+
393
+ if [ -z "$SELECTED_POD" ]; then
394
+ echo "No pod selected"
395
+ exit 1
396
+ fi
397
+
398
+ echo "Selected pod: $SELECTED_POD"
399
+ fi
400
+
401
+ # execute into the selected pod
402
+ exec kubectl exec -it -n "$NAMESPACE" "$SELECTED_POD" -c "$CONTAINER_NAME" -- "$SHELL"
403
+ `.apply(trimIndentation),
404
+ ),
405
+ },
342
406
 
343
- env: {
344
- KUBECONFIG: "/kubeconfig",
407
+ env: {
408
+ KUBECONFIG: "/kubeconfig",
409
+ },
345
410
  },
346
411
  })
347
412
  }
348
413
 
349
- private static getResourceDisplayType(resourceType: string): string {
350
- switch (resourceType) {
351
- case "deployment":
352
- return "Deployment"
353
- case "statefulset":
354
- return "StatefulSet"
355
- case "daemonset":
356
- return "DaemonSet"
357
- default:
358
- return resourceType.charAt(0).toUpperCase() + resourceType.slice(1)
359
- }
414
+ /**
415
+ * Creates a terminal with a custom command.
416
+ *
417
+ * @param meta The metadata for the terminal.
418
+ * @param command The command to run in the terminal.
419
+ * @param spec Additional spec options for the terminal.
420
+ */
421
+ createTerminal(
422
+ name: string,
423
+ meta: UnitTerminal["meta"],
424
+ command: InputArray<string>,
425
+ spec?: { env?: DeepInput<TerminalSpec["env"]>; files?: DeepInput<TerminalSpec["files"]> },
426
+ ): Output<UnitTerminal> {
427
+ const containerName = output(this.containers).apply(containers => {
428
+ return containers[0]?.name ?? this.name
429
+ })
430
+
431
+ return output({
432
+ name,
433
+
434
+ meta: output(this.getTerminalMeta()).apply(currentMeta => ({
435
+ ...currentMeta,
436
+ ...meta,
437
+ })),
438
+
439
+ spec: {
440
+ image: images["terminal-kubectl"].image,
441
+
442
+ command: output(command).apply(command => [
443
+ "exec",
444
+ "kubectl",
445
+ "exec",
446
+ "-it",
447
+ "-n",
448
+ this.metadata.namespace,
449
+ interpolate`${this.kind.apply(k => k.toLowerCase())}/${this.metadata.name}`,
450
+ "-c",
451
+ containerName,
452
+ "--",
453
+ ...command,
454
+ ]),
455
+
456
+ files: {
457
+ "/kubeconfig": fileFromString("kubeconfig", this.cluster.kubeconfig, { isSecret: true }),
458
+ ...spec?.files,
459
+ },
460
+
461
+ env: {
462
+ KUBECONFIG: "/kubeconfig",
463
+ ...spec?.env,
464
+ },
465
+ },
466
+ })
360
467
  }
361
468
  }
362
469
 
363
470
  export abstract class ExposableWorkload extends Workload {
364
471
  protected constructor(
365
472
  type: string,
366
- protected readonly name: string,
367
- args: ExposableWorkloadArgs,
473
+ name: string,
474
+ args: Inputs,
368
475
  opts: ComponentResourceOptions | undefined,
369
476
 
370
- resourceType: string,
371
- cluster: Output<k8s.Cluster>,
477
+ apiVersion: Output<string>,
478
+ kind: Output<string>,
479
+ terminalArgs: Output<Unwrap<WorkloadTerminalArgs>>,
480
+ containers: Output<Container[]>,
481
+ namespace: Output<Namespace>,
372
482
  metadata: Output<types.output.meta.v1.ObjectMeta>,
373
483
  networkPolicy: Output<NetworkPolicy | undefined>,
374
484
 
375
485
  protected readonly _service: Output<Service | undefined>,
376
- protected readonly _httpRoute: Output<HttpRoute | undefined>,
486
+
487
+ /**
488
+ * The access point routes associated with the workload.
489
+ */
490
+ readonly routes: Output<AccessPointRoute[]>,
377
491
  ) {
378
- super(type, name, args, opts, resourceType, cluster, metadata, networkPolicy)
492
+ super(
493
+ type,
494
+ name,
495
+ args,
496
+ opts,
497
+ apiVersion,
498
+ kind,
499
+ terminalArgs,
500
+ containers,
501
+ namespace,
502
+ metadata,
503
+ networkPolicy,
504
+ )
379
505
  }
380
506
 
381
507
  /**
@@ -385,13 +511,6 @@ export abstract class ExposableWorkload extends Workload {
385
511
  return this._service
386
512
  }
387
513
 
388
- /**
389
- * The HTTP route associated with the workload.
390
- */
391
- get optionalHttpRoute(): Output<HttpRoute | undefined> {
392
- return this._httpRoute
393
- }
394
-
395
514
  /**
396
515
  * The service associated with the workload.
397
516
  *
@@ -407,21 +526,6 @@ export abstract class ExposableWorkload extends Workload {
407
526
  })
408
527
  }
409
528
 
410
- /**
411
- * The HTTP route associated with the workload.
412
- *
413
- * Will throw an error if the HTTP route is not available.
414
- */
415
- get httpRoute(): Output<HttpRoute> {
416
- return this._httpRoute.apply(httpRoute => {
417
- if (!httpRoute) {
418
- throw new Error(`The HTTP route of the workload "${this.name}" is not available.`)
419
- }
420
-
421
- return httpRoute
422
- })
423
- }
424
-
425
529
  /**
426
530
  * The entity of the workload.
427
531
  */
@@ -443,7 +547,7 @@ export abstract class ExposableWorkload extends Workload {
443
547
  opts?: CustomResourceOptions,
444
548
  ): Output<ExposableWorkload> {
445
549
  return output(args).apply(async args => {
446
- if (args.existing?.type === "k8s.deployment") {
550
+ if (args.existing?.type === "deployment") {
447
551
  const { Deployment } = await import("./deployment")
448
552
 
449
553
  return Deployment.patch(
@@ -451,13 +555,13 @@ export abstract class ExposableWorkload extends Workload {
451
555
  {
452
556
  ...deepmerge(args, args.deployment),
453
557
  name: args.existing.metadata.name,
454
- namespace: args.existing.metadata.namespace,
558
+ namespace: Namespace.forResourceAsync(args.existing, output(args.namespace).cluster),
455
559
  },
456
560
  opts,
457
561
  )
458
562
  }
459
563
 
460
- if (args.existing?.type === "k8s.stateful-set") {
564
+ if (args.existing?.type === "stateful-set") {
461
565
  const { StatefulSet } = await import("./stateful-set")
462
566
 
463
567
  return StatefulSet.patch(
@@ -465,7 +569,7 @@ export abstract class ExposableWorkload extends Workload {
465
569
  {
466
570
  ...deepmerge(args, args.statefulSet),
467
571
  name: args.existing.metadata.name,
468
- namespace: args.existing.metadata.namespace,
572
+ namespace: Namespace.forResourceAsync(args.existing, output(args.namespace).cluster),
469
573
  },
470
574
  opts,
471
575
  )
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cluster.ts"],"names":[],"mappings":";;;;;AAMA,SAAS,YAAY,EAAA,EAAY;AAC/B,EAAA,MAAM,cAAA,GAAiB,0BAAA;AACvB,EAAA,OAAO,cAAA,CAAe,KAAK,EAAE,CAAA;AAC/B;AAEA,eAAsB,iBAAA,CACpB,YACA,iBAAA,EACmB;AACnB,EAAA,MAAM,OAAA,GAAU,UAAA,CAAW,aAAA,CAAc,SAAS,CAAA;AAClD,EAAA,MAAM,KAAA,GAAQ,MAAM,OAAA,CAAQ,QAAA,EAAS;AAErC,EAAA,OAAO,KAAA,CAAM,KAAA,CAAM,OAAA,CAAQ,CAAA,IAAA,KAAQ;AACjC,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,MAAA,EAAQ,SAAA,IAAa,EAAC;AAC7C,IAAA,MAAM,aAAa,SAAA,CAAU,IAAA,CAAK,CAAA,OAAA,KAAW,OAAA,CAAQ,SAAS,YAAY,CAAA;AAC1E,IAAA,MAAM,aAAa,SAAA,CAAU,IAAA,CAAK,CAAA,OAAA,KAAW,OAAA,CAAQ,SAAS,YAAY,CAAA;AAE1E,IAAA,MAAM,SAAmB,EAAC;AAE1B,IAAA,IAAI,YAAY,OAAA,EAAS;AACvB,MAAA,MAAA,CAAO,IAAA,CAAK,WAAW,OAAO,CAAA;AAAA,IAChC;AAEA,IAAA,IAAI,UAAA,EAAY,OAAA,IAAW,iBAAA,KAAsB,QAAA,EAAU;AACzD,MAAA,MAAA,CAAO,IAAA,CAAK,WAAW,OAAO,CAAA;AAAA,IAChC;AAEA,IAAA,IAAI,UAAA,EAAY,WAAW,iBAAA,KAAsB,QAAA,IAAY,CAAC,WAAA,CAAY,UAAA,CAAW,OAAO,CAAA,EAAG;AAC7F,MAAA,MAAA,CAAO,IAAA,CAAK,WAAW,OAAO,CAAA;AAAA,IAChC;AAEA,IAAA,OAAO,MAAA;AAAA,EACT,CAAC,CAAA;AACH;AAEO,SAAS,kBAAkB,UAAA,EAA6C;AAC7E,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,YAAA;AAAA,IACN,KAAA,EAAO,oBAAA;AAAA,IACP,WAAA,EAAa,2CAAA;AAAA,IACb,IAAA,EAAM,oBAAA;AAAA,IAEN,KAAA,EAAO,cAAA,CAAO,kBAAkB,CAAA,CAAE,KAAA;AAAA,IAClC,OAAA,EAAS,CAAC,MAAA,EAAQ,aAAa,CAAA;AAAA,IAE/B,KAAA,EAAO;AAAA,MACL,aAAA,EAAe,OAAO,UAAU,CAAA;AAAA,MAEhC,aAAA,EAAe,IAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA,MAAA;AAAA,KASjB;AAAA,IAEA,GAAA,EAAK;AAAA,MACH,UAAA,EAAY;AAAA;AACd,GACF;AACF","file":"chunk-5C2BJGES.js","sourcesContent":["import type { k8s } from \"@highstate/library\"\nimport { text } from \"@highstate/contract\"\nimport { secret, type Input, type InstanceTerminal } from \"@highstate/pulumi\"\nimport { CoreV1Api, type KubeConfig } from \"@kubernetes/client-node\"\nimport { images } from \"./shared\"\n\nfunction isPrivateIp(ip: string) {\n const privateIpRegex = /^(10|172\\.16|192\\.168)\\./\n return privateIpRegex.test(ip)\n}\n\nexport async function detectExternalIps(\n kubeConfig: KubeConfig,\n internalIpsPolicy: k8s.InternalIpsPolicy,\n): Promise<string[]> {\n const nodeApi = kubeConfig.makeApiClient(CoreV1Api)\n const nodes = await nodeApi.listNode()\n\n return nodes.items.flatMap(node => {\n const addresses = node.status?.addresses ?? []\n const externalIp = addresses.find(address => address.type === \"ExternalIP\")\n const internalIp = addresses.find(address => address.type === \"InternalIP\")\n\n const result: string[] = []\n\n if (externalIp?.address) {\n result.push(externalIp.address)\n }\n\n if (internalIp?.address && internalIpsPolicy === \"always\") {\n result.push(internalIp.address)\n }\n\n if (internalIp?.address && internalIpsPolicy === \"public\" && !isPrivateIp(internalIp.address)) {\n result.push(internalIp.address)\n }\n\n return result\n })\n}\n\nexport function createK8sTerminal(kubeconfig: Input<string>): InstanceTerminal {\n return {\n name: \"management\",\n title: \"Cluster Management\",\n description: \"Manage the cluster using kubectl and helm\",\n icon: \"devicon:kubernetes\",\n\n image: images[\"terminal-kubectl\"].image,\n command: [\"bash\", \"/welcome.sh\"],\n\n files: {\n \"/kubeconfig\": secret(kubeconfig),\n\n \"/welcome.sh\": text`\n echo \"Connecting to the cluster...\"\n kubectl cluster-info\n\n echo \"Use 'kubectl' and 'helm' to manage the cluster.\"\n echo\n\n exec bash\n `,\n },\n\n env: {\n KUBECONFIG: \"/kubeconfig\",\n },\n }\n}\n"]}