@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.
- package/dist/integrity.json +20 -15
- package/dist/manifest.json +1 -1
- package/dist/rules/wk8204.ts +33 -1
- package/dist/rules/wk8304.ts +70 -0
- package/dist/rules/wk8305.ts +115 -0
- package/dist/rules/wk8306.ts +50 -0
- package/dist/skills/chant-k8s-eks.md +156 -0
- package/dist/skills/chant-k8s-patterns.md +245 -0
- package/dist/skills/chant-k8s.md +36 -227
- package/package.json +27 -24
- package/src/codegen/docs.ts +5 -5
- package/src/composites/adot-collector.ts +245 -0
- package/src/composites/agic-ingress.ts +149 -0
- package/src/composites/alb-ingress.ts +152 -0
- package/src/composites/autoscaled-service.ts +51 -0
- package/src/composites/azure-disk-storage-class.ts +82 -0
- package/src/composites/azure-file-storage-class.ts +77 -0
- package/src/composites/azure-monitor-collector.ts +232 -0
- package/src/composites/batch-job.ts +221 -0
- package/src/composites/composites.test.ts +1584 -0
- package/src/composites/config-connector-context.ts +62 -0
- package/src/composites/configured-app.ts +224 -0
- package/src/composites/cron-workload.ts +6 -0
- package/src/composites/ebs-storage-class.ts +96 -0
- package/src/composites/efs-storage-class.ts +77 -0
- package/src/composites/external-dns-agent.ts +174 -0
- package/src/composites/filestore-storage-class.ts +79 -0
- package/src/composites/fluent-bit-agent.ts +220 -0
- package/src/composites/gce-pd-storage-class.ts +85 -0
- package/src/composites/gke-gateway.ts +143 -0
- package/src/composites/index.ts +47 -0
- package/src/composites/irsa-service-account.ts +114 -0
- package/src/composites/metrics-server.ts +224 -0
- package/src/composites/monitored-service.ts +221 -0
- package/src/composites/network-isolated-app.ts +202 -0
- package/src/composites/node-agent.ts +6 -0
- package/src/composites/secure-ingress.ts +149 -0
- package/src/composites/security-context.ts +10 -0
- package/src/composites/sidecar-app.ts +207 -0
- package/src/composites/stateful-app.ts +67 -15
- package/src/composites/web-app.ts +104 -35
- package/src/composites/worker-pool.ts +38 -4
- package/src/composites/workload-identity-sa.ts +118 -0
- package/src/composites/workload-identity-service-account.ts +116 -0
- package/src/index.ts +24 -2
- package/src/lint/post-synth/post-synth.test.ts +362 -1
- package/src/lint/post-synth/wk8204.ts +33 -1
- package/src/lint/post-synth/wk8304.ts +70 -0
- package/src/lint/post-synth/wk8305.ts +115 -0
- package/src/lint/post-synth/wk8306.ts +50 -0
- package/src/plugin.test.ts +2 -2
- package/src/plugin.ts +556 -242
- package/src/serializer.test.ts +120 -0
- package/src/serializer.ts +16 -4
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MetricsServer composite — Deployment + Service + ServiceAccount + RBAC + APIService.
|
|
3
|
+
*
|
|
4
|
+
* Deploys metrics-server for HPA pod CPU/memory metrics. metrics-server is not an
|
|
5
|
+
* EKS managed addon — it needs K8s workloads. Uses in-cluster RBAC only (no IRSA).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface MetricsServerProps {
|
|
9
|
+
/** Agent name (default: "metrics-server"). */
|
|
10
|
+
name?: string;
|
|
11
|
+
/** Container image (default: "registry.k8s.io/metrics-server/metrics-server:v0.7.2"). */
|
|
12
|
+
image?: string;
|
|
13
|
+
/** Replicas (default: 1). */
|
|
14
|
+
replicas?: number;
|
|
15
|
+
/** Namespace (default: "kube-system"). */
|
|
16
|
+
namespace?: string;
|
|
17
|
+
/** Additional labels. */
|
|
18
|
+
labels?: Record<string, string>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface MetricsServerResult {
|
|
22
|
+
deployment: Record<string, unknown>;
|
|
23
|
+
service: Record<string, unknown>;
|
|
24
|
+
serviceAccount: Record<string, unknown>;
|
|
25
|
+
clusterRole: Record<string, unknown>;
|
|
26
|
+
clusterRoleBinding: Record<string, unknown>;
|
|
27
|
+
/** ClusterRole for aggregated API access. */
|
|
28
|
+
aggregatedClusterRole: Record<string, unknown>;
|
|
29
|
+
/** Binding for aggregated API auth-delegator. */
|
|
30
|
+
authDelegatorBinding: Record<string, unknown>;
|
|
31
|
+
/** APIService registration for v1beta1.metrics.k8s.io. */
|
|
32
|
+
apiService: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Create a MetricsServer composite — returns prop objects for a Deployment,
|
|
37
|
+
* Service, ServiceAccount, ClusterRoles, ClusterRoleBindings, and APIService.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* import { MetricsServer } from "@intentius/chant-lexicon-k8s";
|
|
42
|
+
*
|
|
43
|
+
* const ms = MetricsServer({});
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export function MetricsServer(props: MetricsServerProps): MetricsServerResult {
|
|
47
|
+
const {
|
|
48
|
+
name = "metrics-server",
|
|
49
|
+
image = "registry.k8s.io/metrics-server/metrics-server:v0.7.2",
|
|
50
|
+
replicas = 1,
|
|
51
|
+
namespace = "kube-system",
|
|
52
|
+
labels: extraLabels = {},
|
|
53
|
+
} = props;
|
|
54
|
+
|
|
55
|
+
const saName = `${name}-sa`;
|
|
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 deployment: Record<string, unknown> = {
|
|
64
|
+
metadata: {
|
|
65
|
+
name,
|
|
66
|
+
namespace,
|
|
67
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "metrics" },
|
|
68
|
+
},
|
|
69
|
+
spec: {
|
|
70
|
+
replicas,
|
|
71
|
+
selector: { matchLabels: { "app.kubernetes.io/name": name } },
|
|
72
|
+
template: {
|
|
73
|
+
metadata: { labels: { "app.kubernetes.io/name": name, ...extraLabels } },
|
|
74
|
+
spec: {
|
|
75
|
+
serviceAccountName: saName,
|
|
76
|
+
containers: [
|
|
77
|
+
{
|
|
78
|
+
name,
|
|
79
|
+
image,
|
|
80
|
+
args: [
|
|
81
|
+
"--cert-dir=/tmp",
|
|
82
|
+
"--secure-port=10250",
|
|
83
|
+
"--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname",
|
|
84
|
+
"--kubelet-use-node-status-port",
|
|
85
|
+
"--metric-resolution=15s",
|
|
86
|
+
],
|
|
87
|
+
ports: [{ containerPort: 10250, name: "https", protocol: "TCP" }],
|
|
88
|
+
resources: {
|
|
89
|
+
requests: { cpu: "100m", memory: "200Mi" },
|
|
90
|
+
limits: { cpu: "100m", memory: "200Mi" },
|
|
91
|
+
},
|
|
92
|
+
livenessProbe: {
|
|
93
|
+
httpGet: { path: "/livez", port: 10250, scheme: "HTTPS" },
|
|
94
|
+
initialDelaySeconds: 0,
|
|
95
|
+
periodSeconds: 10,
|
|
96
|
+
failureThreshold: 3,
|
|
97
|
+
},
|
|
98
|
+
readinessProbe: {
|
|
99
|
+
httpGet: { path: "/readyz", port: 10250, scheme: "HTTPS" },
|
|
100
|
+
initialDelaySeconds: 20,
|
|
101
|
+
periodSeconds: 10,
|
|
102
|
+
failureThreshold: 3,
|
|
103
|
+
},
|
|
104
|
+
securityContext: {
|
|
105
|
+
runAsNonRoot: true,
|
|
106
|
+
readOnlyRootFilesystem: true,
|
|
107
|
+
allowPrivilegeEscalation: false,
|
|
108
|
+
},
|
|
109
|
+
volumeMounts: [{ name: "tmp-dir", mountPath: "/tmp" }],
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
volumes: [{ name: "tmp-dir", emptyDir: {} }],
|
|
113
|
+
priorityClassName: "system-cluster-critical",
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const service: Record<string, unknown> = {
|
|
120
|
+
metadata: {
|
|
121
|
+
name,
|
|
122
|
+
namespace,
|
|
123
|
+
labels: {
|
|
124
|
+
...commonLabels,
|
|
125
|
+
"app.kubernetes.io/component": "metrics",
|
|
126
|
+
"kubernetes.io/cluster-service": "true",
|
|
127
|
+
"kubernetes.io/name": "Metrics-server",
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
spec: {
|
|
131
|
+
selector: { "app.kubernetes.io/name": name },
|
|
132
|
+
ports: [{ port: 443, targetPort: 10250, protocol: "TCP", name: "https" }],
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const serviceAccount: Record<string, unknown> = {
|
|
137
|
+
metadata: {
|
|
138
|
+
name: saName,
|
|
139
|
+
namespace,
|
|
140
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "metrics" },
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const clusterRole: Record<string, unknown> = {
|
|
145
|
+
metadata: {
|
|
146
|
+
name: `system:${name}`,
|
|
147
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
|
|
148
|
+
},
|
|
149
|
+
rules: [
|
|
150
|
+
{ apiGroups: [""], resources: ["pods", "nodes", "namespaces", "configmaps"], verbs: ["get", "list", "watch"] },
|
|
151
|
+
{ apiGroups: [""], resources: ["nodes/metrics", "nodes/stats"], verbs: ["get"] },
|
|
152
|
+
],
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const clusterRoleBinding: Record<string, unknown> = {
|
|
156
|
+
metadata: {
|
|
157
|
+
name: `system:${name}`,
|
|
158
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
|
|
159
|
+
},
|
|
160
|
+
roleRef: {
|
|
161
|
+
apiGroup: "rbac.authorization.k8s.io",
|
|
162
|
+
kind: "ClusterRole",
|
|
163
|
+
name: `system:${name}`,
|
|
164
|
+
},
|
|
165
|
+
subjects: [{ kind: "ServiceAccount", name: saName, namespace }],
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const aggregatedClusterRole: Record<string, unknown> = {
|
|
169
|
+
metadata: {
|
|
170
|
+
name: `system:${name}-aggregated-reader`,
|
|
171
|
+
labels: {
|
|
172
|
+
...commonLabels,
|
|
173
|
+
"app.kubernetes.io/component": "rbac",
|
|
174
|
+
"rbac.authorization.k8s.io/aggregate-to-admin": "true",
|
|
175
|
+
"rbac.authorization.k8s.io/aggregate-to-edit": "true",
|
|
176
|
+
"rbac.authorization.k8s.io/aggregate-to-view": "true",
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
rules: [
|
|
180
|
+
{ apiGroups: ["metrics.k8s.io"], resources: ["pods", "nodes"], verbs: ["get", "list", "watch"] },
|
|
181
|
+
],
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const authDelegatorBinding: Record<string, unknown> = {
|
|
185
|
+
metadata: {
|
|
186
|
+
name: `${name}:system:auth-delegator`,
|
|
187
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
|
|
188
|
+
},
|
|
189
|
+
roleRef: {
|
|
190
|
+
apiGroup: "rbac.authorization.k8s.io",
|
|
191
|
+
kind: "ClusterRole",
|
|
192
|
+
name: "system:auth-delegator",
|
|
193
|
+
},
|
|
194
|
+
subjects: [{ kind: "ServiceAccount", name: saName, namespace }],
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const apiService: Record<string, unknown> = {
|
|
198
|
+
apiVersion: "apiregistration.k8s.io/v1",
|
|
199
|
+
kind: "APIService",
|
|
200
|
+
metadata: {
|
|
201
|
+
name: "v1beta1.metrics.k8s.io",
|
|
202
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "metrics" },
|
|
203
|
+
},
|
|
204
|
+
spec: {
|
|
205
|
+
service: { name, namespace },
|
|
206
|
+
group: "metrics.k8s.io",
|
|
207
|
+
version: "v1beta1",
|
|
208
|
+
insecureSkipTLSVerify: true,
|
|
209
|
+
groupPriorityMinimum: 100,
|
|
210
|
+
versionPriority: 100,
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
deployment,
|
|
216
|
+
service,
|
|
217
|
+
serviceAccount,
|
|
218
|
+
clusterRole,
|
|
219
|
+
clusterRoleBinding,
|
|
220
|
+
aggregatedClusterRole,
|
|
221
|
+
authDelegatorBinding,
|
|
222
|
+
apiService,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MonitoredService composite — Deployment + Service + ServiceMonitor + optional PrometheusRule.
|
|
3
|
+
*
|
|
4
|
+
* Observability pattern. ServiceMonitor and PrometheusRule are CRDs
|
|
5
|
+
* from the Prometheus Operator (monitoring.coreos.com/v1), returned
|
|
6
|
+
* as raw objects that can be serialized alongside native K8s resources.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ContainerSecurityContext } from "./security-context";
|
|
10
|
+
|
|
11
|
+
export interface AlertRule {
|
|
12
|
+
/** Alert name. */
|
|
13
|
+
name: string;
|
|
14
|
+
/** PromQL expression. */
|
|
15
|
+
expr: string;
|
|
16
|
+
/** Duration before firing (e.g., "5m"). */
|
|
17
|
+
for?: string;
|
|
18
|
+
/** Severity label (e.g., "warning", "critical"). */
|
|
19
|
+
severity?: string;
|
|
20
|
+
/** Additional annotations (summary, description). */
|
|
21
|
+
annotations?: Record<string, string>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface MonitoredServiceProps {
|
|
25
|
+
/** Application name — used in metadata and labels. */
|
|
26
|
+
name: string;
|
|
27
|
+
/** Container image. */
|
|
28
|
+
image: string;
|
|
29
|
+
/** Application container port (default: 80). */
|
|
30
|
+
port?: number;
|
|
31
|
+
/** Metrics port (default: 9090). */
|
|
32
|
+
metricsPort?: number;
|
|
33
|
+
/** Metrics path (default: "/metrics"). */
|
|
34
|
+
metricsPath?: string;
|
|
35
|
+
/** Scrape interval (default: "30s"). */
|
|
36
|
+
scrapeInterval?: string;
|
|
37
|
+
/** Alert rules — if provided, creates a PrometheusRule. */
|
|
38
|
+
alertRules?: AlertRule[];
|
|
39
|
+
/** Number of replicas (default: 2). */
|
|
40
|
+
replicas?: number;
|
|
41
|
+
/** Additional labels to apply to all resources. */
|
|
42
|
+
labels?: Record<string, string>;
|
|
43
|
+
/** CPU limit (default: "500m"). */
|
|
44
|
+
cpuLimit?: string;
|
|
45
|
+
/** Memory limit (default: "256Mi"). */
|
|
46
|
+
memoryLimit?: string;
|
|
47
|
+
/** CPU request (default: "100m"). */
|
|
48
|
+
cpuRequest?: string;
|
|
49
|
+
/** Memory request (default: "128Mi"). */
|
|
50
|
+
memoryRequest?: string;
|
|
51
|
+
/** Namespace for all resources. */
|
|
52
|
+
namespace?: string;
|
|
53
|
+
/** Environment variables for the container. */
|
|
54
|
+
env?: Array<{ name: string; value: string }>;
|
|
55
|
+
/** Container security context (supports PSS restricted fields). */
|
|
56
|
+
securityContext?: ContainerSecurityContext;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface MonitoredServiceResult {
|
|
60
|
+
deployment: Record<string, unknown>;
|
|
61
|
+
service: Record<string, unknown>;
|
|
62
|
+
serviceMonitor: Record<string, unknown>;
|
|
63
|
+
prometheusRule?: Record<string, unknown>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Create a MonitoredService composite — returns prop objects for
|
|
68
|
+
* a Deployment, Service, ServiceMonitor, and optional PrometheusRule.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* import { MonitoredService } from "@intentius/chant-lexicon-k8s";
|
|
73
|
+
*
|
|
74
|
+
* const { deployment, service, serviceMonitor, prometheusRule } = MonitoredService({
|
|
75
|
+
* name: "api",
|
|
76
|
+
* image: "api:1.0",
|
|
77
|
+
* port: 8080,
|
|
78
|
+
* metricsPort: 9090,
|
|
79
|
+
* alertRules: [
|
|
80
|
+
* { name: "HighErrorRate", expr: 'rate(http_errors_total[5m]) > 0.1', for: "5m", severity: "critical" },
|
|
81
|
+
* ],
|
|
82
|
+
* });
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export function MonitoredService(props: MonitoredServiceProps): MonitoredServiceResult {
|
|
86
|
+
const {
|
|
87
|
+
name,
|
|
88
|
+
image,
|
|
89
|
+
port = 80,
|
|
90
|
+
metricsPort = 9090,
|
|
91
|
+
metricsPath = "/metrics",
|
|
92
|
+
scrapeInterval = "30s",
|
|
93
|
+
alertRules,
|
|
94
|
+
replicas = 2,
|
|
95
|
+
labels: extraLabels = {},
|
|
96
|
+
cpuLimit = "500m",
|
|
97
|
+
memoryLimit = "256Mi",
|
|
98
|
+
cpuRequest = "100m",
|
|
99
|
+
memoryRequest = "128Mi",
|
|
100
|
+
namespace,
|
|
101
|
+
env,
|
|
102
|
+
securityContext,
|
|
103
|
+
} = props;
|
|
104
|
+
|
|
105
|
+
const commonLabels: Record<string, string> = {
|
|
106
|
+
"app.kubernetes.io/name": name,
|
|
107
|
+
"app.kubernetes.io/managed-by": "chant",
|
|
108
|
+
...extraLabels,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const ports: Array<Record<string, unknown>> = [
|
|
112
|
+
{ containerPort: port, name: "http" },
|
|
113
|
+
];
|
|
114
|
+
if (metricsPort !== port) {
|
|
115
|
+
ports.push({ containerPort: metricsPort, name: "metrics" });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const deploymentProps: Record<string, unknown> = {
|
|
119
|
+
metadata: {
|
|
120
|
+
name,
|
|
121
|
+
...(namespace && { namespace }),
|
|
122
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "server" },
|
|
123
|
+
},
|
|
124
|
+
spec: {
|
|
125
|
+
replicas,
|
|
126
|
+
selector: { matchLabels: { "app.kubernetes.io/name": name } },
|
|
127
|
+
template: {
|
|
128
|
+
metadata: { labels: { "app.kubernetes.io/name": name, ...extraLabels } },
|
|
129
|
+
spec: {
|
|
130
|
+
containers: [
|
|
131
|
+
{
|
|
132
|
+
name,
|
|
133
|
+
image,
|
|
134
|
+
ports,
|
|
135
|
+
resources: {
|
|
136
|
+
limits: { cpu: cpuLimit, memory: memoryLimit },
|
|
137
|
+
requests: { cpu: cpuRequest, memory: memoryRequest },
|
|
138
|
+
},
|
|
139
|
+
...(env && { env }),
|
|
140
|
+
...(securityContext && { securityContext }),
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const servicePorts: Array<Record<string, unknown>> = [
|
|
149
|
+
{ port: 80, targetPort: port, protocol: "TCP", name: "http" },
|
|
150
|
+
];
|
|
151
|
+
if (metricsPort !== port) {
|
|
152
|
+
servicePorts.push({ port: metricsPort, targetPort: metricsPort, protocol: "TCP", name: "metrics" });
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const serviceProps: Record<string, unknown> = {
|
|
156
|
+
metadata: {
|
|
157
|
+
name,
|
|
158
|
+
...(namespace && { namespace }),
|
|
159
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "server" },
|
|
160
|
+
},
|
|
161
|
+
spec: {
|
|
162
|
+
selector: { "app.kubernetes.io/name": name },
|
|
163
|
+
ports: servicePorts,
|
|
164
|
+
type: "ClusterIP",
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const serviceMonitorProps: Record<string, unknown> = {
|
|
169
|
+
metadata: {
|
|
170
|
+
name,
|
|
171
|
+
...(namespace && { namespace }),
|
|
172
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "monitoring" },
|
|
173
|
+
},
|
|
174
|
+
spec: {
|
|
175
|
+
selector: {
|
|
176
|
+
matchLabels: { "app.kubernetes.io/name": name },
|
|
177
|
+
},
|
|
178
|
+
endpoints: [
|
|
179
|
+
{
|
|
180
|
+
port: "metrics",
|
|
181
|
+
path: metricsPath,
|
|
182
|
+
interval: scrapeInterval,
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const result: MonitoredServiceResult = {
|
|
189
|
+
deployment: deploymentProps,
|
|
190
|
+
service: serviceProps,
|
|
191
|
+
serviceMonitor: serviceMonitorProps,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
if (alertRules && alertRules.length > 0) {
|
|
195
|
+
result.prometheusRule = {
|
|
196
|
+
metadata: {
|
|
197
|
+
name: `${name}-alerts`,
|
|
198
|
+
...(namespace && { namespace }),
|
|
199
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "monitoring" },
|
|
200
|
+
},
|
|
201
|
+
spec: {
|
|
202
|
+
groups: [
|
|
203
|
+
{
|
|
204
|
+
name: `${name}.rules`,
|
|
205
|
+
rules: alertRules.map((rule) => ({
|
|
206
|
+
alert: rule.name,
|
|
207
|
+
expr: rule.expr,
|
|
208
|
+
...(rule.for && { for: rule.for }),
|
|
209
|
+
labels: {
|
|
210
|
+
...(rule.severity && { severity: rule.severity }),
|
|
211
|
+
},
|
|
212
|
+
...(rule.annotations && { annotations: rule.annotations }),
|
|
213
|
+
})),
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return result;
|
|
221
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NetworkIsolatedApp composite — Deployment + Service + NetworkPolicy.
|
|
3
|
+
*
|
|
4
|
+
* Per-app allow rules beyond NamespaceEnv's default-deny.
|
|
5
|
+
* Creates fine-grained ingress/egress policies for a single application.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ContainerSecurityContext } from "./security-context";
|
|
9
|
+
|
|
10
|
+
export interface NetworkPolicyPeer {
|
|
11
|
+
/** Pod selector for the peer. */
|
|
12
|
+
podSelector?: Record<string, string>;
|
|
13
|
+
/** Namespace selector for the peer. */
|
|
14
|
+
namespaceSelector?: Record<string, string>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface NetworkPolicyEgressPeer extends NetworkPolicyPeer {
|
|
18
|
+
/** Allowed ports for egress. */
|
|
19
|
+
ports?: Array<{ port: number; protocol?: string }>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface NetworkIsolatedAppProps {
|
|
23
|
+
/** Application name — used in metadata and labels. */
|
|
24
|
+
name: string;
|
|
25
|
+
/** Container image. */
|
|
26
|
+
image: string;
|
|
27
|
+
/** Container port (default: 80). */
|
|
28
|
+
port?: number;
|
|
29
|
+
/** Number of replicas (default: 2). */
|
|
30
|
+
replicas?: number;
|
|
31
|
+
/** Allowed ingress sources. */
|
|
32
|
+
allowIngressFrom?: NetworkPolicyPeer[];
|
|
33
|
+
/** Allowed egress destinations. */
|
|
34
|
+
allowEgressTo?: NetworkPolicyEgressPeer[];
|
|
35
|
+
/** Additional labels to apply to all resources. */
|
|
36
|
+
labels?: Record<string, string>;
|
|
37
|
+
/** CPU limit (default: "500m"). */
|
|
38
|
+
cpuLimit?: string;
|
|
39
|
+
/** Memory limit (default: "256Mi"). */
|
|
40
|
+
memoryLimit?: string;
|
|
41
|
+
/** CPU request (default: "100m"). */
|
|
42
|
+
cpuRequest?: string;
|
|
43
|
+
/** Memory request (default: "128Mi"). */
|
|
44
|
+
memoryRequest?: string;
|
|
45
|
+
/** Namespace for all resources. */
|
|
46
|
+
namespace?: string;
|
|
47
|
+
/** Environment variables for the container. */
|
|
48
|
+
env?: Array<{ name: string; value: string }>;
|
|
49
|
+
/** Container security context (supports PSS restricted fields). */
|
|
50
|
+
securityContext?: ContainerSecurityContext;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface NetworkIsolatedAppResult {
|
|
54
|
+
deployment: Record<string, unknown>;
|
|
55
|
+
service: Record<string, unknown>;
|
|
56
|
+
networkPolicy: Record<string, unknown>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Create a NetworkIsolatedApp composite — returns prop objects for
|
|
61
|
+
* a Deployment, Service, and NetworkPolicy.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* import { NetworkIsolatedApp } from "@intentius/chant-lexicon-k8s";
|
|
66
|
+
*
|
|
67
|
+
* const { deployment, service, networkPolicy } = NetworkIsolatedApp({
|
|
68
|
+
* name: "api",
|
|
69
|
+
* image: "api:1.0",
|
|
70
|
+
* port: 8080,
|
|
71
|
+
* allowIngressFrom: [
|
|
72
|
+
* { podSelector: { "app.kubernetes.io/name": "frontend" } },
|
|
73
|
+
* ],
|
|
74
|
+
* allowEgressTo: [
|
|
75
|
+
* { podSelector: { "app.kubernetes.io/name": "postgres" }, ports: [{ port: 5432 }] },
|
|
76
|
+
* ],
|
|
77
|
+
* });
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export function NetworkIsolatedApp(props: NetworkIsolatedAppProps): NetworkIsolatedAppResult {
|
|
81
|
+
const {
|
|
82
|
+
name,
|
|
83
|
+
image,
|
|
84
|
+
port = 80,
|
|
85
|
+
replicas = 2,
|
|
86
|
+
allowIngressFrom,
|
|
87
|
+
allowEgressTo,
|
|
88
|
+
labels: extraLabels = {},
|
|
89
|
+
cpuLimit = "500m",
|
|
90
|
+
memoryLimit = "256Mi",
|
|
91
|
+
cpuRequest = "100m",
|
|
92
|
+
memoryRequest = "128Mi",
|
|
93
|
+
namespace,
|
|
94
|
+
env,
|
|
95
|
+
securityContext,
|
|
96
|
+
} = props;
|
|
97
|
+
|
|
98
|
+
const commonLabels: Record<string, string> = {
|
|
99
|
+
"app.kubernetes.io/name": name,
|
|
100
|
+
"app.kubernetes.io/managed-by": "chant",
|
|
101
|
+
...extraLabels,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const deploymentProps: Record<string, unknown> = {
|
|
105
|
+
metadata: {
|
|
106
|
+
name,
|
|
107
|
+
...(namespace && { namespace }),
|
|
108
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "server" },
|
|
109
|
+
},
|
|
110
|
+
spec: {
|
|
111
|
+
replicas,
|
|
112
|
+
selector: { matchLabels: { "app.kubernetes.io/name": name } },
|
|
113
|
+
template: {
|
|
114
|
+
metadata: { labels: { "app.kubernetes.io/name": name, ...extraLabels } },
|
|
115
|
+
spec: {
|
|
116
|
+
containers: [
|
|
117
|
+
{
|
|
118
|
+
name,
|
|
119
|
+
image,
|
|
120
|
+
ports: [{ containerPort: port, name: "http" }],
|
|
121
|
+
resources: {
|
|
122
|
+
limits: { cpu: cpuLimit, memory: memoryLimit },
|
|
123
|
+
requests: { cpu: cpuRequest, memory: memoryRequest },
|
|
124
|
+
},
|
|
125
|
+
...(env && { env }),
|
|
126
|
+
...(securityContext && { securityContext }),
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const serviceProps: Record<string, unknown> = {
|
|
135
|
+
metadata: {
|
|
136
|
+
name,
|
|
137
|
+
...(namespace && { namespace }),
|
|
138
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "server" },
|
|
139
|
+
},
|
|
140
|
+
spec: {
|
|
141
|
+
selector: { "app.kubernetes.io/name": name },
|
|
142
|
+
ports: [{ port: 80, targetPort: port, protocol: "TCP", name: "http" }],
|
|
143
|
+
type: "ClusterIP",
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// Build network policy
|
|
148
|
+
const policyTypes: string[] = [];
|
|
149
|
+
const policySpec: Record<string, unknown> = {
|
|
150
|
+
podSelector: { matchLabels: { "app.kubernetes.io/name": name } },
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
if (allowIngressFrom) {
|
|
154
|
+
policyTypes.push("Ingress");
|
|
155
|
+
policySpec.ingress = [
|
|
156
|
+
{
|
|
157
|
+
from: allowIngressFrom.map((peer) => ({
|
|
158
|
+
...(peer.podSelector && { podSelector: { matchLabels: peer.podSelector } }),
|
|
159
|
+
...(peer.namespaceSelector && { namespaceSelector: { matchLabels: peer.namespaceSelector } }),
|
|
160
|
+
})),
|
|
161
|
+
ports: [{ port, protocol: "TCP" }],
|
|
162
|
+
},
|
|
163
|
+
];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (allowEgressTo) {
|
|
167
|
+
policyTypes.push("Egress");
|
|
168
|
+
policySpec.egress = allowEgressTo.map((peer) => ({
|
|
169
|
+
to: [
|
|
170
|
+
{
|
|
171
|
+
...(peer.podSelector && { podSelector: { matchLabels: peer.podSelector } }),
|
|
172
|
+
...(peer.namespaceSelector && { namespaceSelector: { matchLabels: peer.namespaceSelector } }),
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
...(peer.ports && {
|
|
176
|
+
ports: peer.ports.map((p) => ({
|
|
177
|
+
port: p.port,
|
|
178
|
+
protocol: p.protocol ?? "TCP",
|
|
179
|
+
})),
|
|
180
|
+
}),
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (policyTypes.length > 0) {
|
|
185
|
+
policySpec.policyTypes = policyTypes;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const networkPolicyProps: Record<string, unknown> = {
|
|
189
|
+
metadata: {
|
|
190
|
+
name: `${name}-policy`,
|
|
191
|
+
...(namespace && { namespace }),
|
|
192
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "network-policy" },
|
|
193
|
+
},
|
|
194
|
+
spec: policySpec,
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
deployment: deploymentProps,
|
|
199
|
+
service: serviceProps,
|
|
200
|
+
networkPolicy: networkPolicyProps,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* security scanners) that need cluster-wide RBAC and tolerations.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import type { ContainerSecurityContext } from "./security-context";
|
|
9
|
+
|
|
8
10
|
export interface NodeAgentProps {
|
|
9
11
|
/** Agent name — used in metadata and labels. */
|
|
10
12
|
name: string;
|
|
@@ -43,6 +45,8 @@ export interface NodeAgentProps {
|
|
|
43
45
|
memoryLimit?: string;
|
|
44
46
|
/** Environment variables for the container. */
|
|
45
47
|
env?: Array<{ name: string; value: string }>;
|
|
48
|
+
/** Container security context (supports PSS restricted fields). */
|
|
49
|
+
securityContext?: ContainerSecurityContext;
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
export interface NodeAgentResult {
|
|
@@ -87,6 +91,7 @@ export function NodeAgent(props: NodeAgentProps): NodeAgentResult {
|
|
|
87
91
|
memoryLimit = "128Mi",
|
|
88
92
|
labels: extraLabels = {},
|
|
89
93
|
env,
|
|
94
|
+
securityContext,
|
|
90
95
|
} = props;
|
|
91
96
|
|
|
92
97
|
const saName = `${name}-sa`;
|
|
@@ -139,6 +144,7 @@ export function NodeAgent(props: NodeAgentProps): NodeAgentResult {
|
|
|
139
144
|
},
|
|
140
145
|
...(env && { env }),
|
|
141
146
|
...(volumeMounts.length > 0 && { volumeMounts }),
|
|
147
|
+
...(securityContext && { securityContext }),
|
|
142
148
|
};
|
|
143
149
|
|
|
144
150
|
const podSpec: Record<string, unknown> = {
|