timonel 2.8.3 → 2.9.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.
@@ -0,0 +1,189 @@
1
+ import { Chart, ApiObject } from 'cdk8s';
2
+ import { Rutter } from '../rutter.js';
3
+ export class BasicChart extends Chart {
4
+ constructor(scope, id, props = {}) {
5
+ super(scope, id, props);
6
+ this.props = props;
7
+ this.initializeRutter();
8
+ this.generateKubernetesManifests();
9
+ }
10
+ initializeRutter() {
11
+ const { appName = 'my-app', image = 'nginx:latest', port = 80, replicas = 1, createNamespace = false, } = this.props;
12
+ const meta = {
13
+ name: appName,
14
+ version: '1.0.0',
15
+ description: `Helm chart for ${appName}`,
16
+ };
17
+ this.rutter = new Rutter({
18
+ meta,
19
+ scope: this,
20
+ defaultValues: {
21
+ appName,
22
+ image,
23
+ port,
24
+ replicas,
25
+ createNamespace,
26
+ },
27
+ });
28
+ if (createNamespace) {
29
+ this.rutter.addConditionalManifest({
30
+ apiVersion: 'v1',
31
+ kind: 'Namespace',
32
+ metadata: {
33
+ name: `{{ .Values.appName }}`,
34
+ },
35
+ }, 'createNamespace', 'namespace');
36
+ }
37
+ this.rutter.addManifest({
38
+ apiVersion: 'apps/v1',
39
+ kind: 'Deployment',
40
+ metadata: {
41
+ name: `{{ .Values.appName }}`,
42
+ labels: {
43
+ app: `{{ .Values.appName }}`,
44
+ },
45
+ },
46
+ spec: {
47
+ replicas: `{{ .Values.replicas }}`,
48
+ selector: {
49
+ matchLabels: {
50
+ app: `{{ .Values.appName }}`,
51
+ },
52
+ },
53
+ template: {
54
+ metadata: {
55
+ labels: {
56
+ app: `{{ .Values.appName }}`,
57
+ },
58
+ },
59
+ spec: {
60
+ containers: [
61
+ {
62
+ name: 'app',
63
+ image: `{{ .Values.image }}`,
64
+ ports: [
65
+ {
66
+ containerPort: `{{ .Values.port }}`,
67
+ },
68
+ ],
69
+ },
70
+ ],
71
+ },
72
+ },
73
+ },
74
+ }, 'deployment');
75
+ this.rutter.addManifest({
76
+ apiVersion: 'v1',
77
+ kind: 'Service',
78
+ metadata: {
79
+ name: `{{ .Values.appName }}`,
80
+ labels: {
81
+ app: `{{ .Values.appName }}`,
82
+ },
83
+ },
84
+ spec: {
85
+ ports: [
86
+ {
87
+ port: `{{ .Values.port }}`,
88
+ targetPort: `{{ .Values.port }}`,
89
+ },
90
+ ],
91
+ selector: {
92
+ app: `{{ .Values.appName }}`,
93
+ },
94
+ },
95
+ }, 'service');
96
+ }
97
+ writeHelmChart(outDir) {
98
+ this.rutter.write(outDir);
99
+ }
100
+ generateKubernetesManifests() {
101
+ const { appName = 'my-app', image = 'nginx:latest', port = 80, replicas = 1, createNamespace = false, } = this.props;
102
+ if (createNamespace) {
103
+ new ApiObject(this, 'namespace', {
104
+ apiVersion: 'v1',
105
+ kind: 'Namespace',
106
+ metadata: {
107
+ name: appName,
108
+ },
109
+ });
110
+ }
111
+ new ApiObject(this, 'deployment', {
112
+ apiVersion: 'apps/v1',
113
+ kind: 'Deployment',
114
+ metadata: {
115
+ name: appName,
116
+ labels: {
117
+ app: appName,
118
+ },
119
+ },
120
+ spec: {
121
+ replicas,
122
+ selector: {
123
+ matchLabels: {
124
+ app: appName,
125
+ },
126
+ },
127
+ template: {
128
+ metadata: {
129
+ labels: {
130
+ app: appName,
131
+ },
132
+ },
133
+ spec: {
134
+ containers: [
135
+ {
136
+ name: 'app',
137
+ image,
138
+ ports: [
139
+ {
140
+ containerPort: port,
141
+ },
142
+ ],
143
+ },
144
+ ],
145
+ },
146
+ },
147
+ },
148
+ });
149
+ new ApiObject(this, 'service', {
150
+ apiVersion: 'v1',
151
+ kind: 'Service',
152
+ metadata: {
153
+ name: appName,
154
+ labels: {
155
+ app: appName,
156
+ },
157
+ },
158
+ spec: {
159
+ ports: [
160
+ {
161
+ port,
162
+ targetPort: port,
163
+ },
164
+ ],
165
+ selector: {
166
+ app: appName,
167
+ },
168
+ },
169
+ });
170
+ }
171
+ }
172
+ export function generateBasicChart(appName = 'my-app') {
173
+ return `import { App } from 'cdk8s';
174
+ import { BasicChart } from 'timonel';
175
+
176
+ const app = new App();
177
+
178
+ const chart = new BasicChart(app, '${appName}', {
179
+ appName: '${appName}',
180
+ image: 'nginx:latest',
181
+ port: 80,
182
+ replicas: 1,
183
+ createNamespace: true // Set to true to create namespace, false to skip
184
+ });
185
+
186
+ // Generate complete Helm chart structure (Chart.yaml, values.yaml, templates/, _helpers.tpl)
187
+ chart.writeHelmChart('dist');
188
+ `;
189
+ }
@@ -0,0 +1,27 @@
1
+ import { Chart } from 'cdk8s';
2
+ import type { ChartProps } from 'cdk8s';
3
+ import type { Construct } from 'constructs';
4
+ import { Rutter } from '../rutter.js';
5
+ export interface SubchartProps extends ChartProps {
6
+ appName?: string;
7
+ image?: string;
8
+ port?: number;
9
+ replicas?: number;
10
+ serviceType?: string;
11
+ enableIngress?: boolean;
12
+ ingressHost?: string;
13
+ env?: Record<string, string>;
14
+ persistentVolume?: {
15
+ enabled: boolean;
16
+ size: string;
17
+ storageClass?: string;
18
+ };
19
+ }
20
+ export declare class Subchart extends Chart {
21
+ private rutter;
22
+ constructor(scope: Construct, id: string, props?: SubchartProps);
23
+ get rutterInstance(): Rutter;
24
+ writeHelmChart(outDir: string): void;
25
+ }
26
+ export declare function generateSubchart(props?: SubchartProps, outDir?: string): void;
27
+ export declare function generateSubchartTemplate(name: string): string;
@@ -0,0 +1,224 @@
1
+ import { App, Chart } from 'cdk8s';
2
+ import { Rutter } from '../rutter.js';
3
+ export class Subchart extends Chart {
4
+ constructor(scope, id, props = {}) {
5
+ super(scope, id, props);
6
+ const { appName = 'subchart-app', image = 'nginx:latest', port = 80, replicas = 1, serviceType = 'ClusterIP', enableIngress = false, ingressHost = 'example.com', env = {}, persistentVolume = { enabled: false, size: '1Gi' }, } = props;
7
+ const meta = {
8
+ name: appName,
9
+ version: '1.0.0',
10
+ description: `Helm chart for ${appName}`,
11
+ };
12
+ this.rutter = new Rutter({
13
+ meta,
14
+ scope: this,
15
+ defaultValues: {
16
+ appName,
17
+ image,
18
+ port,
19
+ replicas,
20
+ serviceType,
21
+ enableIngress,
22
+ ingressHost,
23
+ env,
24
+ persistentVolume,
25
+ ingress: {
26
+ annotations: {},
27
+ className: '',
28
+ hosts: [
29
+ {
30
+ host: ingressHost,
31
+ paths: [
32
+ {
33
+ path: '/',
34
+ pathType: 'Prefix',
35
+ },
36
+ ],
37
+ },
38
+ ],
39
+ tls: [],
40
+ },
41
+ },
42
+ });
43
+ this.rutter.addManifest({
44
+ apiVersion: 'v1',
45
+ kind: 'ConfigMap',
46
+ metadata: {
47
+ name: `{{ .Values.appName }}-config`,
48
+ labels: {
49
+ app: `{{ .Values.appName }}`,
50
+ component: 'config',
51
+ },
52
+ },
53
+ data: {
54
+ 'app.name': `{{ .Values.appName }}`,
55
+ 'app.port': `{{ .Values.port }}`,
56
+ 'app.environment': 'production',
57
+ },
58
+ }, 'configmap');
59
+ this.rutter.addManifest({
60
+ apiVersion: 'v1',
61
+ kind: 'Secret',
62
+ metadata: {
63
+ name: `{{ .Values.appName }}-secret`,
64
+ labels: {
65
+ app: `{{ .Values.appName }}`,
66
+ component: 'secret',
67
+ },
68
+ },
69
+ stringData: {
70
+ 'database.password': 'changeme',
71
+ 'api.key': 'your-api-key-here',
72
+ },
73
+ }, 'secret');
74
+ this.rutter.addManifest({
75
+ apiVersion: 'apps/v1',
76
+ kind: 'Deployment',
77
+ metadata: {
78
+ name: `{{ .Values.appName }}`,
79
+ labels: {
80
+ app: `{{ .Values.appName }}`,
81
+ },
82
+ },
83
+ spec: {
84
+ replicas: `{{ .Values.replicas }}`,
85
+ selector: {
86
+ matchLabels: {
87
+ app: `{{ .Values.appName }}`,
88
+ },
89
+ },
90
+ template: {
91
+ metadata: {
92
+ labels: {
93
+ app: `{{ .Values.appName }}`,
94
+ },
95
+ },
96
+ spec: {
97
+ containers: [
98
+ {
99
+ name: 'app',
100
+ image: `{{ .Values.image }}`,
101
+ ports: [
102
+ {
103
+ containerPort: `{{ .Values.port }}`,
104
+ },
105
+ ],
106
+ },
107
+ ],
108
+ },
109
+ },
110
+ },
111
+ }, 'deployment');
112
+ this.rutter.addManifest({
113
+ apiVersion: 'v1',
114
+ kind: 'Service',
115
+ metadata: {
116
+ name: `{{ .Values.appName }}`,
117
+ labels: {
118
+ app: `{{ .Values.appName }}`,
119
+ },
120
+ },
121
+ spec: {
122
+ type: `{{ .Values.serviceType }}`,
123
+ ports: [
124
+ {
125
+ port: `{{ .Values.port }}`,
126
+ targetPort: `{{ .Values.port }}`,
127
+ },
128
+ ],
129
+ selector: {
130
+ app: `{{ .Values.appName }}`,
131
+ },
132
+ },
133
+ }, 'service');
134
+ this.rutter.addTemplateManifest(`{{- if .Values.enableIngress }}
135
+ apiVersion: networking.k8s.io/v1
136
+ kind: Ingress
137
+ metadata:
138
+ name: {{ include "chart.fullname" . }}
139
+ labels:
140
+ {{- include "chart.labels" . | nindent 4 }}
141
+ {{- with .Values.ingress.annotations }}
142
+ annotations:
143
+ {{- toYaml . | nindent 4 }}
144
+ {{- end }}
145
+ spec:
146
+ {{- if and .Values.ingress.className (not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class")) }}
147
+ ingressClassName: {{ .Values.ingress.className }}
148
+ {{- end }}
149
+ {{- if .Values.ingress.tls }}
150
+ tls:
151
+ {{- range .Values.ingress.tls }}
152
+ - hosts:
153
+ {{- range .hosts }}
154
+ - {{ . | quote }}
155
+ {{- end }}
156
+ secretName: {{ .secretName }}
157
+ {{- end }}
158
+ {{- end }}
159
+ rules:
160
+ {{- range .Values.ingress.hosts }}
161
+ - host: {{ .host | quote }}
162
+ http:
163
+ paths:
164
+ {{- range .paths }}
165
+ - path: {{ .path }}
166
+ {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
167
+ pathType: {{ .pathType }}
168
+ {{- end }}
169
+ backend:
170
+ {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
171
+ service:
172
+ name: {{ include "chart.fullname" $ }}
173
+ port:
174
+ number: {{ $.Values.port | int }}
175
+ {{- else }}
176
+ serviceName: {{ include "chart.fullname" $ }}
177
+ servicePort: {{ $.Values.port | int }}
178
+ {{- end }}
179
+ {{- end }}
180
+ {{- end }}
181
+ {{- end }}`, 'ingress');
182
+ }
183
+ get rutterInstance() {
184
+ return this.rutter;
185
+ }
186
+ writeHelmChart(outDir) {
187
+ console.log('🔧 Subchart.writeHelmChart called with outDir:', outDir);
188
+ console.log('🔧 Rutter instance exists:', !!this.rutter);
189
+ this.rutter.write(outDir);
190
+ console.log('🔧 Subchart.writeHelmChart completed');
191
+ }
192
+ }
193
+ export function generateSubchart(props = {}, outDir = 'dist') {
194
+ const app = new App();
195
+ const subchart = new Subchart(app, 'subchart', props);
196
+ subchart.writeHelmChart(outDir);
197
+ }
198
+ export function generateSubchartTemplate(name) {
199
+ return `import { App } from 'cdk8s';
200
+ import { Subchart } from 'timonel';
201
+
202
+ export default function createChart() {
203
+ const app = new App({
204
+ outdir: 'dist'
205
+ });
206
+
207
+ return new Subchart(app, '${name}', {
208
+ appName: '${name}',
209
+ image: 'nginx:latest',
210
+ port: 80,
211
+ replicas: 1,
212
+ serviceType: 'ClusterIP',
213
+ enableIngress: false,
214
+ ingressHost: 'example.com'
215
+ });
216
+ }
217
+
218
+ // Auto-execute when run directly
219
+ if (import.meta.url === new URL(import.meta.url).href) {
220
+ const chart = createChart();
221
+ chart.node.root.synth();
222
+ }
223
+ `;
224
+ }
@@ -0,0 +1,13 @@
1
+ import type { App } from 'cdk8s';
2
+ import { Chart } from 'cdk8s';
3
+ import type { ChartProps } from '../types.js';
4
+ export declare function generateUmbrellaChart(name: string): string;
5
+ export declare class UmbrellaChartTemplate extends Chart {
6
+ private readonly config;
7
+ private _umbrellaRutter?;
8
+ constructor(scope: App, id: string, config: ChartProps);
9
+ private configure;
10
+ private _addService;
11
+ private _addSubchart;
12
+ writeHelmChart(outputDir: string): void;
13
+ }
@@ -0,0 +1,222 @@
1
+ import { writeFileSync, mkdirSync, existsSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { Chart, ApiObject } from 'cdk8s';
4
+ import YAML from 'yaml';
5
+ export function generateUmbrellaChart(name) {
6
+ return `import { App } from 'cdk8s';
7
+ import { UmbrellaChart } from 'timonel';
8
+
9
+ export function synth(outDir: string) {
10
+ const app = new App({
11
+ outdir: outDir,
12
+ outputFileExtension: '.yaml',
13
+ yamlOutputType: 'FILE_PER_RESOURCE'
14
+ });
15
+
16
+ const chart = new UmbrellaChart(app, '${name}', {
17
+ name: '${name}',
18
+ version: '0.1.0',
19
+ description: '${name} umbrella chart',
20
+ services: [],
21
+ subcharts: []
22
+ });
23
+
24
+ // Generate Helm chart files
25
+ chart.writeHelmChart(outDir);
26
+
27
+ // Also generate CDK8s YAML files
28
+ app.synth();
29
+ }
30
+
31
+ // Auto-execute when run directly
32
+ if (import.meta.url === new URL(import.meta.url).href) {
33
+ synth(process.argv[2] || 'dist');
34
+ }
35
+
36
+ export default synth;`;
37
+ }
38
+ export class UmbrellaChartTemplate extends Chart {
39
+ constructor(scope, id, config) {
40
+ super(scope, id);
41
+ this.config = config;
42
+ this.configure();
43
+ }
44
+ configure() {
45
+ new ApiObject(this, 'umbrella-info', {
46
+ apiVersion: 'v1',
47
+ kind: 'ConfigMap',
48
+ metadata: {
49
+ name: `${this.config.name}-info`,
50
+ labels: {
51
+ 'app.kubernetes.io/name': this.config.name,
52
+ 'app.kubernetes.io/component': 'umbrella-chart',
53
+ 'app.kubernetes.io/version': this.config.version,
54
+ },
55
+ },
56
+ data: {
57
+ 'chart.name': this.config.name,
58
+ 'chart.version': this.config.version,
59
+ 'chart.description': this.config.description || '',
60
+ 'subcharts.count': String(this.config.subcharts?.length || 0),
61
+ },
62
+ });
63
+ this.config.services?.forEach((svc, index) => {
64
+ this._addService(svc, index);
65
+ });
66
+ this.config.subcharts?.forEach((subchart, index) => {
67
+ this._addSubchart(subchart, index);
68
+ });
69
+ }
70
+ _addService(svc, index) {
71
+ new ApiObject(this, `service-${index}`, {
72
+ apiVersion: 'v1',
73
+ kind: 'Service',
74
+ metadata: {
75
+ name: svc.name,
76
+ labels: {
77
+ 'app.kubernetes.io/name': this.config.name,
78
+ 'app.kubernetes.io/component': 'service',
79
+ },
80
+ },
81
+ spec: {
82
+ ports: [
83
+ {
84
+ port: svc.port,
85
+ targetPort: svc.targetPort,
86
+ },
87
+ ],
88
+ selector: {
89
+ 'app.kubernetes.io/name': svc.name,
90
+ },
91
+ },
92
+ });
93
+ }
94
+ _addSubchart(_subchart, _index) {
95
+ }
96
+ writeHelmChart(outputDir) {
97
+ if (!existsSync(outputDir)) {
98
+ mkdirSync(outputDir, { recursive: true });
99
+ }
100
+ const chartYaml = {
101
+ apiVersion: 'v2',
102
+ name: this.config.name,
103
+ description: this.config.description || 'A Helm umbrella chart',
104
+ type: 'application',
105
+ version: this.config.version,
106
+ appVersion: this.config.version,
107
+ dependencies: this.config.subcharts?.map((subchart) => ({
108
+ name: subchart.name,
109
+ version: '1.0.0',
110
+ repository: 'file://./charts/' + subchart.name,
111
+ })) || [],
112
+ };
113
+ writeFileSync(join(outputDir, 'Chart.yaml'), YAML.stringify(chartYaml));
114
+ const valuesYaml = {
115
+ global: {
116
+ namespace: this.config.name,
117
+ },
118
+ ...Object.fromEntries(this.config.subcharts?.map((subchart) => [
119
+ subchart.name,
120
+ {
121
+ enabled: true,
122
+ },
123
+ ]) || []),
124
+ };
125
+ writeFileSync(join(outputDir, 'values.yaml'), YAML.stringify(valuesYaml));
126
+ const chartsDir = join(outputDir, 'charts');
127
+ if (!existsSync(chartsDir)) {
128
+ mkdirSync(chartsDir, { recursive: true });
129
+ }
130
+ this.config.subcharts?.forEach((subchart) => {
131
+ const subchartDir = join(chartsDir, subchart.name);
132
+ if (!existsSync(subchartDir)) {
133
+ mkdirSync(subchartDir, { recursive: true });
134
+ }
135
+ let subchartInstance = null;
136
+ if (typeof subchart.chart === 'function') {
137
+ try {
138
+ subchartInstance = subchart.chart();
139
+ }
140
+ catch (error) {
141
+ console.warn(`Failed to create subchart ${subchart.name}:`, error);
142
+ return;
143
+ }
144
+ }
145
+ else {
146
+ subchartInstance = subchart.chart;
147
+ }
148
+ if (subchartInstance && typeof subchartInstance.writeHelmChart === 'function') {
149
+ subchartInstance.writeHelmChart(subchartDir);
150
+ }
151
+ });
152
+ const templatesDir = join(outputDir, 'templates');
153
+ if (!existsSync(templatesDir)) {
154
+ mkdirSync(templatesDir, { recursive: true });
155
+ }
156
+ const namespaceYaml = {
157
+ apiVersion: 'v1',
158
+ kind: 'Namespace',
159
+ metadata: {
160
+ name: '{{ .Values.global.namespace | default .Release.Name }}',
161
+ labels: {
162
+ 'app.kubernetes.io/name': '{{ include "chart.name" . }}',
163
+ 'app.kubernetes.io/instance': '{{ .Release.Name }}',
164
+ 'app.kubernetes.io/version': '{{ .Chart.AppVersion }}',
165
+ 'app.kubernetes.io/managed-by': '{{ .Release.Service }}',
166
+ },
167
+ },
168
+ };
169
+ writeFileSync(join(templatesDir, 'namespace.yaml'), YAML.stringify(namespaceYaml));
170
+ const helpersTpl = `{{/*
171
+ Expand the name of the chart.
172
+ */}}
173
+ {{- define "chart.name" -}}
174
+ {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
175
+ {{- end }}
176
+
177
+ {{/*
178
+ Create a default fully qualified app name.
179
+ */}}
180
+ {{- define "chart.fullname" -}}
181
+ {{- if .Values.fullnameOverride }}
182
+ {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
183
+ {{- else }}
184
+ {{- $name := default .Chart.Name .Values.nameOverride }}
185
+ {{- if contains $name .Release.Name }}
186
+ {{- .Release.Name | trunc 63 | trimSuffix "-" }}
187
+ {{- else }}
188
+ {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
189
+ {{- end }}
190
+ {{- end }}
191
+ {{- end }}
192
+
193
+ {{/*
194
+ Create chart name and version as used by the chart label.
195
+ */}}
196
+ {{- define "chart.chart" -}}
197
+ {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
198
+ {{- end }}
199
+
200
+ {{/*
201
+ Common labels
202
+ */}}
203
+ {{- define "chart.labels" -}}
204
+ helm.sh/chart: {{ include "chart.chart" . }}
205
+ {{ include "chart.selectorLabels" . }}
206
+ {{- if .Chart.AppVersion }}
207
+ app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
208
+ {{- end }}
209
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
210
+ {{- end }}
211
+
212
+ {{/*
213
+ Selector labels
214
+ */}}
215
+ {{- define "chart.selectorLabels" -}}
216
+ app.kubernetes.io/name: {{ include "chart.name" . }}
217
+ app.kubernetes.io/instance: {{ .Release.Name }}
218
+ {{- end }}
219
+ `;
220
+ writeFileSync(join(templatesDir, '_helpers.tpl'), helpersTpl);
221
+ }
222
+ }
@@ -0,0 +1,20 @@
1
+ import type { Chart } from 'cdk8s';
2
+ export interface ChartProps {
3
+ name: string;
4
+ version: string;
5
+ description: string;
6
+ services?: Array<{
7
+ name: string;
8
+ port: number;
9
+ targetPort: number;
10
+ }>;
11
+ subcharts?: Array<{
12
+ name: string;
13
+ chart: Chart | (() => Chart);
14
+ }>;
15
+ }
16
+ export interface SubchartProps {
17
+ name: string;
18
+ version: string;
19
+ path: string;
20
+ }
@@ -0,0 +1 @@
1
+ export {};