timonel 3.1.1 → 3.1.2
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/CHANGELOG.md +7 -0
- package/README.md +323 -695
- package/dist/cli.js +275 -15
- package/dist/index.d.ts +5 -1
- package/dist/index.js +12 -0
- package/dist/lib/helm.d.ts +471 -0
- package/dist/lib/helm.js +483 -0
- package/dist/lib/helmChartWriter.d.ts +157 -0
- package/dist/lib/helmChartWriter.js +171 -1
- package/dist/lib/policy/configurationLoader.d.ts +132 -0
- package/dist/lib/policy/configurationLoader.js +132 -0
- package/dist/lib/policy/errorContextGenerator.d.ts +89 -0
- package/dist/lib/policy/errorContextGenerator.js +99 -2
- package/dist/lib/policy/errors.d.ts +35 -0
- package/dist/lib/policy/errors.js +36 -0
- package/dist/lib/policy/index.d.ts +9 -0
- package/dist/lib/policy/index.js +16 -0
- package/dist/lib/policy/parallelExecutor.d.ts +90 -0
- package/dist/lib/policy/parallelExecutor.js +86 -3
- package/dist/lib/policy/pluginLoader.d.ts +92 -0
- package/dist/lib/policy/pluginLoader.js +92 -1
- package/dist/lib/policy/pluginRegistry.d.ts +54 -0
- package/dist/lib/policy/pluginRegistry.js +56 -0
- package/dist/lib/policy/policyEngine.d.ts +137 -0
- package/dist/lib/policy/policyEngine.js +191 -5
- package/dist/lib/policy/resultAggregator.d.ts +46 -0
- package/dist/lib/policy/resultAggregator.js +69 -1
- package/dist/lib/policy/resultFormatter.d.ts +88 -0
- package/dist/lib/policy/resultFormatter.js +101 -0
- package/dist/lib/policy/types.d.ts +136 -0
- package/dist/lib/policy/types.js +8 -0
- package/dist/lib/policy/validationCache.d.ts +146 -0
- package/dist/lib/policy/validationCache.js +142 -6
- package/dist/lib/resources/baseResourceProvider.d.ts +45 -0
- package/dist/lib/resources/baseResourceProvider.js +48 -1
- package/dist/lib/resources/cloud/aws/awsResources.d.ts +192 -0
- package/dist/lib/resources/cloud/aws/awsResources.js +163 -1
- package/dist/lib/resources/cloud/aws/karpenterResources.d.ts +131 -0
- package/dist/lib/resources/cloud/aws/karpenterResources.js +77 -0
- package/dist/lib/rutter.d.ts +381 -3
- package/dist/lib/rutter.js +439 -28
- package/dist/lib/security.d.ts +123 -0
- package/dist/lib/security.js +162 -4
- package/dist/lib/templates/flexible-subchart.d.ts +52 -0
- package/dist/lib/templates/flexible-subchart.js +70 -0
- package/dist/lib/templates/umbrella-chart.d.ts +27 -0
- package/dist/lib/templates/umbrella-chart.js +89 -0
- package/dist/lib/types.d.ts +26 -0
- package/dist/lib/umbrella.d.ts +23 -0
- package/dist/lib/umbrella.js +23 -0
- package/dist/lib/umbrellaRutter.d.ts +75 -0
- package/dist/lib/umbrellaRutter.js +82 -2
- package/dist/lib/utils/envVarsLoader.d.ts +49 -0
- package/dist/lib/utils/envVarsLoader.js +53 -0
- package/dist/lib/utils/helmConstructSerializer.d.ts +17 -0
- package/dist/lib/utils/helmConstructSerializer.js +22 -0
- package/dist/lib/utils/helmControlStructures.d.ts +194 -0
- package/dist/lib/utils/helmControlStructures.js +180 -0
- package/dist/lib/utils/helmHelpers/envHelpers.d.ts +13 -0
- package/dist/lib/utils/helmHelpers/envHelpers.js +13 -0
- package/dist/lib/utils/helmHelpers/gitopsHelpers.d.ts +13 -0
- package/dist/lib/utils/helmHelpers/gitopsHelpers.js +13 -0
- package/dist/lib/utils/helmHelpers/index.d.ts +74 -0
- package/dist/lib/utils/helmHelpers/index.js +85 -1
- package/dist/lib/utils/helmHelpers/observabilityHelpers.d.ts +13 -0
- package/dist/lib/utils/helmHelpers/observabilityHelpers.js +13 -0
- package/dist/lib/utils/helmHelpers/types.d.ts +23 -0
- package/dist/lib/utils/helmHelpers/types.js +4 -0
- package/dist/lib/utils/helmHelpers/validationHelpers.d.ts +13 -0
- package/dist/lib/utils/helmHelpers/validationHelpers.js +13 -0
- package/dist/lib/utils/helmHelpers.d.ts +62 -0
- package/dist/lib/utils/helmHelpers.js +77 -0
- package/dist/lib/utils/helmYamlSerializer.d.ts +77 -0
- package/dist/lib/utils/helmYamlSerializer.js +398 -21
- package/dist/lib/utils/logger.d.ts +153 -0
- package/dist/lib/utils/logger.js +170 -2
- package/dist/lib/utils/valuesRef.d.ts +181 -50
- package/dist/lib/utils/valuesRef.js +168 -170
- package/dist/lib/validation/inputValidator.d.ts +45 -0
- package/dist/lib/validation/inputValidator.js +67 -2
- package/dist/types/index.d.ts +34 -0
- package/dist/types/index.js +3 -0
- package/package.json +31 -38
package/dist/lib/helm.js
CHANGED
|
@@ -1,33 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Helpers to embed Helm template expressions while staying type-safe in TypeScript.
|
|
3
|
+
* We treat Helm placeholders as opaque strings that will be preserved in YAML.
|
|
4
|
+
* @since 2.8.0+++
|
|
5
|
+
*/
|
|
1
6
|
import { SecurityUtils } from './security.js';
|
|
7
|
+
/**
|
|
8
|
+
* Creates a reference to a value in .Values of Helm
|
|
9
|
+
*
|
|
10
|
+
* Generates a Helm template expression that references a value
|
|
11
|
+
* in the chart's values.yaml file using dot notation.
|
|
12
|
+
*
|
|
13
|
+
* @param {string} path - Path to the value using dot notation (e.g., 'image.tag')
|
|
14
|
+
* @returns {string} Helm template expression (e.g., '{{ .Values.image.tag }}')
|
|
15
|
+
* @throws {Error} If the path is not valid for Helm templates
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const imageTag = valuesRef('image.tag');
|
|
20
|
+
* // Returns: '{{ .Values.image.tag }}'
|
|
21
|
+
*
|
|
22
|
+
* const replicas = valuesRef('deployment.replicas');
|
|
23
|
+
* // Returns: '{{ .Values.deployment.replicas }}'
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @since 2.8.0+
|
|
27
|
+
*/
|
|
2
28
|
export function valuesRef(path) {
|
|
3
29
|
if (!isValidHelmPath(path)) {
|
|
4
30
|
throw new Error(`Invalid Helm template path: ${path}`);
|
|
5
31
|
}
|
|
6
32
|
return `{{ .Values.${path} }}`;
|
|
7
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Creates a required reference to a value in .Values of Helm
|
|
36
|
+
*
|
|
37
|
+
* Similar to valuesRef, but uses Helm's 'required' function to
|
|
38
|
+
* ensure the value is present, failing chart rendering if not provided.
|
|
39
|
+
*
|
|
40
|
+
* @param {string} path - Path to the value using dot notation
|
|
41
|
+
* @param {string} [message] - Custom error message if value is missing
|
|
42
|
+
* @returns {string} Helm template expression with required validation
|
|
43
|
+
* @throws {Error} If the path is not valid for Helm templates
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* const dbPassword = requiredValuesRef('database.password', 'Database password is required');
|
|
48
|
+
* // Returns: '{{ required "Database password is required" .Values.database.password }}'
|
|
49
|
+
*
|
|
50
|
+
* const apiKey = requiredValuesRef('api.key');
|
|
51
|
+
* // Returns: '{{ required "api.key is required" .Values.api.key }}'
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @since 2.8.0+
|
|
55
|
+
*/
|
|
8
56
|
export function requiredValuesRef(path, message) {
|
|
9
57
|
if (!isValidHelmPath(path)) {
|
|
10
58
|
throw new Error(`Invalid Helm template path: ${path}`);
|
|
11
59
|
}
|
|
12
60
|
const msg = message ?? `${path} is required`;
|
|
61
|
+
// Escape quotes and backslashes in message to prevent template injection
|
|
13
62
|
const escapedMsg = msg.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
14
63
|
return `{{ required "${escapedMsg}" .Values.${path} }}`;
|
|
15
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Validates Helm template path syntax with enhanced security
|
|
67
|
+
*
|
|
68
|
+
* @private
|
|
69
|
+
* @param {string} path - Path to validate
|
|
70
|
+
* @returns {boolean} True if path is valid for Helm templates
|
|
71
|
+
* @since 2.8.0+
|
|
72
|
+
*/
|
|
16
73
|
function isValidHelmPath(path) {
|
|
74
|
+
// Use centralized validation from SecurityUtils
|
|
17
75
|
return SecurityUtils.isValidHelmTemplatePath(path);
|
|
18
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Built-in Helm references for release and chart metadata
|
|
79
|
+
*
|
|
80
|
+
* Object containing the most common references to Helm's built-in
|
|
81
|
+
* variables for release and chart information.
|
|
82
|
+
*
|
|
83
|
+
* @namespace helm
|
|
84
|
+
* @since 2.8.0+
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const deploymentName = `${helm.releaseName}-deployment`;
|
|
89
|
+
* // Uses: '{{ .Release.Name }}-deployment'
|
|
90
|
+
*
|
|
91
|
+
* const version = helm.chartVersion;
|
|
92
|
+
* // Uses: '{{ .Chart.Version }}'
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
19
95
|
export const helm = {
|
|
96
|
+
/** Release name: {{ .Release.Name }} */
|
|
20
97
|
releaseName: '{{ .Release.Name }}',
|
|
98
|
+
/** Chart name: {{ .Chart.Name }} */
|
|
21
99
|
chartName: '{{ .Chart.Name }}',
|
|
100
|
+
/** Chart version: {{ .Chart.Version }} */
|
|
22
101
|
chartVersion: '{{ .Chart.Version }}',
|
|
102
|
+
/** Release namespace: {{ .Release.Namespace }} */
|
|
23
103
|
namespace: '{{ .Release.Namespace }}',
|
|
24
104
|
};
|
|
105
|
+
/**
|
|
106
|
+
* Quotes a Helm template expression for safe YAML embedding
|
|
107
|
+
*
|
|
108
|
+
* Ensures Helm template expressions are properly quoted to avoid
|
|
109
|
+
* YAML parsing issues when the expression contains special characters.
|
|
110
|
+
*
|
|
111
|
+
* @param {string} expr - Helm template expression to quote
|
|
112
|
+
* @returns {string} Quoted expression if it's a Helm template, otherwise unchanged
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```typescript
|
|
116
|
+
* const quoted = quote('{{ .Values.image.tag }}');
|
|
117
|
+
* // Returns: "'{{ .Values.image.tag }}'"
|
|
118
|
+
*
|
|
119
|
+
* const normal = quote('nginx:1.21');
|
|
120
|
+
* // Returns: "nginx:1.21"
|
|
121
|
+
* ```
|
|
122
|
+
*
|
|
123
|
+
* @since 2.8.0+
|
|
124
|
+
*/
|
|
25
125
|
export function quote(expr) {
|
|
126
|
+
// ensure Helm templates are quoted to avoid YAML parsing issues
|
|
26
127
|
if (expr.startsWith('{{') && expr.endsWith('}}')) {
|
|
27
128
|
return `'${expr}'`;
|
|
28
129
|
}
|
|
29
130
|
return expr;
|
|
30
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Indents text by specified number of spaces
|
|
134
|
+
*
|
|
135
|
+
* Helper function that matches Helm's indent function behavior,
|
|
136
|
+
* adding the specified number of spaces to each non-empty line.
|
|
137
|
+
*
|
|
138
|
+
* @param {number} n - Number of spaces to indent
|
|
139
|
+
* @param {string} expr - Text to indent
|
|
140
|
+
* @returns {string} Indented text
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```typescript
|
|
144
|
+
* const indented = indent(4, 'line1\nline2');
|
|
145
|
+
* // Returns: " line1\n line2"
|
|
146
|
+
* ```
|
|
147
|
+
*
|
|
148
|
+
* @since 2.8.0+
|
|
149
|
+
*/
|
|
31
150
|
export function indent(n, expr) {
|
|
32
151
|
const spaces = ' '.repeat(n);
|
|
33
152
|
return expr
|
|
@@ -35,6 +154,21 @@ export function indent(n, expr) {
|
|
|
35
154
|
.map((l) => (l.trim().length ? spaces + l : l))
|
|
36
155
|
.join('\n');
|
|
37
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* Inserts a call to a named Helm template using template function
|
|
159
|
+
*
|
|
160
|
+
* @param {string} name - Name of the template to call
|
|
161
|
+
* @param {string} [context='.'] - Context to pass to the template
|
|
162
|
+
* @returns {string} Helm template expression
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* const tmpl = template('myapp.labels');
|
|
167
|
+
* // Returns: '{{ template "myapp.labels" . }}'
|
|
168
|
+
* ```
|
|
169
|
+
*
|
|
170
|
+
* @since 2.8.0+
|
|
171
|
+
*/
|
|
38
172
|
export function template(name, context = '.') {
|
|
39
173
|
if (!isValidHelmPath(name)) {
|
|
40
174
|
throw new Error(`Invalid template name: ${name}`);
|
|
@@ -44,14 +178,41 @@ export function template(name, context = '.') {
|
|
|
44
178
|
}
|
|
45
179
|
return `{{ template "${name}" ${context} }}`;
|
|
46
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* Inserts a call to a named Helm template using include function
|
|
183
|
+
*
|
|
184
|
+
* Preferred over template() as include allows piping and better error handling.
|
|
185
|
+
*
|
|
186
|
+
* @param {string} name - Name of the template to include
|
|
187
|
+
* @param {string} [context='.'] - Context to pass to the template
|
|
188
|
+
* @returns {string} Helm template expression
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* ```typescript
|
|
192
|
+
* const tmpl = include('myapp.labels');
|
|
193
|
+
* // Returns: '{{ include "myapp.labels" . }}'
|
|
194
|
+
* ```
|
|
195
|
+
*
|
|
196
|
+
* @since 2.8.0+
|
|
197
|
+
*/
|
|
47
198
|
export function include(name, context = '.') {
|
|
48
199
|
return `{{ include "${name}" ${context} }}`;
|
|
49
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* Factory function to create typed Helm value references with reduced code duplication
|
|
203
|
+
* @param path - The path to the value in the Helm values
|
|
204
|
+
* @param type - The expected type of the value
|
|
205
|
+
* @param options - Additional options for reference creation
|
|
206
|
+
* @returns A string that can be used in Helm templates
|
|
207
|
+
* @throws {Error} If the path is invalid
|
|
208
|
+
* @since 2.8.0+
|
|
209
|
+
*/
|
|
50
210
|
export function createTypedRef(path, type, options = {}) {
|
|
51
211
|
if (!isValidHelmPath(path)) {
|
|
52
212
|
throw new Error(`Invalid Helm template path: ${path}`);
|
|
53
213
|
}
|
|
54
214
|
let template = `{{ .Values.${path}`;
|
|
215
|
+
// Apply type casting based on type
|
|
55
216
|
switch (type) {
|
|
56
217
|
case 'number':
|
|
57
218
|
template += ' | int';
|
|
@@ -66,31 +227,119 @@ export function createTypedRef(path, type, options = {}) {
|
|
|
66
227
|
template += ' | float64';
|
|
67
228
|
break;
|
|
68
229
|
}
|
|
230
|
+
// Handle default values following Helm best practices
|
|
69
231
|
if (options.withDefault && options.defaultValue !== undefined) {
|
|
70
232
|
const defaultVal = typeof options.defaultValue === 'string' && options.quote
|
|
71
233
|
? `"${options.defaultValue}"`
|
|
72
234
|
: String(options.defaultValue);
|
|
73
235
|
template += ` | default ${defaultVal}`;
|
|
74
236
|
}
|
|
237
|
+
// Handle string quoting for security
|
|
75
238
|
if (type === 'string' && options.quote && !options.withDefault) {
|
|
76
239
|
template += ' | quote';
|
|
77
240
|
}
|
|
78
241
|
template += ' }}';
|
|
79
242
|
return template;
|
|
80
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Creates a numeric reference from .Values with int cast
|
|
246
|
+
*
|
|
247
|
+
* Generates a Helm template expression that casts the value to integer,
|
|
248
|
+
* truncating any decimal places.
|
|
249
|
+
*
|
|
250
|
+
* @param {string} path - Path to the value using dot notation
|
|
251
|
+
* @param {TypedRefOptions} [options] - Additional options for reference creation
|
|
252
|
+
* @returns {string} Helm template expression with int cast
|
|
253
|
+
* @throws {Error} If the path is not valid for Helm templates
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```typescript
|
|
257
|
+
* const replicas = numberRef('deployment.replicas');
|
|
258
|
+
* // Returns: '{{ .Values.deployment.replicas | int }}'
|
|
259
|
+
* ```
|
|
260
|
+
*
|
|
261
|
+
* @since 2.8.0+
|
|
262
|
+
*/
|
|
81
263
|
export function numberRef(path, options) {
|
|
82
264
|
return createTypedRef(path, 'number', options);
|
|
83
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* Creates a boolean reference from .Values with toBool cast
|
|
268
|
+
*
|
|
269
|
+
* @param {string} path - Path to the value using dot notation
|
|
270
|
+
* @param {TypedRefOptions} [options] - Additional options for reference creation
|
|
271
|
+
* @returns {string} Helm template expression with toBool cast
|
|
272
|
+
* @throws {Error} If the path is not valid for Helm templates
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* ```typescript
|
|
276
|
+
* const enabled = boolRef('feature.enabled');
|
|
277
|
+
* // Returns: '{{ .Values.feature.enabled | toBool }}'
|
|
278
|
+
* ```
|
|
279
|
+
*
|
|
280
|
+
* @since 2.8.0+
|
|
281
|
+
*/
|
|
84
282
|
export function boolRef(path, options) {
|
|
85
283
|
return createTypedRef(path, 'boolean', options);
|
|
86
284
|
}
|
|
285
|
+
/**
|
|
286
|
+
* Creates a string reference from .Values with toString cast
|
|
287
|
+
*
|
|
288
|
+
* @param {string} path - Path to the value using dot notation
|
|
289
|
+
* @param {TypedRefOptions} [options] - Additional options for reference creation
|
|
290
|
+
* @returns {string} Helm template expression with toString cast
|
|
291
|
+
* @throws {Error} If the path is not valid for Helm templates
|
|
292
|
+
*
|
|
293
|
+
* @example
|
|
294
|
+
* ```typescript
|
|
295
|
+
* const version = stringRef('app.version');
|
|
296
|
+
* // Returns: '{{ .Values.app.version | toString }}'
|
|
297
|
+
* ```
|
|
298
|
+
*
|
|
299
|
+
* @since 2.8.0+
|
|
300
|
+
*/
|
|
87
301
|
export function stringRef(path, options) {
|
|
88
302
|
const defaultOptions = { quote: true, ...options };
|
|
89
303
|
return createTypedRef(path, 'string', defaultOptions);
|
|
90
304
|
}
|
|
305
|
+
/**
|
|
306
|
+
* Creates a float reference from .Values with float64 cast
|
|
307
|
+
*
|
|
308
|
+
* @param {string} path - Path to the value using dot notation
|
|
309
|
+
* @param {TypedRefOptions} [options] - Additional options for reference creation
|
|
310
|
+
* @returns {string} Helm template expression with float64 cast
|
|
311
|
+
* @throws {Error} If the path is not valid for Helm templates
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* ```typescript
|
|
315
|
+
* const ratio = floatRef('scaling.ratio');
|
|
316
|
+
* // Returns: '{{ .Values.scaling.ratio | float64 }}'
|
|
317
|
+
* ```
|
|
318
|
+
*
|
|
319
|
+
* @since 2.8.0+
|
|
320
|
+
*/
|
|
91
321
|
export function floatRef(path, options) {
|
|
92
322
|
return createTypedRef(path, 'float', options);
|
|
93
323
|
}
|
|
324
|
+
/**
|
|
325
|
+
* Creates a conditional reference to a value in .Values
|
|
326
|
+
*
|
|
327
|
+
* Generates a Helm template expression that conditionally renders
|
|
328
|
+
* the value only if the condition is truthy.
|
|
329
|
+
*
|
|
330
|
+
* @param {string} path - Path to the value using dot notation
|
|
331
|
+
* @param {string} condition - Path to the condition value
|
|
332
|
+
* @returns {string} Helm template expression with conditional logic
|
|
333
|
+
* @throws {Error} If the path or condition is not valid for Helm templates
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```typescript
|
|
337
|
+
* const secretName = conditionalRef('database.secretName', 'database.enabled');
|
|
338
|
+
* // Returns: '{{ if .Values.database.enabled }}{{ .Values.database.secretName }}{{ end }}'
|
|
339
|
+
* ```
|
|
340
|
+
*
|
|
341
|
+
* @since 2.8.0+
|
|
342
|
+
*/
|
|
94
343
|
export function conditionalRef(path, condition) {
|
|
95
344
|
if (!isValidHelmPath(path)) {
|
|
96
345
|
throw new Error(`Invalid Helm template path: ${path}`);
|
|
@@ -100,38 +349,165 @@ export function conditionalRef(path, condition) {
|
|
|
100
349
|
}
|
|
101
350
|
return `{{ if .Values.${condition} }}{{ .Values.${path} }}{{ end }}`;
|
|
102
351
|
}
|
|
352
|
+
/**
|
|
353
|
+
* Wraps a complete YAML manifest in a Helm conditional
|
|
354
|
+
*
|
|
355
|
+
* Generates a Helm template that conditionally renders an entire
|
|
356
|
+
* Kubernetes manifest based on a values condition.
|
|
357
|
+
*
|
|
358
|
+
* @param {string} yamlContent - Complete YAML manifest content
|
|
359
|
+
* @param {string} condition - Path to the condition value
|
|
360
|
+
* @returns {string} YAML content wrapped in Helm conditional
|
|
361
|
+
* @throws {Error} If the condition path is not valid for Helm templates
|
|
362
|
+
*
|
|
363
|
+
* @example
|
|
364
|
+
* ```typescript
|
|
365
|
+
* const namespaceYaml = `
|
|
366
|
+
* apiVersion: v1
|
|
367
|
+
* kind: Namespace
|
|
368
|
+
* metadata:
|
|
369
|
+
* name: my-namespace
|
|
370
|
+
* `;
|
|
371
|
+
* const conditionalNamespace = conditionalManifest(namespaceYaml, 'createNamespace');
|
|
372
|
+
* // Returns: '{{ if .Values.createNamespace }}\napiVersion: v1\nkind: Namespace...{{ end }}'
|
|
373
|
+
* ```
|
|
374
|
+
*
|
|
375
|
+
* @since 2.8.0+
|
|
376
|
+
*/
|
|
103
377
|
export function conditionalManifest(yamlContent, condition) {
|
|
104
378
|
if (!isValidHelmPath(condition)) {
|
|
105
379
|
throw new Error(`Invalid Helm template condition path: ${condition}`);
|
|
106
380
|
}
|
|
381
|
+
// Remove leading/trailing whitespace and ensure proper formatting
|
|
107
382
|
const cleanYaml = yamlContent.trim();
|
|
108
383
|
return `{{ if .Values.${condition} }}
|
|
109
384
|
${cleanYaml}
|
|
110
385
|
{{ end }}`;
|
|
111
386
|
}
|
|
387
|
+
/**
|
|
388
|
+
* Creates a reference to a value in .Values with a default fallback
|
|
389
|
+
*
|
|
390
|
+
* Generates a Helm template expression that uses the default filter
|
|
391
|
+
* to provide a fallback value when the path is not set.
|
|
392
|
+
*
|
|
393
|
+
* @param {string} path - Path to the value using dot notation
|
|
394
|
+
* @param {string} defaultValue - Default value to use if path is not set
|
|
395
|
+
* @returns {string} Helm template expression with default fallback
|
|
396
|
+
* @throws {Error} If the path is not valid for Helm templates
|
|
397
|
+
*
|
|
398
|
+
* @example
|
|
399
|
+
* ```typescript
|
|
400
|
+
* const image = defaultRef('image.tag', 'latest');
|
|
401
|
+
* // Returns: '{{ .Values.image.tag | default "latest" }}'
|
|
402
|
+
* ```
|
|
403
|
+
*
|
|
404
|
+
* @since 2.8.0+
|
|
405
|
+
*/
|
|
112
406
|
export function defaultRef(path, defaultValue) {
|
|
113
407
|
if (!isValidHelmPath(path)) {
|
|
114
408
|
throw new Error(`Invalid Helm template path: ${path}`);
|
|
115
409
|
}
|
|
410
|
+
// Escape quotes and backslashes in defaultValue to prevent template injection
|
|
116
411
|
const escapedValue = defaultValue.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
117
412
|
return `{{ .Values.${path} | default "${escapedValue}" }}`;
|
|
118
413
|
}
|
|
414
|
+
/**
|
|
415
|
+
* Creates a base64-encoded reference to a value in .Values
|
|
416
|
+
*
|
|
417
|
+
* Generates a Helm template expression that encodes the value
|
|
418
|
+
* using base64 encoding, useful for secrets and certificates.
|
|
419
|
+
*
|
|
420
|
+
* @param {string} path - Path to the value using dot notation
|
|
421
|
+
* @returns {string} Helm template expression with base64 encoding
|
|
422
|
+
* @throws {Error} If the path is not valid for Helm templates
|
|
423
|
+
*
|
|
424
|
+
* @example
|
|
425
|
+
* ```typescript
|
|
426
|
+
* const encodedSecret = base64Ref('secrets.apiKey');
|
|
427
|
+
* // Returns: '{{ .Values.secrets.apiKey | b64enc }}'
|
|
428
|
+
* ```
|
|
429
|
+
*
|
|
430
|
+
* @since 2.8.0+
|
|
431
|
+
*/
|
|
119
432
|
export function base64Ref(path) {
|
|
120
433
|
if (!isValidHelmPath(path)) {
|
|
121
434
|
throw new Error(`Invalid Helm template path: ${path}`);
|
|
122
435
|
}
|
|
123
436
|
return `{{ .Values.${path} | b64enc }}`;
|
|
124
437
|
}
|
|
438
|
+
/**
|
|
439
|
+
* Creates a JSON-serialized reference to a value in .Values
|
|
440
|
+
*
|
|
441
|
+
* Generates a Helm template expression that serializes the value
|
|
442
|
+
* to JSON format, useful for complex configuration objects.
|
|
443
|
+
*
|
|
444
|
+
* @param {string} path - Path to the value using dot notation
|
|
445
|
+
* @returns {string} Helm template expression with JSON serialization
|
|
446
|
+
* @throws {Error} If the path is not valid for Helm templates
|
|
447
|
+
*
|
|
448
|
+
* @example
|
|
449
|
+
* ```typescript
|
|
450
|
+
* const configJson = jsonRef('app.config');
|
|
451
|
+
* // Returns: '{{ .Values.app.config | toJson }}'
|
|
452
|
+
* ```
|
|
453
|
+
*
|
|
454
|
+
* @since 2.6.0
|
|
455
|
+
*/
|
|
125
456
|
export function jsonRef(path) {
|
|
126
457
|
if (!isValidHelmPath(path)) {
|
|
127
458
|
throw new Error(`Invalid Helm template path: ${path}`);
|
|
128
459
|
}
|
|
129
460
|
return `{{ .Values.${path} | toJson }}`;
|
|
130
461
|
}
|
|
462
|
+
/**
|
|
463
|
+
* Flow Control Functions for Helm Templates
|
|
464
|
+
*
|
|
465
|
+
* These functions provide programmatic access to Helm's flow control structures:
|
|
466
|
+
* if/else, with, and range for conditional logic and iteration.
|
|
467
|
+
*
|
|
468
|
+
* @since 2.8.0+
|
|
469
|
+
*/
|
|
470
|
+
/**
|
|
471
|
+
* Creates a conditional if block in Helm templates
|
|
472
|
+
*
|
|
473
|
+
* Generates Helm template expressions for conditional logic using if/else/end.
|
|
474
|
+
* Supports complex conditions with comparison operators and pipeline expressions.
|
|
475
|
+
*
|
|
476
|
+
* @param {string} condition - The condition to evaluate (e.g., 'eq .Values.env "production"')
|
|
477
|
+
* @param {string} content - Content to include when condition is true
|
|
478
|
+
* @param {string} [elseContent] - Optional content for else block
|
|
479
|
+
* @returns {string} Complete Helm if/else template block
|
|
480
|
+
* @throws {Error} If condition contains potentially unsafe template expressions
|
|
481
|
+
*
|
|
482
|
+
* @example
|
|
483
|
+
* ```typescript
|
|
484
|
+
* const conditionalConfig = helmIf(
|
|
485
|
+
* 'eq .Values.environment "production"',
|
|
486
|
+
* 'replicas: 3\nresources:\n limits:\n memory: "1Gi"',
|
|
487
|
+
* 'replicas: 1\nresources:\n limits:\n memory: "512Mi"'
|
|
488
|
+
* );
|
|
489
|
+
* // Returns:
|
|
490
|
+
* // {{- if eq .Values.environment "production" }}
|
|
491
|
+
* // replicas: 3
|
|
492
|
+
* // resources:
|
|
493
|
+
* // limits:
|
|
494
|
+
* // memory: "1Gi"
|
|
495
|
+
* // {{- else }}
|
|
496
|
+
* // replicas: 1
|
|
497
|
+
* // resources:
|
|
498
|
+
* // limits:
|
|
499
|
+
* // memory: "512Mi"
|
|
500
|
+
* // {{- end }}
|
|
501
|
+
* ```
|
|
502
|
+
*
|
|
503
|
+
* @since 2.8.0
|
|
504
|
+
*/
|
|
131
505
|
export function helmIf(condition, content, elseContent) {
|
|
506
|
+
// Validate condition for basic security
|
|
132
507
|
if (!condition || condition.trim().length === 0) {
|
|
133
508
|
throw new Error('Condition cannot be empty');
|
|
134
509
|
}
|
|
510
|
+
// Basic validation to prevent template injection
|
|
135
511
|
if (condition.includes('{{') || condition.includes('}}')) {
|
|
136
512
|
throw new Error('Condition should not contain template delimiters');
|
|
137
513
|
}
|
|
@@ -142,10 +518,44 @@ export function helmIf(condition, content, elseContent) {
|
|
|
142
518
|
result += `\n{{- end }}`;
|
|
143
519
|
return result;
|
|
144
520
|
}
|
|
521
|
+
/**
|
|
522
|
+
* Creates a with block for scoped template context
|
|
523
|
+
*
|
|
524
|
+
* Generates Helm template expressions using 'with' to change the template context.
|
|
525
|
+
* Useful for accessing nested values without repeating the full path.
|
|
526
|
+
*
|
|
527
|
+
* @param {string} path - Path to set as new context (e.g., '.Values.database')
|
|
528
|
+
* @param {string} content - Content to render within the new context
|
|
529
|
+
* @param {string} [elseContent] - Optional content when path is empty/nil
|
|
530
|
+
* @returns {string} Complete Helm with/else/end template block
|
|
531
|
+
* @throws {Error} If path is not valid for Helm templates
|
|
532
|
+
*
|
|
533
|
+
* @example
|
|
534
|
+
* ```typescript
|
|
535
|
+
* const dbConfig = helmWith(
|
|
536
|
+
* '.Values.database',
|
|
537
|
+
* 'host: {{ .host }}\nport: {{ .port }}\nname: {{ .name }}',
|
|
538
|
+
* 'host: "localhost"\nport: 5432\nname: "defaultdb"'
|
|
539
|
+
* );
|
|
540
|
+
* // Returns:
|
|
541
|
+
* // {{- with .Values.database }}
|
|
542
|
+
* // host: {{ .host }}
|
|
543
|
+
* // port: {{ .port }}
|
|
544
|
+
* // name: {{ .name }}
|
|
545
|
+
* // {{- else }}
|
|
546
|
+
* // host: "localhost"
|
|
547
|
+
* // port: 5432
|
|
548
|
+
* // name: "defaultdb"
|
|
549
|
+
* // {{- end }}
|
|
550
|
+
* ```
|
|
551
|
+
*
|
|
552
|
+
* @since 2.8.0
|
|
553
|
+
*/
|
|
145
554
|
export function helmWith(path, content, elseContent) {
|
|
146
555
|
if (!path || path.trim().length === 0) {
|
|
147
556
|
throw new Error('Path cannot be empty');
|
|
148
557
|
}
|
|
558
|
+
// Validate path format
|
|
149
559
|
if (!path.startsWith('.')) {
|
|
150
560
|
throw new Error('Path must start with "." (e.g., ".Values.config")');
|
|
151
561
|
}
|
|
@@ -156,6 +566,48 @@ export function helmWith(path, content, elseContent) {
|
|
|
156
566
|
result += `\n{{- end }}`;
|
|
157
567
|
return result;
|
|
158
568
|
}
|
|
569
|
+
/**
|
|
570
|
+
* Creates a range loop for iterating over collections
|
|
571
|
+
*
|
|
572
|
+
* Generates Helm template expressions using 'range' to iterate over lists, maps, or other collections.
|
|
573
|
+
* Supports both simple iteration and key-value iteration for maps.
|
|
574
|
+
*
|
|
575
|
+
* @param {string} collection - Collection to iterate over (e.g., '.Values.items')
|
|
576
|
+
* @param {string} content - Content template for each iteration
|
|
577
|
+
* @param {object} [options] - Configuration options for the range loop
|
|
578
|
+
* @param {boolean} [options.keyValue=false] - Whether to iterate with key-value pairs
|
|
579
|
+
* @param {string} [options.keyVar='$key'] - Variable name for keys in key-value iteration
|
|
580
|
+
* @param {string} [options.valueVar='$value'] - Variable name for values in key-value iteration
|
|
581
|
+
* @param {string} [options.indexVar='$index'] - Variable name for index in indexed iteration
|
|
582
|
+
* @param {string} [options.itemVar='$item'] - Variable name for items in simple iteration
|
|
583
|
+
* @returns {string} Complete Helm range/end template block
|
|
584
|
+
* @throws {Error} If collection path is not valid
|
|
585
|
+
*
|
|
586
|
+
* @example
|
|
587
|
+
* ```typescript
|
|
588
|
+
* // Simple list iteration
|
|
589
|
+
* const serviceList = helmRange(
|
|
590
|
+
* '.Values.services',
|
|
591
|
+
* '- name: {{ .name }}\n port: {{ .port }}'
|
|
592
|
+
* );
|
|
593
|
+
*
|
|
594
|
+
* // Key-value map iteration
|
|
595
|
+
* const envVars = helmRange(
|
|
596
|
+
* '.Values.env',
|
|
597
|
+
* '- name: {{ $key }}\n value: {{ $value | quote }}',
|
|
598
|
+
* { keyValue: true }
|
|
599
|
+
* );
|
|
600
|
+
*
|
|
601
|
+
* // Indexed iteration
|
|
602
|
+
* const indexedItems = helmRange(
|
|
603
|
+
* '.Values.items',
|
|
604
|
+
* '{{ $index }}: {{ $item }}',
|
|
605
|
+
* { indexVar: '$i', itemVar: '$val' }
|
|
606
|
+
* );
|
|
607
|
+
* ```
|
|
608
|
+
*
|
|
609
|
+
* @since 2.8.0
|
|
610
|
+
*/
|
|
159
611
|
export function helmRange(collection, content, options = {}) {
|
|
160
612
|
if (!collection || collection.trim().length === 0) {
|
|
161
613
|
throw new Error('Collection cannot be empty');
|
|
@@ -163,10 +615,12 @@ export function helmRange(collection, content, options = {}) {
|
|
|
163
615
|
if (!collection.startsWith('.')) {
|
|
164
616
|
throw new Error('Collection path must start with "." (e.g., ".Values.items")');
|
|
165
617
|
}
|
|
618
|
+
// Validate collection path to prevent template injection
|
|
166
619
|
if (!isValidHelmPath(collection)) {
|
|
167
620
|
throw new Error(`Invalid collection path: ${collection}`);
|
|
168
621
|
}
|
|
169
622
|
const { keyValue = false, keyVar = '$key', valueVar = '$value', indexVar = '$index', itemVar = '$item', } = options;
|
|
623
|
+
// Validate variable names to prevent injection
|
|
170
624
|
const validateVarName = (varName, varType) => {
|
|
171
625
|
if (!varName.startsWith('$')) {
|
|
172
626
|
throw new Error(`${varType} must start with $ (e.g., $key, $value)`);
|
|
@@ -175,6 +629,7 @@ export function helmRange(collection, content, options = {}) {
|
|
|
175
629
|
throw new Error(`Invalid ${varType}: ${varName}`);
|
|
176
630
|
}
|
|
177
631
|
};
|
|
632
|
+
// Validate all variable names that will be used
|
|
178
633
|
if (keyValue) {
|
|
179
634
|
validateVarName(keyVar, 'keyVar');
|
|
180
635
|
validateVarName(valueVar, 'valueVar');
|
|
@@ -197,6 +652,33 @@ export function helmRange(collection, content, options = {}) {
|
|
|
197
652
|
}
|
|
198
653
|
return `${rangeExpression}\n${content}\n{{- end }}`;
|
|
199
654
|
}
|
|
655
|
+
/**
|
|
656
|
+
* Creates a complex conditional with multiple else-if branches
|
|
657
|
+
*
|
|
658
|
+
* Generates Helm template expressions for complex conditional logic with multiple branches.
|
|
659
|
+
* Useful for handling multiple environment configurations or feature flags.
|
|
660
|
+
*
|
|
661
|
+
* @param {Array<{condition: string, content: string}>} branches - Array of condition-content pairs
|
|
662
|
+
* @param {string} [defaultContent] - Default content when no conditions match
|
|
663
|
+
* @returns {string} Complete Helm if/else-if/else/end template block
|
|
664
|
+
* @throws {Error} If branches array is empty or contains invalid conditions
|
|
665
|
+
*
|
|
666
|
+
* @example
|
|
667
|
+
* ```typescript
|
|
668
|
+
* const envConfig = helmIfElseIf([
|
|
669
|
+
* {
|
|
670
|
+
* condition: 'eq .Values.environment "production"',
|
|
671
|
+
* content: 'replicas: 5\nresources.limits.memory: "2Gi"'
|
|
672
|
+
* },
|
|
673
|
+
* {
|
|
674
|
+
* condition: 'eq .Values.environment "staging"',
|
|
675
|
+
* content: 'replicas: 2\nresources.limits.memory: "1Gi"'
|
|
676
|
+
* }
|
|
677
|
+
* ], 'replicas: 1\nresources.limits.memory: "512Mi"');
|
|
678
|
+
* ```
|
|
679
|
+
*
|
|
680
|
+
* @since 2.8.0
|
|
681
|
+
*/
|
|
200
682
|
export function helmIfElseIf(branches, defaultContent) {
|
|
201
683
|
if (!branches || branches.length === 0) {
|
|
202
684
|
throw new Error('At least one branch is required');
|
|
@@ -206,6 +688,7 @@ export function helmIfElseIf(branches, defaultContent) {
|
|
|
206
688
|
if (!branch.condition || branch.condition.trim().length === 0) {
|
|
207
689
|
throw new Error(`Branch ${index} condition cannot be empty`);
|
|
208
690
|
}
|
|
691
|
+
// Basic validation to prevent template injection
|
|
209
692
|
if (branch.condition.includes('{{') || branch.condition.includes('}}')) {
|
|
210
693
|
throw new Error(`Branch ${index} condition should not contain template delimiters`);
|
|
211
694
|
}
|