@highstate/k8s 0.7.1 → 0.7.3

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 (44) hide show
  1. package/dist/{helm-wPTgVV1N.js → chunk-K4WKJ4L5.js} +89 -47
  2. package/dist/chunk-K4WKJ4L5.js.map +1 -0
  3. package/dist/{shared-Clzbl5K-.js → chunk-T5Z2M4JE.js} +21 -7
  4. package/dist/chunk-T5Z2M4JE.js.map +1 -0
  5. package/dist/highstate.manifest.json +9 -0
  6. package/dist/index.js +304 -154
  7. package/dist/index.js.map +1 -0
  8. package/dist/units/access-point/index.js +9 -7
  9. package/dist/units/access-point/index.js.map +1 -0
  10. package/dist/units/cert-manager/index.js +29 -29
  11. package/dist/units/cert-manager/index.js.map +1 -0
  12. package/dist/units/dns01-issuer/index.js +22 -14
  13. package/dist/units/dns01-issuer/index.js.map +1 -0
  14. package/dist/units/existing-cluster/index.js +49 -21
  15. package/dist/units/existing-cluster/index.js.map +1 -0
  16. package/package.json +15 -16
  17. package/src/access-point.ts +185 -0
  18. package/src/container.ts +271 -0
  19. package/src/cron-job.ts +77 -0
  20. package/src/deployment.ts +210 -0
  21. package/src/gateway/backend.ts +61 -0
  22. package/src/gateway/http-route.ts +139 -0
  23. package/src/gateway/index.ts +2 -0
  24. package/src/helm.ts +298 -0
  25. package/src/index.ts +61 -0
  26. package/src/job.ts +66 -0
  27. package/src/network-policy.ts +732 -0
  28. package/src/pod.ts +5 -0
  29. package/src/pvc.ts +178 -0
  30. package/src/scripting/bundle.ts +244 -0
  31. package/src/scripting/container.ts +44 -0
  32. package/src/scripting/environment.ts +79 -0
  33. package/src/scripting/index.ts +3 -0
  34. package/src/service.ts +279 -0
  35. package/src/shared.ts +150 -0
  36. package/src/stateful-set.ts +159 -0
  37. package/src/units/access-point/index.ts +12 -0
  38. package/src/units/cert-manager/index.ts +37 -0
  39. package/src/units/dns01-issuer/index.ts +41 -0
  40. package/src/units/dns01-issuer/solver.ts +23 -0
  41. package/src/units/existing-cluster/index.ts +107 -0
  42. package/src/workload.ts +150 -0
  43. package/assets/charts.json +0 -8
  44. package/dist/index.d.ts +0 -1036
package/src/service.ts ADDED
@@ -0,0 +1,279 @@
1
+ import type { k8s } from "@highstate/library"
2
+ import { core, types } from "@pulumi/kubernetes"
3
+ import {
4
+ ComponentResource,
5
+ interpolate,
6
+ normalize,
7
+ output,
8
+ Output,
9
+ Resource,
10
+ type ComponentResourceOptions,
11
+ type Input,
12
+ type InputArray,
13
+ type Inputs,
14
+ } from "@highstate/pulumi"
15
+ import { omit } from "remeda"
16
+ import { deepmerge } from "deepmerge-ts"
17
+ import {
18
+ commonExtraArgs,
19
+ mapMetadata,
20
+ resourceIdToString,
21
+ verifyProvider,
22
+ type CommonArgs,
23
+ type ResourceId,
24
+ } from "./shared"
25
+
26
+ export type ServiceArgs = CommonArgs & {
27
+ port?: Input<types.input.core.v1.ServicePort>
28
+
29
+ /**
30
+ * The service to patch instead of creating a new one.
31
+ */
32
+ patch?: Input<k8s.Service>
33
+
34
+ /**
35
+ * The cluster to create the resource in.
36
+ */
37
+ cluster: Input<k8s.Cluster>
38
+ } & types.input.core.v1.ServiceSpec
39
+
40
+ const serviceExtraArgs = [...commonExtraArgs, "port", "ports", "patch"] as const
41
+
42
+ export abstract class Service extends ComponentResource {
43
+ protected constructor(
44
+ type: string,
45
+ name: string,
46
+ args: Inputs,
47
+ opts: ComponentResourceOptions,
48
+
49
+ /**
50
+ * The cluster info associated with the service.
51
+ */
52
+ readonly clusterInfo: Output<k8s.ClusterInfo>,
53
+
54
+ /**
55
+ * The metadata of the underlying Kubernetes service.
56
+ */
57
+ readonly metadata: Output<types.output.meta.v1.ObjectMeta>,
58
+
59
+ /**
60
+ * The spec of the underlying Kubernetes service.
61
+ */
62
+ readonly spec: Output<types.output.core.v1.ServiceSpec>,
63
+
64
+ /**
65
+ * The status of the underlying Kubernetes service.
66
+ */
67
+ readonly status: Output<types.output.core.v1.ServiceStatus>,
68
+
69
+ /**
70
+ * The resources associated with the service.
71
+ */
72
+ readonly resources: InputArray<Resource>,
73
+ ) {
74
+ super(type, name, args, opts)
75
+ }
76
+
77
+ /**
78
+ * The Highstate service entity.
79
+ */
80
+ get entity(): Output<k8s.Service> {
81
+ return output({
82
+ type: "k8s.service",
83
+ clusterInfo: this.clusterInfo,
84
+ metadata: this.metadata,
85
+ spec: this.spec,
86
+ })
87
+ }
88
+
89
+ static create(name: string, args: ServiceArgs, opts: ComponentResourceOptions): Service {
90
+ return new CreatedService(name, args, opts)
91
+ }
92
+
93
+ static wrap(
94
+ name: string,
95
+ service: Input<core.v1.Service>,
96
+ clusterInfo: Input<k8s.ClusterInfo>,
97
+ opts: ComponentResourceOptions,
98
+ ): Service {
99
+ return new WrappedService(name, service, clusterInfo, opts)
100
+ }
101
+
102
+ static external(
103
+ name: string,
104
+ id: ResourceId,
105
+ clusterInfo: Input<k8s.ClusterInfo>,
106
+ opts: ComponentResourceOptions,
107
+ ): Service {
108
+ return new ExternalService(name, id, clusterInfo, opts)
109
+ }
110
+
111
+ static of(name: string, entity: Input<k8s.Service>, opts: ComponentResourceOptions): Service {
112
+ return new ExternalService(name, output(entity).metadata, output(entity).clusterInfo, opts)
113
+ }
114
+
115
+ /**
116
+ * Returns the host of the service accessible from the cluster.
117
+ *
118
+ * The format is `name.namespace.svc`.
119
+ */
120
+ get clusterHost(): Output<string> {
121
+ return interpolate`${this.metadata.name}.${this.metadata.namespace}.svc`
122
+ }
123
+
124
+ /**
125
+ * Returns the external IP of the service.
126
+ *
127
+ * If the load balancer is available, the external IP is returned, otherwise the IP of the node.
128
+ *
129
+ * If the service has no external IP, `undefined` is returned.
130
+ */
131
+ get externalIp(): Output<string | undefined> {
132
+ return output({ spec: this.spec, status: this.status }).apply(({ spec, status }) => {
133
+ const loadBalancerIp = status.loadBalancer?.ingress?.[0]?.ip
134
+ if (loadBalancerIp) {
135
+ return loadBalancerIp
136
+ }
137
+
138
+ const nodeIp = spec.externalIPs?.[0]
139
+ if (nodeIp) {
140
+ return nodeIp
141
+ }
142
+
143
+ return undefined
144
+ })
145
+ }
146
+
147
+ /**
148
+ * The "most public" IP of the service.
149
+ *
150
+ * Resolves to the external IP if available, otherwise the cluster IP.
151
+ *
152
+ * If the service is headless, an error is thrown.
153
+ */
154
+ get ip(): Output<string> {
155
+ return output({ spec: this.spec, externalIp: this.externalIp }).apply(
156
+ ({ spec, externalIp }) => {
157
+ if (externalIp) {
158
+ return externalIp
159
+ }
160
+
161
+ const clusterIp = spec.clusterIP
162
+ if (clusterIp && clusterIp !== "None") {
163
+ return clusterIp
164
+ }
165
+
166
+ throw new Error("The service does not have neither an external IP nor a cluster IP.")
167
+ },
168
+ )
169
+ }
170
+ }
171
+
172
+ class CreatedService extends Service {
173
+ constructor(name: string, args: ServiceArgs, opts: ComponentResourceOptions) {
174
+ const service = output(args).apply(async args => {
175
+ await verifyProvider(opts.provider, args.cluster.info)
176
+
177
+ return new (args.patch ? core.v1.ServicePatch : core.v1.Service)(
178
+ name,
179
+ {
180
+ metadata: mapMetadata(args.patch?.metadata ?? args, name),
181
+ spec: deepmerge(
182
+ {
183
+ ports: normalize(args.port, args.ports),
184
+ },
185
+ omit(args, serviceExtraArgs),
186
+ ),
187
+ },
188
+ { parent: this, ...opts },
189
+ )
190
+ })
191
+
192
+ super(
193
+ "highstate:k8s:Service",
194
+ name,
195
+ args,
196
+ opts,
197
+
198
+ output(args.cluster).info,
199
+ service.metadata,
200
+ service.spec,
201
+ service.status,
202
+ [service],
203
+ )
204
+ }
205
+ }
206
+
207
+ class WrappedService extends Service {
208
+ constructor(
209
+ name: string,
210
+ service: Input<core.v1.Service>,
211
+ clusterInfo: Input<k8s.ClusterInfo>,
212
+ opts: ComponentResourceOptions,
213
+ ) {
214
+ super(
215
+ "highstate:k8s:WrappedService",
216
+ name,
217
+ { service, clusterInfo },
218
+ opts,
219
+
220
+ output(clusterInfo),
221
+ output(service).metadata,
222
+ output(service).spec,
223
+ output(service).status,
224
+ [service],
225
+ )
226
+ }
227
+ }
228
+
229
+ class ExternalService extends Service {
230
+ constructor(
231
+ name: string,
232
+ id: ResourceId,
233
+ clusterInfo: Input<k8s.ClusterInfo>,
234
+ opts: ComponentResourceOptions,
235
+ ) {
236
+ const service = output(id).apply(async id => {
237
+ await verifyProvider(opts.provider, this.clusterInfo)
238
+
239
+ return core.v1.Service.get(
240
+ //
241
+ name,
242
+ resourceIdToString(id),
243
+ { parent: this, provider: opts.provider },
244
+ )
245
+ })
246
+
247
+ super(
248
+ "highstate:k8s:ExternalService",
249
+ name,
250
+ { id, clusterInfo },
251
+ opts,
252
+
253
+ output(clusterInfo),
254
+ service.metadata,
255
+ service.spec,
256
+ service.status,
257
+ [service],
258
+ )
259
+ }
260
+ }
261
+
262
+ export function mapContainerPortToServicePort(
263
+ port: types.input.core.v1.ContainerPort,
264
+ ): types.input.core.v1.ServicePort {
265
+ return {
266
+ name: port.name,
267
+ port: port.containerPort,
268
+ targetPort: port.containerPort,
269
+ protocol: port.protocol,
270
+ }
271
+ }
272
+
273
+ export function mapServiceToLabelSelector(
274
+ service: core.v1.Service,
275
+ ): types.input.meta.v1.LabelSelector {
276
+ return {
277
+ matchLabels: service.spec.selector,
278
+ }
279
+ }
package/src/shared.ts ADDED
@@ -0,0 +1,150 @@
1
+ import type { PartialKeys } from "@highstate/contract"
2
+ import type { k8s } from "@highstate/library"
3
+ import { Output, output, toPromise, type Input, type Unwrap } from "@highstate/pulumi"
4
+ import { core, Provider, types } from "@pulumi/kubernetes"
5
+ import { mergeDeep } from "remeda"
6
+
7
+ const providers = new Map<string, Provider>()
8
+
9
+ export function getProvider(cluster: Input<k8s.Cluster>): Promise<Provider> {
10
+ const provider = output(cluster).apply(cluster => {
11
+ const existingProvider = providers.get(cluster.info.id)
12
+ if (existingProvider) {
13
+ return existingProvider
14
+ }
15
+
16
+ const provider = new Provider(cluster.info.id, { kubeconfig: cluster.kubeconfig })
17
+ providers.set(cluster.info.id, provider)
18
+
19
+ return provider
20
+ })
21
+
22
+ return toPromise(provider)
23
+ }
24
+
25
+ export async function verifyProvider(
26
+ provider: Provider | undefined,
27
+ clusterInfo: Input<k8s.ClusterInfo>,
28
+ ): Promise<void> {
29
+ if (!provider) {
30
+ throw new Error("The provider must be passed to the resource.")
31
+ }
32
+
33
+ const urn = await toPromise(provider.urn)
34
+ const [, , , resouceName] = urn.split("::")
35
+
36
+ const expectedId = await toPromise(output(clusterInfo).id)
37
+
38
+ if (resouceName !== expectedId) {
39
+ throw new Error(
40
+ "The Kubernetes cluster of the provider is different from the one where the resource is deployed.",
41
+ )
42
+ }
43
+ }
44
+
45
+ export function createNamespace(
46
+ name: string,
47
+ provider: Provider,
48
+ args: core.v1.NamespaceArgs = {},
49
+ ): core.v1.Namespace {
50
+ return new core.v1.Namespace(
51
+ name,
52
+ mergeDeep(args, {
53
+ metadata: {
54
+ name,
55
+ },
56
+ }),
57
+ { provider },
58
+ )
59
+ }
60
+
61
+ export function getNamespace(name = "default", provider: Provider): core.v1.Namespace {
62
+ return core.v1.Namespace.get(name, name, { provider })
63
+ }
64
+
65
+ export type NamespaceLike = core.v1.Namespace | string
66
+
67
+ export type CommonArgs = {
68
+ /**
69
+ * The name of the resource.
70
+ */
71
+ name?: string
72
+
73
+ /**
74
+ * The namespace to create the resource in.
75
+ */
76
+ namespace: Input<NamespaceLike | undefined>
77
+
78
+ /**
79
+ * The metadata to apply to the resource.
80
+ */
81
+ metadata?: Input<types.input.meta.v1.ObjectMeta>
82
+ }
83
+
84
+ export const commonExtraArgs = ["name", "namespace", "metadata"] as const
85
+
86
+ export function mapMetadata(
87
+ args: PartialKeys<Unwrap<CommonArgs>, "namespace">,
88
+ fallbackName?: string,
89
+ ): types.input.meta.v1.ObjectMeta {
90
+ return {
91
+ ...args.metadata,
92
+ name: args.name ?? args.metadata?.name ?? fallbackName,
93
+ namespace: args.namespace ? mapNamespaceLikeToNamespaceName(args.namespace) : undefined,
94
+ }
95
+ }
96
+
97
+ export type SelectorLike = types.input.meta.v1.LabelSelector | Record<string, Input<string>>
98
+
99
+ export function mapSelectorLikeToSelector(
100
+ selector: SelectorLike,
101
+ ): types.input.meta.v1.LabelSelector {
102
+ if ("matchLabels" in selector || "matchExpressions" in selector) {
103
+ return selector
104
+ }
105
+
106
+ return {
107
+ matchLabels: selector as Record<string, Input<string>>,
108
+ }
109
+ }
110
+
111
+ export function mapNamespaceLikeToNamespaceName(namespace: NamespaceLike): Output<string> {
112
+ return core.v1.Namespace.isInstance(namespace) ? namespace.metadata.name : output(namespace)
113
+ }
114
+
115
+ export function mapNamespaceNameToSelector(
116
+ namespace: Input<string>,
117
+ ): types.input.meta.v1.LabelSelector {
118
+ return {
119
+ matchLabels: {
120
+ "kubernetes.io/metadata.name": namespace,
121
+ },
122
+ }
123
+ }
124
+
125
+ export type ResourceId = {
126
+ name: Input<string>
127
+ namespace?: Input<string | undefined>
128
+ }
129
+
130
+ export function resourceIdToString(metadata: Input<ResourceId>): Output<string> {
131
+ return output(metadata).apply(metadata => {
132
+ return metadata.namespace ? `${metadata.namespace}/${metadata.name}` : metadata.name
133
+ })
134
+ }
135
+
136
+ export function getAppName(resourceId: Unwrap<ResourceId>): string {
137
+ if (resourceId.namespace !== resourceId.name) {
138
+ return `${resourceId.namespace ?? "default"}-${resourceId.name}`
139
+ }
140
+
141
+ return resourceId.name
142
+ }
143
+
144
+ export function getAppDisplayName(resourceId: Unwrap<ResourceId>): string {
145
+ if (resourceId.namespace !== resourceId.name) {
146
+ return `${resourceId.namespace ?? "default"}/${resourceId.name}`
147
+ }
148
+
149
+ return resourceId.name
150
+ }
@@ -0,0 +1,159 @@
1
+ import type { k8s } from "@highstate/library"
2
+ import type { HttpRoute } from "./gateway"
3
+ import type { Service } from "./service"
4
+ import {
5
+ output,
6
+ type ComponentResourceOptions,
7
+ ComponentResource,
8
+ Output,
9
+ type Inputs,
10
+ type Input,
11
+ } from "@highstate/pulumi"
12
+ import { apps, type types } from "@pulumi/kubernetes"
13
+ import { omit } from "remeda"
14
+ import { deepmerge } from "deepmerge-ts"
15
+ import {
16
+ getPublicWorkloadComponents,
17
+ publicWorkloadExtraArgs,
18
+ type PublicWorkloadArgs,
19
+ } from "./workload"
20
+ import { mapMetadata, verifyProvider } from "./shared"
21
+ import { mapContainerToRaw } from "./container"
22
+
23
+ export type StatefulSetArgs = Omit<PublicWorkloadArgs, "patch"> & {
24
+ patch?: Input<k8s.StatefulSet>
25
+ } & Partial<types.input.apps.v1.StatefulSetSpec>
26
+
27
+ export abstract class StatefulSet extends ComponentResource {
28
+ protected constructor(
29
+ type: string,
30
+ name: string,
31
+ args: Inputs,
32
+ opts: ComponentResourceOptions,
33
+
34
+ /**
35
+ * The cluster info associated with the stateful set.
36
+ */
37
+ readonly clusterInfo: Output<k8s.ClusterInfo>,
38
+
39
+ /**
40
+ * The metadata of the underlying Kubernetes stateful set.
41
+ */
42
+ readonly metadata: Output<types.output.meta.v1.ObjectMeta>,
43
+
44
+ /**
45
+ * The spec of the underlying Kubernetes stateful set.
46
+ */
47
+ readonly spec: Output<types.output.apps.v1.StatefulSetSpec>,
48
+
49
+ /**
50
+ * The status of the underlying Kubernetes stateful set.
51
+ */
52
+ readonly status: Output<types.output.apps.v1.StatefulSetStatus>,
53
+
54
+ private readonly _service: Output<Service | undefined>,
55
+ private readonly _httpRoute: Output<HttpRoute | undefined>,
56
+ ) {
57
+ super(type, name, args, opts)
58
+ }
59
+
60
+ /**
61
+ * The Highstate stateful set entity.
62
+ */
63
+ get entity(): Output<k8s.StatefulSet> {
64
+ return output({
65
+ type: "k8s.stateful-set",
66
+ clusterInfo: this.clusterInfo,
67
+ metadata: this.metadata,
68
+ service: this.service.entity,
69
+ })
70
+ }
71
+
72
+ get optionalService(): Output<Service | undefined> {
73
+ return this._service
74
+ }
75
+
76
+ /**
77
+ * The service associated with the stateful set.
78
+ */
79
+ get service(): Output<Service> {
80
+ return this._service.apply(service => {
81
+ if (!service) {
82
+ throw new Error("The service is not available.")
83
+ }
84
+
85
+ return service
86
+ })
87
+ }
88
+
89
+ /**
90
+ * The HTTP route associated with the stateful set.
91
+ */
92
+ get httpRoute(): Output<HttpRoute> {
93
+ return this._httpRoute.apply(httpRoute => {
94
+ if (!httpRoute) {
95
+ throw new Error("The HTTP route is not available.")
96
+ }
97
+
98
+ return httpRoute
99
+ })
100
+ }
101
+
102
+ static create(name: string, args: StatefulSetArgs, opts: ComponentResourceOptions): StatefulSet {
103
+ return new CreatedStatefulSet(name, args, opts)
104
+ }
105
+ }
106
+
107
+ class CreatedStatefulSet extends StatefulSet {
108
+ constructor(name: string, args: StatefulSetArgs, opts: ComponentResourceOptions) {
109
+ const { containers, volumes, labels, service, httpRoute } = getPublicWorkloadComponents(
110
+ name,
111
+ args,
112
+ () => this,
113
+ opts,
114
+ )
115
+
116
+ const statefulSet = output({ args, containers, volumes, service }).apply(
117
+ async ({ args, containers, volumes, service }) => {
118
+ await verifyProvider(opts.provider, args.cluster?.info)
119
+
120
+ return new (args.patch ? apps.v1.StatefulSetPatch : apps.v1.StatefulSet)(
121
+ name,
122
+ {
123
+ metadata: mapMetadata(args.patch?.metadata ?? args, name),
124
+ spec: deepmerge(
125
+ {
126
+ serviceName: service?.metadata.name || name,
127
+ template: {
128
+ metadata: !args.patch ? { labels } : undefined,
129
+ spec: {
130
+ containers: containers.map(container => mapContainerToRaw(container, name)),
131
+ volumes,
132
+ },
133
+ },
134
+ selector: !args.patch ? { matchLabels: labels } : undefined,
135
+ },
136
+ omit(args, publicWorkloadExtraArgs),
137
+ ),
138
+ },
139
+ { parent: this, ...opts },
140
+ )
141
+ },
142
+ )
143
+
144
+ super(
145
+ "highstate:k8s:StatefulSet",
146
+ name,
147
+ args,
148
+ opts,
149
+
150
+ output(args.cluster).info,
151
+ statefulSet.metadata,
152
+ statefulSet.spec,
153
+ statefulSet.status,
154
+
155
+ service,
156
+ httpRoute,
157
+ )
158
+ }
159
+ }
@@ -0,0 +1,12 @@
1
+ import { k8s } from "@highstate/library"
2
+ import { forUnit } from "@highstate/pulumi"
3
+
4
+ const { inputs, outputs } = forUnit(k8s.accessPoint)
5
+
6
+ export default outputs({
7
+ accessPoint: {
8
+ dnsProvider: inputs.dnsProvider,
9
+ gateway: inputs.gateway,
10
+ tlsIssuer: inputs.tlsIssuer,
11
+ },
12
+ })
@@ -0,0 +1,37 @@
1
+ import { k8s } from "@highstate/library"
2
+ import { forUnit } from "@highstate/pulumi"
3
+ import { createNamespace, getProvider } from "../../shared"
4
+ import { Chart } from "../../helm"
5
+ import charts from "../../../assets/charts.json"
6
+
7
+ const { inputs, outputs } = forUnit(k8s.certManager)
8
+ const provider = await getProvider(inputs.k8sCluster)
9
+
10
+ const namespace = createNamespace("cert-manager", provider)
11
+
12
+ new Chart(
13
+ "cert-manager",
14
+ {
15
+ namespace: namespace.metadata.name,
16
+ cluster: inputs.k8sCluster,
17
+
18
+ chart: charts["cert-manager"],
19
+
20
+ values: {
21
+ crds: {
22
+ enabled: true,
23
+ },
24
+
25
+ config: {
26
+ apiVersion: "controller.config.cert-manager.io/v1alpha1",
27
+ kind: "ControllerConfiguration",
28
+ enableGatewayAPI: true,
29
+ },
30
+ },
31
+ },
32
+ { provider },
33
+ )
34
+
35
+ export default outputs({
36
+ k8sCluster: inputs.k8sCluster,
37
+ })
@@ -0,0 +1,41 @@
1
+ import { k8s } from "@highstate/library"
2
+ import { forUnit, unsecret } from "@highstate/pulumi"
3
+ import { cert_manager } from "@highstate/cert-manager"
4
+ import { getProvider } from "../../shared"
5
+ import { createDns01Solver } from "./solver"
6
+
7
+ const { name, inputs, outputs } = forUnit(k8s.dns01TlsIssuer)
8
+
9
+ const provider = await getProvider(inputs.k8sCluster)
10
+ const dns01Solver = await createDns01Solver(inputs.dnsProvider, provider)
11
+
12
+ new cert_manager.v1.ClusterIssuer(
13
+ name,
14
+ {
15
+ metadata: {
16
+ name,
17
+ },
18
+ spec: {
19
+ acme: {
20
+ server: "https://acme-v02.api.letsencrypt.org/directory",
21
+ solvers: [
22
+ {
23
+ dns01: dns01Solver,
24
+ selector: { dnsZones: [inputs.dnsProvider.domain] },
25
+ },
26
+ ],
27
+ privateKeySecretRef: {
28
+ name,
29
+ },
30
+ },
31
+ },
32
+ },
33
+ { provider },
34
+ )
35
+
36
+ export default outputs({
37
+ tlsIssuer: {
38
+ clusterInfo: unsecret(inputs.k8sCluster.info),
39
+ clusterIssuerName: name,
40
+ },
41
+ })
@@ -0,0 +1,23 @@
1
+ import type { dns } from "@highstate/library"
2
+ import type { types } from "@highstate/cert-manager"
3
+ import type { Input } from "@pulumi/pulumi"
4
+ import type { Provider } from "@pulumi/kubernetes"
5
+ import { capitalize } from "remeda"
6
+ import { toPromise } from "@highstate/pulumi"
7
+
8
+ export async function createDns01Solver(
9
+ dnsProviderInput: Input<dns.Provider>,
10
+ provider: Provider,
11
+ ): Promise<types.input.cert_manager.v1.ClusterIssuerSpecAcmeSolversDns01> {
12
+ const dnsProvider = await toPromise(dnsProviderInput)
13
+
14
+ const implName = `create${capitalize(dnsProvider.type)}Dns01Solver`
15
+ const implModule = (await import(`@highstate/${dnsProvider.type}`)) as Record<string, unknown>
16
+
17
+ const implFunction = implModule[implName] as (
18
+ dnsProvider: dns.Provider,
19
+ provider: Provider,
20
+ ) => types.input.cert_manager.v1.ClusterIssuerSpecAcmeSolversDns01
21
+
22
+ return implFunction(dnsProvider, provider)
23
+ }