@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.
- package/dist/{helm-wPTgVV1N.js → chunk-K4WKJ4L5.js} +89 -47
- package/dist/chunk-K4WKJ4L5.js.map +1 -0
- package/dist/{shared-Clzbl5K-.js → chunk-T5Z2M4JE.js} +21 -7
- package/dist/chunk-T5Z2M4JE.js.map +1 -0
- package/dist/highstate.manifest.json +9 -0
- package/dist/index.js +304 -154
- package/dist/index.js.map +1 -0
- package/dist/units/access-point/index.js +9 -7
- package/dist/units/access-point/index.js.map +1 -0
- package/dist/units/cert-manager/index.js +29 -29
- package/dist/units/cert-manager/index.js.map +1 -0
- package/dist/units/dns01-issuer/index.js +22 -14
- package/dist/units/dns01-issuer/index.js.map +1 -0
- package/dist/units/existing-cluster/index.js +49 -21
- package/dist/units/existing-cluster/index.js.map +1 -0
- package/package.json +15 -16
- package/src/access-point.ts +185 -0
- package/src/container.ts +271 -0
- package/src/cron-job.ts +77 -0
- package/src/deployment.ts +210 -0
- package/src/gateway/backend.ts +61 -0
- package/src/gateway/http-route.ts +139 -0
- package/src/gateway/index.ts +2 -0
- package/src/helm.ts +298 -0
- package/src/index.ts +61 -0
- package/src/job.ts +66 -0
- package/src/network-policy.ts +732 -0
- package/src/pod.ts +5 -0
- package/src/pvc.ts +178 -0
- package/src/scripting/bundle.ts +244 -0
- package/src/scripting/container.ts +44 -0
- package/src/scripting/environment.ts +79 -0
- package/src/scripting/index.ts +3 -0
- package/src/service.ts +279 -0
- package/src/shared.ts +150 -0
- package/src/stateful-set.ts +159 -0
- package/src/units/access-point/index.ts +12 -0
- package/src/units/cert-manager/index.ts +37 -0
- package/src/units/dns01-issuer/index.ts +41 -0
- package/src/units/dns01-issuer/solver.ts +23 -0
- package/src/units/existing-cluster/index.ts +107 -0
- package/src/workload.ts +150 -0
- package/assets/charts.json +0 -8
- 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
|
+
}
|