@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/service.ts CHANGED
@@ -1,27 +1,29 @@
1
- import type { k8s, network } from "@highstate/library"
2
- import { core, types } from "@pulumi/kubernetes"
1
+ import { filterEndpoints, l4EndpointToString, parseL3Endpoint } from "@highstate/common"
2
+ import { check, getOrCreate } from "@highstate/contract"
3
+ import { k8s, type network } from "@highstate/library"
3
4
  import {
4
- ComponentResource,
5
- normalize,
6
- output,
7
- Output,
8
5
  type ComponentResourceOptions,
9
6
  type Input,
10
7
  type Inputs,
8
+ interpolate,
9
+ normalize,
10
+ type Output,
11
+ output,
12
+ toPromise,
11
13
  } from "@highstate/pulumi"
12
- import { omit, uniqueBy } from "remeda"
14
+ import { core, type types } from "@pulumi/kubernetes"
13
15
  import { deepmerge } from "deepmerge-ts"
14
- import { filterEndpoints, l4EndpointToString, parseL3Endpoint } from "@highstate/common"
16
+ import { omit, uniqueBy } from "remeda"
17
+ import { Namespace } from "./namespace"
15
18
  import {
16
19
  commonExtraArgs,
20
+ getProvider,
17
21
  mapMetadata,
18
- resourceIdToString,
19
- type CommonArgs,
20
- type ResourceId,
21
- type SelectorLike,
22
+ ScopedResource,
23
+ type ScopedResourceArgs,
22
24
  } from "./shared"
23
25
 
24
- export type ServiceArgs = CommonArgs & {
26
+ export type ServiceArgs = ScopedResourceArgs & {
25
27
  /**
26
28
  * The port to expose the service on.
27
29
  */
@@ -32,64 +34,17 @@ export type ServiceArgs = CommonArgs & {
32
34
  *
33
35
  * The type of the service will be determined automatically based on the cluster.
34
36
  */
35
- external?: boolean
37
+ external?: Input<boolean>
36
38
  } & types.input.core.v1.ServiceSpec
37
39
 
38
- const serviceExtraArgs = [...commonExtraArgs, "port", "ports", "external"] as const
39
-
40
- export type ServiceEndpointMetadata = {
41
- clusterId: string
42
- name: string
43
- namespace: string
44
- selector: SelectorLike
45
- targetPort: string | number
46
- }
47
-
48
- /**
49
- * Checks if the endpoint has service metadata.
50
- *
51
- * Alters the type of the endpoint to include the service metadata if it exists.
52
- *
53
- * @param endpoint The endpoint to check.
54
- * @returns True if the endpoint has service metadata, false otherwise.
55
- */
56
- export function hasServiceMetadata(
57
- endpoint: network.L3Endpoint,
58
- ): endpoint is network.L3Endpoint & { metadata: { k8sService: ServiceEndpointMetadata } } {
59
- return endpoint.metadata?.k8sService !== undefined
60
- }
61
-
62
- /**
63
- * Returns the service metadata of the endpoint.
64
- *
65
- * @param endpoint The endpoint to get the service metadata from.
66
- * @returns The service metadata of the endpoint, or undefined if it doesn't exist.
67
- */
68
- export function getServiceMetadata(
69
- endpoint: network.L3Endpoint,
70
- ): ServiceEndpointMetadata | undefined {
71
- return endpoint.metadata?.k8sService as ServiceEndpointMetadata
40
+ export type CreateOrGetServiceArgs = ServiceArgs & {
41
+ /**
42
+ * The service entity to patch/retrieve.
43
+ */
44
+ existing: Input<k8s.Service> | undefined
72
45
  }
73
46
 
74
- /**
75
- * Adds service metadata to the endpoint.
76
- *
77
- * @param endpoint The endpoint to add the metadata to.
78
- * @param metadata The metadata to add.
79
- * @returns The endpoint with the added metadata.
80
- */
81
- export function withServiceMetadata<TEdnpoint extends network.L34Endpoint>(
82
- endpoint: TEdnpoint,
83
- metadata: ServiceEndpointMetadata,
84
- ): TEdnpoint & { metadata: { k8sService: ServiceEndpointMetadata } } {
85
- return {
86
- ...endpoint,
87
- metadata: {
88
- ...endpoint.metadata,
89
- k8sService: metadata,
90
- },
91
- }
92
- }
47
+ const serviceExtraArgs = [...commonExtraArgs, "port", "ports", "external"] as const
93
48
 
94
49
  /**
95
50
  * Checks if the endpoint is from the given cluster.
@@ -98,29 +53,30 @@ export function withServiceMetadata<TEdnpoint extends network.L34Endpoint>(
98
53
  * @param cluster The cluster to check against.
99
54
  * @returns True if the endpoint is from the cluster, false otherwise.
100
55
  */
101
- export function isFromCluster(
56
+ export function isEndpointFromCluster(
102
57
  endpoint: network.L3Endpoint,
103
58
  cluster: k8s.Cluster,
104
- ): endpoint is network.L3Endpoint & { metadata: { k8sService: ServiceEndpointMetadata } } {
105
- return getServiceMetadata(endpoint)?.clusterId === cluster.id
59
+ ): endpoint is k8s.ServiceEndpoint {
60
+ return (
61
+ check(k8s.serviceEndpointSchema, endpoint) &&
62
+ endpoint.metadata["k8s.service"].clusterId === cluster.id
63
+ )
106
64
  }
107
65
 
108
- export abstract class Service extends ComponentResource {
66
+ /**
67
+ * Represents a Kubernetes Service resource with endpoints and metadata.
68
+ */
69
+ export abstract class Service extends ScopedResource {
109
70
  protected constructor(
110
71
  type: string,
111
72
  name: string,
112
73
  args: Inputs,
113
74
  opts: ComponentResourceOptions | undefined,
114
75
 
115
- /**
116
- * The cluster info associated with the service.
117
- */
118
- readonly cluster: Output<k8s.Cluster>,
119
-
120
- /**
121
- * The metadata of the underlying Kubernetes service.
122
- */
123
- readonly metadata: Output<types.output.meta.v1.ObjectMeta>,
76
+ apiVersion: Output<string>,
77
+ kind: Output<string>,
78
+ namespace: Output<Namespace>,
79
+ metadata: Output<types.output.meta.v1.ObjectMeta>,
124
80
 
125
81
  /**
126
82
  * The spec of the underlying Kubernetes service.
@@ -132,7 +88,7 @@ export abstract class Service extends ComponentResource {
132
88
  */
133
89
  readonly status: Output<types.output.core.v1.ServiceStatus>,
134
90
  ) {
135
- super(type, name, args, opts)
91
+ super(type, name, args, opts, apiVersion, kind, namespace, metadata)
136
92
  }
137
93
 
138
94
  /**
@@ -140,57 +96,135 @@ export abstract class Service extends ComponentResource {
140
96
  */
141
97
  get entity(): Output<k8s.Service> {
142
98
  return output({
143
- type: "k8s.service",
99
+ type: "service",
144
100
  clusterId: this.cluster.id,
101
+ clusterName: this.cluster.name,
145
102
  metadata: this.metadata,
146
103
  endpoints: this.endpoints,
147
104
  })
148
105
  }
149
106
 
150
- static create(name: string, args: ServiceArgs, opts: ComponentResourceOptions): Service {
107
+ /**
108
+ * Creates a new service.
109
+ */
110
+ static create(name: string, args: ServiceArgs, opts?: ComponentResourceOptions): Service {
151
111
  return new CreatedService(name, args, opts)
152
112
  }
153
113
 
154
- static wrap(
114
+ /**
115
+ * Creates a new service or patches an existing one.
116
+ *
117
+ * @param name The name of the resource. May not be the same as the service name.
118
+ * @param args The arguments to create or patch the service with.
119
+ * @param opts Optional resource options.
120
+ */
121
+ static createOrPatch(
155
122
  name: string,
156
- service: Input<core.v1.Service>,
157
- cluster: Input<k8s.Cluster>,
123
+ args: CreateOrGetServiceArgs,
158
124
  opts?: ComponentResourceOptions,
159
125
  ): Service {
160
- return new WrappedService(name, service, cluster, opts)
126
+ if (args.existing) {
127
+ return new ServicePatch(name, {
128
+ ...args,
129
+ name: output(args.existing).metadata.name,
130
+ namespace: Namespace.forResourceAsync(args.existing, output(args.namespace).cluster),
131
+ })
132
+ }
133
+
134
+ return new CreatedService(name, args, opts)
161
135
  }
162
136
 
163
- static get(
137
+ /**
138
+ * Creates a new service or gets an existing one.
139
+ *
140
+ * @param name The name of the resource. May not be the same as the service name. Will not be used when existing service is retrieved.
141
+ * @param args The arguments to create or get the service with.
142
+ * @param opts Optional resource options.
143
+ */
144
+ static async createOrGet(
164
145
  name: string,
165
- id: ResourceId,
166
- cluster: Input<k8s.Cluster>,
146
+ args: CreateOrGetServiceArgs,
167
147
  opts?: ComponentResourceOptions,
168
- ): Service {
169
- return new ExternalService(name, id, cluster, opts)
148
+ ): Promise<Service> {
149
+ if (args.existing) {
150
+ return await Service.forAsync(args.existing, output(args.namespace).cluster)
151
+ }
152
+
153
+ return new CreatedService(name, args, opts)
170
154
  }
171
155
 
172
- static of(
173
- name: string,
174
- entity: Input<k8s.Service>,
175
- cluster: Input<k8s.Cluster>,
176
- opts?: ComponentResourceOptions,
177
- ): Service {
178
- return new ExternalService(
179
- name,
180
- output(entity).metadata,
181
- output({ cluster, entity }).apply(({ cluster, entity }) => {
182
- if (cluster.id !== entity.clusterId) {
183
- throw new Error(
184
- `Cluster mismatch when wrapping service "${name}": "${cluster.id}" != "${entity.clusterId}"`,
185
- )
186
- }
187
-
188
- return cluster
189
- }),
190
- opts,
156
+ /**
157
+ * Patches an existing service.
158
+ *
159
+ * Will throw an error if the service does not exist.
160
+ *
161
+ * @param name The name of the resource. May not be the same as the service name.
162
+ * @param args The arguments to patch the service with.
163
+ * @param opts Optional resource options.
164
+ */
165
+ static patch(name: string, args: ServiceArgs, opts?: ComponentResourceOptions): Service {
166
+ return new ServicePatch(name, args, opts)
167
+ }
168
+
169
+ /**
170
+ * Wraps an existing Kubernetes service.
171
+ */
172
+ static wrap(name: string, args: WrappedServiceArgs, opts?: ComponentResourceOptions): Service {
173
+ return new WrappedService(name, args, opts)
174
+ }
175
+
176
+ /**
177
+ * Gets an existing service.
178
+ *
179
+ * Will throw an error if the service does not exist.
180
+ */
181
+ static get(name: string, args: ExternalServiceArgs, opts?: ComponentResourceOptions): Service {
182
+ return new ExternalService(name, args, opts)
183
+ }
184
+
185
+ private static readonly serviceCache = new Map<string, Service>()
186
+
187
+ /**
188
+ * Gets an existing service for a given entity.
189
+ * Prefer this method over `get` when possible.
190
+ *
191
+ * It automatically names the resource with the following format: `{clusterName}.{namespace}.{name}.{clusterId}`.
192
+ *
193
+ * This method is idempotent and will return the same instance for the same entity.
194
+ *
195
+ * @param entity The entity to get the service for.
196
+ * @param cluster The cluster where the service is located.
197
+ */
198
+ static for(entity: k8s.Service, cluster: Input<k8s.Cluster>): Service {
199
+ return getOrCreate(
200
+ Service.serviceCache,
201
+ `${entity.clusterName}.${entity.metadata.namespace}.${entity.metadata.name}.${entity.clusterId}`,
202
+ name => {
203
+ return Service.get(name, {
204
+ name: entity.metadata.name,
205
+ namespace: Namespace.forResourceAsync(entity, cluster),
206
+ })
207
+ },
191
208
  )
192
209
  }
193
210
 
211
+ /**
212
+ * Gets an existing service for a given entity.
213
+ * Prefer this method over `get` when possible.
214
+ *
215
+ * It automatically names the resource with the following format: `{clusterName}.{namespace}.{name}.{clusterId}`.
216
+ *
217
+ * This method is idempotent and will return the same instance for the same entity.
218
+ *
219
+ * @param entity The entity to get the service for.
220
+ * @param cluster The cluster where the service is located.
221
+ */
222
+ static async forAsync(entity: Input<k8s.Service>, cluster: Input<k8s.Cluster>): Promise<Service> {
223
+ const resolvedEntity = await toPromise(entity)
224
+
225
+ return Service.for(resolvedEntity, output(cluster))
226
+ }
227
+
194
228
  /**
195
229
  * Returns the endpoints of the service applying the given filter.
196
230
  *
@@ -199,30 +233,29 @@ export abstract class Service extends ComponentResource {
199
233
  * @param filter If specified, the endpoints are filtered based on the given filter.
200
234
  * @returns The endpoints of the service.
201
235
  */
202
- filterEndpoints(filter?: network.EndpointFilter): Output<network.L4Endpoint[]> {
203
- return output({ endpoints: this.endpoints }).apply(({ endpoints }) => {
204
- return filterEndpoints(endpoints, filter)
205
- })
236
+ filterEndpoints(filter?: network.EndpointFilter): Output<k8s.ServiceEndpoint[]> {
237
+ return output(this.endpoints).apply(endpoints => filterEndpoints(endpoints, filter))
206
238
  }
207
239
 
208
240
  /**
209
241
  * Returns the endpoints of the service including both internal and external endpoints.
210
242
  */
211
- get endpoints(): Output<network.L4Endpoint[]> {
243
+ get endpoints(): Output<k8s.ServiceEndpoint[]> {
212
244
  return output({
213
245
  cluster: this.cluster,
214
246
  metadata: this.metadata,
215
247
  spec: this.spec,
216
248
  status: this.status,
217
249
  }).apply(({ cluster, metadata, spec, status }) => {
218
- const endpointMetadata = {
219
- k8sService: {
250
+ const endpointMetadata: k8s.EndpointServiceMetadata = {
251
+ "k8s.service": {
220
252
  clusterId: cluster.id,
253
+ clusterName: cluster.name,
221
254
  name: metadata.name,
222
255
  namespace: metadata.namespace,
223
256
  selector: spec.selector,
224
257
  targetPort: spec.ports[0].targetPort ?? spec.ports[0].port,
225
- } satisfies ServiceEndpointMetadata,
258
+ },
226
259
  }
227
260
 
228
261
  const clusterIpEndpoints = spec.clusterIPs?.map(ip => ({
@@ -270,33 +303,48 @@ export abstract class Service extends ComponentResource {
270
303
  ...(loadBalancerEndpoints ?? []),
271
304
  ...(nodePortEndpoints ?? []),
272
305
  ],
273
- endpoint => l4EndpointToString(endpoint),
306
+ l4EndpointToString,
274
307
  )
275
308
  })
276
309
  }
277
310
  }
278
311
 
312
+ /**
313
+ * Creates the service spec configuration based on arguments and cluster settings.
314
+ *
315
+ * @param args The service arguments containing port and external configuration.
316
+ * @param cluster The cluster where the service will be created.
317
+ * @returns The service spec configuration.
318
+ */
319
+ function createServiceSpec(args: ServiceArgs, cluster: k8s.Cluster) {
320
+ return output(args).apply(args => {
321
+ return deepmerge(
322
+ {
323
+ ports: normalize(args.port, args.ports),
324
+
325
+ externalIPs: args.external
326
+ ? args.externalIPs
327
+ ? args.externalIPs
328
+ : cluster.externalIps
329
+ : normalize(undefined, args.externalIPs),
330
+
331
+ type: getServiceType(args, cluster),
332
+ },
333
+ omit(args, serviceExtraArgs),
334
+ )
335
+ })
336
+ }
337
+
279
338
  class CreatedService extends Service {
280
339
  constructor(name: string, args: ServiceArgs, opts?: ComponentResourceOptions) {
281
- const service = output(args).apply(args => {
340
+ const service = output(args.namespace).cluster.apply(cluster => {
282
341
  return new core.v1.Service(
283
342
  name,
284
343
  {
285
344
  metadata: mapMetadata(args, name),
286
- spec: deepmerge(
287
- {
288
- ports: normalize(args.port, args.ports),
289
-
290
- externalIPs: args.external
291
- ? (args.externalIPs ?? args.cluster.externalIps)
292
- : args.cluster.externalIps,
293
-
294
- type: getServiceType(args, args.cluster),
295
- },
296
- omit(args, serviceExtraArgs),
297
- ),
345
+ spec: createServiceSpec(args, cluster),
298
346
  },
299
- { parent: this, ...opts },
347
+ { ...opts, parent: this, provider: getProvider(cluster) },
300
348
  )
301
349
  })
302
350
 
@@ -306,7 +354,9 @@ class CreatedService extends Service {
306
354
  args,
307
355
  opts,
308
356
 
309
- output(args.cluster),
357
+ service.apiVersion,
358
+ service.kind,
359
+ output(args.namespace),
310
360
  service.metadata,
311
361
  service.spec,
312
362
  service.status,
@@ -314,50 +364,96 @@ class CreatedService extends Service {
314
364
  }
315
365
  }
316
366
 
367
+ class ServicePatch extends Service {
368
+ constructor(name: string, args: ServiceArgs, opts?: ComponentResourceOptions) {
369
+ const service = output(args.namespace).cluster.apply(cluster => {
370
+ return new core.v1.ServicePatch(
371
+ name,
372
+ {
373
+ metadata: mapMetadata(args, name),
374
+ spec: createServiceSpec(args, cluster),
375
+ },
376
+ { ...opts, parent: this, provider: getProvider(cluster) },
377
+ )
378
+ })
379
+
380
+ super(
381
+ "highstate:k8s:ServicePatch",
382
+ name,
383
+ args,
384
+ opts,
385
+
386
+ service.apiVersion,
387
+ service.kind,
388
+ output(args.namespace),
389
+ service.metadata,
390
+ service.spec,
391
+ service.status,
392
+ )
393
+ }
394
+ }
395
+
396
+ export type WrappedServiceArgs = {
397
+ /**
398
+ * The underlying Kubernetes service to wrap.
399
+ */
400
+ service: Input<core.v1.Service>
401
+
402
+ /**
403
+ * The namespace where the service is located.
404
+ */
405
+ namespace: Input<Namespace>
406
+ }
407
+
317
408
  class WrappedService extends Service {
318
- constructor(
319
- name: string,
320
- service: Input<core.v1.Service>,
321
- cluster: Input<k8s.Cluster>,
322
- opts?: ComponentResourceOptions,
323
- ) {
409
+ constructor(name: string, args: WrappedServiceArgs, opts?: ComponentResourceOptions) {
324
410
  super(
325
411
  "highstate:k8s:WrappedService",
326
412
  name,
327
- { service, clusterInfo: cluster },
413
+ args,
328
414
  opts,
329
415
 
330
- output(cluster),
331
- output(service).metadata,
332
- output(service).spec,
333
- output(service).status,
416
+ output(args.service).apiVersion,
417
+ output(args.service).kind,
418
+ output(args.namespace),
419
+ output(args.service).metadata,
420
+ output(args.service).spec,
421
+ output(args.service).status,
334
422
  )
335
423
  }
336
424
  }
337
425
 
426
+ export type ExternalServiceArgs = {
427
+ /**
428
+ * The name of the service to get.
429
+ */
430
+ name: Input<string>
431
+
432
+ /**
433
+ * The namespace of the service to get.
434
+ */
435
+ namespace: Input<Namespace>
436
+ }
437
+
338
438
  class ExternalService extends Service {
339
- constructor(
340
- name: string,
341
- id: Input<ResourceId>,
342
- cluster: Input<k8s.Cluster>,
343
- opts?: ComponentResourceOptions,
344
- ) {
345
- const service = output(id).apply(id => {
439
+ constructor(name: string, args: ExternalServiceArgs, opts?: ComponentResourceOptions) {
440
+ const service = output(args.namespace).cluster.apply(cluster => {
346
441
  return core.v1.Service.get(
347
- //
348
442
  name,
349
- resourceIdToString(id),
350
- { ...opts, parent: this },
443
+ interpolate`${output(args.namespace).metadata.name}/${args.name}`,
444
+ { ...opts, parent: this, provider: getProvider(cluster) },
351
445
  )
352
446
  })
353
447
 
354
448
  super(
355
449
  "highstate:k8s:ExternalService",
356
450
  name,
357
- { id, cluster },
451
+ args,
358
452
  opts,
359
453
 
360
- output(cluster),
454
+ service.apiVersion,
455
+ service.kind,
456
+ output(args.namespace),
361
457
  service.metadata,
362
458
  service.spec,
363
459
  service.status,
@@ -365,6 +461,12 @@ class ExternalService extends Service {
365
461
  }
366
462
  }
367
463
 
464
+ /**
465
+ * Maps a container port to a service port.
466
+ *
467
+ * @param port The container port to map.
468
+ * @returns The corresponding service port configuration.
469
+ */
368
470
  export function mapContainerPortToServicePort(
369
471
  port: types.input.core.v1.ContainerPort,
370
472
  ): types.input.core.v1.ServicePort {
@@ -376,6 +478,12 @@ export function mapContainerPortToServicePort(
376
478
  }
377
479
  }
378
480
 
481
+ /**
482
+ * Maps a service to a label selector.
483
+ *
484
+ * @param service The service to extract the label selector from.
485
+ * @returns The label selector based on the service's selector.
486
+ */
379
487
  export function mapServiceToLabelSelector(
380
488
  service: core.v1.Service,
381
489
  ): types.input.meta.v1.LabelSelector {
@@ -384,6 +492,13 @@ export function mapServiceToLabelSelector(
384
492
  }
385
493
  }
386
494
 
495
+ /**
496
+ * Determines the appropriate service type based on the service arguments and cluster configuration.
497
+ *
498
+ * @param service The service configuration containing type and external properties.
499
+ * @param cluster The cluster where the service will be created.
500
+ * @returns The service type to use.
501
+ */
387
502
  export function getServiceType(
388
503
  service: Pick<ServiceArgs, "type" | "external"> | undefined,
389
504
  cluster: k8s.Cluster,
@@ -398,3 +513,18 @@ export function getServiceType(
398
513
 
399
514
  return cluster.quirks?.externalServiceType === "LoadBalancer" ? "LoadBalancer" : "NodePort"
400
515
  }
516
+
517
+ /**
518
+ * Converts a network L4 endpoint to a Kubernetes service port.
519
+ *
520
+ * @param endpoint The L4 endpoint to convert.
521
+ * @returns The corresponding Kubernetes service port configuration.
522
+ */
523
+ export function l4EndpointToServicePort(
524
+ endpoint: network.L4Endpoint,
525
+ ): types.input.core.v1.ServicePort {
526
+ return {
527
+ port: endpoint.port,
528
+ protocol: endpoint.protocol.toUpperCase(),
529
+ }
530
+ }