@intentius/chant-lexicon-k8s 0.0.18 → 0.0.24

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.
Files changed (54) hide show
  1. package/dist/integrity.json +9 -4
  2. package/dist/manifest.json +1 -1
  3. package/dist/skills/chant-k8s-aks.md +146 -0
  4. package/{src/skills/kubernetes-patterns.md → dist/skills/chant-k8s-deployment-strategies.md} +1 -1
  5. package/dist/skills/chant-k8s-eks.md +156 -0
  6. package/dist/skills/chant-k8s-gke.md +246 -0
  7. package/{src/skills/kubernetes-security.md → dist/skills/chant-k8s-security.md} +1 -1
  8. package/dist/skills/chant-k8s.md +66 -3
  9. package/package.json +20 -2
  10. package/src/composites/adot-collector.ts +34 -22
  11. package/src/composites/agic-ingress.ts +14 -6
  12. package/src/composites/aks-external-dns-agent.ts +29 -18
  13. package/src/composites/alb-ingress.ts +14 -6
  14. package/src/composites/autoscaled-service.ts +25 -20
  15. package/src/composites/azure-disk-storage-class.ts +14 -6
  16. package/src/composites/azure-file-storage-class.ts +14 -6
  17. package/src/composites/azure-monitor-collector.ts +34 -22
  18. package/src/composites/batch-job.ts +25 -17
  19. package/src/composites/cockroachdb-cluster.ts +148 -58
  20. package/src/composites/composites.test.ts +369 -363
  21. package/src/composites/config-connector-context.ts +15 -8
  22. package/src/composites/configured-app.ts +21 -15
  23. package/src/composites/cron-workload.ts +25 -20
  24. package/src/composites/ebs-storage-class.ts +14 -6
  25. package/src/composites/efs-storage-class.ts +14 -6
  26. package/src/composites/external-dns-agent.ts +26 -20
  27. package/src/composites/filestore-storage-class.ts +14 -6
  28. package/src/composites/fluent-bit-agent.ts +30 -24
  29. package/src/composites/gce-ingress.ts +14 -6
  30. package/src/composites/gce-pd-storage-class.ts +14 -6
  31. package/src/composites/gke-external-dns-agent.ts +34 -21
  32. package/src/composites/gke-fluent-bit-agent.ts +34 -22
  33. package/src/composites/gke-gateway.ts +19 -12
  34. package/src/composites/gke-otel-collector.ts +34 -22
  35. package/src/composites/irsa-service-account.ts +22 -14
  36. package/src/composites/metrics-server.ts +41 -26
  37. package/src/composites/monitored-service.ts +26 -19
  38. package/src/composites/namespace-env.ts +26 -17
  39. package/src/composites/network-isolated-app.ts +21 -16
  40. package/src/composites/node-agent.ts +33 -22
  41. package/src/composites/secure-ingress.ts +19 -11
  42. package/src/composites/sidecar-app.ts +17 -12
  43. package/src/composites/stateful-app.ts +21 -12
  44. package/src/composites/web-app.ts +25 -21
  45. package/src/composites/worker-pool.ts +40 -26
  46. package/src/composites/workload-identity-sa.ts +22 -14
  47. package/src/composites/workload-identity-service-account.ts +22 -16
  48. package/src/plugin.ts +130 -614
  49. package/src/serializer.ts +3 -0
  50. package/src/skills/chant-k8s-deployment-strategies.md +183 -0
  51. package/src/skills/chant-k8s-gke.md +55 -0
  52. package/src/skills/chant-k8s-patterns.md +245 -0
  53. package/src/skills/chant-k8s-security.md +237 -0
  54. package/src/skills/chant-k8s.md +305 -0
@@ -5,6 +5,8 @@
5
5
  * security scanners) that need cluster-wide RBAC and tolerations.
6
6
  */
7
7
 
8
+ import { Composite, mergeDefaults } from "@intentius/chant";
9
+ import { DaemonSet, ServiceAccount, ClusterRole, ClusterRoleBinding, ConfigMap } from "../generated";
8
10
  import type { ContainerSecurityContext } from "./security-context";
9
11
 
10
12
  export interface NodeAgentProps {
@@ -47,14 +49,22 @@ export interface NodeAgentProps {
47
49
  env?: Array<{ name: string; value: string }>;
48
50
  /** Container security context (supports PSS restricted fields). */
49
51
  securityContext?: ContainerSecurityContext;
52
+ /** Per-member defaults for fine-grained overrides. */
53
+ defaults?: {
54
+ daemonSet?: Partial<Record<string, unknown>>;
55
+ serviceAccount?: Partial<Record<string, unknown>>;
56
+ clusterRole?: Partial<Record<string, unknown>>;
57
+ clusterRoleBinding?: Partial<Record<string, unknown>>;
58
+ configMap?: Partial<Record<string, unknown>>;
59
+ };
50
60
  }
51
61
 
52
62
  export interface NodeAgentResult {
53
- daemonSet: Record<string, unknown>;
54
- serviceAccount: Record<string, unknown>;
55
- clusterRole: Record<string, unknown>;
56
- clusterRoleBinding: Record<string, unknown>;
57
- configMap?: Record<string, unknown>;
63
+ daemonSet: InstanceType<typeof DaemonSet>;
64
+ serviceAccount: InstanceType<typeof ServiceAccount>;
65
+ clusterRole: InstanceType<typeof ClusterRole>;
66
+ clusterRoleBinding: InstanceType<typeof ClusterRoleBinding>;
67
+ configMap?: InstanceType<typeof ConfigMap>;
58
68
  }
59
69
 
60
70
  /**
@@ -75,7 +85,7 @@ export interface NodeAgentResult {
75
85
  * });
76
86
  * ```
77
87
  */
78
- export function NodeAgent(props: NodeAgentProps): NodeAgentResult {
88
+ export const NodeAgent = Composite<NodeAgentProps>((props) => {
79
89
  const {
80
90
  name,
81
91
  image,
@@ -92,6 +102,7 @@ export function NodeAgent(props: NodeAgentProps): NodeAgentResult {
92
102
  labels: extraLabels = {},
93
103
  env,
94
104
  securityContext,
105
+ defaults: defs,
95
106
  } = props;
96
107
 
97
108
  const saName = `${name}-sa`;
@@ -156,7 +167,7 @@ export function NodeAgent(props: NodeAgentProps): NodeAgentResult {
156
167
  }),
157
168
  };
158
169
 
159
- const daemonSetProps: Record<string, unknown> = {
170
+ const daemonSet = new DaemonSet(mergeDefaults({
160
171
  metadata: {
161
172
  name,
162
173
  ...(namespace && { namespace }),
@@ -169,27 +180,27 @@ export function NodeAgent(props: NodeAgentProps): NodeAgentResult {
169
180
  spec: podSpec,
170
181
  },
171
182
  },
172
- };
183
+ }, defs?.daemonSet));
173
184
 
174
- const serviceAccountProps: Record<string, unknown> = {
185
+ const serviceAccount = new ServiceAccount(mergeDefaults({
175
186
  metadata: {
176
187
  name: saName,
177
188
  ...(namespace && { namespace }),
178
189
  labels: { ...commonLabels, "app.kubernetes.io/component": "agent" },
179
190
  },
180
- };
191
+ }, defs?.serviceAccount));
181
192
 
182
193
  // ClusterRole — cluster-scoped, no namespace
183
- const clusterRoleProps: Record<string, unknown> = {
194
+ const clusterRole = new ClusterRole(mergeDefaults({
184
195
  metadata: {
185
196
  name: clusterRoleName,
186
197
  labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
187
198
  },
188
199
  rules: rbacRules,
189
- };
200
+ }, defs?.clusterRole));
190
201
 
191
202
  // ClusterRoleBinding — cluster-scoped, no namespace
192
- const clusterRoleBindingProps: Record<string, unknown> = {
203
+ const clusterRoleBinding = new ClusterRoleBinding(mergeDefaults({
193
204
  metadata: {
194
205
  name: bindingName,
195
206
  labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
@@ -206,25 +217,25 @@ export function NodeAgent(props: NodeAgentProps): NodeAgentResult {
206
217
  ...(namespace && { namespace }),
207
218
  },
208
219
  ],
209
- };
220
+ }, defs?.clusterRoleBinding));
210
221
 
211
- const result: NodeAgentResult = {
212
- daemonSet: daemonSetProps,
213
- serviceAccount: serviceAccountProps,
214
- clusterRole: clusterRoleProps,
215
- clusterRoleBinding: clusterRoleBindingProps,
222
+ const result: Record<string, any> = {
223
+ daemonSet,
224
+ serviceAccount,
225
+ clusterRole,
226
+ clusterRoleBinding,
216
227
  };
217
228
 
218
229
  if (config) {
219
- result.configMap = {
230
+ result.configMap = new ConfigMap(mergeDefaults({
220
231
  metadata: {
221
232
  name: configMapName,
222
233
  ...(namespace && { namespace }),
223
234
  labels: { ...commonLabels, "app.kubernetes.io/component": "config" },
224
235
  },
225
236
  data: config,
226
- };
237
+ }, defs?.configMap));
227
238
  }
228
239
 
229
240
  return result;
230
- }
241
+ }, "NodeAgent");
@@ -5,6 +5,9 @@
5
5
  * multiple hosts and paths (unlike the single-path Ingress in WebApp).
6
6
  */
7
7
 
8
+ import { Composite, mergeDefaults } from "@intentius/chant";
9
+ import { Ingress } from "../generated";
10
+
8
11
  export interface SecureIngressHost {
9
12
  /** Hostname (e.g., "api.example.com"). */
10
13
  hostname: string;
@@ -32,11 +35,16 @@ export interface SecureIngressProps {
32
35
  labels?: Record<string, string>;
33
36
  /** Namespace for all resources. */
34
37
  namespace?: string;
38
+ /** Per-member defaults for fine-grained overrides. */
39
+ defaults?: {
40
+ ingress?: Partial<Record<string, unknown>>;
41
+ certificate?: Partial<Record<string, unknown>>;
42
+ };
35
43
  }
36
44
 
37
45
  export interface SecureIngressResult {
38
- ingress: Record<string, unknown>;
39
- certificate?: Record<string, unknown>;
46
+ ingress: InstanceType<typeof Ingress>;
47
+ certificate?: InstanceType<typeof Ingress>; // CRD — use Ingress as proxy Declarable
40
48
  }
41
49
 
42
50
  /**
@@ -60,7 +68,7 @@ export interface SecureIngressResult {
60
68
  * });
61
69
  * ```
62
70
  */
63
- export function SecureIngress(props: SecureIngressProps): SecureIngressResult {
71
+ export const SecureIngress = Composite<SecureIngressProps>((props) => {
64
72
  const {
65
73
  name,
66
74
  hosts,
@@ -69,6 +77,7 @@ export function SecureIngress(props: SecureIngressProps): SecureIngressResult {
69
77
  annotations: extraAnnotations = {},
70
78
  labels: extraLabels = {},
71
79
  namespace,
80
+ defaults: defs,
72
81
  } = props;
73
82
 
74
83
  const commonLabels: Record<string, string> = {
@@ -102,7 +111,7 @@ export function SecureIngress(props: SecureIngressProps): SecureIngressResult {
102
111
 
103
112
  const hasAnnotations = Object.keys(ingressAnnotations).length > 0;
104
113
 
105
- const ingressProps: Record<string, unknown> = {
114
+ const ingress = new Ingress(mergeDefaults({
106
115
  metadata: {
107
116
  name,
108
117
  ...(namespace && { namespace }),
@@ -121,14 +130,13 @@ export function SecureIngress(props: SecureIngressProps): SecureIngressResult {
121
130
  ],
122
131
  }),
123
132
  },
124
- };
133
+ }, defs?.ingress));
125
134
 
126
- const result: SecureIngressResult = {
127
- ingress: ingressProps,
128
- };
135
+ const result: Record<string, any> = { ingress };
129
136
 
130
137
  if (clusterIssuer) {
131
- result.certificate = {
138
+ // Certificate is a CRD — use Ingress constructor as a generic Declarable wrapper
139
+ result.certificate = new Ingress(mergeDefaults({
132
140
  metadata: {
133
141
  name: secretName,
134
142
  ...(namespace && { namespace }),
@@ -142,8 +150,8 @@ export function SecureIngress(props: SecureIngressProps): SecureIngressResult {
142
150
  },
143
151
  dnsNames: allHostnames,
144
152
  },
145
- };
153
+ }, defs?.certificate));
146
154
  }
147
155
 
148
156
  return result;
149
- }
157
+ }, "SecureIngress");
@@ -5,6 +5,8 @@
5
5
  * DB migration init). Supports shared volumes between containers.
6
6
  */
7
7
 
8
+ import { Composite, mergeDefaults } from "@intentius/chant";
9
+ import { Deployment, Service } from "../generated";
8
10
  import type { ContainerSecurityContext } from "./security-context";
9
11
 
10
12
  export interface SidecarContainer {
@@ -74,11 +76,16 @@ export interface SidecarAppProps {
74
76
  env?: Array<{ name: string; value: string }>;
75
77
  /** Container security context for the primary container (supports PSS restricted fields). */
76
78
  securityContext?: ContainerSecurityContext;
79
+ /** Per-member defaults for fine-grained overrides. */
80
+ defaults?: {
81
+ deployment?: Partial<Record<string, unknown>>;
82
+ service?: Partial<Record<string, unknown>>;
83
+ };
77
84
  }
78
85
 
79
86
  export interface SidecarAppResult {
80
- deployment: Record<string, unknown>;
81
- service: Record<string, unknown>;
87
+ deployment: InstanceType<typeof Deployment>;
88
+ service: InstanceType<typeof Service>;
82
89
  }
83
90
 
84
91
  /**
@@ -103,7 +110,7 @@ export interface SidecarAppResult {
103
110
  * });
104
111
  * ```
105
112
  */
106
- export function SidecarApp(props: SidecarAppProps): SidecarAppResult {
113
+ export const SidecarApp = Composite<SidecarAppProps>((props) => {
107
114
  const {
108
115
  name,
109
116
  image,
@@ -120,6 +127,7 @@ export function SidecarApp(props: SidecarAppProps): SidecarAppResult {
120
127
  namespace,
121
128
  env,
122
129
  securityContext,
130
+ defaults: defs,
123
131
  } = props;
124
132
 
125
133
  const commonLabels: Record<string, string> = {
@@ -171,7 +179,7 @@ export function SidecarApp(props: SidecarAppProps): SidecarAppResult {
171
179
  ...(volumes && volumes.length > 0 && { volumes }),
172
180
  };
173
181
 
174
- const deploymentProps: Record<string, unknown> = {
182
+ const deployment = new Deployment(mergeDefaults({
175
183
  metadata: {
176
184
  name,
177
185
  ...(namespace && { namespace }),
@@ -185,9 +193,9 @@ export function SidecarApp(props: SidecarAppProps): SidecarAppResult {
185
193
  spec: podSpec,
186
194
  },
187
195
  },
188
- };
196
+ }, defs?.deployment));
189
197
 
190
- const serviceProps: Record<string, unknown> = {
198
+ const service = new Service(mergeDefaults({
191
199
  metadata: {
192
200
  name,
193
201
  ...(namespace && { namespace }),
@@ -198,10 +206,7 @@ export function SidecarApp(props: SidecarAppProps): SidecarAppResult {
198
206
  ports: [{ port: 80, targetPort: port, protocol: "TCP", name: "http" }],
199
207
  type: "ClusterIP",
200
208
  },
201
- };
209
+ }, defs?.service));
202
210
 
203
- return {
204
- deployment: deploymentProps,
205
- service: serviceProps,
206
- };
207
- }
211
+ return { deployment, service };
212
+ }, "SidecarApp");
@@ -5,6 +5,8 @@
5
5
  * like databases, caches, and message queues.
6
6
  */
7
7
 
8
+ import { Composite, mergeDefaults } from "@intentius/chant";
9
+ import { StatefulSet, Service, PodDisruptionBudget } from "../generated";
8
10
  import type { ContainerSecurityContext } from "./security-context";
9
11
 
10
12
  export interface StatefulAppProps {
@@ -47,12 +49,18 @@ export interface StatefulAppProps {
47
49
  namespace?: string;
48
50
  /** Environment variables for the container. */
49
51
  env?: Array<{ name: string; value: string }>;
52
+ /** Per-member defaults for fine-grained overrides. */
53
+ defaults?: {
54
+ statefulSet?: Partial<Record<string, unknown>>;
55
+ service?: Partial<Record<string, unknown>>;
56
+ pdb?: Partial<Record<string, unknown>>;
57
+ };
50
58
  }
51
59
 
52
60
  export interface StatefulAppResult {
53
- statefulSet: Record<string, unknown>;
54
- service: Record<string, unknown>;
55
- pdb?: Record<string, unknown>;
61
+ statefulSet: InstanceType<typeof StatefulSet>;
62
+ service: InstanceType<typeof Service>;
63
+ pdb?: InstanceType<typeof PodDisruptionBudget>;
56
64
  }
57
65
 
58
66
  /**
@@ -72,7 +80,7 @@ export interface StatefulAppResult {
72
80
  * });
73
81
  * ```
74
82
  */
75
- export function StatefulApp(props: StatefulAppProps): StatefulAppResult {
83
+ export const StatefulApp = Composite<StatefulAppProps>((props) => {
76
84
  const {
77
85
  name,
78
86
  image,
@@ -91,6 +99,7 @@ export function StatefulApp(props: StatefulAppProps): StatefulAppResult {
91
99
  memoryLimit = "1Gi",
92
100
  namespace,
93
101
  env,
102
+ defaults: defs,
94
103
  } = props;
95
104
 
96
105
  const commonLabels: Record<string, string> = {
@@ -125,7 +134,7 @@ export function StatefulApp(props: StatefulAppProps): StatefulAppResult {
125
134
  ...(priorityClassName && { priorityClassName }),
126
135
  };
127
136
 
128
- const statefulSetProps: Record<string, unknown> = {
137
+ const statefulSet = new StatefulSet(mergeDefaults({
129
138
  metadata: {
130
139
  name,
131
140
  ...(namespace && { namespace }),
@@ -150,10 +159,10 @@ export function StatefulApp(props: StatefulAppProps): StatefulAppResult {
150
159
  },
151
160
  ],
152
161
  },
153
- };
162
+ }, defs?.statefulSet));
154
163
 
155
164
  // Headless service (clusterIP: None) for StatefulSet DNS
156
- const serviceProps: Record<string, unknown> = {
165
+ const service = new Service(mergeDefaults({
157
166
  metadata: {
158
167
  name,
159
168
  ...(namespace && { namespace }),
@@ -164,12 +173,12 @@ export function StatefulApp(props: StatefulAppProps): StatefulAppResult {
164
173
  ports: [{ port, targetPort: port, protocol: "TCP", name: "app" }],
165
174
  clusterIP: "None",
166
175
  },
167
- };
176
+ }, defs?.service));
168
177
 
169
- const result: StatefulAppResult = { statefulSet: statefulSetProps, service: serviceProps };
178
+ const result: Record<string, any> = { statefulSet, service };
170
179
 
171
180
  if (minAvailable !== undefined) {
172
- result.pdb = {
181
+ result.pdb = new PodDisruptionBudget(mergeDefaults({
173
182
  metadata: {
174
183
  name,
175
184
  ...(namespace && { namespace }),
@@ -179,8 +188,8 @@ export function StatefulApp(props: StatefulAppProps): StatefulAppResult {
179
188
  minAvailable,
180
189
  selector: { matchLabels: { "app.kubernetes.io/name": name } },
181
190
  },
182
- };
191
+ }, defs?.pdb));
183
192
  }
184
193
 
185
194
  return result;
186
- }
195
+ }, "StatefulApp");
@@ -5,6 +5,8 @@
5
5
  * with common defaults (health probes, resource limits, labels).
6
6
  */
7
7
 
8
+ import { Composite, mergeDefaults } from "@intentius/chant";
9
+ import { Deployment, Service, Ingress, PodDisruptionBudget } from "../generated";
8
10
  import type { ContainerSecurityContext } from "./security-context";
9
11
 
10
12
  export interface WebAppProps {
@@ -56,13 +58,20 @@ export interface WebAppProps {
56
58
  namespace?: string;
57
59
  /** Environment variables for the container. */
58
60
  env?: Array<{ name: string; value: string }>;
61
+ /** Per-member defaults for fine-grained overrides. */
62
+ defaults?: {
63
+ deployment?: Partial<Record<string, unknown>>;
64
+ service?: Partial<Record<string, unknown>>;
65
+ ingress?: Partial<Record<string, unknown>>;
66
+ pdb?: Partial<Record<string, unknown>>;
67
+ };
59
68
  }
60
69
 
61
70
  export interface WebAppResult {
62
- deployment: Record<string, unknown>;
63
- service: Record<string, unknown>;
64
- ingress?: Record<string, unknown>;
65
- pdb?: Record<string, unknown>;
71
+ deployment: InstanceType<typeof Deployment>;
72
+ service: InstanceType<typeof Service>;
73
+ ingress?: InstanceType<typeof Ingress>;
74
+ pdb?: InstanceType<typeof PodDisruptionBudget>;
66
75
  }
67
76
 
68
77
  /**
@@ -84,7 +93,7 @@ export interface WebAppResult {
84
93
  * export { deployment, service, ingress };
85
94
  * ```
86
95
  */
87
- export function WebApp(props: WebAppProps): WebAppResult {
96
+ export const WebApp = Composite<WebAppProps>((props) => {
88
97
  const {
89
98
  name,
90
99
  image,
@@ -102,6 +111,7 @@ export function WebApp(props: WebAppProps): WebAppResult {
102
111
  terminationGracePeriodSeconds,
103
112
  priorityClassName,
104
113
  minAvailable,
114
+ defaults: defs,
105
115
  } = props;
106
116
 
107
117
  const commonLabels: Record<string, string> = {
@@ -146,9 +156,7 @@ export function WebApp(props: WebAppProps): WebAppResult {
146
156
  ...(priorityClassName && { priorityClassName }),
147
157
  };
148
158
 
149
- // We return plain objects that users pass to constructors.
150
- // The actual resource instantiation happens in user code with the generated classes.
151
- const deploymentProps: Record<string, unknown> = {
159
+ const deployment = new Deployment(mergeDefaults({
152
160
  metadata: {
153
161
  name,
154
162
  ...(namespace && { namespace }),
@@ -162,9 +170,9 @@ export function WebApp(props: WebAppProps): WebAppResult {
162
170
  spec: podSpec,
163
171
  },
164
172
  },
165
- };
173
+ }, defs?.deployment));
166
174
 
167
- const serviceProps: Record<string, unknown> = {
175
+ const service = new Service(mergeDefaults({
168
176
  metadata: {
169
177
  name,
170
178
  ...(namespace && { namespace }),
@@ -175,12 +183,9 @@ export function WebApp(props: WebAppProps): WebAppResult {
175
183
  ports: [{ port: 80, targetPort: port, protocol: "TCP", name: "http" }],
176
184
  type: "ClusterIP",
177
185
  },
178
- };
186
+ }, defs?.service));
179
187
 
180
- const result: WebAppResult = {
181
- deployment: deploymentProps,
182
- service: serviceProps,
183
- };
188
+ const result: Record<string, any> = { deployment, service };
184
189
 
185
190
  if (props.ingressHost) {
186
191
  // Build paths — use ingressPaths if provided, otherwise default single "/"
@@ -205,7 +210,7 @@ export function WebApp(props: WebAppProps): WebAppResult {
205
210
  },
206
211
  ];
207
212
 
208
- const ingressProps: Record<string, unknown> = {
213
+ result.ingress = new Ingress(mergeDefaults({
209
214
  metadata: {
210
215
  name,
211
216
  ...(namespace && { namespace }),
@@ -227,12 +232,11 @@ export function WebApp(props: WebAppProps): WebAppResult {
227
232
  ],
228
233
  }),
229
234
  },
230
- };
231
- result.ingress = ingressProps;
235
+ }, defs?.ingress));
232
236
  }
233
237
 
234
238
  if (minAvailable !== undefined) {
235
- result.pdb = {
239
+ result.pdb = new PodDisruptionBudget(mergeDefaults({
236
240
  metadata: {
237
241
  name,
238
242
  ...(namespace && { namespace }),
@@ -242,8 +246,8 @@ export function WebApp(props: WebAppProps): WebAppResult {
242
246
  minAvailable,
243
247
  selector: { matchLabels: { "app.kubernetes.io/name": name } },
244
248
  },
245
- };
249
+ }, defs?.pdb));
246
250
  }
247
251
 
248
252
  return result;
249
- }
253
+ }, "WebApp");
@@ -5,6 +5,11 @@
5
5
  * that need RBAC for secrets/configmaps and optional autoscaling, but no Service.
6
6
  */
7
7
 
8
+ import { Composite, mergeDefaults } from "@intentius/chant";
9
+ import {
10
+ Deployment, ServiceAccount, Role, RoleBinding,
11
+ ConfigMap, HorizontalPodAutoscaler, PodDisruptionBudget,
12
+ } from "../generated";
8
13
  import type { ContainerSecurityContext } from "./security-context";
9
14
 
10
15
  export interface WorkerPoolProps {
@@ -54,16 +59,26 @@ export interface WorkerPoolProps {
54
59
  namespace?: string;
55
60
  /** Environment variables for the container. */
56
61
  env?: Array<{ name: string; value: string }>;
62
+ /** Per-member defaults for fine-grained overrides. */
63
+ defaults?: {
64
+ deployment?: Partial<Record<string, unknown>>;
65
+ serviceAccount?: Partial<Record<string, unknown>>;
66
+ role?: Partial<Record<string, unknown>>;
67
+ roleBinding?: Partial<Record<string, unknown>>;
68
+ configMap?: Partial<Record<string, unknown>>;
69
+ hpa?: Partial<Record<string, unknown>>;
70
+ pdb?: Partial<Record<string, unknown>>;
71
+ };
57
72
  }
58
73
 
59
74
  export interface WorkerPoolResult {
60
- deployment: Record<string, unknown>;
61
- serviceAccount?: Record<string, unknown>;
62
- role?: Record<string, unknown>;
63
- roleBinding?: Record<string, unknown>;
64
- configMap?: Record<string, unknown>;
65
- hpa?: Record<string, unknown>;
66
- pdb?: Record<string, unknown>;
75
+ deployment: InstanceType<typeof Deployment>;
76
+ serviceAccount?: InstanceType<typeof ServiceAccount>;
77
+ role?: InstanceType<typeof Role>;
78
+ roleBinding?: InstanceType<typeof RoleBinding>;
79
+ configMap?: InstanceType<typeof ConfigMap>;
80
+ hpa?: InstanceType<typeof HorizontalPodAutoscaler>;
81
+ pdb?: InstanceType<typeof PodDisruptionBudget>;
67
82
  }
68
83
 
69
84
  /**
@@ -82,7 +97,7 @@ export interface WorkerPoolResult {
82
97
  * });
83
98
  * ```
84
99
  */
85
- export function WorkerPool(props: WorkerPoolProps): WorkerPoolResult {
100
+ export const WorkerPool = Composite<WorkerPoolProps>((props) => {
86
101
  const {
87
102
  name,
88
103
  image,
@@ -103,6 +118,7 @@ export function WorkerPool(props: WorkerPoolProps): WorkerPoolResult {
103
118
  labels: extraLabels = {},
104
119
  namespace,
105
120
  env,
121
+ defaults: defs,
106
122
  } = props;
107
123
 
108
124
  const saName = `${name}-sa`;
@@ -147,7 +163,7 @@ export function WorkerPool(props: WorkerPoolProps): WorkerPoolResult {
147
163
  ...(priorityClassName && { priorityClassName }),
148
164
  };
149
165
 
150
- const deploymentProps: Record<string, unknown> = {
166
+ const deployment = new Deployment(mergeDefaults({
151
167
  metadata: {
152
168
  name,
153
169
  ...(namespace && { namespace }),
@@ -161,31 +177,29 @@ export function WorkerPool(props: WorkerPoolProps): WorkerPoolResult {
161
177
  spec: podSpec,
162
178
  },
163
179
  },
164
- };
180
+ }, defs?.deployment));
165
181
 
166
- const result: WorkerPoolResult = {
167
- deployment: deploymentProps,
168
- };
182
+ const result: Record<string, any> = { deployment };
169
183
 
170
184
  if (createRbac) {
171
- result.serviceAccount = {
185
+ result.serviceAccount = new ServiceAccount(mergeDefaults({
172
186
  metadata: {
173
187
  name: saName,
174
188
  ...(namespace && { namespace }),
175
189
  labels: { ...commonLabels, "app.kubernetes.io/component": "worker" },
176
190
  },
177
- };
191
+ }, defs?.serviceAccount));
178
192
 
179
- result.role = {
193
+ result.role = new Role(mergeDefaults({
180
194
  metadata: {
181
195
  name: roleName,
182
196
  ...(namespace && { namespace }),
183
197
  labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
184
198
  },
185
199
  rules: effectiveRbacRules,
186
- };
200
+ }, defs?.role));
187
201
 
188
- result.roleBinding = {
202
+ result.roleBinding = new RoleBinding(mergeDefaults({
189
203
  metadata: {
190
204
  name: bindingName,
191
205
  ...(namespace && { namespace }),
@@ -203,22 +217,22 @@ export function WorkerPool(props: WorkerPoolProps): WorkerPoolResult {
203
217
  ...(namespace && { namespace }),
204
218
  },
205
219
  ],
206
- };
220
+ }, defs?.roleBinding));
207
221
  }
208
222
 
209
223
  if (config) {
210
- result.configMap = {
224
+ result.configMap = new ConfigMap(mergeDefaults({
211
225
  metadata: {
212
226
  name: configMapName,
213
227
  ...(namespace && { namespace }),
214
228
  labels: { ...commonLabels, "app.kubernetes.io/component": "config" },
215
229
  },
216
230
  data: config,
217
- };
231
+ }, defs?.configMap));
218
232
  }
219
233
 
220
234
  if (minAvailable !== undefined) {
221
- result.pdb = {
235
+ result.pdb = new PodDisruptionBudget(mergeDefaults({
222
236
  metadata: {
223
237
  name,
224
238
  ...(namespace && { namespace }),
@@ -228,12 +242,12 @@ export function WorkerPool(props: WorkerPoolProps): WorkerPoolResult {
228
242
  minAvailable,
229
243
  selector: { matchLabels: { "app.kubernetes.io/name": name } },
230
244
  },
231
- };
245
+ }, defs?.pdb));
232
246
  }
233
247
 
234
248
  if (autoscaling) {
235
249
  const targetCPUPercent = autoscaling.targetCPUPercent ?? 70;
236
- result.hpa = {
250
+ result.hpa = new HorizontalPodAutoscaler(mergeDefaults({
237
251
  metadata: {
238
252
  name,
239
253
  ...(namespace && { namespace }),
@@ -257,8 +271,8 @@ export function WorkerPool(props: WorkerPoolProps): WorkerPoolResult {
257
271
  },
258
272
  ],
259
273
  },
260
- };
274
+ }, defs?.hpa));
261
275
  }
262
276
 
263
277
  return result;
264
- }
278
+ }, "WorkerPool");