@intentius/chant-lexicon-k8s 0.0.14 → 0.0.16

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 (63) hide show
  1. package/dist/integrity.json +8 -4
  2. package/dist/manifest.json +1 -1
  3. package/dist/rules/latest-image-tag.ts +121 -0
  4. package/dist/rules/missing-resource-limits.ts +111 -0
  5. package/dist/rules/wk8204.ts +33 -1
  6. package/dist/rules/wk8304.ts +70 -0
  7. package/dist/rules/wk8305.ts +115 -0
  8. package/dist/rules/wk8306.ts +50 -0
  9. package/package.json +27 -24
  10. package/src/codegen/docs.ts +1 -1
  11. package/src/composites/adot-collector.ts +8 -2
  12. package/src/composites/agic-ingress.ts +148 -0
  13. package/src/composites/aks-external-dns-agent.ts +199 -0
  14. package/src/composites/alb-ingress.ts +2 -1
  15. package/src/composites/autoscaled-service.ts +25 -7
  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 +36 -3
  20. package/src/composites/composites.test.ts +1060 -0
  21. package/src/composites/config-connector-context.ts +62 -0
  22. package/src/composites/configured-app.ts +6 -0
  23. package/src/composites/cron-workload.ts +6 -0
  24. package/src/composites/ebs-storage-class.ts +4 -4
  25. package/src/composites/external-dns-agent.ts +6 -0
  26. package/src/composites/filestore-storage-class.ts +79 -0
  27. package/src/composites/fluent-bit-agent.ts +5 -0
  28. package/src/composites/gce-ingress.ts +143 -0
  29. package/src/composites/gce-pd-storage-class.ts +85 -0
  30. package/src/composites/gke-external-dns-agent.ts +175 -0
  31. package/src/composites/gke-fluent-bit-agent.ts +219 -0
  32. package/src/composites/gke-gateway.ts +143 -0
  33. package/src/composites/gke-otel-collector.ts +229 -0
  34. package/src/composites/index.ts +31 -0
  35. package/src/composites/metrics-server.ts +1 -1
  36. package/src/composites/monitored-service.ts +6 -0
  37. package/src/composites/network-isolated-app.ts +6 -0
  38. package/src/composites/node-agent.ts +6 -0
  39. package/src/composites/security-context.ts +10 -0
  40. package/src/composites/sidecar-app.ts +6 -0
  41. package/src/composites/stateful-app.ts +4 -7
  42. package/src/composites/web-app.ts +4 -7
  43. package/src/composites/worker-pool.ts +4 -7
  44. package/src/composites/workload-identity-sa.ts +118 -0
  45. package/src/composites/workload-identity-service-account.ts +116 -0
  46. package/src/index.ts +20 -1
  47. package/src/lint/post-synth/post-synth.test.ts +362 -1
  48. package/src/lint/post-synth/wk8204.ts +33 -1
  49. package/src/lint/post-synth/wk8304.ts +70 -0
  50. package/src/lint/post-synth/wk8305.ts +115 -0
  51. package/src/lint/post-synth/wk8306.ts +50 -0
  52. package/src/lint/rules/latest-image-tag.ts +121 -0
  53. package/src/lint/rules/missing-resource-limits.ts +111 -0
  54. package/src/lint/rules/rules.test.ts +192 -0
  55. package/src/plugin.test.ts +2 -2
  56. package/src/plugin.ts +129 -209
  57. package/src/serializer.test.ts +120 -0
  58. package/src/serializer.ts +16 -4
  59. package/src/skills/chant-k8s-aks.md +146 -0
  60. package/src/skills/chant-k8s-gke.md +191 -0
  61. package/src/skills/kubernetes-patterns.md +183 -0
  62. package/src/skills/kubernetes-security.md +237 -0
  63. /package/{dist → src}/skills/chant-k8s-eks.md +0 -0
@@ -0,0 +1,62 @@
1
+ /**
2
+ * ConfigConnectorContext composite — bootstrap Config Connector per-namespace context.
3
+ *
4
+ * @gke Creates a ConfigConnectorContext resource that configures
5
+ * Config Connector to manage GCP resources in a specific namespace.
6
+ */
7
+
8
+ export interface ConfigConnectorContextProps {
9
+ /** Context name (default: "configconnectorcontext.core.cnrm.cloud.google.com"). */
10
+ name?: string;
11
+ /** Google service account email for Config Connector to use. */
12
+ googleServiceAccountEmail: string;
13
+ /** Namespace for the context (default: "default"). */
14
+ namespace?: string;
15
+ /** Whether to sync status into spec (default: "absent"). */
16
+ stateIntoSpec?: "absent" | "merge";
17
+ }
18
+
19
+ export interface ConfigConnectorContextResult {
20
+ context: Record<string, unknown>;
21
+ }
22
+
23
+ /**
24
+ * Create a ConfigConnectorContext composite — returns prop objects for
25
+ * a ConfigConnectorContext resource.
26
+ *
27
+ * @gke
28
+ * @example
29
+ * ```ts
30
+ * import { ConfigConnectorContext } from "@intentius/chant-lexicon-k8s";
31
+ *
32
+ * const { context } = ConfigConnectorContext({
33
+ * googleServiceAccountEmail: "cnrm@my-project.iam.gserviceaccount.com",
34
+ * namespace: "config-connector",
35
+ * });
36
+ * ```
37
+ */
38
+ export function ConfigConnectorContext(
39
+ props: ConfigConnectorContextProps,
40
+ ): ConfigConnectorContextResult {
41
+ const {
42
+ name = "configconnectorcontext.core.cnrm.cloud.google.com",
43
+ googleServiceAccountEmail,
44
+ namespace = "default",
45
+ stateIntoSpec = "absent",
46
+ } = props;
47
+
48
+ const contextProps: Record<string, unknown> = {
49
+ apiVersion: "core.cnrm.cloud.google.com/v1beta1",
50
+ kind: "ConfigConnectorContext",
51
+ metadata: {
52
+ name,
53
+ namespace,
54
+ },
55
+ spec: {
56
+ googleServiceAccount: googleServiceAccountEmail,
57
+ stateIntoSpec,
58
+ },
59
+ };
60
+
61
+ return { context: contextProps };
62
+ }
@@ -6,6 +6,8 @@
6
6
  * Volume.fromSecret(), and container.mount() patterns.
7
7
  */
8
8
 
9
+ import type { ContainerSecurityContext } from "./security-context";
10
+
9
11
  export interface ConfiguredAppProps {
10
12
  /** Application name — used in metadata and labels. */
11
13
  name: string;
@@ -49,6 +51,8 @@ export interface ConfiguredAppProps {
49
51
  namespace?: string;
50
52
  /** Environment variables for the container. */
51
53
  env?: Array<{ name: string; value: string }>;
54
+ /** Container security context (supports PSS restricted fields). */
55
+ securityContext?: ContainerSecurityContext;
52
56
  }
53
57
 
54
58
  export interface ConfiguredAppResult {
@@ -96,6 +100,7 @@ export function ConfiguredApp(props: ConfiguredAppProps): ConfiguredAppResult {
96
100
  memoryRequest = "128Mi",
97
101
  namespace,
98
102
  env,
103
+ securityContext,
99
104
  } = props;
100
105
 
101
106
  const configMapName = `${name}-config`;
@@ -154,6 +159,7 @@ export function ConfiguredApp(props: ConfiguredAppProps): ConfiguredAppResult {
154
159
  ...(env && { env }),
155
160
  ...(envFromList.length > 0 && { envFrom: envFromList }),
156
161
  ...(volumeMounts.length > 0 && { volumeMounts }),
162
+ ...(securityContext && { securityContext }),
157
163
  };
158
164
 
159
165
  const podSpec: Record<string, unknown> = {
@@ -5,6 +5,8 @@
5
5
  * proper RBAC permissions.
6
6
  */
7
7
 
8
+ import type { ContainerSecurityContext } from "./security-context";
9
+
8
10
  export interface CronWorkloadProps {
9
11
  /** Workload name — used in metadata and labels. */
10
12
  name: string;
@@ -33,6 +35,8 @@ export interface CronWorkloadProps {
33
35
  namespace?: string;
34
36
  /** Environment variables. */
35
37
  env?: Array<{ name: string; value: string }>;
38
+ /** Container security context (supports PSS restricted fields). */
39
+ securityContext?: ContainerSecurityContext;
36
40
  }
37
41
 
38
42
  export interface CronWorkloadResult {
@@ -75,6 +79,7 @@ export function CronWorkload(props: CronWorkloadProps): CronWorkloadResult {
75
79
  labels: extraLabels = {},
76
80
  namespace,
77
81
  env,
82
+ securityContext,
78
83
  } = props;
79
84
 
80
85
  const saName = `${name}-sa`;
@@ -110,6 +115,7 @@ export function CronWorkload(props: CronWorkloadProps): CronWorkloadResult {
110
115
  ...(command && { command }),
111
116
  ...(args && { args }),
112
117
  ...(env && { env }),
118
+ ...(securityContext && { securityContext }),
113
119
  },
114
120
  ],
115
121
  },
@@ -10,9 +10,9 @@ export interface EbsStorageClassProps {
10
10
  /** EBS volume type (default: "gp3"). */
11
11
  type?: string;
12
12
  /** IOPS for io1/io2/gp3 volumes. */
13
- iops?: string;
13
+ iops?: string | number;
14
14
  /** Throughput for gp3 volumes (MiB/s). */
15
- throughput?: string;
15
+ throughput?: string | number;
16
16
  /** Enable encryption (default: true). */
17
17
  encrypted?: boolean;
18
18
  /** KMS key ID for encryption. */
@@ -76,8 +76,8 @@ export function EbsStorageClass(props: EbsStorageClassProps): EbsStorageClassRes
76
76
  encrypted: String(encrypted),
77
77
  };
78
78
 
79
- if (iops) parameters.iops = iops;
80
- if (throughput) parameters.throughput = throughput;
79
+ if (iops !== undefined) parameters.iops = String(iops);
80
+ if (throughput !== undefined) parameters.throughput = String(throughput);
81
81
  if (kmsKeyId) parameters.kmsKeyId = kmsKeyId;
82
82
 
83
83
  const storageClassProps: Record<string, unknown> = {
@@ -110,6 +110,12 @@ export function ExternalDnsAgent(props: ExternalDnsAgentProps): ExternalDnsAgent
110
110
  requests: { cpu: "50m", memory: "64Mi" },
111
111
  limits: { cpu: "100m", memory: "128Mi" },
112
112
  },
113
+ securityContext: {
114
+ runAsNonRoot: true,
115
+ runAsUser: 65534,
116
+ readOnlyRootFilesystem: true,
117
+ allowPrivilegeEscalation: false,
118
+ },
113
119
  },
114
120
  ],
115
121
  },
@@ -0,0 +1,79 @@
1
+ /**
2
+ * FilestoreStorageClass composite — StorageClass for GCP Filestore CSI driver.
3
+ *
4
+ * @gke Creates a StorageClass with the `filestore.csi.storage.gke.io` provisioner.
5
+ * Filestore provides ReadWriteMany access mode (shared across pods/nodes).
6
+ */
7
+
8
+ export interface FilestoreStorageClassProps {
9
+ /** StorageClass name. */
10
+ name: string;
11
+ /** Filestore tier (default: "standard"). */
12
+ tier?: "standard" | "premium" | "enterprise";
13
+ /** VPC network for the Filestore instance. */
14
+ network?: string;
15
+ /** Reclaim policy (default: "Delete"). */
16
+ reclaimPolicy?: string;
17
+ /** Volume binding mode (default: "WaitForFirstConsumer"). */
18
+ volumeBindingMode?: string;
19
+ /** Additional labels. */
20
+ labels?: Record<string, string>;
21
+ }
22
+
23
+ export interface FilestoreStorageClassResult {
24
+ storageClass: Record<string, unknown>;
25
+ }
26
+
27
+ /**
28
+ * Create a FilestoreStorageClass composite — returns prop objects for
29
+ * a StorageClass with the GCP Filestore CSI provisioner.
30
+ *
31
+ * @gke
32
+ * @example
33
+ * ```ts
34
+ * import { FilestoreStorageClass } from "@intentius/chant-lexicon-k8s";
35
+ *
36
+ * const { storageClass } = FilestoreStorageClass({
37
+ * name: "filestore-standard",
38
+ * tier: "standard",
39
+ * network: "default",
40
+ * });
41
+ * ```
42
+ */
43
+ export function FilestoreStorageClass(props: FilestoreStorageClassProps): FilestoreStorageClassResult {
44
+ const {
45
+ name,
46
+ tier = "standard",
47
+ network,
48
+ reclaimPolicy = "Delete",
49
+ volumeBindingMode = "WaitForFirstConsumer",
50
+ labels: extraLabels = {},
51
+ } = props;
52
+
53
+ const commonLabels: Record<string, string> = {
54
+ "app.kubernetes.io/name": name,
55
+ "app.kubernetes.io/managed-by": "chant",
56
+ ...extraLabels,
57
+ };
58
+
59
+ const parameters: Record<string, string> = {
60
+ tier,
61
+ };
62
+
63
+ if (network) {
64
+ parameters.network = network;
65
+ }
66
+
67
+ const storageClassProps: Record<string, unknown> = {
68
+ metadata: {
69
+ name,
70
+ labels: { ...commonLabels, "app.kubernetes.io/component": "storage" },
71
+ },
72
+ provisioner: "filestore.csi.storage.gke.io",
73
+ parameters,
74
+ reclaimPolicy,
75
+ volumeBindingMode,
76
+ };
77
+
78
+ return { storageClass: storageClassProps };
79
+ }
@@ -130,6 +130,11 @@ export function FluentBitAgent(props: FluentBitAgentProps): FluentBitAgentResult
130
130
  { name: "config", mountPath: `/etc/${name}`, readOnly: true },
131
131
  { name: "state", mountPath: "/var/fluent-bit/state" },
132
132
  ],
133
+ securityContext: {
134
+ runAsUser: 0,
135
+ readOnlyRootFilesystem: true,
136
+ allowPrivilegeEscalation: false,
137
+ },
133
138
  };
134
139
 
135
140
  const daemonSetProps: Record<string, unknown> = {
@@ -0,0 +1,143 @@
1
+ /**
2
+ * GceIngress composite — Ingress with GCE Ingress Controller annotations.
3
+ *
4
+ * @gke Full `kubernetes.io/ingress.*` and `networking.gke.io/*` annotation set
5
+ * including static IP, managed certificates, and FrontendConfig.
6
+ */
7
+
8
+ export interface GceIngressHost {
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 GceIngressProps {
22
+ /** Ingress name — used in metadata and labels. */
23
+ name: string;
24
+ /** Host definitions with paths. */
25
+ hosts: GceIngressHost[];
26
+ /** Global static IP name reserved in GCP (sets `kubernetes.io/ingress.global-static-ip-name`). */
27
+ staticIpName?: string;
28
+ /** GKE managed certificate name (sets `networking.gke.io/managed-certificates`). */
29
+ managedCertificate?: string;
30
+ /** FrontendConfig name for SSL policy / redirects (sets `networking.gke.io/v1beta1.FrontendConfig`). */
31
+ frontendConfig?: string;
32
+ /** Health check path for backend (sets `cloud.google.com/backend-config` is NOT handled here — use BackendConfig CRD). */
33
+ healthCheckPath?: string;
34
+ /** Enable HTTP->HTTPS redirect (default: true when managedCertificate set). */
35
+ sslRedirect?: boolean;
36
+ /** Additional annotations on the Ingress. */
37
+ annotations?: Record<string, string>;
38
+ /** Additional labels to apply to all resources. */
39
+ labels?: Record<string, string>;
40
+ /** Namespace for all resources. */
41
+ namespace?: string;
42
+ }
43
+
44
+ export interface GceIngressResult {
45
+ ingress: Record<string, unknown>;
46
+ }
47
+
48
+ /**
49
+ * Create a GceIngress composite — returns prop objects for
50
+ * an Ingress with GCE Ingress Controller annotations.
51
+ *
52
+ * @gke
53
+ * @example
54
+ * ```ts
55
+ * import { GceIngress } from "@intentius/chant-lexicon-k8s";
56
+ *
57
+ * const { ingress } = GceIngress({
58
+ * name: "api-ingress",
59
+ * hosts: [
60
+ * {
61
+ * hostname: "api.example.com",
62
+ * paths: [{ path: "/", serviceName: "api", servicePort: 80 }],
63
+ * },
64
+ * ],
65
+ * staticIpName: "microservice-ip",
66
+ * managedCertificate: "api-cert",
67
+ * });
68
+ * ```
69
+ */
70
+ export function GceIngress(props: GceIngressProps): GceIngressResult {
71
+ const {
72
+ name,
73
+ hosts,
74
+ staticIpName,
75
+ managedCertificate,
76
+ frontendConfig,
77
+ healthCheckPath,
78
+ sslRedirect,
79
+ annotations: extraAnnotations = {},
80
+ labels: extraLabels = {},
81
+ namespace,
82
+ } = props;
83
+
84
+ const commonLabels: Record<string, string> = {
85
+ "app.kubernetes.io/name": name,
86
+ "app.kubernetes.io/managed-by": "chant",
87
+ ...extraLabels,
88
+ };
89
+
90
+ // Build GCE annotations
91
+ const annotations: Record<string, string> = {
92
+ "kubernetes.io/ingress.class": "gce",
93
+ ...extraAnnotations,
94
+ };
95
+
96
+ if (staticIpName) {
97
+ annotations["kubernetes.io/ingress.global-static-ip-name"] = staticIpName;
98
+ }
99
+
100
+ if (managedCertificate) {
101
+ annotations["networking.gke.io/managed-certificates"] = managedCertificate;
102
+ }
103
+
104
+ if (frontendConfig) {
105
+ annotations["networking.gke.io/v1beta1.FrontendConfig"] = frontendConfig;
106
+ }
107
+
108
+ if (sslRedirect ?? !!managedCertificate) {
109
+ annotations["networking.gke.io/v1beta1.FrontendConfig"] =
110
+ annotations["networking.gke.io/v1beta1.FrontendConfig"] ?? `${name}-frontend-config`;
111
+ }
112
+
113
+ if (healthCheckPath) {
114
+ annotations["cloud.google.com/neg"] = '{"ingress": true}';
115
+ }
116
+
117
+ const ingressRules = hosts.map((host) => ({
118
+ host: host.hostname,
119
+ http: {
120
+ paths: host.paths.map((p) => ({
121
+ path: p.path,
122
+ pathType: p.pathType ?? "Prefix",
123
+ backend: {
124
+ service: { name: p.serviceName, port: { number: p.servicePort } },
125
+ },
126
+ })),
127
+ },
128
+ }));
129
+
130
+ const ingressProps: Record<string, unknown> = {
131
+ metadata: {
132
+ name,
133
+ ...(namespace && { namespace }),
134
+ labels: { ...commonLabels, "app.kubernetes.io/component": "ingress" },
135
+ annotations,
136
+ },
137
+ spec: {
138
+ rules: ingressRules,
139
+ },
140
+ };
141
+
142
+ return { ingress: ingressProps };
143
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * GcePdStorageClass composite — StorageClass for GCE Persistent Disk CSI driver.
3
+ *
4
+ * @gke Creates a StorageClass with the `pd.csi.storage.gke.io` provisioner.
5
+ */
6
+
7
+ export interface GcePdStorageClassProps {
8
+ /** StorageClass name. */
9
+ name: string;
10
+ /** PD type (default: "pd-balanced"). */
11
+ type?: "pd-standard" | "pd-ssd" | "pd-balanced" | "pd-extreme";
12
+ /** Replication type (default: "none"). */
13
+ replicationType?: "none" | "regional-pd";
14
+ /** Filesystem type (default: "ext4"). */
15
+ fsType?: string;
16
+ /** Reclaim policy (default: "Delete"). */
17
+ reclaimPolicy?: string;
18
+ /** Volume binding mode (default: "WaitForFirstConsumer"). */
19
+ volumeBindingMode?: string;
20
+ /** Allow volume expansion (default: true). */
21
+ allowVolumeExpansion?: boolean;
22
+ /** Additional labels. */
23
+ labels?: Record<string, string>;
24
+ }
25
+
26
+ export interface GcePdStorageClassResult {
27
+ storageClass: Record<string, unknown>;
28
+ }
29
+
30
+ /**
31
+ * Create a GcePdStorageClass composite — returns prop objects for
32
+ * a StorageClass with the GCE Persistent Disk CSI provisioner.
33
+ *
34
+ * @gke
35
+ * @example
36
+ * ```ts
37
+ * import { GcePdStorageClass } from "@intentius/chant-lexicon-k8s";
38
+ *
39
+ * const { storageClass } = GcePdStorageClass({
40
+ * name: "pd-ssd",
41
+ * type: "pd-ssd",
42
+ * });
43
+ * ```
44
+ */
45
+ export function GcePdStorageClass(props: GcePdStorageClassProps): GcePdStorageClassResult {
46
+ const {
47
+ name,
48
+ type = "pd-balanced",
49
+ replicationType = "none",
50
+ fsType = "ext4",
51
+ reclaimPolicy = "Delete",
52
+ volumeBindingMode = "WaitForFirstConsumer",
53
+ allowVolumeExpansion = true,
54
+ labels: extraLabels = {},
55
+ } = props;
56
+
57
+ const commonLabels: Record<string, string> = {
58
+ "app.kubernetes.io/name": name,
59
+ "app.kubernetes.io/managed-by": "chant",
60
+ ...extraLabels,
61
+ };
62
+
63
+ const parameters: Record<string, string> = {
64
+ type,
65
+ "csi.storage.k8s.io/fstype": fsType,
66
+ };
67
+
68
+ if (replicationType !== "none") {
69
+ parameters["replication-type"] = replicationType;
70
+ }
71
+
72
+ const storageClassProps: Record<string, unknown> = {
73
+ metadata: {
74
+ name,
75
+ labels: { ...commonLabels, "app.kubernetes.io/component": "storage" },
76
+ },
77
+ provisioner: "pd.csi.storage.gke.io",
78
+ parameters,
79
+ reclaimPolicy,
80
+ volumeBindingMode,
81
+ allowVolumeExpansion,
82
+ };
83
+
84
+ return { storageClass: storageClassProps };
85
+ }
@@ -0,0 +1,175 @@
1
+ /**
2
+ * GkeExternalDnsAgent composite — Deployment + ServiceAccount + ClusterRole + ClusterRoleBinding.
3
+ *
4
+ * @gke Like ExternalDnsAgent but uses --provider=google and GKE Workload Identity
5
+ * instead of IRSA for Cloud DNS management.
6
+ */
7
+
8
+ export interface GkeExternalDnsAgentProps {
9
+ /** GCP service account email for Workload Identity (needs Cloud DNS permissions). */
10
+ gcpServiceAccountEmail: string;
11
+ /** GCP project ID. */
12
+ gcpProjectId: string;
13
+ /** Domain filters — only manage DNS records for these domains. */
14
+ domainFilters: string[];
15
+ /** TXT record owner ID for identifying managed records. */
16
+ txtOwnerId?: string;
17
+ /** Source of DNS records (default: "ingress"). */
18
+ source?: string;
19
+ /** Agent name (default: "external-dns"). */
20
+ name?: string;
21
+ /** Container image (default: "registry.k8s.io/external-dns/external-dns:v0.14.0"). */
22
+ image?: string;
23
+ /** Namespace (default: "kube-system"). */
24
+ namespace?: string;
25
+ /** Additional labels. */
26
+ labels?: Record<string, string>;
27
+ }
28
+
29
+ export interface GkeExternalDnsAgentResult {
30
+ deployment: Record<string, unknown>;
31
+ serviceAccount: Record<string, unknown>;
32
+ clusterRole: Record<string, unknown>;
33
+ clusterRoleBinding: Record<string, unknown>;
34
+ }
35
+
36
+ /**
37
+ * Create a GkeExternalDnsAgent composite — returns prop objects for
38
+ * a Deployment, ServiceAccount (with Workload Identity), ClusterRole, and ClusterRoleBinding.
39
+ *
40
+ * @gke
41
+ * @example
42
+ * ```ts
43
+ * import { GkeExternalDnsAgent } from "@intentius/chant-lexicon-k8s";
44
+ *
45
+ * const { deployment, serviceAccount, clusterRole, clusterRoleBinding } = GkeExternalDnsAgent({
46
+ * gcpServiceAccountEmail: "external-dns@my-project.iam.gserviceaccount.com",
47
+ * gcpProjectId: "my-project",
48
+ * domainFilters: ["example.com"],
49
+ * txtOwnerId: "my-cluster",
50
+ * });
51
+ * ```
52
+ */
53
+ export function GkeExternalDnsAgent(props: GkeExternalDnsAgentProps): GkeExternalDnsAgentResult {
54
+ const {
55
+ gcpServiceAccountEmail,
56
+ gcpProjectId,
57
+ domainFilters,
58
+ txtOwnerId,
59
+ source = "ingress",
60
+ name = "external-dns",
61
+ image = "registry.k8s.io/external-dns/external-dns:v0.14.0",
62
+ namespace = "kube-system",
63
+ labels: extraLabels = {},
64
+ } = props;
65
+
66
+ const saName = `${name}-sa`;
67
+ const clusterRoleName = `${name}-role`;
68
+ const bindingName = `${name}-binding`;
69
+
70
+ const commonLabels: Record<string, string> = {
71
+ "app.kubernetes.io/name": name,
72
+ "app.kubernetes.io/managed-by": "chant",
73
+ ...extraLabels,
74
+ };
75
+
76
+ const args: string[] = [
77
+ `--source=${source}`,
78
+ "--provider=google",
79
+ `--google-project=${gcpProjectId}`,
80
+ "--policy=upsert-only",
81
+ "--registry=txt",
82
+ ];
83
+
84
+ for (const domain of domainFilters) {
85
+ args.push(`--domain-filter=${domain}`);
86
+ }
87
+
88
+ if (txtOwnerId) {
89
+ args.push(`--txt-owner-id=${txtOwnerId}`);
90
+ }
91
+
92
+ const deploymentProps: Record<string, unknown> = {
93
+ metadata: {
94
+ name,
95
+ namespace,
96
+ labels: { ...commonLabels, "app.kubernetes.io/component": "dns" },
97
+ },
98
+ spec: {
99
+ replicas: 1,
100
+ selector: { matchLabels: { "app.kubernetes.io/name": name } },
101
+ template: {
102
+ metadata: { labels: { "app.kubernetes.io/name": name, ...extraLabels } },
103
+ spec: {
104
+ serviceAccountName: saName,
105
+ containers: [
106
+ {
107
+ name,
108
+ image,
109
+ args,
110
+ resources: {
111
+ requests: { cpu: "50m", memory: "64Mi" },
112
+ limits: { cpu: "100m", memory: "128Mi" },
113
+ },
114
+ securityContext: {
115
+ runAsNonRoot: true,
116
+ runAsUser: 65534,
117
+ readOnlyRootFilesystem: true,
118
+ allowPrivilegeEscalation: false,
119
+ },
120
+ },
121
+ ],
122
+ },
123
+ },
124
+ },
125
+ };
126
+
127
+ const serviceAccountProps: Record<string, unknown> = {
128
+ metadata: {
129
+ name: saName,
130
+ namespace,
131
+ labels: { ...commonLabels, "app.kubernetes.io/component": "dns" },
132
+ annotations: {
133
+ "iam.gke.io/gcp-service-account": gcpServiceAccountEmail,
134
+ },
135
+ },
136
+ };
137
+
138
+ const clusterRoleProps: Record<string, unknown> = {
139
+ metadata: {
140
+ name: clusterRoleName,
141
+ labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
142
+ },
143
+ rules: [
144
+ { apiGroups: [""], resources: ["services", "endpoints", "pods"], verbs: ["get", "watch", "list"] },
145
+ { apiGroups: ["extensions", "networking.k8s.io"], resources: ["ingresses"], verbs: ["get", "watch", "list"] },
146
+ { apiGroups: [""], resources: ["nodes"], verbs: ["list", "watch"] },
147
+ ],
148
+ };
149
+
150
+ const clusterRoleBindingProps: Record<string, unknown> = {
151
+ metadata: {
152
+ name: bindingName,
153
+ labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
154
+ },
155
+ roleRef: {
156
+ apiGroup: "rbac.authorization.k8s.io",
157
+ kind: "ClusterRole",
158
+ name: clusterRoleName,
159
+ },
160
+ subjects: [
161
+ {
162
+ kind: "ServiceAccount",
163
+ name: saName,
164
+ namespace,
165
+ },
166
+ ],
167
+ };
168
+
169
+ return {
170
+ deployment: deploymentProps,
171
+ serviceAccount: serviceAccountProps,
172
+ clusterRole: clusterRoleProps,
173
+ clusterRoleBinding: clusterRoleBindingProps,
174
+ };
175
+ }