@intentius/chant-lexicon-k8s 0.0.13 → 0.0.15

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 +20 -15
  2. package/dist/manifest.json +1 -1
  3. package/dist/rules/wk8204.ts +33 -1
  4. package/dist/rules/wk8304.ts +70 -0
  5. package/dist/rules/wk8305.ts +115 -0
  6. package/dist/rules/wk8306.ts +50 -0
  7. package/dist/skills/chant-k8s-eks.md +156 -0
  8. package/dist/skills/chant-k8s-patterns.md +245 -0
  9. package/dist/skills/chant-k8s.md +36 -227
  10. package/package.json +27 -24
  11. package/src/codegen/docs.ts +5 -5
  12. package/src/composites/adot-collector.ts +245 -0
  13. package/src/composites/agic-ingress.ts +149 -0
  14. package/src/composites/alb-ingress.ts +152 -0
  15. package/src/composites/autoscaled-service.ts +51 -0
  16. package/src/composites/azure-disk-storage-class.ts +82 -0
  17. package/src/composites/azure-file-storage-class.ts +77 -0
  18. package/src/composites/azure-monitor-collector.ts +232 -0
  19. package/src/composites/batch-job.ts +221 -0
  20. package/src/composites/composites.test.ts +1584 -0
  21. package/src/composites/config-connector-context.ts +62 -0
  22. package/src/composites/configured-app.ts +224 -0
  23. package/src/composites/cron-workload.ts +6 -0
  24. package/src/composites/ebs-storage-class.ts +96 -0
  25. package/src/composites/efs-storage-class.ts +77 -0
  26. package/src/composites/external-dns-agent.ts +174 -0
  27. package/src/composites/filestore-storage-class.ts +79 -0
  28. package/src/composites/fluent-bit-agent.ts +220 -0
  29. package/src/composites/gce-pd-storage-class.ts +85 -0
  30. package/src/composites/gke-gateway.ts +143 -0
  31. package/src/composites/index.ts +47 -0
  32. package/src/composites/irsa-service-account.ts +114 -0
  33. package/src/composites/metrics-server.ts +224 -0
  34. package/src/composites/monitored-service.ts +221 -0
  35. package/src/composites/network-isolated-app.ts +202 -0
  36. package/src/composites/node-agent.ts +6 -0
  37. package/src/composites/secure-ingress.ts +149 -0
  38. package/src/composites/security-context.ts +10 -0
  39. package/src/composites/sidecar-app.ts +207 -0
  40. package/src/composites/stateful-app.ts +67 -15
  41. package/src/composites/web-app.ts +104 -35
  42. package/src/composites/worker-pool.ts +38 -4
  43. package/src/composites/workload-identity-sa.ts +118 -0
  44. package/src/composites/workload-identity-service-account.ts +116 -0
  45. package/src/index.ts +24 -2
  46. package/src/lint/post-synth/post-synth.test.ts +362 -1
  47. package/src/lint/post-synth/wk8204.ts +33 -1
  48. package/src/lint/post-synth/wk8304.ts +70 -0
  49. package/src/lint/post-synth/wk8305.ts +115 -0
  50. package/src/lint/post-synth/wk8306.ts +50 -0
  51. package/src/plugin.test.ts +2 -2
  52. package/src/plugin.ts +556 -242
  53. package/src/serializer.test.ts +120 -0
  54. package/src/serializer.ts +16 -4
@@ -0,0 +1,245 @@
1
+ /**
2
+ * AdotCollector composite — DaemonSet + RBAC + ConfigMap for AWS Distro for OpenTelemetry.
3
+ *
4
+ * @eks ADOT collector for CloudWatch and X-Ray. NodeAgent specialization
5
+ * with pre-configured pipelines for AWS observability.
6
+ */
7
+
8
+ export interface AdotCollectorProps {
9
+ /** AWS region. */
10
+ region: string;
11
+ /** EKS cluster name. */
12
+ clusterName: string;
13
+ /** Exporters to enable (default: ["cloudwatch", "xray"]). */
14
+ exporters?: ("cloudwatch" | "xray" | "prometheus")[];
15
+ /** Agent name (default: "adot-collector"). */
16
+ name?: string;
17
+ /** ADOT image (default: "public.ecr.aws/aws-observability/aws-otel-collector:latest"). */
18
+ image?: string;
19
+ /** Namespace (default: "amazon-metrics"). */
20
+ namespace?: string;
21
+ /** Additional labels. */
22
+ labels?: Record<string, string>;
23
+ /** CPU request (default: "100m"). */
24
+ cpuRequest?: string;
25
+ /** Memory request (default: "256Mi"). */
26
+ memoryRequest?: string;
27
+ /** CPU limit (default: "500m"). */
28
+ cpuLimit?: string;
29
+ /** Memory limit (default: "512Mi"). */
30
+ memoryLimit?: string;
31
+ /** IAM Role ARN for IRSA (adds eks.amazonaws.com/role-arn annotation to ServiceAccount). */
32
+ iamRoleArn?: string;
33
+ }
34
+
35
+ export interface AdotCollectorResult {
36
+ daemonSet: Record<string, unknown>;
37
+ serviceAccount: Record<string, unknown>;
38
+ clusterRole: Record<string, unknown>;
39
+ clusterRoleBinding: Record<string, unknown>;
40
+ configMap: Record<string, unknown>;
41
+ }
42
+
43
+ /**
44
+ * Create an AdotCollector composite — returns prop objects for
45
+ * a DaemonSet, ServiceAccount, ClusterRole, ClusterRoleBinding, and ConfigMap.
46
+ *
47
+ * @eks
48
+ * @example
49
+ * ```ts
50
+ * import { AdotCollector } from "@intentius/chant-lexicon-k8s";
51
+ *
52
+ * const { daemonSet, serviceAccount, clusterRole, clusterRoleBinding, configMap } = AdotCollector({
53
+ * region: "us-east-1",
54
+ * clusterName: "my-cluster",
55
+ * exporters: ["cloudwatch", "xray"],
56
+ * });
57
+ * ```
58
+ */
59
+ export function AdotCollector(props: AdotCollectorProps): AdotCollectorResult {
60
+ const {
61
+ region,
62
+ clusterName,
63
+ exporters = ["cloudwatch", "xray"],
64
+ name = "adot-collector",
65
+ image = "public.ecr.aws/aws-observability/aws-otel-collector:latest",
66
+ namespace = "amazon-metrics",
67
+ labels: extraLabels = {},
68
+ cpuRequest = "100m",
69
+ memoryRequest = "256Mi",
70
+ cpuLimit = "500m",
71
+ memoryLimit = "512Mi",
72
+ iamRoleArn,
73
+ } = props;
74
+
75
+ const saName = `${name}-sa`;
76
+ const clusterRoleName = `${name}-role`;
77
+ const bindingName = `${name}-binding`;
78
+ const configMapName = `${name}-config`;
79
+
80
+ const commonLabels: Record<string, string> = {
81
+ "app.kubernetes.io/name": name,
82
+ "app.kubernetes.io/managed-by": "chant",
83
+ ...extraLabels,
84
+ };
85
+
86
+ // Build ADOT config YAML
87
+ const exporterConfigs: string[] = [];
88
+ const exporterNames: string[] = [];
89
+
90
+ if (exporters.includes("cloudwatch")) {
91
+ exporterConfigs.push(` awsemf:
92
+ region: ${region}
93
+ namespace: ContainerInsights
94
+ log_group_name: '/aws/containerinsights/${clusterName}/performance'
95
+ dimension_rollup_option: NoDimensionRollup`);
96
+ exporterNames.push("awsemf");
97
+ }
98
+
99
+ if (exporters.includes("xray")) {
100
+ exporterConfigs.push(` awsxray:
101
+ region: ${region}`);
102
+ exporterNames.push("awsxray");
103
+ }
104
+
105
+ if (exporters.includes("prometheus")) {
106
+ exporterConfigs.push(` prometheusremotewrite:
107
+ endpoint: http://prometheus:9090/api/v1/write`);
108
+ exporterNames.push("prometheusremotewrite");
109
+ }
110
+
111
+ const adotConfig = `receivers:
112
+ otlp:
113
+ protocols:
114
+ grpc:
115
+ endpoint: 0.0.0.0:4317
116
+ http:
117
+ endpoint: 0.0.0.0:4318
118
+
119
+ processors:
120
+ batch:
121
+ timeout: 30s
122
+ send_batch_size: 8192
123
+
124
+ exporters:
125
+ ${exporterConfigs.join("\n")}
126
+
127
+ service:
128
+ pipelines:
129
+ metrics:
130
+ receivers: [otlp]
131
+ processors: [batch]
132
+ exporters: [${exporterNames.filter((e) => e !== "awsxray").join(", ") || "awsemf"}]
133
+ traces:
134
+ receivers: [otlp]
135
+ processors: [batch]
136
+ exporters: [${exporterNames.filter((e) => e !== "awsemf").join(", ") || "awsxray"}]
137
+ `;
138
+
139
+ const container: Record<string, unknown> = {
140
+ name,
141
+ image,
142
+ args: ["--config=/etc/adot/config.yaml"],
143
+ ports: [
144
+ { containerPort: 4317, name: "otlp-grpc" },
145
+ { containerPort: 4318, name: "otlp-http" },
146
+ ],
147
+ resources: {
148
+ requests: { cpu: cpuRequest, memory: memoryRequest },
149
+ limits: { cpu: cpuLimit, memory: memoryLimit },
150
+ },
151
+ volumeMounts: [
152
+ { name: "config", mountPath: "/etc/adot", readOnly: true },
153
+ ],
154
+ securityContext: {
155
+ runAsNonRoot: true,
156
+ runAsUser: 10001,
157
+ readOnlyRootFilesystem: true,
158
+ allowPrivilegeEscalation: false,
159
+ },
160
+ };
161
+
162
+ const daemonSetProps: Record<string, unknown> = {
163
+ metadata: {
164
+ name,
165
+ namespace,
166
+ labels: { ...commonLabels, "app.kubernetes.io/component": "agent" },
167
+ },
168
+ spec: {
169
+ selector: { matchLabels: { "app.kubernetes.io/name": name } },
170
+ template: {
171
+ metadata: { labels: { "app.kubernetes.io/name": name, ...extraLabels } },
172
+ spec: {
173
+ serviceAccountName: saName,
174
+ containers: [container],
175
+ volumes: [
176
+ { name: "config", configMap: { name: configMapName } },
177
+ ],
178
+ tolerations: [{ operator: "Exists" }],
179
+ },
180
+ },
181
+ },
182
+ };
183
+
184
+ const serviceAccountProps: Record<string, unknown> = {
185
+ metadata: {
186
+ name: saName,
187
+ namespace,
188
+ labels: { ...commonLabels, "app.kubernetes.io/component": "agent" },
189
+ ...(iamRoleArn ? { annotations: { "eks.amazonaws.com/role-arn": iamRoleArn } } : {}),
190
+ },
191
+ };
192
+
193
+ const clusterRoleProps: Record<string, unknown> = {
194
+ metadata: {
195
+ name: clusterRoleName,
196
+ labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
197
+ },
198
+ rules: [
199
+ { apiGroups: [""], resources: ["pods", "nodes", "endpoints"], verbs: ["get", "list", "watch"] },
200
+ { apiGroups: ["apps"], resources: ["replicasets"], verbs: ["get", "list", "watch"] },
201
+ { apiGroups: ["batch"], resources: ["jobs"], verbs: ["get", "list", "watch"] },
202
+ { apiGroups: [""], resources: ["nodes/proxy"], verbs: ["get"] },
203
+ { apiGroups: [""], resources: ["nodes/stats", "configmaps", "events"], verbs: ["create", "get"] },
204
+ { apiGroups: [""], resources: ["configmaps"], verbs: ["get", "update", "create"], resourceNames: ["otel-container-insight-clusterleader"] },
205
+ ],
206
+ };
207
+
208
+ const clusterRoleBindingProps: Record<string, unknown> = {
209
+ metadata: {
210
+ name: bindingName,
211
+ labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
212
+ },
213
+ roleRef: {
214
+ apiGroup: "rbac.authorization.k8s.io",
215
+ kind: "ClusterRole",
216
+ name: clusterRoleName,
217
+ },
218
+ subjects: [
219
+ {
220
+ kind: "ServiceAccount",
221
+ name: saName,
222
+ namespace,
223
+ },
224
+ ],
225
+ };
226
+
227
+ const configMapProps: Record<string, unknown> = {
228
+ metadata: {
229
+ name: configMapName,
230
+ namespace,
231
+ labels: { ...commonLabels, "app.kubernetes.io/component": "config" },
232
+ },
233
+ data: {
234
+ "config.yaml": adotConfig,
235
+ },
236
+ };
237
+
238
+ return {
239
+ daemonSet: daemonSetProps,
240
+ serviceAccount: serviceAccountProps,
241
+ clusterRole: clusterRoleProps,
242
+ clusterRoleBinding: clusterRoleBindingProps,
243
+ configMap: configMapProps,
244
+ };
245
+ }
@@ -0,0 +1,149 @@
1
+ /**
2
+ * AgicIngress composite — Ingress with Azure Application Gateway Ingress Controller annotations.
3
+ *
4
+ * @aks Full `appgw.ingress.kubernetes.io/*` annotation set including SSL redirect,
5
+ * WAF policy, backend path prefix, and cookie-based affinity.
6
+ */
7
+
8
+ export interface AgicIngressHost {
9
+ /** Hostname (e.g., "api.example.com"). */
10
+ hostname: string;
11
+ /** Path rules for this host. */
12
+ paths: Array<{
13
+ path: string;
14
+ pathType?: string;
15
+ serviceName: string;
16
+ servicePort: number;
17
+ }>;
18
+ }
19
+
20
+ export interface AgicIngressProps {
21
+ /** Ingress name — used in metadata and labels. */
22
+ name: string;
23
+ /** Host definitions with paths. */
24
+ hosts: AgicIngressHost[];
25
+ /** Azure Key Vault certificate URI or secret name for TLS. */
26
+ certificateArn?: string;
27
+ /** WAF policy resource ID. */
28
+ wafPolicyId?: string;
29
+ /** Health check path for backend. */
30
+ healthCheckPath?: string;
31
+ /** Enable HTTP->HTTPS redirect (default: true when certificateArn set). */
32
+ sslRedirect?: boolean;
33
+ /** Backend path prefix override. */
34
+ backendPathPrefix?: string;
35
+ /** Enable cookie-based affinity (default: false). */
36
+ cookieAffinity?: boolean;
37
+ /** Additional annotations on the Ingress. */
38
+ annotations?: Record<string, string>;
39
+ /** Additional labels to apply to all resources. */
40
+ labels?: Record<string, string>;
41
+ /** Namespace for all resources. */
42
+ namespace?: string;
43
+ }
44
+
45
+ export interface AgicIngressResult {
46
+ ingress: Record<string, unknown>;
47
+ }
48
+
49
+ /**
50
+ * Create an AgicIngress composite — returns prop objects for
51
+ * an Ingress with Azure Application Gateway Ingress Controller annotations.
52
+ *
53
+ * @aks
54
+ * @example
55
+ * ```ts
56
+ * import { AgicIngress } from "@intentius/chant-lexicon-k8s";
57
+ *
58
+ * const { ingress } = AgicIngress({
59
+ * name: "api-ingress",
60
+ * hosts: [
61
+ * {
62
+ * hostname: "api.example.com",
63
+ * paths: [{ path: "/", serviceName: "api", servicePort: 80 }],
64
+ * },
65
+ * ],
66
+ * certificateArn: "keyvault-secret-name",
67
+ * wafPolicyId: "/subscriptions/.../Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies/my-waf",
68
+ * });
69
+ * ```
70
+ */
71
+ export function AgicIngress(props: AgicIngressProps): AgicIngressResult {
72
+ const {
73
+ name,
74
+ hosts,
75
+ certificateArn,
76
+ wafPolicyId,
77
+ healthCheckPath,
78
+ sslRedirect,
79
+ backendPathPrefix,
80
+ cookieAffinity = false,
81
+ annotations: extraAnnotations = {},
82
+ labels: extraLabels = {},
83
+ namespace,
84
+ } = props;
85
+
86
+ const commonLabels: Record<string, string> = {
87
+ "app.kubernetes.io/name": name,
88
+ "app.kubernetes.io/managed-by": "chant",
89
+ ...extraLabels,
90
+ };
91
+
92
+ // Build AGIC annotations
93
+ const annotations: Record<string, string> = {
94
+ "kubernetes.io/ingress.class": "azure/application-gateway",
95
+ ...extraAnnotations,
96
+ };
97
+
98
+ if (sslRedirect ?? (certificateArn !== undefined)) {
99
+ annotations["appgw.ingress.kubernetes.io/ssl-redirect"] = "true";
100
+ }
101
+
102
+ if (certificateArn) {
103
+ annotations["appgw.ingress.kubernetes.io/appgw-ssl-certificate"] = certificateArn;
104
+ }
105
+
106
+ if (wafPolicyId) {
107
+ annotations["appgw.ingress.kubernetes.io/waf-policy-for-path"] = wafPolicyId;
108
+ }
109
+
110
+ if (healthCheckPath) {
111
+ annotations["appgw.ingress.kubernetes.io/health-probe-path"] = healthCheckPath;
112
+ }
113
+
114
+ if (backendPathPrefix) {
115
+ annotations["appgw.ingress.kubernetes.io/backend-path-prefix"] = backendPathPrefix;
116
+ }
117
+
118
+ if (cookieAffinity) {
119
+ annotations["appgw.ingress.kubernetes.io/cookie-based-affinity"] = "true";
120
+ }
121
+
122
+ const ingressRules = hosts.map((host) => ({
123
+ host: host.hostname,
124
+ http: {
125
+ paths: host.paths.map((p) => ({
126
+ path: p.path,
127
+ pathType: p.pathType ?? "Prefix",
128
+ backend: {
129
+ service: { name: p.serviceName, port: { number: p.servicePort } },
130
+ },
131
+ })),
132
+ },
133
+ }));
134
+
135
+ const ingressProps: Record<string, unknown> = {
136
+ metadata: {
137
+ name,
138
+ ...(namespace && { namespace }),
139
+ labels: { ...commonLabels, "app.kubernetes.io/component": "ingress" },
140
+ annotations,
141
+ },
142
+ spec: {
143
+ ingressClassName: "azure/application-gateway",
144
+ rules: ingressRules,
145
+ },
146
+ };
147
+
148
+ return { ingress: ingressProps };
149
+ }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * AlbIngress composite — Ingress with AWS Load Balancer Controller annotations.
3
+ *
4
+ * @eks Full `alb.ingress.kubernetes.io/*` annotation set including group name
5
+ * (shared ALB), SSL redirect, subnets, security groups.
6
+ */
7
+
8
+ export interface AlbIngressHost {
9
+ /** Hostname (e.g., "api.example.com"). */
10
+ hostname: string;
11
+ /** Path rules for this host. */
12
+ paths: Array<{
13
+ path: string;
14
+ pathType?: string;
15
+ serviceName: string;
16
+ /** Port on the Kubernetes Service (not the container port). */
17
+ servicePort: number;
18
+ }>;
19
+ }
20
+
21
+ export interface AlbIngressProps {
22
+ /** Ingress name — used in metadata and labels. */
23
+ name: string;
24
+ /** Host definitions with paths. */
25
+ hosts: AlbIngressHost[];
26
+ /** ALB scheme (default: "internet-facing"). */
27
+ scheme?: "internet-facing" | "internal";
28
+ /** Target type (default: "ip"). */
29
+ targetType?: "ip" | "instance";
30
+ /** ACM certificate ARN for TLS. */
31
+ certificateArn?: string;
32
+ /** WAF Web ACL ARN. */
33
+ wafAclArn?: string;
34
+ /** Health check path for target group. */
35
+ healthCheckPath?: string;
36
+ /** Ingress group name for shared ALB. */
37
+ groupName?: string;
38
+ /** Enable HTTP→HTTPS redirect (default: true when certificateArn set). */
39
+ sslRedirect?: boolean;
40
+ /** Additional annotations on the Ingress. */
41
+ annotations?: Record<string, string>;
42
+ /** Additional labels to apply to all resources. */
43
+ labels?: Record<string, string>;
44
+ /** Namespace for all resources. */
45
+ namespace?: string;
46
+ }
47
+
48
+ export interface AlbIngressResult {
49
+ ingress: Record<string, unknown>;
50
+ }
51
+
52
+ /**
53
+ * Create an AlbIngress composite — returns prop objects for
54
+ * an Ingress with AWS ALB Controller annotations.
55
+ *
56
+ * @eks
57
+ * @example
58
+ * ```ts
59
+ * import { AlbIngress } from "@intentius/chant-lexicon-k8s";
60
+ *
61
+ * const { ingress } = AlbIngress({
62
+ * name: "api-ingress",
63
+ * hosts: [
64
+ * {
65
+ * hostname: "api.example.com",
66
+ * paths: [{ path: "/", serviceName: "api", servicePort: 80 }],
67
+ * },
68
+ * ],
69
+ * scheme: "internet-facing",
70
+ * certificateArn: "arn:aws:acm:us-east-1:123456789012:certificate/abc-123",
71
+ * groupName: "shared-alb",
72
+ * });
73
+ * ```
74
+ */
75
+ export function AlbIngress(props: AlbIngressProps): AlbIngressResult {
76
+ const {
77
+ name,
78
+ hosts,
79
+ scheme = "internet-facing",
80
+ targetType = "ip",
81
+ certificateArn,
82
+ wafAclArn,
83
+ healthCheckPath,
84
+ groupName,
85
+ sslRedirect,
86
+ annotations: extraAnnotations = {},
87
+ labels: extraLabels = {},
88
+ namespace,
89
+ } = props;
90
+
91
+ const commonLabels: Record<string, string> = {
92
+ "app.kubernetes.io/name": name,
93
+ "app.kubernetes.io/managed-by": "chant",
94
+ ...extraLabels,
95
+ };
96
+
97
+ // Build ALB annotations
98
+ const annotations: Record<string, string> = {
99
+ "alb.ingress.kubernetes.io/scheme": scheme,
100
+ "alb.ingress.kubernetes.io/target-type": targetType,
101
+ ...extraAnnotations,
102
+ };
103
+
104
+ if (certificateArn) {
105
+ annotations["alb.ingress.kubernetes.io/certificate-arn"] = certificateArn;
106
+ annotations["alb.ingress.kubernetes.io/listen-ports"] = '[{"HTTPS":443}]';
107
+ }
108
+
109
+ if (sslRedirect ?? !!certificateArn) {
110
+ annotations["alb.ingress.kubernetes.io/ssl-redirect"] = "443";
111
+ }
112
+
113
+ if (wafAclArn) {
114
+ annotations["alb.ingress.kubernetes.io/wafv2-acl-arn"] = wafAclArn;
115
+ }
116
+
117
+ if (healthCheckPath) {
118
+ annotations["alb.ingress.kubernetes.io/healthcheck-path"] = healthCheckPath;
119
+ }
120
+
121
+ if (groupName) {
122
+ annotations["alb.ingress.kubernetes.io/group.name"] = groupName;
123
+ }
124
+
125
+ const ingressRules = hosts.map((host) => ({
126
+ host: host.hostname,
127
+ http: {
128
+ paths: host.paths.map((p) => ({
129
+ path: p.path,
130
+ pathType: p.pathType ?? "Prefix",
131
+ backend: {
132
+ service: { name: p.serviceName, port: { number: p.servicePort } },
133
+ },
134
+ })),
135
+ },
136
+ }));
137
+
138
+ const ingressProps: Record<string, unknown> = {
139
+ metadata: {
140
+ name,
141
+ ...(namespace && { namespace }),
142
+ labels: { ...commonLabels, "app.kubernetes.io/component": "ingress" },
143
+ annotations,
144
+ },
145
+ spec: {
146
+ ingressClassName: "alb",
147
+ rules: ingressRules,
148
+ },
149
+ };
150
+
151
+ return { ingress: ingressProps };
152
+ }
@@ -6,6 +6,8 @@
6
6
  * PDB selectors match pod labels, and resource requests are set.
7
7
  */
8
8
 
9
+ import type { ContainerSecurityContext } from "./security-context";
10
+
9
11
  export interface AutoscaledServiceProps {
10
12
  /** Application name — used in metadata and labels. */
11
13
  name: string;
@@ -40,12 +42,33 @@ export interface AutoscaledServiceProps {
40
42
  livenessPath?: string;
41
43
  /** Readiness probe path (default: "/readyz"). */
42
44
  readinessPath?: string;
45
+ /** Init containers (e.g., migrations, cert setup). */
46
+ initContainers?: Array<{
47
+ name: string;
48
+ image: string;
49
+ command?: string[];
50
+ args?: string[];
51
+ }>;
52
+ /** Container security context (supports PSS restricted fields). */
53
+ securityContext?: ContainerSecurityContext;
54
+ /** Termination grace period in seconds. */
55
+ terminationGracePeriodSeconds?: number;
56
+ /** Priority class name for pod scheduling. */
57
+ priorityClassName?: string;
43
58
  /** Additional labels to apply to all resources. */
44
59
  labels?: Record<string, string>;
45
60
  /** Namespace for all resources. */
46
61
  namespace?: string;
47
62
  /** Environment variables for the container. */
48
63
  env?: Array<{ name: string; value: string }>;
64
+ /** Service account name for the pod. */
65
+ serviceAccountName?: string;
66
+ /** Volumes to attach to the pod. */
67
+ volumes?: Array<Record<string, unknown>>;
68
+ /** Volume mounts for the primary container. */
69
+ volumeMounts?: Array<Record<string, unknown>>;
70
+ /** Convenience: auto-generate emptyDir volumes + mounts for writable temp dirs (e.g. ["/tmp", "/var/cache/nginx"]). */
71
+ tmpDirs?: string[];
49
72
  }
50
73
 
51
74
  export interface AutoscaledServiceResult {
@@ -89,9 +112,17 @@ export function AutoscaledService(props: AutoscaledServiceProps): AutoscaledServ
89
112
  topologySpread = false,
90
113
  livenessPath = "/healthz",
91
114
  readinessPath = "/readyz",
115
+ initContainers,
116
+ securityContext,
117
+ terminationGracePeriodSeconds,
118
+ priorityClassName,
92
119
  labels: extraLabels = {},
93
120
  namespace,
94
121
  env,
122
+ serviceAccountName,
123
+ volumes: explicitVolumes = [],
124
+ volumeMounts: explicitMounts = [],
125
+ tmpDirs = [],
95
126
  } = props;
96
127
 
97
128
  const commonLabels: Record<string, string> = {
@@ -115,6 +146,12 @@ export function AutoscaledService(props: AutoscaledServiceProps): AutoscaledServ
115
146
  }];
116
147
  })();
117
148
 
149
+ // Generate emptyDir volumes/mounts from tmpDirs, then merge with explicit
150
+ const tmpVolumes = tmpDirs.map((_, i) => ({ name: `tmp-${i}`, emptyDir: {} }));
151
+ const tmpMounts = tmpDirs.map((dir, i) => ({ name: `tmp-${i}`, mountPath: dir }));
152
+ const allVolumes = [...explicitVolumes, ...tmpVolumes];
153
+ const allMounts = [...explicitMounts, ...tmpMounts];
154
+
118
155
  const resources: Record<string, unknown> = {
119
156
  requests: { cpu: cpuRequest, memory: memoryRequest },
120
157
  ...(cpuLimit || memoryLimit
@@ -156,9 +193,23 @@ export function AutoscaledService(props: AutoscaledServiceProps): AutoscaledServ
156
193
  periodSeconds: 5,
157
194
  },
158
195
  ...(env && { env }),
196
+ ...(securityContext && { securityContext }),
197
+ ...(allMounts.length > 0 && { volumeMounts: allMounts }),
159
198
  },
160
199
  ],
200
+ ...(initContainers && {
201
+ initContainers: initContainers.map((ic) => ({
202
+ name: ic.name,
203
+ image: ic.image,
204
+ ...(ic.command && { command: ic.command }),
205
+ ...(ic.args && { args: ic.args }),
206
+ })),
207
+ }),
161
208
  ...(topologyConstraints && { topologySpreadConstraints: topologyConstraints }),
209
+ ...(terminationGracePeriodSeconds !== undefined && { terminationGracePeriodSeconds }),
210
+ ...(priorityClassName && { priorityClassName }),
211
+ ...(serviceAccountName && { serviceAccountName }),
212
+ ...(allVolumes.length > 0 && { volumes: allVolumes }),
162
213
  },
163
214
  },
164
215
  },