@highstate/k8s 0.19.1 → 0.20.0

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 (105) hide show
  1. package/dist/{chunk-FE4SHRAJ.js → chunk-23X5SXQG.js} +22 -7
  2. package/dist/chunk-23X5SXQG.js.map +1 -0
  3. package/dist/{chunk-LGHFSXNT.js → chunk-ADHZK6V2.js} +14 -10
  4. package/dist/chunk-ADHZK6V2.js.map +1 -0
  5. package/dist/{chunk-VCXWCZ43.js → chunk-BTAEFJ5N.js} +27 -15
  6. package/dist/chunk-BTAEFJ5N.js.map +1 -0
  7. package/dist/{chunk-BR2CLUUD.js → chunk-IXE3OKB4.js} +27 -8
  8. package/dist/chunk-IXE3OKB4.js.map +1 -0
  9. package/dist/{chunk-TWBMG6TD.js → chunk-OG2OPX7B.js} +30 -12
  10. package/dist/chunk-OG2OPX7B.js.map +1 -0
  11. package/dist/{chunk-DCUMJSO6.js → chunk-P26SQ2ZB.js} +17 -51
  12. package/dist/chunk-P26SQ2ZB.js.map +1 -0
  13. package/dist/{chunk-MIC2BHGS.js → chunk-PG27ZY2H.js} +25 -7
  14. package/dist/chunk-PG27ZY2H.js.map +1 -0
  15. package/dist/chunk-PZYGZSN5.js +54 -0
  16. package/dist/{chunk-PZ5AY32C.js.map → chunk-PZYGZSN5.js.map} +1 -1
  17. package/dist/{chunk-YIJUVPU2.js → chunk-S77TE7UC.js} +27 -15
  18. package/dist/chunk-S77TE7UC.js.map +1 -0
  19. package/dist/{chunk-P2VOUU7E.js → chunk-SZKOAHNX.js} +383 -205
  20. package/dist/chunk-SZKOAHNX.js.map +1 -0
  21. package/dist/chunk-TOLFVF4S.js +889 -0
  22. package/dist/chunk-TOLFVF4S.js.map +1 -0
  23. package/dist/{chunk-RVB4WWZZ.js → chunk-TVKT3ZYX.js} +174 -18
  24. package/dist/chunk-TVKT3ZYX.js.map +1 -0
  25. package/dist/cron-job-RKB2HYTO.js +7 -0
  26. package/dist/{cron-job-NX4HD4FI.js.map → cron-job-RKB2HYTO.js.map} +1 -1
  27. package/dist/deployment-T35TUOL2.js +7 -0
  28. package/dist/{deployment-O2LJ5WR5.js.map → deployment-T35TUOL2.js.map} +1 -1
  29. package/dist/highstate.manifest.json +3 -2
  30. package/dist/impl/dynamic-endpoint-resolver.js +90 -0
  31. package/dist/impl/dynamic-endpoint-resolver.js.map +1 -0
  32. package/dist/impl/gateway-route.js +159 -62
  33. package/dist/impl/gateway-route.js.map +1 -1
  34. package/dist/impl/tls-certificate.js +6 -5
  35. package/dist/impl/tls-certificate.js.map +1 -1
  36. package/dist/index.js +106 -23
  37. package/dist/index.js.map +1 -1
  38. package/dist/job-PE4AKOHB.js +7 -0
  39. package/dist/job-PE4AKOHB.js.map +1 -0
  40. package/dist/stateful-set-LUIRHQJY.js +7 -0
  41. package/dist/{stateful-set-VJYKTQ72.js.map → stateful-set-LUIRHQJY.js.map} +1 -1
  42. package/dist/units/cert-manager/index.js +7 -8
  43. package/dist/units/cert-manager/index.js.map +1 -1
  44. package/dist/units/cluster-patch/index.js +6 -6
  45. package/dist/units/cluster-patch/index.js.map +1 -1
  46. package/dist/units/dns01-issuer/index.js +52 -15
  47. package/dist/units/dns01-issuer/index.js.map +1 -1
  48. package/dist/units/existing-cluster/index.js +39 -18
  49. package/dist/units/existing-cluster/index.js.map +1 -1
  50. package/dist/units/gateway-api/index.js +2 -2
  51. package/dist/units/reduced-access-cluster/index.js +8 -8
  52. package/dist/units/reduced-access-cluster/index.js.map +1 -1
  53. package/package.json +9 -7
  54. package/src/cluster.ts +12 -8
  55. package/src/config-map.ts +15 -5
  56. package/src/container.ts +4 -2
  57. package/src/cron-job.ts +25 -4
  58. package/src/deployment.ts +32 -17
  59. package/src/gateway/backend.ts +3 -3
  60. package/src/gateway/gateway.ts +12 -56
  61. package/src/helm.ts +354 -22
  62. package/src/impl/dynamic-endpoint-resolver.ts +109 -0
  63. package/src/impl/gateway-route.ts +231 -57
  64. package/src/impl/tls-certificate.ts +8 -3
  65. package/src/index.ts +1 -0
  66. package/src/job.ts +23 -5
  67. package/src/kubectl.ts +166 -0
  68. package/src/namespace.ts +47 -3
  69. package/src/network-policy.ts +1 -1
  70. package/src/pvc.ts +12 -2
  71. package/src/rbac.ts +28 -5
  72. package/src/scripting/environment.ts +3 -2
  73. package/src/secret.ts +15 -5
  74. package/src/service.ts +28 -6
  75. package/src/shared.ts +30 -2
  76. package/src/stateful-set.ts +32 -17
  77. package/src/tls.ts +31 -5
  78. package/src/units/cluster-patch/index.ts +5 -5
  79. package/src/units/dns01-issuer/index.ts +56 -12
  80. package/src/units/existing-cluster/index.ts +36 -15
  81. package/src/units/reduced-access-cluster/index.ts +6 -3
  82. package/src/worker.ts +4 -2
  83. package/src/workload.ts +453 -213
  84. package/dist/chunk-4G6LLC2X.js +0 -240
  85. package/dist/chunk-4G6LLC2X.js.map +0 -1
  86. package/dist/chunk-BR2CLUUD.js.map +0 -1
  87. package/dist/chunk-DCUMJSO6.js.map +0 -1
  88. package/dist/chunk-FE4SHRAJ.js.map +0 -1
  89. package/dist/chunk-KMLRI5UZ.js +0 -155
  90. package/dist/chunk-KMLRI5UZ.js.map +0 -1
  91. package/dist/chunk-LGHFSXNT.js.map +0 -1
  92. package/dist/chunk-MIC2BHGS.js.map +0 -1
  93. package/dist/chunk-OBDQONMV.js +0 -401
  94. package/dist/chunk-OBDQONMV.js.map +0 -1
  95. package/dist/chunk-P2VOUU7E.js.map +0 -1
  96. package/dist/chunk-PZ5AY32C.js +0 -9
  97. package/dist/chunk-RVB4WWZZ.js.map +0 -1
  98. package/dist/chunk-TWBMG6TD.js.map +0 -1
  99. package/dist/chunk-VCXWCZ43.js.map +0 -1
  100. package/dist/chunk-YIJUVPU2.js.map +0 -1
  101. package/dist/cron-job-NX4HD4FI.js +0 -8
  102. package/dist/deployment-O2LJ5WR5.js +0 -8
  103. package/dist/job-SYME6Y43.js +0 -8
  104. package/dist/job-SYME6Y43.js.map +0 -1
  105. package/dist/stateful-set-VJYKTQ72.js +0 -8
package/src/namespace.ts CHANGED
@@ -1,6 +1,6 @@
1
- import type { k8s } from "@highstate/library"
2
1
  import { getOrCreate } from "@highstate/contract"
3
- import { toPromise } from "@highstate/pulumi"
2
+ import { k8s } from "@highstate/library"
3
+ import { makeEntityOutput, toPromise } from "@highstate/pulumi"
4
4
  import { core, type types } from "@pulumi/kubernetes"
5
5
  import {
6
6
  type ComponentResourceOptions,
@@ -10,6 +10,7 @@ import {
10
10
  output,
11
11
  type Unwrap,
12
12
  } from "@pulumi/pulumi"
13
+ import { ClusterAccessScope } from "./rbac"
13
14
  import {
14
15
  getProvider,
15
16
  mapMetadata,
@@ -48,6 +49,13 @@ export abstract class Namespace extends Resource {
48
49
  static readonly apiVersion = "v1"
49
50
  static readonly kind = "Namespace"
50
51
 
52
+ /**
53
+ * The cluster entity authorized to port-forward into the namespace.
54
+ *
55
+ * Only created for namespaces created with `create` or `createOrGet` methods, not for wrapped or external namespaces.
56
+ */
57
+ portForwardCluster?: Output<k8s.Cluster>
58
+
51
59
  constructor(
52
60
  type: string,
53
61
  name: string,
@@ -74,7 +82,16 @@ export abstract class Namespace extends Resource {
74
82
  * The Highstate namespace entity.
75
83
  */
76
84
  get entity(): Output<k8s.Namespace> {
77
- return output(this.entityBase) as Output<k8s.Namespace>
85
+ return makeEntityOutput({
86
+ entity: k8s.namespaceEntity,
87
+ identity: this.metadata.uid,
88
+ meta: {
89
+ title: this.metadata.name,
90
+ },
91
+ value: {
92
+ ...this.entityBase,
93
+ },
94
+ })
78
95
  }
79
96
 
80
97
  /**
@@ -298,6 +315,33 @@ class CreatedNamespace extends Namespace {
298
315
  namespace.spec,
299
316
  namespace.status,
300
317
  )
318
+
319
+ const scope = new ClusterAccessScope(
320
+ `${name}-port-forward`,
321
+ {
322
+ namespace: this,
323
+ rules: [
324
+ {
325
+ apiGroups: [""],
326
+ resources: ["services"],
327
+ verbs: ["get"],
328
+ },
329
+ {
330
+ apiGroups: [""],
331
+ resources: ["pods"],
332
+ verbs: ["get", "list"],
333
+ },
334
+ {
335
+ apiGroups: [""],
336
+ resources: ["pods/portforward"],
337
+ verbs: ["create"],
338
+ },
339
+ ],
340
+ },
341
+ { parent: this },
342
+ )
343
+
344
+ this.portForwardCluster = scope.cluster
301
345
  }
302
346
  }
303
347
 
@@ -360,7 +360,7 @@ export type NetworkPolicyArgs = ScopedResourceArgs & {
360
360
  * The pod selector for this network policy.
361
361
  * If not provided, it will select all pods in the namespace.
362
362
  */
363
- selector?: SelectorLike
363
+ selector?: Input<SelectorLike>
364
364
 
365
365
  /**
366
366
  * The rule for incoming traffic.
package/src/pvc.ts CHANGED
@@ -1,10 +1,11 @@
1
- import type { k8s } from "@highstate/library"
2
1
  import { getOrCreate } from "@highstate/contract"
2
+ import { k8s } from "@highstate/library"
3
3
  import {
4
4
  type ComponentResourceOptions,
5
5
  type Input,
6
6
  type Inputs,
7
7
  interpolate,
8
+ makeEntityOutput,
8
9
  type Output,
9
10
  output,
10
11
  toPromise,
@@ -73,7 +74,16 @@ export abstract class PersistentVolumeClaim extends NamespacedResource {
73
74
  * The Highstate PVC entity.
74
75
  */
75
76
  get entity(): Output<k8s.PersistentVolumeClaim> {
76
- return output(this.entityBase)
77
+ return makeEntityOutput({
78
+ entity: k8s.persistentVolumeClaimEntity,
79
+ identity: this.metadata.uid,
80
+ meta: {
81
+ title: this.metadata.name,
82
+ },
83
+ value: {
84
+ ...this.entityBase,
85
+ },
86
+ })
77
87
  }
78
88
 
79
89
  /**
package/src/rbac.ts CHANGED
@@ -1,11 +1,12 @@
1
- import type { k8s } from "@highstate/library"
2
1
  import type { Namespace } from "./namespace"
2
+ import { common, type k8s } from "@highstate/library"
3
3
  import {
4
4
  ComponentResource,
5
5
  type ComponentResourceOptions,
6
6
  type Input,
7
7
  type InputArray,
8
8
  interpolate,
9
+ makeEntity,
9
10
  normalizeInputs,
10
11
  type Output,
11
12
  output,
@@ -15,7 +16,6 @@ import {
15
16
  import { KubeConfig } from "@kubernetes/client-node"
16
17
  import { core, rbac, type types } from "@pulumi/kubernetes"
17
18
  import { map, unique } from "remeda"
18
- import { stringify } from "yaml"
19
19
  import { Secret } from "./secret"
20
20
  import {
21
21
  getNamespaceName,
@@ -185,9 +185,14 @@ export class ClusterAccessScope extends ComponentResource {
185
185
  kubeconfig,
186
186
  newToken: accessTokenSecret.getValue("token"),
187
187
  serviceAccount: serviceAccount.metadata.name,
188
- }).apply(({ cluster, kubeconfig, newToken, serviceAccount }) => {
188
+ serviceAccountId: serviceAccount.metadata.uid,
189
+ }).apply(({ cluster, kubeconfig, newToken, serviceAccount, serviceAccountId }) => {
190
+ if (kubeconfig.content.type !== "embedded-secret") {
191
+ throw new Error("Only embedded secrets are supported for cluster kubeconfig for now")
192
+ }
193
+
189
194
  const config = new KubeConfig()
190
- config.loadFromString(kubeconfig)
195
+ config.loadFromString(kubeconfig.content.value.value)
191
196
 
192
197
  // clear all existing contexts and users
193
198
  config.users = []
@@ -205,7 +210,25 @@ export class ClusterAccessScope extends ComponentResource {
205
210
 
206
211
  return {
207
212
  ...cluster,
208
- kubeconfig: stringify(JSON.parse(config.exportConfig())),
213
+ connectionId: serviceAccountId,
214
+ kubeconfig: makeEntity({
215
+ entity: common.fileEntity,
216
+ identity: `${serviceAccountId}:kubeconfig`,
217
+ meta: {
218
+ title: `kubeconfig for SA ${serviceAccount}`,
219
+ },
220
+ value: {
221
+ content: {
222
+ type: "embedded-secret",
223
+ value: config.exportConfig(),
224
+ },
225
+ meta: {
226
+ name: "kubeconfig",
227
+ contentType: "text/yaml",
228
+ mode: 0o600,
229
+ },
230
+ },
231
+ }),
209
232
  }
210
233
  })
211
234
  }
@@ -1,6 +1,7 @@
1
1
  import type { InputEndpoint } from "@highstate/common"
2
2
  import type { Input, InputArray, InputRecord } from "@highstate/pulumi"
3
3
  import type { ContainerEnvironment, ContainerVolumeMount, WorkloadVolume } from "../container"
4
+ import { images } from ".."
4
5
 
5
6
  export type ScriptDistribution = "alpine" | "ubuntu"
6
7
 
@@ -94,7 +95,7 @@ const emptyDistributionEnvironment = {
94
95
  export const emptyScriptEnvironment: ResolvedScriptEnvironment = {
95
96
  alpine: {
96
97
  ...emptyDistributionEnvironment,
97
- image: "alpine@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c",
98
+ image: images.alpine.image,
98
99
  allowedEndpoints: [
99
100
  //
100
101
  "tcp://dl-cdn.alpinelinux.org:443",
@@ -104,7 +105,7 @@ export const emptyScriptEnvironment: ResolvedScriptEnvironment = {
104
105
 
105
106
  ubuntu: {
106
107
  ...emptyDistributionEnvironment,
107
- image: "ubuntu@sha256:72297848456d5d37d1262630108ab308d3e9ec7ed1c3286a32fe09856619a782",
108
+ image: images.ubuntu.image,
108
109
  allowedEndpoints: [
109
110
  //
110
111
  "tcp://archive.ubuntu.com:80",
package/src/secret.ts CHANGED
@@ -1,15 +1,16 @@
1
- import type { k8s } from "@highstate/library"
2
1
  import { getOrCreate } from "@highstate/contract"
3
- import { toPromise } from "@highstate/pulumi"
4
- import { core, type types } from "@pulumi/kubernetes"
2
+ import { k8s } from "@highstate/library"
5
3
  import {
6
4
  type ComponentResourceOptions,
7
5
  type Input,
8
6
  type Inputs,
9
7
  interpolate,
8
+ makeEntityOutput,
10
9
  type Output,
11
10
  output,
12
- } from "@pulumi/pulumi"
11
+ toPromise,
12
+ } from "@highstate/pulumi"
13
+ import { core, type types } from "@pulumi/kubernetes"
13
14
  import { Namespace } from "./namespace"
14
15
  import { getProvider, mapMetadata, NamespacedResource, type ScopedResourceArgs } from "./shared"
15
16
 
@@ -56,7 +57,16 @@ export abstract class Secret extends NamespacedResource {
56
57
  * The Highstate secret entity.
57
58
  */
58
59
  get entity(): Output<k8s.Secret> {
59
- return output(this.entityBase)
60
+ return makeEntityOutput({
61
+ entity: k8s.secretEntity,
62
+ identity: this.metadata.uid,
63
+ meta: {
64
+ title: this.metadata.name,
65
+ },
66
+ value: {
67
+ ...this.entityBase,
68
+ },
69
+ })
60
70
  }
61
71
 
62
72
  /**
package/src/service.ts CHANGED
@@ -6,12 +6,13 @@ import {
6
6
  parseL4Protocol,
7
7
  } from "@highstate/common"
8
8
  import { check, getOrCreate } from "@highstate/contract"
9
- import { k8s, type network } from "@highstate/library"
9
+ import { type ImplementationReference, k8s, type network } from "@highstate/library"
10
10
  import {
11
11
  type ComponentResourceOptions,
12
12
  type Input,
13
13
  type Inputs,
14
14
  interpolate,
15
+ makeEntityOutput,
15
16
  normalize,
16
17
  type Output,
17
18
  output,
@@ -101,9 +102,16 @@ export abstract class Service extends NamespacedResource {
101
102
  * The Highstate service entity.
102
103
  */
103
104
  get entity(): Output<k8s.Service> {
104
- return output({
105
- ...this.entityBase,
106
- endpoints: this.endpoints,
105
+ return makeEntityOutput({
106
+ entity: k8s.serviceEntity,
107
+ identity: this.metadata.uid,
108
+ meta: {
109
+ title: this.metadata.name,
110
+ },
111
+ value: {
112
+ ...this.entityBase,
113
+ endpoints: this.endpoints,
114
+ },
107
115
  })
108
116
  }
109
117
 
@@ -230,6 +238,8 @@ export abstract class Service extends NamespacedResource {
230
238
 
231
239
  /**
232
240
  * Returns the endpoints of the service including both internal and external endpoints.
241
+ *
242
+ * Also includes an `implRef` for port-forwarding if the namespace was created with port-forwarding enabled.
233
243
  */
234
244
  get endpoints(): Output<k8s.ServiceEndpoint[]> {
235
245
  return output({
@@ -237,7 +247,8 @@ export abstract class Service extends NamespacedResource {
237
247
  metadata: this.metadata,
238
248
  spec: this.spec,
239
249
  status: this.status,
240
- }).apply(({ cluster, metadata, spec, status }) => {
250
+ portForwardCluster: this.namespace.portForwardCluster,
251
+ }).apply(({ cluster, metadata, spec, status, portForwardCluster }) => {
241
252
  function createMetadata(isInternal: boolean): k8s.EndpointServiceMetadata {
242
253
  return {
243
254
  "k8s.service": {
@@ -252,6 +263,15 @@ export abstract class Service extends NamespacedResource {
252
263
  }
253
264
  }
254
265
 
266
+ const implRef: ImplementationReference | undefined = portForwardCluster
267
+ ? {
268
+ package: "@highstate/k8s",
269
+ data: {
270
+ cluster: portForwardCluster,
271
+ },
272
+ }
273
+ : undefined
274
+
255
275
  const internalHosts = spec.clusterIPs.length
256
276
  ? [...spec.clusterIPs, `${metadata.name}.${metadata.namespace}.svc.cluster.local`]
257
277
  : []
@@ -263,6 +283,7 @@ export abstract class Service extends NamespacedResource {
263
283
  parseEndpoint,
264
284
  endpoint => l3EndpointToL4(endpoint, port.port, parseL4Protocol(port.protocol)),
265
285
  endpoint => addEndpointMetadata(endpoint, createMetadata(true)),
286
+ endpoint => (implRef ? { ...endpoint, implRef } : endpoint),
266
287
  ),
267
288
  ),
268
289
  )
@@ -282,8 +303,9 @@ export abstract class Service extends NamespacedResource {
282
303
  pipe(
283
304
  ip,
284
305
  parseEndpoint,
285
- endpoint => l3EndpointToL4(endpoint, port.port, parseL4Protocol(port.protocol)),
306
+ endpoint => l3EndpointToL4(endpoint, port.nodePort, parseL4Protocol(port.protocol)),
286
307
  endpoint => addEndpointMetadata(endpoint, createMetadata(false)),
308
+ endpoint => (implRef ? { ...endpoint, implRef } : endpoint),
287
309
  ),
288
310
  ),
289
311
  )
package/src/shared.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { PartialKeys } from "@highstate/contract"
2
- import type { k8s } from "@highstate/library"
2
+ import type { common, k8s } from "@highstate/library"
3
3
  import {
4
4
  ComponentResource,
5
5
  type ComponentResourceOptions,
@@ -24,7 +24,13 @@ export function getProvider(cluster: k8s.Cluster): Provider {
24
24
  return existing
25
25
  }
26
26
 
27
- const provider = new Provider(name, { kubeconfig: secret(cluster.kubeconfig) })
27
+ if (cluster.kubeconfig.content.type !== "embedded-secret") {
28
+ throw new Error("Only embedded secrets are supported for cluster kubeconfig for now")
29
+ }
30
+
31
+ const provider = new Provider(name, {
32
+ kubeconfig: secret(cluster.kubeconfig.content.value.value),
33
+ })
28
34
  providers.set(name, provider)
29
35
 
30
36
  return provider
@@ -36,6 +42,28 @@ export async function getProviderAsync(cluster: Input<k8s.Cluster>): Promise<Pro
36
42
  return getProvider(resolvedCluster)
37
43
  }
38
44
 
45
+ export function getEmbeddedSecretFileContent(file: Input<common.File>): Output<string> {
46
+ return output(file).apply(file => {
47
+ if (file.content.type !== "embedded-secret") {
48
+ throw new Error("Only embedded-secret file contents are supported for kubeconfig for now")
49
+ }
50
+
51
+ return file.content.value.value
52
+ })
53
+ }
54
+
55
+ export function getClusterKubeconfigContent(cluster: Input<k8s.Cluster>): Output<string> {
56
+ return output(cluster).apply(cluster => {
57
+ if (cluster.kubeconfig.content.type !== "embedded-secret") {
58
+ throw new Error(
59
+ "Only embedded-secret file contents are supported for cluster kubeconfig for now",
60
+ )
61
+ }
62
+
63
+ return cluster.kubeconfig.content.value.value
64
+ })
65
+ }
66
+
39
67
  export type NamespaceLike = core.v1.Namespace | Namespace | string
40
68
 
41
69
  export type ScopedResourceArgs = {
@@ -1,14 +1,15 @@
1
1
  import type { AccessPointRoute } from "@highstate/common"
2
- import type { k8s } from "@highstate/library"
3
2
  import type { Container } from "./container"
4
3
  import type { NetworkPolicy } from "./network-policy"
5
4
  import type { Service } from "./service"
6
5
  import { getOrCreate, type UnitTerminal } from "@highstate/contract"
6
+ import { k8s } from "@highstate/library"
7
7
  import {
8
8
  type ComponentResourceOptions,
9
9
  type Input,
10
10
  type Inputs,
11
11
  interpolate,
12
+ makeEntityOutput,
12
13
  type Output,
13
14
  output,
14
15
  toPromise,
@@ -20,14 +21,15 @@ import { omit } from "remeda"
20
21
  import { Namespace } from "./namespace"
21
22
  import { getProvider, mapMetadata } from "./shared"
22
23
  import {
23
- ExposableWorkload,
24
- type ExposableWorkloadArgs,
25
- exposableWorkloadExtraArgs,
26
- getExposableWorkloadComponents,
24
+ filterPatchOwnedContainersInTemplate,
25
+ getWorkloadServiceComponents,
26
+ Workload,
27
+ type WorkloadServiceArgs,
27
28
  type WorkloadTerminalArgs,
29
+ workloadServiceExtraArgs,
28
30
  } from "./workload"
29
31
 
30
- export type StatefulSetArgs = Omit<ExposableWorkloadArgs, "existing"> &
32
+ export type StatefulSetArgs = Omit<WorkloadServiceArgs, "existing"> &
31
33
  Omit<Partial<types.input.apps.v1.StatefulSetSpec>, "template"> & {
32
34
  template?: {
33
35
  metadata?: types.input.meta.v1.ObjectMeta
@@ -42,7 +44,7 @@ export type CreateOrGetStatefulSetArgs = StatefulSetArgs & {
42
44
  existing: Input<k8s.StatefulSet> | undefined
43
45
  }
44
46
 
45
- export abstract class StatefulSet extends ExposableWorkload {
47
+ export abstract class StatefulSet extends Workload {
46
48
  static apiVersion = "apps/v1"
47
49
  static kind = "StatefulSet"
48
50
 
@@ -95,10 +97,17 @@ export abstract class StatefulSet extends ExposableWorkload {
95
97
  * The Highstate stateful set entity.
96
98
  */
97
99
  get entity(): Output<k8s.StatefulSet> {
98
- return output({
99
- ...this.entityBase,
100
- service: this.service.entity,
101
- endpoints: this.service.endpoints,
100
+ return makeEntityOutput({
101
+ entity: k8s.statefulSetEntity,
102
+ identity: this.metadata.uid,
103
+ meta: {
104
+ title: this.metadata.name,
105
+ },
106
+ value: {
107
+ ...this.entityBase,
108
+ service: this.service.entity,
109
+ spec: this.spec,
110
+ },
102
111
  })
103
112
  }
104
113
 
@@ -250,7 +259,7 @@ export abstract class StatefulSet extends ExposableWorkload {
250
259
  class CreatedStatefulSet extends StatefulSet {
251
260
  constructor(name: string, args: StatefulSetArgs, opts?: ComponentResourceOptions) {
252
261
  const { labels, podTemplate, networkPolicy, containers, service, routes } =
253
- getExposableWorkloadComponents(
262
+ getWorkloadServiceComponents(
254
263
  name,
255
264
  {
256
265
  ...args,
@@ -275,7 +284,7 @@ class CreatedStatefulSet extends StatefulSet {
275
284
  template: podTemplate,
276
285
  selector: { matchLabels: labels },
277
286
  },
278
- omit(args, exposableWorkloadExtraArgs),
287
+ omit(args, workloadServiceExtraArgs),
279
288
  ) as types.input.apps.v1.StatefulSetSpec
280
289
  },
281
290
  ),
@@ -312,7 +321,7 @@ class CreatedStatefulSet extends StatefulSet {
312
321
  class StatefulSetPatch extends StatefulSet {
313
322
  constructor(name: string, args: StatefulSetArgs, opts?: ComponentResourceOptions) {
314
323
  const { podTemplate, networkPolicy, containers, service, routes } =
315
- getExposableWorkloadComponents(name, args, () => this, opts, true)
324
+ getWorkloadServiceComponents(name, args, () => this, opts, true)
316
325
 
317
326
  const statefulSet = output(args.namespace).cluster.apply(cluster => {
318
327
  return new apps.v1.StatefulSetPatch(
@@ -320,10 +329,16 @@ class StatefulSetPatch extends StatefulSet {
320
329
  {
321
330
  metadata: mapMetadata(args, name),
322
331
  spec: output({ args, podTemplate }).apply(({ args, podTemplate }) => {
323
- return deepmerge(
332
+ const spec = deepmerge(
324
333
  { template: podTemplate },
325
- omit(args, exposableWorkloadExtraArgs),
326
- ) as types.input.apps.v1.StatefulSetSpec
334
+ omit(args, workloadServiceExtraArgs),
335
+ ) as Unwrap<types.input.apps.v1.StatefulSetSpec>
336
+
337
+ if (spec.template) {
338
+ spec.template = filterPatchOwnedContainersInTemplate(spec.template, podTemplate)
339
+ }
340
+
341
+ return spec
327
342
  }),
328
343
  },
329
344
  {
package/src/tls.ts CHANGED
@@ -1,12 +1,13 @@
1
- import type { k8s } from "@highstate/library"
2
1
  import type { types } from "@pulumi/kubernetes"
3
2
  import { cert_manager, type types as cmTypes } from "@highstate/cert-manager"
4
3
  import { getOrCreate } from "@highstate/contract"
4
+ import { k8s } from "@highstate/library"
5
5
  import {
6
6
  type ComponentResourceOptions,
7
7
  type Input,
8
8
  type Inputs,
9
9
  interpolate,
10
+ makeEntityOutput,
10
11
  type Output,
11
12
  output,
12
13
  toPromise,
@@ -31,6 +32,22 @@ export type CreateOrGetCertificateArgs = CertificateArgs & {
31
32
  existing: Input<k8s.Certificate> | undefined
32
33
  }
33
34
 
35
+ const certManagerCertificateWaitFor = "condition=Ready"
36
+
37
+ function mapCertificateMetadata(
38
+ args: CertificateArgs,
39
+ fallbackName: string,
40
+ ): Output<types.input.meta.v1.ObjectMeta> {
41
+ return mapMetadata(args, fallbackName).apply(metadata => ({
42
+ ...metadata,
43
+ annotations: {
44
+ ...metadata.annotations,
45
+ "pulumi.com/waitFor":
46
+ metadata.annotations?.["pulumi.com/waitFor"] ?? certManagerCertificateWaitFor,
47
+ },
48
+ }))
49
+ }
50
+
34
51
  /**
35
52
  * Represents a cert-manager Certificate resource with metadata and secret.
36
53
  */
@@ -65,8 +82,17 @@ export abstract class Certificate extends NamespacedResource {
65
82
  /**
66
83
  * The Highstate certificate entity.
67
84
  */
68
- get entity(): Output<k8s.NamespacedResource> {
69
- return output(this.entityBase)
85
+ get entity(): Output<k8s.Certificate> {
86
+ return makeEntityOutput({
87
+ entity: k8s.certificateEntity,
88
+ identity: this.metadata.uid,
89
+ meta: {
90
+ title: this.metadata.name,
91
+ },
92
+ value: {
93
+ ...this.entityBase,
94
+ },
95
+ })
70
96
  }
71
97
 
72
98
  /**
@@ -228,7 +254,7 @@ class CreatedCertificate extends Certificate {
228
254
  return new cert_manager.v1.Certificate(
229
255
  name,
230
256
  {
231
- metadata: mapMetadata(args, name),
257
+ metadata: mapCertificateMetadata(args, name),
232
258
  spec: omit(args, commonExtraArgs),
233
259
  },
234
260
  { ...opts, parent: this, provider: getProvider(cluster) },
@@ -255,7 +281,7 @@ class CertificatePatch extends Certificate {
255
281
  return new cert_manager.v1.CertificatePatch(
256
282
  name,
257
283
  {
258
- metadata: mapMetadata(args, name),
284
+ metadata: mapCertificateMetadata(args, name),
259
285
  spec: omit(args, commonExtraArgs),
260
286
  },
261
287
  { ...opts, parent: this, provider: getProvider(cluster) },
@@ -5,18 +5,18 @@ import { forUnit, toPromise } from "@highstate/pulumi"
5
5
  const { args, inputs, outputs } = forUnit(k8s.clusterPatch)
6
6
 
7
7
  const cluster = await toPromise(inputs.k8sCluster)
8
- const endpoints = await parseEndpoints(args.endpoints, inputs.endpoints, 3)
9
- const apiEndpoints = await parseEndpoints(args.apiEndpoints, inputs.apiEndpoints, 4)
8
+ const endpoints = parseEndpoints([...args.endpoints, ...inputs.endpoints], 3)
9
+ const apiEndpoints = parseEndpoints([...args.apiEndpoints, ...inputs.apiEndpoints], 4)
10
10
 
11
11
  const newEndpoints = endpoints.length > 0 ? endpoints : cluster.endpoints
12
12
  const newApiEndpoints = apiEndpoints.length > 0 ? apiEndpoints : cluster.apiEndpoints
13
13
 
14
14
  export default outputs({
15
- k8sCluster: inputs.k8sCluster.apply(k8sCluster => ({
16
- ...k8sCluster,
15
+ k8sCluster: {
16
+ ...inputs.k8sCluster,
17
17
  endpoints: newEndpoints,
18
18
  apiEndpoints: newApiEndpoints,
19
- })),
19
+ },
20
20
 
21
21
  $statusFields: {
22
22
  endpoints: endpoints.map(l3EndpointToString),