@intentius/chant-lexicon-k8s 0.0.12 → 0.0.14

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.
@@ -0,0 +1,215 @@
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
+ export interface AlertRule {
10
+ /** Alert name. */
11
+ name: string;
12
+ /** PromQL expression. */
13
+ expr: string;
14
+ /** Duration before firing (e.g., "5m"). */
15
+ for?: string;
16
+ /** Severity label (e.g., "warning", "critical"). */
17
+ severity?: string;
18
+ /** Additional annotations (summary, description). */
19
+ annotations?: Record<string, string>;
20
+ }
21
+
22
+ export interface MonitoredServiceProps {
23
+ /** Application name — used in metadata and labels. */
24
+ name: string;
25
+ /** Container image. */
26
+ image: string;
27
+ /** Application container port (default: 80). */
28
+ port?: number;
29
+ /** Metrics port (default: 9090). */
30
+ metricsPort?: number;
31
+ /** Metrics path (default: "/metrics"). */
32
+ metricsPath?: string;
33
+ /** Scrape interval (default: "30s"). */
34
+ scrapeInterval?: string;
35
+ /** Alert rules — if provided, creates a PrometheusRule. */
36
+ alertRules?: AlertRule[];
37
+ /** Number of replicas (default: 2). */
38
+ replicas?: number;
39
+ /** Additional labels to apply to all resources. */
40
+ labels?: Record<string, string>;
41
+ /** CPU limit (default: "500m"). */
42
+ cpuLimit?: string;
43
+ /** Memory limit (default: "256Mi"). */
44
+ memoryLimit?: string;
45
+ /** CPU request (default: "100m"). */
46
+ cpuRequest?: string;
47
+ /** Memory request (default: "128Mi"). */
48
+ memoryRequest?: string;
49
+ /** Namespace for all resources. */
50
+ namespace?: string;
51
+ /** Environment variables for the container. */
52
+ env?: Array<{ name: string; value: string }>;
53
+ }
54
+
55
+ export interface MonitoredServiceResult {
56
+ deployment: Record<string, unknown>;
57
+ service: Record<string, unknown>;
58
+ serviceMonitor: Record<string, unknown>;
59
+ prometheusRule?: Record<string, unknown>;
60
+ }
61
+
62
+ /**
63
+ * Create a MonitoredService composite — returns prop objects for
64
+ * a Deployment, Service, ServiceMonitor, and optional PrometheusRule.
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * import { MonitoredService } from "@intentius/chant-lexicon-k8s";
69
+ *
70
+ * const { deployment, service, serviceMonitor, prometheusRule } = MonitoredService({
71
+ * name: "api",
72
+ * image: "api:1.0",
73
+ * port: 8080,
74
+ * metricsPort: 9090,
75
+ * alertRules: [
76
+ * { name: "HighErrorRate", expr: 'rate(http_errors_total[5m]) > 0.1', for: "5m", severity: "critical" },
77
+ * ],
78
+ * });
79
+ * ```
80
+ */
81
+ export function MonitoredService(props: MonitoredServiceProps): MonitoredServiceResult {
82
+ const {
83
+ name,
84
+ image,
85
+ port = 80,
86
+ metricsPort = 9090,
87
+ metricsPath = "/metrics",
88
+ scrapeInterval = "30s",
89
+ alertRules,
90
+ replicas = 2,
91
+ labels: extraLabels = {},
92
+ cpuLimit = "500m",
93
+ memoryLimit = "256Mi",
94
+ cpuRequest = "100m",
95
+ memoryRequest = "128Mi",
96
+ namespace,
97
+ env,
98
+ } = props;
99
+
100
+ const commonLabels: Record<string, string> = {
101
+ "app.kubernetes.io/name": name,
102
+ "app.kubernetes.io/managed-by": "chant",
103
+ ...extraLabels,
104
+ };
105
+
106
+ const ports: Array<Record<string, unknown>> = [
107
+ { containerPort: port, name: "http" },
108
+ ];
109
+ if (metricsPort !== port) {
110
+ ports.push({ containerPort: metricsPort, name: "metrics" });
111
+ }
112
+
113
+ const deploymentProps: Record<string, unknown> = {
114
+ metadata: {
115
+ name,
116
+ ...(namespace && { namespace }),
117
+ labels: { ...commonLabels, "app.kubernetes.io/component": "server" },
118
+ },
119
+ spec: {
120
+ replicas,
121
+ selector: { matchLabels: { "app.kubernetes.io/name": name } },
122
+ template: {
123
+ metadata: { labels: { "app.kubernetes.io/name": name, ...extraLabels } },
124
+ spec: {
125
+ containers: [
126
+ {
127
+ name,
128
+ image,
129
+ ports,
130
+ resources: {
131
+ limits: { cpu: cpuLimit, memory: memoryLimit },
132
+ requests: { cpu: cpuRequest, memory: memoryRequest },
133
+ },
134
+ ...(env && { env }),
135
+ },
136
+ ],
137
+ },
138
+ },
139
+ },
140
+ };
141
+
142
+ const servicePorts: Array<Record<string, unknown>> = [
143
+ { port: 80, targetPort: port, protocol: "TCP", name: "http" },
144
+ ];
145
+ if (metricsPort !== port) {
146
+ servicePorts.push({ port: metricsPort, targetPort: metricsPort, protocol: "TCP", name: "metrics" });
147
+ }
148
+
149
+ const serviceProps: Record<string, unknown> = {
150
+ metadata: {
151
+ name,
152
+ ...(namespace && { namespace }),
153
+ labels: { ...commonLabels, "app.kubernetes.io/component": "server" },
154
+ },
155
+ spec: {
156
+ selector: { "app.kubernetes.io/name": name },
157
+ ports: servicePorts,
158
+ type: "ClusterIP",
159
+ },
160
+ };
161
+
162
+ const serviceMonitorProps: Record<string, unknown> = {
163
+ metadata: {
164
+ name,
165
+ ...(namespace && { namespace }),
166
+ labels: { ...commonLabels, "app.kubernetes.io/component": "monitoring" },
167
+ },
168
+ spec: {
169
+ selector: {
170
+ matchLabels: { "app.kubernetes.io/name": name },
171
+ },
172
+ endpoints: [
173
+ {
174
+ port: "metrics",
175
+ path: metricsPath,
176
+ interval: scrapeInterval,
177
+ },
178
+ ],
179
+ },
180
+ };
181
+
182
+ const result: MonitoredServiceResult = {
183
+ deployment: deploymentProps,
184
+ service: serviceProps,
185
+ serviceMonitor: serviceMonitorProps,
186
+ };
187
+
188
+ if (alertRules && alertRules.length > 0) {
189
+ result.prometheusRule = {
190
+ metadata: {
191
+ name: `${name}-alerts`,
192
+ ...(namespace && { namespace }),
193
+ labels: { ...commonLabels, "app.kubernetes.io/component": "monitoring" },
194
+ },
195
+ spec: {
196
+ groups: [
197
+ {
198
+ name: `${name}.rules`,
199
+ rules: alertRules.map((rule) => ({
200
+ alert: rule.name,
201
+ expr: rule.expr,
202
+ ...(rule.for && { for: rule.for }),
203
+ labels: {
204
+ ...(rule.severity && { severity: rule.severity }),
205
+ },
206
+ ...(rule.annotations && { annotations: rule.annotations }),
207
+ })),
208
+ },
209
+ ],
210
+ },
211
+ };
212
+ }
213
+
214
+ return result;
215
+ }
@@ -0,0 +1,196 @@
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
+ export interface NetworkPolicyPeer {
9
+ /** Pod selector for the peer. */
10
+ podSelector?: Record<string, string>;
11
+ /** Namespace selector for the peer. */
12
+ namespaceSelector?: Record<string, string>;
13
+ }
14
+
15
+ export interface NetworkPolicyEgressPeer extends NetworkPolicyPeer {
16
+ /** Allowed ports for egress. */
17
+ ports?: Array<{ port: number; protocol?: string }>;
18
+ }
19
+
20
+ export interface NetworkIsolatedAppProps {
21
+ /** Application name — used in metadata and labels. */
22
+ name: string;
23
+ /** Container image. */
24
+ image: string;
25
+ /** Container port (default: 80). */
26
+ port?: number;
27
+ /** Number of replicas (default: 2). */
28
+ replicas?: number;
29
+ /** Allowed ingress sources. */
30
+ allowIngressFrom?: NetworkPolicyPeer[];
31
+ /** Allowed egress destinations. */
32
+ allowEgressTo?: NetworkPolicyEgressPeer[];
33
+ /** Additional labels to apply to all resources. */
34
+ labels?: Record<string, string>;
35
+ /** CPU limit (default: "500m"). */
36
+ cpuLimit?: string;
37
+ /** Memory limit (default: "256Mi"). */
38
+ memoryLimit?: string;
39
+ /** CPU request (default: "100m"). */
40
+ cpuRequest?: string;
41
+ /** Memory request (default: "128Mi"). */
42
+ memoryRequest?: string;
43
+ /** Namespace for all resources. */
44
+ namespace?: string;
45
+ /** Environment variables for the container. */
46
+ env?: Array<{ name: string; value: string }>;
47
+ }
48
+
49
+ export interface NetworkIsolatedAppResult {
50
+ deployment: Record<string, unknown>;
51
+ service: Record<string, unknown>;
52
+ networkPolicy: Record<string, unknown>;
53
+ }
54
+
55
+ /**
56
+ * Create a NetworkIsolatedApp composite — returns prop objects for
57
+ * a Deployment, Service, and NetworkPolicy.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * import { NetworkIsolatedApp } from "@intentius/chant-lexicon-k8s";
62
+ *
63
+ * const { deployment, service, networkPolicy } = NetworkIsolatedApp({
64
+ * name: "api",
65
+ * image: "api:1.0",
66
+ * port: 8080,
67
+ * allowIngressFrom: [
68
+ * { podSelector: { "app.kubernetes.io/name": "frontend" } },
69
+ * ],
70
+ * allowEgressTo: [
71
+ * { podSelector: { "app.kubernetes.io/name": "postgres" }, ports: [{ port: 5432 }] },
72
+ * ],
73
+ * });
74
+ * ```
75
+ */
76
+ export function NetworkIsolatedApp(props: NetworkIsolatedAppProps): NetworkIsolatedAppResult {
77
+ const {
78
+ name,
79
+ image,
80
+ port = 80,
81
+ replicas = 2,
82
+ allowIngressFrom,
83
+ allowEgressTo,
84
+ labels: extraLabels = {},
85
+ cpuLimit = "500m",
86
+ memoryLimit = "256Mi",
87
+ cpuRequest = "100m",
88
+ memoryRequest = "128Mi",
89
+ namespace,
90
+ env,
91
+ } = props;
92
+
93
+ const commonLabels: Record<string, string> = {
94
+ "app.kubernetes.io/name": name,
95
+ "app.kubernetes.io/managed-by": "chant",
96
+ ...extraLabels,
97
+ };
98
+
99
+ const deploymentProps: Record<string, unknown> = {
100
+ metadata: {
101
+ name,
102
+ ...(namespace && { namespace }),
103
+ labels: { ...commonLabels, "app.kubernetes.io/component": "server" },
104
+ },
105
+ spec: {
106
+ replicas,
107
+ selector: { matchLabels: { "app.kubernetes.io/name": name } },
108
+ template: {
109
+ metadata: { labels: { "app.kubernetes.io/name": name, ...extraLabels } },
110
+ spec: {
111
+ containers: [
112
+ {
113
+ name,
114
+ image,
115
+ ports: [{ containerPort: port, name: "http" }],
116
+ resources: {
117
+ limits: { cpu: cpuLimit, memory: memoryLimit },
118
+ requests: { cpu: cpuRequest, memory: memoryRequest },
119
+ },
120
+ ...(env && { env }),
121
+ },
122
+ ],
123
+ },
124
+ },
125
+ },
126
+ };
127
+
128
+ const serviceProps: Record<string, unknown> = {
129
+ metadata: {
130
+ name,
131
+ ...(namespace && { namespace }),
132
+ labels: { ...commonLabels, "app.kubernetes.io/component": "server" },
133
+ },
134
+ spec: {
135
+ selector: { "app.kubernetes.io/name": name },
136
+ ports: [{ port: 80, targetPort: port, protocol: "TCP", name: "http" }],
137
+ type: "ClusterIP",
138
+ },
139
+ };
140
+
141
+ // Build network policy
142
+ const policyTypes: string[] = [];
143
+ const policySpec: Record<string, unknown> = {
144
+ podSelector: { matchLabels: { "app.kubernetes.io/name": name } },
145
+ };
146
+
147
+ if (allowIngressFrom) {
148
+ policyTypes.push("Ingress");
149
+ policySpec.ingress = [
150
+ {
151
+ from: allowIngressFrom.map((peer) => ({
152
+ ...(peer.podSelector && { podSelector: { matchLabels: peer.podSelector } }),
153
+ ...(peer.namespaceSelector && { namespaceSelector: { matchLabels: peer.namespaceSelector } }),
154
+ })),
155
+ ports: [{ port, protocol: "TCP" }],
156
+ },
157
+ ];
158
+ }
159
+
160
+ if (allowEgressTo) {
161
+ policyTypes.push("Egress");
162
+ policySpec.egress = allowEgressTo.map((peer) => ({
163
+ to: [
164
+ {
165
+ ...(peer.podSelector && { podSelector: { matchLabels: peer.podSelector } }),
166
+ ...(peer.namespaceSelector && { namespaceSelector: { matchLabels: peer.namespaceSelector } }),
167
+ },
168
+ ],
169
+ ...(peer.ports && {
170
+ ports: peer.ports.map((p) => ({
171
+ port: p.port,
172
+ protocol: p.protocol ?? "TCP",
173
+ })),
174
+ }),
175
+ }));
176
+ }
177
+
178
+ if (policyTypes.length > 0) {
179
+ policySpec.policyTypes = policyTypes;
180
+ }
181
+
182
+ const networkPolicyProps: Record<string, unknown> = {
183
+ metadata: {
184
+ name: `${name}-policy`,
185
+ ...(namespace && { namespace }),
186
+ labels: { ...commonLabels, "app.kubernetes.io/component": "network-policy" },
187
+ },
188
+ spec: policySpec,
189
+ };
190
+
191
+ return {
192
+ deployment: deploymentProps,
193
+ service: serviceProps,
194
+ networkPolicy: networkPolicyProps,
195
+ };
196
+ }
@@ -0,0 +1,149 @@
1
+ /**
2
+ * SecureIngress composite — Ingress + optional cert-manager Certificate.
3
+ *
4
+ * A standalone Ingress with TLS via cert-manager, supporting
5
+ * multiple hosts and paths (unlike the single-path Ingress in WebApp).
6
+ */
7
+
8
+ export interface SecureIngressHost {
9
+ /** Hostname (e.g., "api.example.com"). */
10
+ hostname: string;
11
+ /** Path rules for this host. */
12
+ paths: Array<{
13
+ path: string;
14
+ pathType?: string;
15
+ serviceName: string;
16
+ servicePort: number;
17
+ }>;
18
+ }
19
+
20
+ export interface SecureIngressProps {
21
+ /** Ingress name — used in metadata and labels. */
22
+ name: string;
23
+ /** Host definitions with paths. */
24
+ hosts: SecureIngressHost[];
25
+ /** cert-manager ClusterIssuer name — if set, creates a Certificate resource. */
26
+ clusterIssuer?: string;
27
+ /** Ingress class name (e.g., "nginx"). */
28
+ ingressClassName?: string;
29
+ /** Additional annotations on the Ingress. */
30
+ annotations?: Record<string, string>;
31
+ /** Additional labels to apply to all resources. */
32
+ labels?: Record<string, string>;
33
+ /** Namespace for all resources. */
34
+ namespace?: string;
35
+ }
36
+
37
+ export interface SecureIngressResult {
38
+ ingress: Record<string, unknown>;
39
+ certificate?: Record<string, unknown>;
40
+ }
41
+
42
+ /**
43
+ * Create a SecureIngress composite — returns prop objects for
44
+ * an Ingress and optional cert-manager Certificate.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * import { SecureIngress } from "@intentius/chant-lexicon-k8s";
49
+ *
50
+ * const { ingress, certificate } = SecureIngress({
51
+ * name: "api-ingress",
52
+ * hosts: [
53
+ * {
54
+ * hostname: "api.example.com",
55
+ * paths: [{ path: "/", serviceName: "api", servicePort: 80 }],
56
+ * },
57
+ * ],
58
+ * clusterIssuer: "letsencrypt-prod",
59
+ * ingressClassName: "nginx",
60
+ * });
61
+ * ```
62
+ */
63
+ export function SecureIngress(props: SecureIngressProps): SecureIngressResult {
64
+ const {
65
+ name,
66
+ hosts,
67
+ clusterIssuer,
68
+ ingressClassName,
69
+ annotations: extraAnnotations = {},
70
+ labels: extraLabels = {},
71
+ namespace,
72
+ } = props;
73
+
74
+ const commonLabels: Record<string, string> = {
75
+ "app.kubernetes.io/name": name,
76
+ "app.kubernetes.io/managed-by": "chant",
77
+ ...extraLabels,
78
+ };
79
+
80
+ const allHostnames = hosts.map((h) => h.hostname);
81
+ const secretName = `${name}-tls`;
82
+
83
+ const ingressAnnotations: Record<string, string> = {
84
+ ...extraAnnotations,
85
+ };
86
+ if (clusterIssuer) {
87
+ ingressAnnotations["cert-manager.io/cluster-issuer"] = clusterIssuer;
88
+ }
89
+
90
+ const ingressRules = hosts.map((host) => ({
91
+ host: host.hostname,
92
+ http: {
93
+ paths: host.paths.map((p) => ({
94
+ path: p.path,
95
+ pathType: p.pathType ?? "Prefix",
96
+ backend: {
97
+ service: { name: p.serviceName, port: { number: p.servicePort } },
98
+ },
99
+ })),
100
+ },
101
+ }));
102
+
103
+ const hasAnnotations = Object.keys(ingressAnnotations).length > 0;
104
+
105
+ const ingressProps: Record<string, unknown> = {
106
+ metadata: {
107
+ name,
108
+ ...(namespace && { namespace }),
109
+ labels: { ...commonLabels, "app.kubernetes.io/component": "ingress" },
110
+ ...(hasAnnotations && { annotations: ingressAnnotations }),
111
+ },
112
+ spec: {
113
+ ...(ingressClassName && { ingressClassName }),
114
+ rules: ingressRules,
115
+ ...(clusterIssuer && {
116
+ tls: [
117
+ {
118
+ hosts: allHostnames,
119
+ secretName,
120
+ },
121
+ ],
122
+ }),
123
+ },
124
+ };
125
+
126
+ const result: SecureIngressResult = {
127
+ ingress: ingressProps,
128
+ };
129
+
130
+ if (clusterIssuer) {
131
+ result.certificate = {
132
+ metadata: {
133
+ name: secretName,
134
+ ...(namespace && { namespace }),
135
+ labels: { ...commonLabels, "app.kubernetes.io/component": "certificate" },
136
+ },
137
+ spec: {
138
+ secretName,
139
+ issuerRef: {
140
+ name: clusterIssuer,
141
+ kind: "ClusterIssuer",
142
+ },
143
+ dnsNames: allHostnames,
144
+ },
145
+ };
146
+ }
147
+
148
+ return result;
149
+ }