@highstate/k8s 0.19.1 → 0.21.1
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/chunk-23vn2rdc.js +11 -0
- package/dist/chunk-2pfx13ay.js +11 -0
- package/dist/chunk-46ntav0c.js +299 -0
- package/dist/chunk-556pc9e6.js +155 -0
- package/dist/chunk-7kgjgcft.js +170 -0
- package/dist/{chunk-LGHFSXNT.js → chunk-9hs97f1q.js} +23 -17
- package/dist/chunk-aame3x1b.js +11 -0
- package/dist/chunk-b05q6fm2.js +37 -0
- package/dist/chunk-bmvc9d2d.js +11 -0
- package/dist/chunk-de82bbp2.js +7 -0
- package/dist/chunk-facs31cb.js +624 -0
- package/dist/chunk-h1b79v66.js +1425 -0
- package/dist/chunk-k4w9zpn5.js +215 -0
- package/dist/chunk-pqc6w52f.js +352 -0
- package/dist/chunk-qyshvz32.js +176 -0
- package/dist/chunk-tpfyj6fe.js +199 -0
- package/dist/chunk-z6bmpnm7.js +180 -0
- package/dist/highstate.manifest.json +3 -2
- package/dist/impl/dynamic-endpoint-resolver.js +91 -0
- package/dist/impl/gateway-route.js +226 -166
- package/dist/impl/tls-certificate.js +31 -31
- package/dist/index.js +293 -166
- package/dist/units/cert-manager/index.js +19 -14
- package/dist/units/cluster-patch/index.js +14 -13
- package/dist/units/dns01-issuer/index.js +82 -42
- package/dist/units/existing-cluster/index.js +59 -26
- package/dist/units/gateway-api/index.js +15 -16
- package/dist/units/reduced-access-cluster/index.js +32 -36
- package/package.json +23 -21
- package/src/cluster.ts +12 -8
- package/src/config-map.ts +15 -5
- package/src/container.ts +4 -2
- package/src/cron-job.ts +51 -5
- package/src/deployment.ts +49 -18
- package/src/gateway/backend.ts +3 -3
- package/src/gateway/gateway.ts +12 -56
- package/src/helm.ts +354 -22
- package/src/impl/dynamic-endpoint-resolver.ts +109 -0
- package/src/impl/gateway-route.ts +231 -57
- package/src/impl/tls-certificate.ts +8 -3
- package/src/index.ts +1 -0
- package/src/job.ts +38 -6
- package/src/kubectl.ts +166 -0
- package/src/namespace.ts +47 -3
- package/src/network-policy.ts +1 -1
- package/src/pvc.ts +12 -2
- package/src/rbac.ts +28 -5
- package/src/scripting/bundle.ts +21 -98
- package/src/scripting/environment.ts +4 -10
- package/src/secret.ts +15 -5
- package/src/service.ts +28 -6
- package/src/shared.ts +31 -3
- package/src/stateful-set.ts +49 -18
- package/src/tls.ts +31 -5
- package/src/units/cluster-patch/index.ts +5 -5
- package/src/units/dns01-issuer/index.ts +56 -12
- package/src/units/existing-cluster/index.ts +36 -15
- package/src/units/reduced-access-cluster/index.ts +6 -3
- package/src/worker.ts +4 -2
- package/src/workload.ts +474 -217
- package/LICENSE +0 -21
- package/dist/chunk-4G6LLC2X.js +0 -240
- package/dist/chunk-4G6LLC2X.js.map +0 -1
- package/dist/chunk-BR2CLUUD.js +0 -230
- package/dist/chunk-BR2CLUUD.js.map +0 -1
- package/dist/chunk-DCUMJSO6.js +0 -427
- package/dist/chunk-DCUMJSO6.js.map +0 -1
- package/dist/chunk-FE4SHRAJ.js +0 -286
- package/dist/chunk-FE4SHRAJ.js.map +0 -1
- package/dist/chunk-HH2JJELM.js +0 -13
- package/dist/chunk-HH2JJELM.js.map +0 -1
- package/dist/chunk-KMLRI5UZ.js +0 -155
- package/dist/chunk-KMLRI5UZ.js.map +0 -1
- package/dist/chunk-LGHFSXNT.js.map +0 -1
- package/dist/chunk-MIC2BHGS.js +0 -301
- package/dist/chunk-MIC2BHGS.js.map +0 -1
- package/dist/chunk-OBDQONMV.js +0 -401
- package/dist/chunk-OBDQONMV.js.map +0 -1
- package/dist/chunk-P2VOUU7E.js +0 -1626
- package/dist/chunk-P2VOUU7E.js.map +0 -1
- package/dist/chunk-PZ5AY32C.js +0 -9
- package/dist/chunk-PZ5AY32C.js.map +0 -1
- package/dist/chunk-RVB4WWZZ.js +0 -267
- package/dist/chunk-RVB4WWZZ.js.map +0 -1
- package/dist/chunk-TWBMG6TD.js +0 -315
- package/dist/chunk-TWBMG6TD.js.map +0 -1
- package/dist/chunk-VCXWCZ43.js +0 -279
- package/dist/chunk-VCXWCZ43.js.map +0 -1
- package/dist/chunk-YIJUVPU2.js +0 -297
- package/dist/chunk-YIJUVPU2.js.map +0 -1
- package/dist/cron-job-NX4HD4FI.js +0 -8
- package/dist/cron-job-NX4HD4FI.js.map +0 -1
- package/dist/deployment-O2LJ5WR5.js +0 -8
- package/dist/deployment-O2LJ5WR5.js.map +0 -1
- package/dist/impl/gateway-route.js.map +0 -1
- package/dist/impl/tls-certificate.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/job-SYME6Y43.js +0 -8
- package/dist/job-SYME6Y43.js.map +0 -1
- package/dist/stateful-set-VJYKTQ72.js +0 -8
- package/dist/stateful-set-VJYKTQ72.js.map +0 -1
- package/dist/units/cert-manager/index.js.map +0 -1
- package/dist/units/cluster-patch/index.js.map +0 -1
- package/dist/units/dns01-issuer/index.js.map +0 -1
- package/dist/units/existing-cluster/index.js.map +0 -1
- package/dist/units/gateway-api/index.js.map +0 -1
- package/dist/units/reduced-access-cluster/index.js.map +0 -1
package/src/workload.ts
CHANGED
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
import type { k8s, network } from "@highstate/library"
|
|
2
2
|
import type { types } from "@pulumi/kubernetes"
|
|
3
|
-
import type { Except } from "type-fest"
|
|
3
|
+
import type { DistributedOmit, Except } from "type-fest"
|
|
4
4
|
import type { DeploymentArgs } from "./deployment"
|
|
5
5
|
import type { JobArgs } from "./job"
|
|
6
6
|
import type { StatefulSetArgs } from "./stateful-set"
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
AccessPointRoute,
|
|
9
|
+
type AccessPointRouteArgs,
|
|
10
|
+
type GatewayHttpRuleArgs,
|
|
11
|
+
type GatewayRuleArgs,
|
|
12
|
+
mergeEndpoints,
|
|
13
|
+
} from "@highstate/common"
|
|
8
14
|
import { type TerminalSpec, trimIndentation, type UnitTerminal } from "@highstate/contract"
|
|
9
15
|
import {
|
|
10
16
|
type ComponentResourceOptions,
|
|
11
17
|
type DeepInput,
|
|
12
|
-
fileFromString,
|
|
13
18
|
type InputArray,
|
|
19
|
+
type InputRecord,
|
|
20
|
+
makeFileOutput,
|
|
14
21
|
normalize,
|
|
15
22
|
normalizeInputs,
|
|
23
|
+
toPromise,
|
|
16
24
|
} from "@highstate/pulumi"
|
|
17
25
|
import {
|
|
18
26
|
type ComponentResource,
|
|
@@ -26,7 +34,7 @@ import {
|
|
|
26
34
|
} from "@pulumi/pulumi"
|
|
27
35
|
import { sha256 } from "crypto-hash"
|
|
28
36
|
import { deepmerge } from "deepmerge-ts"
|
|
29
|
-
import { filter, flat, isNonNullish, unique, uniqueBy } from "remeda"
|
|
37
|
+
import { filter, flat, isNonNullish, omit, unique, uniqueBy } from "remeda"
|
|
30
38
|
import {
|
|
31
39
|
type Container,
|
|
32
40
|
getFallbackContainerName,
|
|
@@ -39,7 +47,14 @@ import { Namespace } from "./namespace"
|
|
|
39
47
|
import { NetworkPolicy, type NetworkPolicyArgs } from "./network-policy"
|
|
40
48
|
import { podSpecDefaults } from "./pod"
|
|
41
49
|
import { mapContainerPortToServicePort, Service, type ServiceArgs } from "./service"
|
|
42
|
-
import {
|
|
50
|
+
import {
|
|
51
|
+
commonExtraArgs,
|
|
52
|
+
getClusterKubeconfigContent,
|
|
53
|
+
images,
|
|
54
|
+
NamespacedResource,
|
|
55
|
+
type ScopedResourceArgs,
|
|
56
|
+
type SelectorLike,
|
|
57
|
+
} from "./shared"
|
|
43
58
|
|
|
44
59
|
export type WorkloadTerminalArgs = {
|
|
45
60
|
/**
|
|
@@ -84,31 +99,150 @@ export type WorkloadArgs = ScopedResourceArgs & {
|
|
|
84
99
|
|
|
85
100
|
export const workloadExtraArgs = [...commonExtraArgs, "container", "containers"] as const
|
|
86
101
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Filters pod template containers for patch operations to include only containers owned by the patch args.
|
|
104
|
+
*
|
|
105
|
+
* This prevents patching containers from existing workload spec that are not declared via workload container args.
|
|
106
|
+
*
|
|
107
|
+
* @param template The merged pod template that will be sent to Kubernetes.
|
|
108
|
+
* @param ownedTemplate The pod template generated from workload container args.
|
|
109
|
+
* @returns The pod template with filtered container lists.
|
|
110
|
+
*/
|
|
111
|
+
export function filterPatchOwnedContainersInTemplate(
|
|
112
|
+
template: Unwrap<types.input.core.v1.PodTemplateSpec>,
|
|
113
|
+
ownedTemplate: Unwrap<types.input.core.v1.PodTemplateSpec>,
|
|
114
|
+
): Unwrap<types.input.core.v1.PodTemplateSpec> {
|
|
115
|
+
const ownedContainerNames = unique(
|
|
116
|
+
(ownedTemplate.spec?.containers ?? []).map(container => container.name).filter(isNonNullish),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
const ownedInitContainerNames = unique(
|
|
120
|
+
(ownedTemplate.spec?.initContainers ?? [])
|
|
121
|
+
.map(container => container.name)
|
|
122
|
+
.filter(isNonNullish),
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
const filterByOwnedNames = <TContainer extends { name?: string }>(
|
|
126
|
+
source: TContainer[] | undefined,
|
|
127
|
+
ownedNames: string[],
|
|
128
|
+
): TContainer[] | undefined => {
|
|
129
|
+
if (!source || source.length === 0 || ownedNames.length === 0) {
|
|
130
|
+
return undefined
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const filtered = source.filter(container =>
|
|
134
|
+
container.name ? ownedNames.includes(container.name) : false,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
return filtered.length > 0 ? filtered : undefined
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const containers = filterByOwnedNames(template.spec?.containers, ownedContainerNames)
|
|
141
|
+
const initContainers = filterByOwnedNames(template.spec?.initContainers, ownedInitContainerNames)
|
|
142
|
+
|
|
143
|
+
const {
|
|
144
|
+
containers: _containers,
|
|
145
|
+
initContainers: _initContainers,
|
|
146
|
+
...restSpec
|
|
147
|
+
} = template.spec ?? {}
|
|
148
|
+
|
|
149
|
+
const spec = {
|
|
150
|
+
...restSpec,
|
|
151
|
+
...(containers ? { containers } : {}),
|
|
152
|
+
...(initContainers ? { initContainers } : {}),
|
|
153
|
+
} as Partial<Unwrap<types.input.core.v1.PodSpec>>
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
...template,
|
|
157
|
+
spec: spec as Unwrap<types.input.core.v1.PodSpec>,
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
type WorkloadHttpGatewayRuleArgs = DistributedOmit<GatewayHttpRuleArgs, "backend" | "backends"> & {
|
|
162
|
+
/**
|
|
163
|
+
* The service port to route to.
|
|
164
|
+
*
|
|
165
|
+
* Can be either a numeric port or a named port from the workload service.
|
|
166
|
+
*
|
|
167
|
+
* If not specified, it first falls back to the servicePort of the route,
|
|
168
|
+
* then to the first port of the workload service.
|
|
169
|
+
*/
|
|
170
|
+
servicePort?: Input<number | string>
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
type WorkloadTcpUdpGatewayRuleArgs = DistributedOmit<GatewayRuleArgs, "backend" | "backends"> & {
|
|
174
|
+
/**
|
|
175
|
+
* The service port to route to.
|
|
176
|
+
*
|
|
177
|
+
* Can be either a numeric port or a named port from the workload service.
|
|
178
|
+
*
|
|
179
|
+
* If not specified, it first falls back to the servicePort of the route,
|
|
180
|
+
* then to the first port of the workload service.
|
|
181
|
+
*/
|
|
182
|
+
servicePort?: Input<number | string>
|
|
183
|
+
}
|
|
91
184
|
|
|
92
|
-
|
|
185
|
+
type WorkloadGatewayRuleArgs = WorkloadHttpGatewayRuleArgs | WorkloadTcpUdpGatewayRuleArgs
|
|
186
|
+
|
|
187
|
+
export type WorkloadRouteArgs = Except<AccessPointRouteArgs, "backend" | "backends" | "rules"> & {
|
|
188
|
+
/**
|
|
189
|
+
* The service port to route to by default.
|
|
190
|
+
*
|
|
191
|
+
* Can be either a numeric port or a named port from the workload service.
|
|
192
|
+
*
|
|
193
|
+
* Can be overridden by `rules.*.servicePort`.
|
|
194
|
+
* If omitted, the first port of the workload service is used.
|
|
195
|
+
*/
|
|
196
|
+
servicePort?: Input<number | string>
|
|
197
|
+
} & (
|
|
198
|
+
| {
|
|
199
|
+
type: "http"
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* The path to match for the `default` rule of the listener.
|
|
203
|
+
*/
|
|
204
|
+
path?: Input<string>
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* The paths to match for the `default` rule of the listener.
|
|
208
|
+
*/
|
|
209
|
+
paths?: Input<string[]>
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* The rules of the route.
|
|
213
|
+
*/
|
|
214
|
+
rules?: InputRecord<WorkloadHttpGatewayRuleArgs>
|
|
215
|
+
}
|
|
216
|
+
| {
|
|
217
|
+
type: "tcp" | "udp"
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* The rules of the route.
|
|
221
|
+
*/
|
|
222
|
+
rules?: InputRecord<WorkloadTcpUdpGatewayRuleArgs>
|
|
223
|
+
}
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
export type WorkloadServiceArgs = WorkloadArgs & {
|
|
93
227
|
service?: Input<Omit<ServiceArgs, "cluster" | "namespace">>
|
|
94
228
|
|
|
95
229
|
/**
|
|
96
230
|
* The configuration for the access point route to create.
|
|
97
231
|
*/
|
|
98
|
-
route?: Input<
|
|
232
|
+
route?: Input<WorkloadRouteArgs>
|
|
99
233
|
|
|
100
234
|
/**
|
|
101
235
|
* The configuration for the access point routes to create.
|
|
102
236
|
*/
|
|
103
|
-
routes?: InputArray<
|
|
237
|
+
routes?: InputArray<WorkloadRouteArgs>
|
|
104
238
|
|
|
105
239
|
/**
|
|
106
240
|
* The existing workload to patch.
|
|
107
241
|
*/
|
|
108
|
-
existing?: Input<k8s.
|
|
242
|
+
existing?: Input<k8s.Workload>
|
|
109
243
|
}
|
|
110
244
|
|
|
111
|
-
export const
|
|
245
|
+
export const workloadServiceExtraArgs = [
|
|
112
246
|
...workloadExtraArgs,
|
|
113
247
|
"service",
|
|
114
248
|
"route",
|
|
@@ -116,9 +250,8 @@ export const exposableWorkloadExtraArgs = [
|
|
|
116
250
|
] as const
|
|
117
251
|
|
|
118
252
|
export type WorkloadType = "Deployment" | "StatefulSet" | "Job" | "CronJob"
|
|
119
|
-
export type ExposableWorkloadType = "Deployment" | "StatefulSet"
|
|
120
253
|
|
|
121
|
-
export type GenericWorkloadArgs = Omit<
|
|
254
|
+
export type GenericWorkloadArgs = Omit<WorkloadServiceArgs, "existing"> & {
|
|
122
255
|
/**
|
|
123
256
|
* The type of workload to create.
|
|
124
257
|
*
|
|
@@ -136,14 +269,14 @@ export type GenericWorkloadArgs = Omit<ExposableWorkloadArgs, "existing"> & {
|
|
|
136
269
|
*
|
|
137
270
|
* Will be ignored for other workload types.
|
|
138
271
|
*/
|
|
139
|
-
deployment?: Input<DeploymentArgs
|
|
272
|
+
deployment?: Input<Omit<DeploymentArgs, "name" | "namespace">>
|
|
140
273
|
|
|
141
274
|
/**
|
|
142
275
|
* The args specific to the "StatefulSet" workload type.
|
|
143
276
|
*
|
|
144
277
|
* Will be ignored for other workload types.
|
|
145
278
|
*/
|
|
146
|
-
statefulSet?: Input<StatefulSetArgs
|
|
279
|
+
statefulSet?: Input<Omit<StatefulSetArgs, "name" | "namespace">>
|
|
147
280
|
|
|
148
281
|
/**
|
|
149
282
|
* The args specific to the "Job" workload type.
|
|
@@ -160,33 +293,14 @@ export type GenericWorkloadArgs = Omit<ExposableWorkloadArgs, "existing"> & {
|
|
|
160
293
|
cronJob?: Input<JobArgs>
|
|
161
294
|
}
|
|
162
295
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* The existing workload to patch.
|
|
173
|
-
*/
|
|
174
|
-
existing: Input<k8s.ExposableWorkload | undefined>
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* The args specific to the "Deployment" workload type.
|
|
178
|
-
*
|
|
179
|
-
* Will be ignored for other workload types.
|
|
180
|
-
*/
|
|
181
|
-
deployment?: Input<DeploymentArgs>
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* The args specific to the "StatefulSet" workload type.
|
|
185
|
-
*
|
|
186
|
-
* Will be ignored for other workload types.
|
|
187
|
-
*/
|
|
188
|
-
statefulSet?: Input<StatefulSetArgs>
|
|
189
|
-
}
|
|
296
|
+
const genericWorkloadExtraArgs = [
|
|
297
|
+
"defaultType",
|
|
298
|
+
"existing",
|
|
299
|
+
"deployment",
|
|
300
|
+
"statefulSet",
|
|
301
|
+
"job",
|
|
302
|
+
"cronJob",
|
|
303
|
+
] as const
|
|
190
304
|
|
|
191
305
|
export function getWorkloadComponents(
|
|
192
306
|
name: string,
|
|
@@ -315,9 +429,9 @@ export function getWorkloadComponents(
|
|
|
315
429
|
return { labels, containers, volumes, podSpec, podTemplate, networkPolicy }
|
|
316
430
|
}
|
|
317
431
|
|
|
318
|
-
export function
|
|
432
|
+
export function getWorkloadServiceComponents(
|
|
319
433
|
name: string,
|
|
320
|
-
args:
|
|
434
|
+
args: WorkloadServiceArgs,
|
|
321
435
|
parent: () => ComponentResource,
|
|
322
436
|
opts: ComponentResourceOptions | undefined,
|
|
323
437
|
isForPatch?: boolean,
|
|
@@ -361,7 +475,7 @@ export function getExposableWorkloadComponents(
|
|
|
361
475
|
routesArgs: normalizeInputs(args.route, args.routes),
|
|
362
476
|
service,
|
|
363
477
|
namespace: output(args.namespace),
|
|
364
|
-
}).apply(({ routesArgs, service, namespace }) => {
|
|
478
|
+
}).apply(async ({ routesArgs, service, namespace }) => {
|
|
365
479
|
if (!routesArgs.length || !service) {
|
|
366
480
|
return []
|
|
367
481
|
}
|
|
@@ -370,16 +484,114 @@ export function getExposableWorkloadComponents(
|
|
|
370
484
|
return []
|
|
371
485
|
}
|
|
372
486
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
...routeArgs,
|
|
376
|
-
endpoints: service.endpoints,
|
|
487
|
+
const serviceEndpoints = await toPromise(service.endpoints)
|
|
488
|
+
const servicePorts = await toPromise(service.spec.ports)
|
|
377
489
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
490
|
+
const resolveServiceEndpoints = async (
|
|
491
|
+
servicePort: Input<number | string> | undefined,
|
|
492
|
+
routeName: string,
|
|
493
|
+
): Promise<network.L4Endpoint[]> => {
|
|
494
|
+
if (serviceEndpoints.length === 0) {
|
|
495
|
+
throw new Error(`No endpoints found for workload service in route "${routeName}"`)
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
let resolvedServicePort: number | undefined
|
|
499
|
+
|
|
500
|
+
if (servicePort != null) {
|
|
501
|
+
const requestedServicePort = await toPromise(servicePort)
|
|
502
|
+
|
|
503
|
+
if (typeof requestedServicePort === "string") {
|
|
504
|
+
const namedPort = servicePorts?.find(port => port.name === requestedServicePort)
|
|
505
|
+
|
|
506
|
+
if (!namedPort) {
|
|
507
|
+
throw new Error(
|
|
508
|
+
`Named port "${requestedServicePort}" not found for workload service in route "${routeName}"`,
|
|
509
|
+
)
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
resolvedServicePort = namedPort.port
|
|
513
|
+
} else {
|
|
514
|
+
resolvedServicePort = requestedServicePort
|
|
515
|
+
}
|
|
516
|
+
} else {
|
|
517
|
+
resolvedServicePort = serviceEndpoints[0]?.port
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (resolvedServicePort == null) {
|
|
521
|
+
throw new Error(
|
|
522
|
+
`Unable to resolve service port for workload service in route "${routeName}"`,
|
|
523
|
+
)
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const filteredEndpoints = serviceEndpoints.filter(
|
|
527
|
+
endpoint => endpoint.port === resolvedServicePort,
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
if (filteredEndpoints.length === 0) {
|
|
531
|
+
throw new Error(
|
|
532
|
+
`No endpoints with port ${resolvedServicePort} found for workload service in route "${routeName}"`,
|
|
533
|
+
)
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return filteredEndpoints
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
return await Promise.all(
|
|
540
|
+
routesArgs.map(async (routeArgs, index) => {
|
|
541
|
+
const routeName = `${name}.${index}`
|
|
542
|
+
const routeRules = (await toPromise(routeArgs.rules)) as
|
|
543
|
+
| Record<string, WorkloadGatewayRuleArgs>
|
|
544
|
+
| undefined
|
|
545
|
+
const routeRuleValues = Object.values(routeRules ?? {})
|
|
546
|
+
const needsDefaultBackend =
|
|
547
|
+
routeRuleValues.length === 0 || routeRuleValues.some(rule => rule.servicePort == null)
|
|
548
|
+
|
|
549
|
+
const defaultServiceEndpoints = needsDefaultBackend
|
|
550
|
+
? await resolveServiceEndpoints(routeArgs.servicePort, routeName)
|
|
551
|
+
: undefined
|
|
552
|
+
|
|
553
|
+
const resolvedRules = routeRules
|
|
554
|
+
? await Promise.all(
|
|
555
|
+
Object.entries(routeRules).map(async ([ruleName, rule]) => {
|
|
556
|
+
const ruleServiceEndpoints = await resolveServiceEndpoints(
|
|
557
|
+
rule.servicePort ?? routeArgs.servicePort,
|
|
558
|
+
`${routeName}:${ruleName}`,
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
return [
|
|
562
|
+
ruleName,
|
|
563
|
+
{
|
|
564
|
+
...omit(rule, ["servicePort"]),
|
|
565
|
+
backend: {
|
|
566
|
+
endpoints: ruleServiceEndpoints,
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
] as const
|
|
570
|
+
}),
|
|
571
|
+
)
|
|
572
|
+
: undefined
|
|
573
|
+
|
|
574
|
+
const resolvedRulesInput = resolvedRules
|
|
575
|
+
? (Object.fromEntries(resolvedRules) as unknown as InputRecord<GatewayRuleArgs>)
|
|
576
|
+
: undefined
|
|
577
|
+
|
|
578
|
+
return new AccessPointRoute(routeName, {
|
|
579
|
+
...omit(routeArgs, ["servicePort", "rules"]),
|
|
580
|
+
...(defaultServiceEndpoints
|
|
581
|
+
? {
|
|
582
|
+
backend: {
|
|
583
|
+
endpoints: defaultServiceEndpoints,
|
|
584
|
+
},
|
|
585
|
+
}
|
|
586
|
+
: {}),
|
|
587
|
+
rules: resolvedRulesInput,
|
|
588
|
+
metadata: {
|
|
589
|
+
...(routeArgs.metadata ?? {}),
|
|
590
|
+
"k8s.namespace": namespace,
|
|
591
|
+
},
|
|
592
|
+
})
|
|
593
|
+
}),
|
|
594
|
+
)
|
|
383
595
|
})
|
|
384
596
|
|
|
385
597
|
return { labels, containers, volumes, podSpec, podTemplate, networkPolicy, service, routes }
|
|
@@ -409,17 +621,81 @@ export abstract class Workload extends NamespacedResource {
|
|
|
409
621
|
* Will be created if one or more containers have `allowedEndpoints` defined.
|
|
410
622
|
*/
|
|
411
623
|
readonly networkPolicy: Output<NetworkPolicy | undefined>,
|
|
624
|
+
|
|
625
|
+
protected readonly _service: Output<Service | undefined> = output(undefined),
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* The access point routes associated with the workload.
|
|
629
|
+
*/
|
|
630
|
+
readonly routes: Output<AccessPointRoute[]> = output([]),
|
|
412
631
|
) {
|
|
413
632
|
super(type, name, args, opts, metadata, namespace)
|
|
414
633
|
}
|
|
415
634
|
|
|
635
|
+
abstract get entity(): Output<k8s.Workload>
|
|
636
|
+
|
|
416
637
|
protected abstract get templateMetadata(): Output<types.output.meta.v1.ObjectMeta>
|
|
417
638
|
|
|
418
639
|
protected abstract getTerminalMeta(): Output<UnitTerminal["meta"]>
|
|
419
640
|
|
|
420
|
-
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: for pulumi which for some reason tries to copy all properties
|
|
421
641
|
private set terminal(_value: never) {}
|
|
422
642
|
|
|
643
|
+
private set logsTerminal(_value: never) {}
|
|
644
|
+
|
|
645
|
+
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: for pulumi which for some reason tries to copy all properties
|
|
646
|
+
private set terminals(_value: never) {}
|
|
647
|
+
|
|
648
|
+
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: for pulumi which for some reason tries to copy all properties
|
|
649
|
+
private set optionalService(_value: never) {}
|
|
650
|
+
|
|
651
|
+
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: for pulumi which for some reason tries to copy all properties
|
|
652
|
+
private set service(_value: never) {}
|
|
653
|
+
|
|
654
|
+
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: for pulumi which for some reason tries to copy all properties
|
|
655
|
+
private set selector(_value: never) {}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* The service associated with the workload.
|
|
659
|
+
*/
|
|
660
|
+
get optionalService(): Output<Service | undefined> {
|
|
661
|
+
return this._service
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* The service associated with the workload.
|
|
666
|
+
*
|
|
667
|
+
* Will throw an error if the service is not available.
|
|
668
|
+
*/
|
|
669
|
+
get service(): Output<Service> {
|
|
670
|
+
return this._service.apply(service => {
|
|
671
|
+
if (!service) {
|
|
672
|
+
throw new Error(`The service of the workload "${this.name}" is not available.`)
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
return service
|
|
676
|
+
})
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* The merged and deduplicated L3 endpoints of all routes.
|
|
681
|
+
*/
|
|
682
|
+
get endpoints(): Output<network.L3Endpoint[]> {
|
|
683
|
+
return this.routes.apply(routes =>
|
|
684
|
+
output(routes.map(route => route.route.endpoints))
|
|
685
|
+
.apply(endpoints => flat(endpoints))
|
|
686
|
+
.apply(mergeEndpoints),
|
|
687
|
+
)
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* The selector matching pods created from this workload's template labels.
|
|
692
|
+
*/
|
|
693
|
+
get selector(): Output<SelectorLike> {
|
|
694
|
+
return this.podTemplate.apply(template => ({
|
|
695
|
+
matchLabels: template.metadata?.labels,
|
|
696
|
+
}))
|
|
697
|
+
}
|
|
698
|
+
|
|
423
699
|
/**
|
|
424
700
|
* The instance terminal to interact with the workload's pods.
|
|
425
701
|
*/
|
|
@@ -445,11 +721,15 @@ export abstract class Workload extends NamespacedResource {
|
|
|
445
721
|
command: ["bash", "/welcome.sh"],
|
|
446
722
|
|
|
447
723
|
files: {
|
|
448
|
-
"/kubeconfig":
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
724
|
+
"/kubeconfig": makeFileOutput({
|
|
725
|
+
name: "kubeconfig",
|
|
726
|
+
content: getClusterKubeconfigContent(this.cluster),
|
|
727
|
+
isSecret: true,
|
|
728
|
+
}),
|
|
729
|
+
|
|
730
|
+
"/welcome.sh": makeFileOutput({
|
|
731
|
+
name: "welcome.sh",
|
|
732
|
+
content: interpolate`
|
|
453
733
|
#!/bin/bash
|
|
454
734
|
set -euo pipefail
|
|
455
735
|
|
|
@@ -494,7 +774,98 @@ export abstract class Workload extends NamespacedResource {
|
|
|
494
774
|
# execute into the selected pod
|
|
495
775
|
exec kubectl exec -it -n "$NAMESPACE" "$SELECTED_POD" -c "$CONTAINER_NAME" -- "$SHELL"
|
|
496
776
|
`.apply(trimIndentation),
|
|
497
|
-
),
|
|
777
|
+
}),
|
|
778
|
+
},
|
|
779
|
+
|
|
780
|
+
env: {
|
|
781
|
+
KUBECONFIG: "/kubeconfig",
|
|
782
|
+
},
|
|
783
|
+
},
|
|
784
|
+
})
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* The instance terminal to view the workload's logs.
|
|
789
|
+
*/
|
|
790
|
+
get logsTerminal(): Output<UnitTerminal> {
|
|
791
|
+
const containerName = this.podTemplate.spec.containers.apply(containers => containers[0].name)
|
|
792
|
+
|
|
793
|
+
const podLabelSelector = this.templateMetadata
|
|
794
|
+
.apply(meta => meta.labels ?? {})
|
|
795
|
+
.apply(labels =>
|
|
796
|
+
Object.entries(labels)
|
|
797
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
798
|
+
.join(","),
|
|
799
|
+
)
|
|
800
|
+
|
|
801
|
+
return output({
|
|
802
|
+
name: interpolate`${this.metadata.name}.logs`,
|
|
803
|
+
|
|
804
|
+
meta: output(this.getTerminalMeta()).apply(meta => ({
|
|
805
|
+
...meta,
|
|
806
|
+
title: `${meta.title} Logs`,
|
|
807
|
+
globalTitle: `${meta.globalTitle} | Logs`,
|
|
808
|
+
description: `The logs of ${meta.title.toLowerCase()}.`,
|
|
809
|
+
})),
|
|
810
|
+
|
|
811
|
+
spec: {
|
|
812
|
+
image: images["terminal-kubectl"].image,
|
|
813
|
+
command: ["bash", "/welcome.sh"],
|
|
814
|
+
|
|
815
|
+
files: {
|
|
816
|
+
"/kubeconfig": makeFileOutput({
|
|
817
|
+
name: "kubeconfig",
|
|
818
|
+
content: getClusterKubeconfigContent(this.cluster),
|
|
819
|
+
isSecret: true,
|
|
820
|
+
}),
|
|
821
|
+
|
|
822
|
+
"/welcome.sh": makeFileOutput({
|
|
823
|
+
name: "welcome.sh",
|
|
824
|
+
content: interpolate`
|
|
825
|
+
#!/bin/bash
|
|
826
|
+
set -euo pipefail
|
|
827
|
+
|
|
828
|
+
NAMESPACE="${this.metadata.namespace}"
|
|
829
|
+
RESOURCE_TYPE="${this.kind.toLowerCase()}"
|
|
830
|
+
RESOURCE_NAME="${this.metadata.name}"
|
|
831
|
+
CONTAINER_NAME="${containerName}"
|
|
832
|
+
LABEL_SELECTOR="${podLabelSelector}"
|
|
833
|
+
|
|
834
|
+
echo "Connecting to logs of $RESOURCE_TYPE \"$RESOURCE_NAME\" in namespace \"$NAMESPACE\""
|
|
835
|
+
|
|
836
|
+
# get all pods for this workload
|
|
837
|
+
PODS=$(kubectl get pods -n "$NAMESPACE" -l "$LABEL_SELECTOR" -o jsonpath='{.items[*].metadata.name}' 2>/dev/null || echo "")
|
|
838
|
+
|
|
839
|
+
if [ -z "$PODS" ]; then
|
|
840
|
+
echo "No pods found"
|
|
841
|
+
exit 1
|
|
842
|
+
fi
|
|
843
|
+
|
|
844
|
+
# convert space-separated string to array
|
|
845
|
+
read -ra POD_ARRAY <<< "$PODS"
|
|
846
|
+
|
|
847
|
+
if [ \${#POD_ARRAY[@]} -eq 1 ]; then
|
|
848
|
+
# single pod found, connect directly
|
|
849
|
+
SELECTED_POD="\${POD_ARRAY[0]}"
|
|
850
|
+
echo "Found single pod: $SELECTED_POD"
|
|
851
|
+
else
|
|
852
|
+
# multiple pods found, use fzf for selection
|
|
853
|
+
echo "Found \${#POD_ARRAY[@]} pods. Please select one."
|
|
854
|
+
|
|
855
|
+
SELECTED_POD=$(printf '%s\n' "\${POD_ARRAY[@]}" | fzf --prompt="Select pod: " --height 10 --border --info=inline)
|
|
856
|
+
|
|
857
|
+
if [ -z "$SELECTED_POD" ]; then
|
|
858
|
+
echo "No pod selected"
|
|
859
|
+
exit 1
|
|
860
|
+
fi
|
|
861
|
+
|
|
862
|
+
echo "Selected pod: $SELECTED_POD"
|
|
863
|
+
fi
|
|
864
|
+
|
|
865
|
+
# stream logs for the selected pod
|
|
866
|
+
exec kubectl logs -f -n "$NAMESPACE" "$SELECTED_POD" -c "$CONTAINER_NAME"
|
|
867
|
+
`.apply(trimIndentation),
|
|
868
|
+
}),
|
|
498
869
|
},
|
|
499
870
|
|
|
500
871
|
env: {
|
|
@@ -504,6 +875,13 @@ export abstract class Workload extends NamespacedResource {
|
|
|
504
875
|
})
|
|
505
876
|
}
|
|
506
877
|
|
|
878
|
+
/**
|
|
879
|
+
* The instance terminals to interact with the workload's pods and view its logs.
|
|
880
|
+
*/
|
|
881
|
+
get terminals(): Output<UnitTerminal>[] {
|
|
882
|
+
return [this.logsTerminal, this.terminal]
|
|
883
|
+
}
|
|
884
|
+
|
|
507
885
|
/**
|
|
508
886
|
* Creates a terminal with a custom command.
|
|
509
887
|
*
|
|
@@ -517,9 +895,7 @@ export abstract class Workload extends NamespacedResource {
|
|
|
517
895
|
command: InputArray<string>,
|
|
518
896
|
spec?: { env?: DeepInput<TerminalSpec["env"]>; files?: DeepInput<TerminalSpec["files"]> },
|
|
519
897
|
): Output<UnitTerminal> {
|
|
520
|
-
const containerName =
|
|
521
|
-
return containers[0]?.name ?? this.name
|
|
522
|
-
})
|
|
898
|
+
const containerName = this.podTemplate.spec.containers.apply(containers => containers[0].name)
|
|
523
899
|
|
|
524
900
|
return output({
|
|
525
901
|
name,
|
|
@@ -539,7 +915,7 @@ export abstract class Workload extends NamespacedResource {
|
|
|
539
915
|
"-it",
|
|
540
916
|
"-n",
|
|
541
917
|
this.metadata.namespace,
|
|
542
|
-
`${this.kind.toLowerCase()}/${this.metadata.name}`,
|
|
918
|
+
interpolate`${this.kind.toLowerCase()}/${this.metadata.name}`,
|
|
543
919
|
"-c",
|
|
544
920
|
containerName,
|
|
545
921
|
"--",
|
|
@@ -547,7 +923,11 @@ export abstract class Workload extends NamespacedResource {
|
|
|
547
923
|
]),
|
|
548
924
|
|
|
549
925
|
files: {
|
|
550
|
-
"/kubeconfig":
|
|
926
|
+
"/kubeconfig": makeFileOutput({
|
|
927
|
+
name: "kubeconfig",
|
|
928
|
+
content: getClusterKubeconfigContent(this.cluster),
|
|
929
|
+
isSecret: true,
|
|
930
|
+
}),
|
|
551
931
|
...spec?.files,
|
|
552
932
|
},
|
|
553
933
|
|
|
@@ -568,13 +948,17 @@ export abstract class Workload extends NamespacedResource {
|
|
|
568
948
|
opts?: CustomResourceOptions,
|
|
569
949
|
): Output<Workload> {
|
|
570
950
|
return output(args).apply(async args => {
|
|
951
|
+
const baseArgs = omit(args, genericWorkloadExtraArgs)
|
|
952
|
+
|
|
571
953
|
if (args.existing?.kind === "Deployment") {
|
|
572
954
|
const { Deployment } = await import("./deployment")
|
|
573
955
|
|
|
956
|
+
const deploymentArgs = deepmerge(baseArgs, args.deployment ?? {}) as DeploymentArgs
|
|
957
|
+
|
|
574
958
|
return Deployment.patch(
|
|
575
959
|
name,
|
|
576
960
|
{
|
|
577
|
-
...
|
|
961
|
+
...deploymentArgs,
|
|
578
962
|
name: args.existing.metadata.name,
|
|
579
963
|
namespace: Namespace.forResourceAsync(args.existing, output(args.namespace).cluster),
|
|
580
964
|
},
|
|
@@ -585,10 +969,12 @@ export abstract class Workload extends NamespacedResource {
|
|
|
585
969
|
if (args.existing?.kind === "StatefulSet") {
|
|
586
970
|
const { StatefulSet } = await import("./stateful-set")
|
|
587
971
|
|
|
972
|
+
const statefulSetArgs = deepmerge(baseArgs, args.statefulSet ?? {}) as StatefulSetArgs
|
|
973
|
+
|
|
588
974
|
return StatefulSet.patch(
|
|
589
975
|
name,
|
|
590
976
|
{
|
|
591
|
-
...
|
|
977
|
+
...statefulSetArgs,
|
|
592
978
|
name: args.existing.metadata.name,
|
|
593
979
|
namespace: Namespace.forResourceAsync(args.existing, output(args.namespace).cluster),
|
|
594
980
|
},
|
|
@@ -599,10 +985,12 @@ export abstract class Workload extends NamespacedResource {
|
|
|
599
985
|
if (args.existing?.kind === "Job") {
|
|
600
986
|
const { Job } = await import("./job")
|
|
601
987
|
|
|
988
|
+
const jobArgs = deepmerge(baseArgs, args.job ?? {}) as JobArgs
|
|
989
|
+
|
|
602
990
|
return Job.patch(
|
|
603
991
|
name,
|
|
604
992
|
{
|
|
605
|
-
...
|
|
993
|
+
...jobArgs,
|
|
606
994
|
name: args.existing.metadata.name,
|
|
607
995
|
namespace: Namespace.forResourceAsync(args.existing, output(args.namespace).cluster),
|
|
608
996
|
},
|
|
@@ -613,10 +1001,12 @@ export abstract class Workload extends NamespacedResource {
|
|
|
613
1001
|
if (args.existing?.kind === "CronJob") {
|
|
614
1002
|
const { CronJob } = await import("./cron-job")
|
|
615
1003
|
|
|
1004
|
+
const cronJobArgs = deepmerge(baseArgs, args.cronJob ?? {}) as JobArgs
|
|
1005
|
+
|
|
616
1006
|
return CronJob.patch(
|
|
617
1007
|
name,
|
|
618
1008
|
{
|
|
619
|
-
...
|
|
1009
|
+
...cronJobArgs,
|
|
620
1010
|
name: args.existing.metadata.name,
|
|
621
1011
|
namespace: Namespace.forResourceAsync(args.existing, output(args.namespace).cluster),
|
|
622
1012
|
},
|
|
@@ -627,166 +1017,33 @@ export abstract class Workload extends NamespacedResource {
|
|
|
627
1017
|
if (args.defaultType === "Deployment") {
|
|
628
1018
|
const { Deployment } = await import("./deployment")
|
|
629
1019
|
|
|
630
|
-
|
|
1020
|
+
const deploymentArgs = deepmerge(baseArgs, args.deployment ?? {}) as DeploymentArgs
|
|
1021
|
+
|
|
1022
|
+
return Deployment.create(name, deploymentArgs, opts)
|
|
631
1023
|
}
|
|
632
1024
|
|
|
633
1025
|
if (args.defaultType === "StatefulSet") {
|
|
634
1026
|
const { StatefulSet } = await import("./stateful-set")
|
|
635
1027
|
|
|
636
|
-
|
|
1028
|
+
const statefulSetArgs = deepmerge(baseArgs, args.statefulSet ?? {}) as StatefulSetArgs
|
|
1029
|
+
|
|
1030
|
+
return StatefulSet.create(name, statefulSetArgs, opts)
|
|
637
1031
|
}
|
|
638
1032
|
|
|
639
1033
|
if (args.defaultType === "Job") {
|
|
640
1034
|
const { Job } = await import("./job")
|
|
641
1035
|
|
|
642
|
-
|
|
1036
|
+
const jobArgs = deepmerge(baseArgs, args.job ?? {}) as JobArgs
|
|
1037
|
+
|
|
1038
|
+
return Job.create(name, jobArgs, opts)
|
|
643
1039
|
}
|
|
644
1040
|
|
|
645
1041
|
if (args.defaultType === "CronJob") {
|
|
646
1042
|
const { CronJob } = await import("./cron-job")
|
|
647
1043
|
|
|
648
|
-
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
throw new Error(`Unknown workload type: ${args.defaultType as string}`)
|
|
652
|
-
})
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
export abstract class ExposableWorkload extends Workload {
|
|
657
|
-
protected constructor(
|
|
658
|
-
type: string,
|
|
659
|
-
name: string,
|
|
660
|
-
args: Inputs,
|
|
661
|
-
opts: ComponentResourceOptions | undefined,
|
|
662
|
-
|
|
663
|
-
metadata: Output<types.output.meta.v1.ObjectMeta>,
|
|
664
|
-
namespace: Output<Namespace>,
|
|
665
|
-
|
|
666
|
-
terminalArgs: Output<Unwrap<WorkloadTerminalArgs>>,
|
|
667
|
-
containers: Output<Container[]>,
|
|
668
|
-
podTemplate: Output<types.output.core.v1.PodTemplateSpec>,
|
|
669
|
-
networkPolicy: Output<NetworkPolicy | undefined>,
|
|
670
|
-
|
|
671
|
-
protected readonly _service: Output<Service | undefined>,
|
|
672
|
-
|
|
673
|
-
/**
|
|
674
|
-
* The access point routes associated with the workload.
|
|
675
|
-
*/
|
|
676
|
-
readonly routes: Output<AccessPointRoute[]>,
|
|
677
|
-
) {
|
|
678
|
-
super(
|
|
679
|
-
type,
|
|
680
|
-
name,
|
|
681
|
-
args,
|
|
682
|
-
opts,
|
|
683
|
-
metadata,
|
|
684
|
-
namespace,
|
|
685
|
-
terminalArgs,
|
|
686
|
-
containers,
|
|
687
|
-
podTemplate,
|
|
688
|
-
networkPolicy,
|
|
689
|
-
)
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: for pulumi which for some reason tries to copy all properties
|
|
693
|
-
private set optionalService(_value: never) {}
|
|
694
|
-
|
|
695
|
-
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: for pulumi which for some reason tries to copy all properties
|
|
696
|
-
private set service(_value: never) {}
|
|
697
|
-
|
|
698
|
-
/**
|
|
699
|
-
* The service associated with the workload.
|
|
700
|
-
*/
|
|
701
|
-
get optionalService(): Output<Service | undefined> {
|
|
702
|
-
return this._service
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
/**
|
|
706
|
-
* The service associated with the workload.
|
|
707
|
-
*
|
|
708
|
-
* Will throw an error if the service is not available.
|
|
709
|
-
*/
|
|
710
|
-
get service(): Output<Service> {
|
|
711
|
-
return this._service.apply(service => {
|
|
712
|
-
if (!service) {
|
|
713
|
-
throw new Error(`The service of the workload "${this.name}" is not available.`)
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
return service
|
|
717
|
-
})
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
/**
|
|
721
|
-
* The merged and deduplicated L3 endpoints of all routes.
|
|
722
|
-
*/
|
|
723
|
-
get endpoints(): Output<network.L3Endpoint[]> {
|
|
724
|
-
return this.routes.apply(routes =>
|
|
725
|
-
output(routes.map(route => route.route.endpoints))
|
|
726
|
-
.apply(endpoints => flat(endpoints))
|
|
727
|
-
.apply(mergeEndpoints),
|
|
728
|
-
)
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
/**
|
|
732
|
-
* The entity of the workload.
|
|
733
|
-
*/
|
|
734
|
-
abstract get entity(): Output<k8s.ExposableWorkload>
|
|
735
|
-
|
|
736
|
-
/**
|
|
737
|
-
* The sped of the underlying Kubernetes workload.
|
|
738
|
-
*/
|
|
739
|
-
abstract get spec(): Output<
|
|
740
|
-
types.output.apps.v1.DeploymentSpec | types.output.apps.v1.StatefulSetSpec
|
|
741
|
-
>
|
|
742
|
-
|
|
743
|
-
/**
|
|
744
|
-
* Creates a generic exposable workload or patches the existing one.
|
|
745
|
-
*/
|
|
746
|
-
static createOrPatchGeneric(
|
|
747
|
-
name: string,
|
|
748
|
-
args: GenericExposableWorkloadArgs,
|
|
749
|
-
opts?: CustomResourceOptions,
|
|
750
|
-
): Output<ExposableWorkload> {
|
|
751
|
-
return output(args).apply(async args => {
|
|
752
|
-
if (args.existing?.kind === "Deployment") {
|
|
753
|
-
const { Deployment } = await import("./deployment")
|
|
754
|
-
|
|
755
|
-
return Deployment.patch(
|
|
756
|
-
name,
|
|
757
|
-
{
|
|
758
|
-
...deepmerge(args, args.deployment),
|
|
759
|
-
name: args.existing.metadata.name,
|
|
760
|
-
namespace: Namespace.forResourceAsync(args.existing, output(args.namespace).cluster),
|
|
761
|
-
},
|
|
762
|
-
opts,
|
|
763
|
-
)
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
if (args.existing?.kind === "StatefulSet") {
|
|
767
|
-
const { StatefulSet } = await import("./stateful-set")
|
|
768
|
-
|
|
769
|
-
return StatefulSet.patch(
|
|
770
|
-
name,
|
|
771
|
-
{
|
|
772
|
-
...deepmerge(args, args.statefulSet),
|
|
773
|
-
name: args.existing.metadata.name,
|
|
774
|
-
namespace: Namespace.forResourceAsync(args.existing, output(args.namespace).cluster),
|
|
775
|
-
},
|
|
776
|
-
opts,
|
|
777
|
-
)
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
if (args.defaultType === "Deployment") {
|
|
781
|
-
const { Deployment } = await import("./deployment")
|
|
782
|
-
|
|
783
|
-
return Deployment.create(name, deepmerge(args, args.deployment), opts)
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
if (args.defaultType === "StatefulSet") {
|
|
787
|
-
const { StatefulSet } = await import("./stateful-set")
|
|
1044
|
+
const cronJobArgs = deepmerge(baseArgs, args.cronJob ?? {}) as JobArgs
|
|
788
1045
|
|
|
789
|
-
return
|
|
1046
|
+
return CronJob.create(name, cronJobArgs, opts)
|
|
790
1047
|
}
|
|
791
1048
|
|
|
792
1049
|
throw new Error(`Unknown workload type: ${args.defaultType as string}`)
|