@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
@@ -1,32 +1,37 @@
1
1
  {
2
2
  "algorithm": "xxhash64",
3
3
  "artifacts": {
4
- "manifest.json": "4ed66408d8e29abe",
4
+ "manifest.json": "490e9ca9417db298",
5
5
  "meta.json": "1ce194f36f9b5f90",
6
6
  "types/index.d.ts": "beec4cc869064186",
7
7
  "rules/hardcoded-namespace.ts": "54b216c71018e101",
8
- "rules/wk8006.ts": "6e04f754f79f076e",
9
8
  "rules/wk8201.ts": "4dbbd20e21b5fa04",
10
- "rules/wk8103.ts": "e5d6a506d966e312",
11
- "rules/k8s-helpers.ts": "53a6d3bfbedb2852",
9
+ "rules/wk8204.ts": "9244d6fdd6d2f7d",
12
10
  "rules/wk8301.ts": "283265ab0c5b8511",
13
- "rules/wk8105.ts": "8dbcfe399f23656a",
11
+ "rules/wk8306.ts": "5338575bfb0f9251",
12
+ "rules/wk8102.ts": "78d4aac387107b56",
13
+ "rules/wk8305.ts": "5c4b9482a0f3b91d",
14
14
  "rules/wk8101.ts": "f8ffcf6e5c89076b",
15
15
  "rules/wk8302.ts": "a80d1eab37c0dbe4",
16
+ "rules/wk8005.ts": "a9a1b93b80f3aa51",
17
+ "rules/wk8209.ts": "820df53f304e0a59",
16
18
  "rules/wk8203.ts": "f5bf17539c0428c5",
17
- "rules/wk8042.ts": "6064c84481ae8551",
18
- "rules/wk8202.ts": "6bd950ae2128256c",
19
- "rules/wk8207.ts": "6f2bc621d530afa2",
20
19
  "rules/wk8104.ts": "94bd75c16b8d50b7",
20
+ "rules/wk8303.ts": "c545464e2af8fbb9",
21
+ "rules/wk8103.ts": "e5d6a506d966e312",
21
22
  "rules/wk8205.ts": "d000527c6371a05",
23
+ "rules/wk8042.ts": "6064c84481ae8551",
24
+ "rules/wk8006.ts": "6e04f754f79f076e",
22
25
  "rules/wk8041.ts": "4df512c93caaef50",
23
- "rules/wk8102.ts": "78d4aac387107b56",
24
- "rules/wk8005.ts": "a9a1b93b80f3aa51",
25
- "rules/wk8303.ts": "c545464e2af8fbb9",
26
- "rules/wk8204.ts": "31c3f8eac8455795",
26
+ "rules/wk8304.ts": "f51bf894c5a08dbe",
27
+ "rules/wk8202.ts": "6bd950ae2128256c",
27
28
  "rules/wk8208.ts": "1133f9e53c174ae9",
28
- "rules/wk8209.ts": "820df53f304e0a59",
29
- "skills/chant-k8s.md": "f1c4ed163f8fa84c"
29
+ "rules/wk8105.ts": "8dbcfe399f23656a",
30
+ "rules/k8s-helpers.ts": "53a6d3bfbedb2852",
31
+ "rules/wk8207.ts": "6f2bc621d530afa2",
32
+ "skills/chant-k8s.md": "c7db82c3ba37c78",
33
+ "skills/chant-k8s-eks.md": "f79f31f058c7f2ed",
34
+ "skills/chant-k8s-patterns.md": "c5151ed799145c4b"
30
35
  },
31
- "composite": "c630f44a73860007"
36
+ "composite": "2a6c7f09f87d9a38"
32
37
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "k8s",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "K8s",
6
6
  "intrinsics": [],
@@ -4,6 +4,10 @@
4
4
  * Containers should set securityContext.runAsNonRoot to true at either
5
5
  * the container level or pod level. Running as root inside a container
6
6
  * increases the blast radius of a container breakout.
7
+ *
8
+ * Additionally warns when runAsNonRoot: true is set but no explicit
9
+ * runAsUser is provided — without a numeric UID, K8s relies on the
10
+ * image's USER directive, which may be root (UID 0).
7
11
  */
8
12
 
9
13
  import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
@@ -30,13 +34,17 @@ export const wk8204: PostSynthCheck = {
30
34
  // Check pod-level securityContext
31
35
  const podSecCtx = podSpec.securityContext as Record<string, unknown> | undefined;
32
36
  const podRunAsNonRoot = podSecCtx?.runAsNonRoot === true;
37
+ const podRunAsUser = podSecCtx?.runAsUser;
33
38
 
34
39
  const containers = extractContainers(manifest);
35
40
  for (const container of containers) {
36
41
  const secCtx = container.securityContext;
37
42
  const containerRunAsNonRoot = secCtx?.runAsNonRoot === true;
43
+ const containerRunAsUser = secCtx?.runAsUser;
44
+
45
+ const hasRunAsNonRoot = podRunAsNonRoot || containerRunAsNonRoot;
38
46
 
39
- if (!podRunAsNonRoot && !containerRunAsNonRoot) {
47
+ if (!hasRunAsNonRoot) {
40
48
  diagnostics.push({
41
49
  checkId: "WK8204",
42
50
  severity: "warning",
@@ -44,6 +52,30 @@ export const wk8204: PostSynthCheck = {
44
52
  entity: resourceName,
45
53
  lexicon: "k8s",
46
54
  });
55
+ continue;
56
+ }
57
+
58
+ // runAsNonRoot is true — check for explicit runAsUser
59
+ const effectiveRunAsUser = containerRunAsUser ?? podRunAsUser;
60
+
61
+ if (effectiveRunAsUser === 0) {
62
+ // Contradictory: runAsNonRoot: true + runAsUser: 0
63
+ diagnostics.push({
64
+ checkId: "WK8204",
65
+ severity: "warning",
66
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" has runAsNonRoot: true but runAsUser: 0 — these settings are contradictory and the container will fail to start`,
67
+ entity: resourceName,
68
+ lexicon: "k8s",
69
+ });
70
+ } else if (effectiveRunAsUser === undefined || effectiveRunAsUser === null) {
71
+ // runAsNonRoot: true but no explicit UID
72
+ diagnostics.push({
73
+ checkId: "WK8204",
74
+ severity: "warning",
75
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" has runAsNonRoot: true but no explicit runAsUser — set a numeric UID to ensure the container doesn't run as root`,
76
+ entity: resourceName,
77
+ lexicon: "k8s",
78
+ });
47
79
  }
48
80
  }
49
81
  }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * WK8304: SSL Redirect Without Certificate
3
+ *
4
+ * Flags Ingress resources that have `alb.ingress.kubernetes.io/ssl-redirect`
5
+ * annotation set but are missing `alb.ingress.kubernetes.io/certificate-arn`
6
+ * or don't have HTTPS in their listen-ports.
7
+ */
8
+
9
+ import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
10
+ import { getPrimaryOutput, parseK8sManifests } from "./k8s-helpers";
11
+
12
+ export const wk8304: PostSynthCheck = {
13
+ id: "WK8304",
14
+ description: "SSL redirect without certificate — ssl-redirect annotation requires a valid certificate-arn and HTTPS listen-ports",
15
+
16
+ check(ctx: PostSynthContext): PostSynthDiagnostic[] {
17
+ const diagnostics: PostSynthDiagnostic[] = [];
18
+
19
+ for (const [, output] of ctx.outputs) {
20
+ const yaml = getPrimaryOutput(output);
21
+ const manifests = parseK8sManifests(yaml);
22
+
23
+ for (const manifest of manifests) {
24
+ if (manifest.kind !== "Ingress") continue;
25
+
26
+ const annotations = manifest.metadata?.annotations as Record<string, string> | undefined;
27
+ if (!annotations) continue;
28
+
29
+ const sslRedirect = annotations["alb.ingress.kubernetes.io/ssl-redirect"];
30
+ if (!sslRedirect) continue;
31
+
32
+ const resourceName = manifest.metadata?.name ?? "Ingress";
33
+ const certArn = annotations["alb.ingress.kubernetes.io/certificate-arn"];
34
+
35
+ if (!certArn) {
36
+ diagnostics.push({
37
+ checkId: "WK8304",
38
+ severity: "warning",
39
+ message: `Ingress "${resourceName}" has ssl-redirect annotation but no certificate-arn — HTTPS redirect will fail without a TLS certificate`,
40
+ entity: resourceName,
41
+ lexicon: "k8s",
42
+ });
43
+ continue;
44
+ }
45
+
46
+ // Check listen-ports includes HTTPS
47
+ const listenPorts = annotations["alb.ingress.kubernetes.io/listen-ports"];
48
+ if (listenPorts) {
49
+ try {
50
+ const ports = JSON.parse(listenPorts) as Array<Record<string, number>>;
51
+ const hasHttps = ports.some((p) => "HTTPS" in p);
52
+ if (!hasHttps) {
53
+ diagnostics.push({
54
+ checkId: "WK8304",
55
+ severity: "warning",
56
+ message: `Ingress "${resourceName}" has ssl-redirect but listen-ports does not include HTTPS`,
57
+ entity: resourceName,
58
+ lexicon: "k8s",
59
+ });
60
+ }
61
+ } catch {
62
+ // Can't parse listen-ports — skip this check
63
+ }
64
+ }
65
+ }
66
+ }
67
+
68
+ return diagnostics;
69
+ },
70
+ };
@@ -0,0 +1,115 @@
1
+ /**
2
+ * WK8305: Ingress Port Not Matching Service
3
+ *
4
+ * Flags Ingress backends whose `service.port.number` does not match
5
+ * any declared port on the referenced Service in the manifest set.
6
+ */
7
+
8
+ import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
9
+ import { getPrimaryOutput, parseK8sManifests } from "./k8s-helpers";
10
+ import type { K8sManifest } from "./k8s-helpers";
11
+
12
+ export const wk8305: PostSynthCheck = {
13
+ id: "WK8305",
14
+ description: "Ingress port not matching Service — backend port must match a declared Service port",
15
+
16
+ check(ctx: PostSynthContext): PostSynthDiagnostic[] {
17
+ const diagnostics: PostSynthDiagnostic[] = [];
18
+
19
+ for (const [, output] of ctx.outputs) {
20
+ const yaml = getPrimaryOutput(output);
21
+ const manifests = parseK8sManifests(yaml);
22
+
23
+ // Build a map of Service name+namespace → set of port numbers
24
+ const servicePorts = collectServicePorts(manifests);
25
+
26
+ for (const manifest of manifests) {
27
+ if (manifest.kind !== "Ingress") continue;
28
+
29
+ const ingressName = manifest.metadata?.name ?? "Ingress";
30
+ const ingressNamespace = manifest.metadata?.namespace ?? "default";
31
+ const spec = manifest.spec;
32
+ if (!spec) continue;
33
+
34
+ const rules = spec.rules as Array<Record<string, unknown>> | undefined;
35
+ if (!rules) continue;
36
+
37
+ for (const rule of rules) {
38
+ const http = rule.http as Record<string, unknown> | undefined;
39
+ if (!http) continue;
40
+
41
+ const paths = http.paths as Array<Record<string, unknown>> | undefined;
42
+ if (!paths) continue;
43
+
44
+ for (const pathEntry of paths) {
45
+ const backend = pathEntry.backend as Record<string, unknown> | undefined;
46
+ if (!backend) continue;
47
+
48
+ const service = backend.service as Record<string, unknown> | undefined;
49
+ if (!service) continue;
50
+
51
+ const svcName = service.name as string | undefined;
52
+ const port = service.port as Record<string, unknown> | undefined;
53
+ const portNumber = port?.number as number | undefined;
54
+
55
+ if (!svcName || portNumber === undefined) continue;
56
+
57
+ // Look up the Service in the manifest set
58
+ const key = `${ingressNamespace}/${svcName}`;
59
+ const knownPorts = servicePorts.get(key);
60
+
61
+ // Skip if the Service is not in the manifest set (external service)
62
+ if (!knownPorts) continue;
63
+
64
+ if (!knownPorts.has(portNumber)) {
65
+ diagnostics.push({
66
+ checkId: "WK8305",
67
+ severity: "warning",
68
+ message: `Ingress "${ingressName}" references Service "${svcName}" port ${portNumber}, but the Service only declares ports [${[...knownPorts].join(", ")}]`,
69
+ entity: ingressName,
70
+ lexicon: "k8s",
71
+ });
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }
77
+
78
+ return diagnostics;
79
+ },
80
+ };
81
+
82
+ /**
83
+ * Collect port numbers from all Service manifests, keyed by namespace/name.
84
+ */
85
+ function collectServicePorts(manifests: K8sManifest[]): Map<string, Set<number>> {
86
+ const result = new Map<string, Set<number>>();
87
+
88
+ for (const manifest of manifests) {
89
+ if (manifest.kind !== "Service") continue;
90
+
91
+ const name = manifest.metadata?.name;
92
+ const namespace = manifest.metadata?.namespace ?? "default";
93
+ if (!name) continue;
94
+
95
+ const key = `${namespace}/${name}`;
96
+ const ports = new Set<number>();
97
+
98
+ const spec = manifest.spec;
99
+ if (spec) {
100
+ const specPorts = spec.ports as Array<Record<string, unknown>> | undefined;
101
+ if (specPorts) {
102
+ for (const p of specPorts) {
103
+ const port = p.port as number | undefined;
104
+ if (port !== undefined) {
105
+ ports.add(port);
106
+ }
107
+ }
108
+ }
109
+ }
110
+
111
+ result.set(key, ports);
112
+ }
113
+
114
+ return result;
115
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * WK8306: Container Command Starts With Flag
3
+ *
4
+ * If `command[0]` starts with `-` or `--`, it's almost certainly a mistake —
5
+ * the first element should be the binary/entrypoint, flags belong in `args`.
6
+ * This causes OCI runtime errors because the container runtime tries to
7
+ * execute the flag as a binary.
8
+ */
9
+
10
+ import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
11
+ import { getPrimaryOutput, parseK8sManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
12
+
13
+ export const wk8306: PostSynthCheck = {
14
+ id: "WK8306",
15
+ description: "Container command starts with flag — first element should be a binary, not a flag",
16
+
17
+ check(ctx: PostSynthContext): PostSynthDiagnostic[] {
18
+ const diagnostics: PostSynthDiagnostic[] = [];
19
+
20
+ for (const [, output] of ctx.outputs) {
21
+ const yaml = getPrimaryOutput(output);
22
+ const manifests = parseK8sManifests(yaml);
23
+
24
+ for (const manifest of manifests) {
25
+ if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
26
+
27
+ const resourceName = manifest.metadata?.name ?? manifest.kind;
28
+ const containers = extractContainers(manifest);
29
+
30
+ for (const container of containers) {
31
+ const command = (container as Record<string, unknown>).command as unknown[] | undefined;
32
+ if (!Array.isArray(command) || command.length === 0) continue;
33
+
34
+ const firstArg = String(command[0]);
35
+ if (firstArg.startsWith("-")) {
36
+ diagnostics.push({
37
+ checkId: "WK8306",
38
+ severity: "error",
39
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" has command[0]="${firstArg}" which starts with a flag — the first element should be the binary, flags belong in args`,
40
+ entity: resourceName,
41
+ lexicon: "k8s",
42
+ });
43
+ }
44
+ }
45
+ }
46
+ }
47
+
48
+ return diagnostics;
49
+ },
50
+ };
@@ -0,0 +1,156 @@
1
+ ---
2
+ skill: chant-k8s-eks
3
+ description: EKS-specific Kubernetes patterns and composites
4
+ user-invocable: true
5
+ ---
6
+
7
+ # EKS Kubernetes Patterns
8
+
9
+ ## EKS Composites Overview
10
+
11
+ These composites produce K8s YAML with EKS-specific annotations and configurations.
12
+
13
+ ### IrsaServiceAccount — ServiceAccount with IAM Role annotation
14
+
15
+ ```typescript
16
+ import { IrsaServiceAccount } from "@intentius/chant-lexicon-k8s";
17
+
18
+ const { serviceAccount, role, roleBinding } = IrsaServiceAccount({
19
+ name: "app-sa",
20
+ iamRoleArn: "arn:aws:iam::123456789012:role/my-app-role",
21
+ rbacRules: [
22
+ { apiGroups: [""], resources: ["secrets"], verbs: ["get"] },
23
+ ],
24
+ namespace: "prod",
25
+ });
26
+ ```
27
+
28
+ ### AlbIngress — Ingress with AWS ALB Controller annotations
29
+
30
+ ```typescript
31
+ import { AlbIngress } from "@intentius/chant-lexicon-k8s";
32
+
33
+ const { ingress } = AlbIngress({
34
+ name: "api-ingress",
35
+ hosts: [
36
+ {
37
+ hostname: "api.example.com",
38
+ paths: [{ path: "/", serviceName: "api", servicePort: 80 }],
39
+ },
40
+ ],
41
+ scheme: "internet-facing",
42
+ certificateArn: "arn:aws:acm:us-east-1:123456789012:certificate/abc-123",
43
+ groupName: "shared-alb",
44
+ healthCheckPath: "/healthz",
45
+ });
46
+ ```
47
+
48
+ Features:
49
+ - Auto-sets `alb.ingress.kubernetes.io/*` annotations
50
+ - SSL redirect enabled by default when `certificateArn` set
51
+ - `groupName` for shared ALB across multiple Ingresses
52
+ - `wafAclArn` for WAFv2 integration
53
+
54
+ ### EbsStorageClass — StorageClass for EBS CSI
55
+
56
+ ```typescript
57
+ import { EbsStorageClass } from "@intentius/chant-lexicon-k8s";
58
+
59
+ const { storageClass } = EbsStorageClass({
60
+ name: "gp3-encrypted",
61
+ type: "gp3",
62
+ encrypted: true,
63
+ iops: "3000",
64
+ throughput: "125",
65
+ });
66
+ ```
67
+
68
+ ### EfsStorageClass — StorageClass for EFS CSI (ReadWriteMany)
69
+
70
+ ```typescript
71
+ import { EfsStorageClass } from "@intentius/chant-lexicon-k8s";
72
+
73
+ const { storageClass } = EfsStorageClass({
74
+ name: "efs-shared",
75
+ fileSystemId: "fs-12345678",
76
+ });
77
+ ```
78
+
79
+ Use EFS when you need ReadWriteMany (shared across pods/nodes). Use EBS for ReadWriteOnce (single pod).
80
+
81
+ ### FluentBitAgent — DaemonSet for CloudWatch logging
82
+
83
+ ```typescript
84
+ import { FluentBitAgent } from "@intentius/chant-lexicon-k8s";
85
+
86
+ const result = FluentBitAgent({
87
+ logGroup: "/aws/eks/my-cluster/containers",
88
+ region: "us-east-1",
89
+ clusterName: "my-cluster",
90
+ });
91
+ ```
92
+
93
+ ### ExternalDnsAgent — ExternalDNS for Route53
94
+
95
+ ```typescript
96
+ import { ExternalDnsAgent } from "@intentius/chant-lexicon-k8s";
97
+
98
+ const result = ExternalDnsAgent({
99
+ iamRoleArn: "arn:aws:iam::123456789012:role/external-dns-role",
100
+ domainFilters: ["example.com"],
101
+ txtOwnerId: "my-cluster",
102
+ });
103
+ ```
104
+
105
+ ### AdotCollector — ADOT for CloudWatch/X-Ray
106
+
107
+ ```typescript
108
+ import { AdotCollector } from "@intentius/chant-lexicon-k8s";
109
+
110
+ const result = AdotCollector({
111
+ region: "us-east-1",
112
+ clusterName: "my-cluster",
113
+ exporters: ["cloudwatch", "xray"],
114
+ });
115
+ ```
116
+
117
+ ## Pod Identity vs IRSA
118
+
119
+ | Feature | IRSA | Pod Identity |
120
+ |---------|------|-------------|
121
+ | K8s annotation needed | Yes (`eks.amazonaws.com/role-arn`) | No |
122
+ | Composite available | **IrsaServiceAccount** | None needed |
123
+ | Setup | OIDC provider + IAM role trust policy | EKS Pod Identity Agent add-on + association |
124
+ | When to use | Existing clusters, broad compatibility | New clusters (EKS 1.28+), simpler management |
125
+
126
+ For Pod Identity, no K8s-side composite is needed — configure the association via AWS API/CloudFormation and use a plain ServiceAccount.
127
+
128
+ ## Karpenter
129
+
130
+ Karpenter replaces Cluster Autoscaler for node provisioning. Karpenter NodePool and EC2NodeClass are simple CRDs — use CRD import rather than composites:
131
+
132
+ ```bash
133
+ # Import Karpenter CRDs into your chant project
134
+ chant import --url https://raw.githubusercontent.com/aws/karpenter/main/pkg/apis/crds/karpenter.sh_nodepools.yaml
135
+ ```
136
+
137
+ ## Fargate Considerations
138
+
139
+ When running on EKS Fargate:
140
+ - **No DaemonSets** — FluentBitAgent and AdotCollector cannot run on Fargate nodes
141
+ - **No hostPath volumes** — use EFS for shared storage
142
+ - **No privileged containers** — security context restrictions apply
143
+ - For Fargate logging, use the built-in Fluent Bit log router (Fargate logging configuration)
144
+
145
+ ## EKS Add-ons
146
+
147
+ Common add-ons managed via AWS (not K8s manifests):
148
+ - **vpc-cni** — Amazon VPC CNI plugin
149
+ - **coredns** — Cluster DNS
150
+ - **kube-proxy** — Network proxy
151
+ - **aws-ebs-csi-driver** — EBS CSI driver (required for EbsStorageClass)
152
+ - **aws-efs-csi-driver** — EFS CSI driver (required for EfsStorageClass)
153
+ - **adot** — AWS Distro for OpenTelemetry (alternative to AdotCollector composite)
154
+ - **aws-guardduty-agent** — Runtime threat detection
155
+
156
+ Configure add-ons via the AWS lexicon (`@intentius/chant-lexicon-aws`) CloudFormation resources.