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
|
@@ -1,12 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Result Cache Implementation
|
|
3
|
+
*
|
|
4
|
+
* This module provides caching functionality for policy validation results to optimize
|
|
5
|
+
* performance for repeated manifest validations. It implements cache invalidation
|
|
6
|
+
* strategies and optimizations for large chart processing.
|
|
7
|
+
*
|
|
8
|
+
* @since 3.0.0
|
|
9
|
+
*/
|
|
1
10
|
import { createLogger } from '../utils/logger.js';
|
|
11
|
+
/**
|
|
12
|
+
* Default cache configuration
|
|
13
|
+
*/
|
|
2
14
|
const DEFAULT_CACHE_OPTIONS = {
|
|
3
15
|
maxEntries: 1000,
|
|
4
|
-
maxAge: 30 * 60 * 1000,
|
|
16
|
+
maxAge: 30 * 60 * 1000, // 30 minutes
|
|
5
17
|
enableCompression: false,
|
|
6
18
|
evictionStrategy: 'lru',
|
|
7
19
|
cacheFailures: true,
|
|
8
|
-
minExecutionTimeToCache: 10,
|
|
20
|
+
minExecutionTimeToCache: 10, // 10ms
|
|
9
21
|
};
|
|
22
|
+
/**
|
|
23
|
+
* Validation result cache implementation
|
|
24
|
+
*/
|
|
10
25
|
export class ValidationCache {
|
|
11
26
|
constructor(options = {}) {
|
|
12
27
|
this.cache = new Map();
|
|
@@ -24,8 +39,15 @@ export class ValidationCache {
|
|
|
24
39
|
options: this.options,
|
|
25
40
|
operation: 'cache_init',
|
|
26
41
|
});
|
|
42
|
+
// Set up periodic cleanup
|
|
27
43
|
this.startPeriodicCleanup();
|
|
28
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Gets a cached validation result if available
|
|
47
|
+
* @param manifestHash - Hash of the manifests
|
|
48
|
+
* @param pluginHash - Hash of the plugin configuration
|
|
49
|
+
* @returns Cached result or undefined if not found
|
|
50
|
+
*/
|
|
29
51
|
get(manifestHash, pluginHash) {
|
|
30
52
|
const cacheKey = this.generateCacheKey(manifestHash, pluginHash);
|
|
31
53
|
const entry = this.cache.get(cacheKey);
|
|
@@ -39,6 +61,7 @@ export class ValidationCache {
|
|
|
39
61
|
});
|
|
40
62
|
return undefined;
|
|
41
63
|
}
|
|
64
|
+
// Check if entry has expired
|
|
42
65
|
if (this.isExpired(entry)) {
|
|
43
66
|
this.cache.delete(cacheKey);
|
|
44
67
|
this.stats.misses++;
|
|
@@ -51,6 +74,7 @@ export class ValidationCache {
|
|
|
51
74
|
});
|
|
52
75
|
return undefined;
|
|
53
76
|
}
|
|
77
|
+
// Update access statistics
|
|
54
78
|
entry.accessCount++;
|
|
55
79
|
entry.lastAccessed = Date.now();
|
|
56
80
|
this.stats.hits++;
|
|
@@ -64,7 +88,14 @@ export class ValidationCache {
|
|
|
64
88
|
});
|
|
65
89
|
return entry.result;
|
|
66
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Stores a validation result in the cache
|
|
93
|
+
* @param manifestHash - Hash of the manifests
|
|
94
|
+
* @param pluginHash - Hash of the plugin configuration
|
|
95
|
+
* @param result - Validation result to cache
|
|
96
|
+
*/
|
|
67
97
|
set(manifestHash, pluginHash, result) {
|
|
98
|
+
// Check if we should cache this result
|
|
68
99
|
if (!this.shouldCache(result)) {
|
|
69
100
|
this.logger.debug('Skipping cache storage', {
|
|
70
101
|
manifestHash: manifestHash.substring(0, 8),
|
|
@@ -76,6 +107,7 @@ export class ValidationCache {
|
|
|
76
107
|
return;
|
|
77
108
|
}
|
|
78
109
|
const cacheKey = this.generateCacheKey(manifestHash, pluginHash);
|
|
110
|
+
// Check if we need to evict entries
|
|
79
111
|
if (this.cache.size >= this.options.maxEntries) {
|
|
80
112
|
this.evictEntries();
|
|
81
113
|
}
|
|
@@ -98,12 +130,21 @@ export class ValidationCache {
|
|
|
98
130
|
operation: 'cache_store',
|
|
99
131
|
});
|
|
100
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Invalidates cache entries based on criteria
|
|
135
|
+
* @param criteria - Invalidation criteria
|
|
136
|
+
*/
|
|
101
137
|
invalidate(criteria) {
|
|
102
138
|
if (criteria.all) {
|
|
103
139
|
return this.invalidateAll();
|
|
104
140
|
}
|
|
105
141
|
return this.invalidateSelective(criteria);
|
|
106
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Invalidates all cache entries
|
|
145
|
+
* @returns Number of invalidated entries
|
|
146
|
+
* @private
|
|
147
|
+
*/
|
|
107
148
|
invalidateAll() {
|
|
108
149
|
const invalidatedCount = this.cache.size;
|
|
109
150
|
this.cache.clear();
|
|
@@ -114,6 +155,12 @@ export class ValidationCache {
|
|
|
114
155
|
this.updateStats();
|
|
115
156
|
return invalidatedCount;
|
|
116
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Invalidates cache entries based on selective criteria
|
|
160
|
+
* @param criteria - Invalidation criteria
|
|
161
|
+
* @returns Number of invalidated entries
|
|
162
|
+
* @private
|
|
163
|
+
*/
|
|
117
164
|
invalidateSelective(criteria) {
|
|
118
165
|
const keysToDelete = [];
|
|
119
166
|
for (const [key, entry] of this.cache.entries()) {
|
|
@@ -132,6 +179,13 @@ export class ValidationCache {
|
|
|
132
179
|
this.updateStats();
|
|
133
180
|
return keysToDelete.length;
|
|
134
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Determines if a cache entry should be invalidated
|
|
184
|
+
* @param entry - Cache entry to check
|
|
185
|
+
* @param criteria - Invalidation criteria
|
|
186
|
+
* @returns True if entry should be invalidated
|
|
187
|
+
* @private
|
|
188
|
+
*/
|
|
135
189
|
shouldInvalidateEntry(entry, criteria) {
|
|
136
190
|
if (criteria.manifestHash && entry.manifestHash === criteria.manifestHash) {
|
|
137
191
|
return true;
|
|
@@ -144,9 +198,16 @@ export class ValidationCache {
|
|
|
144
198
|
}
|
|
145
199
|
return false;
|
|
146
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* Gets current cache statistics
|
|
203
|
+
* @returns Cache statistics
|
|
204
|
+
*/
|
|
147
205
|
getStats() {
|
|
148
206
|
return { ...this.stats };
|
|
149
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* Clears all cache statistics
|
|
210
|
+
*/
|
|
150
211
|
clearStats() {
|
|
151
212
|
this.stats = {
|
|
152
213
|
hits: 0,
|
|
@@ -160,23 +221,48 @@ export class ValidationCache {
|
|
|
160
221
|
operation: 'cache_stats_clear',
|
|
161
222
|
});
|
|
162
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* Generates a cache key from manifest and plugin hashes
|
|
226
|
+
* @param manifestHash - Hash of the manifests
|
|
227
|
+
* @param pluginHash - Hash of the plugin configuration
|
|
228
|
+
* @returns Cache key string
|
|
229
|
+
* @private
|
|
230
|
+
*/
|
|
163
231
|
generateCacheKey(manifestHash, pluginHash) {
|
|
164
232
|
return `${manifestHash}:${pluginHash}`;
|
|
165
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* Checks if a cache entry has expired
|
|
236
|
+
* @param entry - Cache entry to check
|
|
237
|
+
* @returns True if expired
|
|
238
|
+
* @private
|
|
239
|
+
*/
|
|
166
240
|
isExpired(entry) {
|
|
167
241
|
return Date.now() - entry.timestamp > this.options.maxAge;
|
|
168
242
|
}
|
|
243
|
+
/**
|
|
244
|
+
* Determines if a validation result should be cached
|
|
245
|
+
* @param result - Validation result
|
|
246
|
+
* @returns True if should be cached
|
|
247
|
+
* @private
|
|
248
|
+
*/
|
|
169
249
|
shouldCache(result) {
|
|
250
|
+
// Don't cache if execution time is too short
|
|
170
251
|
if (result.metadata.executionTime < this.options.minExecutionTimeToCache) {
|
|
171
252
|
return false;
|
|
172
253
|
}
|
|
254
|
+
// Don't cache failures if disabled
|
|
173
255
|
if (!this.options.cacheFailures && !result.valid) {
|
|
174
256
|
return false;
|
|
175
257
|
}
|
|
176
258
|
return true;
|
|
177
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* Evicts cache entries based on the configured strategy
|
|
262
|
+
* @private
|
|
263
|
+
*/
|
|
178
264
|
evictEntries() {
|
|
179
|
-
const entriesToEvict = Math.max(1, Math.floor(this.options.maxEntries * 0.1));
|
|
265
|
+
const entriesToEvict = Math.max(1, Math.floor(this.options.maxEntries * 0.1)); // Evict 10%
|
|
180
266
|
switch (this.options.evictionStrategy) {
|
|
181
267
|
case 'lru':
|
|
182
268
|
this.evictLRU(entriesToEvict);
|
|
@@ -195,6 +281,11 @@ export class ValidationCache {
|
|
|
195
281
|
operation: 'cache_evict',
|
|
196
282
|
});
|
|
197
283
|
}
|
|
284
|
+
/**
|
|
285
|
+
* Evicts least recently used entries
|
|
286
|
+
* @param count - Number of entries to evict
|
|
287
|
+
* @private
|
|
288
|
+
*/
|
|
198
289
|
evictLRU(count) {
|
|
199
290
|
const entries = Array.from(this.cache.entries())
|
|
200
291
|
.sort(([, a], [, b]) => a.lastAccessed - b.lastAccessed)
|
|
@@ -203,6 +294,11 @@ export class ValidationCache {
|
|
|
203
294
|
this.cache.delete(key);
|
|
204
295
|
}
|
|
205
296
|
}
|
|
297
|
+
/**
|
|
298
|
+
* Evicts least frequently used entries
|
|
299
|
+
* @param count - Number of entries to evict
|
|
300
|
+
* @private
|
|
301
|
+
*/
|
|
206
302
|
evictLFU(count) {
|
|
207
303
|
const entries = Array.from(this.cache.entries())
|
|
208
304
|
.sort(([, a], [, b]) => a.accessCount - b.accessCount)
|
|
@@ -211,6 +307,11 @@ export class ValidationCache {
|
|
|
211
307
|
this.cache.delete(key);
|
|
212
308
|
}
|
|
213
309
|
}
|
|
310
|
+
/**
|
|
311
|
+
* Evicts oldest entries (time to live)
|
|
312
|
+
* @param count - Number of entries to evict
|
|
313
|
+
* @private
|
|
314
|
+
*/
|
|
214
315
|
evictTTL(count) {
|
|
215
316
|
const entries = Array.from(this.cache.entries())
|
|
216
317
|
.sort(([, a], [, b]) => a.timestamp - b.timestamp)
|
|
@@ -219,6 +320,10 @@ export class ValidationCache {
|
|
|
219
320
|
this.cache.delete(key);
|
|
220
321
|
}
|
|
221
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* Updates cache statistics
|
|
325
|
+
* @private
|
|
326
|
+
*/
|
|
222
327
|
updateStats() {
|
|
223
328
|
const totalRequests = this.stats.hits + this.stats.misses;
|
|
224
329
|
this.stats.entries = this.cache.size;
|
|
@@ -232,22 +337,36 @@ export class ValidationCache {
|
|
|
232
337
|
this.stats.averageAccessCount = 0;
|
|
233
338
|
}
|
|
234
339
|
}
|
|
340
|
+
/**
|
|
341
|
+
* Estimates memory usage of the cache
|
|
342
|
+
* @returns Estimated memory usage in bytes
|
|
343
|
+
* @private
|
|
344
|
+
*/
|
|
235
345
|
estimateMemoryUsage() {
|
|
236
346
|
let totalSize = 0;
|
|
237
347
|
for (const entry of this.cache.values()) {
|
|
238
|
-
|
|
348
|
+
// Rough estimation of entry size
|
|
349
|
+
totalSize += JSON.stringify(entry.result).length * 2; // UTF-16 encoding
|
|
239
350
|
totalSize += entry.manifestHash.length * 2;
|
|
240
351
|
totalSize += entry.pluginHash.length * 2;
|
|
241
|
-
totalSize += 64;
|
|
352
|
+
totalSize += 64; // Overhead for timestamps and counters
|
|
242
353
|
}
|
|
243
354
|
return totalSize;
|
|
244
355
|
}
|
|
356
|
+
/**
|
|
357
|
+
* Starts periodic cleanup of expired entries
|
|
358
|
+
* @private
|
|
359
|
+
*/
|
|
245
360
|
startPeriodicCleanup() {
|
|
246
|
-
const cleanupInterval = Math.min(this.options.maxAge / 4, 5 * 60 * 1000);
|
|
361
|
+
const cleanupInterval = Math.min(this.options.maxAge / 4, 5 * 60 * 1000); // Every 5 minutes or 1/4 of maxAge
|
|
247
362
|
globalThis.setInterval(() => {
|
|
248
363
|
this.cleanupExpiredEntries();
|
|
249
364
|
}, cleanupInterval);
|
|
250
365
|
}
|
|
366
|
+
/**
|
|
367
|
+
* Removes expired entries from the cache
|
|
368
|
+
* @private
|
|
369
|
+
*/
|
|
251
370
|
cleanupExpiredEntries() {
|
|
252
371
|
const keysToDelete = [];
|
|
253
372
|
const now = Date.now();
|
|
@@ -269,10 +388,21 @@ export class ValidationCache {
|
|
|
269
388
|
}
|
|
270
389
|
}
|
|
271
390
|
}
|
|
391
|
+
/**
|
|
392
|
+
* Utility function to generate a hash from manifests
|
|
393
|
+
* @param manifests - Array of manifest objects
|
|
394
|
+
* @returns Hash string
|
|
395
|
+
*/
|
|
272
396
|
export function generateManifestHash(manifests) {
|
|
273
397
|
const manifestString = JSON.stringify(manifests, Object.keys(manifests).sort());
|
|
274
398
|
return hashString(manifestString);
|
|
275
399
|
}
|
|
400
|
+
/**
|
|
401
|
+
* Utility function to generate a hash from plugin configuration
|
|
402
|
+
* @param plugins - Array of plugin names
|
|
403
|
+
* @param pluginConfigs - Plugin configurations
|
|
404
|
+
* @returns Hash string
|
|
405
|
+
*/
|
|
276
406
|
export function generatePluginHash(plugins, pluginConfigs) {
|
|
277
407
|
const configString = JSON.stringify({
|
|
278
408
|
plugins: plugins.sort(),
|
|
@@ -280,6 +410,12 @@ export function generatePluginHash(plugins, pluginConfigs) {
|
|
|
280
410
|
}, Object.keys({ plugins: plugins.sort(), configs: pluginConfigs }).sort());
|
|
281
411
|
return hashString(configString);
|
|
282
412
|
}
|
|
413
|
+
/**
|
|
414
|
+
* Simple string hashing function (djb2 algorithm)
|
|
415
|
+
* @param str - String to hash
|
|
416
|
+
* @returns Hash string
|
|
417
|
+
* @private
|
|
418
|
+
*/
|
|
283
419
|
function hashString(str) {
|
|
284
420
|
let hash = 5381;
|
|
285
421
|
for (let i = 0; i < str.length; i++) {
|
|
@@ -1,13 +1,58 @@
|
|
|
1
1
|
import { ApiObject } from 'cdk8s';
|
|
2
2
|
import type { Chart } from 'cdk8s';
|
|
3
|
+
/**
|
|
4
|
+
* Base interface for all resource providers
|
|
5
|
+
* Provides common functionality and ensures consistent patterns across providers
|
|
6
|
+
*
|
|
7
|
+
* @since 2.8.0+
|
|
8
|
+
*/
|
|
3
9
|
export interface IResourceProvider {
|
|
10
|
+
/** Reference to the CDK8s chart */
|
|
4
11
|
readonly chart: Chart;
|
|
5
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Abstract base class for resource providers
|
|
15
|
+
* Implements common functionality and validation patterns
|
|
16
|
+
*
|
|
17
|
+
* @since 2.4.0
|
|
18
|
+
*/
|
|
6
19
|
export declare abstract class BaseResourceProvider implements IResourceProvider {
|
|
7
20
|
readonly chart: Chart;
|
|
8
21
|
constructor(chart: Chart);
|
|
22
|
+
/**
|
|
23
|
+
* Validates that a name follows Kubernetes naming conventions
|
|
24
|
+
* @param name - Resource name to validate
|
|
25
|
+
* @param resourceType - Type of resource for error messages
|
|
26
|
+
* @throws Error if name is invalid
|
|
27
|
+
*/
|
|
9
28
|
protected validateKubernetesName(name: string, resourceType: string): void;
|
|
29
|
+
/**
|
|
30
|
+
* Validates that labels follow Kubernetes conventions
|
|
31
|
+
* @param labels - Labels to validate
|
|
32
|
+
* @param resourceType - Type of resource for error messages
|
|
33
|
+
*/
|
|
10
34
|
protected validateLabels(labels: Record<string, string> | undefined, resourceType: string): void;
|
|
35
|
+
/**
|
|
36
|
+
* Creates a standardized ApiObject with common metadata
|
|
37
|
+
* @param name - Resource name
|
|
38
|
+
* @param apiVersion - Kubernetes API version
|
|
39
|
+
* @param kind - Kubernetes resource kind
|
|
40
|
+
* @param spec - Resource specification
|
|
41
|
+
* @param labels - Optional labels
|
|
42
|
+
* @param annotations - Optional annotations
|
|
43
|
+
* @returns Created ApiObject
|
|
44
|
+
*/
|
|
11
45
|
protected createApiObject(name: string, apiVersion: string, kind: string, spec: Record<string, unknown>, labels?: Record<string, string>, annotations?: Record<string, string>): ApiObject;
|
|
46
|
+
/**
|
|
47
|
+
* Creates an ApiObject for resources that don't use spec wrapper (like StorageClass)
|
|
48
|
+
* @param name - Resource name
|
|
49
|
+
* @param apiVersion - API version
|
|
50
|
+
* @param kind - Resource kind
|
|
51
|
+
* @param fields - Root-level fields for the resource
|
|
52
|
+
* @param labels - Optional labels
|
|
53
|
+
* @param annotations - Optional annotations
|
|
54
|
+
* @returns Created ApiObject
|
|
55
|
+
* @since 2.8.0+
|
|
56
|
+
*/
|
|
12
57
|
protected createRootLevelApiObject(name: string, apiVersion: string, kind: string, fields: Record<string, unknown>, labels?: Record<string, string>, annotations?: Record<string, string>): ApiObject;
|
|
13
58
|
}
|
|
@@ -1,36 +1,70 @@
|
|
|
1
1
|
import { ApiObject } from 'cdk8s';
|
|
2
|
+
/**
|
|
3
|
+
* Abstract base class for resource providers
|
|
4
|
+
* Implements common functionality and validation patterns
|
|
5
|
+
*
|
|
6
|
+
* @since 2.4.0
|
|
7
|
+
*/
|
|
2
8
|
export class BaseResourceProvider {
|
|
3
9
|
constructor(chart) {
|
|
4
10
|
this.chart = chart;
|
|
5
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Validates that a name follows Kubernetes naming conventions
|
|
14
|
+
* @param name - Resource name to validate
|
|
15
|
+
* @param resourceType - Type of resource for error messages
|
|
16
|
+
* @throws Error if name is invalid
|
|
17
|
+
*/
|
|
6
18
|
validateKubernetesName(name, resourceType) {
|
|
7
19
|
if (!name || typeof name !== 'string') {
|
|
8
20
|
throw new Error(`${resourceType} name must be a non-empty string`);
|
|
9
21
|
}
|
|
22
|
+
// Kubernetes name validation (RFC 1123 subdomain)
|
|
23
|
+
// eslint-disable-next-line security/detect-unsafe-regex -- Simple character class regex is safe
|
|
10
24
|
const nameRegex = /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/;
|
|
11
25
|
if (!nameRegex.test(name) || name.length > 63) {
|
|
12
26
|
throw new Error(`${resourceType} name '${name}' must be a valid Kubernetes name (lowercase alphanumeric and hyphens, max 63 chars)`);
|
|
13
27
|
}
|
|
14
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Validates that labels follow Kubernetes conventions
|
|
31
|
+
* @param labels - Labels to validate
|
|
32
|
+
* @param resourceType - Type of resource for error messages
|
|
33
|
+
*/
|
|
15
34
|
validateLabels(labels, resourceType) {
|
|
16
35
|
if (!labels)
|
|
17
36
|
return;
|
|
18
37
|
Object.entries(labels).forEach(([key, value]) => {
|
|
38
|
+
// Validate label key
|
|
19
39
|
if (key.length > 253) {
|
|
20
40
|
throw new Error(`${resourceType} label key '${key}' exceeds 253 characters`);
|
|
21
41
|
}
|
|
42
|
+
// Validate label value
|
|
22
43
|
if (value.length > 63) {
|
|
23
44
|
throw new Error(`${resourceType} label value '${value}' exceeds 63 characters`);
|
|
24
45
|
}
|
|
46
|
+
// Skip validation for Helm template values
|
|
25
47
|
if (value.includes('{{') && value.includes('}}')) {
|
|
26
48
|
return;
|
|
27
49
|
}
|
|
50
|
+
// Basic character validation
|
|
51
|
+
// eslint-disable-next-line security/detect-unsafe-regex -- Simple character class regex is safe
|
|
28
52
|
const labelRegex = /^[a-zA-Z0-9]([-a-zA-Z0-9_.]*[a-zA-Z0-9])?$/;
|
|
29
53
|
if (!labelRegex.test(value)) {
|
|
30
54
|
throw new Error(`${resourceType} label value '${value}' contains invalid characters`);
|
|
31
55
|
}
|
|
32
56
|
});
|
|
33
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Creates a standardized ApiObject with common metadata
|
|
60
|
+
* @param name - Resource name
|
|
61
|
+
* @param apiVersion - Kubernetes API version
|
|
62
|
+
* @param kind - Kubernetes resource kind
|
|
63
|
+
* @param spec - Resource specification
|
|
64
|
+
* @param labels - Optional labels
|
|
65
|
+
* @param annotations - Optional annotations
|
|
66
|
+
* @returns Created ApiObject
|
|
67
|
+
*/
|
|
34
68
|
createApiObject(name, apiVersion, kind, spec, labels, annotations) {
|
|
35
69
|
this.validateKubernetesName(name, kind);
|
|
36
70
|
this.validateLabels(labels, kind);
|
|
@@ -45,9 +79,21 @@ export class BaseResourceProvider {
|
|
|
45
79
|
spec,
|
|
46
80
|
});
|
|
47
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Creates an ApiObject for resources that don't use spec wrapper (like StorageClass)
|
|
84
|
+
* @param name - Resource name
|
|
85
|
+
* @param apiVersion - API version
|
|
86
|
+
* @param kind - Resource kind
|
|
87
|
+
* @param fields - Root-level fields for the resource
|
|
88
|
+
* @param labels - Optional labels
|
|
89
|
+
* @param annotations - Optional annotations
|
|
90
|
+
* @returns Created ApiObject
|
|
91
|
+
* @since 2.8.0+
|
|
92
|
+
*/
|
|
48
93
|
createRootLevelApiObject(name, apiVersion, kind, fields, labels, annotations) {
|
|
49
94
|
this.validateKubernetesName(name, kind);
|
|
50
95
|
this.validateLabels(labels, kind);
|
|
96
|
+
// Validate that fields doesn't contain reserved keys
|
|
51
97
|
const reservedKeys = ['apiVersion', 'kind', 'metadata'];
|
|
52
98
|
const conflictingKeys = Object.keys(fields).filter((key) => reservedKeys.includes(key));
|
|
53
99
|
if (conflictingKeys.length > 0) {
|
|
@@ -61,7 +107,8 @@ export class BaseResourceProvider {
|
|
|
61
107
|
...(labels && Object.keys(labels).length > 0 ? { labels } : {}),
|
|
62
108
|
...(annotations && Object.keys(annotations).length > 0 ? { annotations } : {}),
|
|
63
109
|
},
|
|
64
|
-
|
|
110
|
+
// Safe: fields validated above to not contain reserved keys (apiVersion, kind, metadata)
|
|
111
|
+
...fields, // Fields go at root level, not under spec
|
|
65
112
|
});
|
|
66
113
|
}
|
|
67
114
|
}
|