timonel 2.10.2 → 2.12.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.
- package/CHANGELOG.md +17 -0
- package/README.md +8 -54
- package/dist/cli.js +65 -52
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/lib/helmChartWriter.js +17 -2
- package/dist/lib/rutter.d.ts +1 -0
- package/dist/lib/rutter.js +64 -11
- package/dist/lib/templates/flexible-subchart.d.ts +1 -0
- package/dist/lib/templates/flexible-subchart.js +101 -0
- package/dist/lib/templates/umbrella-chart.d.ts +13 -2
- package/dist/lib/templates/umbrella-chart.js +128 -42
- package/dist/lib/utils/helmHelpers/envHelpers.d.ts +2 -0
- package/dist/lib/utils/helmHelpers/envHelpers.js +97 -0
- package/dist/lib/utils/helmHelpers/gitopsHelpers.d.ts +2 -0
- package/dist/lib/utils/helmHelpers/gitopsHelpers.js +132 -0
- package/dist/lib/utils/helmHelpers/index.d.ts +23 -0
- package/dist/lib/utils/helmHelpers/index.js +132 -0
- package/dist/lib/utils/helmHelpers/observabilityHelpers.d.ts +2 -0
- package/dist/lib/utils/helmHelpers/observabilityHelpers.js +221 -0
- package/dist/lib/utils/helmHelpers/types.d.ts +16 -0
- package/dist/lib/utils/helmHelpers/types.js +1 -0
- package/dist/lib/utils/helmHelpers/validationHelpers.d.ts +2 -0
- package/dist/lib/utils/helmHelpers/validationHelpers.js +201 -0
- package/dist/lib/utils/helmHelpers.d.ts +11 -1
- package/dist/lib/utils/helmHelpers.js +20 -7
- package/dist/lib/utils/helmYamlSerializer.d.ts +53 -1
- package/dist/lib/utils/helmYamlSerializer.js +325 -29
- package/dist/lib/utils/logger.d.ts +49 -0
- package/dist/lib/utils/logger.js +418 -0
- package/package.json +21 -11
- package/dist/lib/templates/basic-chart.d.ts +0 -19
- package/dist/lib/templates/basic-chart.js +0 -210
- package/dist/lib/templates/subchart.d.ts +0 -27
- package/dist/lib/templates/subchart.js +0 -219
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
# [2.12.0](https://github.com/KenkoGeek/timonel/compare/v2.11.0...v2.12.0) (2025-09-28)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
- **cli:** harden flag handling and path validation ([#130](https://github.com/KenkoGeek/timonel/issues/130)) ([2186d88](https://github.com/KenkoGeek/timonel/commit/2186d886428752025e7bf531c4a6541dade683a6))
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
- **docs:** update references and CLI [skip ci] ([23704d3](https://github.com/KenkoGeek/timonel/commit/23704d3164cfbb92d183af40c70e27a32b566d06))
|
|
10
|
+
- **docs:** update references and CLI [skip ci] ([be4bcdc](https://github.com/KenkoGeek/timonel/commit/be4bcdca62cb3a9339e5ad70efc0ba31a6d9d5be))
|
|
11
|
+
|
|
12
|
+
# [2.11.0](https://github.com/KenkoGeek/timonel/compare/v2.10.2...v2.11.0) (2025-09-26)
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
- **helm:** add comprehensive helm helpers with Helm template validation ([#123](https://github.com/KenkoGeek/timonel/issues/123)) ([bda8115](https://github.com/KenkoGeek/timonel/commit/bda811546d308eaa3ee5aec7fd8356b4a508940e))
|
|
17
|
+
|
|
1
18
|
## [2.10.2](https://github.com/KenkoGeek/timonel/compare/v2.10.1...v2.10.2) (2025-09-20)
|
|
2
19
|
|
|
3
20
|
### Bug Fixes
|
package/README.md
CHANGED
|
@@ -15,22 +15,18 @@ Helm charts using cdk8s. Define Kubernetes resources with type-safe classes and
|
|
|
15
15
|
Helm charts with `Chart.yaml`, `values.yaml`, environment-specific values files, and `templates/`
|
|
16
16
|
directory.
|
|
17
17
|
|
|
18
|
-
> 🚀 **New in v2.9.2**: Enhanced umbrella chart support with flexible subchart templates, improved
|
|
19
|
-
> CLI version handling, and optimized GitHub workflows.
|
|
20
|
-
|
|
21
18
|
## ✨ Key Features
|
|
22
19
|
|
|
23
20
|
- **🔒 Type-safe API** with strict TypeScript and cdk8s constructs
|
|
24
21
|
- **🔧 Flexible resource creation** with built-in methods and `addManifest()` for custom resources
|
|
25
22
|
- **🌍 Multi-environment support** with automatic values files generation
|
|
26
23
|
- **☂️ Umbrella Charts** for managing multiple subcharts as a single unit
|
|
24
|
+
- **🛠️ Enhanced Helm Helpers** with environment, GitOps, observability, and validation helpers
|
|
27
25
|
- **☁️ Cloud integrations**:
|
|
28
|
-
- **AWS**: EBS/EFS StorageClass, ALB Ingress, IRSA ServiceAccount
|
|
29
|
-
- **Azure**: Azure Disk, Azure Files, AKS integrations
|
|
30
|
-
- **GCP**: GCE Persistent Disk, GKE integrations
|
|
26
|
+
- **AWS**: EBS/EFS StorageClass, ALB Ingress, IRSA ServiceAccount, ECR, and Karpenter
|
|
31
27
|
- **🛡️ Security-first approach** with NetworkPolicies and best practices
|
|
32
28
|
- **⚡ Minimal CLI** (`tl`) for scaffolding and chart generation
|
|
33
|
-
- **📦 Flexible subchart templates** supporting cdk8s
|
|
29
|
+
- **📦 Flexible subchart templates** supporting cdk8s and cdk8s-plus-33
|
|
34
30
|
|
|
35
31
|
## 🚀 Quick Start
|
|
36
32
|
|
|
@@ -42,9 +38,6 @@ npm install -g timonel
|
|
|
42
38
|
|
|
43
39
|
# Or use with pnpm
|
|
44
40
|
pnpm add -g timonel
|
|
45
|
-
|
|
46
|
-
# Verify installation
|
|
47
|
-
tl --version
|
|
48
41
|
```
|
|
49
42
|
|
|
50
43
|
### Basic Usage
|
|
@@ -161,10 +154,10 @@ If you get `Error: Cannot find module 'cdk8s'` when running `tl umbrella synth`:
|
|
|
161
154
|
"version": "1.0.0",
|
|
162
155
|
"type": "module",
|
|
163
156
|
"dependencies": {
|
|
164
|
-
"cdk8s": "^2.70.
|
|
165
|
-
"cdk8s-plus-33": "^2.3.
|
|
157
|
+
"cdk8s": "^2.70.16",
|
|
158
|
+
"cdk8s-plus-33": "^2.3.8",
|
|
166
159
|
"constructs": "^10.4.2",
|
|
167
|
-
"timonel": "^2.
|
|
160
|
+
"timonel": "^2.11.0"
|
|
168
161
|
},
|
|
169
162
|
"devDependencies": {
|
|
170
163
|
"@types/node": "^24.5.2",
|
|
@@ -180,45 +173,6 @@ npm install
|
|
|
180
173
|
tl umbrella synth # Now it works!
|
|
181
174
|
```
|
|
182
175
|
|
|
183
|
-
### Version Display Issues
|
|
184
|
-
|
|
185
|
-
If the CLI shows an incorrect version:
|
|
186
|
-
|
|
187
|
-
**Problem**: The CLI might be using cached or hardcoded version information.
|
|
188
|
-
|
|
189
|
-
**Solution**: Rebuild the project:
|
|
190
|
-
|
|
191
|
-
```bash
|
|
192
|
-
# In the Timonel project directory
|
|
193
|
-
pnpm build
|
|
194
|
-
tl --version # Should show correct version
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### TypeScript Compilation Errors
|
|
198
|
-
|
|
199
|
-
If you encounter TypeScript errors when using umbrella charts:
|
|
200
|
-
|
|
201
|
-
**Problem**: Type mismatches in subchart configurations.
|
|
202
|
-
|
|
203
|
-
**Solution**: Ensure proper type casting for version and description fields:
|
|
204
|
-
|
|
205
|
-
```typescript
|
|
206
|
-
// ✅ Correct
|
|
207
|
-
const subchart = {
|
|
208
|
-
name: 'my-service',
|
|
209
|
-
version: '1.0.0' as string,
|
|
210
|
-
description: 'My service' as string,
|
|
211
|
-
chart: myChartFunction,
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
// ❌ Incorrect
|
|
215
|
-
const subchart = {
|
|
216
|
-
name: 'my-service',
|
|
217
|
-
version: someUnknownValue, // This will cause TypeScript errors
|
|
218
|
-
chart: myChartFunction,
|
|
219
|
-
};
|
|
220
|
-
```
|
|
221
|
-
|
|
222
176
|
## 🤝 Contributing
|
|
223
177
|
|
|
224
178
|
See our [Contributing Guide](https://github.com/KenkoGeek/timonel/wiki/Contributing) for development
|
|
@@ -246,5 +200,5 @@ MIT
|
|
|
246
200
|
[maintained-url]: https://github.com/kenkogeek/
|
|
247
201
|
[ci-badge]: https://github.com/KenkoGeek/timonel/actions/workflows/test.yaml/badge.svg?branch=main
|
|
248
202
|
[ci-url]: https://github.com/KenkoGeek/timonel/actions/workflows/test.yaml
|
|
249
|
-
[codeql-badge]: https://github.com/KenkoGeek/timonel/actions/workflows/codeql.
|
|
250
|
-
[codeql-url]: https://github.com/KenkoGeek/timonel/actions/workflows/codeql.
|
|
203
|
+
[codeql-badge]: https://github.com/KenkoGeek/timonel/actions/workflows/codeql.yml/badge.svg
|
|
204
|
+
[codeql-url]: https://github.com/KenkoGeek/timonel/actions/workflows/codeql.yml
|
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import fs from 'fs';
|
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import { SecurityUtils } from './lib/security.js';
|
|
7
|
+
import { createLogger } from './lib/utils/logger.js';
|
|
7
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
9
|
const __dirname = path.dirname(__filename);
|
|
9
10
|
const UMBRELLA_CONFIG_FILE = 'umbrella.config.json';
|
|
@@ -11,46 +12,30 @@ const UMBRELLA_FILE_NAME = 'umbrella.ts';
|
|
|
11
12
|
const PACKAGE_JSON_FILE = 'package.json';
|
|
12
13
|
function getVersion() {
|
|
13
14
|
try {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
for (const packagePath of possiblePaths) {
|
|
21
|
-
try {
|
|
22
|
-
const validatedPath = SecurityUtils.validatePath(packagePath, process.cwd());
|
|
23
|
-
if (fs.existsSync(validatedPath)) {
|
|
24
|
-
try {
|
|
25
|
-
const packageJson = JSON.parse(fs.readFileSync(validatedPath, 'utf8'));
|
|
26
|
-
return packageJson.version || 'unknown';
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
15
|
+
const packagePath = path.resolve(__dirname, '..', PACKAGE_JSON_FILE);
|
|
16
|
+
const allowedBase = path.resolve(__dirname, '..');
|
|
17
|
+
const validatedPath = SecurityUtils.validatePath(packagePath, allowedBase);
|
|
18
|
+
const packageJson = JSON.parse(fs.readFileSync(validatedPath, 'utf8'));
|
|
19
|
+
if (packageJson.version) {
|
|
20
|
+
return packageJson.version;
|
|
36
21
|
}
|
|
37
|
-
if (process.env.npm_package_version) {
|
|
38
|
-
return process.env.npm_package_version;
|
|
39
|
-
}
|
|
40
|
-
return 'unknown';
|
|
41
22
|
}
|
|
42
23
|
catch {
|
|
43
|
-
return 'unknown';
|
|
44
24
|
}
|
|
25
|
+
if (process.env.npm_package_version) {
|
|
26
|
+
return process.env.npm_package_version;
|
|
27
|
+
}
|
|
28
|
+
return 'unknown';
|
|
45
29
|
}
|
|
30
|
+
const cliLogger = createLogger('cli');
|
|
46
31
|
function log(msg, silent = false) {
|
|
47
32
|
if (!silent) {
|
|
48
|
-
|
|
33
|
+
cliLogger.info(msg, { operation: 'cli_log' });
|
|
49
34
|
}
|
|
50
35
|
}
|
|
51
36
|
function logError(msg, silent = false) {
|
|
52
37
|
if (!silent) {
|
|
53
|
-
|
|
38
|
+
cliLogger.error(msg, { operation: 'cli_error' });
|
|
54
39
|
}
|
|
55
40
|
}
|
|
56
41
|
function usageAndExit(msg, silent = false) {
|
|
@@ -80,7 +65,6 @@ function usageAndExit(msg, silent = false) {
|
|
|
80
65
|
' --env <environment> Use environment-specific values',
|
|
81
66
|
' --set <key=value> Override values (can be used multiple times)',
|
|
82
67
|
' --help, -h Show this help message',
|
|
83
|
-
' --version, -v Show version information',
|
|
84
68
|
'',
|
|
85
69
|
'Examples:',
|
|
86
70
|
' tl init my-app',
|
|
@@ -104,8 +88,8 @@ async function cmdInit(name, silent = false) {
|
|
|
104
88
|
const base = path.join(process.cwd(), validName);
|
|
105
89
|
const chartFile = path.join(base, 'chart.ts');
|
|
106
90
|
fs.mkdirSync(base, { recursive: true });
|
|
107
|
-
const {
|
|
108
|
-
fs.writeFileSync(chartFile,
|
|
91
|
+
const { generateFlexibleSubchartTemplate } = await import('./lib/templates/flexible-subchart.js');
|
|
92
|
+
fs.writeFileSync(chartFile, generateFlexibleSubchartTemplate(validName));
|
|
109
93
|
log(`Chart created at ${base}`, silent);
|
|
110
94
|
log(`Generated chart.ts file`, silent);
|
|
111
95
|
log(`Run 'tl synth ${validName}' to generate complete Helm chart`, silent);
|
|
@@ -161,7 +145,23 @@ await import(pathToFileURL('${tempChartFile}').href);
|
|
|
161
145
|
}
|
|
162
146
|
}
|
|
163
147
|
async function cmdValidate(flags) {
|
|
164
|
-
const
|
|
148
|
+
const lintArgs = ['lint', '.'];
|
|
149
|
+
if (flags?.env) {
|
|
150
|
+
try {
|
|
151
|
+
const sanitizedEnv = SecurityUtils.sanitizeEnvironmentName(flags.env);
|
|
152
|
+
lintArgs.push('-f', `values-${sanitizedEnv}.yaml`);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
console.error(SecurityUtils.sanitizeLogMessage(error.message));
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (flags?.set) {
|
|
160
|
+
for (const setValue of flags.set) {
|
|
161
|
+
lintArgs.push('--set', setValue);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const result = spawnSync('helm', lintArgs, {
|
|
165
165
|
stdio: flags?.silent ? 'pipe' : 'inherit',
|
|
166
166
|
encoding: 'utf8',
|
|
167
167
|
});
|
|
@@ -179,6 +179,21 @@ async function cmdDeploy(release, namespace, flags) {
|
|
|
179
179
|
if (namespace) {
|
|
180
180
|
args.push('--namespace', namespace);
|
|
181
181
|
}
|
|
182
|
+
if (flags?.env) {
|
|
183
|
+
try {
|
|
184
|
+
const sanitizedEnv = SecurityUtils.sanitizeEnvironmentName(flags.env);
|
|
185
|
+
args.push('-f', `values-${sanitizedEnv}.yaml`);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.error(SecurityUtils.sanitizeLogMessage(error.message));
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (flags?.set) {
|
|
193
|
+
for (const setValue of flags.set) {
|
|
194
|
+
args.push('--set', setValue);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
182
197
|
const result = spawnSync('helm', args.filter((arg) => Boolean(arg)), {
|
|
183
198
|
stdio: flags?.silent ? 'pipe' : 'inherit',
|
|
184
199
|
encoding: 'utf8',
|
|
@@ -342,20 +357,31 @@ async function cmdUmbrellaAdd(subchartPath, silent = false) {
|
|
|
342
357
|
}
|
|
343
358
|
const config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
|
|
344
359
|
const subchartName = path.basename(validSubchartPath);
|
|
345
|
-
const
|
|
360
|
+
const chartsRoot = path.join(process.cwd(), 'charts');
|
|
361
|
+
const targetSubchartPath = path.join(chartsRoot, validSubchartPath);
|
|
362
|
+
let subchartDir;
|
|
363
|
+
try {
|
|
364
|
+
subchartDir = SecurityUtils.validatePath(targetSubchartPath, chartsRoot);
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
console.error(SecurityUtils.sanitizeLogMessage(error.message));
|
|
368
|
+
process.exit(1);
|
|
369
|
+
}
|
|
346
370
|
fs.mkdirSync(subchartDir, { recursive: true });
|
|
371
|
+
const relativeSubchartPath = path.relative(chartsRoot, subchartDir) || subchartName;
|
|
372
|
+
const normalizedSubchartPath = relativeSubchartPath.split(path.sep).join('/');
|
|
347
373
|
const chartFile = path.join(subchartDir, 'chart.ts');
|
|
348
|
-
const {
|
|
349
|
-
const subchartContent =
|
|
374
|
+
const { generateFlexibleSubchartTemplate } = await import('./lib/templates/flexible-subchart.js');
|
|
375
|
+
const subchartContent = generateFlexibleSubchartTemplate(subchartName);
|
|
350
376
|
fs.writeFileSync(chartFile, subchartContent);
|
|
351
377
|
config.subcharts.push({
|
|
352
378
|
name: subchartName,
|
|
353
379
|
version: getVersion(),
|
|
354
|
-
path: `./charts/${
|
|
380
|
+
path: `./charts/${normalizedSubchartPath}/chart.ts`,
|
|
355
381
|
});
|
|
356
382
|
fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
|
|
357
|
-
updateUmbrellaTs(
|
|
358
|
-
log(`Subchart ${subchartName} added to umbrella at path '${
|
|
383
|
+
updateUmbrellaTs(normalizedSubchartPath, subchartName);
|
|
384
|
+
log(`Subchart ${subchartName} added to umbrella at path '${normalizedSubchartPath}'`, silent);
|
|
359
385
|
}
|
|
360
386
|
async function cmdUmbrellaSynth(outDir, flags) {
|
|
361
387
|
const umbrellaFile = path.join(process.cwd(), UMBRELLA_FILE_NAME);
|
|
@@ -429,13 +455,6 @@ function parseFlags(args) {
|
|
|
429
455
|
}
|
|
430
456
|
break;
|
|
431
457
|
}
|
|
432
|
-
case '--version':
|
|
433
|
-
case '-v':
|
|
434
|
-
if (!flags.silent) {
|
|
435
|
-
console.log(`Timonel v${getVersion()}`);
|
|
436
|
-
}
|
|
437
|
-
process.exit(0);
|
|
438
|
-
break;
|
|
439
458
|
case '--help':
|
|
440
459
|
case '-h':
|
|
441
460
|
usageAndExit(undefined, flags.silent);
|
|
@@ -481,12 +500,6 @@ async function main() {
|
|
|
481
500
|
usageAndExit(undefined, isSilent);
|
|
482
501
|
return;
|
|
483
502
|
}
|
|
484
|
-
if (args.includes('--version') || args.includes('-v')) {
|
|
485
|
-
if (!isSilent) {
|
|
486
|
-
console.log(`Timonel v${getVersion()}`);
|
|
487
|
-
}
|
|
488
|
-
process.exit(0);
|
|
489
|
-
}
|
|
490
503
|
const command = args.shift();
|
|
491
504
|
const flags = parseFlags(args);
|
|
492
505
|
await executeCommand(command, args, flags);
|
package/dist/index.d.ts
CHANGED
|
@@ -3,12 +3,11 @@ export * from './lib/helmChartWriter.js';
|
|
|
3
3
|
export * from './lib/rutter.js';
|
|
4
4
|
export * from './lib/security.js';
|
|
5
5
|
export * from './lib/umbrella.js';
|
|
6
|
-
export { BasicChart } from './lib/templates/basic-chart.js';
|
|
7
6
|
export { FlexibleSubchart, createFlexibleSubchart } from './lib/templates/flexible-subchart.js';
|
|
8
|
-
export { Subchart } from './lib/templates/subchart.js';
|
|
9
7
|
export { UmbrellaChartTemplate as UmbrellaChart } from './lib/templates/umbrella-chart.js';
|
|
10
8
|
export type { ChartProps, SubchartProps } from './lib/types.js';
|
|
11
9
|
export type { AWSALBIngressSpec, AWSEBSStorageClassSpec, AWSEFSStorageClassSpec, AWSIRSAServiceAccountSpec, } from './lib/resources/cloud/aws/awsResources.js';
|
|
12
10
|
export type { KarpenterDisruption, KarpenterDisruptionBudget, KarpenterEC2NodeClassSpec, KarpenterNodeClaimSpec, KarpenterNodePoolSpec, } from './lib/resources/cloud/aws/karpenterResources.js';
|
|
13
11
|
export { DEFAULT_TERMINATION_GRACE_PERIOD, isValidDisruptionBudget, isValidKubernetesDuration, KarpenterVersionUtils, } from './lib/resources/cloud/aws/karpenterResources.js';
|
|
14
12
|
export { AWS_HELPERS, createHelper, FILE_ACCESS_HELPERS, formatHelpers, generateHelpersTemplate, getDefaultHelpers, getHelpersByCategory, KUBERNETES_HELPERS, SPRIG_HELPERS, STANDARD_HELPERS, TEMPLATE_FUNCTION_HELPERS, type HelperDefinition, } from './lib/utils/helmHelpers.js';
|
|
13
|
+
export { createLogger, logger, LogLevel, TimonelLogger, type LogContext, type LoggerConfig, } from './lib/utils/logger.js';
|
package/dist/index.js
CHANGED
|
@@ -3,9 +3,8 @@ export * from './lib/helmChartWriter.js';
|
|
|
3
3
|
export * from './lib/rutter.js';
|
|
4
4
|
export * from './lib/security.js';
|
|
5
5
|
export * from './lib/umbrella.js';
|
|
6
|
-
export { BasicChart } from './lib/templates/basic-chart.js';
|
|
7
6
|
export { FlexibleSubchart, createFlexibleSubchart } from './lib/templates/flexible-subchart.js';
|
|
8
|
-
export { Subchart } from './lib/templates/subchart.js';
|
|
9
7
|
export { UmbrellaChartTemplate as UmbrellaChart } from './lib/templates/umbrella-chart.js';
|
|
10
8
|
export { DEFAULT_TERMINATION_GRACE_PERIOD, isValidDisruptionBudget, isValidKubernetesDuration, KarpenterVersionUtils, } from './lib/resources/cloud/aws/karpenterResources.js';
|
|
11
9
|
export { AWS_HELPERS, createHelper, FILE_ACCESS_HELPERS, formatHelpers, generateHelpersTemplate, getDefaultHelpers, getHelpersByCategory, KUBERNETES_HELPERS, SPRIG_HELPERS, STANDARD_HELPERS, TEMPLATE_FUNCTION_HELPERS, } from './lib/utils/helmHelpers.js';
|
|
10
|
+
export { createLogger, logger, LogLevel, TimonelLogger, } from './lib/utils/logger.js';
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { SecurityUtils } from './security.js';
|
|
4
|
+
import { createLogger } from './utils/logger.js';
|
|
4
5
|
import { dumpHelmAwareYaml } from './utils/helmYamlSerializer.js';
|
|
5
6
|
export class HelmChartWriter {
|
|
6
7
|
static write(opts) {
|
|
7
8
|
const { outDir, meta, defaultValues = {}, envValues = {}, assets, helpersTpl, notesTpl, valuesSchema, } = opts;
|
|
8
|
-
|
|
9
|
+
const logger = createLogger('helm-chart-writer');
|
|
10
|
+
const timer = logger.time('helm_chart_write');
|
|
11
|
+
logger.info('Starting Helm chart write operation', {
|
|
12
|
+
chartName: meta.name,
|
|
13
|
+
version: meta.version,
|
|
14
|
+
outputDirectory: outDir,
|
|
15
|
+
assetCount: assets.length,
|
|
16
|
+
operation: 'helm_write_start',
|
|
17
|
+
});
|
|
9
18
|
const validatedOutDir = SecurityUtils.validatePath(outDir, process.cwd());
|
|
10
19
|
this.createDirectories(validatedOutDir);
|
|
11
20
|
this.writeChartYaml(validatedOutDir, meta);
|
|
@@ -15,7 +24,13 @@ export class HelmChartWriter {
|
|
|
15
24
|
this.writeNotes(validatedOutDir, notesTpl);
|
|
16
25
|
this.writeSchema(validatedOutDir, valuesSchema);
|
|
17
26
|
this.writeHelmIgnore(validatedOutDir);
|
|
18
|
-
|
|
27
|
+
logger.info('Helm chart write operation completed successfully', {
|
|
28
|
+
chartName: meta.name,
|
|
29
|
+
version: meta.version,
|
|
30
|
+
outputDirectory: validatedOutDir,
|
|
31
|
+
operation: 'helm_write_complete',
|
|
32
|
+
});
|
|
33
|
+
timer();
|
|
19
34
|
}
|
|
20
35
|
static createDirectories(outDir) {
|
|
21
36
|
fs.mkdirSync(path.join(outDir, 'templates'), { recursive: true });
|
package/dist/lib/rutter.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export declare class Rutter {
|
|
|
16
16
|
private readonly karpenterResources;
|
|
17
17
|
private readonly meta;
|
|
18
18
|
private readonly props;
|
|
19
|
+
private readonly logger;
|
|
19
20
|
constructor(props: RutterProps);
|
|
20
21
|
addAWSEBSStorageClass(spec: AWSEBSStorageClassSpec): ApiObject;
|
|
21
22
|
addAWSEFSStorageClass(spec: AWSEFSStorageClassSpec): ApiObject;
|
package/dist/lib/rutter.js
CHANGED
|
@@ -3,17 +3,24 @@ import * as jsYaml from 'js-yaml';
|
|
|
3
3
|
import { include } from './helm.js';
|
|
4
4
|
import { HelmChartWriter } from './helmChartWriter.js';
|
|
5
5
|
import { AWSResources } from './resources/cloud/aws/awsResources.js';
|
|
6
|
+
import { createLogger } from './utils/logger.js';
|
|
6
7
|
import { KarpenterResources } from './resources/cloud/aws/karpenterResources.js';
|
|
7
8
|
import { dumpHelmAwareYaml } from './utils/helmYamlSerializer.js';
|
|
8
9
|
import { generateHelpersTemplate } from './utils/helmHelpers.js';
|
|
9
10
|
export class Rutter {
|
|
10
11
|
constructor(props) {
|
|
11
12
|
this.assets = [];
|
|
12
|
-
|
|
13
|
+
this.logger = createLogger('rutter');
|
|
13
14
|
this.defaultValues = props.defaultValues ?? {};
|
|
14
15
|
this.envValues = props.envValues ?? {};
|
|
15
16
|
this.meta = props.meta;
|
|
16
17
|
this.props = props;
|
|
18
|
+
this.logger.info('Initializing chart', {
|
|
19
|
+
chartName: props.meta.name,
|
|
20
|
+
version: props.meta.version,
|
|
21
|
+
namespace: props.namespace,
|
|
22
|
+
operation: 'chart_initialization',
|
|
23
|
+
});
|
|
17
24
|
this.app = new App();
|
|
18
25
|
this.chart = new Chart(this.app, props.meta.name, {
|
|
19
26
|
...props.chartProps,
|
|
@@ -134,24 +141,40 @@ ${yamlContent.trim()}
|
|
|
134
141
|
}
|
|
135
142
|
validateManifestStructure(manifest) {
|
|
136
143
|
if (!manifest.apiVersion) {
|
|
137
|
-
|
|
144
|
+
this.logger.error('Validation failed: Missing apiVersion', {
|
|
145
|
+
operation: 'manifest_validation',
|
|
146
|
+
issue: 'missing_api_version',
|
|
147
|
+
});
|
|
138
148
|
throw new Error('Manifest must have an apiVersion');
|
|
139
149
|
}
|
|
140
150
|
if (!manifest.kind) {
|
|
141
|
-
|
|
151
|
+
this.logger.error('Validation failed: Missing kind', {
|
|
152
|
+
operation: 'manifest_validation',
|
|
153
|
+
issue: 'missing_kind',
|
|
154
|
+
});
|
|
142
155
|
throw new Error('Manifest must have a kind');
|
|
143
156
|
}
|
|
144
157
|
if (!manifest.metadata) {
|
|
145
|
-
|
|
158
|
+
this.logger.error('Validation failed: Missing metadata', {
|
|
159
|
+
operation: 'manifest_validation',
|
|
160
|
+
issue: 'missing_metadata',
|
|
161
|
+
});
|
|
146
162
|
throw new Error('Manifest must have metadata');
|
|
147
163
|
}
|
|
148
164
|
const metadata = manifest.metadata;
|
|
149
165
|
if (!metadata.name) {
|
|
150
|
-
|
|
166
|
+
this.logger.error('Validation failed: Missing metadata.name', {
|
|
167
|
+
operation: 'manifest_validation',
|
|
168
|
+
issue: 'missing_metadata_name',
|
|
169
|
+
});
|
|
151
170
|
throw new Error('Manifest metadata must have a name');
|
|
152
171
|
}
|
|
153
172
|
if (typeof metadata.name !== 'string') {
|
|
154
|
-
|
|
173
|
+
this.logger.error('Validation failed: metadata.name must be a string', {
|
|
174
|
+
operation: 'manifest_validation',
|
|
175
|
+
issue: 'invalid_metadata_name_type',
|
|
176
|
+
actual_type: typeof metadata.name,
|
|
177
|
+
});
|
|
155
178
|
throw new Error('Manifest metadata.name must be a string');
|
|
156
179
|
}
|
|
157
180
|
}
|
|
@@ -168,7 +191,11 @@ ${yamlContent.trim()}
|
|
|
168
191
|
return [...this.assets];
|
|
169
192
|
}
|
|
170
193
|
toSynthArray() {
|
|
171
|
-
|
|
194
|
+
const timer = this.logger.time('chart_synthesis');
|
|
195
|
+
this.logger.debug('Starting chart synthesis', {
|
|
196
|
+
chartName: this.meta.name,
|
|
197
|
+
operation: 'synthesis_start',
|
|
198
|
+
});
|
|
172
199
|
const apiObjectIds = [];
|
|
173
200
|
for (const child of this.chart.node.children) {
|
|
174
201
|
if (child instanceof ApiObject && !child.node.id.endsWith('-placeholder')) {
|
|
@@ -184,7 +211,12 @@ ${yamlContent.trim()}
|
|
|
184
211
|
}
|
|
185
212
|
return true;
|
|
186
213
|
});
|
|
187
|
-
|
|
214
|
+
this.logger.info('Processing manifest objects', {
|
|
215
|
+
chartName: this.meta.name,
|
|
216
|
+
manifestCount: manifestObjs.length,
|
|
217
|
+
apiObjectCount: apiObjectIds.length,
|
|
218
|
+
operation: 'manifest_processing',
|
|
219
|
+
});
|
|
188
220
|
const enriched = manifestObjs.map((obj) => {
|
|
189
221
|
if (obj && typeof obj === 'object') {
|
|
190
222
|
const o = obj;
|
|
@@ -229,6 +261,13 @@ ${yamlContent.trim()}
|
|
|
229
261
|
this.assets.forEach((asset) => {
|
|
230
262
|
synthAssets.push({ id: asset.id, yaml: asset.yaml });
|
|
231
263
|
});
|
|
264
|
+
this.logger.info('Chart synthesis completed', {
|
|
265
|
+
chartName: this.meta.name,
|
|
266
|
+
totalAssets: synthAssets.length,
|
|
267
|
+
additionalAssets: this.assets.length,
|
|
268
|
+
operation: 'synthesis_complete',
|
|
269
|
+
});
|
|
270
|
+
timer();
|
|
232
271
|
return synthAssets;
|
|
233
272
|
}
|
|
234
273
|
processHelmTemplates(yaml) {
|
|
@@ -399,7 +438,12 @@ ${yamlContent.trim()}
|
|
|
399
438
|
return processed;
|
|
400
439
|
}
|
|
401
440
|
write(outDir) {
|
|
402
|
-
|
|
441
|
+
const timer = this.logger.time('chart_write');
|
|
442
|
+
this.logger.info('Starting chart write operation', {
|
|
443
|
+
chartName: this.meta.name,
|
|
444
|
+
outputDirectory: outDir,
|
|
445
|
+
operation: 'chart_write_start',
|
|
446
|
+
});
|
|
403
447
|
let helpersContent;
|
|
404
448
|
if (this.props.helpersTpl) {
|
|
405
449
|
if (typeof this.props.helpersTpl === 'string') {
|
|
@@ -420,7 +464,11 @@ ${helper.template}
|
|
|
420
464
|
helpersContent = generateHelpersTemplate(this.props.cloudProvider);
|
|
421
465
|
}
|
|
422
466
|
const synthAssets = this.toSynthArray();
|
|
423
|
-
|
|
467
|
+
this.logger.info('Generated assets for chart', {
|
|
468
|
+
chartName: this.meta.name,
|
|
469
|
+
assetCount: synthAssets.length,
|
|
470
|
+
operation: 'assets_generated',
|
|
471
|
+
});
|
|
424
472
|
HelmChartWriter.write({
|
|
425
473
|
outDir,
|
|
426
474
|
meta: this.meta,
|
|
@@ -429,7 +477,12 @@ ${helper.template}
|
|
|
429
477
|
assets: synthAssets,
|
|
430
478
|
helpersTpl: helpersContent,
|
|
431
479
|
});
|
|
432
|
-
|
|
480
|
+
this.logger.info('Chart write operation completed successfully', {
|
|
481
|
+
chartName: this.meta.name,
|
|
482
|
+
outputDirectory: outDir,
|
|
483
|
+
operation: 'chart_write_complete',
|
|
484
|
+
});
|
|
485
|
+
timer();
|
|
433
486
|
}
|
|
434
487
|
}
|
|
435
488
|
Rutter.HELPER_NAME = 'chart.name';
|
|
@@ -105,3 +105,104 @@ export class FlexibleSubchart extends Chart {
|
|
|
105
105
|
export function createFlexibleSubchart(scope, id, config) {
|
|
106
106
|
return new FlexibleSubchart(scope, id, config);
|
|
107
107
|
}
|
|
108
|
+
export function generateFlexibleSubchartTemplate(name) {
|
|
109
|
+
return `import { App } from 'cdk8s';
|
|
110
|
+
import { Rutter } from 'timonel';
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Creates a new chart with simple Rutter implementation
|
|
114
|
+
* @returns Rutter instance for Helm chart generation
|
|
115
|
+
* @since 2.11.0
|
|
116
|
+
*/
|
|
117
|
+
export default function createChart() {
|
|
118
|
+
const app = new App({ outdir: 'dist' });
|
|
119
|
+
|
|
120
|
+
const rutter = new Rutter({
|
|
121
|
+
meta: {
|
|
122
|
+
name: '${name}',
|
|
123
|
+
version: '1.0.0',
|
|
124
|
+
description: '${name} subchart'
|
|
125
|
+
},
|
|
126
|
+
scope: app,
|
|
127
|
+
defaultValues: {
|
|
128
|
+
appName: '${name}',
|
|
129
|
+
image: 'nginx:latest',
|
|
130
|
+
port: 80,
|
|
131
|
+
replicas: 1,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Add simple deployment
|
|
136
|
+
rutter.addManifest({
|
|
137
|
+
apiVersion: 'apps/v1',
|
|
138
|
+
kind: 'Deployment',
|
|
139
|
+
metadata: {
|
|
140
|
+
name: '${name}',
|
|
141
|
+
labels: {
|
|
142
|
+
'app.kubernetes.io/name': '${name}'
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
spec: {
|
|
146
|
+
replicas: 1,
|
|
147
|
+
selector: {
|
|
148
|
+
matchLabels: {
|
|
149
|
+
'app.kubernetes.io/name': '${name}'
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
template: {
|
|
153
|
+
metadata: {
|
|
154
|
+
labels: {
|
|
155
|
+
'app.kubernetes.io/name': '${name}'
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
spec: {
|
|
159
|
+
containers: [{
|
|
160
|
+
name: '${name}',
|
|
161
|
+
image: 'nginx:latest',
|
|
162
|
+
ports: [{
|
|
163
|
+
containerPort: 80
|
|
164
|
+
}],
|
|
165
|
+
env: [
|
|
166
|
+
{ name: 'APP_NAME', value: '${name}' },
|
|
167
|
+
{ name: 'PORT', value: '80' }
|
|
168
|
+
]
|
|
169
|
+
}]
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}, 'deployment');
|
|
174
|
+
|
|
175
|
+
// Add Service
|
|
176
|
+
rutter.addManifest({
|
|
177
|
+
apiVersion: 'v1',
|
|
178
|
+
kind: 'Service',
|
|
179
|
+
metadata: {
|
|
180
|
+
name: '${name}',
|
|
181
|
+
labels: {
|
|
182
|
+
'app.kubernetes.io/name': '${name}'
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
spec: {
|
|
186
|
+
type: 'ClusterIP',
|
|
187
|
+
ports: [{
|
|
188
|
+
port: 80,
|
|
189
|
+
targetPort: 80,
|
|
190
|
+
protocol: 'TCP',
|
|
191
|
+
name: 'http'
|
|
192
|
+
}],
|
|
193
|
+
selector: {
|
|
194
|
+
'app.kubernetes.io/name': '${name}'
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}, 'service');
|
|
198
|
+
|
|
199
|
+
return rutter;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Auto-execute when run directly
|
|
203
|
+
if (import.meta.url === new URL(import.meta.url).href) {
|
|
204
|
+
const chart = createChart();
|
|
205
|
+
chart.write('dist');
|
|
206
|
+
}
|
|
207
|
+
`;
|
|
208
|
+
}
|