@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
@@ -0,0 +1,185 @@
|
|
1
|
+
import type { k8s } from "@highstate/library"
|
2
|
+
import type { core, Provider } from "@pulumi/kubernetes"
|
3
|
+
import { DnsRecord } from "@highstate/common"
|
4
|
+
import { gateway } from "@highstate/gateway-api"
|
5
|
+
import {
|
6
|
+
normalize,
|
7
|
+
Output,
|
8
|
+
output,
|
9
|
+
toPromise,
|
10
|
+
type Input,
|
11
|
+
type InputArray,
|
12
|
+
} from "@highstate/pulumi"
|
13
|
+
import { NetworkPolicy } from "./network-policy"
|
14
|
+
import { getAppDisplayName, getAppName, mapNamespaceLikeToNamespaceName } from "./shared"
|
15
|
+
|
16
|
+
export type UseAccessPointResult = {
|
17
|
+
/**
|
18
|
+
* The gateway instance created according to the access point.
|
19
|
+
*/
|
20
|
+
gateway: gateway.v1.Gateway
|
21
|
+
|
22
|
+
/**
|
23
|
+
* The DNS record associated created according to the access point and gateway.
|
24
|
+
*/
|
25
|
+
dnsRecords: DnsRecord[]
|
26
|
+
|
27
|
+
/**
|
28
|
+
* The network policies associated with the access point.
|
29
|
+
*/
|
30
|
+
networkPolicies: NetworkPolicy[]
|
31
|
+
}
|
32
|
+
|
33
|
+
export type UseAccessPointArgs = Omit<CreateGatewayArgs, "gateway"> & {
|
34
|
+
accessPoint: Input<k8s.AccessPoint>
|
35
|
+
}
|
36
|
+
|
37
|
+
export function useAccessPoint(args: UseAccessPointArgs): Promise<UseAccessPointResult> {
|
38
|
+
const result = output({ args, namespaceName: output(args.namespace).metadata.name }).apply(
|
39
|
+
({ args, namespaceName }) => {
|
40
|
+
const gateway = createGateway({
|
41
|
+
...args,
|
42
|
+
annotations: {
|
43
|
+
"cert-manager.io/cluster-issuer": args.accessPoint.tlsIssuer.clusterIssuerName,
|
44
|
+
},
|
45
|
+
gateway: args.accessPoint.gateway,
|
46
|
+
})
|
47
|
+
|
48
|
+
const dnsRecords = normalize(args.fqdn, args.fqdns).map(fqdn => {
|
49
|
+
return DnsRecord.create(fqdn, {
|
50
|
+
provider: args.accessPoint.dnsProvider,
|
51
|
+
type: "A",
|
52
|
+
value: args.accessPoint.gateway.ip,
|
53
|
+
})
|
54
|
+
})
|
55
|
+
|
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
|
+
cni: args.accessPoint.gateway.service.clusterInfo.cni,
|
66
|
+
namespace: args.namespace,
|
67
|
+
|
68
|
+
description: `Allow ingress traffic from the gateway "${displayName}".`,
|
69
|
+
|
70
|
+
ingressRule: {
|
71
|
+
fromNamespace: args.accessPoint.gateway.service.metadata.namespace,
|
72
|
+
fromSelector: args.accessPoint.gateway.service.spec.selector,
|
73
|
+
},
|
74
|
+
},
|
75
|
+
{ provider: args.provider },
|
76
|
+
),
|
77
|
+
|
78
|
+
NetworkPolicy.create(
|
79
|
+
`allow-egress-to-${namespaceName}`,
|
80
|
+
{
|
81
|
+
cni: args.accessPoint.gateway.service.clusterInfo.cni,
|
82
|
+
namespace: args.accessPoint.gateway.service.metadata.namespace,
|
83
|
+
selector: args.accessPoint.gateway.service.spec.selector,
|
84
|
+
|
85
|
+
description: `Allow egress traffic to the namespace "${namespaceName}".`,
|
86
|
+
|
87
|
+
egressRule: {
|
88
|
+
toNamespace: args.namespace,
|
89
|
+
},
|
90
|
+
},
|
91
|
+
{ provider: args.provider },
|
92
|
+
),
|
93
|
+
)
|
94
|
+
}
|
95
|
+
|
96
|
+
return output({
|
97
|
+
gateway,
|
98
|
+
dnsRecords,
|
99
|
+
networkPolicies,
|
100
|
+
})
|
101
|
+
},
|
102
|
+
)
|
103
|
+
|
104
|
+
return toPromise(result)
|
105
|
+
}
|
106
|
+
|
107
|
+
export type StandardAccessPointArgs = {
|
108
|
+
fqdn: string
|
109
|
+
}
|
110
|
+
|
111
|
+
export type StandardAccessPointInputs = {
|
112
|
+
accessPoint: Output<k8s.AccessPoint>
|
113
|
+
k8sCluster: Output<k8s.Cluster>
|
114
|
+
}
|
115
|
+
|
116
|
+
export function useStandardAcessPoint(
|
117
|
+
appName: string,
|
118
|
+
namespace: core.v1.Namespace,
|
119
|
+
args: StandardAccessPointArgs,
|
120
|
+
inputs: StandardAccessPointInputs,
|
121
|
+
provider: Provider,
|
122
|
+
): Promise<UseAccessPointResult> {
|
123
|
+
return useAccessPoint({
|
124
|
+
name: appName,
|
125
|
+
namespace,
|
126
|
+
|
127
|
+
fqdn: args.fqdn,
|
128
|
+
|
129
|
+
accessPoint: inputs.accessPoint,
|
130
|
+
clusterInfo: inputs.k8sCluster.info,
|
131
|
+
provider,
|
132
|
+
})
|
133
|
+
}
|
134
|
+
|
135
|
+
export type CreateGatewayArgs = {
|
136
|
+
name: string
|
137
|
+
namespace: Input<core.v1.Namespace>
|
138
|
+
annotations?: Input<Record<string, string>>
|
139
|
+
|
140
|
+
fqdn?: Input<string>
|
141
|
+
fqdns?: InputArray<string>
|
142
|
+
|
143
|
+
gateway: Input<k8s.Gateway>
|
144
|
+
clusterInfo: Input<k8s.ClusterInfo>
|
145
|
+
provider: Provider
|
146
|
+
}
|
147
|
+
|
148
|
+
export function createGateway(args: CreateGatewayArgs): Output<gateway.v1.Gateway> {
|
149
|
+
return output(args).apply(args => {
|
150
|
+
if (args.clusterInfo.id !== args.gateway.clusterInfo.id) {
|
151
|
+
throw new Error(
|
152
|
+
"The provided Kubernetes cluster is different from the one where the gateway controller is deployed.",
|
153
|
+
)
|
154
|
+
}
|
155
|
+
|
156
|
+
return new gateway.v1.Gateway(
|
157
|
+
args.name,
|
158
|
+
{
|
159
|
+
metadata: {
|
160
|
+
name: args.name,
|
161
|
+
namespace: mapNamespaceLikeToNamespaceName(args.namespace),
|
162
|
+
annotations: args.annotations,
|
163
|
+
},
|
164
|
+
spec: {
|
165
|
+
gatewayClassName: output(args.gateway).gatewayClassName,
|
166
|
+
listeners: normalize(args.fqdn, args.fqdns).map(fqdn => {
|
167
|
+
const normalizedName = fqdn.replace(/\*/g, "wildcard")
|
168
|
+
|
169
|
+
return {
|
170
|
+
name: `https-${normalizedName}`,
|
171
|
+
port: output(args.gateway).httpsListenerPort,
|
172
|
+
protocol: "HTTPS",
|
173
|
+
hostname: fqdn,
|
174
|
+
tls: {
|
175
|
+
mode: "Terminate",
|
176
|
+
certificateRefs: [{ name: normalizedName }],
|
177
|
+
},
|
178
|
+
}
|
179
|
+
}),
|
180
|
+
},
|
181
|
+
},
|
182
|
+
{ provider: args.provider, deletedWith: args.namespace },
|
183
|
+
)
|
184
|
+
})
|
185
|
+
}
|
package/src/container.ts
ADDED
@@ -0,0 +1,271 @@
|
|
1
|
+
import type { PartialKeys } from "@highstate/contract"
|
2
|
+
import { core, type types } from "@pulumi/kubernetes"
|
3
|
+
import { normalize, output, type Input, type InputArray, type Unwrap } from "@highstate/pulumi"
|
4
|
+
import { concat, map, omit } from "remeda"
|
5
|
+
import { PersistentVolumeClaim } from "./pvc"
|
6
|
+
|
7
|
+
export type Container = Omit<PartialKeys<types.input.core.v1.Container, "name">, "volumeMounts"> & {
|
8
|
+
/**
|
9
|
+
* The single port to add to the container.
|
10
|
+
*/
|
11
|
+
port?: Input<types.input.core.v1.ContainerPort>
|
12
|
+
|
13
|
+
/**
|
14
|
+
* The volume mount to attach to the container.
|
15
|
+
*/
|
16
|
+
volumeMount?: Input<ContainerVolumeMount>
|
17
|
+
|
18
|
+
/**
|
19
|
+
* The volume mounts to attach to the container.
|
20
|
+
*/
|
21
|
+
volumeMounts?: InputArray<ContainerVolumeMount>
|
22
|
+
|
23
|
+
/**
|
24
|
+
* The volume to include in the parent workload.
|
25
|
+
* It is like the `volumes` property, but defined at the container level.
|
26
|
+
* It will be defined as a volume mount in the parent workload automatically.
|
27
|
+
*/
|
28
|
+
volume?: Input<WorkloadVolume>
|
29
|
+
|
30
|
+
/**
|
31
|
+
* The volumes to include in the parent workload.
|
32
|
+
* It is like the `volumes` property, but defined at the container level.
|
33
|
+
* It will be defined as a volume mount in the parent workload automatically.
|
34
|
+
*/
|
35
|
+
volumes?: InputArray<WorkloadVolume>
|
36
|
+
|
37
|
+
/**
|
38
|
+
* The map of environment variables to set in the container.
|
39
|
+
* It is like the `env` property, but more convenient to use.
|
40
|
+
*/
|
41
|
+
environment?: Input<ContainerEnvironment>
|
42
|
+
|
43
|
+
/**
|
44
|
+
* The source of environment variables to set in the container.
|
45
|
+
* It is like the `envFrom` property, but more convenient to use.
|
46
|
+
*/
|
47
|
+
environmentSource?: Input<ContainerEnvironmentSource>
|
48
|
+
|
49
|
+
/**
|
50
|
+
* The sources of environment variables to set in the container.
|
51
|
+
* It is like the `envFrom` property, but more convenient to use.
|
52
|
+
*/
|
53
|
+
environmentSources?: InputArray<ContainerEnvironmentSource>
|
54
|
+
}
|
55
|
+
|
56
|
+
const containerExtraArgs = [
|
57
|
+
"port",
|
58
|
+
"volumeMount",
|
59
|
+
"volume",
|
60
|
+
"environment",
|
61
|
+
"environmentSource",
|
62
|
+
"environmentSources",
|
63
|
+
] as const
|
64
|
+
|
65
|
+
export type ContainerEnvironment = Record<
|
66
|
+
string,
|
67
|
+
Input<string | undefined | null | ContainerEnvironmentVariable>
|
68
|
+
>
|
69
|
+
|
70
|
+
export type ContainerEnvironmentVariable =
|
71
|
+
| types.input.core.v1.EnvVarSource
|
72
|
+
| {
|
73
|
+
/**
|
74
|
+
* The secret to select from.
|
75
|
+
*/
|
76
|
+
secret: Input<core.v1.Secret>
|
77
|
+
|
78
|
+
/**
|
79
|
+
* The key of the secret to select from.
|
80
|
+
*/
|
81
|
+
key: string
|
82
|
+
}
|
83
|
+
| {
|
84
|
+
/**
|
85
|
+
* The config map to select from.
|
86
|
+
*/
|
87
|
+
configMap: Input<core.v1.ConfigMap>
|
88
|
+
|
89
|
+
/**
|
90
|
+
* The key of the config map to select from.
|
91
|
+
*/
|
92
|
+
key: string
|
93
|
+
}
|
94
|
+
|
95
|
+
export type ContainerEnvironmentSource =
|
96
|
+
| types.input.core.v1.EnvFromSource
|
97
|
+
| core.v1.ConfigMap
|
98
|
+
| core.v1.Secret
|
99
|
+
|
100
|
+
export type ContainerVolumeMount =
|
101
|
+
| types.input.core.v1.VolumeMount
|
102
|
+
| (Omit<types.input.core.v1.VolumeMount, "name"> & {
|
103
|
+
/**
|
104
|
+
* The volume to mount.
|
105
|
+
*/
|
106
|
+
volume: Input<WorkloadVolume>
|
107
|
+
})
|
108
|
+
|
109
|
+
export type WorkloadVolume =
|
110
|
+
| types.input.core.v1.Volume
|
111
|
+
| core.v1.PersistentVolumeClaim
|
112
|
+
| PersistentVolumeClaim
|
113
|
+
| core.v1.ConfigMap
|
114
|
+
| core.v1.Secret
|
115
|
+
|
116
|
+
export function mapContainerToRaw(
|
117
|
+
container: Unwrap<Container>,
|
118
|
+
fallbackName: string,
|
119
|
+
): types.input.core.v1.Container {
|
120
|
+
const containerName = container.name ?? fallbackName
|
121
|
+
|
122
|
+
return {
|
123
|
+
...omit(container, containerExtraArgs),
|
124
|
+
|
125
|
+
name: containerName,
|
126
|
+
ports: normalize(container.port, container.ports),
|
127
|
+
|
128
|
+
volumeMounts: map(normalize(container.volumeMount, container.volumeMounts), mapVolumeMount),
|
129
|
+
|
130
|
+
env: concat(
|
131
|
+
container.environment ? mapContainerEnvironment(container.environment) : [],
|
132
|
+
container.env ?? [],
|
133
|
+
),
|
134
|
+
|
135
|
+
envFrom: concat(
|
136
|
+
map(
|
137
|
+
normalize(container.environmentSource, container.environmentSources),
|
138
|
+
mapEnvironmentSource,
|
139
|
+
),
|
140
|
+
container.envFrom ?? [],
|
141
|
+
),
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
145
|
+
export function mapContainerEnvironment(
|
146
|
+
environment: Unwrap<ContainerEnvironment>,
|
147
|
+
): types.input.core.v1.EnvVar[] {
|
148
|
+
const envVars: types.input.core.v1.EnvVar[] = []
|
149
|
+
|
150
|
+
for (const [name, value] of Object.entries(environment)) {
|
151
|
+
if (!value) {
|
152
|
+
continue
|
153
|
+
}
|
154
|
+
|
155
|
+
if (typeof value === "string") {
|
156
|
+
envVars.push({ name, value })
|
157
|
+
continue
|
158
|
+
}
|
159
|
+
|
160
|
+
if ("secret" in value) {
|
161
|
+
envVars.push({
|
162
|
+
name,
|
163
|
+
valueFrom: {
|
164
|
+
secretKeyRef: {
|
165
|
+
name: value.secret.metadata.name,
|
166
|
+
key: value.key,
|
167
|
+
},
|
168
|
+
},
|
169
|
+
})
|
170
|
+
continue
|
171
|
+
}
|
172
|
+
|
173
|
+
if ("configMap" in value) {
|
174
|
+
envVars.push({
|
175
|
+
name,
|
176
|
+
valueFrom: {
|
177
|
+
configMapKeyRef: {
|
178
|
+
name: value.configMap.metadata.name,
|
179
|
+
key: value.key,
|
180
|
+
},
|
181
|
+
},
|
182
|
+
})
|
183
|
+
continue
|
184
|
+
}
|
185
|
+
|
186
|
+
envVars.push({ name, valueFrom: value })
|
187
|
+
}
|
188
|
+
|
189
|
+
return envVars
|
190
|
+
}
|
191
|
+
|
192
|
+
export function mapVolumeMount(volumeMount: ContainerVolumeMount): types.input.core.v1.VolumeMount {
|
193
|
+
if ("volume" in volumeMount) {
|
194
|
+
return omit(
|
195
|
+
{
|
196
|
+
...volumeMount,
|
197
|
+
name: output(volumeMount.volume)
|
198
|
+
.apply(mapWorkloadVolume)
|
199
|
+
.apply(volume => output(volume.name)),
|
200
|
+
},
|
201
|
+
["volume"],
|
202
|
+
)
|
203
|
+
}
|
204
|
+
|
205
|
+
return {
|
206
|
+
...volumeMount,
|
207
|
+
name: volumeMount.name,
|
208
|
+
}
|
209
|
+
}
|
210
|
+
|
211
|
+
export function mapEnvironmentSource(
|
212
|
+
envFrom: ContainerEnvironmentSource,
|
213
|
+
): types.input.core.v1.EnvFromSource {
|
214
|
+
if (envFrom instanceof core.v1.ConfigMap) {
|
215
|
+
return {
|
216
|
+
configMapRef: {
|
217
|
+
name: envFrom.metadata.name,
|
218
|
+
},
|
219
|
+
}
|
220
|
+
}
|
221
|
+
|
222
|
+
if (envFrom instanceof core.v1.Secret) {
|
223
|
+
return {
|
224
|
+
secretRef: {
|
225
|
+
name: envFrom.metadata.name,
|
226
|
+
},
|
227
|
+
}
|
228
|
+
}
|
229
|
+
|
230
|
+
return envFrom
|
231
|
+
}
|
232
|
+
|
233
|
+
export function mapWorkloadVolume(volume: WorkloadVolume) {
|
234
|
+
if (volume instanceof PersistentVolumeClaim) {
|
235
|
+
return {
|
236
|
+
name: volume.metadata.name,
|
237
|
+
persistentVolumeClaim: {
|
238
|
+
claimName: volume.metadata.name,
|
239
|
+
},
|
240
|
+
}
|
241
|
+
}
|
242
|
+
|
243
|
+
if (volume instanceof core.v1.PersistentVolumeClaim) {
|
244
|
+
return {
|
245
|
+
name: volume.metadata.name,
|
246
|
+
persistentVolumeClaim: {
|
247
|
+
claimName: volume.metadata.name,
|
248
|
+
},
|
249
|
+
}
|
250
|
+
}
|
251
|
+
|
252
|
+
if (volume instanceof core.v1.ConfigMap) {
|
253
|
+
return {
|
254
|
+
name: volume.metadata.name,
|
255
|
+
configMap: {
|
256
|
+
name: volume.metadata.name,
|
257
|
+
},
|
258
|
+
}
|
259
|
+
}
|
260
|
+
|
261
|
+
if (volume instanceof core.v1.Secret) {
|
262
|
+
return {
|
263
|
+
name: volume.metadata.name,
|
264
|
+
secret: {
|
265
|
+
secretName: volume.metadata.name,
|
266
|
+
},
|
267
|
+
}
|
268
|
+
}
|
269
|
+
|
270
|
+
return volume
|
271
|
+
}
|
package/src/cron-job.ts
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
import type { RequiredKeys } from "@highstate/contract"
|
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"
|
12
|
+
import { mergeDeep, omit } from "remeda"
|
13
|
+
import { mapContainerToRaw, mapWorkloadVolume, type Container } from "./container"
|
14
|
+
import { commonExtraArgs, mapMetadata, type CommonArgs } from "./shared"
|
15
|
+
|
16
|
+
export type CronJobArgs = CommonArgs & {
|
17
|
+
container?: Input<Container>
|
18
|
+
containers?: InputArray<Container>
|
19
|
+
} & Omit<RequiredKeys<Partial<types.input.batch.v1.CronJobSpec>, "schedule">, "jobTemplate"> & {
|
20
|
+
jobTemplate?: {
|
21
|
+
metadata?: types.input.meta.v1.ObjectMeta
|
22
|
+
spec?: Omit<types.input.batch.v1.JobSpec, "template"> & {
|
23
|
+
template?: {
|
24
|
+
metadata?: types.input.meta.v1.ObjectMeta
|
25
|
+
spec?: Partial<types.input.core.v1.PodSpec>
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
const cronJobExtraArgs = [...commonExtraArgs, "container", "containers"] as const
|
32
|
+
|
33
|
+
export class CronJob extends ComponentResource {
|
34
|
+
/**
|
35
|
+
* The underlying Kubernetes job.
|
36
|
+
*/
|
37
|
+
public readonly cronJob: Output<batch.v1.CronJob>
|
38
|
+
|
39
|
+
constructor(name: string, args: CronJobArgs, opts?: ComponentResourceOptions) {
|
40
|
+
super("highstate:k8s:CronJob", name, args, opts)
|
41
|
+
|
42
|
+
this.cronJob = output(args).apply(args => {
|
43
|
+
const containers = normalize(args.container, args.containers)
|
44
|
+
|
45
|
+
return new batch.v1.CronJob(
|
46
|
+
name,
|
47
|
+
{
|
48
|
+
metadata: mapMetadata(args, name),
|
49
|
+
|
50
|
+
spec: mergeDeep(
|
51
|
+
{
|
52
|
+
jobTemplate: {
|
53
|
+
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
|
+
},
|
63
|
+
},
|
64
|
+
},
|
65
|
+
|
66
|
+
schedule: args.schedule,
|
67
|
+
} satisfies types.input.batch.v1.CronJobSpec,
|
68
|
+
omit(args, cronJobExtraArgs) as types.input.batch.v1.CronJobSpec,
|
69
|
+
),
|
70
|
+
},
|
71
|
+
{ parent: this, ...opts },
|
72
|
+
)
|
73
|
+
})
|
74
|
+
|
75
|
+
this.registerOutputs({ cronJob: this.cronJob })
|
76
|
+
}
|
77
|
+
}
|