@highstate/k8s 0.4.5
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/index.d.ts +676 -0
- package/dist/index.js +950 -0
- package/dist/shared-hajqPzR4.js +64 -0
- package/dist/units/access-point/index.js +13 -0
- package/dist/units/cert-manager/index.js +34 -0
- package/dist/units/dns01-issuer/index.js +51 -0
- package/dist/units/existing-cluster/index.js +38 -0
- package/package.json +47 -0
package/dist/index.js
ADDED
@@ -0,0 +1,950 @@
|
|
1
|
+
import { m as mapMetadata, c as commonExtraArgs, a as mapSelectorLikeToSelector, b as mapNamespaceNameToSelector, d as mapNamespaceLikeToNamespaceName } from './shared-hajqPzR4.js';
|
2
|
+
export { f as createNamespace, e as createProvider, r as resolveChartPath } from './shared-hajqPzR4.js';
|
3
|
+
import { ComponentResource, output, normalize, interpolate, all, toPromise, apply } from '@highstate/pulumi';
|
4
|
+
import { core, apps, networking, batch } from '@pulumi/kubernetes';
|
5
|
+
import { gateway } from '@highstate/gateway-api';
|
6
|
+
import { mergeDeep, omit, pipe, map, concat, capitalize, flat, merge } from 'remeda';
|
7
|
+
import { DnsRecord } from '@highstate/common';
|
8
|
+
import { ComponentResource as ComponentResource$1, output as output$1 } from '@pulumi/pulumi';
|
9
|
+
import { deepmerge } from 'deepmerge-ts';
|
10
|
+
import { trimIndentation, text } from '@highstate/contract';
|
11
|
+
import 'node:fs';
|
12
|
+
import 'node:path';
|
13
|
+
|
14
|
+
const serviceExtraArgs = [...commonExtraArgs, "port", "ports"];
|
15
|
+
class Service extends ComponentResource {
|
16
|
+
/**
|
17
|
+
* The underlying Kubernetes service.
|
18
|
+
*/
|
19
|
+
service;
|
20
|
+
constructor(name, args, opts) {
|
21
|
+
super("highstate:k8s:Service", name, args, opts);
|
22
|
+
this.service = output(args).apply((args2) => {
|
23
|
+
return new core.v1.Service(
|
24
|
+
name,
|
25
|
+
{
|
26
|
+
metadata: mapMetadata(args2, name),
|
27
|
+
spec: mergeDeep(
|
28
|
+
{
|
29
|
+
ports: normalize(args2.port, args2.ports)
|
30
|
+
},
|
31
|
+
omit(args2, serviceExtraArgs)
|
32
|
+
)
|
33
|
+
},
|
34
|
+
{ ...opts, parent: this }
|
35
|
+
);
|
36
|
+
});
|
37
|
+
this.registerOutputs({
|
38
|
+
service: this.service
|
39
|
+
});
|
40
|
+
}
|
41
|
+
}
|
42
|
+
function mapContainerPortToServicePort(port) {
|
43
|
+
return {
|
44
|
+
port: port.containerPort,
|
45
|
+
targetPort: port.containerPort,
|
46
|
+
protocol: port.protocol
|
47
|
+
};
|
48
|
+
}
|
49
|
+
function mapServiceToLabelSelector(service) {
|
50
|
+
return {
|
51
|
+
matchLabels: service.spec.selector
|
52
|
+
};
|
53
|
+
}
|
54
|
+
function getServiceHost(service) {
|
55
|
+
return interpolate`${service.metadata.name}.${service.metadata.namespace}.svc`;
|
56
|
+
}
|
57
|
+
function getRequiredServicePortByName(service, name) {
|
58
|
+
return service.spec.ports.apply((ports) => {
|
59
|
+
const port = ports.find((p) => p.name === name);
|
60
|
+
if (!port) {
|
61
|
+
throw new Error(`Service does not have a port with name ${name}`);
|
62
|
+
}
|
63
|
+
return port.port;
|
64
|
+
});
|
65
|
+
}
|
66
|
+
|
67
|
+
function resolveBackendRef(ref) {
|
68
|
+
if (core.v1.Service.isInstance(ref)) {
|
69
|
+
return output({
|
70
|
+
name: ref.metadata.name,
|
71
|
+
namespace: ref.metadata.namespace,
|
72
|
+
port: ref.spec.ports[0].port
|
73
|
+
});
|
74
|
+
}
|
75
|
+
if ("service" in ref) {
|
76
|
+
const service = output(ref.service);
|
77
|
+
return output({
|
78
|
+
name: service.metadata.name,
|
79
|
+
namespace: service.metadata.namespace,
|
80
|
+
port: all([ref.service, ref.port]).apply(([service2, port]) => {
|
81
|
+
return typeof port === "number" ? output(port) : getRequiredServicePortByName(service2, port);
|
82
|
+
})
|
83
|
+
});
|
84
|
+
}
|
85
|
+
return output({
|
86
|
+
name: ref.name,
|
87
|
+
namespace: ref.namespace,
|
88
|
+
port: ref.port
|
89
|
+
});
|
90
|
+
}
|
91
|
+
|
92
|
+
class HttpRoute extends ComponentResource {
|
93
|
+
/**
|
94
|
+
* The underlying Kubernetes resource.
|
95
|
+
*/
|
96
|
+
route;
|
97
|
+
constructor(name, args, opts) {
|
98
|
+
super("highstate:k8s:HttpRoute", name, args, opts);
|
99
|
+
this.route = output({
|
100
|
+
args,
|
101
|
+
gatewayNamespace: output(args.gateway).metadata.namespace
|
102
|
+
}).apply(({ args: args2, gatewayNamespace }) => {
|
103
|
+
return new gateway.v1.HTTPRoute(
|
104
|
+
name,
|
105
|
+
{
|
106
|
+
metadata: mapMetadata(
|
107
|
+
{
|
108
|
+
...args2,
|
109
|
+
namespace: gatewayNamespace
|
110
|
+
},
|
111
|
+
name
|
112
|
+
),
|
113
|
+
spec: {
|
114
|
+
hostnames: normalize(args2.hostname, args2.hostnames),
|
115
|
+
parentRefs: [
|
116
|
+
{
|
117
|
+
name: args2.gateway.metadata.name
|
118
|
+
}
|
119
|
+
],
|
120
|
+
rules: normalize(args2.rule, args2.rules).map((rule) => ({
|
121
|
+
sessionPersistence: rule.sessionPersistence,
|
122
|
+
timeouts: rule.timeouts,
|
123
|
+
matches: pipe(
|
124
|
+
normalize(rule.match, rule.matches),
|
125
|
+
map(mapHttpRouteRuleMatch),
|
126
|
+
addDefaultPathMatch
|
127
|
+
),
|
128
|
+
filters: normalize(rule.filter, rule.filters),
|
129
|
+
backendRefs: rule.backend ? [resolveBackendRef(rule.backend)] : void 0
|
130
|
+
}))
|
131
|
+
}
|
132
|
+
},
|
133
|
+
{ parent: this, ...opts }
|
134
|
+
);
|
135
|
+
});
|
136
|
+
this.registerOutputs({ route: this.route });
|
137
|
+
}
|
138
|
+
}
|
139
|
+
function addDefaultPathMatch(matches) {
|
140
|
+
return matches.length ? matches : [{ path: { type: "PathPrefix", value: "/" } }];
|
141
|
+
}
|
142
|
+
function mapHttpRouteRuleMatch(match) {
|
143
|
+
if (typeof match === "string") {
|
144
|
+
return { path: { type: "PathPrefix", value: match } };
|
145
|
+
}
|
146
|
+
return match;
|
147
|
+
}
|
148
|
+
|
149
|
+
const containerExtraArgs = [
|
150
|
+
"port",
|
151
|
+
"volumeMount",
|
152
|
+
"volume",
|
153
|
+
"environment",
|
154
|
+
"environmentSource",
|
155
|
+
"environmentSources"
|
156
|
+
];
|
157
|
+
function mapContainerToRaw(fallbackName, container) {
|
158
|
+
return {
|
159
|
+
...omit(container, containerExtraArgs),
|
160
|
+
name: container.name ?? fallbackName,
|
161
|
+
ports: normalize(container.port, container.ports),
|
162
|
+
volumeMounts: map(normalize(container.volumeMount, container.volumeMounts), mapVolumeMount),
|
163
|
+
env: concat(
|
164
|
+
container.environment ? mapContainerEnvironment(container.environment) : [],
|
165
|
+
container.env ?? []
|
166
|
+
),
|
167
|
+
envFrom: concat(
|
168
|
+
map(
|
169
|
+
normalize(container.environmentSource, container.environmentSources),
|
170
|
+
mapEnvironmentSource
|
171
|
+
),
|
172
|
+
container.envFrom ?? []
|
173
|
+
)
|
174
|
+
};
|
175
|
+
}
|
176
|
+
function mapContainerEnvironment(environment) {
|
177
|
+
const envVars = [];
|
178
|
+
for (const [name, value] of Object.entries(environment)) {
|
179
|
+
if (!value) {
|
180
|
+
continue;
|
181
|
+
}
|
182
|
+
if (typeof value === "string") {
|
183
|
+
envVars.push({ name, value });
|
184
|
+
continue;
|
185
|
+
}
|
186
|
+
if ("secret" in value) {
|
187
|
+
envVars.push({
|
188
|
+
name,
|
189
|
+
valueFrom: {
|
190
|
+
secretKeyRef: {
|
191
|
+
name: value.secret.metadata.name,
|
192
|
+
key: value.key
|
193
|
+
}
|
194
|
+
}
|
195
|
+
});
|
196
|
+
continue;
|
197
|
+
}
|
198
|
+
if ("configMap" in value) {
|
199
|
+
envVars.push({
|
200
|
+
name,
|
201
|
+
valueFrom: {
|
202
|
+
configMapKeyRef: {
|
203
|
+
name: value.configMap.metadata.name,
|
204
|
+
key: value.key
|
205
|
+
}
|
206
|
+
}
|
207
|
+
});
|
208
|
+
continue;
|
209
|
+
}
|
210
|
+
envVars.push({ name, valueFrom: value });
|
211
|
+
}
|
212
|
+
return envVars;
|
213
|
+
}
|
214
|
+
function mapVolumeMount(volumeMount) {
|
215
|
+
if ("volume" in volumeMount) {
|
216
|
+
return {
|
217
|
+
...volumeMount,
|
218
|
+
name: output(volumeMount.volume).apply(mapWorkloadVolume).apply((volume) => output(volume.name))
|
219
|
+
};
|
220
|
+
}
|
221
|
+
return volumeMount;
|
222
|
+
}
|
223
|
+
function mapEnvironmentSource(envFrom) {
|
224
|
+
if (envFrom instanceof core.v1.ConfigMap) {
|
225
|
+
return {
|
226
|
+
configMapRef: {
|
227
|
+
name: envFrom.metadata.name
|
228
|
+
}
|
229
|
+
};
|
230
|
+
}
|
231
|
+
if (envFrom instanceof core.v1.Secret) {
|
232
|
+
return {
|
233
|
+
secretRef: {
|
234
|
+
name: envFrom.metadata.name
|
235
|
+
}
|
236
|
+
};
|
237
|
+
}
|
238
|
+
return envFrom;
|
239
|
+
}
|
240
|
+
function mapWorkloadVolume(volume) {
|
241
|
+
if (volume instanceof core.v1.PersistentVolumeClaim) {
|
242
|
+
return {
|
243
|
+
name: volume.metadata.name,
|
244
|
+
persistentVolumeClaim: {
|
245
|
+
claimName: volume.metadata.name
|
246
|
+
}
|
247
|
+
};
|
248
|
+
}
|
249
|
+
if (volume instanceof core.v1.ConfigMap) {
|
250
|
+
return {
|
251
|
+
name: volume.metadata.name,
|
252
|
+
configMap: {
|
253
|
+
name: volume.metadata.name
|
254
|
+
}
|
255
|
+
};
|
256
|
+
}
|
257
|
+
if (volume instanceof core.v1.Secret) {
|
258
|
+
return {
|
259
|
+
name: volume.metadata.name,
|
260
|
+
secret: {
|
261
|
+
secretName: volume.metadata.name
|
262
|
+
}
|
263
|
+
};
|
264
|
+
}
|
265
|
+
return volume;
|
266
|
+
}
|
267
|
+
|
268
|
+
const deploymentExtraArgs = [
|
269
|
+
...commonExtraArgs,
|
270
|
+
"container",
|
271
|
+
"containers",
|
272
|
+
"gateway",
|
273
|
+
"service",
|
274
|
+
"httpRoute"
|
275
|
+
];
|
276
|
+
class Deployment extends ComponentResource {
|
277
|
+
/**
|
278
|
+
* The underlying Kubernetes deployment.
|
279
|
+
*/
|
280
|
+
deployment;
|
281
|
+
/**
|
282
|
+
* The service associated with the deployment.
|
283
|
+
*/
|
284
|
+
service;
|
285
|
+
/**
|
286
|
+
* The HTTP route associated with the deployment.
|
287
|
+
*/
|
288
|
+
httpRoute;
|
289
|
+
constructor(name, args, opts) {
|
290
|
+
super("highstate:k8s:Deployment", name, args, opts);
|
291
|
+
const labels = {
|
292
|
+
"app.kubernetes.io/name": name
|
293
|
+
};
|
294
|
+
this.deployment = output(args).apply((args2) => {
|
295
|
+
const containers = normalize(args2.container, args2.containers);
|
296
|
+
return new apps.v1.Deployment(
|
297
|
+
name,
|
298
|
+
{
|
299
|
+
metadata: mapMetadata(args2, name),
|
300
|
+
spec: mergeDeep(
|
301
|
+
{
|
302
|
+
template: {
|
303
|
+
metadata: {
|
304
|
+
labels
|
305
|
+
},
|
306
|
+
spec: {
|
307
|
+
containers: containers.map((container) => mapContainerToRaw(name, container)),
|
308
|
+
volumes: containers.flatMap((container) => normalize(container.volume, container.volumes)).map(mapWorkloadVolume)
|
309
|
+
}
|
310
|
+
},
|
311
|
+
selector: {
|
312
|
+
matchLabels: labels
|
313
|
+
}
|
314
|
+
},
|
315
|
+
omit(args2, deploymentExtraArgs)
|
316
|
+
)
|
317
|
+
},
|
318
|
+
{
|
319
|
+
...opts,
|
320
|
+
parent: this
|
321
|
+
}
|
322
|
+
);
|
323
|
+
});
|
324
|
+
this.service = output({
|
325
|
+
args: args.service,
|
326
|
+
httpRouteArgs: args.httpRoute,
|
327
|
+
namespace: this.deployment.metadata.namespace,
|
328
|
+
containers: this.deployment.spec.template.spec.containers
|
329
|
+
}).apply(({ args: args2, httpRouteArgs, namespace, containers }) => {
|
330
|
+
if (!args2 && !httpRouteArgs) {
|
331
|
+
return void 0;
|
332
|
+
}
|
333
|
+
const ports = containers.flatMap((container) => container.ports ?? []);
|
334
|
+
return new Service(
|
335
|
+
args2?.name ?? name,
|
336
|
+
{
|
337
|
+
...args2,
|
338
|
+
namespace,
|
339
|
+
ports: (
|
340
|
+
// allow to completely override the ports
|
341
|
+
!args2?.port && !args2?.ports ? ports.map(mapContainerPortToServicePort) : args2?.ports
|
342
|
+
),
|
343
|
+
selector: labels
|
344
|
+
},
|
345
|
+
{
|
346
|
+
...opts,
|
347
|
+
parent: this
|
348
|
+
}
|
349
|
+
);
|
350
|
+
});
|
351
|
+
this.httpRoute = output({
|
352
|
+
args: args.httpRoute,
|
353
|
+
service: this.service
|
354
|
+
}).apply(({ args: args2, service }) => {
|
355
|
+
if (!args2 || !service) {
|
356
|
+
return void 0;
|
357
|
+
}
|
358
|
+
return new HttpRoute(
|
359
|
+
name,
|
360
|
+
{
|
361
|
+
...args2,
|
362
|
+
gateway: args2.gateway,
|
363
|
+
rule: {
|
364
|
+
backend: service.service
|
365
|
+
}
|
366
|
+
},
|
367
|
+
{
|
368
|
+
...opts,
|
369
|
+
parent: this
|
370
|
+
}
|
371
|
+
);
|
372
|
+
});
|
373
|
+
this.registerOutputs({
|
374
|
+
deployment: this.deployment,
|
375
|
+
service: this.service
|
376
|
+
});
|
377
|
+
}
|
378
|
+
getRequiredService() {
|
379
|
+
return this.service.apply((service) => {
|
380
|
+
if (!service) {
|
381
|
+
throw new Error("The service is not available for this deployment.");
|
382
|
+
}
|
383
|
+
return service;
|
384
|
+
});
|
385
|
+
}
|
386
|
+
}
|
387
|
+
|
388
|
+
function mapPersistentVolumeClaimToRaw(args) {
|
389
|
+
return {
|
390
|
+
metadata: mapMetadata(args),
|
391
|
+
spec: args
|
392
|
+
};
|
393
|
+
}
|
394
|
+
|
395
|
+
class StatefulSet extends ComponentResource {
|
396
|
+
/**
|
397
|
+
* The underlying Kubernetes stateful set.
|
398
|
+
*/
|
399
|
+
statefulSet;
|
400
|
+
/**
|
401
|
+
* The service associated with the stateful set.
|
402
|
+
*/
|
403
|
+
service;
|
404
|
+
constructor(name, args, opts) {
|
405
|
+
super("highstate:k8s:StatefulSet", name, args, opts);
|
406
|
+
const labels = {
|
407
|
+
"app.kubernetes.io/name": name
|
408
|
+
};
|
409
|
+
this.service = output(args.service).apply((service) => {
|
410
|
+
return new Service(
|
411
|
+
service?.name ?? name,
|
412
|
+
{
|
413
|
+
...service,
|
414
|
+
clusterIP: "None"
|
415
|
+
},
|
416
|
+
{ ...opts, parent: this }
|
417
|
+
);
|
418
|
+
});
|
419
|
+
this.statefulSet = output(args).apply((args2) => {
|
420
|
+
return new apps.v1.StatefulSet(name, {
|
421
|
+
metadata: mapMetadata(args2),
|
422
|
+
spec: mergeDeep(args2, {
|
423
|
+
serviceName: this.service.service.metadata.name,
|
424
|
+
template: {
|
425
|
+
metadata: {
|
426
|
+
labels
|
427
|
+
},
|
428
|
+
spec: {
|
429
|
+
containers: normalize(args2.container, args2.containers).map((container) => mapContainerToRaw(name, container))
|
430
|
+
}
|
431
|
+
},
|
432
|
+
selector: {
|
433
|
+
matchLabels: labels
|
434
|
+
},
|
435
|
+
volumeClaimTemplates: normalize(args2.volumeClaim, args2.volumeClaims).map(mapPersistentVolumeClaimToRaw)
|
436
|
+
})
|
437
|
+
});
|
438
|
+
});
|
439
|
+
this.registerOutputs({
|
440
|
+
statefulSet: this.statefulSet,
|
441
|
+
service: this.service
|
442
|
+
});
|
443
|
+
}
|
444
|
+
}
|
445
|
+
|
446
|
+
class NetworkPolicy extends ComponentResource {
|
447
|
+
/**
|
448
|
+
* The underlying network policy resource.
|
449
|
+
*/
|
450
|
+
networkPolicy;
|
451
|
+
constructor(name, args, opts) {
|
452
|
+
super("k8s:network-policy", name, args, opts);
|
453
|
+
const normalizedArgs = output(args).apply((args2) => {
|
454
|
+
const ingressRules = normalize(args2.ingressRule, args2.ingressRules);
|
455
|
+
const egressRules = normalize(args2.egressRule, args2.egressRules);
|
456
|
+
return {
|
457
|
+
...args2,
|
458
|
+
podSelector: args2.selector ? mapSelectorLikeToSelector(args2.selector) : {},
|
459
|
+
isolateEgress: args2.isolateEgress ?? false,
|
460
|
+
isolateIngress: args2.isolateIngress ?? false,
|
461
|
+
ingressRules: ingressRules.map((rule) => ({
|
462
|
+
all: rule.fromAll ?? false,
|
463
|
+
cidrs: normalize(rule.fromCidr, rule.fromCidrs),
|
464
|
+
fqdns: [],
|
465
|
+
services: normalize(rule.fromService, rule.fromServices),
|
466
|
+
namespaces: normalize(rule.fromNamespace, rule.fromNamespaces),
|
467
|
+
selectors: normalize(rule.fromSelector, rule.fromSelectors),
|
468
|
+
ports: normalize(rule.toPort, rule.toPorts)
|
469
|
+
})),
|
470
|
+
egressRules: egressRules.map((rule) => ({
|
471
|
+
all: rule.toAll ?? false,
|
472
|
+
cidrs: normalize(rule.toCidr, rule.toCidrs),
|
473
|
+
fqdns: normalize(rule.toFqdn, rule.toFqdns),
|
474
|
+
services: normalize(rule.toService, rule.toServices),
|
475
|
+
namespaces: normalize(rule.toNamespace, rule.toNamespaces),
|
476
|
+
selectors: normalize(rule.toSelector, rule.toSelectors),
|
477
|
+
ports: normalize(rule.toPort, rule.toPorts)
|
478
|
+
}))
|
479
|
+
};
|
480
|
+
});
|
481
|
+
this.networkPolicy = normalizedArgs.apply((args2) => {
|
482
|
+
return output(
|
483
|
+
this.create(name, args2, { ...opts, parent: this })
|
484
|
+
);
|
485
|
+
});
|
486
|
+
this.registerOutputs({ networkPolicy: this.networkPolicy });
|
487
|
+
}
|
488
|
+
static create(name, args, opts) {
|
489
|
+
return output(args).apply(async (args2) => {
|
490
|
+
if (!args2.cni || args2.cni === "unknown") {
|
491
|
+
return new NativeNetworkPolicy(name, args2, opts);
|
492
|
+
}
|
493
|
+
const implName = `${capitalize(args2.cni)}NetworkPolicy`;
|
494
|
+
const implModule = await import(`@highstate/${args2.cni}`);
|
495
|
+
const implClass = implModule[implName];
|
496
|
+
if (!implClass) {
|
497
|
+
throw new Error(`No implementation found for ${args2.cni}`);
|
498
|
+
}
|
499
|
+
return new implClass(name, args2, opts);
|
500
|
+
});
|
501
|
+
}
|
502
|
+
}
|
503
|
+
class NativeNetworkPolicy extends NetworkPolicy {
|
504
|
+
create(name, args, opts) {
|
505
|
+
const ingress = NativeNetworkPolicy.createIngressRules(args);
|
506
|
+
const egress = NativeNetworkPolicy.createEgressRules(args);
|
507
|
+
const policyTypes = [];
|
508
|
+
if (ingress.length > 0 || args.isolateIngress) {
|
509
|
+
policyTypes.push("Ingress");
|
510
|
+
}
|
511
|
+
if (egress.length > 0 || args.isolateEgress) {
|
512
|
+
policyTypes.push("Egress");
|
513
|
+
}
|
514
|
+
return new networking.v1.NetworkPolicy(
|
515
|
+
name,
|
516
|
+
{
|
517
|
+
metadata: mergeDeep(mapMetadata(args, name), {
|
518
|
+
annotations: args.description ? { "kubernetes.io/description": args.description } : void 0
|
519
|
+
}),
|
520
|
+
spec: {
|
521
|
+
podSelector: args.podSelector,
|
522
|
+
ingress,
|
523
|
+
egress,
|
524
|
+
policyTypes
|
525
|
+
}
|
526
|
+
},
|
527
|
+
opts
|
528
|
+
);
|
529
|
+
}
|
530
|
+
static fallbackIpBlock = {
|
531
|
+
cidr: "0.0.0.0/0",
|
532
|
+
except: ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
|
533
|
+
};
|
534
|
+
static createIngressRules(args) {
|
535
|
+
return args.ingressRules.map((rule) => ({
|
536
|
+
from: rule.all ? void 0 : NativeNetworkPolicy.createRulePeers(rule),
|
537
|
+
ports: NativeNetworkPolicy.mapPorts(rule.ports)
|
538
|
+
}));
|
539
|
+
}
|
540
|
+
static createEgressRules(args) {
|
541
|
+
const needFallback = args.egressRules.some((rule) => rule.fqdns.length > 0);
|
542
|
+
if (needFallback) {
|
543
|
+
return [{ to: [{ ipBlock: NativeNetworkPolicy.fallbackIpBlock }] }];
|
544
|
+
}
|
545
|
+
return args.egressRules.map((rule) => ({
|
546
|
+
to: rule.all ? void 0 : NativeNetworkPolicy.createRulePeers(rule),
|
547
|
+
ports: NativeNetworkPolicy.mapPorts(rule.ports)
|
548
|
+
}));
|
549
|
+
}
|
550
|
+
static createRulePeers(args) {
|
551
|
+
return [
|
552
|
+
...NativeNetworkPolicy.createCidrPeers(args),
|
553
|
+
...NativeNetworkPolicy.createServicePeers(args),
|
554
|
+
...NativeNetworkPolicy.createSelectorPeers(args)
|
555
|
+
];
|
556
|
+
}
|
557
|
+
static createCidrPeers(args) {
|
558
|
+
return args.cidrs.map((cidr) => ({ ipBlock: { cidr } }));
|
559
|
+
}
|
560
|
+
static createServicePeers(args) {
|
561
|
+
return args.services.map((service) => {
|
562
|
+
const selector = mapServiceToLabelSelector(service);
|
563
|
+
return {
|
564
|
+
namespaceSelector: mapNamespaceNameToSelector(service.metadata.namespace),
|
565
|
+
podSelector: selector
|
566
|
+
};
|
567
|
+
});
|
568
|
+
}
|
569
|
+
static createSelectorPeers(args) {
|
570
|
+
const selectorPeers = args.selectors.map((selector) => ({
|
571
|
+
podSelector: mapSelectorLikeToSelector(selector)
|
572
|
+
}));
|
573
|
+
const namespacePeers = args.namespaces.map(NativeNetworkPolicy.createNamespacePeer);
|
574
|
+
if (namespacePeers.length === 0) {
|
575
|
+
return selectorPeers;
|
576
|
+
}
|
577
|
+
if (selectorPeers.length === 0) {
|
578
|
+
return namespacePeers;
|
579
|
+
}
|
580
|
+
return flat(
|
581
|
+
selectorPeers.map((selectorPeer) => {
|
582
|
+
return namespacePeers.map((namespacePeer) => merge(selectorPeer, namespacePeer));
|
583
|
+
})
|
584
|
+
);
|
585
|
+
}
|
586
|
+
static createNamespacePeer(namespace) {
|
587
|
+
const namespaceName = mapNamespaceLikeToNamespaceName(namespace);
|
588
|
+
const namespaceSelector = mapNamespaceNameToSelector(namespaceName);
|
589
|
+
return { namespaceSelector };
|
590
|
+
}
|
591
|
+
static mapPorts(ports) {
|
592
|
+
return ports.map((port) => {
|
593
|
+
if ("port" in port) {
|
594
|
+
return {
|
595
|
+
port: port.port,
|
596
|
+
protocol: port.protocol ?? "TCP"
|
597
|
+
};
|
598
|
+
}
|
599
|
+
return {
|
600
|
+
port: port.range[0],
|
601
|
+
endPort: port.range[1],
|
602
|
+
protocol: port.protocol ?? "TCP"
|
603
|
+
};
|
604
|
+
});
|
605
|
+
}
|
606
|
+
}
|
607
|
+
|
608
|
+
function useAccessPoint(args) {
|
609
|
+
const result = output(args).apply((args2) => {
|
610
|
+
const gateway2 = createGateway({
|
611
|
+
...args2,
|
612
|
+
annotations: {
|
613
|
+
"cert-manager.io/cluster-issuer": args2.accessPoint.tlsIssuer.clusterIssuerName
|
614
|
+
},
|
615
|
+
gateway: args2.accessPoint.gateway
|
616
|
+
});
|
617
|
+
const dnsRecord = DnsRecord.create(args2.fqdn, {
|
618
|
+
provider: args2.accessPoint.dnsProvider,
|
619
|
+
type: "A",
|
620
|
+
value: args2.accessPoint.gateway.ip
|
621
|
+
});
|
622
|
+
return output({
|
623
|
+
gateway: gateway2,
|
624
|
+
dnsRecord
|
625
|
+
});
|
626
|
+
});
|
627
|
+
return toPromise(result);
|
628
|
+
}
|
629
|
+
function createGateway(args) {
|
630
|
+
return new gateway.v1.Gateway(
|
631
|
+
args.name,
|
632
|
+
{
|
633
|
+
metadata: {
|
634
|
+
name: args.name,
|
635
|
+
namespace: output(args.namespace).apply(mapNamespaceLikeToNamespaceName),
|
636
|
+
annotations: args.annotations
|
637
|
+
},
|
638
|
+
spec: {
|
639
|
+
gatewayClassName: output(args.gateway).gatewayClassName,
|
640
|
+
listeners: [
|
641
|
+
{
|
642
|
+
name: "https",
|
643
|
+
port: output(args.gateway).httpsListenerPort,
|
644
|
+
protocol: "HTTPS",
|
645
|
+
hostname: args.fqdn,
|
646
|
+
tls: {
|
647
|
+
mode: "Terminate",
|
648
|
+
certificateRefs: [{ name: args.fqdn }]
|
649
|
+
}
|
650
|
+
}
|
651
|
+
]
|
652
|
+
}
|
653
|
+
},
|
654
|
+
{ provider: args.provider }
|
655
|
+
);
|
656
|
+
}
|
657
|
+
|
658
|
+
const emptyDistributionEnvironment = {
|
659
|
+
preInstallPackages: [],
|
660
|
+
preInstallScripts: {},
|
661
|
+
packages: []
|
662
|
+
};
|
663
|
+
const emptyScriptEnvironment = {
|
664
|
+
alpine: emptyDistributionEnvironment,
|
665
|
+
ubuntu: emptyDistributionEnvironment,
|
666
|
+
setupScripts: {},
|
667
|
+
cleanupScripts: {},
|
668
|
+
scripts: {},
|
669
|
+
volumes: [],
|
670
|
+
volumeMounts: [],
|
671
|
+
environment: {}
|
672
|
+
};
|
673
|
+
|
674
|
+
class ScriptBundle extends ComponentResource$1 {
|
675
|
+
/**
|
676
|
+
* The config map containing the scripts.
|
677
|
+
*/
|
678
|
+
configMap;
|
679
|
+
/**
|
680
|
+
* The volumes that should be included in the workload.
|
681
|
+
*/
|
682
|
+
volumes;
|
683
|
+
/**
|
684
|
+
* The volume mounts that should be defined in the container.
|
685
|
+
*/
|
686
|
+
volumeMounts;
|
687
|
+
/**
|
688
|
+
* The environment variables that should be defined in the container.
|
689
|
+
*/
|
690
|
+
environment;
|
691
|
+
/**
|
692
|
+
* The distribution to use for the scripts.
|
693
|
+
*/
|
694
|
+
distribution;
|
695
|
+
constructor(name, args, opts) {
|
696
|
+
super("highstate:k8s:ScriptBundle", name, args, opts);
|
697
|
+
const scriptEnvironment = pipe(
|
698
|
+
output$1(args),
|
699
|
+
apply((args2) => normalize(args2.environment, args2.environments)),
|
700
|
+
apply((args2) => deepmerge(emptyScriptEnvironment, ...args2))
|
701
|
+
);
|
702
|
+
this.distribution = args.distribution;
|
703
|
+
this.environment = scriptEnvironment.environment;
|
704
|
+
this.configMap = output$1({ scriptEnvironment, args }).apply(({ scriptEnvironment: scriptEnvironment2, args: args2 }) => {
|
705
|
+
return new core.v1.ConfigMap(
|
706
|
+
name,
|
707
|
+
{
|
708
|
+
metadata: mapMetadata(args2, name),
|
709
|
+
data: createScriptData(this.distribution, scriptEnvironment2)
|
710
|
+
},
|
711
|
+
{ ...opts, parent: this }
|
712
|
+
);
|
713
|
+
});
|
714
|
+
this.volumes = scriptEnvironment.volumes.apply((volumes) => {
|
715
|
+
return [
|
716
|
+
...volumes,
|
717
|
+
{
|
718
|
+
name: this.configMap.metadata.name,
|
719
|
+
configMap: {
|
720
|
+
name: this.configMap.metadata.name,
|
721
|
+
defaultMode: 360
|
722
|
+
// read and execute permissions
|
723
|
+
}
|
724
|
+
}
|
725
|
+
];
|
726
|
+
});
|
727
|
+
this.volumeMounts = scriptEnvironment.volumeMounts.apply((volumeMounts) => {
|
728
|
+
return [
|
729
|
+
...volumeMounts,
|
730
|
+
{
|
731
|
+
volume: this.configMap,
|
732
|
+
mountPath: "/scripts"
|
733
|
+
}
|
734
|
+
];
|
735
|
+
});
|
736
|
+
this.registerOutputs({
|
737
|
+
configMap: this.configMap,
|
738
|
+
volumes: this.volumes,
|
739
|
+
volumeMounts: this.volumeMounts,
|
740
|
+
environment: this.environment
|
741
|
+
});
|
742
|
+
}
|
743
|
+
}
|
744
|
+
function createScriptData(distribution, environment) {
|
745
|
+
const scriptData = {};
|
746
|
+
const actions = [];
|
747
|
+
const distributionEnvironment = environment[distribution];
|
748
|
+
if (distributionEnvironment.preInstallPackages.length > 0) {
|
749
|
+
scriptData["pre-install-packages.sh"] = getInstallPackagesScript(
|
750
|
+
distribution,
|
751
|
+
distributionEnvironment.preInstallPackages
|
752
|
+
);
|
753
|
+
actions.push(`
|
754
|
+
echo "+ Installing pre-install packages..."
|
755
|
+
/scripts/pre-install-packages.sh
|
756
|
+
echo "+ Pre-install packages installed successfully"
|
757
|
+
`);
|
758
|
+
}
|
759
|
+
if (Object.keys(distributionEnvironment.preInstallScripts).length > 0) {
|
760
|
+
for (const key in distributionEnvironment.preInstallScripts) {
|
761
|
+
scriptData[`pre-install-${key}`] = distributionEnvironment.preInstallScripts[key];
|
762
|
+
actions.push(`
|
763
|
+
echo "+ Running pre-install script '${key}'..."
|
764
|
+
/scripts/pre-install-${key}
|
765
|
+
echo "+ Pre-install script '${key}'... Done"
|
766
|
+
`);
|
767
|
+
}
|
768
|
+
}
|
769
|
+
if (distributionEnvironment.packages.length > 0) {
|
770
|
+
scriptData["install-packages.sh"] = getInstallPackagesScript(
|
771
|
+
distribution,
|
772
|
+
distributionEnvironment.packages
|
773
|
+
);
|
774
|
+
actions.push(`
|
775
|
+
echo "+ Installing packages..."
|
776
|
+
/scripts/install-packages.sh
|
777
|
+
echo "+ Packages installed successfully"
|
778
|
+
`);
|
779
|
+
}
|
780
|
+
if (Object.keys(environment.setupScripts).length > 0) {
|
781
|
+
for (const key in environment.setupScripts) {
|
782
|
+
scriptData[`setup-${key}`] = environment.setupScripts[key];
|
783
|
+
actions.push(`
|
784
|
+
echo "+ Running setup script '${key}'..."
|
785
|
+
/scripts/setup-${key}
|
786
|
+
echo "+ Setup script '${key}'... Done"
|
787
|
+
`);
|
788
|
+
}
|
789
|
+
}
|
790
|
+
if (Object.keys(environment.cleanupScripts).length > 0) {
|
791
|
+
const cleanupActions = [];
|
792
|
+
for (const key in environment.cleanupScripts) {
|
793
|
+
scriptData[`cleanup-${key}`] = environment.cleanupScripts[key];
|
794
|
+
cleanupActions.push(`
|
795
|
+
echo "+ Running cleanup script '${key}'..."
|
796
|
+
/scripts/cleanup-${key}
|
797
|
+
echo "+ Cleanup script '${key}'... Done"
|
798
|
+
`);
|
799
|
+
}
|
800
|
+
actions.push(`
|
801
|
+
function cleanup() {
|
802
|
+
${cleanupActions.map((s) => s.trim()).join("\n\n")}
|
803
|
+
}
|
804
|
+
|
805
|
+
trap cleanup EXIT
|
806
|
+
trap cleanup SIGTERM
|
807
|
+
`);
|
808
|
+
}
|
809
|
+
for (const key in environment.scripts) {
|
810
|
+
scriptData[key] = environment.scripts[key];
|
811
|
+
}
|
812
|
+
scriptData["entrypoint.sh"] = trimIndentation(`
|
813
|
+
#!/bin/sh
|
814
|
+
set -e
|
815
|
+
|
816
|
+
if [ -z "$1" ]; then
|
817
|
+
echo "Usage: entrypoint.sh <main script> [args...]"
|
818
|
+
exit 1
|
819
|
+
fi
|
820
|
+
|
821
|
+
${actions.map((s) => s.trim()).join("\n\n")}
|
822
|
+
|
823
|
+
echo "+ Running main script..."
|
824
|
+
$@
|
825
|
+
echo "+ Main script completed"
|
826
|
+
`);
|
827
|
+
return scriptData;
|
828
|
+
}
|
829
|
+
function getInstallPackagesScript(distribution, packages) {
|
830
|
+
if (distribution === "alpine") {
|
831
|
+
return text`
|
832
|
+
#!/bin/sh
|
833
|
+
set -e
|
834
|
+
|
835
|
+
apk add --no-cache ${packages.join(" ")}
|
836
|
+
`;
|
837
|
+
} else {
|
838
|
+
return text`
|
839
|
+
#!/bin/sh
|
840
|
+
set -e
|
841
|
+
|
842
|
+
apt-get update
|
843
|
+
apt-get install -y ${packages.join(" ")}
|
844
|
+
`;
|
845
|
+
}
|
846
|
+
}
|
847
|
+
|
848
|
+
function createScriptContainer(options) {
|
849
|
+
return output$1(options).apply((options2) => {
|
850
|
+
const image = options2.bundle.distribution === "alpine" ? "alpine@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c" : "ubuntu@sha256:72297848456d5d37d1262630108ab308d3e9ec7ed1c3286a32fe09856619a782";
|
851
|
+
return {
|
852
|
+
image,
|
853
|
+
command: ["/scripts/entrypoint.sh", `/scripts/${options2.main}`],
|
854
|
+
...options2,
|
855
|
+
volumeMounts: merge(options2.bundle.volumeMounts, options2.volumeMounts),
|
856
|
+
volumes: merge(options2.bundle.volumes, options2.volumes),
|
857
|
+
environment: merge(options2.bundle.environment, options2.environment)
|
858
|
+
};
|
859
|
+
});
|
860
|
+
}
|
861
|
+
|
862
|
+
const jobExtraArgs = [...commonExtraArgs, "container", "containers"];
|
863
|
+
class Job extends ComponentResource {
|
864
|
+
/**
|
865
|
+
* The underlying Kubernetes job.
|
866
|
+
*/
|
867
|
+
job;
|
868
|
+
constructor(name, args, opts) {
|
869
|
+
super("highstate:k8s:Job", name, args, opts);
|
870
|
+
this.job = output(args).apply((args2) => {
|
871
|
+
const containers = normalize(args2.container, args2.containers);
|
872
|
+
return new batch.v1.Job(
|
873
|
+
name,
|
874
|
+
{
|
875
|
+
metadata: mapMetadata(args2, name),
|
876
|
+
spec: mergeDeep(
|
877
|
+
{
|
878
|
+
template: {
|
879
|
+
spec: {
|
880
|
+
containers: containers.map((container) => mapContainerToRaw(name, container)),
|
881
|
+
volumes: containers.flatMap((container) => normalize(container.volume, container.volumes)).map(mapWorkloadVolume),
|
882
|
+
restartPolicy: "Never"
|
883
|
+
}
|
884
|
+
}
|
885
|
+
},
|
886
|
+
omit(args2, jobExtraArgs)
|
887
|
+
)
|
888
|
+
},
|
889
|
+
{ parent: this, ...opts }
|
890
|
+
);
|
891
|
+
});
|
892
|
+
this.registerOutputs({ job: this.job });
|
893
|
+
}
|
894
|
+
}
|
895
|
+
|
896
|
+
const cronJobExtraArgs = [...commonExtraArgs, "container", "containers"];
|
897
|
+
class CronJob extends ComponentResource {
|
898
|
+
/**
|
899
|
+
* The underlying Kubernetes job.
|
900
|
+
*/
|
901
|
+
cronJob;
|
902
|
+
constructor(name, args, opts) {
|
903
|
+
super("highstate:k8s:CronJob", name, args, opts);
|
904
|
+
this.cronJob = output(args).apply((args2) => {
|
905
|
+
const containers = normalize(args2.container, args2.containers);
|
906
|
+
return new batch.v1.CronJob(
|
907
|
+
name,
|
908
|
+
{
|
909
|
+
metadata: mapMetadata(args2, name),
|
910
|
+
spec: mergeDeep(
|
911
|
+
{
|
912
|
+
jobTemplate: {
|
913
|
+
spec: {
|
914
|
+
template: {
|
915
|
+
spec: {
|
916
|
+
containers: containers.map((container) => mapContainerToRaw(name, container)),
|
917
|
+
volumes: containers.flatMap((container) => normalize(container.volume, container.volumes)).map(mapWorkloadVolume)
|
918
|
+
}
|
919
|
+
}
|
920
|
+
}
|
921
|
+
},
|
922
|
+
schedule: args2.schedule
|
923
|
+
},
|
924
|
+
omit(args2, cronJobExtraArgs)
|
925
|
+
)
|
926
|
+
},
|
927
|
+
{ parent: this, ...opts }
|
928
|
+
);
|
929
|
+
});
|
930
|
+
this.registerOutputs({ cronJob: this.cronJob });
|
931
|
+
}
|
932
|
+
}
|
933
|
+
|
934
|
+
function getChartServiceOutput(chart, name) {
|
935
|
+
const services = chart.resources.apply((resources) => {
|
936
|
+
return resources.filter((r) => core.v1.Service.isInstance(r)).map((service) => ({ name: service.metadata.name, service }));
|
937
|
+
});
|
938
|
+
return output$1(services).apply((services2) => {
|
939
|
+
const service = services2.find((s) => s.name === name)?.service;
|
940
|
+
if (!service) {
|
941
|
+
throw new Error(`Service with name '${name}' not found in the chart resources`);
|
942
|
+
}
|
943
|
+
return service;
|
944
|
+
});
|
945
|
+
}
|
946
|
+
function getChartService(chart, name) {
|
947
|
+
return toPromise(getChartServiceOutput(chart, name));
|
948
|
+
}
|
949
|
+
|
950
|
+
export { CronJob, Deployment, HttpRoute, Job, NetworkPolicy, ScriptBundle, Service, StatefulSet, createScriptContainer, getChartService, getChartServiceOutput, getServiceHost, mapContainerPortToServicePort, mapMetadata, mapNamespaceLikeToNamespaceName, mapNamespaceNameToSelector, mapSelectorLikeToSelector, mapServiceToLabelSelector, useAccessPoint };
|