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,1115 +0,0 @@
1
- import { ApiObject } from 'cdk8s';
2
- import { BaseResourceProvider } from '../baseResourceProvider.js';
3
- import { include } from '../../helm.js';
4
- /**
5
- * Core Kubernetes resources provider
6
- * Handles fundamental Kubernetes resources like Deployments, Services, ConfigMaps, etc.
7
- *
8
- * @since 2.4.0
9
- */
10
- export class CoreResources extends BaseResourceProvider {
11
- /**
12
- * Creates a Deployment resource
13
- * @param spec - Deployment specification
14
- * @returns Created Deployment ApiObject
15
- *
16
- * @example
17
- * ```typescript
18
- * coreResources.addDeployment({
19
- * name: 'web-app',
20
- * image: 'nginx:1.21',
21
- * replicas: 3,
22
- * containerPort: 80
23
- * });
24
- * ```
25
- *
26
- * @since 2.4.0
27
- */
28
- addDeployment(spec) {
29
- const matchLabels = spec.matchLabels ?? {
30
- [CoreResources.LABEL_NAME]: include(CoreResources.HELPER_NAME),
31
- };
32
- const labels = {
33
- ...matchLabels,
34
- ...spec.labels,
35
- };
36
- const containers = [
37
- {
38
- name: spec.name,
39
- image: spec.image,
40
- ...(spec.containerPort ? { ports: [{ containerPort: spec.containerPort }] } : {}),
41
- ...(spec.env ? { env: spec.env } : {}),
42
- ...(spec.resources ? { resources: spec.resources } : {}),
43
- ...(spec.volumeMounts ? { volumeMounts: spec.volumeMounts } : {}),
44
- ...(spec.securityContext ? { securityContext: spec.securityContext } : {}),
45
- },
46
- ];
47
- const deploymentSpec = {
48
- replicas: this.normalizeReplicas(spec.replicas),
49
- selector: { matchLabels },
50
- template: {
51
- metadata: { labels },
52
- spec: {
53
- containers,
54
- ...(spec.volumes ? { volumes: spec.volumes } : {}),
55
- ...(spec.serviceAccountName ? { serviceAccountName: spec.serviceAccountName } : {}),
56
- ...(spec.securityContext ? { securityContext: spec.securityContext } : {}),
57
- ...(spec.nodeSelector ? { nodeSelector: spec.nodeSelector } : {}),
58
- ...(spec.tolerations ? { tolerations: spec.tolerations } : {}),
59
- ...(spec.affinity ? { affinity: spec.affinity } : {}),
60
- },
61
- },
62
- };
63
- return new ApiObject(this.chart, spec.name, {
64
- apiVersion: CoreResources.APPS_API_VERSION,
65
- kind: 'Deployment',
66
- metadata: {
67
- name: spec.name,
68
- ...(labels && Object.keys(labels).length > 0 ? { labels } : {}),
69
- ...(spec.annotations && Object.keys(spec.annotations).length > 0
70
- ? { annotations: spec.annotations }
71
- : {}),
72
- },
73
- spec: deploymentSpec,
74
- });
75
- }
76
- /**
77
- * Creates a Service resource
78
- * @param spec - Service specification
79
- * @returns Created Service ApiObject
80
- *
81
- * @example
82
- * ```typescript
83
- * coreResources.addService({
84
- * name: 'web-service',
85
- * port: 80,
86
- * targetPort: 8080,
87
- * type: 'ClusterIP'
88
- * });
89
- * ```
90
- *
91
- * @since 2.4.0
92
- */
93
- addService(spec) {
94
- const selector = spec.selector ?? {
95
- [CoreResources.LABEL_NAME]: include(CoreResources.HELPER_NAME),
96
- };
97
- const ports = spec.ports ?? [
98
- {
99
- port: spec.port,
100
- targetPort: spec.targetPort ?? spec.port,
101
- ...(spec.protocol ? { protocol: spec.protocol } : {}),
102
- ...(spec.name ? { name: spec.name } : {}),
103
- },
104
- ];
105
- const serviceSpec = {
106
- selector,
107
- ports,
108
- ...(spec.type ? { type: spec.type } : {}),
109
- ...(spec.clusterIP ? { clusterIP: spec.clusterIP } : {}),
110
- ...(spec.externalIPs ? { externalIPs: spec.externalIPs } : {}),
111
- ...(spec.loadBalancerIP ? { loadBalancerIP: spec.loadBalancerIP } : {}),
112
- ...(spec.sessionAffinity ? { sessionAffinity: spec.sessionAffinity } : {}),
113
- };
114
- return new ApiObject(this.chart, spec.name, {
115
- apiVersion: CoreResources.CORE_API_VERSION,
116
- kind: 'Service',
117
- metadata: {
118
- name: spec.name,
119
- ...(spec.labels ? { labels: spec.labels } : {}),
120
- ...(spec.annotations ? { annotations: spec.annotations } : {}),
121
- },
122
- spec: serviceSpec,
123
- });
124
- }
125
- /**
126
- * Creates a ConfigMap resource
127
- * @param spec - ConfigMap specification
128
- * @returns Created ConfigMap ApiObject
129
- *
130
- * @example
131
- * ```typescript
132
- * coreResources.addConfigMap({
133
- * name: 'app-config',
134
- * data: {
135
- * 'config.yaml': 'key: value',
136
- * 'app.properties': 'debug=true'
137
- * }
138
- * });
139
- * ```
140
- *
141
- * @since 2.4.0
142
- */
143
- addConfigMap(spec) {
144
- return new ApiObject(this.chart, spec.name, {
145
- apiVersion: CoreResources.CORE_API_VERSION,
146
- kind: 'ConfigMap',
147
- metadata: {
148
- name: spec.name,
149
- ...(spec.labels ? { labels: spec.labels } : {}),
150
- ...(spec.annotations ? { annotations: spec.annotations } : {}),
151
- },
152
- ...(spec.data ? { data: spec.data } : {}),
153
- ...(spec.binaryData ? { binaryData: spec.binaryData } : {}),
154
- });
155
- }
156
- /**
157
- * Creates a Secret resource
158
- * @param spec - Secret specification
159
- * @returns Created Secret ApiObject
160
- *
161
- * @example
162
- * ```typescript
163
- * coreResources.addSecret({
164
- * name: 'app-secrets',
165
- * type: 'Opaque',
166
- * data: {
167
- * username: 'YWRtaW4=',
168
- * password: 'MWYyZDFlMmU2N2Rm'
169
- * }
170
- * });
171
- * ```
172
- *
173
- * @since 2.4.0
174
- */
175
- addSecret(spec) {
176
- // Validate and encode base64 data if needed
177
- const processedData = spec.data ? this.validateAndEncodeSecretData(spec.data) : undefined;
178
- return new ApiObject(this.chart, spec.name, {
179
- apiVersion: CoreResources.CORE_API_VERSION,
180
- kind: 'Secret',
181
- metadata: {
182
- name: spec.name,
183
- ...(spec.labels ? { labels: spec.labels } : {}),
184
- ...(spec.annotations ? { annotations: spec.annotations } : {}),
185
- },
186
- type: spec.type ?? 'Opaque',
187
- ...(processedData ? { data: processedData } : {}),
188
- ...(spec.stringData ? { stringData: spec.stringData } : {}),
189
- });
190
- }
191
- /**
192
- * Validates and encodes Secret data as base64
193
- * @param data - Raw secret data
194
- * @returns Base64 encoded data
195
- * @private
196
- * @since 2.5.0
197
- */
198
- validateAndEncodeSecretData(data) {
199
- const encodedData = {};
200
- for (const [key, value] of Object.entries(data)) {
201
- // Check if value is already base64 encoded (basic validation)
202
- if (this.isBase64Encoded(value)) {
203
- // eslint-disable-next-line security/detect-object-injection -- Safe: key is from Object.entries iteration
204
- encodedData[key] = value;
205
- }
206
- else {
207
- // Encode as base64
208
- // eslint-disable-next-line security/detect-object-injection -- Safe: key is from Object.entries iteration
209
- encodedData[key] = Buffer.from(value, 'utf8').toString('base64');
210
- }
211
- }
212
- return encodedData;
213
- }
214
- /**
215
- * Checks if a string is base64 encoded
216
- * @param str - String to check
217
- * @returns True if string appears to be base64 encoded
218
- * @private
219
- * @since 2.5.0
220
- */
221
- isBase64Encoded(str) {
222
- // Basic base64 validation - checks for valid base64 characters and padding
223
- const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
224
- if (!base64Regex.test(str)) {
225
- return false;
226
- }
227
- try {
228
- // Attempt decode without exposing the decoded content
229
- Buffer.from(str, 'base64');
230
- return true;
231
- }
232
- catch {
233
- return false;
234
- }
235
- }
236
- /**
237
- * Creates a ServiceAccount resource
238
- * @param spec - ServiceAccount specification
239
- * @returns Created ServiceAccount ApiObject
240
- *
241
- * @example
242
- * ```typescript
243
- * coreResources.addServiceAccount({
244
- * name: 'app-service-account',
245
- * automountServiceAccountToken: false
246
- * });
247
- * ```
248
- *
249
- * @since 2.4.0
250
- */
251
- addServiceAccount(spec) {
252
- return new ApiObject(this.chart, spec.name, {
253
- apiVersion: CoreResources.CORE_API_VERSION,
254
- kind: 'ServiceAccount',
255
- metadata: {
256
- name: spec.name,
257
- ...(spec.labels ? { labels: spec.labels } : {}),
258
- ...(spec.annotations ? { annotations: spec.annotations } : {}),
259
- },
260
- ...(spec.automountServiceAccountToken !== undefined
261
- ? { automountServiceAccountToken: spec.automountServiceAccountToken }
262
- : {}),
263
- ...(spec.imagePullSecrets ? { imagePullSecrets: spec.imagePullSecrets } : {}),
264
- ...(spec.secrets ? { secrets: spec.secrets } : {}),
265
- });
266
- }
267
- /**
268
- * Creates a DaemonSet resource
269
- * @param spec - DaemonSet specification
270
- * @returns Created DaemonSet ApiObject
271
- *
272
- * @example
273
- * ```typescript
274
- * coreResources.addDaemonSet({
275
- * name: 'log-collector',
276
- * image: 'fluentd:v1.14',
277
- * containerPort: 24224,
278
- * hostNetwork: true
279
- * });
280
- * ```
281
- *
282
- * @since 2.4.0
283
- */
284
- addDaemonSet(spec) {
285
- const matchLabels = spec.matchLabels ?? {
286
- [CoreResources.LABEL_NAME]: include(CoreResources.HELPER_NAME),
287
- };
288
- const daemonSetSpec = this.buildDaemonSetSpec(spec, matchLabels);
289
- const labels = {
290
- [CoreResources.LABEL_NAME]: include(CoreResources.HELPER_NAME),
291
- ...(spec.labels || {}),
292
- };
293
- return new ApiObject(this.chart, spec.name, {
294
- apiVersion: CoreResources.APPS_API_VERSION,
295
- kind: 'DaemonSet',
296
- metadata: {
297
- name: spec.name,
298
- ...(labels && Object.keys(labels).length > 0 ? { labels } : {}),
299
- ...(spec.annotations && Object.keys(spec.annotations).length > 0
300
- ? { annotations: spec.annotations }
301
- : {}),
302
- },
303
- spec: daemonSetSpec,
304
- });
305
- }
306
- /**
307
- * Builds DaemonSet specification object
308
- * @private
309
- */
310
- buildDaemonSetSpec(spec, matchLabels) {
311
- const containers = this.buildDaemonSetContainers(spec);
312
- const podSpec = this.buildDaemonSetPodSpec(spec, containers);
313
- return {
314
- selector: { matchLabels },
315
- template: {
316
- metadata: {
317
- labels: { ...matchLabels, ...(spec.labels || {}) },
318
- ...(spec.annotations && Object.keys(spec.annotations).length > 0
319
- ? { annotations: spec.annotations }
320
- : {}),
321
- },
322
- spec: podSpec,
323
- },
324
- ...(spec.updateStrategy && { updateStrategy: spec.updateStrategy }),
325
- };
326
- }
327
- /**
328
- * Builds container specification for DaemonSet
329
- * @private
330
- */
331
- buildDaemonSetContainers(spec) {
332
- const container = {
333
- name: spec.name,
334
- image: spec.image,
335
- };
336
- if (spec.containerPort) {
337
- container['ports'] = [{ containerPort: spec.containerPort }];
338
- }
339
- if (spec.env) {
340
- container['env'] = spec.env;
341
- }
342
- if (spec.resources) {
343
- container['resources'] = spec.resources;
344
- }
345
- if (spec.volumeMounts) {
346
- container['volumeMounts'] = spec.volumeMounts;
347
- }
348
- if (spec.securityContext) {
349
- container['securityContext'] = spec.securityContext;
350
- }
351
- return [container];
352
- }
353
- /**
354
- * Builds pod specification for DaemonSet
355
- * @private
356
- */
357
- buildDaemonSetPodSpec(spec, containers) {
358
- const podSpec = {
359
- containers,
360
- };
361
- if (spec.serviceAccountName) {
362
- podSpec['serviceAccountName'] = spec.serviceAccountName;
363
- }
364
- if (spec.nodeSelector) {
365
- podSpec['nodeSelector'] = spec.nodeSelector;
366
- }
367
- if (spec.tolerations) {
368
- podSpec['tolerations'] = spec.tolerations;
369
- }
370
- if (spec.affinity) {
371
- podSpec['affinity'] = spec.affinity;
372
- }
373
- if (spec.volumes) {
374
- podSpec['volumes'] = spec.volumes;
375
- }
376
- if (spec.hostNetwork) {
377
- podSpec['hostNetwork'] = spec.hostNetwork;
378
- }
379
- if (spec.hostPID) {
380
- podSpec['hostPID'] = spec.hostPID;
381
- }
382
- if (spec.dnsPolicy) {
383
- podSpec['dnsPolicy'] = spec.dnsPolicy;
384
- }
385
- if (spec.priorityClassName) {
386
- podSpec['priorityClassName'] = spec.priorityClassName;
387
- }
388
- return podSpec;
389
- }
390
- /**
391
- * Creates a StatefulSet resource
392
- * @param spec - StatefulSet specification
393
- * @returns Created StatefulSet ApiObject
394
- *
395
- * @example
396
- * ```typescript
397
- * coreResources.addStatefulSet({
398
- * name: 'database',
399
- * image: 'postgres:13',
400
- * replicas: 3,
401
- * containerPort: 5432,
402
- * serviceName: 'database-headless'
403
- * });
404
- * ```
405
- *
406
- * @since 2.4.0
407
- */
408
- addStatefulSet(spec) {
409
- const matchLabels = spec.matchLabels ?? {
410
- [CoreResources.LABEL_NAME]: include(CoreResources.HELPER_NAME),
411
- };
412
- const statefulSetSpec = this.buildStatefulSetSpec(spec, matchLabels);
413
- const labels = {
414
- [CoreResources.LABEL_NAME]: include(CoreResources.HELPER_NAME),
415
- ...(spec.labels || {}),
416
- };
417
- return new ApiObject(this.chart, spec.name, {
418
- apiVersion: CoreResources.APPS_API_VERSION,
419
- kind: 'StatefulSet',
420
- metadata: {
421
- name: spec.name,
422
- ...(labels && Object.keys(labels).length > 0 ? { labels } : {}),
423
- ...(spec.annotations && Object.keys(spec.annotations).length > 0
424
- ? { annotations: spec.annotations }
425
- : {}),
426
- },
427
- spec: statefulSetSpec,
428
- });
429
- }
430
- /**
431
- * Builds StatefulSet specification object
432
- * @private
433
- */
434
- buildStatefulSetSpec(spec, matchLabels) {
435
- const containers = this.buildStatefulSetContainers(spec);
436
- const podSpec = this.buildStatefulSetPodSpec(spec, containers);
437
- const statefulSetSpec = {
438
- replicas: this.normalizeReplicas(spec.replicas),
439
- selector: { matchLabels },
440
- serviceName: spec.serviceName,
441
- template: {
442
- metadata: {
443
- labels: { ...matchLabels, ...(spec.labels || {}) },
444
- ...(spec.annotations && Object.keys(spec.annotations).length > 0
445
- ? { annotations: spec.annotations }
446
- : {}),
447
- },
448
- spec: podSpec,
449
- },
450
- };
451
- if (spec.volumeClaimTemplates) {
452
- statefulSetSpec['volumeClaimTemplates'] = spec.volumeClaimTemplates;
453
- }
454
- if (spec.updateStrategy) {
455
- statefulSetSpec['updateStrategy'] = spec.updateStrategy;
456
- }
457
- if (spec.podManagementPolicy) {
458
- statefulSetSpec['podManagementPolicy'] = spec.podManagementPolicy;
459
- }
460
- if (spec.revisionHistoryLimit !== undefined) {
461
- statefulSetSpec['revisionHistoryLimit'] = spec.revisionHistoryLimit;
462
- }
463
- return statefulSetSpec;
464
- }
465
- /**
466
- * Builds container specification for StatefulSet
467
- * @private
468
- */
469
- buildStatefulSetContainers(spec) {
470
- const container = {
471
- name: spec.name,
472
- image: spec.image,
473
- };
474
- if (spec.containerPort) {
475
- container['ports'] = [{ containerPort: spec.containerPort }];
476
- }
477
- if (spec.env) {
478
- container['env'] = spec.env;
479
- }
480
- if (spec.resources) {
481
- container['resources'] = spec.resources;
482
- }
483
- if (spec.volumeMounts) {
484
- container['volumeMounts'] = spec.volumeMounts;
485
- }
486
- if (spec.securityContext) {
487
- container['securityContext'] = spec.securityContext;
488
- }
489
- if (spec.livenessProbe) {
490
- container['livenessProbe'] = spec.livenessProbe;
491
- }
492
- if (spec.readinessProbe) {
493
- container['readinessProbe'] = spec.readinessProbe;
494
- }
495
- return [container];
496
- }
497
- /**
498
- * Builds pod specification for StatefulSet
499
- * @private
500
- */
501
- buildStatefulSetPodSpec(spec, containers) {
502
- const podSpec = {
503
- containers,
504
- };
505
- if (spec.serviceAccountName) {
506
- podSpec['serviceAccountName'] = spec.serviceAccountName;
507
- }
508
- if (spec.nodeSelector) {
509
- podSpec['nodeSelector'] = spec.nodeSelector;
510
- }
511
- if (spec.tolerations) {
512
- podSpec['tolerations'] = spec.tolerations;
513
- }
514
- if (spec.affinity) {
515
- podSpec['affinity'] = spec.affinity;
516
- }
517
- if (spec.volumes) {
518
- podSpec['volumes'] = spec.volumes;
519
- }
520
- if (spec.initContainers) {
521
- podSpec['initContainers'] = spec.initContainers;
522
- }
523
- if (spec.terminationGracePeriodSeconds !== undefined) {
524
- podSpec['terminationGracePeriodSeconds'] = spec.terminationGracePeriodSeconds;
525
- }
526
- return podSpec;
527
- }
528
- /**
529
- * Creates a Role resource for RBAC
530
- * @param spec - Role specification
531
- * @returns Created Role ApiObject
532
- *
533
- * @example
534
- * ```typescript
535
- * coreResources.addRole({
536
- * name: 'pod-reader',
537
- * rules: [
538
- * {
539
- * apiGroups: [''],
540
- * resources: ['pods'],
541
- * verbs: ['get', 'list', 'watch']
542
- * }
543
- * ]
544
- * });
545
- * ```
546
- *
547
- * @since 2.4.0
548
- */
549
- addRole(spec) {
550
- const labels = {
551
- [CoreResources.LABEL_NAME]: include(CoreResources.HELPER_NAME),
552
- ...(spec.labels || {}),
553
- };
554
- return new ApiObject(this.chart, spec.name, {
555
- apiVersion: CoreResources.RBAC_API_VERSION,
556
- kind: 'Role',
557
- metadata: {
558
- name: spec.name,
559
- ...(spec.namespace && { namespace: spec.namespace }),
560
- ...(labels && Object.keys(labels).length > 0 ? { labels } : {}),
561
- ...(spec.annotations && Object.keys(spec.annotations).length > 0
562
- ? { annotations: spec.annotations }
563
- : {}),
564
- },
565
- rules: spec.rules,
566
- });
567
- }
568
- /**
569
- * Creates a ClusterRole resource for cluster-wide RBAC
570
- * @param spec - ClusterRole specification
571
- * @returns Created ClusterRole ApiObject
572
- *
573
- * @example
574
- * ```typescript
575
- * coreResources.addClusterRole({
576
- * name: 'cluster-admin',
577
- * rules: [
578
- * {
579
- * apiGroups: ['*'],
580
- * resources: ['*'],
581
- * verbs: ['*']
582
- * }
583
- * ]
584
- * });
585
- * ```
586
- *
587
- * @since 2.4.0
588
- */
589
- addClusterRole(spec) {
590
- const labels = {
591
- [CoreResources.LABEL_NAME]: include(CoreResources.HELPER_NAME),
592
- ...(spec.labels || {}),
593
- };
594
- return new ApiObject(this.chart, spec.name, {
595
- apiVersion: CoreResources.RBAC_API_VERSION,
596
- kind: 'ClusterRole',
597
- metadata: {
598
- name: spec.name,
599
- ...(labels && Object.keys(labels).length > 0 ? { labels } : {}),
600
- ...(spec.annotations && Object.keys(spec.annotations).length > 0
601
- ? { annotations: spec.annotations }
602
- : {}),
603
- },
604
- rules: spec.rules,
605
- });
606
- }
607
- /**
608
- * Creates a RoleBinding resource for RBAC
609
- * @param spec - RoleBinding specification
610
- * @returns Created RoleBinding ApiObject
611
- *
612
- * @example
613
- * ```typescript
614
- * coreResources.addRoleBinding({
615
- * name: 'pod-reader-binding',
616
- * roleRef: {
617
- * kind: 'Role',
618
- * name: 'pod-reader',
619
- * apiGroup: 'rbac.authorization.k8s.io'
620
- * },
621
- * subjects: [
622
- * {
623
- * kind: 'ServiceAccount',
624
- * name: 'my-service-account',
625
- * namespace: 'default'
626
- * }
627
- * ]
628
- * });
629
- * ```
630
- *
631
- * @since 2.4.0
632
- */
633
- addRoleBinding(spec) {
634
- const labels = {
635
- [CoreResources.LABEL_NAME]: include(CoreResources.HELPER_NAME),
636
- ...(spec.labels || {}),
637
- };
638
- return new ApiObject(this.chart, spec.name, {
639
- apiVersion: CoreResources.RBAC_API_VERSION,
640
- kind: 'RoleBinding',
641
- metadata: {
642
- name: spec.name,
643
- ...(spec.namespace && { namespace: spec.namespace }),
644
- ...(labels && Object.keys(labels).length > 0 ? { labels } : {}),
645
- ...(spec.annotations && Object.keys(spec.annotations).length > 0
646
- ? { annotations: spec.annotations }
647
- : {}),
648
- },
649
- roleRef: spec.roleRef,
650
- subjects: spec.subjects,
651
- });
652
- }
653
- /**
654
- * Creates a ClusterRoleBinding resource for cluster-wide RBAC
655
- * @param spec - ClusterRoleBinding specification
656
- * @returns Created ClusterRoleBinding ApiObject
657
- *
658
- * @example
659
- * ```typescript
660
- * coreResources.addClusterRoleBinding({
661
- * name: 'cluster-admin-binding',
662
- * roleRef: {
663
- * kind: 'ClusterRole',
664
- * name: 'cluster-admin',
665
- * apiGroup: 'rbac.authorization.k8s.io'
666
- * },
667
- * subjects: [
668
- * {
669
- * kind: 'ServiceAccount',
670
- * name: 'admin-user',
671
- * namespace: 'kube-system'
672
- * }
673
- * ]
674
- * });
675
- * ```
676
- *
677
- * @since 2.4.0
678
- */
679
- addClusterRoleBinding(spec) {
680
- const labels = {
681
- [CoreResources.LABEL_NAME]: include(CoreResources.HELPER_NAME),
682
- ...(spec.labels || {}),
683
- };
684
- return new ApiObject(this.chart, spec.name, {
685
- apiVersion: CoreResources.RBAC_API_VERSION,
686
- kind: 'ClusterRoleBinding',
687
- metadata: {
688
- name: spec.name,
689
- ...(labels && Object.keys(labels).length > 0 ? { labels } : {}),
690
- ...(spec.annotations && Object.keys(spec.annotations).length > 0
691
- ? { annotations: spec.annotations }
692
- : {}),
693
- },
694
- roleRef: spec.roleRef,
695
- subjects: spec.subjects,
696
- });
697
- }
698
- /**
699
- * Creates a Job resource for batch workloads
700
- * @param spec - Job specification
701
- * @returns Created Job ApiObject
702
- *
703
- * @example
704
- * ```typescript
705
- * coreResources.addJob({
706
- * name: 'data-processor',
707
- * image: 'busybox:1.35',
708
- * command: ['sh', '-c', 'echo "Processing data..." && sleep 30'],
709
- * completions: 1,
710
- * parallelism: 1
711
- * });
712
- * ```
713
- *
714
- * @since 2.4.0
715
- */
716
- addJob(spec) {
717
- const matchLabels = spec.matchLabels ?? {
718
- [CoreResources.LABEL_NAME]: include(CoreResources.HELPER_NAME),
719
- };
720
- const jobSpec = this.buildJobSpec(spec, matchLabels);
721
- const labels = {
722
- [CoreResources.LABEL_NAME]: include(CoreResources.HELPER_NAME),
723
- ...(spec.labels || {}),
724
- };
725
- return new ApiObject(this.chart, spec.name, {
726
- apiVersion: 'batch/v1',
727
- kind: 'Job',
728
- metadata: {
729
- name: spec.name,
730
- ...(labels && Object.keys(labels).length > 0 ? { labels } : {}),
731
- ...(spec.annotations && Object.keys(spec.annotations).length > 0
732
- ? { annotations: spec.annotations }
733
- : {}),
734
- },
735
- spec: jobSpec,
736
- });
737
- }
738
- /**
739
- * Builds Job specification object
740
- * @private
741
- */
742
- buildJobSpec(spec, matchLabels) {
743
- const containers = this.buildJobContainers(spec);
744
- const podSpec = this.buildJobPodSpec(spec, containers);
745
- const jobSpec = {
746
- template: {
747
- metadata: {
748
- labels: { ...matchLabels, ...(spec.labels || {}) },
749
- ...(spec.annotations && Object.keys(spec.annotations).length > 0
750
- ? { annotations: spec.annotations }
751
- : {}),
752
- },
753
- spec: podSpec,
754
- },
755
- };
756
- if (spec.completions !== undefined) {
757
- jobSpec['completions'] = spec.completions;
758
- }
759
- if (spec.parallelism !== undefined) {
760
- jobSpec['parallelism'] = spec.parallelism;
761
- }
762
- if (spec.backoffLimit !== undefined) {
763
- jobSpec['backoffLimit'] = spec.backoffLimit;
764
- }
765
- if (spec.activeDeadlineSeconds !== undefined) {
766
- jobSpec['activeDeadlineSeconds'] = spec.activeDeadlineSeconds;
767
- }
768
- if (spec.ttlSecondsAfterFinished !== undefined) {
769
- jobSpec['ttlSecondsAfterFinished'] = spec.ttlSecondsAfterFinished;
770
- }
771
- if (spec.completionMode) {
772
- jobSpec['completionMode'] = spec.completionMode;
773
- }
774
- if (spec.suspend !== undefined) {
775
- jobSpec['suspend'] = spec.suspend;
776
- }
777
- return jobSpec;
778
- }
779
- /**
780
- * Builds container specification for Job
781
- * @private
782
- */
783
- buildJobContainers(spec) {
784
- const containerSpec = {
785
- name: spec.name,
786
- image: spec.image,
787
- };
788
- if (spec.command)
789
- containerSpec['command'] = spec.command;
790
- if (spec.args)
791
- containerSpec['args'] = spec.args;
792
- if (spec.env)
793
- containerSpec['env'] = spec.env;
794
- if (spec.resources)
795
- containerSpec['resources'] = spec.resources;
796
- if (spec.volumeMounts)
797
- containerSpec['volumeMounts'] = spec.volumeMounts;
798
- if (spec.securityContext)
799
- containerSpec['securityContext'] = spec.securityContext;
800
- return this.buildGenericContainers(containerSpec);
801
- }
802
- /**
803
- * Builds pod specification for Job
804
- * @private
805
- */
806
- buildJobPodSpec(spec, containers) {
807
- const podSpec = {
808
- containers,
809
- restartPolicy: spec.restartPolicy ?? 'Never',
810
- };
811
- if (spec.serviceAccountName) {
812
- podSpec['serviceAccountName'] = spec.serviceAccountName;
813
- }
814
- if (spec.nodeSelector) {
815
- podSpec['nodeSelector'] = spec.nodeSelector;
816
- }
817
- if (spec.tolerations) {
818
- podSpec['tolerations'] = spec.tolerations;
819
- }
820
- if (spec.affinity) {
821
- podSpec['affinity'] = spec.affinity;
822
- }
823
- if (spec.volumes) {
824
- podSpec['volumes'] = spec.volumes;
825
- }
826
- if (spec.initContainers) {
827
- podSpec['initContainers'] = spec.initContainers;
828
- }
829
- return podSpec;
830
- }
831
- /**
832
- * Creates a ReplicaSet resource
833
- * @param spec - ReplicaSet specification
834
- * @returns Created ReplicaSet ApiObject
835
- *
836
- * @example
837
- * ```typescript
838
- * coreResources.addReplicaSet({
839
- * name: 'web-replica',
840
- * image: 'nginx:1.21',
841
- * replicas: 3,
842
- * containerPort: 80
843
- * });
844
- * ```
845
- *
846
- * @since 2.4.0
847
- */
848
- addReplicaSet(spec) {
849
- const matchLabels = spec.matchLabels ?? {
850
- [CoreResources.LABEL_NAME]: include(CoreResources.HELPER_NAME),
851
- };
852
- const containers = [
853
- {
854
- name: spec.name,
855
- image: spec.image,
856
- ...(spec.containerPort ? { ports: [{ containerPort: spec.containerPort }] } : {}),
857
- ...(spec.env ? { env: spec.env } : {}),
858
- ...(spec.resources ? { resources: spec.resources } : {}),
859
- ...(spec.volumeMounts ? { volumeMounts: spec.volumeMounts } : {}),
860
- ...(spec.securityContext ? { securityContext: spec.securityContext } : {}),
861
- },
862
- ];
863
- const replicaSetSpec = {
864
- replicas: this.normalizeReplicas(spec.replicas),
865
- selector: { matchLabels },
866
- template: {
867
- metadata: { labels: { ...matchLabels, ...spec.labels } },
868
- spec: {
869
- containers,
870
- ...(spec.volumes ? { volumes: spec.volumes } : {}),
871
- ...(spec.serviceAccountName ? { serviceAccountName: spec.serviceAccountName } : {}),
872
- ...(spec.nodeSelector ? { nodeSelector: spec.nodeSelector } : {}),
873
- ...(spec.tolerations ? { tolerations: spec.tolerations } : {}),
874
- ...(spec.affinity ? { affinity: spec.affinity } : {}),
875
- },
876
- },
877
- };
878
- return new ApiObject(this.chart, spec.name, {
879
- apiVersion: CoreResources.APPS_API_VERSION,
880
- kind: 'ReplicaSet',
881
- metadata: {
882
- name: spec.name,
883
- ...(spec.labels ? { labels: spec.labels } : {}),
884
- ...(spec.annotations ? { annotations: spec.annotations } : {}),
885
- },
886
- spec: replicaSetSpec,
887
- });
888
- }
889
- /**
890
- * Creates a Pod resource
891
- * @param spec - Pod specification
892
- * @returns Created Pod ApiObject
893
- *
894
- * @example
895
- * ```typescript
896
- * coreResources.addPod({
897
- * name: 'web-pod',
898
- * image: 'nginx:1.21',
899
- * containerPort: 80
900
- * });
901
- * ```
902
- *
903
- * @since 2.4.0
904
- */
905
- addPod(spec) {
906
- const containers = [
907
- {
908
- name: spec.name,
909
- image: spec.image,
910
- ...(spec.containerPort ? { ports: [{ containerPort: spec.containerPort }] } : {}),
911
- ...(spec.env ? { env: spec.env } : {}),
912
- ...(spec.resources ? { resources: spec.resources } : {}),
913
- ...(spec.volumeMounts ? { volumeMounts: spec.volumeMounts } : {}),
914
- ...(spec.securityContext ? { securityContext: spec.securityContext } : {}),
915
- },
916
- ];
917
- const podSpec = {
918
- containers,
919
- restartPolicy: spec.restartPolicy ?? 'Always',
920
- ...(spec.volumes ? { volumes: spec.volumes } : {}),
921
- ...(spec.serviceAccountName ? { serviceAccountName: spec.serviceAccountName } : {}),
922
- ...(spec.nodeSelector ? { nodeSelector: spec.nodeSelector } : {}),
923
- ...(spec.tolerations ? { tolerations: spec.tolerations } : {}),
924
- ...(spec.affinity ? { affinity: spec.affinity } : {}),
925
- ...(spec.initContainers ? { initContainers: spec.initContainers } : {}),
926
- };
927
- return new ApiObject(this.chart, spec.name, {
928
- apiVersion: CoreResources.CORE_API_VERSION,
929
- kind: 'Pod',
930
- metadata: {
931
- name: spec.name,
932
- ...(spec.labels ? { labels: spec.labels } : {}),
933
- ...(spec.annotations ? { annotations: spec.annotations } : {}),
934
- },
935
- spec: podSpec,
936
- });
937
- }
938
- /**
939
- * Creates a CronJob resource
940
- * @param spec - CronJob specification
941
- * @returns Created CronJob ApiObject
942
- *
943
- * @example
944
- * ```typescript
945
- * coreResources.addCronJob({
946
- * name: 'backup-job',
947
- * schedule: '0 2 * * *',
948
- * image: 'backup:latest',
949
- * command: ['sh', '-c', 'backup-script.sh']
950
- * });
951
- * ```
952
- *
953
- * @since 2.4.0
954
- */
955
- addCronJob(spec) {
956
- const matchLabels = spec.matchLabels ?? {
957
- [CoreResources.LABEL_NAME]: include(CoreResources.HELPER_NAME),
958
- };
959
- const cronJobSpec = this.buildCronJobSpec(spec, matchLabels);
960
- return new ApiObject(this.chart, spec.name, {
961
- apiVersion: 'batch/v1',
962
- kind: 'CronJob',
963
- metadata: {
964
- name: spec.name,
965
- ...(spec.labels ? { labels: spec.labels } : {}),
966
- ...(spec.annotations ? { annotations: spec.annotations } : {}),
967
- },
968
- spec: cronJobSpec,
969
- });
970
- }
971
- /**
972
- * Builds CronJob specification object
973
- * @private
974
- */
975
- buildCronJobSpec(spec, matchLabels) {
976
- const containerSpec = {
977
- name: spec.name,
978
- image: spec.image,
979
- };
980
- if (spec.command)
981
- containerSpec['command'] = spec.command;
982
- if (spec.args)
983
- containerSpec['args'] = spec.args;
984
- if (spec.env)
985
- containerSpec['env'] = spec.env;
986
- if (spec.resources)
987
- containerSpec['resources'] = spec.resources;
988
- if (spec.volumeMounts)
989
- containerSpec['volumeMounts'] = spec.volumeMounts;
990
- if (spec.securityContext)
991
- containerSpec['securityContext'] = spec.securityContext;
992
- const containers = this.buildGenericContainers(containerSpec);
993
- const podSpec = this.buildCronJobPodSpec(spec, containers);
994
- const cronJobSpec = {
995
- schedule: spec.schedule,
996
- jobTemplate: {
997
- spec: {
998
- template: {
999
- metadata: { labels: { ...matchLabels, ...spec.labels } },
1000
- spec: podSpec,
1001
- },
1002
- ...(spec.backoffLimit !== undefined ? { backoffLimit: spec.backoffLimit } : {}),
1003
- ...(spec.activeDeadlineSeconds !== undefined
1004
- ? { activeDeadlineSeconds: spec.activeDeadlineSeconds }
1005
- : {}),
1006
- },
1007
- },
1008
- };
1009
- if (spec.concurrencyPolicy) {
1010
- cronJobSpec['concurrencyPolicy'] = spec.concurrencyPolicy;
1011
- }
1012
- if (spec.suspend !== undefined) {
1013
- cronJobSpec['suspend'] = spec.suspend;
1014
- }
1015
- if (spec.successfulJobsHistoryLimit !== undefined) {
1016
- cronJobSpec['successfulJobsHistoryLimit'] = spec.successfulJobsHistoryLimit;
1017
- }
1018
- if (spec.failedJobsHistoryLimit !== undefined) {
1019
- cronJobSpec['failedJobsHistoryLimit'] = spec.failedJobsHistoryLimit;
1020
- }
1021
- if (spec.startingDeadlineSeconds !== undefined) {
1022
- cronJobSpec['startingDeadlineSeconds'] = spec.startingDeadlineSeconds;
1023
- }
1024
- return cronJobSpec;
1025
- }
1026
- /**
1027
- * Builds generic container specification
1028
- * @private
1029
- */
1030
- buildGenericContainers(spec) {
1031
- const container = {
1032
- name: spec['name'],
1033
- image: spec['image'],
1034
- };
1035
- if (spec['containerPort']) {
1036
- container['ports'] = [{ containerPort: spec['containerPort'] }];
1037
- }
1038
- if (spec['command']) {
1039
- container['command'] = spec['command'];
1040
- }
1041
- if (spec['args']) {
1042
- container['args'] = spec['args'];
1043
- }
1044
- if (spec['env']) {
1045
- container['env'] = spec['env'];
1046
- }
1047
- if (spec['resources']) {
1048
- container['resources'] = spec['resources'];
1049
- }
1050
- if (spec['volumeMounts']) {
1051
- container['volumeMounts'] = spec['volumeMounts'];
1052
- }
1053
- if (spec['securityContext']) {
1054
- container['securityContext'] = spec['securityContext'];
1055
- }
1056
- return [container];
1057
- }
1058
- /**
1059
- * Builds pod specification for CronJob
1060
- * @private
1061
- */
1062
- buildCronJobPodSpec(spec, containers) {
1063
- const podSpec = {
1064
- containers,
1065
- restartPolicy: spec.restartPolicy ?? 'OnFailure',
1066
- };
1067
- if (spec.volumes) {
1068
- podSpec['volumes'] = spec.volumes;
1069
- }
1070
- if (spec.serviceAccountName) {
1071
- podSpec['serviceAccountName'] = spec.serviceAccountName;
1072
- }
1073
- if (spec.nodeSelector) {
1074
- podSpec['nodeSelector'] = spec.nodeSelector;
1075
- }
1076
- if (spec.tolerations) {
1077
- podSpec['tolerations'] = spec.tolerations;
1078
- }
1079
- if (spec.affinity) {
1080
- podSpec['affinity'] = spec.affinity;
1081
- }
1082
- return podSpec;
1083
- }
1084
- /**
1085
- * Normalizes replicas value to ensure it's always a number
1086
- * @param replicas - Replicas value (number, string, or undefined)
1087
- * @returns Normalized number value or Helm template string
1088
- * @private
1089
- * @since 2.5.0
1090
- */
1091
- normalizeReplicas(replicas) {
1092
- if (replicas === undefined || replicas === null) {
1093
- return 1;
1094
- }
1095
- // Validate Helm template value with safe regex (no ReDoS vulnerability)
1096
- if (typeof replicas === 'string') {
1097
- // Simple check for Helm template pattern without catastrophic backtracking
1098
- if (replicas.trim().startsWith('{{') && replicas.trim().endsWith('}}')) {
1099
- return replicas.trim();
1100
- }
1101
- const parsed = parseInt(replicas, 10);
1102
- return isNaN(parsed) ? 1 : Math.max(0, parsed);
1103
- }
1104
- if (typeof replicas === 'number') {
1105
- return Math.max(0, Math.floor(replicas));
1106
- }
1107
- return 1;
1108
- }
1109
- }
1110
- CoreResources.APPS_API_VERSION = 'apps/v1';
1111
- CoreResources.CORE_API_VERSION = 'v1';
1112
- CoreResources.RBAC_API_VERSION = 'rbac.authorization.k8s.io/v1';
1113
- CoreResources.LABEL_NAME = 'app.kubernetes.io/name';
1114
- CoreResources.HELPER_NAME = 'chart.name';
1115
- //# sourceMappingURL=coreResources.js.map