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/README.md
CHANGED
|
@@ -10,832 +10,460 @@
|
|
|
10
10
|
[![TypeScript][ts-badge]][ts-url]
|
|
11
11
|
[![Maintained by KenkoGeek][maintained-badge]][maintained-url]
|
|
12
12
|
|
|
13
|
-
**Timonel**
|
|
14
|
-
|
|
15
|
-
Helm
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
## ✨ Key Features
|
|
19
|
-
|
|
20
|
-
### Core Capabilities
|
|
21
|
-
|
|
22
|
-
- **🔒 Type-safe API** with strict TypeScript and cdk8s constructs
|
|
23
|
-
- **🔧 Flexible resource creation** with built-in methods and `addManifest()` for custom resources
|
|
24
|
-
- **🌍 Multi-environment support** with automatic values files generation
|
|
25
|
-
- **☂️ Umbrella Charts** for managing multiple subcharts as a single unit
|
|
26
|
-
- **⚡ Minimal CLI** (`tl`) for scaffolding, synthesis, validation, and deployment
|
|
27
|
-
- **📦 Flexible subchart templates** supporting both cdk8s and cdk8s-plus-33
|
|
28
|
-
|
|
29
|
-
### Type-Safe Helm Helpers (v3.0+)
|
|
30
|
-
|
|
31
|
-
#### ValuesRef System (NEW in v3.0 - RECOMMENDED)
|
|
32
|
-
|
|
33
|
-
Type-safe proxy-based values references with full IDE support:
|
|
34
|
-
|
|
35
|
-
- Import: `import { valuesRef } from 'timonel'`
|
|
36
|
-
- **Comparison operators**: `eq`, `ne`, `gt`, `ge`, `lt`, `le`
|
|
37
|
-
- **Logical operators**: `not`, `and`, `or`
|
|
38
|
-
- **String functions**: `quote`, `upper`, `lower`, `trim`, `replace`, `contains`
|
|
39
|
-
- **Default values**: `default()`
|
|
40
|
-
- **Type checking**: `kindIs`, `hasKey`
|
|
41
|
-
- **YAML functions**: `toYaml`, `toJson`, `nindent`, `indent`
|
|
42
|
-
- **Field-level conditionals**: `v.if()`, `v.ifElse()` - Complex conditional logic
|
|
43
|
-
- **Range loops**: `v.range()` - Type-safe iteration
|
|
44
|
-
- **Context switching**: `v.with()` - Scoped value access
|
|
45
|
-
|
|
46
|
-
#### Composable Helpers
|
|
47
|
-
|
|
48
|
-
Template definition and inclusion helpers:
|
|
49
|
-
|
|
50
|
-
- `helmInclude`, `helmDefine`, `helmVar`, `helmBlock`, `helmComment`, `helmFragment`
|
|
51
|
-
- `template`, `include`, `quote`, `indent`
|
|
52
|
-
|
|
53
|
-
#### Value Reference Helpers
|
|
54
|
-
|
|
55
|
-
Useful string-based utilities (no ValuesRef equivalent):
|
|
56
|
-
|
|
57
|
-
- `requiredValuesRef` - Required value with validation
|
|
58
|
-
- `numberRef`, `boolRef`, `floatRef` - Type-cast references (int, bool, float64)
|
|
59
|
-
- `base64Ref` - Base64 encoding
|
|
60
|
-
|
|
61
|
-
#### Legacy Helpers (NOT RECOMMENDED)
|
|
62
|
-
|
|
63
|
-
**⚠️ Use ValuesRef system instead:**
|
|
64
|
-
|
|
65
|
-
- `valuesRef(path)` → use `v.path` (ValuesRef system)
|
|
66
|
-
- `stringRef()` → use `v.quote()` (ValuesRef system)
|
|
67
|
-
- `defaultRef()` → use `v.default()` (ValuesRef system)
|
|
68
|
-
- `jsonRef()` → use `v.toJson()` (ValuesRef system)
|
|
69
|
-
- `conditionalRef()` → use `v.if()` (ValuesRef system)
|
|
70
|
-
- `helmIf`, `helmIfSimple` → use `v.if()` (ValuesRef system)
|
|
71
|
-
- `helmRange` → use `v.range()` (ValuesRef system)
|
|
72
|
-
- `helmWith` → use `v.with()` (ValuesRef system)
|
|
73
|
-
- `helmIfElseIf` → use `v.if()` with nested conditions
|
|
74
|
-
|
|
75
|
-
### Enhanced Helm Helpers
|
|
76
|
-
|
|
77
|
-
- **Environment Helpers**: `envRef`, `envDefault`, `envRequired`, `envFromSecret`, `envFromConfigMap`
|
|
78
|
-
- **GitOps Helpers**: `gitBranch`, `gitCommit`, `gitTag`, `gitopsAnnotations`
|
|
79
|
-
- **Observability Helpers**: `prometheusAnnotations`, `datadogAnnotations`, `tracingAnnotations`
|
|
80
|
-
- **Validation Helpers**: `validateRequired`, `validatePattern`, `validateRange`, `validateEnum`
|
|
81
|
-
- **Standard Helpers**: 40+ built-in Helm helpers (chart.name, chart.fullname, chart.labels, etc.)
|
|
82
|
-
|
|
83
|
-
### Cloud Integrations
|
|
84
|
-
|
|
85
|
-
- **AWS Resources**:
|
|
86
|
-
- EBS/EFS StorageClass with encryption and performance options
|
|
87
|
-
- ALB Ingress with SSL/TLS and health checks
|
|
88
|
-
- IRSA ServiceAccount for pod-level IAM roles
|
|
89
|
-
- ECR integration
|
|
90
|
-
- Karpenter NodePool, NodeClaim, and EC2NodeClass
|
|
91
|
-
- **Karpenter Features**:
|
|
92
|
-
- Disruption budgets and consolidation policies
|
|
93
|
-
- Instance type selection and requirements
|
|
94
|
-
- Spot instance support
|
|
95
|
-
- Custom AMI and user data
|
|
96
|
-
|
|
97
|
-
### Security & Validation
|
|
98
|
-
|
|
99
|
-
- **🛡️ Security-first approach**:
|
|
100
|
-
- Input validation (CWE-20, CWE-22/23)
|
|
101
|
-
- Path traversal prevention
|
|
102
|
-
- Command injection prevention (CWE-78/77/88)
|
|
103
|
-
- Log injection protection (CWE-117)
|
|
104
|
-
- Code injection prevention (CWE-94)
|
|
105
|
-
- **🔍 Policy Engine** (NEW):
|
|
106
|
-
- Extensible validation framework for Kubernetes manifests
|
|
107
|
-
- Plugin-based architecture for custom policy rules
|
|
108
|
-
- Zero-impact integration (completely optional)
|
|
109
|
-
- Support for security, compliance, and best practice policies
|
|
110
|
-
- **NetworkPolicy support** for pod-level network isolation
|
|
111
|
-
- **Helm chart validation** with `validateHelmYaml`
|
|
112
|
-
- **SecurityUtils** for path validation and sanitization
|
|
113
|
-
|
|
114
|
-
### Developer Experience
|
|
13
|
+
**Timonel** is a TypeScript library for generating complete Helm charts programmatically on top of
|
|
14
|
+
cdk8s and cdk8s-plus. Kubernetes resources stay typed in application code while Timonel handles
|
|
15
|
+
Helm chart structure, values files, helpers, environment variants, policy validation, and final
|
|
16
|
+
chart output.
|
|
115
17
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
- **YAML serialization** with Helm template preservation
|
|
119
|
-
- **TypeScript strict mode** with all compiler checks enabled
|
|
120
|
-
- **Comprehensive error handling** with detailed messages
|
|
121
|
-
|
|
122
|
-
## 🚀 Quick Start
|
|
123
|
-
|
|
124
|
-
### Installation
|
|
18
|
+
The library API is the primary product. The `tl` CLI is a convenience layer for scaffolding,
|
|
19
|
+
synthesis, Helm validation, deployment, and umbrella-chart workflows.
|
|
125
20
|
|
|
126
|
-
|
|
127
|
-
# Install Timonel globally
|
|
128
|
-
npm install -g timonel
|
|
21
|
+
## Architecture
|
|
129
22
|
|
|
130
|
-
|
|
131
|
-
pnpm add -g timonel
|
|
132
|
-
```
|
|
23
|
+
Timonel is intentionally **typed-first**. Use Kubernetes resource APIs in this order:
|
|
133
24
|
|
|
134
|
-
|
|
25
|
+
1. `cdk8s-plus-33` when it already provides the resource abstraction;
|
|
26
|
+
2. cdk8s `ApiObject` or a focused typed Timonel abstraction when a higher-level construct does not
|
|
27
|
+
fit;
|
|
28
|
+
3. object-form `Rutter.addManifest()` for CRDs or custom resources without a suitable typed
|
|
29
|
+
construct;
|
|
30
|
+
4. raw YAML only as a legacy escape hatch.
|
|
135
31
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
tl init my-app
|
|
32
|
+
The raw-string `addManifest()` overload and `addTemplateManifest()` are deprecated and planned for
|
|
33
|
+
removal in the next major release.
|
|
139
34
|
|
|
140
|
-
|
|
141
|
-
tl synth my-app my-app-dist
|
|
35
|
+
## Requirements
|
|
142
36
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
37
|
+
- Node.js `^22.22.2`, `^24.15.0`, or `>=26.0.0`
|
|
38
|
+
- pnpm `>=9`
|
|
39
|
+
- Helm 3 when using `tl validate`, `tl deploy`, or manual chart validation
|
|
146
40
|
|
|
147
|
-
|
|
41
|
+
## Installation
|
|
148
42
|
|
|
149
43
|
```bash
|
|
150
|
-
|
|
151
|
-
tl umbrella init my-umbrella-app
|
|
152
|
-
|
|
153
|
-
# Add subcharts
|
|
154
|
-
tl umbrella add frontend
|
|
155
|
-
tl umbrella add backend
|
|
156
|
-
|
|
157
|
-
# Generate umbrella chart
|
|
158
|
-
tl umbrella synth
|
|
44
|
+
pnpm add timonel cdk8s cdk8s-plus-33 constructs
|
|
159
45
|
```
|
|
160
46
|
|
|
161
|
-
|
|
47
|
+
npm and other compatible package managers can also install the package.
|
|
162
48
|
|
|
163
|
-
|
|
49
|
+
## Quick start
|
|
164
50
|
|
|
165
51
|
```typescript
|
|
166
|
-
import
|
|
52
|
+
import * as kplus from 'cdk8s-plus-33';
|
|
53
|
+
import { Rutter } from 'timonel';
|
|
167
54
|
|
|
168
55
|
const chart = new Rutter({
|
|
169
56
|
meta: {
|
|
170
|
-
name: '
|
|
57
|
+
name: 'my-app',
|
|
171
58
|
version: '1.0.0',
|
|
172
|
-
description: '
|
|
59
|
+
description: 'Typed Helm chart generated by Timonel',
|
|
173
60
|
},
|
|
174
61
|
defaultValues: {
|
|
175
|
-
|
|
176
|
-
image: {
|
|
177
|
-
repository: 'nginx',
|
|
178
|
-
tag: 'latest',
|
|
179
|
-
},
|
|
62
|
+
environment: 'production',
|
|
180
63
|
},
|
|
181
64
|
});
|
|
182
65
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
spec: {
|
|
193
|
-
replicas: helm('{{ .Values.replicas }}'),
|
|
194
|
-
selector: {
|
|
195
|
-
matchLabels: helmInclude('chart.selectorLabels', '.', { pipe: 'nindent 6' }),
|
|
196
|
-
},
|
|
197
|
-
template: {
|
|
198
|
-
metadata: {
|
|
199
|
-
labels: helmInclude('chart.selectorLabels', '.', { pipe: 'nindent 8' }),
|
|
200
|
-
},
|
|
201
|
-
spec: {
|
|
202
|
-
containers: [
|
|
203
|
-
{
|
|
204
|
-
name: 'web',
|
|
205
|
-
image: helm('{{ .Values.image.repository }}:{{ .Values.image.tag }}'),
|
|
206
|
-
ports: [{ containerPort: 80, name: 'http' }],
|
|
207
|
-
},
|
|
208
|
-
],
|
|
209
|
-
},
|
|
66
|
+
const deployment = new kplus.Deployment(chart.getChart(), 'App', {
|
|
67
|
+
metadata: { name: 'my-app' },
|
|
68
|
+
containers: [
|
|
69
|
+
{
|
|
70
|
+
name: 'app',
|
|
71
|
+
image: 'nginx:1.27',
|
|
72
|
+
portNumber: 80,
|
|
73
|
+
resources: {
|
|
74
|
+
cpu: { request: kplus.Cpu.millis(100) },
|
|
210
75
|
},
|
|
211
76
|
},
|
|
212
|
-
|
|
213
|
-
'deployment',
|
|
214
|
-
);
|
|
215
|
-
|
|
216
|
-
// Generate the chart
|
|
217
|
-
chart.write('./dist');
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
### Policy Engine Integration
|
|
221
|
-
|
|
222
|
-
```typescript
|
|
223
|
-
import { Rutter, PolicyEngine } from 'timonel';
|
|
224
|
-
import { securityPolicies } from '@mycompany/k8s-security-policies';
|
|
225
|
-
|
|
226
|
-
// Create policy engine with custom plugins
|
|
227
|
-
const policyEngine = new PolicyEngine().use(securityPolicies).configure({
|
|
228
|
-
timeout: 5000,
|
|
229
|
-
parallel: true,
|
|
77
|
+
],
|
|
230
78
|
});
|
|
231
79
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
policyEngine,
|
|
80
|
+
deployment.exposeViaService();
|
|
81
|
+
|
|
82
|
+
new kplus.HorizontalPodAutoscaler(chart.getChart(), 'AppHpa', {
|
|
83
|
+
target: deployment,
|
|
84
|
+
minReplicas: 1,
|
|
85
|
+
maxReplicas: 5,
|
|
239
86
|
});
|
|
240
87
|
|
|
241
|
-
|
|
242
|
-
chart.write('./dist'); // Fails if policy violations found
|
|
88
|
+
await chart.write('./dist/my-app');
|
|
243
89
|
```
|
|
244
90
|
|
|
245
|
-
|
|
91
|
+
The generated chart contains `Chart.yaml`, `values.yaml`, `templates/`, `_helpers.tpl`, and
|
|
92
|
+
`.helmignore`.
|
|
246
93
|
|
|
247
|
-
|
|
248
|
-
import { PolicyPlugin, PolicyViolation } from 'timonel';
|
|
94
|
+
Validate the final Helm output:
|
|
249
95
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
description: 'Custom security validation rules',
|
|
254
|
-
|
|
255
|
-
async validate(manifests, context) {
|
|
256
|
-
const violations: PolicyViolation[] = [];
|
|
257
|
-
|
|
258
|
-
for (const manifest of manifests) {
|
|
259
|
-
if (manifest.kind === 'Deployment') {
|
|
260
|
-
// Check for security context
|
|
261
|
-
if (!manifest.spec?.template?.spec?.securityContext) {
|
|
262
|
-
violations.push({
|
|
263
|
-
plugin: this.name,
|
|
264
|
-
severity: 'error',
|
|
265
|
-
message: 'Deployment must specify securityContext',
|
|
266
|
-
resourcePath: `${manifest.kind}/${manifest.metadata?.name}`,
|
|
267
|
-
suggestion: 'Add spec.template.spec.securityContext to your Deployment',
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
return violations;
|
|
274
|
-
},
|
|
275
|
-
};
|
|
96
|
+
```bash
|
|
97
|
+
helm lint ./dist/my-app
|
|
98
|
+
helm template my-app ./dist/my-app
|
|
276
99
|
```
|
|
277
100
|
|
|
278
|
-
|
|
101
|
+
## `Rutter`
|
|
279
102
|
|
|
280
|
-
|
|
281
|
-
import { UmbrellaChartTemplate } from 'timonel';
|
|
103
|
+
`Rutter` owns a cdk8s `Chart` and converts its resource tree into Helm chart assets.
|
|
282
104
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
version: '1.0.0',
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
name: 'backend',
|
|
294
|
-
version: '1.0.0',
|
|
295
|
-
chart: backendChartFunction,
|
|
105
|
+
```typescript
|
|
106
|
+
const chart = new Rutter({
|
|
107
|
+
meta: { name: 'orders', version: '1.0.0' },
|
|
108
|
+
namespace: 'orders',
|
|
109
|
+
defaultValues: {
|
|
110
|
+
replicas: 2,
|
|
111
|
+
},
|
|
112
|
+
envValues: {
|
|
113
|
+
production: {
|
|
114
|
+
replicas: 4,
|
|
296
115
|
},
|
|
297
|
-
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
export const umbrella = new UmbrellaChartTemplate(umbrellaConfig);
|
|
116
|
+
},
|
|
117
|
+
});
|
|
301
118
|
```
|
|
302
119
|
|
|
303
|
-
|
|
120
|
+
Useful methods include:
|
|
121
|
+
|
|
122
|
+
| API | Purpose |
|
|
123
|
+
| ------------------------- | --------------------------------------------------------- |
|
|
124
|
+
| `getChart()` | Access the real cdk8s `Chart` for native typed constructs |
|
|
125
|
+
| `write(outDir)` | Synthesize and write the complete Helm chart |
|
|
126
|
+
| `toSynthArray()` | Asynchronously synthesize Helm assets |
|
|
127
|
+
| `getMeta()` | Read chart metadata |
|
|
128
|
+
| `getDefaultValues()` | Read default values |
|
|
129
|
+
| `getEnvValues()` | Read environment-specific values |
|
|
130
|
+
| `addManifest(object, id)` | Object fallback for custom resources |
|
|
304
131
|
|
|
305
|
-
|
|
132
|
+
`toSynthArraySync()` remains for compatibility and is deprecated. It cannot be used with a policy
|
|
133
|
+
engine.
|
|
306
134
|
|
|
307
|
-
|
|
135
|
+
### Existing construct tree
|
|
308
136
|
|
|
309
|
-
|
|
310
|
-
The new system uses generics and returns a proxy object with methods.
|
|
137
|
+
Pass `scope` when Timonel should participate in an existing cdk8s/constructs tree:
|
|
311
138
|
|
|
312
139
|
```typescript
|
|
313
|
-
import {
|
|
140
|
+
import { App } from 'cdk8s';
|
|
141
|
+
import * as kplus from 'cdk8s-plus-33';
|
|
142
|
+
import { Rutter } from 'timonel';
|
|
314
143
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
144
|
+
const app = new App();
|
|
145
|
+
const chart = new Rutter({
|
|
146
|
+
scope: app,
|
|
147
|
+
meta: { name: 'shared-tree', version: '1.0.0' },
|
|
148
|
+
});
|
|
320
149
|
|
|
321
|
-
|
|
150
|
+
new kplus.ConfigMap(chart.getChart(), 'Config', {
|
|
151
|
+
metadata: { name: 'shared-tree' },
|
|
152
|
+
data: { mode: 'production' },
|
|
153
|
+
});
|
|
154
|
+
```
|
|
322
155
|
|
|
323
|
-
|
|
324
|
-
const replicas = v.replicaCount; // {{ .Values.replicaCount }}
|
|
325
|
-
const imageTag = v.image.tag; // {{ .Values.image.tag }}
|
|
156
|
+
## Type-safe Helm values
|
|
326
157
|
|
|
327
|
-
|
|
328
|
-
const isProd = v.environment.eq('production'); // eq .Values.environment "production"
|
|
329
|
-
const hasReplicas = v.replicaCount.gt(1); // gt .Values.replicaCount 1
|
|
158
|
+
`valuesRef<T>()` exposes Helm values using the shape of a TypeScript type.
|
|
330
159
|
|
|
331
|
-
|
|
332
|
-
|
|
160
|
+
```typescript
|
|
161
|
+
import { valuesRef } from 'timonel';
|
|
333
162
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
163
|
+
interface Values {
|
|
164
|
+
environment: string;
|
|
165
|
+
replicas: number;
|
|
166
|
+
image: {
|
|
167
|
+
repository: string;
|
|
168
|
+
tag: string;
|
|
169
|
+
};
|
|
170
|
+
autoscaling: {
|
|
171
|
+
enabled: boolean;
|
|
172
|
+
};
|
|
173
|
+
env: Array<{
|
|
174
|
+
name: string;
|
|
175
|
+
value: string;
|
|
176
|
+
}>;
|
|
177
|
+
}
|
|
337
178
|
|
|
338
|
-
|
|
339
|
-
const port = v.port.default(8080); // {{ .Values.port | default 8080 }}
|
|
179
|
+
const v = valuesRef<Values>();
|
|
340
180
|
|
|
341
|
-
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
},
|
|
346
|
-
};
|
|
181
|
+
const imageTag = v.image.tag.quote();
|
|
182
|
+
const multipleReplicas = v.replicas.gt(1);
|
|
183
|
+
const production = v.environment.eq('production');
|
|
184
|
+
const fixedTag = v.image.tag.default('1.0.0');
|
|
347
185
|
|
|
348
|
-
|
|
349
|
-
const envVars = v.env.range((item, index) => ({
|
|
186
|
+
const env = v.env.range((item) => ({
|
|
350
187
|
name: item.name,
|
|
351
188
|
value: item.value,
|
|
352
189
|
}));
|
|
353
190
|
|
|
354
|
-
|
|
355
|
-
const dbConfig = v.database.with((db) => ({
|
|
356
|
-
host: db.host,
|
|
357
|
-
port: db.port,
|
|
358
|
-
}));
|
|
191
|
+
const replicasField = v.replicas.if(v.autoscaling.enabled.not(), v.replicas);
|
|
359
192
|
```
|
|
360
193
|
|
|
361
|
-
|
|
194
|
+
The compiler rejects value paths that do not exist in `Values`.
|
|
362
195
|
|
|
363
|
-
|
|
196
|
+
### Reserved values keys
|
|
364
197
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
// Template inclusion with pipe
|
|
369
|
-
const labels = helmInclude('chart.labels', '.', { pipe: 'nindent 4' });
|
|
370
|
-
|
|
371
|
-
// Define a named template
|
|
372
|
-
const myTemplate = helmDefine('myapp.config', {
|
|
373
|
-
key: 'value',
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
// Combine multiple constructs
|
|
377
|
-
const combined = helmFragment(helmInclude('chart.labels', '.'), { customKey: 'customValue' });
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
#### Legacy Flow Control (NOT RECOMMENDED)
|
|
381
|
-
|
|
382
|
-
**⚠️ These are legacy and NOT RECOMMENDED. Use ValuesRef system (v.if, v.range, v.with)
|
|
383
|
-
instead:**
|
|
198
|
+
Names used by the proxy API, such as `default`, `range`, and `with`, and root helpers such as
|
|
199
|
+
`release`, `chart`, and `capabilities`, are accessed with the typed `at()` method when those names
|
|
200
|
+
also exist in your values schema:
|
|
384
201
|
|
|
385
202
|
```typescript
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
const v = valuesRef<MyValues>();
|
|
391
|
-
const newRef = v.production; // {{ .Values.production }}
|
|
203
|
+
interface Values {
|
|
204
|
+
release: { name: string };
|
|
205
|
+
settings: { default: string };
|
|
206
|
+
}
|
|
392
207
|
|
|
393
|
-
|
|
394
|
-
const config = helmIf('.Values.production', { replicas: 5 }, { replicas: 1 });
|
|
208
|
+
const v = valuesRef<Values>();
|
|
395
209
|
|
|
396
|
-
|
|
397
|
-
const
|
|
210
|
+
const releaseName = v.at('release').name;
|
|
211
|
+
const defaultSetting = v.settings.at('default');
|
|
398
212
|
```
|
|
399
213
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
- ✅ **100% Type-Safe** - Catch errors at compile time with TypeScript generics
|
|
403
|
-
- ✅ **No Raw Strings** - Eliminate manual template interpolation and typos
|
|
404
|
-
- ✅ **Full IDE Support** - Autocomplete, type hints, and refactoring support
|
|
405
|
-
- ✅ **Proxy-based** - Chainable methods for complex logic
|
|
406
|
-
- ✅ **Composable** - Nest and combine operations naturally
|
|
407
|
-
- ❌ **Legacy helpers** - String-based, error-prone, no IDE support
|
|
408
|
-
|
|
409
|
-
**Migration:** Replace `valuesRef(path)` with `v.path`. Replace `helmIf`, `helmRange`, `helmWith`
|
|
410
|
-
with `v.if()`, `v.range()`, `v.with()`.
|
|
214
|
+
`at()` accepts only keys from the current TypeScript values type.
|
|
411
215
|
|
|
412
|
-
|
|
413
|
-
[Type-Safe Helm Helpers Guide](https://github.com/KenkoGeek/timonel/wiki/Helm-Helpers-System) for
|
|
414
|
-
complete documentation, examples, and best practices.
|
|
216
|
+
## Custom resources
|
|
415
217
|
|
|
416
|
-
|
|
218
|
+
Use object-form `addManifest()` when no appropriate typed construct is available:
|
|
417
219
|
|
|
418
|
-
|
|
419
|
-
|
|
220
|
+
```typescript
|
|
221
|
+
chart.addManifest(
|
|
222
|
+
{
|
|
223
|
+
apiVersion: 'monitoring.coreos.com/v1',
|
|
224
|
+
kind: 'ServiceMonitor',
|
|
225
|
+
metadata: { name: 'orders' },
|
|
226
|
+
spec: {
|
|
227
|
+
selector: {
|
|
228
|
+
matchLabels: { app: 'orders' },
|
|
229
|
+
},
|
|
230
|
+
endpoints: [{ port: 'http' }],
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
'OrdersServiceMonitor',
|
|
234
|
+
);
|
|
235
|
+
```
|
|
420
236
|
|
|
421
|
-
|
|
237
|
+
Do not switch a standard Kubernetes resource to raw YAML merely because one field contains Helm
|
|
238
|
+
logic. Prefer typed constructs and Timonel's Helm-value helpers where they fit.
|
|
422
239
|
|
|
423
|
-
|
|
424
|
-
- **⚡ Zero Impact**: Completely optional with no performance overhead when unused
|
|
425
|
-
- **🛡️ Security Focus**: Built-in support for security and compliance policies
|
|
426
|
-
- **🔄 Async Support**: Handles both synchronous and asynchronous validation plugins
|
|
427
|
-
- **📊 Rich Reporting**: Detailed violation reports with suggestions and context
|
|
428
|
-
- **⏱️ Timeout Protection**: Configurable timeouts prevent hanging validations
|
|
429
|
-
- **🔧 Configurable**: Environment-specific policy configuration support
|
|
430
|
-
- **🚀 Performance Optimized**: Parallel execution, caching, and resource monitoring
|
|
431
|
-
- **🔄 Error Resilience**: Graceful degradation and retry mechanisms
|
|
432
|
-
- **📈 Observability**: Structured logging and performance metrics
|
|
240
|
+
## Umbrella charts
|
|
433
241
|
|
|
434
|
-
|
|
242
|
+
`UmbrellaRutter` combines multiple `Rutter` instances as Helm dependencies:
|
|
435
243
|
|
|
436
244
|
```typescript
|
|
437
|
-
import
|
|
245
|
+
import * as kplus from 'cdk8s-plus-33';
|
|
246
|
+
import { Rutter, UmbrellaRutter } from 'timonel';
|
|
438
247
|
|
|
439
|
-
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
248
|
+
function serviceChart(name: string): Rutter {
|
|
249
|
+
const chart = new Rutter({
|
|
250
|
+
meta: { name, version: '1.0.0' },
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
new kplus.ConfigMap(chart.getChart(), 'Config', {
|
|
254
|
+
metadata: { name: `${name}-config` },
|
|
255
|
+
});
|
|
445
256
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
await policyEngine.use(await import('@kubernetes/best-practices'));
|
|
257
|
+
return chart;
|
|
258
|
+
}
|
|
449
259
|
|
|
450
|
-
const
|
|
451
|
-
meta: {
|
|
452
|
-
|
|
260
|
+
const umbrella = new UmbrellaRutter({
|
|
261
|
+
meta: {
|
|
262
|
+
name: 'commerce',
|
|
263
|
+
version: '1.0.0',
|
|
264
|
+
},
|
|
265
|
+
subcharts: [
|
|
266
|
+
{ name: 'catalog', version: '1.0.0', rutter: serviceChart('catalog') },
|
|
267
|
+
{ name: 'orders', version: '1.0.0', rutter: serviceChart('orders') },
|
|
268
|
+
],
|
|
453
269
|
});
|
|
454
270
|
|
|
455
|
-
|
|
271
|
+
await umbrella.write('./dist/commerce');
|
|
456
272
|
```
|
|
457
273
|
|
|
458
|
-
|
|
274
|
+
The CLI also supports dependency and inline umbrella synthesis modes.
|
|
459
275
|
|
|
460
|
-
|
|
276
|
+
## AWS and EKS helpers
|
|
461
277
|
|
|
462
|
-
|
|
463
|
-
- **Best Practices Plugin** - Kubernetes best practices (resource limits, naming, probes)
|
|
464
|
-
- **AWS Plugin** - AWS-specific validations (EKS, ALB, IRSA, cost optimization)
|
|
278
|
+
Timonel includes focused helpers for Kubernetes-side AWS integrations:
|
|
465
279
|
|
|
466
|
-
|
|
280
|
+
- EBS and EFS `StorageClass` resources;
|
|
281
|
+
- IRSA and ECR-oriented `ServiceAccount` resources;
|
|
282
|
+
- ALB `Ingress` configuration;
|
|
283
|
+
- Karpenter `NodePool`, `NodeClaim`, and `EC2NodeClass` resources;
|
|
284
|
+
- convenience NodePool builders for disruption and scheduling policies.
|
|
467
285
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
286
|
+
Example:
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
chart.addAWSIRSAServiceAccount({
|
|
290
|
+
name: 'orders',
|
|
291
|
+
roleArn: 'arn:aws:iam::123456789012:role/orders',
|
|
292
|
+
});
|
|
293
|
+
```
|
|
471
294
|
|
|
472
|
-
**
|
|
295
|
+
These APIs generate Kubernetes resources. They do **not** create IAM roles, storage systems, ECR
|
|
296
|
+
repositories, controllers, cluster networking, or Karpenter CRDs in AWS.
|
|
473
297
|
|
|
474
|
-
|
|
475
|
-
- `@aws/well-architected-policies` - AWS Well-Architected Framework validation
|
|
476
|
-
- `@security/cis-benchmarks` - CIS Kubernetes Benchmark validation
|
|
298
|
+
## Policy Engine
|
|
477
299
|
|
|
478
|
-
|
|
300
|
+
The optional Policy Engine validates synthesized Kubernetes manifests through user-supplied
|
|
301
|
+
plugins before Timonel writes the Helm chart.
|
|
479
302
|
|
|
480
303
|
```typescript
|
|
481
|
-
import { PolicyPlugin
|
|
304
|
+
import type { PolicyPlugin } from 'timonel';
|
|
305
|
+
import { PolicyEngine, Rutter } from 'timonel';
|
|
482
306
|
|
|
483
|
-
|
|
484
|
-
name: '
|
|
307
|
+
const requireMetadataName: PolicyPlugin = {
|
|
308
|
+
name: 'require-metadata-name',
|
|
485
309
|
version: '1.0.0',
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
violations.push({
|
|
506
|
-
plugin: this.name,
|
|
507
|
-
severity: config?.strictMode ? 'error' : 'warning',
|
|
508
|
-
message: 'Deployment should specify securityContext',
|
|
509
|
-
resourcePath: `${manifest.kind}/${manifest.metadata?.name}`,
|
|
510
|
-
field: 'spec.template.spec.securityContext',
|
|
511
|
-
suggestion: 'Add securityContext with runAsNonRoot: true',
|
|
512
|
-
context: {
|
|
513
|
-
kubernetesVersion: context.kubernetesVersion,
|
|
514
|
-
environment: context.environment,
|
|
515
|
-
},
|
|
516
|
-
});
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// Validate namespace restrictions
|
|
520
|
-
const namespace = manifest.metadata?.namespace || 'default';
|
|
521
|
-
if (config?.allowedNamespaces && !config.allowedNamespaces.includes(namespace)) {
|
|
522
|
-
violations.push({
|
|
523
|
-
plugin: this.name,
|
|
524
|
-
severity: 'error',
|
|
525
|
-
message: `Deployment in unauthorized namespace: ${namespace}`,
|
|
526
|
-
resourcePath: `${manifest.kind}/${manifest.metadata?.name}`,
|
|
527
|
-
field: 'metadata.namespace',
|
|
528
|
-
suggestion: `Deploy to allowed namespaces: ${config.allowedNamespaces.join(', ')}`,
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
return violations;
|
|
310
|
+
async validate(manifests) {
|
|
311
|
+
return manifests.flatMap((manifest) => {
|
|
312
|
+
if (!manifest || typeof manifest !== 'object') return [];
|
|
313
|
+
|
|
314
|
+
const object = manifest as {
|
|
315
|
+
kind?: string;
|
|
316
|
+
metadata?: { name?: string };
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
if (object.metadata?.name) return [];
|
|
320
|
+
|
|
321
|
+
return [
|
|
322
|
+
{
|
|
323
|
+
plugin: 'require-metadata-name',
|
|
324
|
+
severity: 'error' as const,
|
|
325
|
+
message: `${object.kind ?? 'Resource'} must have metadata.name`,
|
|
326
|
+
},
|
|
327
|
+
];
|
|
328
|
+
});
|
|
535
329
|
},
|
|
536
330
|
};
|
|
537
|
-
```
|
|
538
331
|
|
|
539
|
-
### Advanced Configuration
|
|
540
|
-
|
|
541
|
-
```typescript
|
|
542
332
|
const policyEngine = new PolicyEngine({
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
failFast: false, // Collect all violations before failing
|
|
547
|
-
gracefulDegradation: true, // Continue on plugin failures
|
|
548
|
-
|
|
549
|
-
// Performance optimization
|
|
550
|
-
cacheOptions: {
|
|
551
|
-
maxSize: 1000, // Cache up to 1000 validation results
|
|
552
|
-
ttl: 300000, // 5 minute cache TTL
|
|
553
|
-
enableStats: true, // Enable cache performance monitoring
|
|
554
|
-
},
|
|
555
|
-
|
|
556
|
-
// Parallel execution tuning
|
|
557
|
-
parallelOptions: {
|
|
558
|
-
maxConcurrency: 4, // Run up to 4 plugins concurrently
|
|
559
|
-
enableResourceMonitoring: true,
|
|
560
|
-
},
|
|
333
|
+
timeout: 5000,
|
|
334
|
+
parallel: true,
|
|
335
|
+
});
|
|
561
336
|
|
|
562
|
-
|
|
563
|
-
retryConfig: {
|
|
564
|
-
maxAttempts: 3,
|
|
565
|
-
baseDelay: 1000,
|
|
566
|
-
retryOnTimeout: true,
|
|
567
|
-
retryOnPluginError: false,
|
|
568
|
-
},
|
|
337
|
+
await policyEngine.use(requireMetadataName);
|
|
569
338
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
strictMode: true,
|
|
574
|
-
allowedNamespaces: ['default', 'kube-system'],
|
|
575
|
-
securityContext: {
|
|
576
|
-
required: true,
|
|
577
|
-
runAsNonRoot: true,
|
|
578
|
-
},
|
|
579
|
-
},
|
|
580
|
-
'best-practices-plugin': {
|
|
581
|
-
enforceResourceLimits: true,
|
|
582
|
-
requireLabels: ['app', 'version', 'environment'],
|
|
583
|
-
maxReplicas: 50,
|
|
584
|
-
},
|
|
585
|
-
'aws-plugin': {
|
|
586
|
-
region: 'us-west-2',
|
|
587
|
-
enforceTagging: true,
|
|
588
|
-
costOptimization: {
|
|
589
|
-
enabled: true,
|
|
590
|
-
maxInstanceSize: 'xlarge',
|
|
591
|
-
},
|
|
592
|
-
},
|
|
593
|
-
},
|
|
339
|
+
const chart = new Rutter({
|
|
340
|
+
meta: { name: 'validated', version: '1.0.0' },
|
|
341
|
+
policyEngine,
|
|
594
342
|
});
|
|
595
|
-
|
|
596
|
-
// Register plugins
|
|
597
|
-
await policyEngine.use(securityPolicies);
|
|
598
|
-
await policyEngine.use(bestPracticesPolicies);
|
|
599
|
-
await policyEngine.use(awsPolicies);
|
|
600
343
|
```
|
|
601
344
|
|
|
602
|
-
|
|
345
|
+
Policy options include plugin timeouts, retries, graceful degradation, caching, parallel execution,
|
|
346
|
+
inline plugin configuration, schema validation, and environment-variable configuration.
|
|
603
347
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
const engine = new PolicyEngine({
|
|
608
|
-
environment,
|
|
609
|
-
configurationLoader: {
|
|
610
|
-
configurationFiles: ['config/policy-engine.json', `config/environments/${environment}.json`],
|
|
611
|
-
},
|
|
612
|
-
});
|
|
348
|
+
`ConfigurationLoader` currently accepts file-related options, but file-backed policy configuration
|
|
349
|
+
loading is not implemented. Do not rely on `configurationFiles` to load plugin configuration from
|
|
350
|
+
disk in the current release.
|
|
613
351
|
|
|
614
|
-
|
|
615
|
-
await engine.use(baseSecurity);
|
|
616
|
-
|
|
617
|
-
// Environment-specific policies
|
|
618
|
-
switch (environment) {
|
|
619
|
-
case 'production':
|
|
620
|
-
await engine.use(strictSecurity);
|
|
621
|
-
await engine.use(compliancePolicies);
|
|
622
|
-
await engine.use(awsPolicies);
|
|
623
|
-
break;
|
|
624
|
-
case 'staging':
|
|
625
|
-
await engine.use(moderateSecurity);
|
|
626
|
-
await engine.use(awsPolicies);
|
|
627
|
-
break;
|
|
628
|
-
case 'development':
|
|
629
|
-
// Minimal policies for development
|
|
630
|
-
await engine.use(basicSecurity);
|
|
631
|
-
break;
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
return engine;
|
|
635
|
-
};
|
|
636
|
-
```
|
|
352
|
+
## Environment variable configuration
|
|
637
353
|
|
|
638
|
-
|
|
354
|
+
The environment-variable loader reads YAML/JSON configuration and generates Kubernetes `env`
|
|
355
|
+
entries:
|
|
639
356
|
|
|
640
357
|
```typescript
|
|
641
|
-
|
|
642
|
-
import { Rutter, PolicyEngine, PolicyEngineError } from 'timonel';
|
|
643
|
-
|
|
644
|
-
const validateChart = async (chartPath: string, environment: string) => {
|
|
645
|
-
const policyEngine = await createPolicyEngine(environment);
|
|
646
|
-
|
|
647
|
-
try {
|
|
648
|
-
const chart = new Rutter({
|
|
649
|
-
meta: { name: 'my-app', version: process.env.VERSION },
|
|
650
|
-
policyEngine,
|
|
651
|
-
});
|
|
358
|
+
import { loadAndGenerateEnvVars } from 'timonel';
|
|
652
359
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
console.log('✅ Chart validation passed', {
|
|
658
|
-
environment,
|
|
659
|
-
cacheHitRate: stats.hitRate,
|
|
660
|
-
pluginCount: policyEngine.getPluginCount(),
|
|
661
|
-
});
|
|
662
|
-
} catch (error) {
|
|
663
|
-
if (error instanceof PolicyEngineError) {
|
|
664
|
-
console.error('❌ Policy violations found:');
|
|
665
|
-
|
|
666
|
-
// Group violations by severity
|
|
667
|
-
const errors = error.violations.filter((v) => v.severity === 'error');
|
|
668
|
-
const warnings = error.violations.filter((v) => v.severity === 'warning');
|
|
669
|
-
|
|
670
|
-
if (errors.length > 0) {
|
|
671
|
-
console.error(`\n🚨 Errors (${errors.length}):`);
|
|
672
|
-
errors.forEach((v) => {
|
|
673
|
-
console.error(` • ${v.resourcePath}: ${v.message}`);
|
|
674
|
-
if (v.suggestion) {
|
|
675
|
-
console.error(` 💡 ${v.suggestion}`);
|
|
676
|
-
}
|
|
677
|
-
});
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
if (warnings.length > 0) {
|
|
681
|
-
console.warn(`\n⚠️ Warnings (${warnings.length}):`);
|
|
682
|
-
warnings.forEach((v) => {
|
|
683
|
-
console.warn(` • ${v.resourcePath}: ${v.message}`);
|
|
684
|
-
});
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
// Fail CI/CD on errors, but allow warnings
|
|
688
|
-
if (errors.length > 0) {
|
|
689
|
-
process.exit(1);
|
|
690
|
-
}
|
|
691
|
-
} else {
|
|
692
|
-
throw error;
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
};
|
|
696
|
-
|
|
697
|
-
// Usage in GitHub Actions, GitLab CI, etc.
|
|
698
|
-
await validateChart('./dist', process.env.ENVIRONMENT || 'development');
|
|
360
|
+
const env = loadAndGenerateEnvVars({
|
|
361
|
+
configPath: './env-config.yaml',
|
|
362
|
+
defaultScope: 'global.env',
|
|
363
|
+
});
|
|
699
364
|
```
|
|
700
365
|
|
|
701
|
-
|
|
366
|
+
It can generate literal Helm values and Kubernetes `secretKeyRef` entries. It does not fetch values
|
|
367
|
+
from Vault, AWS Secrets Manager, or other external secret stores.
|
|
702
368
|
|
|
703
|
-
|
|
369
|
+
## CLI
|
|
704
370
|
|
|
705
|
-
|
|
371
|
+
Run the locally installed CLI with `pnpm exec tl` or expose the package binary through your package
|
|
372
|
+
manager.
|
|
706
373
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
374
|
+
```text
|
|
375
|
+
tl init <chart-name>
|
|
376
|
+
tl synth [chartDir] [outDir]
|
|
377
|
+
tl validate
|
|
378
|
+
tl deploy <release> [namespace]
|
|
379
|
+
tl templates
|
|
380
|
+
tl umbrella init <name>
|
|
381
|
+
tl umbrella add <subchart>
|
|
382
|
+
tl umbrella synth [outDir]
|
|
383
|
+
```
|
|
712
384
|
|
|
713
|
-
|
|
385
|
+
Common flags:
|
|
714
386
|
|
|
715
|
-
|
|
716
|
-
-
|
|
717
|
-
|
|
387
|
+
```text
|
|
388
|
+
--dry-run
|
|
389
|
+
--silent
|
|
390
|
+
--env <environment>
|
|
391
|
+
--set <key=value>
|
|
392
|
+
--mode <dependencies|inline>
|
|
393
|
+
--help, -h
|
|
394
|
+
```
|
|
718
395
|
|
|
719
|
-
|
|
396
|
+
`--dry-run` validates the requested operation and reports what would happen without writing chart
|
|
397
|
+
files or invoking Helm. Combine it with `--silent` when the preview itself should also be suppressed.
|
|
720
398
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
- **Observability** - Monitoring and logging best practices
|
|
724
|
-
- **Backup & Recovery** - Data protection policies
|
|
399
|
+
`tl validate` and `tl deploy` execute the Helm CLI, so Helm must be installed and the current working
|
|
400
|
+
directory must point at the chart you intend to validate or deploy.
|
|
725
401
|
|
|
726
|
-
|
|
402
|
+
## Generated chart structure
|
|
727
403
|
|
|
728
|
-
|
|
729
|
-
// package.json for a policy plugin
|
|
730
|
-
{
|
|
731
|
-
"name": "@mycompany/k8s-security-policies",
|
|
732
|
-
"version": "1.0.0",
|
|
733
|
-
"description": "Security policies for Kubernetes manifests",
|
|
734
|
-
"main": "dist/index.js",
|
|
735
|
-
"types": "dist/index.d.ts",
|
|
736
|
-
"keywords": ["timonel", "policy", "security", "kubernetes"],
|
|
737
|
-
"peerDependencies": {
|
|
738
|
-
"timonel": "^3.1.0"
|
|
739
|
-
}
|
|
740
|
-
}
|
|
404
|
+
A normal chart written by `Rutter` looks like:
|
|
741
405
|
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
406
|
+
```text
|
|
407
|
+
my-app/
|
|
408
|
+
├── Chart.yaml
|
|
409
|
+
├── values.yaml
|
|
410
|
+
├── values-production.yaml # when envValues.production exists
|
|
411
|
+
├── .helmignore
|
|
412
|
+
└── templates/
|
|
413
|
+
├── _helpers.tpl
|
|
414
|
+
├── App.yaml
|
|
415
|
+
├── AppHpa.yaml
|
|
416
|
+
└── ...
|
|
746
417
|
```
|
|
747
418
|
|
|
748
|
-
|
|
419
|
+
Resource filenames use stable cdk8s construct identifiers where possible.
|
|
749
420
|
|
|
750
|
-
|
|
421
|
+
## Documentation
|
|
751
422
|
|
|
752
|
-
|
|
753
|
-
// Monitor policy engine performance
|
|
754
|
-
const result = await policyEngine.validate(manifests, { name: 'example-chart', version: '1.0.0' });
|
|
755
|
-
|
|
756
|
-
console.log('Validation Performance:', {
|
|
757
|
-
executionTime: result.metadata.executionTime,
|
|
758
|
-
pluginCount: result.metadata.pluginCount,
|
|
759
|
-
manifestCount: result.metadata.manifestCount,
|
|
760
|
-
violationsFound: result.violations.length,
|
|
761
|
-
});
|
|
423
|
+
The GitHub wiki contains the full documentation set:
|
|
762
424
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
425
|
+
- [Home](https://github.com/KenkoGeek/timonel/wiki)
|
|
426
|
+
- [Quick Start](https://github.com/KenkoGeek/timonel/wiki/Quick-Start)
|
|
427
|
+
- [Architecture](https://github.com/KenkoGeek/timonel/wiki/Architecture)
|
|
428
|
+
- [API Reference](https://github.com/KenkoGeek/timonel/wiki/API-Reference)
|
|
429
|
+
- [Type-Safe Helm Helpers](https://github.com/KenkoGeek/timonel/wiki/Helm-Helpers-System)
|
|
430
|
+
- [Umbrella Charts](https://github.com/KenkoGeek/timonel/wiki/Examples-Umbrella-Charts)
|
|
431
|
+
- [AWS Resources](https://github.com/KenkoGeek/timonel/wiki/AWS-Resources)
|
|
432
|
+
- [Policy Engine](https://github.com/KenkoGeek/timonel/wiki/Policy-Engine)
|
|
433
|
+
- [CLI Reference](https://github.com/KenkoGeek/timonel/wiki/CLI-Reference)
|
|
434
|
+
- [Migration Guide](https://github.com/KenkoGeek/timonel/wiki/Migration-Guide)
|
|
435
|
+
- [Contributing](https://github.com/KenkoGeek/timonel/wiki/Contributing)
|
|
436
|
+
- [Release and Versioning](https://github.com/KenkoGeek/timonel/wiki/Release-and-Versioning)
|
|
771
437
|
|
|
772
|
-
|
|
773
|
-
policyEngine.invalidateCache({ all: true });
|
|
774
|
-
```
|
|
438
|
+
## Development
|
|
775
439
|
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
- **[Configuration Reference](https://github.com/KenkoGeek/timonel/wiki/Policy-Configuration)** -
|
|
787
|
-
Policy engine configuration options
|
|
788
|
-
- **[Policy Examples](https://github.com/KenkoGeek/timonel/wiki/Policy-Examples)** - Example plugins
|
|
789
|
-
and usage patterns
|
|
790
|
-
- **[Examples](https://github.com/KenkoGeek/timonel/wiki/Examples)** - Real-world usage examples
|
|
791
|
-
- **[Best Practices](https://github.com/KenkoGeek/timonel/wiki/Best-Practices)** - Recommended
|
|
792
|
-
patterns and practices
|
|
793
|
-
- **[Contributing](https://github.com/KenkoGeek/timonel/wiki/Contributing)** - Development setup
|
|
794
|
-
and guidelines
|
|
795
|
-
- **[Timonel Examples Repository](https://github.com/KenkoGeek/timonel-examples)** - Curated
|
|
796
|
-
collection of ready-to-run Timonel sample projects (Outdated)
|
|
797
|
-
|
|
798
|
-
## 🔧 Troubleshooting
|
|
799
|
-
|
|
800
|
-
### CDK8s Module Not Found Error
|
|
801
|
-
|
|
802
|
-
If you get `Error: Cannot find module 'cdk8s'` when running `tl umbrella synth`:
|
|
803
|
-
|
|
804
|
-
**Problem**: Timonel is installed globally, but your project needs CDK8s dependencies locally.
|
|
805
|
-
|
|
806
|
-
**Solution**: Create a `package.json` in your project directory:
|
|
807
|
-
|
|
808
|
-
```json
|
|
809
|
-
{
|
|
810
|
-
"name": "my-timonel-project",
|
|
811
|
-
"version": "1.0.0",
|
|
812
|
-
"type": "module",
|
|
813
|
-
"dependencies": {
|
|
814
|
-
"cdk8s": "^2.70.28",
|
|
815
|
-
"cdk8s-plus-33": "^2.4.6",
|
|
816
|
-
"constructs": "^10.4.3",
|
|
817
|
-
"timonel": "^3.1.0"
|
|
818
|
-
},
|
|
819
|
-
"devDependencies": {
|
|
820
|
-
"@types/node": "^24.5.2",
|
|
821
|
-
"typescript": "^5.9.2"
|
|
822
|
-
}
|
|
823
|
-
}
|
|
440
|
+
```bash
|
|
441
|
+
pnpm install --frozen-lockfile
|
|
442
|
+
pnpm ci:check
|
|
443
|
+
pnpm test:unit
|
|
444
|
+
pnpm test:integration
|
|
445
|
+
pnpm test:coverage
|
|
446
|
+
pnpm md:lint
|
|
447
|
+
pnpm doc:coverage:validate
|
|
448
|
+
pnpm security:audit
|
|
449
|
+
pnpm pack
|
|
824
450
|
```
|
|
825
451
|
|
|
826
|
-
|
|
452
|
+
Contribution and agent rules are defined in [`AGENTS.md`](AGENTS.md).
|
|
827
453
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
```
|
|
454
|
+
## Releases
|
|
455
|
+
|
|
456
|
+
Development follows a trunk-based model centered on `main`:
|
|
832
457
|
|
|
833
|
-
|
|
458
|
+
- pull requests target `main`;
|
|
459
|
+
- successful main CI can publish an npm `canary` build;
|
|
460
|
+
- stable publication is an explicit workflow with production approval;
|
|
461
|
+
- npm publication uses Trusted Publishing/OIDC and provenance.
|
|
834
462
|
|
|
835
|
-
See
|
|
836
|
-
|
|
463
|
+
See the [release guide](https://github.com/KenkoGeek/timonel/wiki/Release-and-Versioning) for the
|
|
464
|
+
current workflow and operational constraints.
|
|
837
465
|
|
|
838
|
-
##
|
|
466
|
+
## License
|
|
839
467
|
|
|
840
468
|
MIT
|
|
841
469
|
|
|
@@ -849,13 +477,13 @@ MIT
|
|
|
849
477
|
[security-url]: SECURITY.md
|
|
850
478
|
[pnpm-badge]: https://img.shields.io/badge/pm-pnpm-ffd95a?logo=pnpm&logoColor=fff&labelColor=24292e
|
|
851
479
|
[pnpm-url]: https://pnpm.io/
|
|
852
|
-
[node-badge]: https://img.shields.io/badge/node
|
|
480
|
+
[node-badge]: https://img.shields.io/badge/node-22.22.2%20%7C%2024.15%20%7C%2026-339933?logo=node.js&logoColor=fff
|
|
853
481
|
[node-url]: https://nodejs.org/
|
|
854
|
-
[ts-badge]: https://img.shields.io/badge/TypeScript-
|
|
482
|
+
[ts-badge]: https://img.shields.io/badge/TypeScript-6.x-3178C6?logo=typescript&logoColor=fff
|
|
855
483
|
[ts-url]: https://www.typescriptlang.org/
|
|
856
484
|
[maintained-badge]: https://img.shields.io/badge/maintained%20by-KenkoGeek-6C78AF?style=flat
|
|
857
485
|
[maintained-url]: https://github.com/kenkogeek/
|
|
858
|
-
[ci-badge]: https://github.com/KenkoGeek/timonel/actions/workflows/
|
|
859
|
-
[ci-url]: https://github.com/KenkoGeek/timonel/actions/workflows/
|
|
486
|
+
[ci-badge]: https://github.com/KenkoGeek/timonel/actions/workflows/ci.yml/badge.svg?branch=main
|
|
487
|
+
[ci-url]: https://github.com/KenkoGeek/timonel/actions/workflows/ci.yml
|
|
860
488
|
[codeql-badge]: https://github.com/KenkoGeek/timonel/actions/workflows/codeql.yml/badge.svg
|
|
861
489
|
[codeql-url]: https://github.com/KenkoGeek/timonel/actions/workflows/codeql.yml
|