timonel 2.1.1 → 2.2.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 +6 -0
- package/README.md +43 -809
- package/SECURITY.md +48 -20
- package/dist/lib/Rutter.d.ts +280 -0
- package/dist/lib/Rutter.d.ts.map +1 -1
- package/dist/lib/Rutter.js +353 -0
- package/dist/lib/Rutter.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,854 +10,88 @@
|
|
|
10
10
|
[![TypeScript][ts-badge]][ts-url]
|
|
11
11
|
[![Maintained by KenkoGeek][maintained-badge]][maintained-url]
|
|
12
12
|
|
|
13
|
-
Timonel (Spanish for
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
values files, and `templates/`.
|
|
13
|
+
Timonel (Spanish for "helmsman") is a TypeScript library to programmatically generate Helm charts
|
|
14
|
+
using cdk8s. Define Kubernetes resources with classes and synthesize a full Helm chart with
|
|
15
|
+
`Chart.yaml`, `values.yaml`, per‑environment values files, and `templates/`.
|
|
17
16
|
|
|
18
|
-
Key
|
|
17
|
+
## ✨ Key Features
|
|
19
18
|
|
|
20
|
-
- Type-safe API
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
- **
|
|
24
|
-
-
|
|
19
|
+
- **Type-safe API** with strict TypeScript and cdk8s constructs
|
|
20
|
+
- **Multi-environment support** with automatic values files generation
|
|
21
|
+
- **Umbrella Charts** for managing multiple subcharts as a single unit
|
|
22
|
+
- **Multi-cloud integrations** for AWS, Azure, and GCP
|
|
23
|
+
- **Security-first approach** with NetworkPolicies and best practices
|
|
24
|
+
- **Minimal CLI** (`tl`) for scaffolding and chart generation
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- Programmatic Helm helpers: generate `templates/_helpers.tpl` and call helpers using `template()`/`include()`.
|
|
29
|
-
- Umbrella chart support with automatic dependency management.
|
|
30
|
-
|
|
31
|
-
## Installation
|
|
32
|
-
|
|
33
|
-
### Using npm
|
|
26
|
+
## 🚀 Quick Start
|
|
34
27
|
|
|
35
28
|
```bash
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
# Install Timonel globally
|
|
30
|
+
npm install -g timonel
|
|
38
31
|
|
|
39
|
-
|
|
32
|
+
# Create your first chart
|
|
33
|
+
tl init my-app
|
|
40
34
|
|
|
41
|
-
|
|
35
|
+
# Generate Helm chart
|
|
36
|
+
tl synth charts/my-app charts/my-app-dist
|
|
42
37
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
corepack prepare pnpm@latest --activate
|
|
46
|
-
pnpm install
|
|
47
|
-
pnpm run build
|
|
38
|
+
# Use with Helm
|
|
39
|
+
helm install my-app charts/my-app-dist
|
|
48
40
|
```
|
|
49
41
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
After installing dependencies, enable git hooks with Husky:
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
pnpm dlx husky init || npx husky init
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
This sets up pre-commit and commit-msg hooks to run lint-staged and commitlint.
|
|
59
|
-
The pre-commit hook also runs Node-based Markdown linting (markdownlint)
|
|
60
|
-
automatically.
|
|
61
|
-
|
|
62
|
-
Markdown commands:
|
|
63
|
-
|
|
64
|
-
- Lint: `pnpm run md:lint`
|
|
65
|
-
- Fix: `pnpm run md:fix`
|
|
66
|
-
|
|
67
|
-
## Quick start
|
|
68
|
-
|
|
69
|
-
<!-- markdownlint-disable MD029 -->
|
|
70
|
-
|
|
71
|
-
1. Create an example project
|
|
72
|
-
|
|
73
|
-
```bash
|
|
74
|
-
tl init my-app-src
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
This generates `charts/my-app-src/chart.ts` with a working example.
|
|
78
|
-
|
|
79
|
-
2. Synthesize Helm chart artifacts
|
|
80
|
-
|
|
81
|
-
```bash
|
|
82
|
-
tl synth charts/my-app-src charts/my-app/
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Expected output:
|
|
86
|
-
|
|
87
|
-
- `charts/my-app/Chart.yaml`
|
|
88
|
-
- `charts/my-app/values.yaml`
|
|
89
|
-
- `charts/my-app/values-dev.yaml`, `values-prod.yaml` (if defined)
|
|
90
|
-
- `charts/my-app/templates/*.yaml`
|
|
91
|
-
|
|
92
|
-
3. Use with Helm
|
|
93
|
-
|
|
94
|
-
```bash
|
|
95
|
-
helm template charts/my-app -f charts/my-app/values-dev.yaml
|
|
96
|
-
helm install my-app charts/my-app -f charts/my-app/values-prod.yaml
|
|
97
|
-
```
|
|
42
|
+
## 📚 Documentation
|
|
98
43
|
|
|
99
|
-
|
|
100
|
-
exist. Use a clean folder or move old artifacts before running synth.
|
|
44
|
+
For comprehensive documentation, examples, and guides, visit our **[Wiki](https://github.com/KenkoGeek/timonel/wiki)**:
|
|
101
45
|
|
|
102
|
-
|
|
46
|
+
- **[Installation](https://github.com/KenkoGeek/timonel/wiki/Installation)** - Setup and requirements
|
|
47
|
+
- **[Quick Start](https://github.com/KenkoGeek/timonel/wiki/Quick-Start)** - Your first chart in 5 minutes
|
|
48
|
+
- **[Examples](https://github.com/KenkoGeek/timonel/wiki/Examples)** - Real-world deployment patterns
|
|
49
|
+
- **[Multi-Cloud Support](https://github.com/KenkoGeek/timonel/wiki/Multi-Cloud-Support)** - AWS,
|
|
50
|
+
Azure, GCP integrations
|
|
51
|
+
- **[CLI Reference](https://github.com/KenkoGeek/timonel/wiki/CLI-Commands)** - Complete command guide
|
|
103
52
|
|
|
104
|
-
|
|
105
|
-
`.tgz` that you can publish or attach to releases:
|
|
106
|
-
|
|
107
|
-
```bash
|
|
108
|
-
tl package charts/my-app charts/
|
|
109
|
-
# or set HELM_BIN if helm is not in PATH
|
|
110
|
-
HELM_BIN=/usr/local/bin/helm tl package charts/my-app charts/
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
## Library API
|
|
53
|
+
## 💡 Basic Example
|
|
114
54
|
|
|
115
55
|
```typescript
|
|
116
56
|
import { Rutter } from 'timonel';
|
|
117
|
-
import {
|
|
118
|
-
valuesRef,
|
|
119
|
-
helm,
|
|
120
|
-
template,
|
|
121
|
-
include,
|
|
122
|
-
numberRef,
|
|
123
|
-
boolRef,
|
|
124
|
-
stringRef,
|
|
125
|
-
floatRef,
|
|
126
|
-
} from 'timonel';
|
|
57
|
+
import { valuesRef } from 'timonel';
|
|
127
58
|
|
|
128
59
|
const rutter = new Rutter({
|
|
129
|
-
meta: { name: 'my-app', version: '0.1.0'
|
|
60
|
+
meta: { name: 'my-app', version: '0.1.0' },
|
|
130
61
|
defaultValues: {
|
|
131
62
|
image: { repository: 'nginx', tag: '1.27' },
|
|
132
63
|
replicas: 2,
|
|
133
|
-
service: { port: 80 },
|
|
134
64
|
},
|
|
135
|
-
envValues: {
|
|
136
|
-
|
|
137
|
-
{
|
|
138
|
-
|
|
139
|
-
body: `{{- printf "%s-%s" .Chart.Name .Release.Name | trunc 63 | trimSuffix "-" -}}`,
|
|
140
|
-
},
|
|
141
|
-
],
|
|
142
|
-
// Custom manifest naming options
|
|
143
|
-
manifestName: 'my-app-resources', // Custom name for manifest files
|
|
144
|
-
singleManifestFile: true, // Combine all resources into one file
|
|
65
|
+
envValues: {
|
|
66
|
+
dev: { replicas: 1 },
|
|
67
|
+
prod: { replicas: 5 },
|
|
68
|
+
},
|
|
145
69
|
});
|
|
146
70
|
|
|
147
71
|
rutter.addDeployment({
|
|
148
72
|
name: 'my-app',
|
|
149
73
|
image: `${valuesRef('image.repository')}:${valuesRef('image.tag')}`,
|
|
150
|
-
replicas:
|
|
74
|
+
replicas: Number(valuesRef('replicas')),
|
|
151
75
|
containerPort: 80,
|
|
152
76
|
});
|
|
153
77
|
|
|
154
78
|
rutter.addService({ name: 'my-app', port: 80 });
|
|
155
79
|
|
|
156
|
-
|
|
157
|
-
rutter.
|
|
158
|
-
|
|
159
|
-
scaleTargetRef: { apiVersion: 'apps/v1', kind: 'Deployment', name: 'my-app' },
|
|
160
|
-
minReplicas: 1,
|
|
161
|
-
maxReplicas: 10,
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
rutter.addVerticalPodAutoscaler({
|
|
165
|
-
name: 'my-app-vpa',
|
|
166
|
-
targetRef: { apiVersion: 'apps/v1', kind: 'Deployment', name: 'my-app' },
|
|
167
|
-
updatePolicy: { updateMode: 'Auto' },
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
rutter.addPodDisruptionBudget({
|
|
171
|
-
name: 'my-app-pdb',
|
|
172
|
-
minAvailable: 1,
|
|
173
|
-
selector: { matchLabels: { app: 'my-app' } },
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
// Add one-time Job for database migration
|
|
177
|
-
rutter.addJob({
|
|
178
|
-
name: 'db-migration',
|
|
179
|
-
image: 'migrate:latest',
|
|
180
|
-
command: ['migrate'],
|
|
181
|
-
args: ['up'],
|
|
182
|
-
restartPolicy: 'OnFailure',
|
|
183
|
-
backoffLimit: 3,
|
|
184
|
-
activeDeadlineSeconds: 600,
|
|
185
|
-
ttlSecondsAfterFinished: 86400,
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
// Add scheduled CronJob for backups
|
|
189
|
-
rutter.addCronJob({
|
|
190
|
-
name: 'backup-job',
|
|
191
|
-
schedule: '0 2 * * *', // Daily at 2 AM
|
|
192
|
-
image: 'backup:latest',
|
|
193
|
-
command: ['backup.sh'],
|
|
194
|
-
restartPolicy: 'OnFailure',
|
|
195
|
-
concurrencyPolicy: 'Forbid',
|
|
196
|
-
successfulJobsHistoryLimit: 3,
|
|
197
|
-
failedJobsHistoryLimit: 1,
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
// Add IRSA ServiceAccount for AWS access
|
|
201
|
-
rutter.addAWSIRSAServiceAccount({
|
|
202
|
-
name: 'my-app-irsa',
|
|
203
|
-
roleArn: 'arn:aws:iam::123456789012:role/MyAppRole',
|
|
204
|
-
stsEndpointType: 'regional',
|
|
205
|
-
tokenExpiration: 3600,
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
rutter.write('dist/charts/my-app');
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### Custom Manifest Naming
|
|
212
|
-
|
|
213
|
-
> **⚠️ Breaking Change Notice**: Starting from v1.1.0, template files no longer include
|
|
214
|
-
> automatic numbering prefixes (0000-, 0001-, etc.). Files now use descriptive names
|
|
215
|
-
> following Helm best practices. If your scripts or tools depend on numbered filenames,
|
|
216
|
-
> please update them accordingly.
|
|
217
|
-
|
|
218
|
-
Timonel provides flexible options for naming your Kubernetes manifest files:
|
|
219
|
-
|
|
220
|
-
#### Single Manifest File
|
|
221
|
-
|
|
222
|
-
```typescript
|
|
223
|
-
const rutter = new Rutter({
|
|
224
|
-
meta: { name: 'my-app', version: '0.1.0' },
|
|
225
|
-
manifestName: 'application',
|
|
226
|
-
singleManifestFile: true, // All resources in one file
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
// Generates: templates/application.yaml
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
#### Separate Files with Custom Names
|
|
233
|
-
|
|
234
|
-
```typescript
|
|
235
|
-
const rutter = new Rutter({
|
|
236
|
-
meta: { name: 'my-app', version: '0.1.0' },
|
|
237
|
-
manifestName: 'my-app',
|
|
238
|
-
singleManifestFile: false, // Each resource in its own file (default)
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
// Generates:
|
|
242
|
-
// templates/my-app-deployment-web.yaml
|
|
243
|
-
// templates/my-app-service-web.yaml
|
|
244
|
-
// templates/my-app-configmap-config.yaml
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
#### Default Behavior
|
|
248
|
-
|
|
249
|
-
```typescript
|
|
250
|
-
const rutter = new Rutter({
|
|
251
|
-
meta: { name: 'my-app', version: '0.1.0' },
|
|
252
|
-
// No manifestName specified
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
// Generates:
|
|
256
|
-
// templates/deployment-web.yaml
|
|
257
|
-
// templates/service-web.yaml
|
|
258
|
-
// templates/configmap-config.yaml
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
## Umbrella Charts
|
|
262
|
-
|
|
263
|
-
Create umbrella charts that combine multiple subcharts into a single deployable unit:
|
|
264
|
-
|
|
265
|
-
```typescript
|
|
266
|
-
import { createUmbrella, Rutter } from 'timonel';
|
|
267
|
-
|
|
268
|
-
// Create individual subcharts
|
|
269
|
-
const mysql = new Rutter({
|
|
270
|
-
meta: { name: 'mysql', version: '0.1.0' },
|
|
271
|
-
// MySQL configuration...
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
const wordpress = new Rutter({
|
|
275
|
-
meta: { name: 'wordpress', version: '0.1.0' },
|
|
276
|
-
// WordPress configuration...
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
// Combine into umbrella chart
|
|
280
|
-
const umbrella = createUmbrella({
|
|
281
|
-
meta: {
|
|
282
|
-
name: 'wordpress-stack',
|
|
283
|
-
version: '1.0.0',
|
|
284
|
-
description: 'Complete WordPress stack with MySQL database',
|
|
285
|
-
},
|
|
286
|
-
subcharts: [
|
|
287
|
-
{ name: 'mysql', rutter: mysql },
|
|
288
|
-
{ name: 'wordpress', rutter: wordpress },
|
|
289
|
-
],
|
|
290
|
-
defaultValues: {
|
|
291
|
-
global: { storageClass: 'gp2' },
|
|
292
|
-
mysql: { persistence: { size: '8Gi' } },
|
|
293
|
-
wordpress: { service: { type: 'LoadBalancer' } },
|
|
294
|
-
},
|
|
295
|
-
envValues: {
|
|
296
|
-
dev: { mysql: { persistence: { size: '5Gi' } } },
|
|
297
|
-
prod: { mysql: { persistence: { size: '20Gi' } } },
|
|
298
|
-
},
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
umbrella.write('dist/wordpress-stack');
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
### CLI for Umbrella Charts
|
|
305
|
-
|
|
306
|
-
```bash
|
|
307
|
-
# Create umbrella structure
|
|
308
|
-
tl umbrella init my-stack
|
|
309
|
-
|
|
310
|
-
# Add subcharts
|
|
311
|
-
tl umbrella add database
|
|
312
|
-
tl umbrella add frontend
|
|
313
|
-
|
|
314
|
-
# Generate umbrella chart
|
|
315
|
-
tl umbrella synth ./dist
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
## Examples
|
|
319
|
-
|
|
320
|
-
The `examples/` directory contains complete working examples:
|
|
321
|
-
|
|
322
|
-
- **aws-game-2048**: Production-ready AWS 2048 game deployment showcasing AWS-specific features
|
|
323
|
-
- AWS ALB Ingress with health checks and SSL support
|
|
324
|
-
- HorizontalPodAutoscaler (HPA) for automatic scaling based on CPU utilization
|
|
325
|
-
- PodDisruptionBudget (PDB) for high availability during updates
|
|
326
|
-
- Multi-environment configuration (dev/staging/prod)
|
|
327
|
-
- **wordpress**: WordPress with MySQL database setup (single chart)
|
|
328
|
-
- **wordpress-umbrella**: WordPress stack using umbrella charts (MySQL + WordPress subcharts)
|
|
329
|
-
|
|
330
|
-
Each example includes its own README with deployment instructions.
|
|
331
|
-
|
|
332
|
-
### Umbrella Chart Example
|
|
333
|
-
|
|
334
|
-
See `examples/wordpress-umbrella/` for a complete umbrella chart implementation that
|
|
335
|
-
separates MySQL and WordPress into individual subcharts with proper dependencies,
|
|
336
|
-
shared values, and multi-environment support.
|
|
337
|
-
|
|
338
|
-
- `valuesRef(path)`: returns a Helm placeholder string for `.Values.*`.
|
|
339
|
-
- You can pass these strings directly into cdk8s constructs; they are preserved in the YAML.
|
|
340
|
-
- `template(name, ctx='.')` and `include(name, ctx='.')`: inject calls to helpers defined in `_helpers.tpl`.
|
|
341
|
-
- `numberRef(path)`, `boolRef(path)`: cast `.Values.*` to numeric/boolean using Sprig
|
|
342
|
-
(`int`, `toBool`). Use for template strings, not direct numeric fields.
|
|
343
|
-
- `stringRef(path)`, `floatRef(path)`: cast `.Values.*` to string/float using Sprig
|
|
344
|
-
(`toString`, `float64`).
|
|
345
|
-
- For numeric fields (ports, replicas), use literal values or template strings with proper casting.
|
|
346
|
-
|
|
347
|
-
## Multi-Cloud Support
|
|
348
|
-
|
|
349
|
-
Timonel provides comprehensive cloud-specific helpers for major Kubernetes platforms:
|
|
350
|
-
|
|
351
|
-
### AWS Multi-Cloud Support
|
|
352
|
-
|
|
353
|
-
Comprehensive AWS-specific helpers for EKS deployments:
|
|
354
|
-
|
|
355
|
-
### IRSA (IAM Roles for Service Accounts)
|
|
356
|
-
|
|
357
|
-
Securely access AWS services from Kubernetes pods using IAM roles:
|
|
358
|
-
|
|
359
|
-
```typescript
|
|
360
|
-
// Dedicated IRSA ServiceAccount
|
|
361
|
-
rutter.addAWSIRSAServiceAccount({
|
|
362
|
-
name: 'app-s3-access',
|
|
363
|
-
roleArn: 'arn:aws:iam::123456789012:role/AppS3Role',
|
|
364
|
-
audience: 'sts.amazonaws.com', // optional, defaults to sts.amazonaws.com
|
|
365
|
-
stsEndpointType: 'regional', // recommended for better performance
|
|
366
|
-
tokenExpiration: 3600, // optional, token lifetime in seconds
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
// General ServiceAccount with IRSA support
|
|
370
|
-
rutter.addServiceAccount({
|
|
371
|
-
name: 'my-app-sa',
|
|
372
|
-
awsRoleArn: 'arn:aws:iam::123456789012:role/MyAppRole',
|
|
373
|
-
awsStsEndpointType: 'regional',
|
|
374
|
-
awsTokenExpiration: 7200,
|
|
375
|
-
automountServiceAccountToken: true,
|
|
376
|
-
});
|
|
80
|
+
export default function run(outDir: string) {
|
|
81
|
+
rutter.write(outDir);
|
|
82
|
+
}
|
|
377
83
|
```
|
|
378
84
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
```typescript
|
|
382
|
-
// EBS GP3 StorageClass
|
|
383
|
-
rutter.addAWSEBSStorageClass({
|
|
384
|
-
name: 'fast-ssd',
|
|
385
|
-
volumeType: 'gp3',
|
|
386
|
-
encrypted: true,
|
|
387
|
-
iops: 3000,
|
|
388
|
-
throughput: 125,
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
// EFS StorageClass for shared storage
|
|
392
|
-
rutter.addAWSEFSStorageClass({
|
|
393
|
-
name: 'shared-storage',
|
|
394
|
-
reclaimPolicy: 'Retain',
|
|
395
|
-
});
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
### AWS Load Balancer Controller
|
|
399
|
-
|
|
400
|
-
```typescript
|
|
401
|
-
// ALB Ingress with health checks
|
|
402
|
-
rutter.addAWSALBIngress({
|
|
403
|
-
name: 'app-ingress',
|
|
404
|
-
scheme: 'internet-facing',
|
|
405
|
-
targetType: 'ip',
|
|
406
|
-
healthCheckPath: '/health',
|
|
407
|
-
certificateArn:
|
|
408
|
-
'arn:aws:acm:us-west-2:123456789012:certificate/12345678-1234-1234-1234-123456789012',
|
|
409
|
-
rules: [
|
|
410
|
-
{
|
|
411
|
-
paths: [
|
|
412
|
-
{
|
|
413
|
-
path: '/',
|
|
414
|
-
pathType: 'Prefix',
|
|
415
|
-
backend: { service: { name: 'my-app', port: { number: 80 } } },
|
|
416
|
-
},
|
|
417
|
-
],
|
|
418
|
-
},
|
|
419
|
-
],
|
|
420
|
-
});
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
### AWS Secrets Manager and Parameter Store
|
|
424
|
-
|
|
425
|
-
Integrate with AWS Secrets Manager and Parameter Store using the Secrets Store CSI Driver:
|
|
426
|
-
|
|
427
|
-
```typescript
|
|
428
|
-
// Secrets Manager integration
|
|
429
|
-
rutter.addAWSSecretProviderClass({
|
|
430
|
-
name: 'app-secrets',
|
|
431
|
-
region: 'us-west-2',
|
|
432
|
-
objects: [
|
|
433
|
-
{
|
|
434
|
-
objectName: 'prod/myapp/database',
|
|
435
|
-
objectType: 'secretsmanager',
|
|
436
|
-
objectAlias: 'db-credentials',
|
|
437
|
-
},
|
|
438
|
-
{
|
|
439
|
-
objectName: 'prod/myapp/api-keys',
|
|
440
|
-
objectType: 'secretsmanager',
|
|
441
|
-
jmesPath: '["api_key", "secret_key"]', // Extract specific keys from JSON
|
|
442
|
-
},
|
|
443
|
-
],
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
// Parameter Store integration
|
|
447
|
-
rutter.addAWSSecretProviderClass({
|
|
448
|
-
name: 'app-config',
|
|
449
|
-
region: 'us-west-2',
|
|
450
|
-
objects: [
|
|
451
|
-
{
|
|
452
|
-
objectName: '/myapp/config/debug-mode',
|
|
453
|
-
objectType: 'ssmparameter',
|
|
454
|
-
objectAlias: 'debug-flag',
|
|
455
|
-
},
|
|
456
|
-
{
|
|
457
|
-
objectName: '/myapp/config/cache-ttl',
|
|
458
|
-
objectType: 'ssmparameter',
|
|
459
|
-
objectAlias: 'cache-timeout',
|
|
460
|
-
},
|
|
461
|
-
],
|
|
462
|
-
});
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
### Azure Multi-Cloud Support
|
|
466
|
-
|
|
467
|
-
Azure-specific helpers for AKS deployments:
|
|
468
|
-
|
|
469
|
-
#### Azure Disk Storage Classes
|
|
470
|
-
|
|
471
|
-
```typescript
|
|
472
|
-
// Premium ZRS disk for production workloads
|
|
473
|
-
rutter.addAzureDiskStorageClass({
|
|
474
|
-
name: 'azure-premium-zrs',
|
|
475
|
-
skuName: 'Premium_ZRS',
|
|
476
|
-
fsType: 'ext4',
|
|
477
|
-
cachingMode: 'ReadWrite',
|
|
478
|
-
allowVolumeExpansion: true,
|
|
479
|
-
volumeBindingMode: 'WaitForFirstConsumer',
|
|
480
|
-
tags: {
|
|
481
|
-
Environment: 'production',
|
|
482
|
-
Application: 'database',
|
|
483
|
-
},
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
// Standard SSD for cost-optimized workloads
|
|
487
|
-
rutter.addAzureDiskStorageClass({
|
|
488
|
-
name: 'azure-standard-ssd',
|
|
489
|
-
skuName: 'StandardSSD_LRS',
|
|
490
|
-
fsType: 'ext4',
|
|
491
|
-
cachingMode: 'ReadOnly',
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
// Ultra SSD for high-performance workloads
|
|
495
|
-
rutter.addAzureDiskStorageClass({
|
|
496
|
-
name: 'azure-ultra-ssd',
|
|
497
|
-
skuName: 'UltraSSD_LRS',
|
|
498
|
-
fsType: 'ext4',
|
|
499
|
-
cachingMode: 'None',
|
|
500
|
-
diskIOPSReadWrite: 2000,
|
|
501
|
-
diskMBpsReadWrite: 200,
|
|
502
|
-
logicalSectorSize: 4096,
|
|
503
|
-
});
|
|
504
|
-
```
|
|
505
|
-
|
|
506
|
-
#### Azure Application Gateway Ingress Controller (AGIC)
|
|
507
|
-
|
|
508
|
-
Comprehensive AGIC support for AKS deployments with advanced features:
|
|
509
|
-
|
|
510
|
-
```typescript
|
|
511
|
-
// AGIC Ingress with SSL and health checks
|
|
512
|
-
rutter.addAzureAGICIngress({
|
|
513
|
-
name: 'app-ingress',
|
|
514
|
-
rules: [
|
|
515
|
-
{
|
|
516
|
-
host: 'app.example.com',
|
|
517
|
-
paths: [
|
|
518
|
-
{
|
|
519
|
-
path: '/',
|
|
520
|
-
pathType: 'Prefix',
|
|
521
|
-
backend: { service: { name: 'app-service', port: { number: 80 } } },
|
|
522
|
-
},
|
|
523
|
-
],
|
|
524
|
-
},
|
|
525
|
-
],
|
|
526
|
-
sslRedirect: true,
|
|
527
|
-
backendProtocol: 'https',
|
|
528
|
-
healthProbePath: '/health',
|
|
529
|
-
healthProbeInterval: 30,
|
|
530
|
-
cookieBasedAffinity: true,
|
|
531
|
-
requestTimeout: 60,
|
|
532
|
-
appgwSslCertificate: 'my-ssl-cert',
|
|
533
|
-
wafPolicyForPath:
|
|
534
|
-
'/subscriptions/sub-id/resourceGroups/rg/providers/Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies/waf-policy',
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
// Advanced AGIC features
|
|
538
|
-
rutter.addAzureAGICIngress({
|
|
539
|
-
name: 'advanced-ingress',
|
|
540
|
-
rules: [
|
|
541
|
-
/* rules */
|
|
542
|
-
],
|
|
543
|
-
backendPathPrefix: '/api/v1',
|
|
544
|
-
backendHostname: 'internal.example.com',
|
|
545
|
-
usePrivateIp: true,
|
|
546
|
-
overrideFrontendPort: 8080,
|
|
547
|
-
connectionDraining: true,
|
|
548
|
-
connectionDrainingTimeout: 60,
|
|
549
|
-
hostnameExtension: ['api.example.com', 'admin.example.com'],
|
|
550
|
-
appgwTrustedRootCertificate: ['root-cert-1', 'root-cert-2'],
|
|
551
|
-
rewriteRuleSet: 'custom-rewrite-rules',
|
|
552
|
-
rulePriority: 100,
|
|
553
|
-
});
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
## Network Security with NetworkPolicies
|
|
557
|
-
|
|
558
|
-
Timonel provides comprehensive NetworkPolicy helpers for implementing Zero Trust network security
|
|
559
|
-
in Kubernetes:
|
|
560
|
-
|
|
561
|
-
### Zero Trust Network Security
|
|
562
|
-
|
|
563
|
-
Implement defense-in-depth with deny-by-default policies:
|
|
564
|
-
|
|
565
|
-
```typescript
|
|
566
|
-
// 1. Deny all traffic by default (recommended starting point)
|
|
567
|
-
rutter.addDenyAllNetworkPolicy('default-deny-all');
|
|
568
|
-
|
|
569
|
-
// 2. Allow specific traffic as needed
|
|
570
|
-
rutter.addAllowFromPodsNetworkPolicy({
|
|
571
|
-
name: 'allow-frontend-to-backend',
|
|
572
|
-
targetPodSelector: { app: 'backend' },
|
|
573
|
-
sourcePodSelector: { app: 'frontend' },
|
|
574
|
-
ports: [{ protocol: 'TCP', port: 8080 }],
|
|
575
|
-
});
|
|
576
|
-
```
|
|
577
|
-
|
|
578
|
-
### Advanced NetworkPolicy Examples
|
|
579
|
-
|
|
580
|
-
#### Multi-tier Application Security
|
|
581
|
-
|
|
582
|
-
```typescript
|
|
583
|
-
// Web tier - allow external traffic on port 80/443
|
|
584
|
-
rutter.addNetworkPolicy({
|
|
585
|
-
name: 'web-tier-policy',
|
|
586
|
-
podSelector: { matchLabels: { tier: 'web' } },
|
|
587
|
-
policyTypes: ['Ingress', 'Egress'],
|
|
588
|
-
ingress: [
|
|
589
|
-
{
|
|
590
|
-
ports: [
|
|
591
|
-
{ protocol: 'TCP', port: 80 },
|
|
592
|
-
{ protocol: 'TCP', port: 443 },
|
|
593
|
-
],
|
|
594
|
-
},
|
|
595
|
-
],
|
|
596
|
-
egress: [
|
|
597
|
-
// Allow access to app tier
|
|
598
|
-
{
|
|
599
|
-
to: [{ podSelector: { matchLabels: { tier: 'app' } } }],
|
|
600
|
-
ports: [{ protocol: 'TCP', port: 8080 }],
|
|
601
|
-
},
|
|
602
|
-
// Allow DNS resolution
|
|
603
|
-
{
|
|
604
|
-
to: [{ namespaceSelector: { matchLabels: { name: 'kube-system' } } }],
|
|
605
|
-
ports: [
|
|
606
|
-
{ protocol: 'UDP', port: 53 },
|
|
607
|
-
{ protocol: 'TCP', port: 53 },
|
|
608
|
-
],
|
|
609
|
-
},
|
|
610
|
-
],
|
|
611
|
-
});
|
|
612
|
-
|
|
613
|
-
// App tier - only allow traffic from web tier
|
|
614
|
-
rutter.addNetworkPolicy({
|
|
615
|
-
name: 'app-tier-policy',
|
|
616
|
-
podSelector: { matchLabels: { tier: 'app' } },
|
|
617
|
-
policyTypes: ['Ingress', 'Egress'],
|
|
618
|
-
ingress: [
|
|
619
|
-
{
|
|
620
|
-
from: [{ podSelector: { matchLabels: { tier: 'web' } } }],
|
|
621
|
-
ports: [{ protocol: 'TCP', port: 8080 }],
|
|
622
|
-
},
|
|
623
|
-
],
|
|
624
|
-
egress: [
|
|
625
|
-
// Allow access to database
|
|
626
|
-
{
|
|
627
|
-
to: [{ podSelector: { matchLabels: { tier: 'database' } } }],
|
|
628
|
-
ports: [{ protocol: 'TCP', port: 5432 }],
|
|
629
|
-
},
|
|
630
|
-
// Allow external API calls (with CIDR restrictions)
|
|
631
|
-
{
|
|
632
|
-
to: [
|
|
633
|
-
{
|
|
634
|
-
ipBlock: {
|
|
635
|
-
cidr: '0.0.0.0/0',
|
|
636
|
-
except: ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'],
|
|
637
|
-
},
|
|
638
|
-
},
|
|
639
|
-
],
|
|
640
|
-
ports: [{ protocol: 'TCP', port: 443 }],
|
|
641
|
-
},
|
|
642
|
-
],
|
|
643
|
-
});
|
|
644
|
-
|
|
645
|
-
// Database tier - most restrictive
|
|
646
|
-
rutter.addNetworkPolicy({
|
|
647
|
-
name: 'database-tier-policy',
|
|
648
|
-
podSelector: { matchLabels: { tier: 'database' } },
|
|
649
|
-
policyTypes: ['Ingress', 'Egress'],
|
|
650
|
-
ingress: [
|
|
651
|
-
{
|
|
652
|
-
from: [{ podSelector: { matchLabels: { tier: 'app' } } }],
|
|
653
|
-
ports: [{ protocol: 'TCP', port: 5432 }],
|
|
654
|
-
},
|
|
655
|
-
],
|
|
656
|
-
egress: [
|
|
657
|
-
// Only allow DNS resolution
|
|
658
|
-
{
|
|
659
|
-
to: [{ namespaceSelector: { matchLabels: { name: 'kube-system' } } }],
|
|
660
|
-
ports: [
|
|
661
|
-
{ protocol: 'UDP', port: 53 },
|
|
662
|
-
{ protocol: 'TCP', port: 53 },
|
|
663
|
-
],
|
|
664
|
-
},
|
|
665
|
-
],
|
|
666
|
-
});
|
|
667
|
-
```
|
|
668
|
-
|
|
669
|
-
#### Cross-Namespace Communication
|
|
670
|
-
|
|
671
|
-
```typescript
|
|
672
|
-
// Allow traffic from monitoring namespace
|
|
673
|
-
rutter.addAllowFromNamespaceNetworkPolicy({
|
|
674
|
-
name: 'allow-monitoring',
|
|
675
|
-
targetPodSelector: { app: 'backend' },
|
|
676
|
-
sourceNamespaceSelector: { name: 'monitoring' },
|
|
677
|
-
ports: [{ protocol: 'TCP', port: 9090 }], // Prometheus metrics
|
|
678
|
-
});
|
|
679
|
-
|
|
680
|
-
// Allow traffic to shared services namespace
|
|
681
|
-
rutter.addNetworkPolicy({
|
|
682
|
-
name: 'allow-to-shared-services',
|
|
683
|
-
podSelector: { matchLabels: { app: 'backend' } },
|
|
684
|
-
policyTypes: ['Egress'],
|
|
685
|
-
egress: [
|
|
686
|
-
{
|
|
687
|
-
to: [
|
|
688
|
-
{
|
|
689
|
-
namespaceSelector: { matchLabels: { name: 'shared-services' } },
|
|
690
|
-
podSelector: { matchLabels: { app: 'redis' } },
|
|
691
|
-
},
|
|
692
|
-
],
|
|
693
|
-
ports: [{ protocol: 'TCP', port: 6379 }],
|
|
694
|
-
},
|
|
695
|
-
],
|
|
696
|
-
});
|
|
697
|
-
```
|
|
698
|
-
|
|
699
|
-
### Security Best Practices
|
|
700
|
-
|
|
701
|
-
#### 1. Start with Deny-All Policies
|
|
702
|
-
|
|
703
|
-
```typescript
|
|
704
|
-
// Always start with deny-all for maximum security
|
|
705
|
-
rutter.addDenyAllNetworkPolicy('default-deny-all');
|
|
706
|
-
|
|
707
|
-
// Then add specific allow rules
|
|
708
|
-
rutter.addAllowFromPodsNetworkPolicy({
|
|
709
|
-
name: 'allow-specific-communication',
|
|
710
|
-
targetPodSelector: { app: 'api' },
|
|
711
|
-
sourcePodSelector: { app: 'frontend' },
|
|
712
|
-
ports: [{ protocol: 'TCP', port: 8080 }],
|
|
713
|
-
});
|
|
714
|
-
```
|
|
715
|
-
|
|
716
|
-
#### 2. Separate Ingress and Egress Policies
|
|
717
|
-
|
|
718
|
-
```typescript
|
|
719
|
-
// Separate policies for better maintainability
|
|
720
|
-
rutter.addDenyAllIngressNetworkPolicy('deny-all-ingress');
|
|
721
|
-
rutter.addDenyAllEgressNetworkPolicy('deny-all-egress');
|
|
722
|
-
```
|
|
723
|
-
|
|
724
|
-
#### 3. Use CIDR Blocks for External Access
|
|
725
|
-
|
|
726
|
-
```typescript
|
|
727
|
-
// Restrict external access to specific IP ranges
|
|
728
|
-
rutter.addNetworkPolicy({
|
|
729
|
-
name: 'external-api-access',
|
|
730
|
-
podSelector: { matchLabels: { app: 'backend' } },
|
|
731
|
-
policyTypes: ['Egress'],
|
|
732
|
-
egress: [
|
|
733
|
-
{
|
|
734
|
-
to: [
|
|
735
|
-
{
|
|
736
|
-
ipBlock: {
|
|
737
|
-
cidr: '203.0.113.0/24', // Specific external service
|
|
738
|
-
},
|
|
739
|
-
},
|
|
740
|
-
],
|
|
741
|
-
ports: [{ protocol: 'TCP', port: 443 }],
|
|
742
|
-
},
|
|
743
|
-
],
|
|
744
|
-
});
|
|
745
|
-
```
|
|
746
|
-
|
|
747
|
-
#### 4. Include DNS Resolution
|
|
748
|
-
|
|
749
|
-
```typescript
|
|
750
|
-
// Always allow DNS for name resolution
|
|
751
|
-
const dnsEgressRule = {
|
|
752
|
-
to: [{ namespaceSelector: { matchLabels: { name: 'kube-system' } } }],
|
|
753
|
-
ports: [
|
|
754
|
-
{ protocol: 'UDP', port: 53 },
|
|
755
|
-
{ protocol: 'TCP', port: 53 },
|
|
756
|
-
],
|
|
757
|
-
};
|
|
758
|
-
|
|
759
|
-
rutter.addNetworkPolicy({
|
|
760
|
-
name: 'app-with-dns',
|
|
761
|
-
podSelector: { matchLabels: { app: 'backend' } },
|
|
762
|
-
policyTypes: ['Egress'],
|
|
763
|
-
egress: [dnsEgressRule /* other rules */],
|
|
764
|
-
});
|
|
765
|
-
```
|
|
766
|
-
|
|
767
|
-
### Basic NetworkPolicy
|
|
768
|
-
|
|
769
|
-
```typescript
|
|
770
|
-
// Custom NetworkPolicy with full control
|
|
771
|
-
rutter.addNetworkPolicy({
|
|
772
|
-
name: 'custom-policy',
|
|
773
|
-
podSelector: { matchLabels: { app: 'backend' } },
|
|
774
|
-
policyTypes: ['Ingress', 'Egress'],
|
|
775
|
-
ingress: [
|
|
776
|
-
{
|
|
777
|
-
from: [{ podSelector: { matchLabels: { app: 'frontend' } } }],
|
|
778
|
-
ports: [{ protocol: 'TCP', port: 8080 }],
|
|
779
|
-
},
|
|
780
|
-
],
|
|
781
|
-
egress: [
|
|
782
|
-
{
|
|
783
|
-
to: [{ podSelector: { matchLabels: { app: 'database' } } }],
|
|
784
|
-
ports: [{ protocol: 'TCP', port: 5432 }],
|
|
785
|
-
},
|
|
786
|
-
],
|
|
787
|
-
});
|
|
788
|
-
```
|
|
789
|
-
|
|
790
|
-
## Multi-environment values
|
|
791
|
-
|
|
792
|
-
Provide `envValues` in the `Rutter` constructor to automatically create
|
|
793
|
-
`values-<env>.yaml` files. Each environment file overrides defaults from
|
|
794
|
-
`values.yaml`.
|
|
795
|
-
|
|
796
|
-
## Security
|
|
797
|
-
|
|
798
|
-
- ESLint security plugin with comprehensive vulnerability detection
|
|
799
|
-
- Automated dependency scanning via Dependabot
|
|
800
|
-
- Security audit in CI/CD pipeline
|
|
801
|
-
- Provenance-enabled npm publishing
|
|
802
|
-
- CodeQL analysis for code security
|
|
803
|
-
|
|
804
|
-
## Notes on cdk8s and Helm templates
|
|
805
|
-
|
|
806
|
-
- cdk8s synthesizes Kubernetes manifests. Timonel wraps them into a Helm chart
|
|
807
|
-
structure and allows Helm placeholders to appear in string fields (e.g.,
|
|
808
|
-
`{{ .Values.image.tag }}`).
|
|
809
|
-
- For advanced templating, Timonel can generate `_helpers.tpl`. Provide `helpersTpl`
|
|
810
|
-
in `Rutter` as a string (verbatim) or as named helpers. Use `template()` or `include()`
|
|
811
|
-
to reference them in your manifests.
|
|
812
|
-
|
|
813
|
-
## Roadmap
|
|
814
|
-
|
|
815
|
-
- ✅ Auto-scaling helpers (HPA, VPA, PodDisruptionBudget)
|
|
816
|
-
- ✅ AWS multi-cloud support (EBS, EFS, ALB, IRSA, Secrets Manager, Parameter Store)
|
|
817
|
-
- ✅ Custom manifest naming and file organization
|
|
818
|
-
- ✅ Azure multi-cloud support (Azure Disk StorageClass, AGIC)
|
|
819
|
-
- ✅ Job and CronJob helpers for batch workloads
|
|
820
|
-
- GCP multi-cloud support (GKE-specific helpers)
|
|
821
|
-
- Richer CLI (resource generators, diff)
|
|
822
|
-
- Template validation and testing utilities
|
|
823
|
-
|
|
824
|
-
## Contributing
|
|
825
|
-
|
|
826
|
-
1. Fork the repository
|
|
827
|
-
2. Create a feature branch: `git checkout -b feature/amazing-feature`
|
|
828
|
-
3. Make your changes following the existing code style
|
|
829
|
-
4. Run tests: `pnpm ci:check`
|
|
830
|
-
5. Update CHANGELOG.md following [Keep a Changelog](https://keepachangelog.com/) format
|
|
831
|
-
6. Commit using conventional commits: `git commit -m 'feat: add amazing feature'`
|
|
832
|
-
7. Push to the branch: `git push origin feature/amazing-feature`
|
|
833
|
-
8. Open a Pull Request using the provided template
|
|
834
|
-
|
|
835
|
-
## Troubleshooting
|
|
836
|
-
|
|
837
|
-
### Common Issues
|
|
838
|
-
|
|
839
|
-
#### Error: chart.ts not found
|
|
840
|
-
|
|
841
|
-
- Ensure you're running `tl synth` from the correct directory
|
|
842
|
-
- Verify the chart.ts file exists in the specified path
|
|
843
|
-
|
|
844
|
-
#### TypeScript compilation errors
|
|
845
|
-
|
|
846
|
-
- Check Node.js version (requires 20+)
|
|
847
|
-
- Run `pnpm install` to ensure dependencies are installed
|
|
848
|
-
- Verify TypeScript configuration in tsconfig.json
|
|
849
|
-
|
|
850
|
-
#### Helm template errors
|
|
85
|
+
## 🤝 Contributing
|
|
851
86
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
- Use `helm template --debug` for detailed error information
|
|
87
|
+
See our [Contributing Guide](https://github.com/KenkoGeek/timonel/wiki/Contributing) for development
|
|
88
|
+
setup and guidelines.
|
|
855
89
|
|
|
856
|
-
## License
|
|
90
|
+
## 📄 License
|
|
857
91
|
|
|
858
92
|
MIT
|
|
859
93
|
|
|
860
|
-
<!-- Badges
|
|
94
|
+
<!-- Badges -->
|
|
861
95
|
|
|
862
96
|
[license-badge]: https://img.shields.io/badge/License-MIT-yellow.svg
|
|
863
97
|
[license-url]: https://opensource.org/licenses/MIT
|