@highstate/k8s 0.6.2 → 0.7.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/index.js CHANGED
@@ -1,149 +1,111 @@
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';
1
+ import { m as mapMetadata, c as commonExtraArgs, v as verifyProvider, r as resourceIdToString, a as mapSelectorLikeToSelector, b as mapNamespaceNameToSelector, d as mapNamespaceLikeToNamespaceName, g as getAppDisplayName, e as getAppName } from './shared-Clzbl5K-.js';
2
+ export { h as createNamespace, f as createProvider, i as getNamespace } from './shared-Clzbl5K-.js';
3
+ import { ComponentResource, output, normalize, toPromise, apply } from '@highstate/pulumi';
4
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';
5
+ import { omit, concat, map, uniqueBy, capitalize, mergeDeep, flat, merge, pipe } from 'remeda';
9
6
  import { deepmerge } from 'deepmerge-ts';
7
+ import { output as output$1, ComponentResource as ComponentResource$1 } from '@pulumi/pulumi';
8
+ import { S as Service, m as mapContainerPortToServicePort, H as HttpRoute, a as mapServiceToLabelSelector } from './helm-wPTgVV1N.js';
9
+ export { C as Chart, R as RenderedChart, b as getChartService, g as getChartServiceOutput, r as resolveHelmChart } from './helm-wPTgVV1N.js';
10
+ import { parseDomain, ParseResultType } from 'parse-domain';
11
+ import '@highstate/library';
12
+ import { DnsRecord } from '@highstate/common';
13
+ import { gateway } from '@highstate/gateway-api';
10
14
  import { trimIndentation, text } from '@highstate/contract';
11
- import 'node:fs';
12
- import 'node:path';
15
+ import 'path';
16
+ import 'node:fs/promises';
17
+ import 'nano-spawn';
18
+ import 'crypto-hash';
19
+ import '@pulumi/command';
20
+ import 'glob';
13
21
 
14
- const serviceExtraArgs = [...commonExtraArgs, "port", "ports"];
15
- class Service extends ComponentResource {
22
+ const extraPersistentVolumeClaimArgs = [...commonExtraArgs, "size", "cluster"];
23
+ class PersistentVolumeClaim extends ComponentResource {
24
+ constructor(type, name, args, opts, clusterInfo, metadata, spec, status) {
25
+ super(type, name, args, opts);
26
+ this.clusterInfo = clusterInfo;
27
+ this.metadata = metadata;
28
+ this.spec = spec;
29
+ this.status = status;
30
+ }
16
31
  /**
17
- * The underlying Kubernetes service.
32
+ * The Highstate service entity.
18
33
  */
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)) {
34
+ get entity() {
69
35
  return output({
70
- name: ref.metadata.name,
71
- namespace: ref.metadata.namespace,
72
- port: ref.spec.ports[0].port
36
+ type: "k8s.persistent-volume-claim",
37
+ clusterInfo: this.clusterInfo,
38
+ metadata: this.metadata
73
39
  });
74
40
  }
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
- });
41
+ static create(name, args, opts) {
42
+ return new CreatedPersistentVolumeClaim(name, args, opts);
43
+ }
44
+ static of(name, entity, opts) {
45
+ return new ExternalPersistentVolumeClaim(
46
+ name,
47
+ output(entity).metadata,
48
+ output(entity).clusterInfo,
49
+ opts
50
+ );
84
51
  }
85
- return output({
86
- name: ref.name,
87
- namespace: ref.namespace,
88
- port: ref.port
89
- });
90
52
  }
91
-
92
- class HttpRoute extends ComponentResource {
93
- /**
94
- * The underlying Kubernetes resource.
95
- */
96
- route;
53
+ class CreatedPersistentVolumeClaim extends PersistentVolumeClaim {
97
54
  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(
55
+ const pvc = output(args).apply((args2) => {
56
+ return new core.v1.PersistentVolumeClaim(
104
57
  name,
105
58
  {
106
- metadata: mapMetadata(
59
+ metadata: mapMetadata(args2, name),
60
+ spec: deepmerge(
107
61
  {
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
62
+ accessModes: ["ReadWriteOnce"],
63
+ resources: {
64
+ requests: {
65
+ storage: args2.size ?? "100Mi"
66
+ }
118
67
  }
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
- }
68
+ },
69
+ omit(args2, extraPersistentVolumeClaimArgs)
70
+ )
132
71
  },
133
- { parent: this, ...opts }
72
+ opts
134
73
  );
135
74
  });
136
- this.registerOutputs({ route: this.route });
75
+ super(
76
+ "k8s:PersistentVolumeClaim",
77
+ name,
78
+ args,
79
+ opts,
80
+ output(args.cluster).info,
81
+ pvc.metadata,
82
+ pvc.spec,
83
+ pvc.status
84
+ );
137
85
  }
138
86
  }
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 } };
87
+ class ExternalPersistentVolumeClaim extends PersistentVolumeClaim {
88
+ constructor(name, id, clusterInfo, opts) {
89
+ const pvc = output(id).apply(async (id2) => {
90
+ await verifyProvider(opts.provider, this.clusterInfo);
91
+ return core.v1.PersistentVolumeClaim.get(
92
+ //
93
+ name,
94
+ resourceIdToString(id2),
95
+ { parent: this, provider: opts.provider }
96
+ );
97
+ });
98
+ super(
99
+ "highstate:k8s:ExternalPersistentVolumeClaim",
100
+ name,
101
+ { id, clusterInfo },
102
+ opts,
103
+ output(clusterInfo),
104
+ pvc.metadata,
105
+ pvc.spec,
106
+ pvc.status
107
+ );
145
108
  }
146
- return match;
147
109
  }
148
110
 
149
111
  const containerExtraArgs = [
@@ -154,10 +116,11 @@ const containerExtraArgs = [
154
116
  "environmentSource",
155
117
  "environmentSources"
156
118
  ];
157
- function mapContainerToRaw(fallbackName, container) {
119
+ function mapContainerToRaw(container, fallbackName) {
120
+ const containerName = container.name ?? fallbackName;
158
121
  return {
159
122
  ...omit(container, containerExtraArgs),
160
- name: container.name ?? fallbackName,
123
+ name: containerName,
161
124
  ports: normalize(container.port, container.ports),
162
125
  volumeMounts: map(normalize(container.volumeMount, container.volumeMounts), mapVolumeMount),
163
126
  env: concat(
@@ -221,7 +184,10 @@ function mapVolumeMount(volumeMount) {
221
184
  ["volume"]
222
185
  );
223
186
  }
224
- return volumeMount;
187
+ return {
188
+ ...volumeMount,
189
+ name: volumeMount.name
190
+ };
225
191
  }
226
192
  function mapEnvironmentSource(envFrom) {
227
193
  if (envFrom instanceof core.v1.ConfigMap) {
@@ -241,6 +207,14 @@ function mapEnvironmentSource(envFrom) {
241
207
  return envFrom;
242
208
  }
243
209
  function mapWorkloadVolume(volume) {
210
+ if (volume instanceof PersistentVolumeClaim) {
211
+ return {
212
+ name: volume.metadata.name,
213
+ persistentVolumeClaim: {
214
+ claimName: volume.metadata.name
215
+ }
216
+ };
217
+ }
244
218
  if (volume instanceof core.v1.PersistentVolumeClaim) {
245
219
  return {
246
220
  name: volume.metadata.name,
@@ -268,182 +242,272 @@ function mapWorkloadVolume(volume) {
268
242
  return volume;
269
243
  }
270
244
 
271
- const deploymentExtraArgs = [
272
- ...commonExtraArgs,
273
- "container",
274
- "containers",
275
- "gateway",
276
- "service",
277
- "httpRoute"
278
- ];
245
+ const workloadExtraArgs = [...commonExtraArgs, "container", "containers"];
246
+ const publicWorkloadExtraArgs = [...workloadExtraArgs, "service", "httpRoute"];
247
+ function getWorkloadComponents(name, args) {
248
+ const labels = {
249
+ "app.kubernetes.io/name": name
250
+ };
251
+ const containers = output$1(args).apply((args2) => normalize(args2.container, args2.containers));
252
+ const volumes = containers.apply((containers2) => {
253
+ const containerVolumes = containers2.flatMap((container) => normalize(container.volume, container.volumes)).map(mapWorkloadVolume);
254
+ const containerVolumeMounts = containers2.flatMap((container) => {
255
+ return normalize(container.volumeMount, container.volumeMounts).map((volumeMount) => {
256
+ return "volume" in volumeMount ? volumeMount.volume : void 0;
257
+ }).filter(Boolean);
258
+ }).map(mapWorkloadVolume);
259
+ return uniqueBy([...containerVolumes, ...containerVolumeMounts], (volume) => volume.name);
260
+ });
261
+ return { labels, containers, volumes };
262
+ }
263
+ function getPublicWorkloadComponents(name, args, parent, opts) {
264
+ const { labels, containers, volumes } = getWorkloadComponents(name, args);
265
+ const service = output$1({ args, containers }).apply(({ args: args2, containers: containers2 }) => {
266
+ if (!args2.service && !args2.httpRoute) {
267
+ return void 0;
268
+ }
269
+ if (args2.patch?.service) {
270
+ return Service.of(name, args2.patch.service, { parent: parent(), ...opts });
271
+ }
272
+ if (args2.patch) {
273
+ return void 0;
274
+ }
275
+ const ports = containers2.flatMap((container) => normalize(container.port, container.ports));
276
+ return Service.create(
277
+ name,
278
+ {
279
+ ...args2.service,
280
+ selector: labels,
281
+ cluster: args2.cluster,
282
+ namespace: args2.namespace,
283
+ ports: (
284
+ // allow to completely override the ports
285
+ !args2.service?.port && !args2.service?.ports ? ports.map(mapContainerPortToServicePort) : args2.service?.ports
286
+ )
287
+ },
288
+ { parent: parent(), ...opts }
289
+ );
290
+ });
291
+ const httpRoute = output$1({
292
+ args,
293
+ service
294
+ }).apply(({ args: args2, service: service2 }) => {
295
+ if (!args2.httpRoute || !service2) {
296
+ return void 0;
297
+ }
298
+ if (args2.patch) {
299
+ return void 0;
300
+ }
301
+ return new HttpRoute(
302
+ name,
303
+ {
304
+ ...args2.httpRoute,
305
+ rule: {
306
+ backend: service2
307
+ }
308
+ },
309
+ { parent: parent(), ...opts }
310
+ );
311
+ });
312
+ return { labels, containers, volumes, service, httpRoute };
313
+ }
314
+
279
315
  class Deployment extends ComponentResource {
316
+ constructor(type, name, args, opts, clusterInfo, metadata, spec, status, _service, _httpRoute, resources) {
317
+ super(type, name, args, opts);
318
+ this.clusterInfo = clusterInfo;
319
+ this.metadata = metadata;
320
+ this.spec = spec;
321
+ this.status = status;
322
+ this._service = _service;
323
+ this._httpRoute = _httpRoute;
324
+ this.resources = resources;
325
+ }
280
326
  /**
281
- * The underlying Kubernetes deployment.
327
+ * The Highstate deployment entity.
282
328
  */
283
- deployment;
329
+ get entity() {
330
+ return output({
331
+ type: "k8s.deployment",
332
+ clusterInfo: this.clusterInfo,
333
+ metadata: this.metadata,
334
+ spec: this.spec,
335
+ service: this._service.apply((service) => service?.entity)
336
+ });
337
+ }
284
338
  /**
285
339
  * The service associated with the deployment.
286
340
  */
287
- service;
341
+ get service() {
342
+ return this._service.apply((service) => {
343
+ if (!service) {
344
+ throw new Error("The service is not available.");
345
+ }
346
+ return service;
347
+ });
348
+ }
288
349
  /**
289
350
  * The HTTP route associated with the deployment.
290
351
  */
291
- httpRoute;
352
+ get httpRoute() {
353
+ return this._httpRoute.apply((httpRoute) => {
354
+ if (!httpRoute) {
355
+ throw new Error("The HTTP route is not available.");
356
+ }
357
+ return httpRoute;
358
+ });
359
+ }
360
+ static create(name, args, opts) {
361
+ return new CreatedDeployment(name, args, opts);
362
+ }
363
+ }
364
+ class CreatedDeployment extends Deployment {
292
365
  constructor(name, args, opts) {
293
- super("highstate:k8s:Deployment", name, args, opts);
294
- const labels = {
295
- "app.kubernetes.io/name": name
296
- };
297
- this.deployment = output(args).apply((args2) => {
298
- const containers = normalize(args2.container, args2.containers);
299
- return new apps.v1.Deployment(
300
- name,
301
- {
302
- metadata: mapMetadata(args2, name),
303
- spec: mergeDeep(
304
- {
305
- template: {
306
- metadata: {
307
- labels
366
+ const { labels, containers, volumes, service, httpRoute } = getPublicWorkloadComponents(
367
+ name,
368
+ args,
369
+ () => this,
370
+ opts
371
+ );
372
+ const deployment = output({ args, containers, volumes }).apply(
373
+ async ({ args: args2, containers: containers2, volumes: volumes2 }) => {
374
+ await verifyProvider(opts.provider, args2.cluster.info);
375
+ return new (args2.patch ? apps.v1.DeploymentPatch : apps.v1.Deployment)(
376
+ name,
377
+ {
378
+ metadata: mapMetadata(args2.patch?.metadata ?? args2, name),
379
+ spec: deepmerge(
380
+ {
381
+ template: {
382
+ metadata: {
383
+ labels
384
+ },
385
+ spec: {
386
+ containers: containers2.map((container) => mapContainerToRaw(container, name)),
387
+ volumes: volumes2
388
+ }
308
389
  },
309
- spec: {
310
- containers: containers.map((container) => mapContainerToRaw(name, container)),
311
- volumes: containers.flatMap((container) => normalize(container.volume, container.volumes)).map(mapWorkloadVolume)
390
+ selector: {
391
+ matchLabels: labels
312
392
  }
313
393
  },
314
- selector: {
315
- matchLabels: labels
316
- }
317
- },
318
- omit(args2, deploymentExtraArgs)
319
- )
320
- },
321
- {
322
- ...opts,
323
- parent: this
324
- }
325
- );
326
- });
327
- this.service = output({
328
- args: args.service,
329
- httpRouteArgs: args.httpRoute,
330
- namespace: this.deployment.metadata.namespace,
331
- containers: this.deployment.spec.template.spec.containers
332
- }).apply(({ args: args2, httpRouteArgs, namespace, containers }) => {
333
- if (!args2 && !httpRouteArgs) {
334
- return void 0;
335
- }
336
- const ports = containers.flatMap((container) => container.ports ?? []);
337
- return new Service(
338
- args2?.name ?? name,
339
- {
340
- ...args2,
341
- namespace,
342
- ports: (
343
- // allow to completely override the ports
344
- !args2?.port && !args2?.ports ? ports.map(mapContainerPortToServicePort) : args2?.ports
345
- ),
346
- selector: labels
347
- },
348
- {
349
- ...opts,
350
- parent: this
351
- }
352
- );
353
- });
354
- this.httpRoute = output({
355
- args: args.httpRoute,
356
- service: this.service
357
- }).apply(({ args: args2, service }) => {
358
- if (!args2 || !service) {
359
- return void 0;
360
- }
361
- return new HttpRoute(
362
- name,
363
- {
364
- ...args2,
365
- gateway: args2.gateway,
366
- rule: {
367
- backend: service.service
368
- }
369
- },
370
- {
371
- ...opts,
372
- parent: this
373
- }
374
- );
375
- });
376
- this.registerOutputs({
377
- deployment: this.deployment,
378
- service: this.service
379
- });
380
- }
381
- getRequiredService() {
382
- return this.service.apply((service) => {
383
- if (!service) {
384
- throw new Error("The service is not available for this deployment.");
394
+ omit(args2, publicWorkloadExtraArgs)
395
+ )
396
+ },
397
+ { parent: this, ...opts }
398
+ );
385
399
  }
386
- return service;
387
- });
400
+ );
401
+ super(
402
+ "highstate:k8s:Deployment",
403
+ name,
404
+ args,
405
+ opts,
406
+ output(args.cluster).info,
407
+ deployment.metadata,
408
+ deployment.spec,
409
+ deployment.status,
410
+ service,
411
+ httpRoute,
412
+ [deployment]
413
+ );
388
414
  }
389
415
  }
390
416
 
391
- function mapPersistentVolumeClaimToRaw(args) {
392
- return {
393
- metadata: mapMetadata(args),
394
- spec: args
395
- };
396
- }
397
-
398
417
  class StatefulSet extends ComponentResource {
418
+ constructor(type, name, args, opts, clusterInfo, metadata, spec, status, _service, _httpRoute) {
419
+ super(type, name, args, opts);
420
+ this.clusterInfo = clusterInfo;
421
+ this.metadata = metadata;
422
+ this.spec = spec;
423
+ this.status = status;
424
+ this._service = _service;
425
+ this._httpRoute = _httpRoute;
426
+ }
399
427
  /**
400
- * The underlying Kubernetes stateful set.
428
+ * The Highstate stateful set entity.
401
429
  */
402
- statefulSet;
430
+ get entity() {
431
+ return output({
432
+ type: "k8s.stateful-set",
433
+ clusterInfo: this.clusterInfo,
434
+ metadata: this.metadata,
435
+ service: this.service.entity
436
+ });
437
+ }
403
438
  /**
404
439
  * The service associated with the stateful set.
405
440
  */
406
- service;
407
- constructor(name, args, opts) {
408
- super("highstate:k8s:StatefulSet", name, args, opts);
409
- const labels = {
410
- "app.kubernetes.io/name": name
411
- };
412
- this.service = output(args.service).apply((service) => {
413
- return new Service(
414
- service?.name ?? name,
415
- {
416
- ...service,
417
- clusterIP: "None"
418
- },
419
- { ...opts, parent: this }
420
- );
421
- });
422
- this.statefulSet = output(args).apply((args2) => {
423
- return new apps.v1.StatefulSet(name, {
424
- metadata: mapMetadata(args2),
425
- spec: mergeDeep(args2, {
426
- serviceName: this.service.service.metadata.name,
427
- template: {
428
- metadata: {
429
- labels
430
- },
431
- spec: {
432
- containers: normalize(args2.container, args2.containers).map((container) => mapContainerToRaw(name, container))
433
- }
434
- },
435
- selector: {
436
- matchLabels: labels
437
- },
438
- volumeClaimTemplates: normalize(args2.volumeClaim, args2.volumeClaims).map(mapPersistentVolumeClaimToRaw)
439
- })
440
- });
441
+ get service() {
442
+ return this._service.apply((service) => {
443
+ if (!service) {
444
+ throw new Error("The service is not available.");
445
+ }
446
+ return service;
441
447
  });
442
- this.registerOutputs({
443
- statefulSet: this.statefulSet,
444
- service: this.service
448
+ }
449
+ /**
450
+ * The HTTP route associated with the stateful set.
451
+ */
452
+ get httpRoute() {
453
+ return this._httpRoute.apply((httpRoute) => {
454
+ if (!httpRoute) {
455
+ throw new Error("The HTTP route is not available.");
456
+ }
457
+ return httpRoute;
445
458
  });
446
459
  }
460
+ static create(name, args, opts) {
461
+ return new CreatedStatefulSet(name, args, opts);
462
+ }
463
+ }
464
+ class CreatedStatefulSet extends StatefulSet {
465
+ constructor(name, args, opts) {
466
+ const { containers, volumes, labels, service, httpRoute } = getPublicWorkloadComponents(
467
+ name,
468
+ args,
469
+ () => this,
470
+ opts
471
+ );
472
+ const statefulSet = output({ args, containers, volumes, service }).apply(
473
+ async ({ args: args2, containers: containers2, volumes: volumes2, service: service2 }) => {
474
+ await verifyProvider(opts.provider, args2.cluster?.info);
475
+ return new (args2.patch ? apps.v1.StatefulSetPatch : apps.v1.StatefulSet)(
476
+ name,
477
+ {
478
+ metadata: mapMetadata(args2.patch?.metadata ?? args2, name),
479
+ spec: deepmerge(
480
+ {
481
+ serviceName: service2?.metadata.name || name,
482
+ template: {
483
+ metadata: !args2.patch ? { labels } : void 0,
484
+ spec: {
485
+ containers: containers2.map((container) => mapContainerToRaw(container, name)),
486
+ volumes: volumes2
487
+ }
488
+ },
489
+ selector: !args2.patch ? { matchLabels: labels } : void 0
490
+ },
491
+ omit(args2, publicWorkloadExtraArgs)
492
+ )
493
+ },
494
+ { parent: this, ...opts }
495
+ );
496
+ }
497
+ );
498
+ super(
499
+ "highstate:k8s:StatefulSet",
500
+ name,
501
+ args,
502
+ opts,
503
+ output(args.cluster).info,
504
+ statefulSet.metadata,
505
+ statefulSet.spec,
506
+ statefulSet.status,
507
+ service,
508
+ httpRoute
509
+ );
510
+ }
447
511
  }
448
512
 
449
513
  class NetworkPolicy extends ComponentResource {
@@ -456,11 +520,28 @@ class NetworkPolicy extends ComponentResource {
456
520
  const normalizedArgs = output(args).apply((args2) => {
457
521
  const ingressRules = normalize(args2.ingressRule, args2.ingressRules);
458
522
  const egressRules = normalize(args2.egressRule, args2.egressRules);
523
+ const endpoints = normalize(args2.egressRule?.toEndpoint, args2.egressRule?.toEndpoints);
524
+ const parsedEndpoints = endpoints.map((endpoint) => parseDomain(endpoint));
525
+ const cidrsFromEndpoints = parsedEndpoints.filter((result) => result.type === ParseResultType.Ip).map((result) => NetworkPolicy.mapCidrFromEndpoint(result));
526
+ const fqdnsFromEndpoints = parsedEndpoints.filter((result) => result.type !== ParseResultType.Invalid).map((result) => result.hostname);
527
+ const extraEgressRules = [];
528
+ if (args2.allowKubeDns) {
529
+ extraEgressRules.push({
530
+ namespaces: ["kube-system"],
531
+ selectors: [{ matchLabels: { "k8s-app": "kube-dns" } }],
532
+ ports: [{ port: 53, protocol: "UDP" }],
533
+ all: false,
534
+ cidrs: [],
535
+ fqdns: [],
536
+ services: []
537
+ });
538
+ }
459
539
  return {
460
540
  ...args2,
461
541
  podSelector: args2.selector ? mapSelectorLikeToSelector(args2.selector) : {},
462
542
  isolateEgress: args2.isolateEgress ?? false,
463
543
  isolateIngress: args2.isolateIngress ?? false,
544
+ allowKubeApiServer: args2.allowKubeApiServer ?? false,
464
545
  ingressRules: ingressRules.map((rule) => ({
465
546
  all: rule.fromAll ?? false,
466
547
  cidrs: normalize(rule.fromCidr, rule.fromCidrs),
@@ -470,15 +551,17 @@ class NetworkPolicy extends ComponentResource {
470
551
  selectors: normalize(rule.fromSelector, rule.fromSelectors),
471
552
  ports: normalize(rule.toPort, rule.toPorts)
472
553
  })),
473
- egressRules: egressRules.map((rule) => ({
474
- all: rule.toAll ?? false,
475
- cidrs: normalize(rule.toCidr, rule.toCidrs),
476
- fqdns: normalize(rule.toFqdn, rule.toFqdns),
477
- services: normalize(rule.toService, rule.toServices),
478
- namespaces: normalize(rule.toNamespace, rule.toNamespaces),
479
- selectors: normalize(rule.toSelector, rule.toSelectors),
480
- ports: normalize(rule.toPort, rule.toPorts)
481
- }))
554
+ egressRules: egressRules.map((rule) => {
555
+ return {
556
+ all: rule.toAll ?? false,
557
+ cidrs: normalize(rule.toCidr, rule.toCidrs).concat(cidrsFromEndpoints),
558
+ fqdns: normalize(rule.toFqdn, rule.toFqdns).concat(fqdnsFromEndpoints),
559
+ services: normalize(rule.toService, rule.toServices),
560
+ namespaces: normalize(rule.toNamespace, rule.toNamespaces),
561
+ selectors: normalize(rule.toSelector, rule.toSelectors),
562
+ ports: normalize(rule.toPort, rule.toPorts)
563
+ };
564
+ }).concat(extraEgressRules)
482
565
  };
483
566
  });
484
567
  this.networkPolicy = normalizedArgs.apply((args2) => {
@@ -488,9 +571,16 @@ class NetworkPolicy extends ComponentResource {
488
571
  });
489
572
  this.registerOutputs({ networkPolicy: this.networkPolicy });
490
573
  }
574
+ static mapCidrFromEndpoint(result) {
575
+ if (result.ipVersion === 4) {
576
+ return `${result.hostname}/32`;
577
+ }
578
+ return `${result.hostname}/128`;
579
+ }
580
+ static supportedCNIs = ["cilium"];
491
581
  static create(name, args, opts) {
492
582
  return output(args).apply(async (args2) => {
493
- if (!args2.cni || args2.cni === "unknown") {
583
+ if (!args2.cni || !NetworkPolicy.supportedCNIs.includes(args2.cni)) {
494
584
  return new NativeNetworkPolicy(name, args2, opts);
495
585
  }
496
586
  const implName = `${capitalize(args2.cni)}NetworkPolicy`;
@@ -502,6 +592,56 @@ class NetworkPolicy extends ComponentResource {
502
592
  return new implClass(name, args2, opts);
503
593
  });
504
594
  }
595
+ static allowInsideNamespace(namespace, k8sCluster, opts) {
596
+ return NetworkPolicy.create(
597
+ "allow-inside-namespace",
598
+ {
599
+ namespace,
600
+ cni: output(k8sCluster).info.cni,
601
+ description: "Allow all traffic inside the namespace.",
602
+ selector: {},
603
+ ingressRule: { fromNamespace: namespace },
604
+ egressRule: { toNamespace: namespace }
605
+ },
606
+ opts
607
+ );
608
+ }
609
+ static allowKubeApiServer(namespace, k8sCluster, opts) {
610
+ return NetworkPolicy.create(
611
+ "allow-kube-api-server",
612
+ {
613
+ namespace,
614
+ cni: output(k8sCluster).info.cni,
615
+ description: "Allow all traffic to the Kubernetes API server from the namespace.",
616
+ allowKubeApiServer: true
617
+ },
618
+ opts
619
+ );
620
+ }
621
+ static allowKubeDns(namespace, k8sCluster, opts) {
622
+ return NetworkPolicy.create(
623
+ "allow-kube-dns",
624
+ {
625
+ namespace,
626
+ cni: output(k8sCluster).info.cni,
627
+ description: "Allow all traffic to the Kubernetes DNS server from the namespace.",
628
+ allowKubeDns: true
629
+ },
630
+ opts
631
+ );
632
+ }
633
+ static allowAllEgress(namespace, k8sCluster, opts) {
634
+ return NetworkPolicy.create(
635
+ "allow-all-egress",
636
+ {
637
+ namespace,
638
+ cni: output(k8sCluster).info.cni,
639
+ description: "Allow all egress traffic from the namespace.",
640
+ egressRule: { toAll: true }
641
+ },
642
+ opts
643
+ );
644
+ }
505
645
  }
506
646
  class NativeNetworkPolicy extends NetworkPolicy {
507
647
  create(name, args, opts) {
@@ -545,10 +685,16 @@ class NativeNetworkPolicy extends NetworkPolicy {
545
685
  if (needFallback) {
546
686
  return [{ to: [{ ipBlock: NativeNetworkPolicy.fallbackIpBlock }] }];
547
687
  }
548
- return args.egressRules.map((rule) => ({
549
- to: rule.all ? void 0 : NativeNetworkPolicy.createRulePeers(rule),
550
- ports: NativeNetworkPolicy.mapPorts(rule.ports)
551
- }));
688
+ const extraRules = [];
689
+ if (args.allowKubeApiServer) {
690
+ extraRules.push({ to: [{ ipBlock: { cidr: "10.96.0.1" } }] });
691
+ }
692
+ return args.egressRules.map((rule) => {
693
+ return {
694
+ to: rule.all ? void 0 : NativeNetworkPolicy.createRulePeers(rule),
695
+ ports: NativeNetworkPolicy.mapPorts(rule.ports)
696
+ };
697
+ }).concat(extraRules);
552
698
  }
553
699
  static createRulePeers(args) {
554
700
  return [
@@ -609,28 +755,80 @@ class NativeNetworkPolicy extends NetworkPolicy {
609
755
  }
610
756
 
611
757
  function useAccessPoint(args) {
612
- const result = output(args).apply((args2) => {
613
- const gateway2 = createGateway({
614
- ...args2,
615
- annotations: {
616
- "cert-manager.io/cluster-issuer": args2.accessPoint.tlsIssuer.clusterIssuerName
617
- },
618
- gateway: args2.accessPoint.gateway
619
- });
620
- const dnsRecord = DnsRecord.create(args2.fqdn, {
621
- provider: args2.accessPoint.dnsProvider,
622
- type: "A",
623
- value: args2.accessPoint.gateway.ip
624
- });
625
- return output({
626
- gateway: gateway2,
627
- dnsRecord
628
- });
629
- });
758
+ const result = output({ args, namespaceName: output(args.namespace).metadata.name }).apply(
759
+ ({ args: args2, namespaceName }) => {
760
+ const gateway2 = createGateway({
761
+ ...args2,
762
+ annotations: {
763
+ "cert-manager.io/cluster-issuer": args2.accessPoint.tlsIssuer.clusterIssuerName
764
+ },
765
+ gateway: args2.accessPoint.gateway
766
+ });
767
+ const dnsRecords = normalize(args2.fqdn, args2.fqdns).map((fqdn) => {
768
+ return DnsRecord.create(fqdn, {
769
+ provider: args2.accessPoint.dnsProvider,
770
+ type: "A",
771
+ value: args2.accessPoint.gateway.ip
772
+ });
773
+ });
774
+ const networkPolicies = [];
775
+ if (args2.accessPoint.gateway.service) {
776
+ const displayName = getAppDisplayName(args2.accessPoint.gateway.service.metadata);
777
+ networkPolicies.push(
778
+ NetworkPolicy.create(
779
+ `allow-ingress-from-${getAppName(args2.accessPoint.gateway.service.metadata)}`,
780
+ {
781
+ cni: args2.accessPoint.gateway.service.clusterInfo.cni,
782
+ namespace: args2.namespace,
783
+ description: `Allow ingress traffic from the gateway "${displayName}".`,
784
+ ingressRule: {
785
+ fromNamespace: args2.accessPoint.gateway.service.metadata.namespace,
786
+ fromSelector: args2.accessPoint.gateway.service.spec.selector
787
+ }
788
+ },
789
+ { provider: args2.provider }
790
+ ),
791
+ NetworkPolicy.create(
792
+ `allow-egress-to-${namespaceName}`,
793
+ {
794
+ cni: args2.accessPoint.gateway.service.clusterInfo.cni,
795
+ namespace: args2.accessPoint.gateway.service.metadata.namespace,
796
+ selector: args2.accessPoint.gateway.service.spec.selector,
797
+ description: `Allow egress traffic to the namespace "${namespaceName}".`,
798
+ egressRule: {
799
+ toNamespace: args2.namespace
800
+ }
801
+ },
802
+ { provider: args2.provider }
803
+ )
804
+ );
805
+ }
806
+ return output({
807
+ gateway: gateway2,
808
+ dnsRecords,
809
+ networkPolicies
810
+ });
811
+ }
812
+ );
630
813
  return toPromise(result);
631
814
  }
815
+ function useStandardAcessPoint(appName, namespace, args, inputs, provider) {
816
+ return useAccessPoint({
817
+ name: appName,
818
+ namespace,
819
+ fqdn: args.fqdn,
820
+ accessPoint: inputs.accessPoint,
821
+ clusterInfo: inputs.k8sCluster.info,
822
+ provider
823
+ });
824
+ }
632
825
  function createGateway(args) {
633
826
  return output(args).apply((args2) => {
827
+ if (args2.clusterInfo.id !== args2.gateway.clusterInfo.id) {
828
+ throw new Error(
829
+ "The provided Kubernetes cluster is different from the one where the gateway controller is deployed."
830
+ );
831
+ }
634
832
  return new gateway.v1.Gateway(
635
833
  args2.name,
636
834
  {
@@ -641,18 +839,19 @@ function createGateway(args) {
641
839
  },
642
840
  spec: {
643
841
  gatewayClassName: output(args2.gateway).gatewayClassName,
644
- listeners: [
645
- {
646
- name: "https",
842
+ listeners: normalize(args2.fqdn, args2.fqdns).map((fqdn) => {
843
+ const normalizedName = fqdn.replace(/\*/g, "wildcard");
844
+ return {
845
+ name: `https-${normalizedName}`,
647
846
  port: output(args2.gateway).httpsListenerPort,
648
847
  protocol: "HTTPS",
649
- hostname: args2.fqdn,
848
+ hostname: fqdn,
650
849
  tls: {
651
850
  mode: "Terminate",
652
- certificateRefs: [{ name: args2.fqdn }]
851
+ certificateRefs: [{ name: normalizedName }]
653
852
  }
654
- }
655
- ]
853
+ };
854
+ })
656
855
  }
657
856
  },
658
857
  { provider: args2.provider, deletedWith: args2.namespace }
@@ -882,7 +1081,7 @@ class Job extends ComponentResource {
882
1081
  {
883
1082
  template: {
884
1083
  spec: {
885
- containers: containers.map((container) => mapContainerToRaw(name, container)),
1084
+ containers: containers.map((container) => mapContainerToRaw(container, name)),
886
1085
  volumes: containers.flatMap((container) => normalize(container.volume, container.volumes)).map(mapWorkloadVolume),
887
1086
  restartPolicy: "Never"
888
1087
  }
@@ -918,7 +1117,7 @@ class CronJob extends ComponentResource {
918
1117
  spec: {
919
1118
  template: {
920
1119
  spec: {
921
- containers: containers.map((container) => mapContainerToRaw(name, container)),
1120
+ containers: containers.map((container) => mapContainerToRaw(container, name)),
922
1121
  volumes: containers.flatMap((container) => normalize(container.volume, container.volumes)).map(mapWorkloadVolume)
923
1122
  }
924
1123
  }
@@ -936,20 +1135,4 @@ class CronJob extends ComponentResource {
936
1135
  }
937
1136
  }
938
1137
 
939
- function getChartServiceOutput(chart, name) {
940
- const services = chart.resources.apply((resources) => {
941
- return resources.filter((r) => core.v1.Service.isInstance(r)).map((service) => ({ name: service.metadata.name, service }));
942
- });
943
- return output$1(services).apply((services2) => {
944
- const service = services2.find((s) => s.name === name)?.service;
945
- if (!service) {
946
- throw new Error(`Service with name '${name}' not found in the chart resources`);
947
- }
948
- return service;
949
- });
950
- }
951
- function getChartService(chart, name) {
952
- return toPromise(getChartServiceOutput(chart, name));
953
- }
954
-
955
- export { CronJob, Deployment, HttpRoute, Job, NetworkPolicy, ScriptBundle, Service, StatefulSet, createScriptContainer, getChartService, getChartServiceOutput, getServiceHost, mapContainerPortToServicePort, mapMetadata, mapNamespaceLikeToNamespaceName, mapNamespaceNameToSelector, mapSelectorLikeToSelector, mapServiceToLabelSelector, useAccessPoint };
1138
+ export { CronJob, Deployment, HttpRoute, Job, NetworkPolicy, PersistentVolumeClaim, ScriptBundle, Service, StatefulSet, createScriptContainer, getAppDisplayName, getAppName, mapContainerPortToServicePort, mapMetadata, mapNamespaceLikeToNamespaceName, mapNamespaceNameToSelector, mapSelectorLikeToSelector, mapServiceToLabelSelector, useAccessPoint, useStandardAcessPoint };