@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
package/src/plugin.ts CHANGED
@@ -42,11 +42,14 @@ export const k8sPlugin: LexiconPlugin = {
42
42
  const { wk8301 } = require("./lint/post-synth/wk8301");
43
43
  const { wk8302 } = require("./lint/post-synth/wk8302");
44
44
  const { wk8303 } = require("./lint/post-synth/wk8303");
45
+ const { wk8304 } = require("./lint/post-synth/wk8304");
46
+ const { wk8305 } = require("./lint/post-synth/wk8305");
47
+ const { wk8306 } = require("./lint/post-synth/wk8306");
45
48
  return [
46
49
  wk8005, wk8006, wk8041, wk8042,
47
50
  wk8101, wk8102, wk8103, wk8104, wk8105,
48
51
  wk8201, wk8202, wk8203, wk8204, wk8205, wk8207, wk8208, wk8209,
49
- wk8301, wk8302, wk8303,
52
+ wk8301, wk8302, wk8303, wk8304, wk8305, wk8306,
50
53
  ];
51
54
  },
52
55
 
@@ -513,20 +516,6 @@ kubectl port-forward pod/<pod-name> 8080:8080
513
516
  | CreateContainerError | Container config issue | \`kubectl describe pod\` → Events | Check volume mounts, configmap/secret refs, security context |
514
517
  | Init:CrashLoopBackOff | Init container failing | \`kubectl logs -c <init-container>\` | Fix init container command, check dependencies |
515
518
 
516
- ### Resource inspection
517
-
518
- \`\`\`bash
519
- # Get all resources in namespace
520
- kubectl get all -n <namespace>
521
-
522
- # YAML output for debugging
523
- kubectl get deployment/my-app -o yaml
524
-
525
- # Check resource usage
526
- kubectl top pods -l app.kubernetes.io/name=my-app
527
- kubectl top nodes
528
- \`\`\`
529
-
530
519
  ## Production safety
531
520
 
532
521
  ### Pre-apply validation
@@ -542,22 +531,6 @@ kubectl apply -f manifests.yaml --dry-run=server
542
531
  kubectl apply -f manifests.yaml --dry-run=client
543
532
  \`\`\`
544
533
 
545
- ### Rollback
546
-
547
- \`\`\`bash
548
- # Check rollout history
549
- kubectl rollout history deployment/my-app
550
-
551
- # Undo last rollout
552
- kubectl rollout undo deployment/my-app
553
-
554
- # Roll back to a specific revision
555
- kubectl rollout undo deployment/my-app --to-revision=2
556
-
557
- # Watch rollout progress
558
- kubectl rollout status deployment/my-app --timeout=300s
559
- \`\`\`
560
-
561
534
  ### Deployment strategies
562
535
 
563
536
  - **RollingUpdate** (default): Gradually replaces pods. Set \`maxSurge\` and \`maxUnavailable\`.
@@ -565,157 +538,42 @@ kubectl rollout status deployment/my-app --timeout=300s
565
538
  - **Canary**: Deploy a second Deployment with 1 replica + same selector labels. Route percentage via Ingress annotations or service mesh.
566
539
  - **Blue/Green**: Two full Deployments (blue/green), switch Service selector between them.
567
540
 
568
- ## Composites
569
-
570
- Composites are higher-level functions that produce multiple coordinated K8s resources from a single call. They return plain prop objects — not class instances — that you pass to generated constructors or serialize directly.
571
-
572
- ### WebApp — Deployment + Service + optional Ingress
573
-
574
- \`\`\`typescript
575
- import { WebApp } from "@intentius/chant-lexicon-k8s";
576
-
577
- const { deployment, service, ingress } = WebApp({
578
- name: "frontend",
579
- image: "frontend:1.0",
580
- port: 3000,
581
- replicas: 3,
582
- ingressHost: "frontend.example.com",
583
- ingressTlsSecret: "frontend-tls",
584
- });
585
- \`\`\`
586
-
587
- ### StatefulApp StatefulSet + headless Service + PVC
588
-
589
- \`\`\`typescript
590
- import { StatefulApp } from "@intentius/chant-lexicon-k8s";
591
-
592
- const { statefulSet, service } = StatefulApp({
593
- name: "postgres",
594
- image: "postgres:16",
595
- storageSize: "20Gi",
596
- env: [{ name: "POSTGRES_DB", value: "mydb" }],
597
- });
598
- \`\`\`
599
-
600
- ### CronWorkloadCronJob + ServiceAccount + Role + RoleBinding
601
-
602
- \`\`\`typescript
603
- import { CronWorkload } from "@intentius/chant-lexicon-k8s";
604
-
605
- const { cronJob, serviceAccount, role, roleBinding } = CronWorkload({
606
- name: "db-backup",
607
- image: "postgres:16",
608
- schedule: "0 2 * * *",
609
- command: ["pg_dump", "-h", "postgres", "mydb"],
610
- rbacRules: [{ apiGroups: [""], resources: ["secrets"], verbs: ["get"] }],
611
- });
612
- \`\`\`
613
-
614
- ### AutoscaledService — Deployment + Service + HPA + PDB
615
-
616
- Production HTTP service with autoscaling, health probes, and optional topology spreading.
617
-
618
- \`\`\`typescript
619
- import { AutoscaledService } from "@intentius/chant-lexicon-k8s";
620
-
621
- const { deployment, service, hpa, pdb } = AutoscaledService({
622
- name: "api",
623
- image: "api:2.0",
624
- port: 8080,
625
- maxReplicas: 10,
626
- minReplicas: 3,
627
- cpuRequest: "200m",
628
- memoryRequest: "256Mi",
629
- cpuLimit: "1",
630
- memoryLimit: "1Gi",
631
- // Probe paths — defaults: /healthz and /readyz
632
- livenessPath: "/healthz",
633
- readinessPath: "/readyz",
634
- // Zone-aware topology spreading (default: false)
635
- topologySpread: true,
636
- // Custom: topologySpread: { maxSkew: 2, topologyKey: "kubernetes.io/hostname" }
637
- });
638
- \`\`\`
639
-
640
- Key features:
641
- - **Probe paths**: \`livenessPath\` (default \`/healthz\`) and \`readinessPath\` (default \`/readyz\`) — override for apps that don't serve those routes
642
- - **Topology spread**: \`topologySpread: true\` adds zone-aware spreading (maxSkew=1, DoNotSchedule); pass an object for custom key/skew
643
- - **PDB minAvailable**: accepts number or string percentage (e.g., \`"50%"\`)
644
-
645
- ### WorkerPool — Deployment + RBAC + optional ConfigMap + optional HPA
646
-
647
- Background queue workers with optional RBAC and autoscaling.
648
-
649
- \`\`\`typescript
650
- import { WorkerPool } from "@intentius/chant-lexicon-k8s";
651
-
652
- const { deployment, serviceAccount, role, roleBinding, configMap, hpa } = WorkerPool({
653
- name: "email-worker",
654
- image: "worker:1.0",
655
- command: ["bundle", "exec", "sidekiq"],
656
- config: { REDIS_URL: "redis://redis:6379", QUEUE: "emails" },
657
- autoscaling: { minReplicas: 2, maxReplicas: 20, targetCPUPercent: 60 },
658
- });
659
- \`\`\`
660
-
661
- Key features:
662
- - **RBAC opt-out**: Pass \`rbacRules: []\` to skip ServiceAccount/Role/RoleBinding creation entirely. Omitting \`rbacRules\` (undefined) creates default rules for secrets/configmaps.
663
- - \`serviceAccount\`, \`role\`, \`roleBinding\` are optional in the result — check before use
664
-
665
- ### NamespaceEnv — Namespace + ResourceQuota + LimitRange + NetworkPolicy
666
-
667
- Multi-tenant namespace provisioning with resource guardrails and network isolation.
668
-
669
- \`\`\`typescript
670
- import { NamespaceEnv } from "@intentius/chant-lexicon-k8s";
671
-
672
- const { namespace, resourceQuota, limitRange, networkPolicy } = NamespaceEnv({
673
- name: "team-alpha",
674
- cpuQuota: "8",
675
- memoryQuota: "16Gi",
676
- maxPods: 50,
677
- defaultCpuRequest: "100m",
678
- defaultMemoryRequest: "128Mi",
679
- defaultCpuLimit: "500m",
680
- defaultMemoryLimit: "512Mi",
681
- defaultDenyIngress: true,
682
- defaultDenyEgress: true,
683
- });
684
- \`\`\`
685
-
686
- Key features:
687
- - **Quota-without-limits warning**: Setting ResourceQuota without LimitRange defaults emits a warning — pods without explicit resource requests will fail to schedule
688
- - **Network policies**: \`defaultDenyIngress\` (default true), \`defaultDenyEgress\` (default false) — can be combined or used individually
689
-
690
- ### NodeAgent — DaemonSet + ServiceAccount + ClusterRole + ClusterRoleBinding + optional ConfigMap
691
-
692
- Per-node agents (log collectors, security scanners, monitoring exporters).
693
-
694
- \`\`\`typescript
695
- import { NodeAgent } from "@intentius/chant-lexicon-k8s";
696
-
697
- const { daemonSet, serviceAccount, clusterRole, clusterRoleBinding, configMap } = NodeAgent({
698
- name: "log-collector",
699
- image: "fluentd:v1.16",
700
- port: 24224,
701
- hostPaths: [
702
- { name: "varlog", hostPath: "/var/log", mountPath: "/var/log" },
703
- ],
704
- config: { "fluent.conf": "..." },
705
- rbacRules: [{ apiGroups: [""], resources: ["pods", "namespaces"], verbs: ["get", "list", "watch"] }],
706
- cpuRequest: "50m", // default: 50m
707
- memoryRequest: "64Mi", // default: 64Mi
708
- cpuLimit: "200m", // default: 200m
709
- memoryLimit: "128Mi", // default: 128Mi
710
- namespace: "monitoring",
711
- });
712
- \`\`\`
713
-
714
- Key features:
715
- - **Container resources**: Always set with sensible defaults (50m/64Mi requests, 200m/128Mi limits) — override per-agent
716
- - **Host paths**: Mounted read-only by default; set \`readOnly: false\` to enable writes
717
- - **Tolerations**: \`tolerateAllTaints: true\` (default) ensures agent runs on every node
718
- - Uses **ClusterRole/ClusterRoleBinding** (cluster-scoped) for node-level access
541
+ ## Choosing the Right Composite
542
+
543
+ Composites are higher-level functions that produce multiple coordinated K8s resources from a single call. They return plain prop objects — not class instances.
544
+
545
+ ### Decision Tree
546
+
547
+ | Need | Composite | Resources |
548
+ |------|-----------|-----------|
549
+ | Stateless web app | **WebApp** | Deployment + Service + optional Ingress + optional PDB |
550
+ | Stateful database/cache | **StatefulApp** | StatefulSet + headless Service + PVC + optional PDB |
551
+ | Production HTTP service with autoscaling | **AutoscaledService** | Deployment + Service + HPA + PDB |
552
+ | Background queue workers | **WorkerPool** | Deployment + RBAC + optional ConfigMap + optional HPA + optional PDB |
553
+ | Scheduled jobs | **CronWorkload** | CronJob + RBAC |
554
+ | One-shot batch jobs | **BatchJob** | Job + optional RBAC |
555
+ | App with ConfigMap/Secret mounts | **ConfiguredApp** | Deployment + Service + optional ConfigMap |
556
+ | Multi-container sidecar patterns | **SidecarApp** | Deployment + Service |
557
+ | App with Prometheus monitoring | **MonitoredService** | Deployment + Service + ServiceMonitor + optional PrometheusRule |
558
+ | App with fine-grained network policies | **NetworkIsolatedApp** | Deployment + Service + NetworkPolicy |
559
+ | Namespace with quotas and isolation | **NamespaceEnv** | Namespace + ResourceQuota + LimitRange + NetworkPolicy |
560
+ | Per-node agent (custom) | **NodeAgent** | DaemonSet + RBAC + optional ConfigMap |
561
+ | Multi-host TLS Ingress (cert-manager) | **SecureIngress** | Ingress + optional Certificate |
562
+ | EKS IRSA ServiceAccount | **IrsaServiceAccount** | ServiceAccount + optional RBAC |
563
+ | AWS ALB Ingress | **AlbIngress** | Ingress with ALB annotations |
564
+ | EBS StorageClass | **EbsStorageClass** | StorageClass (ebs.csi.aws.com) |
565
+ | EFS StorageClass | **EfsStorageClass** | StorageClass (efs.csi.aws.com) |
566
+ | Fluent Bit for CloudWatch | **FluentBitAgent** | DaemonSet + RBAC + ConfigMap |
567
+ | ExternalDNS for Route53 | **ExternalDnsAgent** | Deployment + IRSA SA + ClusterRole |
568
+ | ADOT for CloudWatch/X-Ray | **AdotCollector** | DaemonSet + RBAC + ConfigMap |
569
+
570
+ ### Hardening options (available on Deployment-based composites)
571
+
572
+ - \`minAvailable\` — creates a PodDisruptionBudget (WebApp, StatefulApp, WorkerPool; AutoscaledService always has one)
573
+ - \`initContainers\`run before main containers (WebApp, StatefulApp, AutoscaledService, ConfiguredApp, SidecarApp)
574
+ - \`securityContext\` — container security settings (WebApp, StatefulApp, AutoscaledService, WorkerPool)
575
+ - \`terminationGracePeriodSeconds\` — graceful shutdown (WebApp, StatefulApp, AutoscaledService, WorkerPool)
576
+ - \`priorityClassName\` pod scheduling priority (WebApp, StatefulApp, AutoscaledService, WorkerPool)
719
577
 
720
578
  ### Common patterns across all composites
721
579
 
@@ -724,49 +582,6 @@ Key features:
724
582
  - Pass \`namespace: "prod"\` to set namespace on all namespaced resources
725
583
  - Pass \`env: [{ name: "KEY", value: "val" }]\` for container environment variables
726
584
 
727
- ## Namespace management
728
-
729
- Use the **NamespaceEnv** composite (above) to manage namespaces declaratively. For manual kubectl management:
730
-
731
- \`\`\`bash
732
- # Create namespace
733
- kubectl create namespace my-project
734
-
735
- # Set default resource quotas
736
- kubectl apply -f - <<EOF
737
- apiVersion: v1
738
- kind: ResourceQuota
739
- metadata:
740
- name: default-quota
741
- namespace: my-project
742
- spec:
743
- hard:
744
- requests.cpu: "4"
745
- requests.memory: 8Gi
746
- limits.cpu: "8"
747
- limits.memory: 16Gi
748
- pods: "20"
749
- EOF
750
-
751
- # Set default container limits via LimitRange
752
- kubectl apply -f - <<EOF
753
- apiVersion: v1
754
- kind: LimitRange
755
- metadata:
756
- name: default-limits
757
- namespace: my-project
758
- spec:
759
- limits:
760
- - default:
761
- cpu: 500m
762
- memory: 256Mi
763
- defaultRequest:
764
- cpu: 100m
765
- memory: 128Mi
766
- type: Container
767
- EOF
768
- \`\`\`
769
-
770
585
  ## Troubleshooting reference table
771
586
 
772
587
  | Symptom | Likely cause | Resolution |
@@ -775,11 +590,8 @@ EOF
775
590
  | Pod stuck in Pending | PVC not bound | Check StorageClass exists, PV available |
776
591
  | Pod stuck in Pending | Node selector/affinity mismatch | Verify node labels match selectors |
777
592
  | Pod stuck in ContainerCreating | ConfigMap/Secret not found | Ensure referenced ConfigMaps/Secrets exist |
778
- | Pod stuck in ContainerCreating | Volume mount failure | Check PVC status, CSI driver health |
779
593
  | Service returns 503 | No ready endpoints | Check pod readiness probes, selector match |
780
- | Service returns 503 | Wrong port configuration | Verify targetPort matches containerPort |
781
594
  | Ingress returns 404 | Backend service not found | Check Ingress rules, service name/port |
782
- | Ingress returns 404 | Wrong path matching | Check pathType (Prefix vs Exact) |
783
595
  | HPA not scaling | Metrics server not installed | Install metrics-server |
784
596
  | HPA not scaling | Resource requests not set | Add CPU/memory requests to containers |
785
597
  | CronJob not running | Invalid cron expression | Validate cron syntax (5-field format) |
@@ -892,20 +704,522 @@ const { deployment, service, hpa, pdb } = AutoscaledService({
892
704
  });`,
893
705
  },
894
706
  {
895
- title: "NamespaceEnv composite",
896
- description: "Multi-tenant namespace with guardrails",
897
- input: "Set up a team namespace with quotas and network isolation",
898
- output: `import { NamespaceEnv } from "@intentius/chant-lexicon-k8s";
899
-
900
- const { namespace, resourceQuota, limitRange, networkPolicy } = NamespaceEnv({
901
- name: "team-alpha",
902
- cpuQuota: "8",
903
- memoryQuota: "16Gi",
904
- defaultCpuRequest: "100m",
905
- defaultMemoryRequest: "128Mi",
906
- defaultCpuLimit: "500m",
907
- defaultMemoryLimit: "512Mi",
908
- defaultDenyIngress: true,
707
+ title: "BatchJob composite",
708
+ description: "One-shot batch job with RBAC",
709
+ input: "Run a database migration job",
710
+ output: `import { BatchJob } from "@intentius/chant-lexicon-k8s";
711
+
712
+ const { job, serviceAccount, role, roleBinding } = BatchJob({
713
+ name: "db-migrate",
714
+ image: "api:1.0",
715
+ command: ["python", "manage.py", "migrate"],
716
+ backoffLimit: 3,
717
+ ttlSecondsAfterFinished: 3600,
718
+ });`,
719
+ },
720
+ ],
721
+ },
722
+ {
723
+ name: "chant-k8s-eks",
724
+ description: "EKS-specific Kubernetes composites — IRSA, ALB, EBS/EFS, Fluent Bit, ExternalDNS, ADOT",
725
+ content: `---
726
+ skill: chant-k8s-eks
727
+ description: EKS-specific Kubernetes patterns and composites
728
+ user-invocable: true
729
+ ---
730
+
731
+ # EKS Kubernetes Patterns
732
+
733
+ ## EKS Composites Overview
734
+
735
+ These composites produce K8s YAML with EKS-specific annotations and configurations.
736
+
737
+ ### IrsaServiceAccount — ServiceAccount with IAM Role annotation
738
+
739
+ \`\`\`typescript
740
+ import { IrsaServiceAccount } from "@intentius/chant-lexicon-k8s";
741
+
742
+ const { serviceAccount, role, roleBinding } = IrsaServiceAccount({
743
+ name: "app-sa",
744
+ iamRoleArn: "arn:aws:iam::123456789012:role/my-app-role",
745
+ rbacRules: [
746
+ { apiGroups: [""], resources: ["secrets"], verbs: ["get"] },
747
+ ],
748
+ namespace: "prod",
749
+ });
750
+ \`\`\`
751
+
752
+ ### AlbIngress — Ingress with AWS ALB Controller annotations
753
+
754
+ \`\`\`typescript
755
+ import { AlbIngress } from "@intentius/chant-lexicon-k8s";
756
+
757
+ const { ingress } = AlbIngress({
758
+ name: "api-ingress",
759
+ hosts: [
760
+ {
761
+ hostname: "api.example.com",
762
+ paths: [{ path: "/", serviceName: "api", servicePort: 80 }],
763
+ },
764
+ ],
765
+ scheme: "internet-facing",
766
+ certificateArn: "arn:aws:acm:us-east-1:123456789012:certificate/abc-123",
767
+ groupName: "shared-alb",
768
+ healthCheckPath: "/healthz",
769
+ });
770
+ \`\`\`
771
+
772
+ Features:
773
+ - Auto-sets \`alb.ingress.kubernetes.io/*\` annotations
774
+ - SSL redirect enabled by default when \`certificateArn\` set
775
+ - \`groupName\` for shared ALB across multiple Ingresses
776
+ - \`wafAclArn\` for WAFv2 integration
777
+
778
+ ### EbsStorageClass — StorageClass for EBS CSI
779
+
780
+ \`\`\`typescript
781
+ import { EbsStorageClass } from "@intentius/chant-lexicon-k8s";
782
+
783
+ const { storageClass } = EbsStorageClass({
784
+ name: "gp3-encrypted",
785
+ type: "gp3",
786
+ encrypted: true,
787
+ iops: "3000",
788
+ throughput: "125",
789
+ });
790
+ \`\`\`
791
+
792
+ ### EfsStorageClass — StorageClass for EFS CSI (ReadWriteMany)
793
+
794
+ \`\`\`typescript
795
+ import { EfsStorageClass } from "@intentius/chant-lexicon-k8s";
796
+
797
+ const { storageClass } = EfsStorageClass({
798
+ name: "efs-shared",
799
+ fileSystemId: "fs-12345678",
800
+ });
801
+ \`\`\`
802
+
803
+ Use EFS when you need ReadWriteMany (shared across pods/nodes). Use EBS for ReadWriteOnce (single pod).
804
+
805
+ ### FluentBitAgent — DaemonSet for CloudWatch logging
806
+
807
+ \`\`\`typescript
808
+ import { FluentBitAgent } from "@intentius/chant-lexicon-k8s";
809
+
810
+ const result = FluentBitAgent({
811
+ logGroup: "/aws/eks/my-cluster/containers",
812
+ region: "us-east-1",
813
+ clusterName: "my-cluster",
814
+ });
815
+ \`\`\`
816
+
817
+ ### ExternalDnsAgent — ExternalDNS for Route53
818
+
819
+ \`\`\`typescript
820
+ import { ExternalDnsAgent } from "@intentius/chant-lexicon-k8s";
821
+
822
+ const result = ExternalDnsAgent({
823
+ iamRoleArn: "arn:aws:iam::123456789012:role/external-dns-role",
824
+ domainFilters: ["example.com"],
825
+ txtOwnerId: "my-cluster",
826
+ });
827
+ \`\`\`
828
+
829
+ ### AdotCollector — ADOT for CloudWatch/X-Ray
830
+
831
+ \`\`\`typescript
832
+ import { AdotCollector } from "@intentius/chant-lexicon-k8s";
833
+
834
+ const result = AdotCollector({
835
+ region: "us-east-1",
836
+ clusterName: "my-cluster",
837
+ exporters: ["cloudwatch", "xray"],
838
+ });
839
+ \`\`\`
840
+
841
+ ## Pod Identity vs IRSA
842
+
843
+ | Feature | IRSA | Pod Identity |
844
+ |---------|------|-------------|
845
+ | K8s annotation needed | Yes (\`eks.amazonaws.com/role-arn\`) | No |
846
+ | Composite available | **IrsaServiceAccount** | None needed |
847
+ | Setup | OIDC provider + IAM role trust policy | EKS Pod Identity Agent add-on + association |
848
+ | When to use | Existing clusters, broad compatibility | New clusters (EKS 1.28+), simpler management |
849
+
850
+ For Pod Identity, no K8s-side composite is needed — configure the association via AWS API/CloudFormation and use a plain ServiceAccount.
851
+
852
+ ## Karpenter
853
+
854
+ Karpenter replaces Cluster Autoscaler for node provisioning. Karpenter NodePool and EC2NodeClass are simple CRDs — use CRD import rather than composites:
855
+
856
+ \`\`\`bash
857
+ # Import Karpenter CRDs into your chant project
858
+ chant import --url https://raw.githubusercontent.com/aws/karpenter/main/pkg/apis/crds/karpenter.sh_nodepools.yaml
859
+ \`\`\`
860
+
861
+ ## Fargate Considerations
862
+
863
+ When running on EKS Fargate:
864
+ - **No DaemonSets** — FluentBitAgent and AdotCollector cannot run on Fargate nodes
865
+ - **No hostPath volumes** — use EFS for shared storage
866
+ - **No privileged containers** — security context restrictions apply
867
+ - For Fargate logging, use the built-in Fluent Bit log router (Fargate logging configuration)
868
+
869
+ ## EKS Add-ons
870
+
871
+ Common add-ons managed via AWS (not K8s manifests):
872
+ - **vpc-cni** — Amazon VPC CNI plugin
873
+ - **coredns** — Cluster DNS
874
+ - **kube-proxy** — Network proxy
875
+ - **aws-ebs-csi-driver** — EBS CSI driver (required for EbsStorageClass)
876
+ - **aws-efs-csi-driver** — EFS CSI driver (required for EfsStorageClass)
877
+ - **adot** — AWS Distro for OpenTelemetry (alternative to AdotCollector composite)
878
+ - **aws-guardduty-agent** — Runtime threat detection
879
+
880
+ Configure add-ons via the AWS lexicon (\`@intentius/chant-lexicon-aws\`) CloudFormation resources.
881
+ `,
882
+ triggers: [
883
+ { type: "context", value: "eks" },
884
+ { type: "context", value: "irsa" },
885
+ { type: "context", value: "alb" },
886
+ { type: "context", value: "ebs" },
887
+ { type: "context", value: "efs" },
888
+ { type: "context", value: "fluent-bit" },
889
+ { type: "context", value: "cloudwatch" },
890
+ { type: "context", value: "karpenter" },
891
+ { type: "context", value: "fargate" },
892
+ ],
893
+ preConditions: [
894
+ "chant CLI is installed (chant --version succeeds)",
895
+ "EKS cluster is provisioned",
896
+ "kubectl is configured for the EKS cluster",
897
+ ],
898
+ postConditions: [
899
+ "EKS-specific resources are deployed and functional",
900
+ ],
901
+ parameters: [],
902
+ examples: [
903
+ {
904
+ title: "IRSA ServiceAccount",
905
+ description: "Create a ServiceAccount with IAM role for S3 access",
906
+ input: "Create an IRSA ServiceAccount for my app that needs S3 access",
907
+ output: `import { IrsaServiceAccount } from "@intentius/chant-lexicon-k8s";
908
+
909
+ const { serviceAccount } = IrsaServiceAccount({
910
+ name: "app-sa",
911
+ iamRoleArn: "arn:aws:iam::123456789012:role/app-s3-role",
912
+ namespace: "prod",
913
+ });`,
914
+ },
915
+ {
916
+ title: "ALB Ingress with TLS",
917
+ description: "Create an ALB Ingress with ACM certificate",
918
+ input: "Set up an internet-facing ALB with TLS for my API",
919
+ output: `import { AlbIngress } from "@intentius/chant-lexicon-k8s";
920
+
921
+ const { ingress } = AlbIngress({
922
+ name: "api-ingress",
923
+ hosts: [{ hostname: "api.example.com", paths: [{ path: "/", serviceName: "api", servicePort: 80 }] }],
924
+ certificateArn: "arn:aws:acm:us-east-1:123456789012:certificate/abc-123",
925
+ });`,
926
+ },
927
+ ],
928
+ },
929
+ {
930
+ name: "chant-k8s-patterns",
931
+ description: "Advanced K8s patterns — sidecars, observability, TLS, network isolation, config/secret mounting",
932
+ content: `---
933
+ skill: chant-k8s-patterns
934
+ description: Advanced Kubernetes deployment patterns and composites
935
+ user-invocable: true
936
+ ---
937
+
938
+ # Advanced Kubernetes Patterns
939
+
940
+ ## Sidecar Patterns
941
+
942
+ ### SidecarApp — multi-container Deployment
943
+
944
+ \`\`\`typescript
945
+ import { SidecarApp } from "@intentius/chant-lexicon-k8s";
946
+
947
+ // Envoy sidecar proxy
948
+ const { deployment, service } = SidecarApp({
949
+ name: "api",
950
+ image: "api:1.0",
951
+ port: 8080,
952
+ sidecars: [
953
+ {
954
+ name: "envoy",
955
+ image: "envoyproxy/envoy:v1.28",
956
+ ports: [{ containerPort: 9901, name: "admin" }],
957
+ resources: { requests: { cpu: "100m", memory: "128Mi" }, limits: { cpu: "200m", memory: "256Mi" } },
958
+ },
959
+ ],
960
+ initContainers: [
961
+ { name: "migrate", image: "api:1.0", command: ["python", "manage.py", "migrate"] },
962
+ ],
963
+ sharedVolumes: [{ name: "tmp", emptyDir: {} }],
964
+ });
965
+ \`\`\`
966
+
967
+ Common sidecar use cases:
968
+ - **Envoy proxy** — service mesh, mTLS, traffic management
969
+ - **Log forwarder** — Fluent Bit sidecar for app-specific log routing
970
+ - **Auth proxy** — OAuth2 Proxy for authentication
971
+ - **Config watcher** — reload config on ConfigMap changes
972
+
973
+ ## Config and Secret Mounting
974
+
975
+ ### ConfiguredApp — automatic volume wiring
976
+
977
+ \`\`\`typescript
978
+ import { ConfiguredApp } from "@intentius/chant-lexicon-k8s";
979
+
980
+ const { deployment, service, configMap } = ConfiguredApp({
981
+ name: "api",
982
+ image: "api:1.0",
983
+ port: 8080,
984
+ // Mount ConfigMap as volume
985
+ configData: { "app.conf": "key=value\\nother=setting" },
986
+ configMountPath: "/etc/api",
987
+ // Mount existing Secret as volume
988
+ secretName: "api-creds",
989
+ secretMountPath: "/secrets",
990
+ // Inject as environment variables
991
+ envFrom: { secretRef: "api-env-secret", configMapRef: "api-env-config" },
992
+ // Run migrations before the app starts
993
+ initContainers: [
994
+ { name: "migrate", image: "api:1.0", command: ["./migrate.sh"] },
995
+ ],
996
+ });
997
+ \`\`\`
998
+
999
+ ### Volume patterns
1000
+
1001
+ | Pattern | Use Case | ConfiguredApp Props |
1002
+ |---------|----------|---------------------|
1003
+ | ConfigMap as file | Config files, templates | \`configData\` + \`configMountPath\` |
1004
+ | Secret as file | TLS certs, credentials | \`secretName\` + \`secretMountPath\` |
1005
+ | ConfigMap as env | Simple key-value config | \`envFrom.configMapRef\` |
1006
+ | Secret as env | Database URLs, API keys | \`envFrom.secretRef\` |
1007
+
1008
+ ## TLS / cert-manager
1009
+
1010
+ ### SecureIngress — multi-host TLS with cert-manager
1011
+
1012
+ \`\`\`typescript
1013
+ import { SecureIngress } from "@intentius/chant-lexicon-k8s";
1014
+
1015
+ const { ingress, certificate } = SecureIngress({
1016
+ name: "app-ingress",
1017
+ hosts: [
1018
+ {
1019
+ hostname: "api.example.com",
1020
+ paths: [
1021
+ { path: "/v1", serviceName: "api-v1", servicePort: 80 },
1022
+ { path: "/v2", serviceName: "api-v2", servicePort: 80 },
1023
+ ],
1024
+ },
1025
+ {
1026
+ hostname: "admin.example.com",
1027
+ paths: [{ path: "/", serviceName: "admin", servicePort: 80 }],
1028
+ },
1029
+ ],
1030
+ clusterIssuer: "letsencrypt-prod",
1031
+ ingressClassName: "nginx",
1032
+ });
1033
+ \`\`\`
1034
+
1035
+ Features:
1036
+ - Multiple hosts and paths per Ingress
1037
+ - Automatic cert-manager Certificate when \`clusterIssuer\` set
1038
+ - TLS secret auto-provisioned by cert-manager
1039
+
1040
+ ### cert-manager setup (prerequisite)
1041
+
1042
+ \`\`\`bash
1043
+ # Install cert-manager
1044
+ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
1045
+
1046
+ # Create ClusterIssuer for Let's Encrypt
1047
+ kubectl apply -f - <<EOF
1048
+ apiVersion: cert-manager.io/v1
1049
+ kind: ClusterIssuer
1050
+ metadata:
1051
+ name: letsencrypt-prod
1052
+ spec:
1053
+ acme:
1054
+ server: https://acme-v02.api.letsencrypt.org/directory
1055
+ email: admin@example.com
1056
+ privateKeySecretRef:
1057
+ name: letsencrypt-prod-key
1058
+ solvers:
1059
+ - http01:
1060
+ ingress:
1061
+ class: nginx
1062
+ EOF
1063
+ \`\`\`
1064
+
1065
+ ## Observability
1066
+
1067
+ ### MonitoredService — Prometheus monitoring
1068
+
1069
+ \`\`\`typescript
1070
+ import { MonitoredService } from "@intentius/chant-lexicon-k8s";
1071
+
1072
+ const { deployment, service, serviceMonitor, prometheusRule } = MonitoredService({
1073
+ name: "api",
1074
+ image: "api:1.0",
1075
+ port: 8080,
1076
+ metricsPort: 9090,
1077
+ metricsPath: "/metrics",
1078
+ scrapeInterval: "15s",
1079
+ alertRules: [
1080
+ {
1081
+ name: "HighErrorRate",
1082
+ expr: 'rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05',
1083
+ for: "5m",
1084
+ severity: "critical",
1085
+ annotations: { summary: "High error rate on {{ $labels.instance }}" },
1086
+ },
1087
+ {
1088
+ name: "HighLatency",
1089
+ expr: 'histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 1',
1090
+ for: "10m",
1091
+ severity: "warning",
1092
+ },
1093
+ ],
1094
+ });
1095
+ \`\`\`
1096
+
1097
+ ### Prerequisites
1098
+
1099
+ - Prometheus Operator installed (for ServiceMonitor/PrometheusRule CRDs)
1100
+ - Prometheus configured to discover ServiceMonitors
1101
+
1102
+ ## Network Isolation
1103
+
1104
+ ### NetworkIsolatedApp — per-app firewall rules
1105
+
1106
+ \`\`\`typescript
1107
+ import { NetworkIsolatedApp } from "@intentius/chant-lexicon-k8s";
1108
+
1109
+ const { deployment, service, networkPolicy } = NetworkIsolatedApp({
1110
+ name: "api",
1111
+ image: "api:1.0",
1112
+ port: 8080,
1113
+ allowIngressFrom: [
1114
+ { podSelector: { "app.kubernetes.io/name": "frontend" } },
1115
+ { namespaceSelector: { "kubernetes.io/metadata.name": "monitoring" } },
1116
+ ],
1117
+ allowEgressTo: [
1118
+ { podSelector: { "app.kubernetes.io/name": "postgres" }, ports: [{ port: 5432 }] },
1119
+ { podSelector: { "app.kubernetes.io/name": "redis" }, ports: [{ port: 6379 }] },
1120
+ ],
1121
+ });
1122
+ \`\`\`
1123
+
1124
+ ### Combining with NamespaceEnv
1125
+
1126
+ Use NamespaceEnv for namespace-level default-deny, then NetworkIsolatedApp for per-app allow rules:
1127
+
1128
+ \`\`\`typescript
1129
+ // Namespace: deny all by default
1130
+ const ns = NamespaceEnv({ name: "prod", defaultDenyIngress: true, defaultDenyEgress: true });
1131
+
1132
+ // App: allow specific traffic
1133
+ const app = NetworkIsolatedApp({
1134
+ name: "api",
1135
+ image: "api:1.0",
1136
+ namespace: "prod",
1137
+ allowIngressFrom: [{ podSelector: { "app.kubernetes.io/name": "gateway" } }],
1138
+ allowEgressTo: [{ podSelector: { "app.kubernetes.io/name": "db" }, ports: [{ port: 5432 }] }],
1139
+ });
1140
+ \`\`\`
1141
+
1142
+ ## Blue/Green and Canary
1143
+
1144
+ These patterns use standard K8s resources — no special composite needed.
1145
+
1146
+ ### Blue/Green
1147
+
1148
+ \`\`\`typescript
1149
+ // Two Deployments with different versions
1150
+ const blue = WebApp({ name: "app-blue", image: "app:1.0", labels: { version: "blue" } });
1151
+ const green = WebApp({ name: "app-green", image: "app:2.0", labels: { version: "green" } });
1152
+
1153
+ // Service points to active version — switch by changing selector
1154
+ // Active: blue → green (update the Service selector)
1155
+ \`\`\`
1156
+
1157
+ ### Canary
1158
+
1159
+ \`\`\`typescript
1160
+ // Main deployment (90% traffic)
1161
+ const main = AutoscaledService({ name: "app", image: "app:1.0", minReplicas: 9, maxReplicas: 20, ... });
1162
+
1163
+ // Canary deployment (10% traffic) — same app label, fewer replicas
1164
+ const canary = WebApp({ name: "app-canary", image: "app:2.0", replicas: 1, labels: { track: "canary" } });
1165
+ // Both share the same Service selector ("app.kubernetes.io/name": "app") for traffic splitting
1166
+ \`\`\`
1167
+
1168
+ ## Gateway API (future direction)
1169
+
1170
+ Gateway API is the successor to Ingress. Key differences:
1171
+ - **HTTPRoute** replaces Ingress rules
1172
+ - **Gateway** replaces IngressClass
1173
+ - Built-in traffic splitting, header matching, URL rewriting
1174
+ - Currently in beta — use Ingress/SecureIngress/AlbIngress for production today
1175
+
1176
+ When Gateway API reaches GA, new composites will be added. For now, use CRD import if you need Gateway API resources.
1177
+ `,
1178
+ triggers: [
1179
+ { type: "context", value: "sidecar" },
1180
+ { type: "context", value: "init-container" },
1181
+ { type: "context", value: "prometheus" },
1182
+ { type: "context", value: "cert-manager" },
1183
+ { type: "context", value: "tls" },
1184
+ { type: "context", value: "network-policy" },
1185
+ { type: "context", value: "canary" },
1186
+ { type: "context", value: "blue-green" },
1187
+ { type: "context", value: "configmap-mount" },
1188
+ { type: "context", value: "secret-mount" },
1189
+ ],
1190
+ preConditions: [
1191
+ "chant CLI is installed (chant --version succeeds)",
1192
+ "kubectl is configured and can access the cluster",
1193
+ ],
1194
+ postConditions: [
1195
+ "Pattern resources are deployed and functional",
1196
+ ],
1197
+ parameters: [],
1198
+ examples: [
1199
+ {
1200
+ title: "Sidecar with envoy proxy",
1201
+ description: "Deploy an app with an Envoy sidecar",
1202
+ input: "Add an envoy sidecar proxy to my API",
1203
+ output: `import { SidecarApp } from "@intentius/chant-lexicon-k8s";
1204
+
1205
+ const { deployment, service } = SidecarApp({
1206
+ name: "api",
1207
+ image: "api:1.0",
1208
+ port: 8080,
1209
+ sidecars: [{ name: "envoy", image: "envoyproxy/envoy:v1.28", ports: [{ containerPort: 9901 }] }],
1210
+ });`,
1211
+ },
1212
+ {
1213
+ title: "Monitored service with alerts",
1214
+ description: "Deploy a service with Prometheus monitoring and alerting",
1215
+ input: "Set up monitoring for my API with error rate alerts",
1216
+ output: `import { MonitoredService } from "@intentius/chant-lexicon-k8s";
1217
+
1218
+ const { deployment, service, serviceMonitor, prometheusRule } = MonitoredService({
1219
+ name: "api",
1220
+ image: "api:1.0",
1221
+ metricsPort: 9090,
1222
+ alertRules: [{ name: "HighErrorRate", expr: 'rate(http_errors[5m]) > 0.1', severity: "critical" }],
909
1223
  });`,
910
1224
  },
911
1225
  ],