@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.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 };