@highstate/k8s 0.9.30 → 0.9.32

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 (45) hide show
  1. package/dist/{chunk-4YC2SLCU.js → chunk-2F2IID7N.js} +8 -3
  2. package/dist/chunk-2F2IID7N.js.map +1 -0
  3. package/dist/{chunk-64LNVTHJ.js → chunk-6FUARTRJ.js} +5 -5
  4. package/dist/{chunk-64LNVTHJ.js.map → chunk-6FUARTRJ.js.map} +1 -1
  5. package/dist/{chunk-2FKGGWPJ.js → chunk-6IVLEA6D.js} +3 -3
  6. package/dist/{chunk-2FKGGWPJ.js.map → chunk-6IVLEA6D.js.map} +1 -1
  7. package/dist/{chunk-HOARC4LU.js → chunk-GEXFRUDO.js} +40 -23
  8. package/dist/chunk-GEXFRUDO.js.map +1 -0
  9. package/dist/{chunk-XYJNM2EN.js → chunk-OEOMZP2C.js} +114 -12
  10. package/dist/chunk-OEOMZP2C.js.map +1 -0
  11. package/dist/{chunk-ARG3I734.js → chunk-WJQSKV2K.js} +3 -3
  12. package/dist/{chunk-ARG3I734.js.map → chunk-WJQSKV2K.js.map} +1 -1
  13. package/dist/deployment-BT6MUIT5.js +8 -0
  14. package/dist/{deployment-PKZ4IQMG.js.map → deployment-BT6MUIT5.js.map} +1 -1
  15. package/dist/highstate.manifest.json +2 -2
  16. package/dist/impl/gateway-route.js +295 -73
  17. package/dist/impl/gateway-route.js.map +1 -1
  18. package/dist/impl/tls-certificate.js +2 -2
  19. package/dist/impl/tls-certificate.js.map +1 -1
  20. package/dist/index.js +7 -6
  21. package/dist/index.js.map +1 -1
  22. package/dist/stateful-set-O44CY62K.js +8 -0
  23. package/dist/{stateful-set-OMPM2F7W.js.map → stateful-set-O44CY62K.js.map} +1 -1
  24. package/dist/units/cert-manager/index.js +4 -4
  25. package/dist/units/gateway-api/index.js +1 -1
  26. package/dist/units/gateway-api/index.js.map +1 -1
  27. package/dist/units/reduced-access-cluster/index.js +3 -3
  28. package/package.json +10 -10
  29. package/src/container.ts +8 -0
  30. package/src/gateway/gateway.ts +22 -5
  31. package/src/gateway/http-route.ts +6 -1
  32. package/src/gateway/index.ts +2 -0
  33. package/src/gateway/tcp-route.ts +87 -0
  34. package/src/gateway/udp-route.ts +87 -0
  35. package/src/impl/gateway-route.ts +418 -85
  36. package/src/impl/tls-certificate.ts +1 -1
  37. package/src/index.ts +1 -0
  38. package/src/tls.ts +9 -1
  39. package/src/units/gateway-api/index.ts +1 -1
  40. package/src/workload.ts +56 -21
  41. package/dist/chunk-4YC2SLCU.js.map +0 -1
  42. package/dist/chunk-HOARC4LU.js.map +0 -1
  43. package/dist/chunk-XYJNM2EN.js.map +0 -1
  44. package/dist/deployment-PKZ4IQMG.js +0 -8
  45. package/dist/stateful-set-OMPM2F7W.js +0 -8
@@ -0,0 +1,87 @@
1
+ import type { Gateway } from "./gateway"
2
+ import { gateway, type types } from "@highstate/gateway-api"
3
+ import {
4
+ ComponentResource,
5
+ type ComponentResourceOptions,
6
+ type Input,
7
+ type InputArray,
8
+ normalizeInputsAndMap,
9
+ type Output,
10
+ output,
11
+ } from "@highstate/pulumi"
12
+ import { getProvider, mapMetadata, type ScopedResourceArgs } from "../shared"
13
+ import { type BackendRef, resolveBackendRef } from "./backend"
14
+
15
+ export type UdpRouteArgs = Omit<ScopedResourceArgs, "namespace"> & {
16
+ /**
17
+ * The gateway to associate with the route.
18
+ */
19
+ gateway: Input<Gateway>
20
+
21
+ /**
22
+ * The name of the listener to attach the route to.
23
+ */
24
+ listenerName: Input<string>
25
+
26
+ /**
27
+ * The backend reference handled by the route.
28
+ */
29
+ backend?: Input<BackendRef>
30
+
31
+ /**
32
+ * The backend references handled by the route.
33
+ */
34
+ backends?: InputArray<BackendRef>
35
+ }
36
+
37
+ export class UdpRoute extends ComponentResource {
38
+ /**
39
+ * The underlying Kubernetes resource.
40
+ */
41
+ public readonly route: Output<gateway.v1alpha2.UDPRoute>
42
+
43
+ constructor(name: string, args: UdpRouteArgs, opts?: ComponentResourceOptions) {
44
+ super("highstate:k8s:UdpRoute", name, args, opts)
45
+
46
+ const gatewayOutput = output(args.gateway)
47
+
48
+ const parentRefs = output({
49
+ gateway: gatewayOutput,
50
+ listenerName: args.listenerName,
51
+ }).apply(
52
+ ({ gateway, listenerName }) =>
53
+ [
54
+ {
55
+ group: "gateway.networking.k8s.io",
56
+ kind: "Gateway",
57
+ name: gateway.metadata.name,
58
+ namespace: gateway.namespace.metadata.name,
59
+ sectionName: listenerName,
60
+ },
61
+ ] satisfies types.input.gateway.v1alpha2.UDPRouteSpecParentRefs[],
62
+ )
63
+
64
+ const backendRefs = normalizeInputsAndMap(args.backend, args.backends, resolveBackendRef)
65
+
66
+ this.route = gatewayOutput.cluster.apply(cluster => {
67
+ return new gateway.v1alpha2.UDPRoute(
68
+ name,
69
+ {
70
+ metadata: mapMetadata(args, name).apply(metadata => ({
71
+ ...metadata,
72
+ namespace: gatewayOutput.namespace.metadata.name,
73
+ })),
74
+ spec: {
75
+ parentRefs,
76
+ rules: [
77
+ {
78
+ backendRefs,
79
+ },
80
+ ],
81
+ } satisfies types.input.gateway.v1alpha2.UDPRouteSpec,
82
+ },
83
+ { ...opts, parent: this, provider: getProvider(cluster) },
84
+ )
85
+ })
86
+ }
87
+ }
@@ -1,9 +1,14 @@
1
1
  import type { Secret } from "../secret"
2
- import { filterEndpoints, gatewayRouteMediator, type TlsCertificate } from "@highstate/common"
3
- import { k8s } from "@highstate/library"
4
- import { type Input, toPromise } from "@highstate/pulumi"
2
+ import {
3
+ filterEndpoints,
4
+ type GatewayRouteSpec,
5
+ gatewayRouteMediator,
6
+ type TlsCertificate,
7
+ } from "@highstate/common"
8
+ import { k8s, type network } from "@highstate/library"
9
+ import { type ComponentResourceOptions, type Input, toPromise } from "@highstate/pulumi"
5
10
  import { core } from "@pulumi/kubernetes"
6
- import { Gateway, HttpRoute } from "../gateway"
11
+ import { Gateway, HttpRoute, TcpRoute, UdpRoute } from "../gateway"
7
12
  import { Namespace } from "../namespace"
8
13
  import { l4EndpointToServicePort, Service } from "../service"
9
14
  import { getProvider, mapMetadata } from "../shared"
@@ -21,107 +26,209 @@ export const createGatewayRoute = gatewayRouteMediator.implement(
21
26
 
22
27
  const certificateRef = certSecret
23
28
  ? {
24
- kind: "Secret",
25
- group: "",
29
+ kind: "Secret" as const,
30
+ group: "" as const,
26
31
  name: certSecret.metadata.name,
27
32
  }
28
33
  : undefined
29
34
 
30
- const gateway = await Gateway.createOnce(
31
- {
35
+ if (spec.type === "http") {
36
+ return await createHttpGatewayRoute({
32
37
  name,
38
+ spec,
39
+ opts,
40
+ data,
33
41
  namespace,
34
- gatewayClassName: data.className,
35
- listeners: [
36
- {
37
- name: "https",
38
- port: data.httpsPort,
39
- protocol: "HTTPS",
40
- tls: {
41
- mode: "Terminate",
42
- certificateRefs: certificateRef ? [certificateRef] : undefined,
43
- },
44
- },
45
- ],
46
- },
42
+ certificateRef,
43
+ })
44
+ }
45
+
46
+ const protocol = spec.type === "tcp" ? "TCP" : "UDP"
47
+
48
+ return await createL4GatewayRoute({
49
+ name,
50
+ spec,
47
51
  opts,
48
- )
52
+ data,
53
+ namespace,
54
+ protocol,
55
+ })
56
+ },
57
+ )
49
58
 
50
- // 1. short path - just create an HTTP route backed by a service
51
- if (spec.nativeData instanceof Service) {
52
- const httpRoute = new HttpRoute(
53
- name,
54
- {
55
- gateway,
56
- rule: { backend: spec.nativeData },
57
- },
58
- opts,
59
- )
59
+ type HttpGatewayRouteSpec = Extract<GatewayRouteSpec, { type: "http" }>
60
+ type L4GatewayRouteSpec = Extract<GatewayRouteSpec, { type: "tcp" | "udp" }>
60
61
 
61
- return {
62
- resource: httpRoute,
63
- endpoints: await toPromise(gateway.endpoints),
62
+ type CreateHttpGatewayRouteArgs = {
63
+ name: string
64
+ spec: HttpGatewayRouteSpec
65
+ opts: ComponentResourceOptions | undefined
66
+ data: k8s.GatewayData
67
+ namespace: Namespace
68
+ certificateRef:
69
+ | {
70
+ kind: "Secret"
71
+ group: ""
72
+ name: Input<string>
64
73
  }
65
- }
74
+ | undefined
75
+ }
66
76
 
67
- // 2. long path - create a virtual service with provided endpoints
68
- const endpoints = await toPromise(spec.endpoints)
69
- const hostnameEndpoints = filterEndpoints(endpoints, undefined, ["hostname"])
70
- const ipEndpoints = filterEndpoints(endpoints, undefined, ["ipv4", "ipv6"])
77
+ async function createHttpGatewayRoute({
78
+ name,
79
+ spec,
80
+ opts,
81
+ data,
82
+ namespace,
83
+ certificateRef,
84
+ }: CreateHttpGatewayRouteArgs) {
85
+ const backendService =
86
+ spec.nativeData instanceof Service
87
+ ? spec.nativeData
88
+ : (await createServiceFromEndpoints(name, namespace, spec.endpoints, data.cluster, opts))
89
+ .service
71
90
 
72
- let service: Service
91
+ const listeners = [
92
+ {
93
+ name: "https",
94
+ port: data.httpsPort,
95
+ protocol: "HTTPS",
96
+ tls: {
97
+ mode: "Terminate",
98
+ certificateRefs: certificateRef ? [certificateRef] : undefined,
99
+ },
100
+ },
101
+ ]
73
102
 
74
- if (
75
- hostnameEndpoints.length > 0 &&
76
- hostnameEndpoints[0].visibility > ipEndpoints[0]?.visibility
77
- ) {
78
- // if the hostname endpoints are more visible, create a service for the first hostname with ExternalName
79
- service = Service.create(`hs-backend-${name}`, {
80
- namespace,
81
- type: "ExternalName",
82
- externalName: hostnameEndpoints[0].hostname,
83
- ports: hostnameEndpoints.map(l4EndpointToServicePort),
84
- })
85
- } else {
86
- // otherwise, create a headless service and populate it with IPs
87
- service = Service.create(`hs-backend-${name}`, {
88
- namespace,
89
- type: "ClusterIP",
90
- ports: ipEndpoints.map(l4EndpointToServicePort),
91
- })
103
+ const gateway = await Gateway.createOnce(
104
+ {
105
+ name: data.className,
106
+ namespace,
107
+ gatewayClassName: data.className,
108
+ listeners,
109
+ },
110
+ opts,
111
+ )
112
+
113
+ const httpRoute = new HttpRoute(
114
+ name,
115
+ {
116
+ gateway,
117
+ rule: {
118
+ backend: backendService,
119
+ },
120
+ },
121
+ opts,
122
+ )
123
+
124
+ return {
125
+ resource: httpRoute,
126
+ endpoints: await toPromise(gateway.endpoints),
127
+ }
128
+ }
129
+
130
+ type CreateL4GatewayRouteArgs = {
131
+ name: string
132
+ spec: L4GatewayRouteSpec
133
+ opts: ComponentResourceOptions | undefined
134
+ data: k8s.GatewayData
135
+ namespace: Namespace
136
+ protocol: "TCP" | "UDP"
137
+ }
138
+
139
+ async function createL4GatewayRoute({
140
+ name,
141
+ spec,
142
+ opts,
143
+ data,
144
+ namespace,
145
+ protocol,
146
+ }: CreateL4GatewayRouteArgs) {
147
+ const serviceData =
148
+ spec.nativeData instanceof Service
149
+ ? {
150
+ service: spec.nativeData,
151
+ ports: await getServicePorts(spec.nativeData),
152
+ }
153
+ : await createServiceFromEndpoints(name, namespace, spec.endpoints, data.cluster, opts)
154
+
155
+ const serviceName = await toPromise(serviceData.service.metadata.name)
156
+
157
+ const backendPort = await selectBackendPort({
158
+ ports: serviceData.ports,
159
+ protocol,
160
+ targetPort: spec.targetPort,
161
+ serviceName,
162
+ routeName: name,
163
+ })
92
164
 
93
- const endpointsName = `hs-backend-${name}`
165
+ const listenerPort = await resolveListenerPort({
166
+ requestedPort: spec.port,
167
+ backendPort,
168
+ protocol,
169
+ routeName: name,
170
+ })
94
171
 
95
- new core.v1.Endpoints(
96
- endpointsName,
172
+ const listenerName = `${protocol.toLowerCase()}-${listenerPort}`
173
+
174
+ const gateway = await Gateway.createOnce(
175
+ {
176
+ name: data.className,
177
+ namespace,
178
+ gatewayClassName: data.className,
179
+ listeners: [
97
180
  {
98
- metadata: mapMetadata({ namespace }, endpointsName),
99
- subsets: ipEndpoints.map(endpoint => ({
100
- addresses: [{ ip: endpoint.address }],
101
- ports: [l4EndpointToServicePort(endpoint)],
102
- })),
181
+ name: listenerName,
182
+ port: listenerPort,
183
+ protocol,
103
184
  },
104
- { ...opts, provider: getProvider(data.cluster), parent: service },
185
+ ],
186
+ },
187
+ opts,
188
+ )
189
+
190
+ const backendRef = serviceData.service.metadata.apply(metadata => {
191
+ if (!metadata?.name) {
192
+ throw new Error(
193
+ `Service "${serviceName}" referenced by gateway route "${name}" does not have a name.`,
105
194
  )
106
195
  }
107
196
 
108
- const httpRoute = new HttpRoute(
109
- name,
110
- {
111
- gateway,
112
- rule: {
113
- backend: service,
114
- },
115
- },
116
- opts,
117
- )
118
-
119
197
  return {
120
- resource: httpRoute,
121
- endpoints: await toPromise(gateway.endpoints),
198
+ name: metadata.name,
199
+ namespace: metadata.namespace,
200
+ port: backendPort.port,
122
201
  }
123
- },
124
- )
202
+ })
203
+
204
+ const routeOpts = { ...opts, parent: gateway }
205
+
206
+ const route =
207
+ protocol === "TCP"
208
+ ? new TcpRoute(
209
+ name,
210
+ {
211
+ gateway,
212
+ listenerName,
213
+ backend: backendRef,
214
+ },
215
+ routeOpts,
216
+ )
217
+ : new UdpRoute(
218
+ name,
219
+ {
220
+ gateway,
221
+ listenerName,
222
+ backend: backendRef,
223
+ },
224
+ routeOpts,
225
+ )
226
+
227
+ return {
228
+ resource: route,
229
+ endpoints: await toPromise(gateway.endpoints),
230
+ }
231
+ }
125
232
 
126
233
  async function getCertificateSecret(
127
234
  _name: string,
@@ -143,13 +250,239 @@ async function getCertificateSecret(
143
250
  const targetClusterId = await toPromise(namespace.cluster.id)
144
251
 
145
252
  if (certNamespace === targetNamespace && certClusterId === targetClusterId) {
146
- // 1. short path - same namespace and cluster, just return the secret
147
253
  return await toPromise(resource.secret)
148
254
  }
149
255
  }
150
256
 
151
- // 2. long path - create a new secret in the target namespace with the certificate data
152
257
  throw new Error(
153
258
  "Not implemented: copying certificate secret across namespaces/clusters/different systems",
154
259
  )
155
260
  }
261
+
262
+ type ServicePortInfo = {
263
+ name: string | undefined
264
+ port: number
265
+ protocol: "TCP" | "UDP"
266
+ targetPort?: number | string
267
+ }
268
+
269
+ async function createServiceFromEndpoints(
270
+ name: string,
271
+ namespace: Namespace,
272
+ endpointsInput: Input<network.L4Endpoint[]>,
273
+ cluster: k8s.Cluster,
274
+ opts: ComponentResourceOptions | undefined,
275
+ ): Promise<{ service: Service; ports: ServicePortInfo[] }> {
276
+ const endpoints = await toPromise(endpointsInput)
277
+
278
+ if (!endpoints.length) {
279
+ throw new Error(`Gateway route "${name}" has no endpoints to expose.`)
280
+ }
281
+
282
+ const hostnameEndpoints = filterEndpoints(endpoints, undefined, ["hostname"])
283
+ const ipEndpoints = filterEndpoints(endpoints, undefined, ["ipv4", "ipv6"])
284
+
285
+ if (
286
+ hostnameEndpoints.length > 0 &&
287
+ hostnameEndpoints[0].visibility > ipEndpoints[0]?.visibility
288
+ ) {
289
+ const hostnamePortInfos: ServicePortInfo[] = []
290
+ for (const endpoint of hostnameEndpoints) {
291
+ hostnamePortInfos.push(toServicePortInfoFromEndpoint(endpoint))
292
+ }
293
+
294
+ const service = Service.create(`hs-backend-${name}`, {
295
+ namespace,
296
+ type: "ExternalName",
297
+ externalName: hostnameEndpoints[0].hostname,
298
+ ports: hostnameEndpoints.map(l4EndpointToServicePort),
299
+ })
300
+
301
+ return {
302
+ service,
303
+ ports: hostnamePortInfos,
304
+ }
305
+ }
306
+
307
+ if (ipEndpoints.length === 0) {
308
+ throw new Error(`Gateway route "${name}" requires at least one IP endpoint.`)
309
+ }
310
+
311
+ const ipPortInfos: ServicePortInfo[] = []
312
+ for (const endpoint of ipEndpoints) {
313
+ ipPortInfos.push(toServicePortInfoFromEndpoint(endpoint))
314
+ }
315
+
316
+ const service = Service.create(`hs-backend-${name}`, {
317
+ namespace,
318
+ type: "ClusterIP",
319
+ ports: ipEndpoints.map(l4EndpointToServicePort),
320
+ })
321
+
322
+ const endpointsName = `hs-backend-${name}`
323
+
324
+ new core.v1.Endpoints(
325
+ endpointsName,
326
+ {
327
+ metadata: mapMetadata({ namespace }, endpointsName),
328
+ subsets: ipEndpoints.map(endpoint => ({
329
+ addresses: [{ ip: endpoint.address }],
330
+ ports: [l4EndpointToServicePort(endpoint)],
331
+ })),
332
+ },
333
+ { ...opts, provider: getProvider(cluster), parent: service },
334
+ )
335
+
336
+ return {
337
+ service,
338
+ ports: ipPortInfos,
339
+ }
340
+ }
341
+
342
+ async function getServicePorts(service: Service): Promise<ServicePortInfo[]> {
343
+ const spec = await toPromise(service.spec)
344
+ const ports = spec.ports ?? []
345
+
346
+ const result: ServicePortInfo[] = []
347
+
348
+ for (const port of ports) {
349
+ const value = port.port
350
+ const protocol = (port.protocol ?? "TCP").toUpperCase()
351
+
352
+ if (value === undefined || (protocol !== "TCP" && protocol !== "UDP")) {
353
+ continue
354
+ }
355
+
356
+ result.push({
357
+ name: port.name ?? undefined,
358
+ port: value,
359
+ protocol: protocol as "TCP" | "UDP",
360
+ targetPort: port.targetPort as number | string | undefined,
361
+ })
362
+ }
363
+
364
+ return result
365
+ }
366
+
367
+ function toServicePortInfoFromEndpoint(endpoint: network.L4Endpoint): ServicePortInfo {
368
+ return {
369
+ name: undefined,
370
+ port: endpoint.port,
371
+ protocol: endpoint.protocol.toUpperCase() as "TCP" | "UDP",
372
+ targetPort: endpoint.port,
373
+ }
374
+ }
375
+
376
+ async function selectBackendPort({
377
+ ports,
378
+ protocol,
379
+ targetPort,
380
+ serviceName,
381
+ routeName,
382
+ }: {
383
+ ports: ServicePortInfo[]
384
+ protocol: "TCP" | "UDP"
385
+ targetPort: Input<string | number | undefined> | undefined
386
+ serviceName: string
387
+ routeName: string
388
+ }): Promise<ServicePortInfo> {
389
+ const candidates = ports.filter(port => port.protocol === protocol)
390
+
391
+ if (candidates.length === 0) {
392
+ throw new Error(
393
+ `Service "${serviceName}" does not expose any ${protocol} ports required by gateway route "${routeName}".`,
394
+ )
395
+ }
396
+
397
+ if (!targetPort) {
398
+ return candidates[0]
399
+ }
400
+
401
+ const resolvedTarget = await toPromise(targetPort)
402
+
403
+ if (resolvedTarget === undefined || resolvedTarget === null) {
404
+ return candidates[0]
405
+ }
406
+
407
+ if (typeof resolvedTarget === "number") {
408
+ const match = candidates.find(candidate => {
409
+ if (candidate.port === resolvedTarget) {
410
+ return true
411
+ }
412
+
413
+ if (typeof candidate.targetPort === "number") {
414
+ return candidate.targetPort === resolvedTarget
415
+ }
416
+
417
+ return false
418
+ })
419
+
420
+ if (match) {
421
+ return match
422
+ }
423
+
424
+ throw new Error(
425
+ `Gateway route "${routeName}" requested target port ${resolvedTarget}, but service "${serviceName}" does not expose it for ${protocol} backends.`,
426
+ )
427
+ }
428
+
429
+ const targetString = String(resolvedTarget)
430
+
431
+ const match = candidates.find(candidate => {
432
+ if (candidate.name === targetString) {
433
+ return true
434
+ }
435
+
436
+ if (typeof candidate.targetPort === "string") {
437
+ return candidate.targetPort === targetString
438
+ }
439
+
440
+ return false
441
+ })
442
+
443
+ if (match) {
444
+ return match
445
+ }
446
+
447
+ throw new Error(
448
+ `Gateway route "${routeName}" requested target port "${targetString}", but service "${serviceName}" does not expose it for ${protocol} backends.`,
449
+ )
450
+ }
451
+
452
+ async function resolveListenerPort({
453
+ requestedPort,
454
+ backendPort,
455
+ protocol,
456
+ routeName,
457
+ }: {
458
+ requestedPort: Input<number | undefined> | undefined
459
+ backendPort: ServicePortInfo
460
+ protocol: "TCP" | "UDP"
461
+ routeName: string
462
+ }): Promise<number> {
463
+ if (!requestedPort) {
464
+ return backendPort.port
465
+ }
466
+
467
+ const resolved = await toPromise(requestedPort)
468
+
469
+ if (resolved === undefined || resolved === null) {
470
+ return backendPort.port
471
+ }
472
+
473
+ if (!Number.isInteger(resolved)) {
474
+ throw new Error(
475
+ `Gateway route "${routeName}" must use integer listener ports for ${protocol.toLowerCase()} traffic.`,
476
+ )
477
+ }
478
+
479
+ const port = Number(resolved)
480
+
481
+ if (port < 1 || port > 65535) {
482
+ throw new Error(
483
+ `Gateway route "${routeName}" specified listener port ${port}, which is outside the valid range 1-65535.`,
484
+ )
485
+ }
486
+
487
+ return port
488
+ }
@@ -25,7 +25,7 @@ export const createCertificate = tlsCertificateMediator.implement(
25
25
  name: data.clusterIssuerName,
26
26
  kind: "ClusterIssuer",
27
27
  },
28
- secretName: `hs.certificate.${name}`,
28
+ secretName: `hs-certificate-${name}`,
29
29
  },
30
30
  { ...opts, provider },
31
31
  )
package/src/index.ts CHANGED
@@ -18,5 +18,6 @@ export * from "./secret"
18
18
  export * from "./service"
19
19
  export * from "./shared"
20
20
  export * from "./stateful-set"
21
+ export * from "./tls"
21
22
  export * from "./worker"
22
23
  export * from "./workload"
package/src/tls.ts CHANGED
@@ -30,6 +30,8 @@ export type CreateOrGetCertificateArgs = CertificateArgs & {
30
30
  * Represents a cert-manager Certificate resource with metadata and secret.
31
31
  */
32
32
  export abstract class Certificate extends ComponentResource {
33
+ private _secret?: Output<Secret>
34
+
33
35
  protected constructor(
34
36
  type: string,
35
37
  private readonly name: string,
@@ -82,7 +84,11 @@ export abstract class Certificate extends ComponentResource {
82
84
  * The secret containing the certificate data.
83
85
  */
84
86
  get secret(): Output<Secret> {
85
- return output({
87
+ if (this._secret) {
88
+ return this._secret
89
+ }
90
+
91
+ this._secret = output({
86
92
  secretName: this.spec.apply(spec => spec.secretName),
87
93
  namespace: this.namespace,
88
94
  }).apply(({ secretName, namespace }) => {
@@ -91,6 +97,8 @@ export abstract class Certificate extends ComponentResource {
91
97
  namespace,
92
98
  })
93
99
  })
100
+
101
+ return this._secret
94
102
  }
95
103
 
96
104
  /**
@@ -10,7 +10,7 @@ const provider = await getProviderAsync(inputs.k8sCluster)
10
10
  new yaml.v2.ConfigFile(
11
11
  "gateway-api",
12
12
  {
13
- file: "https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/standard-install.yaml",
13
+ file: "https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/experimental-install.yaml",
14
14
  },
15
15
  { provider },
16
16
  )