timonel 2.8.0-beta.4 → 2.8.0

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.
Files changed (72) hide show
  1. package/CHANGELOG.md +9 -19
  2. package/README.md +12 -55
  3. package/dist/cli.js +2 -2
  4. package/dist/cli.js.map +1 -1
  5. package/dist/index.d.ts +7 -15
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +5 -7
  8. package/dist/index.js.map +1 -1
  9. package/dist/lib/helm.d.ts +234 -4
  10. package/dist/lib/helm.d.ts.map +1 -1
  11. package/dist/lib/helm.js +309 -20
  12. package/dist/lib/helm.js.map +1 -1
  13. package/dist/lib/helmChartWriter.js +5 -19
  14. package/dist/lib/helmChartWriter.js.map +1 -1
  15. package/dist/lib/resources/baseResourceProvider.d.ts +1 -1
  16. package/dist/lib/resources/baseResourceProvider.d.ts.map +1 -1
  17. package/dist/lib/resources/baseResourceProvider.js.map +1 -1
  18. package/dist/lib/resources/cloud/aws/awsResources.d.ts +5 -6
  19. package/dist/lib/resources/cloud/aws/awsResources.d.ts.map +1 -1
  20. package/dist/lib/resources/cloud/aws/awsResources.js +59 -30
  21. package/dist/lib/resources/cloud/aws/awsResources.js.map +1 -1
  22. package/dist/lib/rutter.d.ts +73 -708
  23. package/dist/lib/rutter.d.ts.map +1 -1
  24. package/dist/lib/rutter.js +157 -837
  25. package/dist/lib/rutter.js.map +1 -1
  26. package/dist/lib/security.d.ts +2 -1
  27. package/dist/lib/security.d.ts.map +1 -1
  28. package/dist/lib/security.js +33 -4
  29. package/dist/lib/security.js.map +1 -1
  30. package/dist/lib/umbrellaRutter.d.ts +1 -1
  31. package/dist/lib/umbrellaRutter.d.ts.map +1 -1
  32. package/dist/lib/utils/helmHelpers.d.ts +2 -12
  33. package/dist/lib/utils/helmHelpers.d.ts.map +1 -1
  34. package/dist/lib/utils/helmHelpers.js +2 -46
  35. package/dist/lib/utils/helmHelpers.js.map +1 -1
  36. package/package.json +2 -2
  37. package/dist/lib/resources/autoscaling/autoscalingResources.d.ts +0 -248
  38. package/dist/lib/resources/autoscaling/autoscalingResources.d.ts.map +0 -1
  39. package/dist/lib/resources/autoscaling/autoscalingResources.js +0 -81
  40. package/dist/lib/resources/autoscaling/autoscalingResources.js.map +0 -1
  41. package/dist/lib/resources/cloud/azure/azureResources.d.ts +0 -169
  42. package/dist/lib/resources/cloud/azure/azureResources.d.ts.map +0 -1
  43. package/dist/lib/resources/cloud/azure/azureResources.js +0 -218
  44. package/dist/lib/resources/cloud/azure/azureResources.js.map +0 -1
  45. package/dist/lib/resources/cloud/gcp/gcpResources.d.ts +0 -164
  46. package/dist/lib/resources/cloud/gcp/gcpResources.d.ts.map +0 -1
  47. package/dist/lib/resources/cloud/gcp/gcpResources.js +0 -192
  48. package/dist/lib/resources/cloud/gcp/gcpResources.js.map +0 -1
  49. package/dist/lib/resources/core/coreResources.d.ts +0 -790
  50. package/dist/lib/resources/core/coreResources.d.ts.map +0 -1
  51. package/dist/lib/resources/core/coreResources.js +0 -1115
  52. package/dist/lib/resources/core/coreResources.js.map +0 -1
  53. package/dist/lib/resources/core/storageResources.d.ts +0 -133
  54. package/dist/lib/resources/core/storageResources.d.ts.map +0 -1
  55. package/dist/lib/resources/core/storageResources.js +0 -120
  56. package/dist/lib/resources/core/storageResources.js.map +0 -1
  57. package/dist/lib/resources/network/networkResources.d.ts +0 -229
  58. package/dist/lib/resources/network/networkResources.d.ts.map +0 -1
  59. package/dist/lib/resources/network/networkResources.js +0 -310
  60. package/dist/lib/resources/network/networkResources.js.map +0 -1
  61. package/dist/lib/resources/validation/validationResources.d.ts +0 -26
  62. package/dist/lib/resources/validation/validationResources.d.ts.map +0 -1
  63. package/dist/lib/resources/validation/validationResources.js +0 -33
  64. package/dist/lib/resources/validation/validationResources.js.map +0 -1
  65. package/dist/lib/utils/podTemplates.d.ts +0 -192
  66. package/dist/lib/utils/podTemplates.d.ts.map +0 -1
  67. package/dist/lib/utils/podTemplates.js +0 -331
  68. package/dist/lib/utils/podTemplates.js.map +0 -1
  69. package/dist/lib/utils/resourceNaming.d.ts +0 -57
  70. package/dist/lib/utils/resourceNaming.d.ts.map +0 -1
  71. package/dist/lib/utils/resourceNaming.js +0 -82
  72. package/dist/lib/utils/resourceNaming.js.map +0 -1
@@ -1,19 +1,10 @@
1
- import { Chart, App, Testing } from 'cdk8s';
1
+ import { ApiObject, App, Chart, Testing } from 'cdk8s';
2
2
  import YAML from 'yaml';
3
- // Import resource providers
4
- import { CoreResources } from './resources/core/coreResources.js';
5
- import { StorageResources } from './resources/core/storageResources.js';
3
+ import { helm, include } from './helm.js';
4
+ import { HelmChartWriter } from './helmChartWriter.js';
6
5
  import { AWSResources } from './resources/cloud/aws/awsResources.js';
7
- import { AzureResources } from './resources/cloud/azure/azureResources.js';
8
- import { GCPResources } from './resources/cloud/gcp/gcpResources.js';
9
- import { AutoscalingResources } from './resources/autoscaling/autoscalingResources.js';
10
- import { NetworkResources } from './resources/network/networkResources.js';
11
6
  import { KarpenterResources } from './resources/cloud/aws/karpenterResources.js';
12
- // Import HelmChartWriter for write functionality
13
- import { HelmChartWriter } from './helmChartWriter.js';
14
- import { include, helm } from './helm.js';
15
7
  import { generateHelpersTemplate } from './utils/helmHelpers.js';
16
- import { generateManifestName } from './utils/resourceNaming.js';
17
8
  /**
18
9
  * Rutter class with modular architecture
19
10
  * Provides a clean API while delegating to specialized resource providers
@@ -35,346 +26,9 @@ export class Rutter {
35
26
  ...(props.namespace ? { namespace: props.namespace } : {}),
36
27
  });
37
28
  // Initialize resource providers
38
- this.coreResources = new CoreResources(this.chart);
39
- this.storageResources = new StorageResources(this.chart);
40
29
  this.awsResources = new AWSResources(this.chart);
41
- this.azureResources = new AzureResources(this.chart);
42
- this.gcpResources = new GCPResources(this.chart);
43
- this.autoscalingResources = new AutoscalingResources(this.chart);
44
- this.networkResources = new NetworkResources(this.chart);
45
30
  this.karpenterResources = new KarpenterResources(this.chart);
46
31
  }
47
- // Core Kubernetes Resources
48
- /**
49
- * Creates a Deployment resource
50
- * @param spec - Deployment specification
51
- * @returns Created Deployment ApiObject
52
- *
53
- * @example
54
- * ```typescript
55
- * rutter.addDeployment({
56
- * name: 'web-app',
57
- * image: 'nginx:1.21',
58
- * replicas: 3,
59
- * containerPort: 80
60
- * });
61
- * ```
62
- *
63
- * @since 1.0.0
64
- */
65
- addDeployment(spec) {
66
- return this.coreResources.addDeployment(spec);
67
- }
68
- /**
69
- * Creates a DaemonSet resource
70
- * @param spec - DaemonSet specification
71
- * @returns Created DaemonSet ApiObject
72
- *
73
- * @example
74
- * ```typescript
75
- * rutter.addDaemonSet({
76
- * name: 'log-collector',
77
- * image: 'fluentd:v1.14',
78
- * containerPort: 24224,
79
- * hostNetwork: true
80
- * });
81
- * ```
82
- *
83
- * @since 2.4.0
84
- */
85
- addDaemonSet(spec) {
86
- return this.coreResources.addDaemonSet(spec);
87
- }
88
- /**
89
- * Creates a StatefulSet resource
90
- * @param spec - StatefulSet specification
91
- * @returns Created StatefulSet ApiObject
92
- *
93
- * @example
94
- * ```typescript
95
- * rutter.addStatefulSet({
96
- * name: 'database',
97
- * image: 'postgres:13',
98
- * replicas: 3,
99
- * serviceName: 'database-headless',
100
- * containerPort: 5432
101
- * });
102
- * ```
103
- *
104
- * @since 2.4.0
105
- */
106
- addStatefulSet(spec) {
107
- return this.coreResources.addStatefulSet(spec);
108
- }
109
- /**
110
- * Creates a Role resource for RBAC
111
- * @param spec - Role specification
112
- * @returns Created Role ApiObject
113
- *
114
- * @example
115
- * ```typescript
116
- * rutter.addRole({
117
- * name: 'pod-reader',
118
- * rules: [
119
- * {
120
- * apiGroups: [''],
121
- * resources: ['pods'],
122
- * verbs: ['get', 'list', 'watch']
123
- * }
124
- * ]
125
- * });
126
- * ```
127
- *
128
- * @since 2.4.0
129
- */
130
- addRole(spec) {
131
- return this.coreResources.addRole(spec);
132
- }
133
- /**
134
- * Creates a ClusterRole resource for cluster-wide RBAC
135
- * @param spec - ClusterRole specification
136
- * @returns Created ClusterRole ApiObject
137
- *
138
- * @example
139
- * ```typescript
140
- * rutter.addClusterRole({
141
- * name: 'cluster-reader',
142
- * rules: [
143
- * {
144
- * apiGroups: [''],
145
- * resources: ['nodes', 'namespaces'],
146
- * verbs: ['get', 'list', 'watch']
147
- * }
148
- * ]
149
- * });
150
- * ```
151
- *
152
- * @since 2.4.0
153
- */
154
- addClusterRole(spec) {
155
- return this.coreResources.addClusterRole(spec);
156
- }
157
- /**
158
- * Creates a RoleBinding resource for RBAC
159
- * @param spec - RoleBinding specification
160
- * @returns Created RoleBinding ApiObject
161
- *
162
- * @example
163
- * ```typescript
164
- * rutter.addRoleBinding({
165
- * name: 'pod-reader-binding',
166
- * roleRef: {
167
- * kind: 'Role',
168
- * name: 'pod-reader',
169
- * apiGroup: 'rbac.authorization.k8s.io'
170
- * },
171
- * subjects: [
172
- * {
173
- * kind: 'ServiceAccount',
174
- * name: 'my-service-account'
175
- * }
176
- * ]
177
- * });
178
- * ```
179
- *
180
- * @since 2.4.0
181
- */
182
- addRoleBinding(spec) {
183
- return this.coreResources.addRoleBinding(spec);
184
- }
185
- /**
186
- * Creates a ClusterRoleBinding resource for cluster-wide RBAC
187
- * @param spec - ClusterRoleBinding specification
188
- * @returns Created ClusterRoleBinding ApiObject
189
- *
190
- * @example
191
- * ```typescript
192
- * rutter.addClusterRoleBinding({
193
- * name: 'cluster-reader-binding',
194
- * roleRef: {
195
- * kind: 'ClusterRole',
196
- * name: 'cluster-reader',
197
- * apiGroup: 'rbac.authorization.k8s.io'
198
- * },
199
- * subjects: [
200
- * {
201
- * kind: 'ServiceAccount',
202
- * name: 'monitoring-agent',
203
- * namespace: 'monitoring'
204
- * }
205
- * ]
206
- * });
207
- * ```
208
- *
209
- * @since 2.4.0
210
- */
211
- addClusterRoleBinding(spec) {
212
- return this.coreResources.addClusterRoleBinding(spec);
213
- }
214
- /**
215
- * Creates a Job resource for batch workloads
216
- * @param spec - Job specification
217
- * @returns Created Job ApiObject
218
- *
219
- * @example
220
- * ```typescript
221
- * rutter.addJob({
222
- * name: 'data-processor',
223
- * image: 'busybox:1.35',
224
- * command: ['sh', '-c', 'echo "Processing..." && sleep 30'],
225
- * completions: 1
226
- * });
227
- * ```
228
- *
229
- * @since 2.4.0
230
- */
231
- addJob(spec) {
232
- return this.coreResources.addJob(spec);
233
- }
234
- /**
235
- * Creates a Service resource
236
- * @param spec - Service specification
237
- * @returns Created Service ApiObject
238
- *
239
- * @example
240
- * ```typescript
241
- * rutter.addService({
242
- * name: 'web-service',
243
- * port: 80,
244
- * targetPort: 8080,
245
- * type: 'ClusterIP'
246
- * });
247
- * ```
248
- *
249
- * @since 1.0.0
250
- */
251
- addService(spec) {
252
- return this.coreResources.addService(spec);
253
- }
254
- /**
255
- * Creates a ConfigMap resource
256
- * @param spec - ConfigMap specification
257
- * @returns Created ConfigMap ApiObject
258
- *
259
- * @example
260
- * ```typescript
261
- * rutter.addConfigMap({
262
- * name: 'app-config',
263
- * data: {
264
- * 'config.yaml': 'key: value',
265
- * 'app.properties': 'debug=true'
266
- * }
267
- * });
268
- * ```
269
- *
270
- * @since 1.0.0
271
- */
272
- addConfigMap(spec) {
273
- return this.coreResources.addConfigMap(spec);
274
- }
275
- /**
276
- * Creates a Secret resource
277
- * @param spec - Secret specification
278
- * @returns Created Secret ApiObject
279
- *
280
- * @example
281
- * ```typescript
282
- * rutter.addSecret({
283
- * name: 'app-secrets',
284
- * type: 'Opaque',
285
- * data: {
286
- * username: 'YWRtaW4=',
287
- * password: 'MWYyZDFlMmU2N2Rm'
288
- * }
289
- * });
290
- * ```
291
- *
292
- * @since 1.0.0
293
- */
294
- addSecret(spec) {
295
- return this.coreResources.addSecret(spec);
296
- }
297
- /**
298
- * Creates a ServiceAccount resource
299
- * @param spec - ServiceAccount specification
300
- * @returns Created ServiceAccount ApiObject
301
- *
302
- * @example
303
- * ```typescript
304
- * rutter.addServiceAccount({
305
- * name: 'app-service-account',
306
- * automountServiceAccountToken: false
307
- * });
308
- * ```
309
- *
310
- * @since 1.0.0
311
- */
312
- addServiceAccount(spec) {
313
- return this.coreResources.addServiceAccount(spec);
314
- }
315
- // Storage Resources
316
- /**
317
- * Creates a PersistentVolume resource
318
- * @param spec - PersistentVolume specification
319
- * @returns Created PersistentVolume ApiObject
320
- *
321
- * @example
322
- * ```typescript
323
- * rutter.addPersistentVolume({
324
- * name: 'data-pv',
325
- * capacity: '10Gi',
326
- * accessModes: ['ReadWriteOnce'],
327
- * storageClassName: 'fast-ssd'
328
- * });
329
- * ```
330
- *
331
- * @since 1.0.0
332
- */
333
- addPersistentVolume(spec) {
334
- return this.storageResources.addPersistentVolume(spec);
335
- }
336
- /**
337
- * Creates a PersistentVolumeClaim resource
338
- * @param spec - PersistentVolumeClaim specification
339
- * @returns Created PersistentVolumeClaim ApiObject
340
- *
341
- * @example
342
- * ```typescript
343
- * rutter.addPersistentVolumeClaim({
344
- * name: 'data-pvc',
345
- * size: '10Gi',
346
- * accessModes: ['ReadWriteOnce'],
347
- * storageClassName: 'fast-ssd'
348
- * });
349
- * ```
350
- *
351
- * @since 1.0.0
352
- */
353
- addPersistentVolumeClaim(spec) {
354
- return this.storageResources.addPersistentVolumeClaim(spec);
355
- }
356
- /**
357
- * Creates a StorageClass resource
358
- * @param spec - StorageClass specification
359
- * @returns Created StorageClass ApiObject
360
- *
361
- * @example
362
- * ```typescript
363
- * rutter.addStorageClass({
364
- * name: 'fast-ssd',
365
- * provisioner: 'kubernetes.io/aws-ebs',
366
- * parameters: {
367
- * type: 'gp3',
368
- * encrypted: 'true'
369
- * }
370
- * });
371
- * ```
372
- *
373
- * @since 1.0.0
374
- */
375
- addStorageClass(spec) {
376
- return this.storageResources.addStorageClass(spec);
377
- }
378
32
  // AWS Resources
379
33
  /**
380
34
  * Creates an AWS EBS StorageClass
@@ -423,8 +77,8 @@ export class Rutter {
423
77
  * @example
424
78
  * ```typescript
425
79
  * rutter.addAWSIRSAServiceAccount({
426
- * name: 'aws-service-account',
427
- * roleArn: 'arn:aws:iam::123456789012:role/MyRole'
80
+ * name: 'my-service-account',
81
+ * roleArn: 'arn:aws:iam::ACCOUNT_ID:role/MyRole'
428
82
  * });
429
83
  * ```
430
84
  *
@@ -458,116 +112,6 @@ export class Rutter {
458
112
  addAWSALBIngress(spec) {
459
113
  return this.awsResources.addALBIngress(spec);
460
114
  }
461
- // Azure Cloud Resources
462
- /**
463
- * Creates an Azure Disk StorageClass
464
- * @param spec - Azure Disk StorageClass specification
465
- * @returns Created StorageClass ApiObject
466
- *
467
- * @since 2.4.0
468
- */
469
- addAzureDiskStorageClass(spec) {
470
- return this.azureResources.addAzureDiskStorageClass(spec);
471
- }
472
- /**
473
- * Creates an Azure File StorageClass
474
- * @param spec - Azure File StorageClass specification
475
- * @returns Created StorageClass ApiObject
476
- *
477
- * @since 2.4.0
478
- */
479
- addAzureFileStorageClass(spec) {
480
- return this.azureResources.addAzureFileStorageClass(spec);
481
- }
482
- /**
483
- * Creates an Azure Application Gateway Ingress
484
- * @param spec - AGIC Ingress specification
485
- * @returns Created Ingress ApiObject
486
- *
487
- * @since 2.4.0
488
- */
489
- addAzureApplicationGatewayIngress(spec) {
490
- return this.azureResources.addApplicationGatewayIngress(spec);
491
- }
492
- /**
493
- * Creates an Azure Key Vault SecretProviderClass
494
- * @param spec - Azure Key Vault SecretProviderClass specification
495
- * @returns Created SecretProviderClass ApiObject
496
- *
497
- * @since 2.4.0
498
- */
499
- addAzureKeyVaultSecretProviderClass(spec) {
500
- return this.azureResources.addAzureKeyVaultSecretProviderClass(spec);
501
- }
502
- /**
503
- * Creates an Azure Container Registry ServiceAccount
504
- * @param spec - Azure ACR ServiceAccount specification
505
- * @returns Created ServiceAccount ApiObject
506
- *
507
- * @since 2.4.0
508
- */
509
- addAzureACRServiceAccount(spec) {
510
- return this.azureResources.addAzureACRServiceAccount(spec);
511
- }
512
- // GCP Cloud Resources
513
- /**
514
- * Creates a GCP Persistent Disk StorageClass
515
- * @param spec - GCP PD StorageClass specification
516
- * @returns Created StorageClass ApiObject
517
- *
518
- * @since 2.4.0
519
- */
520
- addGCPPersistentDiskStorageClass(spec) {
521
- return this.gcpResources.addPersistentDiskStorageClass(spec);
522
- }
523
- /**
524
- * Creates a GCP Filestore StorageClass
525
- * @param spec - GCP Filestore StorageClass specification
526
- * @returns Created StorageClass ApiObject
527
- *
528
- * @since 2.4.0
529
- */
530
- addGCPFilestoreStorageClass(spec) {
531
- return this.gcpResources.addFilestoreStorageClass(spec);
532
- }
533
- /**
534
- * Creates a GCE Ingress with Google Cloud Load Balancer
535
- * @param spec - GCE Ingress specification
536
- * @returns Created Ingress ApiObject
537
- *
538
- * @since 2.4.0
539
- */
540
- addGCPGCEIngress(spec) {
541
- return this.gcpResources.addGCEIngress(spec);
542
- }
543
- /**
544
- * Creates a ServiceAccount with Workload Identity annotations
545
- * @param spec - Workload Identity ServiceAccount specification
546
- * @returns Created ServiceAccount ApiObject
547
- *
548
- * @since 2.4.0
549
- */
550
- addGCPWorkloadIdentityServiceAccount(spec) {
551
- return this.gcpResources.addWorkloadIdentityServiceAccount(spec);
552
- }
553
- /**
554
- * Creates a ServiceAccount with GCP Artifact Registry access
555
- * @param spec - Artifact Registry ServiceAccount specification
556
- * @returns Created ServiceAccount ApiObject
557
- *
558
- * @example
559
- * ```typescript
560
- * rutter.addGCPArtifactRegistryServiceAccount({
561
- * name: 'artifact-registry-sa',
562
- * googleServiceAccount: 'my-gsa@project.iam.gserviceaccount.com'
563
- * });
564
- * ```
565
- *
566
- * @since 2.7.0
567
- */
568
- addGCPArtifactRegistryServiceAccount(spec) {
569
- return this.gcpResources.addArtifactRegistryServiceAccount(spec);
570
- }
571
115
  // AWS Karpenter Resources
572
116
  /**
573
117
  * Creates a Karpenter NodePool resource
@@ -696,235 +240,6 @@ export class Rutter {
696
240
  addKarpenterNodePoolWithScheduling(spec) {
697
241
  return this.karpenterResources.addKarpenterNodePoolWithScheduling(spec);
698
242
  }
699
- // Network Resources
700
- /**
701
- * Creates a NetworkPolicy resource
702
- * @param spec - NetworkPolicy specification
703
- * @returns Created NetworkPolicy ApiObject
704
- *
705
- * @example
706
- * ```typescript
707
- * rutter.addNetworkPolicy({
708
- * name: 'deny-all',
709
- * podSelector: {},
710
- * policyTypes: ['Ingress', 'Egress']
711
- * });
712
- * ```
713
- *
714
- * @since 2.7.0
715
- */
716
- addNetworkPolicy(spec) {
717
- return this.networkResources.addNetworkPolicy(spec);
718
- }
719
- /**
720
- * Creates a deny-all NetworkPolicy that blocks all traffic
721
- * @param name - Policy name
722
- * @param podSelector - Pod selector to apply policy to
723
- * @param labels - Optional labels
724
- * @param annotations - Optional annotations
725
- * @returns Created NetworkPolicy ApiObject
726
- *
727
- * @example
728
- * ```typescript
729
- * rutter.addDenyAllNetworkPolicy('deny-all', { matchLabels: { app: 'web' } });
730
- * ```
731
- *
732
- * @since 2.7.0
733
- */
734
- addDenyAllNetworkPolicy(name, podSelector = {}, labels, annotations) {
735
- return this.networkResources.addDenyAllNetworkPolicy(name, podSelector, labels, annotations);
736
- }
737
- /**
738
- * Creates a NetworkPolicy that allows traffic from specific pods
739
- * @param name - Policy name
740
- * @param podSelector - Pod selector to apply policy to
741
- * @param fromPodSelector - Pod selector for allowed source pods
742
- * @param ports - Optional ports to allow
743
- * @param labels - Optional labels
744
- * @param annotations - Optional annotations
745
- * @returns Created NetworkPolicy ApiObject
746
- *
747
- * @example
748
- * ```typescript
749
- * rutter.addAllowFromPodsNetworkPolicy(
750
- * 'allow-from-frontend',
751
- * { matchLabels: { app: 'backend' } },
752
- * { matchLabels: { app: 'frontend' } },
753
- * [{ port: 8080, protocol: 'TCP' }]
754
- * );
755
- * ```
756
- *
757
- * @since 2.7.0
758
- */
759
- addAllowFromPodsNetworkPolicy(name, podSelector, fromPodSelector, ports, labels, annotations) {
760
- return this.networkResources.addAllowFromPodsNetworkPolicy(name, podSelector, fromPodSelector, ports, labels, annotations);
761
- }
762
- /**
763
- * Creates a NetworkPolicy that allows traffic from specific namespaces
764
- * @param name - Policy name
765
- * @param podSelector - Pod selector to apply policy to
766
- * @param fromNamespaceSelector - Namespace selector for allowed source namespaces
767
- * @param ports - Optional ports to allow
768
- * @param labels - Optional labels
769
- * @param annotations - Optional annotations
770
- * @returns Created NetworkPolicy ApiObject
771
- *
772
- * @example
773
- * ```typescript
774
- * rutter.addAllowFromNamespaceNetworkPolicy(
775
- * 'allow-from-monitoring',
776
- * { matchLabels: { app: 'web' } },
777
- * { matchLabels: { name: 'monitoring' } },
778
- * [{ port: 9090, protocol: 'TCP' }]
779
- * );
780
- * ```
781
- *
782
- * @since 2.7.0
783
- */
784
- addAllowFromNamespaceNetworkPolicy(name, podSelector, fromNamespaceSelector, ports, labels, annotations) {
785
- return this.networkResources.addAllowFromNamespaceNetworkPolicy(name, podSelector, fromNamespaceSelector, ports, labels, annotations);
786
- }
787
- addIngress(specOrName, host, serviceName, servicePort, options) {
788
- if (typeof specOrName === 'string') {
789
- return this.createSimpleIngress(specOrName, host, serviceName, servicePort, options);
790
- }
791
- // Original spec-based overload
792
- return this.networkResources.addIngress(specOrName);
793
- }
794
- /**
795
- * Creates a simple Ingress with enhanced options
796
- * @private
797
- */
798
- createSimpleIngress(name, host, serviceName, servicePort, options) {
799
- const tlsSecretName = typeof options?.tls === 'string' ? options.tls : `${name}-tls`;
800
- const annotations = this.buildIngressAnnotations(options);
801
- const spec = {
802
- name,
803
- rules: [
804
- {
805
- host,
806
- http: {
807
- paths: [
808
- {
809
- path: options?.path || '/',
810
- pathType: options?.pathType || 'Prefix',
811
- backend: {
812
- service: {
813
- name: serviceName,
814
- port: { number: servicePort },
815
- },
816
- },
817
- },
818
- ],
819
- },
820
- },
821
- ],
822
- ...(options?.className && { ingressClassName: options.className }),
823
- ...(Object.keys(annotations).length > 0 && { annotations }),
824
- ...(options?.tls && {
825
- tls: [
826
- {
827
- hosts: [host],
828
- secretName: tlsSecretName,
829
- },
830
- ],
831
- }),
832
- };
833
- return this.networkResources.addIngress(spec);
834
- }
835
- /**
836
- * Builds annotations for Ingress based on options
837
- * @private
838
- */
839
- buildIngressAnnotations(options) {
840
- const annotations = { ...options?.annotations };
841
- this.addSSLRedirectAnnotation(annotations, options);
842
- this.addCertManagerAnnotation(annotations, options);
843
- this.addNginxAnnotations(annotations, options);
844
- return annotations;
845
- }
846
- /**
847
- * Adds SSL redirect annotation based on class and options
848
- * @private
849
- */
850
- addSSLRedirectAnnotation(annotations, options) {
851
- if (!options?.tls || options.sslRedirect === false)
852
- return;
853
- if (options.className === 'nginx' || !options.className) {
854
- annotations['nginx.ingress.kubernetes.io/ssl-redirect'] = 'true';
855
- }
856
- else if (options.className === 'alb') {
857
- annotations['alb.ingress.kubernetes.io/ssl-redirect'] = '443';
858
- }
859
- }
860
- /**
861
- * Adds cert-manager annotation if specified
862
- * @private
863
- */
864
- addCertManagerAnnotation(annotations, options) {
865
- if (options?.certManager) {
866
- annotations['cert-manager.io/cluster-issuer'] = options.certManager;
867
- }
868
- }
869
- /**
870
- * Adds NGINX-specific annotations
871
- * @private
872
- */
873
- addNginxAnnotations(annotations, options) {
874
- if (options?.className !== 'nginx' && options?.className)
875
- return;
876
- if (options?.cors) {
877
- annotations['nginx.ingress.kubernetes.io/enable-cors'] = 'true';
878
- }
879
- if (options?.bodySize) {
880
- annotations['nginx.ingress.kubernetes.io/proxy-body-size'] = options.bodySize;
881
- }
882
- if (options?.timeout) {
883
- annotations['nginx.ingress.kubernetes.io/proxy-read-timeout'] = options.timeout.toString();
884
- }
885
- if (options?.backendProtocol && options.backendProtocol !== 'HTTP') {
886
- annotations['nginx.ingress.kubernetes.io/backend-protocol'] = options.backendProtocol;
887
- }
888
- if (options?.rewriteTarget) {
889
- annotations['nginx.ingress.kubernetes.io/rewrite-target'] = options.rewriteTarget;
890
- }
891
- }
892
- /**
893
- * Creates a secure Ingress resource with TLS validation and environment-aware security defaults
894
- * @param spec - Enhanced Ingress specification with security options
895
- * @returns Created Ingress ApiObject
896
- *
897
- * @example
898
- * ```typescript
899
- * // Production Ingress with mandatory TLS
900
- * rutter.addSecureIngress({
901
- * name: 'secure-web-ingress',
902
- * environment: 'production',
903
- * tls: [{
904
- * hosts: ['secure.example.com'],
905
- * secretName: 'secure-example-tls'
906
- * }],
907
- * forceSSLRedirect: true,
908
- * rules: [{
909
- * host: 'secure.example.com',
910
- * http: {
911
- * paths: [{
912
- * path: '/',
913
- * pathType: 'Prefix',
914
- * backend: {
915
- * service: { name: 'web-service', port: { number: 80 } }
916
- * }
917
- * }]
918
- * }
919
- * }]
920
- * });
921
- * ```
922
- *
923
- * @since 2.8.0
924
- */
925
- addSecureIngress(spec) {
926
- return this.networkResources.addSecureIngress(spec);
927
- }
928
243
  // AWS ECR ServiceAccount
929
244
  /**
930
245
  * Creates a ServiceAccount with ECR access annotations
@@ -935,7 +250,7 @@ export class Rutter {
935
250
  * ```typescript
936
251
  * rutter.addAWSECRServiceAccount({
937
252
  * name: 'ecr-service-account',
938
- * roleArn: 'arn:aws:iam::123456789012:role/ECRAccessRole'
253
+ * roleArn: 'arn:aws:iam::ACCOUNT_ID:role/ECRAccessRole'
939
254
  * });
940
255
  * ```
941
256
  *
@@ -944,103 +259,21 @@ export class Rutter {
944
259
  addAWSECRServiceAccount(spec) {
945
260
  return this.awsResources.addECRServiceAccount(spec);
946
261
  }
947
- // Autoscaling Resources
948
- /**
949
- * Creates a Horizontal Pod Autoscaler (HPA) resource
950
- * @param spec - HPA specification
951
- * @returns Created HPA ApiObject
952
- *
953
- * @example
954
- * ```typescript
955
- * rutter.addHorizontalPodAutoscaler({
956
- * name: 'web-hpa',
957
- * scaleTargetRef: {
958
- * apiVersion: 'apps/v1',
959
- * kind: 'Deployment',
960
- * name: 'web-app'
961
- * },
962
- * minReplicas: 2,
963
- * maxReplicas: 10,
964
- * metrics: [{
965
- * type: 'Resource',
966
- * resource: {
967
- * name: 'cpu',
968
- * target: { type: 'Utilization', averageUtilization: 70 }
969
- * }
970
- * }]
971
- * });
972
- * ```
973
- *
974
- * @since 2.7.0
975
- */
976
- addHorizontalPodAutoscaler(spec) {
977
- return this.autoscalingResources.addHorizontalPodAutoscaler(spec);
978
- }
979
- /**
980
- * Creates a Vertical Pod Autoscaler (VPA) resource
981
- * @param spec - VPA specification
982
- * @returns Created VPA ApiObject
983
- *
984
- * @example
985
- * ```typescript
986
- * rutter.addVerticalPodAutoscaler({
987
- * name: 'web-vpa',
988
- * targetRef: {
989
- * apiVersion: 'apps/v1',
990
- * kind: 'Deployment',
991
- * name: 'web-app'
992
- * },
993
- * updatePolicy: { updateMode: 'Auto' }
994
- * });
995
- * ```
996
- *
997
- * @since 2.7.0
998
- */
999
- addVerticalPodAutoscaler(spec) {
1000
- return this.autoscalingResources.addVerticalPodAutoscaler(spec);
1001
- }
262
+ // Utility methods
1002
263
  /**
1003
- * Creates a CronJob resource
1004
- * @param spec - CronJob specification
1005
- * @returns Created CronJob ApiObject
264
+ * Adds a Kubernetes manifest to the chart using CDK8S
1006
265
  *
1007
- * @example
1008
- * ```typescript
1009
- * rutter.addCronJob({
1010
- * name: 'backup-job',
1011
- * schedule: '0 2 * * *',
1012
- * jobTemplate: {
1013
- * spec: {
1014
- * template: {
1015
- * spec: {
1016
- * containers: [{
1017
- * name: 'backup',
1018
- * image: 'backup:latest',
1019
- * command: ['backup.sh']
1020
- * }],
1021
- * restartPolicy: 'OnFailure'
1022
- * }
1023
- * }
1024
- * }
1025
- * }
1026
- * });
1027
- * ```
266
+ * This method allows you to add any Kubernetes manifest (CRDs, custom resources, etc.)
267
+ * either as YAML strings or as objects. The manifest will be processed using CDK8S
268
+ * for better type safety and validation.
1028
269
  *
1029
- * @since 2.7.0
1030
- */
1031
- addCronJob(spec) {
1032
- return this.autoscalingResources.addCronJob(spec);
1033
- }
1034
- // Utility methods
1035
- /**
1036
- * Adds a custom resource definition (CRD) YAML or object
1037
- * @param yamlOrObject - CRD YAML content string or object
1038
- * @param id - Unique identifier for the CRD
270
+ * @param yamlOrObject - Kubernetes manifest as YAML string or object
271
+ * @param id - Unique identifier for the manifest (required for multiple manifests)
1039
272
  *
1040
273
  * @example
1041
274
  * ```typescript
1042
- * // Using YAML string
1043
- * rutter.addCrd(`
275
+ * // Using YAML string for CRD
276
+ * rutter.addManifest(`
1044
277
  * apiVersion: apiextensions.k8s.io/v1
1045
278
  * kind: CustomResourceDefinition
1046
279
  * metadata:
@@ -1048,82 +281,144 @@ export class Rutter {
1048
281
  * spec:
1049
282
  * group: example.com
1050
283
  * versions: []
1051
- * `);
284
+ * `, 'my-crd');
1052
285
  *
1053
- * // Using object
1054
- * rutter.addCrd({
1055
- * apiVersion: 'apiextensions.k8s.io/v1',
1056
- * kind: 'CustomResourceDefinition',
1057
- * metadata: { name: 'example.com' },
1058
- * spec: { group: 'example.com', versions: [] }
1059
- * });
286
+ * // Using object for any Kubernetes resource
287
+ * rutter.addManifest({
288
+ * apiVersion: 'v1',
289
+ * kind: 'ConfigMap',
290
+ * metadata: { name: 'my-config' },
291
+ * data: { key: 'value' }
292
+ * }, 'my-configmap');
1060
293
  * ```
1061
294
  *
1062
- * @since 1.0.0
295
+ * @since 3.0.0
296
+ * @note Consider using cdk8s-plus constructs for better type safety when available
1063
297
  */
1064
- addCrd(yamlOrObject, id = 'crd') {
1065
- let yaml;
1066
- let crdObject;
298
+ addManifest(yamlOrObject, id) {
299
+ let manifestObject;
1067
300
  if (typeof yamlOrObject === 'string') {
1068
- // Validate YAML string
301
+ // Parse YAML string to object
1069
302
  try {
1070
- crdObject = YAML.parse(yamlOrObject);
1071
- yaml = yamlOrObject.trim();
303
+ manifestObject = YAML.parse(yamlOrObject);
1072
304
  }
1073
305
  catch (error) {
1074
- throw new Error(`Invalid YAML provided to addCrd(): ${error instanceof Error ? error.message : 'Unknown error'}`);
306
+ throw new Error(`Invalid YAML provided to addManifest(): ${error instanceof Error ? error.message : 'Unknown error'}`);
1075
307
  }
1076
308
  }
1077
309
  else if (typeof yamlOrObject === 'object' && yamlOrObject !== null) {
1078
- // Use object directly and convert to YAML
1079
- crdObject = yamlOrObject;
1080
- try {
1081
- yaml = YAML.stringify(yamlOrObject).trim();
1082
- }
1083
- catch (error) {
1084
- throw new Error(`Failed to convert object to YAML in addCrd(): ${error instanceof Error ? error.message : 'Unknown error'}`);
1085
- }
310
+ // Use object directly
311
+ manifestObject = yamlOrObject;
1086
312
  }
1087
313
  else {
1088
- throw new Error('addCrd() requires either a YAML string or an object');
314
+ throw new Error('addManifest() requires either a YAML string or an object');
1089
315
  }
1090
- // Ensure we have valid content
1091
- if (!yaml) {
1092
- throw new Error('addCrd() received empty or invalid content');
316
+ // Validate basic Kubernetes manifest structure
317
+ this.validateManifestStructure(manifestObject);
318
+ // Create CDK8S ApiObject from the manifest
319
+ return new ApiObject(this.chart, id, {
320
+ apiVersion: manifestObject['apiVersion'],
321
+ kind: manifestObject['kind'],
322
+ metadata: manifestObject['metadata'],
323
+ spec: manifestObject['spec'],
324
+ });
325
+ }
326
+ /**
327
+ * Adds a Kubernetes manifest wrapped in a Helm conditional
328
+ *
329
+ * This method creates a manifest that will only be rendered if the specified
330
+ * condition evaluates to true. The condition is checked against .Values in Helm.
331
+ *
332
+ * @param manifestObject - JavaScript object representing the Kubernetes manifest
333
+ * @param condition - Path to the condition value in .Values (e.g., 'createNamespace')
334
+ * @param id - Unique identifier for this manifest within the chart
335
+ * @returns The created ApiObject instance
336
+ * @throws {Error} If the manifest structure is incorrect or condition path is invalid
337
+ *
338
+ * @example
339
+ * ```typescript
340
+ * // Add a namespace that's only created if .Values.createNamespace is true
341
+ * rutter.addConditionalManifest(
342
+ * {
343
+ * apiVersion: 'v1',
344
+ * kind: 'Namespace',
345
+ * metadata: {
346
+ * name: valuesRef('namespace'),
347
+ * labels: {
348
+ * environment: conditionalRef('environment', 'environment')
349
+ * }
350
+ * }
351
+ * },
352
+ * 'createNamespace',
353
+ * 'namespace'
354
+ * );
355
+ * ```
356
+ *
357
+ * @since 2.7.4
358
+ */
359
+ addConditionalManifest(manifestObject, condition, id) {
360
+ // Validate condition path
361
+ if (!condition || typeof condition !== 'string') {
362
+ throw new Error('Condition must be a non-empty string');
1093
363
  }
1094
- // Validate CRD structure
1095
- this.validateCrdStructure(crdObject);
1096
- this.assets.push({ id, yaml, target: 'crds' });
364
+ // Validate basic Kubernetes manifest structure
365
+ this.validateManifestStructure(manifestObject);
366
+ // Store the manifest as a conditional asset that will be processed during write
367
+ const conditionalAsset = {
368
+ id,
369
+ yaml: `{{- if .Values.${condition} }}\n${YAML.stringify(manifestObject)}{{- end }}`,
370
+ target: 'templates',
371
+ };
372
+ this.assets.push(conditionalAsset);
373
+ // Create a placeholder CDK8S ApiObject for consistency
374
+ return new ApiObject(this.chart, id, {
375
+ apiVersion: manifestObject['apiVersion'],
376
+ kind: manifestObject['kind'],
377
+ metadata: {
378
+ ...(manifestObject['metadata'] || {}),
379
+ annotations: {
380
+ ...(manifestObject['metadata']?.['annotations'] || {}),
381
+ 'timonel.sh/conditional': condition,
382
+ },
383
+ },
384
+ spec: manifestObject['spec'],
385
+ });
1097
386
  }
1098
387
  /**
1099
- * Validates CRD structure according to Kubernetes specification
1100
- * @param crd - CRD object to validate
388
+ * Validates the structure of a Kubernetes manifest object
389
+ * @param manifest - The manifest object to validate
1101
390
  * @private
1102
- * @since 2.5.0
391
+ * @since 3.0.0
1103
392
  */
1104
- validateCrdStructure(crd) {
1105
- if (crd['apiVersion'] !== 'apiextensions.k8s.io/v1') {
1106
- throw new Error('CRD must use apiVersion: apiextensions.k8s.io/v1');
393
+ validateManifestStructure(manifest) {
394
+ if (!manifest['apiVersion'] || typeof manifest['apiVersion'] !== 'string') {
395
+ throw new Error('Kubernetes manifest must have a valid apiVersion');
1107
396
  }
1108
- if (crd['kind'] !== 'CustomResourceDefinition') {
1109
- throw new Error('CRD must have kind: CustomResourceDefinition');
397
+ if (!manifest['kind'] || typeof manifest['kind'] !== 'string') {
398
+ throw new Error('Kubernetes manifest must have a valid kind');
1110
399
  }
1111
- if (!crd['metadata'] || typeof crd['metadata'] !== 'object' || crd['metadata'] === null) {
1112
- throw new Error('CRD must have a metadata object');
400
+ if (!manifest['metadata'] ||
401
+ typeof manifest['metadata'] !== 'object' ||
402
+ manifest['metadata'] === null) {
403
+ throw new Error('Kubernetes manifest must have valid metadata');
1113
404
  }
1114
- const metadata = crd['metadata'];
405
+ const metadata = manifest['metadata'];
1115
406
  if (!metadata['name'] || typeof metadata['name'] !== 'string') {
1116
- throw new Error('CRD must have a valid metadata.name');
1117
- }
1118
- if (!crd['spec'] || typeof crd['spec'] !== 'object' || crd['spec'] === null) {
1119
- throw new Error('CRD must have a spec object');
407
+ throw new Error('Kubernetes manifest metadata must have a valid name');
1120
408
  }
1121
- const spec = crd['spec'];
1122
- if (!spec['group'] || typeof spec['group'] !== 'string') {
1123
- throw new Error('CRD must have a valid spec.group');
1124
- }
1125
- if (!Array.isArray(spec['versions']) || spec['versions'].length === 0) {
1126
- throw new Error('CRD must have at least one version in spec.versions');
409
+ // Additional validation for specific resource types
410
+ const kind = manifest['kind'];
411
+ if (kind === 'CustomResourceDefinition') {
412
+ if (!manifest['spec'] || typeof manifest['spec'] !== 'object' || manifest['spec'] === null) {
413
+ throw new Error('CustomResourceDefinition must have a valid spec');
414
+ }
415
+ const spec = manifest['spec'];
416
+ if (!spec['group'] || typeof spec['group'] !== 'string') {
417
+ throw new Error('CustomResourceDefinition spec must have a valid group');
418
+ }
419
+ if (!Array.isArray(spec['versions']) || spec['versions'].length === 0) {
420
+ throw new Error('CustomResourceDefinition must have at least one version in spec.versions');
421
+ }
1127
422
  }
1128
423
  }
1129
424
  /**
@@ -1169,6 +464,13 @@ export class Rutter {
1169
464
  * @since 2.4.0
1170
465
  */
1171
466
  toSynthArray() {
467
+ // Get ApiObject IDs before synthesis
468
+ const apiObjectIds = [];
469
+ for (const child of this.chart.node.children) {
470
+ if (child instanceof ApiObject) {
471
+ apiObjectIds.push(child.node.id);
472
+ }
473
+ }
1172
474
  // Use cdk8s Testing.synth to obtain manifest objects
1173
475
  const manifestObjs = Testing.synth(this.chart);
1174
476
  // Enforce common labels best-practice on all rendered objects
@@ -1200,7 +502,7 @@ export class Rutter {
1200
502
  if (this.props.singleManifestFile) {
1201
503
  // Combine all resources into single manifest
1202
504
  const combinedYaml = enriched
1203
- .map((obj) => YAML.stringify(obj).trim())
505
+ .map((obj) => this.processHelmTemplates(YAML.stringify(obj).trim()))
1204
506
  .filter(Boolean)
1205
507
  .join('\n---\n');
1206
508
  const manifestId = this.props.manifestPrefix ?? 'manifests';
@@ -1209,9 +511,12 @@ export class Rutter {
1209
511
  else {
1210
512
  // Create separate files for each resource (default behavior)
1211
513
  enriched.forEach((obj, index) => {
1212
- const yaml = YAML.stringify(obj).trim();
514
+ const yaml = this.processHelmTemplates(YAML.stringify(obj).trim());
1213
515
  if (yaml) {
1214
- const manifestId = generateManifestName(obj, this.props.namingStrategy ?? 'descriptive', index + 1, this.props.manifestPrefix);
516
+ // Use descriptive name from ApiObject ID if available, otherwise fallback to generic name
517
+ // eslint-disable-next-line security/detect-object-injection
518
+ const apiObjectId = apiObjectIds[index];
519
+ const manifestId = apiObjectId || `manifest-${index + 1}`;
1215
520
  synthAssets.push({ id: manifestId, yaml });
1216
521
  }
1217
522
  });
@@ -1222,6 +527,21 @@ export class Rutter {
1222
527
  });
1223
528
  return synthAssets;
1224
529
  }
530
+ /**
531
+ * Process YAML content to remove quotes from Helm templates
532
+ * This fixes the issue where YAML.stringify adds quotes around Helm template expressions
533
+ * Removes quotes from all Helm template expressions
534
+ */
535
+ processHelmTemplates(yaml) {
536
+ // Remove quotes from Helm template expressions
537
+ // Match patterns like "{{ .Values.replicas | int }}", "{{ .Release.Name }}", etc.
538
+ // Handle both key-value pairs and standalone values
539
+ return yaml
540
+ .replace(/": "\{\{([^}]+)\}\}"/g, ': {{ $1 }}')
541
+ .replace(/: "\{\{([^}]+)\}\}"/g, ': {{ $1 }}')
542
+ .replace(/"\{\{([^}]+)\}\}"/g, '{{ $1 }}')
543
+ .replace(/": "\.nan"/g, ': .nan');
544
+ }
1225
545
  /**
1226
546
  * Writes the Helm chart to the specified output directory
1227
547
  * @param outDir - Output directory path