@highstate/k8s 0.9.4 → 0.9.6

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 (63) hide show
  1. package/dist/chunk-DQSCJM5S.js +183 -0
  2. package/dist/chunk-DQSCJM5S.js.map +1 -0
  3. package/dist/chunk-FKNHHKOL.js +260 -0
  4. package/dist/chunk-FKNHHKOL.js.map +1 -0
  5. package/dist/chunk-HW3NS3MC.js +347 -0
  6. package/dist/chunk-HW3NS3MC.js.map +1 -0
  7. package/dist/chunk-OQ7UXASD.js +193 -0
  8. package/dist/chunk-OQ7UXASD.js.map +1 -0
  9. package/dist/chunk-QGHMLKTW.js +1123 -0
  10. package/dist/chunk-QGHMLKTW.js.map +1 -0
  11. package/dist/chunk-UNVSWG6D.js +214 -0
  12. package/dist/chunk-UNVSWG6D.js.map +1 -0
  13. package/dist/deployment-ZP3ASKPT.js +10 -0
  14. package/dist/deployment-ZP3ASKPT.js.map +1 -0
  15. package/dist/highstate.manifest.json +8 -6
  16. package/dist/index.js +291 -954
  17. package/dist/index.js.map +1 -1
  18. package/dist/stateful-set-2AH7RAF7.js +10 -0
  19. package/dist/stateful-set-2AH7RAF7.js.map +1 -0
  20. package/dist/units/access-point/index.js +6 -1
  21. package/dist/units/access-point/index.js.map +1 -1
  22. package/dist/units/cert-manager/index.js +19 -24
  23. package/dist/units/cert-manager/index.js.map +1 -1
  24. package/dist/units/cluster-dns/index.js +36 -0
  25. package/dist/units/cluster-dns/index.js.map +1 -0
  26. package/dist/units/cluster-patch/index.js +34 -0
  27. package/dist/units/cluster-patch/index.js.map +1 -0
  28. package/dist/units/dns01-issuer/index.js +2 -2
  29. package/dist/units/dns01-issuer/index.js.map +1 -1
  30. package/dist/units/existing-cluster/index.js +22 -14
  31. package/dist/units/existing-cluster/index.js.map +1 -1
  32. package/dist/units/gateway-api/index.js +1 -1
  33. package/package.json +12 -10
  34. package/src/access-point.ts +44 -39
  35. package/src/container.ts +54 -5
  36. package/src/cron-job.ts +14 -30
  37. package/src/deployment.ts +170 -127
  38. package/src/gateway/http-route.ts +7 -5
  39. package/src/helm.ts +57 -8
  40. package/src/index.ts +11 -4
  41. package/src/job.ts +14 -32
  42. package/src/namespace.ts +241 -0
  43. package/src/network-policy.ts +371 -87
  44. package/src/network.ts +41 -0
  45. package/src/pvc.ts +43 -25
  46. package/src/scripting/bundle.ts +125 -22
  47. package/src/scripting/container.ts +16 -11
  48. package/src/scripting/environment.ts +56 -6
  49. package/src/secret.ts +195 -0
  50. package/src/service.ts +209 -89
  51. package/src/shared.ts +42 -51
  52. package/src/stateful-set.ts +193 -88
  53. package/src/units/access-point/index.ts +8 -1
  54. package/src/units/cert-manager/index.ts +15 -20
  55. package/src/units/cluster-dns/index.ts +37 -0
  56. package/src/units/cluster-patch/index.ts +35 -0
  57. package/src/units/dns01-issuer/index.ts +1 -1
  58. package/src/units/existing-cluster/index.ts +24 -14
  59. package/src/workload.ts +342 -44
  60. package/dist/chunk-K4WKJ4L5.js +0 -455
  61. package/dist/chunk-K4WKJ4L5.js.map +0 -1
  62. package/dist/chunk-T5Z2M4JE.js +0 -103
  63. package/dist/chunk-T5Z2M4JE.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/units/existing-cluster/index.ts"],"sourcesContent":["import { k8s } from \"@highstate/library\"\nimport { forUnit, secret, toPromise } from \"@highstate/pulumi\"\nimport { core, Provider } from \"@pulumi/kubernetes\"\nimport { KubeConfig, AppsV1Api } from \"@kubernetes/client-node\"\nimport { createK8sTerminal, detectExternalIps } from \"../../cluster\"\n\nconst { name, args, secrets, outputs } = forUnit(k8s.existingCluster)\n\nconst kubeconfigContent = await toPromise(secrets.kubeconfig.apply(JSON.stringify))\n\nconst provider = new Provider(name, { kubeconfig: kubeconfigContent })\n\nlet cni: string | undefined\n\nconst kubeConfig = new KubeConfig()\nkubeConfig.loadFromString(kubeconfigContent)\n\nconst appsApi = kubeConfig.makeApiClient(AppsV1Api)\n\nconst isCilium = await appsApi\n .readNamespacedDaemonSet({ name: \"cilium\", namespace: \"kube-system\" })\n .then(() => true)\n .catch(() => false)\n\nif (isCilium) {\n cni = \"cilium\"\n}\n\nconst externalIps =\n args.externalIps ?? (await detectExternalIps(kubeConfig, args.internalIpsPolicy))\n\nconst kubeSystem = core.v1.Namespace.get(\"kube-system\", \"kube-system\", { provider })\n\nexport default outputs({\n cluster: {\n info: {\n id: kubeSystem.metadata.uid,\n name,\n cni,\n externalIps,\n tunDevicePolicy: args.tunDevicePolicy,\n },\n kubeconfig: secret(kubeconfigContent),\n },\n\n $terminals: [createK8sTerminal(kubeconfigContent)],\n\n $status: {\n clusterId: kubeSystem.metadata.uid,\n cni: cni ?? \"unknown\",\n externalIps: {\n value: externalIps.join(\", \"),\n complementaryTo: \"externalIps\",\n },\n },\n})\n"],"mappings":";;;;;;AAAA,SAAS,WAAW;AACpB,SAAS,SAAS,QAAQ,iBAAiB;AAC3C,SAAS,MAAM,gBAAgB;AAC/B,SAAS,YAAY,iBAAiB;AAGtC,IAAM,EAAE,MAAM,MAAM,SAAS,QAAQ,IAAI,QAAQ,IAAI,eAAe;AAEpE,IAAM,oBAAoB,MAAM,UAAU,QAAQ,WAAW,MAAM,KAAK,SAAS,CAAC;AAElF,IAAM,WAAW,IAAI,SAAS,MAAM,EAAE,YAAY,kBAAkB,CAAC;AAErE,IAAI;AAEJ,IAAM,aAAa,IAAI,WAAW;AAClC,WAAW,eAAe,iBAAiB;AAE3C,IAAM,UAAU,WAAW,cAAc,SAAS;AAElD,IAAM,WAAW,MAAM,QACpB,wBAAwB,EAAE,MAAM,UAAU,WAAW,cAAc,CAAC,EACpE,KAAK,MAAM,IAAI,EACf,MAAM,MAAM,KAAK;AAEpB,IAAI,UAAU;AACZ,QAAM;AACR;AAEA,IAAM,cACJ,KAAK,eAAgB,MAAM,kBAAkB,YAAY,KAAK,iBAAiB;AAEjF,IAAM,aAAa,KAAK,GAAG,UAAU,IAAI,eAAe,eAAe,EAAE,SAAS,CAAC;AAEnF,IAAO,2BAAQ,QAAQ;AAAA,EACrB,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,IAAI,WAAW,SAAS;AAAA,MACxB;AAAA,MACA;AAAA,MACA;AAAA,MACA,iBAAiB,KAAK;AAAA,IACxB;AAAA,IACA,YAAY,OAAO,iBAAiB;AAAA,EACtC;AAAA,EAEA,YAAY,CAAC,kBAAkB,iBAAiB,CAAC;AAAA,EAEjD,SAAS;AAAA,IACP,WAAW,WAAW,SAAS;AAAA,IAC/B,KAAK,OAAO;AAAA,IACZ,aAAa;AAAA,MACX,OAAO,YAAY,KAAK,IAAI;AAAA,MAC5B,iBAAiB;AAAA,IACnB;AAAA,EACF;AACF,CAAC;","names":[]}
1
+ {"version":3,"sources":["../../../src/units/existing-cluster/index.ts"],"sourcesContent":["import { k8s } from \"@highstate/library\"\nimport { forUnit, secret, toPromise } from \"@highstate/pulumi\"\nimport { core, Provider } from \"@pulumi/kubernetes\"\nimport { KubeConfig, AppsV1Api } from \"@kubernetes/client-node\"\nimport {\n l3EndpointToString,\n l4EndpointToString,\n parseL3Endpoint,\n parseL4Endpoint,\n} from \"@highstate/common\"\nimport { createK8sTerminal, detectExternalIps } from \"../../cluster\"\n\nconst { name, args, secrets, outputs } = forUnit(k8s.existingCluster)\n\nconst kubeconfigContent = await toPromise(secrets.kubeconfig.apply(JSON.stringify))\n\nconst provider = new Provider(name, { kubeconfig: kubeconfigContent })\n\nlet cni: k8s.CNI = \"other\"\n\nconst kubeConfig = new KubeConfig()\nkubeConfig.loadFromString(kubeconfigContent)\n\nconst appsApi = kubeConfig.makeApiClient(AppsV1Api)\n\nconst isCilium = await appsApi\n .readNamespacedDaemonSet({ name: \"cilium\", namespace: \"kube-system\" })\n .then(() => true)\n .catch(() => false)\n\nif (isCilium) {\n cni = \"cilium\"\n}\n\nconst externalIps =\n args.externalIps ?? (await detectExternalIps(kubeConfig, args.internalIpsPolicy))\n\nconst endpoints = externalIps.map(parseL3Endpoint)\nconst apiEndpoints = [parseL4Endpoint(kubeConfig.clusters[0].server.replace(\"https://\", \"\"))]\n\nconst kubeSystem = core.v1.Namespace.get(\"kube-system\", \"kube-system\", { provider })\n\nexport default outputs({\n k8sCluster: {\n id: kubeSystem.metadata.uid,\n name,\n cni,\n externalIps,\n endpoints,\n apiEndpoints,\n quirks: args.quirks,\n kubeconfig: secret(kubeconfigContent),\n },\n\n endpoints,\n apiEndpoints,\n\n $terminals: [createK8sTerminal(kubeconfigContent)],\n\n $status: {\n clusterId: kubeSystem.metadata.uid,\n cni,\n endpoints: endpoints.map(l3EndpointToString),\n apiEndpoints: apiEndpoints.map(l4EndpointToString),\n },\n})\n"],"mappings":";;;;;;AAAA,SAAS,WAAW;AACpB,SAAS,SAAS,QAAQ,iBAAiB;AAC3C,SAAS,MAAM,gBAAgB;AAC/B,SAAS,YAAY,iBAAiB;AACtC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,IAAM,EAAE,MAAM,MAAM,SAAS,QAAQ,IAAI,QAAQ,IAAI,eAAe;AAEpE,IAAM,oBAAoB,MAAM,UAAU,QAAQ,WAAW,MAAM,KAAK,SAAS,CAAC;AAElF,IAAM,WAAW,IAAI,SAAS,MAAM,EAAE,YAAY,kBAAkB,CAAC;AAErE,IAAI,MAAe;AAEnB,IAAM,aAAa,IAAI,WAAW;AAClC,WAAW,eAAe,iBAAiB;AAE3C,IAAM,UAAU,WAAW,cAAc,SAAS;AAElD,IAAM,WAAW,MAAM,QACpB,wBAAwB,EAAE,MAAM,UAAU,WAAW,cAAc,CAAC,EACpE,KAAK,MAAM,IAAI,EACf,MAAM,MAAM,KAAK;AAEpB,IAAI,UAAU;AACZ,QAAM;AACR;AAEA,IAAM,cACJ,KAAK,eAAgB,MAAM,kBAAkB,YAAY,KAAK,iBAAiB;AAEjF,IAAM,YAAY,YAAY,IAAI,eAAe;AACjD,IAAM,eAAe,CAAC,gBAAgB,WAAW,SAAS,CAAC,EAAE,OAAO,QAAQ,YAAY,EAAE,CAAC,CAAC;AAE5F,IAAM,aAAa,KAAK,GAAG,UAAU,IAAI,eAAe,eAAe,EAAE,SAAS,CAAC;AAEnF,IAAO,2BAAQ,QAAQ;AAAA,EACrB,YAAY;AAAA,IACV,IAAI,WAAW,SAAS;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,KAAK;AAAA,IACb,YAAY,OAAO,iBAAiB;AAAA,EACtC;AAAA,EAEA;AAAA,EACA;AAAA,EAEA,YAAY,CAAC,kBAAkB,iBAAiB,CAAC;AAAA,EAEjD,SAAS;AAAA,IACP,WAAW,WAAW,SAAS;AAAA,IAC/B;AAAA,IACA,WAAW,UAAU,IAAI,kBAAkB;AAAA,IAC3C,cAAc,aAAa,IAAI,kBAAkB;AAAA,EACnD;AACF,CAAC;","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getProvider
3
- } from "../../chunk-T5Z2M4JE.js";
3
+ } from "../../chunk-FKNHHKOL.js";
4
4
 
5
5
  // src/units/gateway-api/index.ts
6
6
  import { k8s } from "@highstate/library";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@highstate/k8s",
3
- "version": "0.9.4",
3
+ "version": "0.9.6",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -13,6 +13,8 @@
13
13
  },
14
14
  "./units/access-point": "./dist/units/access-point/index.js",
15
15
  "./units/cert-manager": "./dist/units/cert-manager/index.js",
16
+ "./units/cluster-patch": "./dist/units/cluster-patch/index.js",
17
+ "./units/cluster-dns": "./dist/units/cluster-dns/index.js",
16
18
  "./units/dns01-issuer": "./dist/units/dns01-issuer/index.js",
17
19
  "./units/existing-cluster": "./dist/units/existing-cluster/index.js",
18
20
  "./units/gateway-api": "./dist/units/gateway-api/index.js"
@@ -26,12 +28,12 @@
26
28
  "generate-crds": "./scripts/generate-crds.sh"
27
29
  },
28
30
  "dependencies": {
29
- "@highstate/cert-manager": "^0.9.4",
30
- "@highstate/common": "^0.9.4",
31
- "@highstate/contract": "^0.9.4",
32
- "@highstate/gateway-api": "^0.9.4",
33
- "@highstate/library": "^0.9.4",
34
- "@highstate/pulumi": "^0.9.4",
31
+ "@highstate/cert-manager": "^0.9.6",
32
+ "@highstate/common": "^0.9.6",
33
+ "@highstate/contract": "^0.9.6",
34
+ "@highstate/gateway-api": "^0.9.6",
35
+ "@highstate/library": "^0.9.6",
36
+ "@highstate/pulumi": "^0.9.6",
35
37
  "@kubernetes/client-node": "^1.1.0",
36
38
  "@pulumi/command": "^1.0.2",
37
39
  "@pulumi/kubernetes": "^4.18.0",
@@ -40,11 +42,11 @@
40
42
  "deepmerge-ts": "^7.1.5",
41
43
  "glob": "^11.0.1",
42
44
  "nano-spawn": "^0.2.0",
43
- "parse-domain": "^8.2.2",
45
+ "pkg-types": "^2.1.0",
44
46
  "remeda": "^2.21.0"
45
47
  },
46
48
  "devDependencies": {
47
- "@highstate/cli": "^0.9.4"
49
+ "@highstate/cli": "^0.9.6"
48
50
  },
49
- "gitHead": "021af8cde467823881e2dc82ccb606c9617772ea"
51
+ "gitHead": "3c7e8883ef6cbc3e3f7c2c5529115767aae5c96a"
50
52
  }
@@ -1,6 +1,7 @@
1
1
  import type { k8s } from "@highstate/library"
2
- import type { core, Provider } from "@pulumi/kubernetes"
3
- import { DnsRecord } from "@highstate/common"
2
+ import type { Provider } from "@pulumi/kubernetes"
3
+ import type { Namespace } from "./namespace"
4
+ import { DnsRecordSet, filterEndpoints, l3EndpointToString } from "@highstate/common"
4
5
  import { gateway } from "@highstate/gateway-api"
5
6
  import {
6
7
  normalize,
@@ -11,7 +12,8 @@ import {
11
12
  type InputArray,
12
13
  } from "@highstate/pulumi"
13
14
  import { NetworkPolicy } from "./network-policy"
14
- import { getAppDisplayName, getAppName, mapNamespaceLikeToNamespaceName } from "./shared"
15
+ import { getProvider, mapNamespaceLikeToNamespaceName } from "./shared"
16
+ import { isFromCluster } from "./service"
15
17
 
16
18
  export type UseAccessPointResult = {
17
19
  /**
@@ -20,9 +22,9 @@ export type UseAccessPointResult = {
20
22
  gateway: gateway.v1.Gateway
21
23
 
22
24
  /**
23
- * The DNS record associated created according to the access point and gateway.
25
+ * The DNS record sets associated created according to the access point and gateway.
24
26
  */
25
- dnsRecords: DnsRecord[]
27
+ dnsRecordSets: DnsRecordSet[]
26
28
 
27
29
  /**
28
30
  * The network policies associated with the access point.
@@ -37,6 +39,12 @@ export type UseAccessPointArgs = Omit<CreateGatewayArgs, "gateway"> & {
37
39
  export function useAccessPoint(args: UseAccessPointArgs): Promise<UseAccessPointResult> {
38
40
  const result = output({ args, namespaceName: output(args.namespace).metadata.name }).apply(
39
41
  ({ args, namespaceName }) => {
42
+ if (args.accessPoint.clusterId !== args.cluster.id) {
43
+ throw new Error(
44
+ "The provided Kubernetes cluster is different from the one where the access point is deployed.",
45
+ )
46
+ }
47
+
40
48
  const gateway = createGateway({
41
49
  ...args,
42
50
  annotations: {
@@ -45,43 +53,41 @@ export function useAccessPoint(args: UseAccessPointArgs): Promise<UseAccessPoint
45
53
  gateway: args.accessPoint.gateway,
46
54
  })
47
55
 
48
- const dnsRecords = normalize(args.fqdn, args.fqdns).flatMap(fqdn => {
49
- return DnsRecord.createSet(fqdn, {
56
+ const dnsRecordSets = normalize(args.fqdn, args.fqdns).flatMap(fqdn => {
57
+ return DnsRecordSet.create(fqdn, {
50
58
  providers: args.accessPoint.dnsProviders,
51
- type: "A",
52
- value: args.accessPoint.gateway.ip,
59
+ values: filterEndpoints(
60
+ args.accessPoint.gateway.endpoints.filter(endpoint => endpoint.type !== "hostname"),
61
+ ),
53
62
  })
54
63
  })
55
64
 
56
- const networkPolicies: Output<NetworkPolicy>[] = []
57
-
58
- if (args.accessPoint.gateway.service) {
59
- const displayName = getAppDisplayName(args.accessPoint.gateway.service.metadata)
60
-
61
- networkPolicies.push(
62
- NetworkPolicy.create(
63
- `allow-ingress-from-${getAppName(args.accessPoint.gateway.service.metadata)}`,
64
- {
65
- namespace: args.namespace,
66
- cluster: args.cluster,
65
+ const networkPolicies: Output<NetworkPolicy>[] = [
66
+ NetworkPolicy.create(
67
+ `allow-ingress-from-${l3EndpointToString(args.accessPoint.gateway.endpoints[0])}`,
68
+ {
69
+ namespace: args.namespace,
70
+ cluster: args.cluster,
67
71
 
68
- description: `Allow ingress traffic from the gateway "${displayName}".`,
72
+ description: `Allow ingress traffic from the gateway at "${l3EndpointToString(args.accessPoint.gateway.endpoints[0])}".`,
69
73
 
70
- ingressRule: {
71
- fromNamespace: args.accessPoint.gateway.service.metadata.namespace,
72
- fromSelector: args.accessPoint.gateway.service.spec.selector,
73
- },
74
+ ingressRule: {
75
+ fromEndpoints: args.accessPoint.gateway.endpoints,
74
76
  },
75
- { provider: args.provider },
76
- ),
77
+ },
78
+ { provider: args.provider },
79
+ ),
80
+ ]
77
81
 
82
+ if (isFromCluster(args.accessPoint.gateway.endpoints[0], args.cluster)) {
83
+ networkPolicies.push(
78
84
  NetworkPolicy.create(
79
85
  `allow-egress-to-${namespaceName}`,
80
86
  {
81
- namespace: args.accessPoint.gateway.service.metadata.namespace,
87
+ namespace: args.accessPoint.gateway.endpoints[0].metadata.k8sService.namespace,
82
88
  cluster: args.cluster,
83
89
 
84
- selector: args.accessPoint.gateway.service.spec.selector,
90
+ selector: args.accessPoint.gateway.endpoints[0].metadata.k8sService.selector,
85
91
 
86
92
  description: `Allow egress traffic to the namespace "${namespaceName}".`,
87
93
 
@@ -96,7 +102,7 @@ export function useAccessPoint(args: UseAccessPointArgs): Promise<UseAccessPoint
96
102
 
97
103
  return output({
98
104
  gateway,
99
- dnsRecords: output(dnsRecords).apply(records => records.flat()),
105
+ dnsRecordSets,
100
106
  networkPolicies,
101
107
  })
102
108
  },
@@ -106,6 +112,7 @@ export function useAccessPoint(args: UseAccessPointArgs): Promise<UseAccessPoint
106
112
  }
107
113
 
108
114
  export type StandardAccessPointArgs = {
115
+ appName: string
109
116
  fqdn: string
110
117
  }
111
118
 
@@ -114,28 +121,26 @@ export type StandardAccessPointInputs = {
114
121
  k8sCluster: Output<k8s.Cluster>
115
122
  }
116
123
 
117
- export function useStandardAcessPoint(
118
- appName: string,
119
- namespace: core.v1.Namespace,
124
+ export async function useStandardAcessPoint(
125
+ namespace: Namespace,
120
126
  args: StandardAccessPointArgs,
121
127
  inputs: StandardAccessPointInputs,
122
- provider: Provider,
123
128
  ): Promise<UseAccessPointResult> {
124
- return useAccessPoint({
125
- name: appName,
129
+ return await useAccessPoint({
130
+ name: args.appName,
126
131
  namespace,
127
132
 
128
133
  fqdn: args.fqdn,
129
134
 
130
135
  accessPoint: inputs.accessPoint,
131
136
  cluster: inputs.k8sCluster,
132
- provider,
137
+ provider: await getProvider(inputs.k8sCluster),
133
138
  })
134
139
  }
135
140
 
136
141
  export type CreateGatewayArgs = {
137
142
  name: string
138
- namespace: Input<core.v1.Namespace>
143
+ namespace: Input<Namespace>
139
144
  annotations?: Input<Record<string, string>>
140
145
 
141
146
  fqdn?: Input<string>
@@ -148,7 +153,7 @@ export type CreateGatewayArgs = {
148
153
 
149
154
  export function createGateway(args: CreateGatewayArgs): Output<gateway.v1.Gateway> {
150
155
  return output(args).apply(args => {
151
- if (args.cluster.info.id !== args.gateway.clusterInfo.id) {
156
+ if (args.cluster.id !== args.gateway.clusterId) {
152
157
  throw new Error(
153
158
  "The provided Kubernetes cluster is different from the one where the gateway controller is deployed.",
154
159
  )
package/src/container.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import type { PartialKeys } from "@highstate/contract"
2
+ import type { k8s, network } from "@highstate/library"
2
3
  import { core, type types } from "@pulumi/kubernetes"
3
4
  import { normalize, output, type Input, type InputArray, type Unwrap } from "@highstate/pulumi"
4
5
  import { concat, map, omit } from "remeda"
5
6
  import { PersistentVolumeClaim } from "./pvc"
7
+ import { Secret } from "./secret"
6
8
 
7
9
  export type Container = Omit<PartialKeys<types.input.core.v1.Container, "name">, "volumeMounts"> & {
8
10
  /**
@@ -51,6 +53,20 @@ export type Container = Omit<PartialKeys<types.input.core.v1.Container, "name">,
51
53
  * It is like the `envFrom` property, but more convenient to use.
52
54
  */
53
55
  environmentSources?: InputArray<ContainerEnvironmentSource>
56
+
57
+ /**
58
+ * The list of endpoints that the container is allowed to access.
59
+ *
60
+ * This is used to generate a network policy.
61
+ */
62
+ allowedEndpoints?: InputArray<network.L34Endpoint>
63
+
64
+ /**
65
+ * Enable the TUN device in the container.
66
+ *
67
+ * All necessary security context settings will be applied to the container.
68
+ */
69
+ enableTun?: Input<boolean>
54
70
  }
55
71
 
56
72
  const containerExtraArgs = [
@@ -73,7 +89,7 @@ export type ContainerEnvironmentVariable =
73
89
  /**
74
90
  * The secret to select from.
75
91
  */
76
- secret: Input<core.v1.Secret>
92
+ secret: Input<core.v1.Secret | Secret>
77
93
 
78
94
  /**
79
95
  * The key of the secret to select from.
@@ -110,16 +126,18 @@ export type WorkloadVolume =
110
126
  | types.input.core.v1.Volume
111
127
  | core.v1.PersistentVolumeClaim
112
128
  | PersistentVolumeClaim
129
+ | Secret
113
130
  | core.v1.ConfigMap
114
131
  | core.v1.Secret
115
132
 
116
133
  export function mapContainerToRaw(
117
134
  container: Unwrap<Container>,
135
+ cluster: k8s.Cluster,
118
136
  fallbackName: string,
119
137
  ): types.input.core.v1.Container {
120
138
  const containerName = container.name ?? fallbackName
121
139
 
122
- return {
140
+ const spec = {
123
141
  ...omit(container, containerExtraArgs),
124
142
 
125
143
  name: containerName,
@@ -139,7 +157,29 @@ export function mapContainerToRaw(
139
157
  ),
140
158
  container.envFrom ?? [],
141
159
  ),
160
+ } as Unwrap<types.input.core.v1.Container>
161
+
162
+ if (container.enableTun) {
163
+ spec.securityContext ??= {}
164
+ spec.securityContext.capabilities ??= {}
165
+ spec.securityContext.capabilities.add = ["NET_ADMIN"]
166
+
167
+ if (cluster.quirks?.tunDevicePolicy?.type === "plugin") {
168
+ spec.resources ??= {}
169
+ spec.resources.limits ??= {}
170
+ spec.resources.limits[cluster.quirks.tunDevicePolicy.resourceName] =
171
+ cluster.quirks.tunDevicePolicy.resourceValue
172
+ } else {
173
+ spec.volumeMounts ??= []
174
+ spec.volumeMounts.push({
175
+ name: "tun-device",
176
+ mountPath: "/dev/net/tun",
177
+ readOnly: false,
178
+ })
179
+ }
142
180
  }
181
+
182
+ return spec
143
183
  }
144
184
 
145
185
  export function mapContainerEnvironment(
@@ -240,7 +280,16 @@ export function mapWorkloadVolume(volume: WorkloadVolume) {
240
280
  }
241
281
  }
242
282
 
243
- if (volume instanceof core.v1.PersistentVolumeClaim) {
283
+ if (volume instanceof Secret) {
284
+ return {
285
+ name: volume.metadata.name,
286
+ secret: {
287
+ secretName: volume.metadata.name,
288
+ },
289
+ }
290
+ }
291
+
292
+ if (core.v1.PersistentVolumeClaim.isInstance(volume)) {
244
293
  return {
245
294
  name: volume.metadata.name,
246
295
  persistentVolumeClaim: {
@@ -249,7 +298,7 @@ export function mapWorkloadVolume(volume: WorkloadVolume) {
249
298
  }
250
299
  }
251
300
 
252
- if (volume instanceof core.v1.ConfigMap) {
301
+ if (core.v1.ConfigMap.isInstance(volume)) {
253
302
  return {
254
303
  name: volume.metadata.name,
255
304
  configMap: {
@@ -258,7 +307,7 @@ export function mapWorkloadVolume(volume: WorkloadVolume) {
258
307
  }
259
308
  }
260
309
 
261
- if (volume instanceof core.v1.Secret) {
310
+ if (core.v1.Secret.isInstance(volume)) {
262
311
  return {
263
312
  name: volume.metadata.name,
264
313
  secret: {
package/src/cron-job.ts CHANGED
@@ -1,22 +1,12 @@
1
1
  import type { RequiredKeys } from "@highstate/contract"
2
2
  import { batch, type types } from "@pulumi/kubernetes"
3
- import {
4
- ComponentResource,
5
- normalize,
6
- Output,
7
- output,
8
- type ComponentResourceOptions,
9
- type Input,
10
- type InputArray,
11
- } from "@highstate/pulumi"
3
+ import { ComponentResource, Output, output, type ComponentResourceOptions } from "@highstate/pulumi"
12
4
  import { mergeDeep, omit } from "remeda"
13
- import { mapContainerToRaw, mapWorkloadVolume, type Container } from "./container"
14
- import { commonExtraArgs, mapMetadata, type CommonArgs } from "./shared"
5
+ import { commonExtraArgs, getProvider, mapMetadata } from "./shared"
6
+ import { getWorkloadComponents, type WorkloadArgs } from "./workload"
15
7
 
16
- export type CronJobArgs = CommonArgs & {
17
- container?: Input<Container>
18
- containers?: InputArray<Container>
19
- } & Omit<RequiredKeys<Partial<types.input.batch.v1.CronJobSpec>, "schedule">, "jobTemplate"> & {
8
+ export type CronJobArgs = WorkloadArgs &
9
+ Omit<RequiredKeys<Partial<types.input.batch.v1.CronJobSpec>, "schedule">, "jobTemplate"> & {
20
10
  jobTemplate?: {
21
11
  metadata?: types.input.meta.v1.ObjectMeta
22
12
  spec?: Omit<types.input.batch.v1.JobSpec, "template"> & {
@@ -36,12 +26,12 @@ export class CronJob extends ComponentResource {
36
26
  */
37
27
  public readonly cronJob: Output<batch.v1.CronJob>
38
28
 
39
- constructor(name: string, args: CronJobArgs, opts?: ComponentResourceOptions) {
29
+ constructor(name: string, args: CronJobArgs, opts: ComponentResourceOptions) {
40
30
  super("highstate:k8s:CronJob", name, args, opts)
41
31
 
42
- this.cronJob = output(args).apply(args => {
43
- const containers = normalize(args.container, args.containers)
32
+ const { podTemplate } = getWorkloadComponents(name, args, () => this, opts)
44
33
 
34
+ this.cronJob = output({ args, podTemplate }).apply(async ({ args, podTemplate }) => {
45
35
  return new batch.v1.CronJob(
46
36
  name,
47
37
  {
@@ -51,15 +41,7 @@ export class CronJob extends ComponentResource {
51
41
  {
52
42
  jobTemplate: {
53
43
  spec: {
54
- template: {
55
- spec: {
56
- containers: containers.map(container => mapContainerToRaw(container, name)),
57
-
58
- volumes: containers
59
- .flatMap(container => normalize(container.volume, container.volumes))
60
- .map(mapWorkloadVolume),
61
- },
62
- },
44
+ template: podTemplate,
63
45
  },
64
46
  },
65
47
 
@@ -68,10 +50,12 @@ export class CronJob extends ComponentResource {
68
50
  omit(args, cronJobExtraArgs) as types.input.batch.v1.CronJobSpec,
69
51
  ),
70
52
  },
71
- { parent: this, ...opts },
53
+ {
54
+ ...opts,
55
+ parent: this,
56
+ provider: await getProvider(args.cluster),
57
+ },
72
58
  )
73
59
  })
74
-
75
- this.registerOutputs({ cronJob: this.cronJob })
76
60
  }
77
61
  }