@intentius/chant-lexicon-k8s 0.0.12
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 +32 -0
- package/dist/manifest.json +8 -0
- package/dist/meta.json +1413 -0
- package/dist/rules/hardcoded-namespace.ts +56 -0
- package/dist/rules/k8s-helpers.ts +149 -0
- package/dist/rules/wk8005.ts +59 -0
- package/dist/rules/wk8006.ts +68 -0
- package/dist/rules/wk8041.ts +73 -0
- package/dist/rules/wk8042.ts +48 -0
- package/dist/rules/wk8101.ts +65 -0
- package/dist/rules/wk8102.ts +42 -0
- package/dist/rules/wk8103.ts +45 -0
- package/dist/rules/wk8104.ts +69 -0
- package/dist/rules/wk8105.ts +45 -0
- package/dist/rules/wk8201.ts +55 -0
- package/dist/rules/wk8202.ts +46 -0
- package/dist/rules/wk8203.ts +46 -0
- package/dist/rules/wk8204.ts +54 -0
- package/dist/rules/wk8205.ts +56 -0
- package/dist/rules/wk8207.ts +45 -0
- package/dist/rules/wk8208.ts +45 -0
- package/dist/rules/wk8209.ts +45 -0
- package/dist/rules/wk8301.ts +51 -0
- package/dist/rules/wk8302.ts +46 -0
- package/dist/rules/wk8303.ts +96 -0
- package/dist/skills/chant-k8s.md +433 -0
- package/dist/types/index.d.ts +2934 -0
- package/package.json +30 -0
- package/src/actions/actions.test.ts +83 -0
- package/src/actions/apps.ts +23 -0
- package/src/actions/batch.ts +9 -0
- package/src/actions/core.ts +62 -0
- package/src/actions/index.ts +50 -0
- package/src/actions/networking.ts +15 -0
- package/src/actions/rbac.ts +13 -0
- package/src/codegen/docs-cli.ts +3 -0
- package/src/codegen/docs.ts +1147 -0
- package/src/codegen/generate-cli.ts +41 -0
- package/src/codegen/generate-lexicon.ts +69 -0
- package/src/codegen/generate-typescript.ts +97 -0
- package/src/codegen/generate.ts +144 -0
- package/src/codegen/naming.test.ts +63 -0
- package/src/codegen/naming.ts +187 -0
- package/src/codegen/package.ts +56 -0
- package/src/codegen/patches.ts +108 -0
- package/src/codegen/snapshot.test.ts +95 -0
- package/src/codegen/typecheck.test.ts +24 -0
- package/src/codegen/typecheck.ts +4 -0
- package/src/codegen/versions.ts +43 -0
- package/src/composites/autoscaled-service.ts +236 -0
- package/src/composites/composites.test.ts +1109 -0
- package/src/composites/cron-workload.ts +167 -0
- package/src/composites/index.ts +14 -0
- package/src/composites/namespace-env.ts +163 -0
- package/src/composites/node-agent.ts +224 -0
- package/src/composites/stateful-app.ts +134 -0
- package/src/composites/web-app.ts +180 -0
- package/src/composites/worker-pool.ts +230 -0
- package/src/coverage.test.ts +27 -0
- package/src/coverage.ts +35 -0
- package/src/crd/loader.ts +112 -0
- package/src/crd/parser.test.ts +217 -0
- package/src/crd/parser.ts +279 -0
- package/src/crd/types.ts +54 -0
- package/src/default-labels.test.ts +111 -0
- package/src/default-labels.ts +122 -0
- package/src/generated/index.d.ts +2934 -0
- package/src/generated/index.ts +203 -0
- package/src/generated/lexicon-k8s.json +1413 -0
- package/src/generated/runtime.ts +4 -0
- package/src/import/generator.test.ts +121 -0
- package/src/import/generator.ts +285 -0
- package/src/import/parser.test.ts +156 -0
- package/src/import/parser.ts +204 -0
- package/src/import/roundtrip.test.ts +86 -0
- package/src/index.ts +38 -0
- package/src/lint/post-synth/k8s-helpers.test.ts +219 -0
- package/src/lint/post-synth/k8s-helpers.ts +149 -0
- package/src/lint/post-synth/post-synth.test.ts +969 -0
- package/src/lint/post-synth/wk8005.ts +59 -0
- package/src/lint/post-synth/wk8006.ts +68 -0
- package/src/lint/post-synth/wk8041.ts +73 -0
- package/src/lint/post-synth/wk8042.ts +48 -0
- package/src/lint/post-synth/wk8101.ts +65 -0
- package/src/lint/post-synth/wk8102.ts +42 -0
- package/src/lint/post-synth/wk8103.ts +45 -0
- package/src/lint/post-synth/wk8104.ts +69 -0
- package/src/lint/post-synth/wk8105.ts +45 -0
- package/src/lint/post-synth/wk8201.ts +55 -0
- package/src/lint/post-synth/wk8202.ts +46 -0
- package/src/lint/post-synth/wk8203.ts +46 -0
- package/src/lint/post-synth/wk8204.ts +54 -0
- package/src/lint/post-synth/wk8205.ts +56 -0
- package/src/lint/post-synth/wk8207.ts +45 -0
- package/src/lint/post-synth/wk8208.ts +45 -0
- package/src/lint/post-synth/wk8209.ts +45 -0
- package/src/lint/post-synth/wk8301.ts +51 -0
- package/src/lint/post-synth/wk8302.ts +46 -0
- package/src/lint/post-synth/wk8303.ts +96 -0
- package/src/lint/rules/hardcoded-namespace.ts +56 -0
- package/src/lint/rules/rules.test.ts +69 -0
- package/src/lsp/completions.test.ts +64 -0
- package/src/lsp/completions.ts +20 -0
- package/src/lsp/hover.test.ts +69 -0
- package/src/lsp/hover.ts +68 -0
- package/src/package-cli.ts +28 -0
- package/src/plugin.test.ts +209 -0
- package/src/plugin.ts +915 -0
- package/src/serializer.test.ts +275 -0
- package/src/serializer.ts +278 -0
- package/src/spec/fetch.test.ts +24 -0
- package/src/spec/fetch.ts +68 -0
- package/src/spec/parse.test.ts +102 -0
- package/src/spec/parse.ts +477 -0
- package/src/testdata/manifests/configmap.yaml +7 -0
- package/src/testdata/manifests/deployment.yaml +22 -0
- package/src/testdata/manifests/full-app.yaml +61 -0
- package/src/testdata/manifests/secret.yaml +7 -0
- package/src/testdata/manifests/service.yaml +15 -0
- package/src/validate-cli.ts +21 -0
- package/src/validate.test.ts +29 -0
- package/src/validate.ts +46 -0
- package/src/variables.ts +36 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CronWorkload composite — CronJob + ServiceAccount + Role + RoleBinding.
|
|
3
|
+
*
|
|
4
|
+
* A higher-level construct for deploying scheduled workloads with
|
|
5
|
+
* proper RBAC permissions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface CronWorkloadProps {
|
|
9
|
+
/** Workload name — used in metadata and labels. */
|
|
10
|
+
name: string;
|
|
11
|
+
/** Container image. */
|
|
12
|
+
image: string;
|
|
13
|
+
/** Cron schedule expression (e.g., "0 * * * *"). */
|
|
14
|
+
schedule: string;
|
|
15
|
+
/** Command to run in the container. */
|
|
16
|
+
command?: string[];
|
|
17
|
+
/** Arguments to the command. */
|
|
18
|
+
args?: string[];
|
|
19
|
+
/** RBAC rules for the service account. */
|
|
20
|
+
rbacRules?: Array<{
|
|
21
|
+
apiGroups: string[];
|
|
22
|
+
resources: string[];
|
|
23
|
+
verbs: string[];
|
|
24
|
+
}>;
|
|
25
|
+
/** Job history limits. */
|
|
26
|
+
successfulJobsHistoryLimit?: number;
|
|
27
|
+
failedJobsHistoryLimit?: number;
|
|
28
|
+
/** Restart policy for the job (default: "OnFailure"). */
|
|
29
|
+
restartPolicy?: string;
|
|
30
|
+
/** Additional labels to apply to all resources. */
|
|
31
|
+
labels?: Record<string, string>;
|
|
32
|
+
/** Namespace for all resources. */
|
|
33
|
+
namespace?: string;
|
|
34
|
+
/** Environment variables. */
|
|
35
|
+
env?: Array<{ name: string; value: string }>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface CronWorkloadResult {
|
|
39
|
+
cronJob: Record<string, unknown>;
|
|
40
|
+
serviceAccount: Record<string, unknown>;
|
|
41
|
+
role: Record<string, unknown>;
|
|
42
|
+
roleBinding: Record<string, unknown>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Create a CronWorkload composite — returns prop objects for
|
|
47
|
+
* a CronJob, ServiceAccount, Role, and RoleBinding.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* import { CronWorkload } from "@intentius/chant-lexicon-k8s";
|
|
52
|
+
*
|
|
53
|
+
* const { cronJob, serviceAccount, role, roleBinding } = CronWorkload({
|
|
54
|
+
* name: "db-backup",
|
|
55
|
+
* image: "postgres:16",
|
|
56
|
+
* schedule: "0 2 * * *",
|
|
57
|
+
* command: ["pg_dump", "-h", "postgres", "mydb"],
|
|
58
|
+
* rbacRules: [
|
|
59
|
+
* { apiGroups: [""], resources: ["secrets"], verbs: ["get"] },
|
|
60
|
+
* ],
|
|
61
|
+
* });
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export function CronWorkload(props: CronWorkloadProps): CronWorkloadResult {
|
|
65
|
+
const {
|
|
66
|
+
name,
|
|
67
|
+
image,
|
|
68
|
+
schedule,
|
|
69
|
+
command,
|
|
70
|
+
args,
|
|
71
|
+
rbacRules = [],
|
|
72
|
+
successfulJobsHistoryLimit = 3,
|
|
73
|
+
failedJobsHistoryLimit = 1,
|
|
74
|
+
restartPolicy = "OnFailure",
|
|
75
|
+
labels: extraLabels = {},
|
|
76
|
+
namespace,
|
|
77
|
+
env,
|
|
78
|
+
} = props;
|
|
79
|
+
|
|
80
|
+
const saName = `${name}-sa`;
|
|
81
|
+
const roleName = `${name}-role`;
|
|
82
|
+
const bindingName = `${name}-binding`;
|
|
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
|
+
const cronJobProps: Record<string, unknown> = {
|
|
91
|
+
metadata: {
|
|
92
|
+
name,
|
|
93
|
+
...(namespace && { namespace }),
|
|
94
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "worker" },
|
|
95
|
+
},
|
|
96
|
+
spec: {
|
|
97
|
+
schedule,
|
|
98
|
+
successfulJobsHistoryLimit,
|
|
99
|
+
failedJobsHistoryLimit,
|
|
100
|
+
jobTemplate: {
|
|
101
|
+
spec: {
|
|
102
|
+
template: {
|
|
103
|
+
spec: {
|
|
104
|
+
serviceAccountName: saName,
|
|
105
|
+
restartPolicy,
|
|
106
|
+
containers: [
|
|
107
|
+
{
|
|
108
|
+
name,
|
|
109
|
+
image,
|
|
110
|
+
...(command && { command }),
|
|
111
|
+
...(args && { args }),
|
|
112
|
+
...(env && { env }),
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const serviceAccountProps: Record<string, unknown> = {
|
|
123
|
+
metadata: {
|
|
124
|
+
name: saName,
|
|
125
|
+
...(namespace && { namespace }),
|
|
126
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "worker" },
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const roleProps: Record<string, unknown> = {
|
|
131
|
+
metadata: {
|
|
132
|
+
name: roleName,
|
|
133
|
+
...(namespace && { namespace }),
|
|
134
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
|
|
135
|
+
},
|
|
136
|
+
rules: rbacRules.length > 0 ? rbacRules : [
|
|
137
|
+
{ apiGroups: [""], resources: ["pods"], verbs: ["get", "list"] },
|
|
138
|
+
],
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const roleBindingProps: Record<string, unknown> = {
|
|
142
|
+
metadata: {
|
|
143
|
+
name: bindingName,
|
|
144
|
+
...(namespace && { namespace }),
|
|
145
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
|
|
146
|
+
},
|
|
147
|
+
roleRef: {
|
|
148
|
+
apiGroup: "rbac.authorization.k8s.io",
|
|
149
|
+
kind: "Role",
|
|
150
|
+
name: roleName,
|
|
151
|
+
},
|
|
152
|
+
subjects: [
|
|
153
|
+
{
|
|
154
|
+
kind: "ServiceAccount",
|
|
155
|
+
name: saName,
|
|
156
|
+
...(namespace && { namespace }),
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
cronJob: cronJobProps,
|
|
163
|
+
serviceAccount: serviceAccountProps,
|
|
164
|
+
role: roleProps,
|
|
165
|
+
roleBinding: roleBindingProps,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { WebApp } from "./web-app";
|
|
2
|
+
export type { WebAppProps, WebAppResult } from "./web-app";
|
|
3
|
+
export { StatefulApp } from "./stateful-app";
|
|
4
|
+
export type { StatefulAppProps, StatefulAppResult } from "./stateful-app";
|
|
5
|
+
export { CronWorkload } from "./cron-workload";
|
|
6
|
+
export type { CronWorkloadProps, CronWorkloadResult } from "./cron-workload";
|
|
7
|
+
export { AutoscaledService } from "./autoscaled-service";
|
|
8
|
+
export type { AutoscaledServiceProps, AutoscaledServiceResult } from "./autoscaled-service";
|
|
9
|
+
export { WorkerPool } from "./worker-pool";
|
|
10
|
+
export type { WorkerPoolProps, WorkerPoolResult } from "./worker-pool";
|
|
11
|
+
export { NamespaceEnv } from "./namespace-env";
|
|
12
|
+
export type { NamespaceEnvProps, NamespaceEnvResult } from "./namespace-env";
|
|
13
|
+
export { NodeAgent } from "./node-agent";
|
|
14
|
+
export type { NodeAgentProps, NodeAgentResult } from "./node-agent";
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NamespaceEnv composite — Namespace + optional ResourceQuota + LimitRange + NetworkPolicy.
|
|
3
|
+
*
|
|
4
|
+
* A higher-level construct for multi-tenant namespace provisioning with
|
|
5
|
+
* resource guardrails and network isolation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface NamespaceEnvProps {
|
|
9
|
+
/** Namespace name. */
|
|
10
|
+
name: string;
|
|
11
|
+
/** Total namespace CPU quota (e.g., "8"). */
|
|
12
|
+
cpuQuota?: string;
|
|
13
|
+
/** Total namespace memory quota (e.g., "16Gi"). */
|
|
14
|
+
memoryQuota?: string;
|
|
15
|
+
/** Maximum pods in namespace. */
|
|
16
|
+
maxPods?: number;
|
|
17
|
+
/** LimitRange default CPU request (e.g., "100m"). */
|
|
18
|
+
defaultCpuRequest?: string;
|
|
19
|
+
/** LimitRange default memory request (e.g., "128Mi"). */
|
|
20
|
+
defaultMemoryRequest?: string;
|
|
21
|
+
/** LimitRange default CPU limit (e.g., "500m"). */
|
|
22
|
+
defaultCpuLimit?: string;
|
|
23
|
+
/** LimitRange default memory limit (e.g., "512Mi"). */
|
|
24
|
+
defaultMemoryLimit?: string;
|
|
25
|
+
/** Create default-deny ingress NetworkPolicy (default: true). */
|
|
26
|
+
defaultDenyIngress?: boolean;
|
|
27
|
+
/** Create default-deny egress NetworkPolicy (default: false). */
|
|
28
|
+
defaultDenyEgress?: boolean;
|
|
29
|
+
/** Additional labels to apply to all resources. */
|
|
30
|
+
labels?: Record<string, string>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface NamespaceEnvResult {
|
|
34
|
+
namespace: Record<string, unknown>;
|
|
35
|
+
resourceQuota?: Record<string, unknown>;
|
|
36
|
+
limitRange?: Record<string, unknown>;
|
|
37
|
+
networkPolicy?: Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create a NamespaceEnv composite — returns prop objects for
|
|
42
|
+
* a Namespace, optional ResourceQuota, LimitRange, and NetworkPolicy.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* import { NamespaceEnv } from "@intentius/chant-lexicon-k8s";
|
|
47
|
+
*
|
|
48
|
+
* const { namespace, resourceQuota, limitRange, networkPolicy } = NamespaceEnv({
|
|
49
|
+
* name: "team-alpha",
|
|
50
|
+
* cpuQuota: "8",
|
|
51
|
+
* memoryQuota: "16Gi",
|
|
52
|
+
* defaultCpuRequest: "100m",
|
|
53
|
+
* defaultMemoryRequest: "128Mi",
|
|
54
|
+
* defaultDenyIngress: true,
|
|
55
|
+
* });
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export function NamespaceEnv(props: NamespaceEnvProps): NamespaceEnvResult {
|
|
59
|
+
const {
|
|
60
|
+
name,
|
|
61
|
+
cpuQuota,
|
|
62
|
+
memoryQuota,
|
|
63
|
+
maxPods,
|
|
64
|
+
defaultCpuRequest,
|
|
65
|
+
defaultMemoryRequest,
|
|
66
|
+
defaultCpuLimit,
|
|
67
|
+
defaultMemoryLimit,
|
|
68
|
+
defaultDenyIngress = true,
|
|
69
|
+
defaultDenyEgress = false,
|
|
70
|
+
labels: extraLabels = {},
|
|
71
|
+
} = props;
|
|
72
|
+
|
|
73
|
+
const commonLabels: Record<string, string> = {
|
|
74
|
+
"app.kubernetes.io/name": name,
|
|
75
|
+
"app.kubernetes.io/managed-by": "chant",
|
|
76
|
+
...extraLabels,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const namespaceProps: Record<string, unknown> = {
|
|
80
|
+
metadata: {
|
|
81
|
+
name,
|
|
82
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "namespace" },
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const result: NamespaceEnvResult = {
|
|
87
|
+
namespace: namespaceProps,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// ResourceQuota — only if at least one quota prop is set
|
|
91
|
+
const hasQuota = cpuQuota || memoryQuota || maxPods !== undefined;
|
|
92
|
+
if (hasQuota) {
|
|
93
|
+
const hard: Record<string, unknown> = {};
|
|
94
|
+
if (cpuQuota) hard["limits.cpu"] = cpuQuota;
|
|
95
|
+
if (memoryQuota) hard["limits.memory"] = memoryQuota;
|
|
96
|
+
if (maxPods !== undefined) hard.pods = String(maxPods);
|
|
97
|
+
|
|
98
|
+
result.resourceQuota = {
|
|
99
|
+
metadata: {
|
|
100
|
+
name: `${name}-quota`,
|
|
101
|
+
namespace: name,
|
|
102
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "quota" },
|
|
103
|
+
},
|
|
104
|
+
spec: { hard },
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// LimitRange — only if at least one default limit prop is set
|
|
109
|
+
const hasLimits = defaultCpuRequest || defaultMemoryRequest || defaultCpuLimit || defaultMemoryLimit;
|
|
110
|
+
|
|
111
|
+
if (hasQuota && !hasLimits) {
|
|
112
|
+
console.warn(
|
|
113
|
+
`[NamespaceEnv] "${name}": ResourceQuota set but no LimitRange defaults. ` +
|
|
114
|
+
`Pods without explicit resource requests will fail to schedule.`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
if (hasLimits) {
|
|
118
|
+
const defaultLimits: Record<string, string> = {};
|
|
119
|
+
const defaultRequests: Record<string, string> = {};
|
|
120
|
+
|
|
121
|
+
if (defaultCpuLimit) defaultLimits.cpu = defaultCpuLimit;
|
|
122
|
+
if (defaultMemoryLimit) defaultLimits.memory = defaultMemoryLimit;
|
|
123
|
+
if (defaultCpuRequest) defaultRequests.cpu = defaultCpuRequest;
|
|
124
|
+
if (defaultMemoryRequest) defaultRequests.memory = defaultMemoryRequest;
|
|
125
|
+
|
|
126
|
+
const limit: Record<string, unknown> = { type: "Container" };
|
|
127
|
+
if (Object.keys(defaultLimits).length > 0) limit.default = defaultLimits;
|
|
128
|
+
if (Object.keys(defaultRequests).length > 0) limit.defaultRequest = defaultRequests;
|
|
129
|
+
|
|
130
|
+
result.limitRange = {
|
|
131
|
+
metadata: {
|
|
132
|
+
name: `${name}-limits`,
|
|
133
|
+
namespace: name,
|
|
134
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "limits" },
|
|
135
|
+
},
|
|
136
|
+
spec: {
|
|
137
|
+
limits: [limit],
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// NetworkPolicy — default-deny ingress and/or egress
|
|
143
|
+
const hasNetworkPolicy = defaultDenyIngress || defaultDenyEgress;
|
|
144
|
+
if (hasNetworkPolicy) {
|
|
145
|
+
const policyTypes: string[] = [];
|
|
146
|
+
if (defaultDenyIngress) policyTypes.push("Ingress");
|
|
147
|
+
if (defaultDenyEgress) policyTypes.push("Egress");
|
|
148
|
+
|
|
149
|
+
result.networkPolicy = {
|
|
150
|
+
metadata: {
|
|
151
|
+
name: `${name}-default-deny`,
|
|
152
|
+
namespace: name,
|
|
153
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "network-policy" },
|
|
154
|
+
},
|
|
155
|
+
spec: {
|
|
156
|
+
podSelector: {},
|
|
157
|
+
policyTypes,
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NodeAgent composite — DaemonSet + ServiceAccount + ClusterRole + ClusterRoleBinding + optional ConfigMap.
|
|
3
|
+
*
|
|
4
|
+
* A higher-level construct for per-node agents (Fluentd, Prometheus Node Exporter,
|
|
5
|
+
* security scanners) that need cluster-wide RBAC and tolerations.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface NodeAgentProps {
|
|
9
|
+
/** Agent name — used in metadata and labels. */
|
|
10
|
+
name: string;
|
|
11
|
+
/** Container image. */
|
|
12
|
+
image: string;
|
|
13
|
+
/** Metrics port (optional). */
|
|
14
|
+
port?: number;
|
|
15
|
+
/** Host path mounts. */
|
|
16
|
+
hostPaths?: Array<{
|
|
17
|
+
name: string;
|
|
18
|
+
hostPath: string;
|
|
19
|
+
mountPath: string;
|
|
20
|
+
readOnly?: boolean;
|
|
21
|
+
}>;
|
|
22
|
+
/** Config data — creates a ConfigMap mounted at /etc/{name}/. */
|
|
23
|
+
config?: Record<string, string>;
|
|
24
|
+
/** ClusterRole RBAC rules (required). */
|
|
25
|
+
rbacRules: Array<{
|
|
26
|
+
apiGroups: string[];
|
|
27
|
+
resources: string[];
|
|
28
|
+
verbs: string[];
|
|
29
|
+
}>;
|
|
30
|
+
/** Tolerate all taints so agent runs on every node (default: true). */
|
|
31
|
+
tolerateAllTaints?: boolean;
|
|
32
|
+
/** Namespace for namespaced resources (DaemonSet, ServiceAccount, ConfigMap). */
|
|
33
|
+
namespace?: string;
|
|
34
|
+
/** Additional labels to apply to all resources. */
|
|
35
|
+
labels?: Record<string, string>;
|
|
36
|
+
/** CPU request (default: "50m"). */
|
|
37
|
+
cpuRequest?: string;
|
|
38
|
+
/** Memory request (default: "64Mi"). */
|
|
39
|
+
memoryRequest?: string;
|
|
40
|
+
/** CPU limit (default: "200m"). */
|
|
41
|
+
cpuLimit?: string;
|
|
42
|
+
/** Memory limit (default: "128Mi"). */
|
|
43
|
+
memoryLimit?: string;
|
|
44
|
+
/** Environment variables for the container. */
|
|
45
|
+
env?: Array<{ name: string; value: string }>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface NodeAgentResult {
|
|
49
|
+
daemonSet: Record<string, unknown>;
|
|
50
|
+
serviceAccount: Record<string, unknown>;
|
|
51
|
+
clusterRole: Record<string, unknown>;
|
|
52
|
+
clusterRoleBinding: Record<string, unknown>;
|
|
53
|
+
configMap?: Record<string, unknown>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Create a NodeAgent composite — returns prop objects for
|
|
58
|
+
* a DaemonSet, ServiceAccount, ClusterRole, ClusterRoleBinding, and optional ConfigMap.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* import { NodeAgent } from "@intentius/chant-lexicon-k8s";
|
|
63
|
+
*
|
|
64
|
+
* const { daemonSet, serviceAccount, clusterRole, clusterRoleBinding } = NodeAgent({
|
|
65
|
+
* name: "log-collector",
|
|
66
|
+
* image: "fluentd:v1.16",
|
|
67
|
+
* hostPaths: [{ name: "varlog", hostPath: "/var/log", mountPath: "/var/log" }],
|
|
68
|
+
* rbacRules: [
|
|
69
|
+
* { apiGroups: [""], resources: ["pods", "namespaces"], verbs: ["get", "list", "watch"] },
|
|
70
|
+
* ],
|
|
71
|
+
* });
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export function NodeAgent(props: NodeAgentProps): NodeAgentResult {
|
|
75
|
+
const {
|
|
76
|
+
name,
|
|
77
|
+
image,
|
|
78
|
+
port,
|
|
79
|
+
hostPaths = [],
|
|
80
|
+
config,
|
|
81
|
+
rbacRules,
|
|
82
|
+
tolerateAllTaints = true,
|
|
83
|
+
namespace,
|
|
84
|
+
cpuRequest = "50m",
|
|
85
|
+
memoryRequest = "64Mi",
|
|
86
|
+
cpuLimit = "200m",
|
|
87
|
+
memoryLimit = "128Mi",
|
|
88
|
+
labels: extraLabels = {},
|
|
89
|
+
env,
|
|
90
|
+
} = props;
|
|
91
|
+
|
|
92
|
+
const saName = `${name}-sa`;
|
|
93
|
+
const clusterRoleName = `${name}-role`;
|
|
94
|
+
const bindingName = `${name}-binding`;
|
|
95
|
+
const configMapName = `${name}-config`;
|
|
96
|
+
|
|
97
|
+
const commonLabels: Record<string, string> = {
|
|
98
|
+
"app.kubernetes.io/name": name,
|
|
99
|
+
"app.kubernetes.io/managed-by": "chant",
|
|
100
|
+
...extraLabels,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Build volumes and volumeMounts from hostPaths
|
|
104
|
+
const volumes: Array<Record<string, unknown>> = [];
|
|
105
|
+
const volumeMounts: Array<Record<string, unknown>> = [];
|
|
106
|
+
|
|
107
|
+
for (const hp of hostPaths) {
|
|
108
|
+
volumes.push({
|
|
109
|
+
name: hp.name,
|
|
110
|
+
hostPath: { path: hp.hostPath },
|
|
111
|
+
});
|
|
112
|
+
volumeMounts.push({
|
|
113
|
+
name: hp.name,
|
|
114
|
+
mountPath: hp.mountPath,
|
|
115
|
+
readOnly: hp.readOnly !== false,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ConfigMap volume
|
|
120
|
+
if (config) {
|
|
121
|
+
volumes.push({
|
|
122
|
+
name: "config",
|
|
123
|
+
configMap: { name: configMapName },
|
|
124
|
+
});
|
|
125
|
+
volumeMounts.push({
|
|
126
|
+
name: "config",
|
|
127
|
+
mountPath: `/etc/${name}`,
|
|
128
|
+
readOnly: true,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const container: Record<string, unknown> = {
|
|
133
|
+
name,
|
|
134
|
+
image,
|
|
135
|
+
...(port && { ports: [{ containerPort: port, name: "metrics" }] }),
|
|
136
|
+
resources: {
|
|
137
|
+
requests: { cpu: cpuRequest, memory: memoryRequest },
|
|
138
|
+
limits: { cpu: cpuLimit, memory: memoryLimit },
|
|
139
|
+
},
|
|
140
|
+
...(env && { env }),
|
|
141
|
+
...(volumeMounts.length > 0 && { volumeMounts }),
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const podSpec: Record<string, unknown> = {
|
|
145
|
+
serviceAccountName: saName,
|
|
146
|
+
containers: [container],
|
|
147
|
+
...(volumes.length > 0 && { volumes }),
|
|
148
|
+
...(tolerateAllTaints && {
|
|
149
|
+
tolerations: [{ operator: "Exists" }],
|
|
150
|
+
}),
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const daemonSetProps: Record<string, unknown> = {
|
|
154
|
+
metadata: {
|
|
155
|
+
name,
|
|
156
|
+
...(namespace && { namespace }),
|
|
157
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "agent" },
|
|
158
|
+
},
|
|
159
|
+
spec: {
|
|
160
|
+
selector: { matchLabels: { "app.kubernetes.io/name": name } },
|
|
161
|
+
template: {
|
|
162
|
+
metadata: { labels: { "app.kubernetes.io/name": name, ...extraLabels } },
|
|
163
|
+
spec: podSpec,
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const serviceAccountProps: Record<string, unknown> = {
|
|
169
|
+
metadata: {
|
|
170
|
+
name: saName,
|
|
171
|
+
...(namespace && { namespace }),
|
|
172
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "agent" },
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// ClusterRole — cluster-scoped, no namespace
|
|
177
|
+
const clusterRoleProps: Record<string, unknown> = {
|
|
178
|
+
metadata: {
|
|
179
|
+
name: clusterRoleName,
|
|
180
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
|
|
181
|
+
},
|
|
182
|
+
rules: rbacRules,
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// ClusterRoleBinding — cluster-scoped, no namespace
|
|
186
|
+
const clusterRoleBindingProps: Record<string, unknown> = {
|
|
187
|
+
metadata: {
|
|
188
|
+
name: bindingName,
|
|
189
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
|
|
190
|
+
},
|
|
191
|
+
roleRef: {
|
|
192
|
+
apiGroup: "rbac.authorization.k8s.io",
|
|
193
|
+
kind: "ClusterRole",
|
|
194
|
+
name: clusterRoleName,
|
|
195
|
+
},
|
|
196
|
+
subjects: [
|
|
197
|
+
{
|
|
198
|
+
kind: "ServiceAccount",
|
|
199
|
+
name: saName,
|
|
200
|
+
...(namespace && { namespace }),
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const result: NodeAgentResult = {
|
|
206
|
+
daemonSet: daemonSetProps,
|
|
207
|
+
serviceAccount: serviceAccountProps,
|
|
208
|
+
clusterRole: clusterRoleProps,
|
|
209
|
+
clusterRoleBinding: clusterRoleBindingProps,
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
if (config) {
|
|
213
|
+
result.configMap = {
|
|
214
|
+
metadata: {
|
|
215
|
+
name: configMapName,
|
|
216
|
+
...(namespace && { namespace }),
|
|
217
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "config" },
|
|
218
|
+
},
|
|
219
|
+
data: config,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return result;
|
|
224
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StatefulApp composite — StatefulSet + headless Service + PVC template.
|
|
3
|
+
*
|
|
4
|
+
* A higher-level construct for deploying stateful applications
|
|
5
|
+
* like databases, caches, and message queues.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface StatefulAppProps {
|
|
9
|
+
/** Application name — used in metadata and labels. */
|
|
10
|
+
name: string;
|
|
11
|
+
/** Container image (e.g., "postgres:16"). */
|
|
12
|
+
image: string;
|
|
13
|
+
/** Container port (default: 5432). */
|
|
14
|
+
port?: number;
|
|
15
|
+
/** Number of replicas (default: 1). */
|
|
16
|
+
replicas?: number;
|
|
17
|
+
/** Storage size for the PVC (e.g., "10Gi"). */
|
|
18
|
+
storageSize?: string;
|
|
19
|
+
/** Storage class name. */
|
|
20
|
+
storageClassName?: string;
|
|
21
|
+
/** Volume mount path inside the container (default: "/data"). */
|
|
22
|
+
mountPath?: string;
|
|
23
|
+
/** Additional labels to apply to all resources. */
|
|
24
|
+
labels?: Record<string, string>;
|
|
25
|
+
/** CPU limit (e.g., "1"). */
|
|
26
|
+
cpuLimit?: string;
|
|
27
|
+
/** Memory limit (e.g., "1Gi"). */
|
|
28
|
+
memoryLimit?: string;
|
|
29
|
+
/** Namespace for all resources. */
|
|
30
|
+
namespace?: string;
|
|
31
|
+
/** Environment variables for the container. */
|
|
32
|
+
env?: Array<{ name: string; value: string }>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface StatefulAppResult {
|
|
36
|
+
statefulSet: Record<string, unknown>;
|
|
37
|
+
service: Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create a StatefulApp composite — returns prop objects for
|
|
42
|
+
* a StatefulSet and headless Service.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* import { StatefulApp } from "@intentius/chant-lexicon-k8s";
|
|
47
|
+
*
|
|
48
|
+
* const { statefulSet, service } = StatefulApp({
|
|
49
|
+
* name: "postgres",
|
|
50
|
+
* image: "postgres:16",
|
|
51
|
+
* port: 5432,
|
|
52
|
+
* storageSize: "20Gi",
|
|
53
|
+
* env: [{ name: "POSTGRES_PASSWORD", value: "changeme" }],
|
|
54
|
+
* });
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export function StatefulApp(props: StatefulAppProps): StatefulAppResult {
|
|
58
|
+
const {
|
|
59
|
+
name,
|
|
60
|
+
image,
|
|
61
|
+
port = 5432,
|
|
62
|
+
replicas = 1,
|
|
63
|
+
storageSize = "10Gi",
|
|
64
|
+
storageClassName,
|
|
65
|
+
mountPath = "/data",
|
|
66
|
+
labels: extraLabels = {},
|
|
67
|
+
cpuLimit = "1",
|
|
68
|
+
memoryLimit = "1Gi",
|
|
69
|
+
namespace,
|
|
70
|
+
env,
|
|
71
|
+
} = props;
|
|
72
|
+
|
|
73
|
+
const commonLabels: Record<string, string> = {
|
|
74
|
+
"app.kubernetes.io/name": name,
|
|
75
|
+
"app.kubernetes.io/managed-by": "chant",
|
|
76
|
+
...extraLabels,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const statefulSetProps: Record<string, unknown> = {
|
|
80
|
+
metadata: {
|
|
81
|
+
name,
|
|
82
|
+
...(namespace && { namespace }),
|
|
83
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "database" },
|
|
84
|
+
},
|
|
85
|
+
spec: {
|
|
86
|
+
serviceName: name,
|
|
87
|
+
replicas,
|
|
88
|
+
selector: { matchLabels: { "app.kubernetes.io/name": name } },
|
|
89
|
+
template: {
|
|
90
|
+
metadata: { labels: { "app.kubernetes.io/name": name, ...extraLabels } },
|
|
91
|
+
spec: {
|
|
92
|
+
containers: [
|
|
93
|
+
{
|
|
94
|
+
name,
|
|
95
|
+
image,
|
|
96
|
+
ports: [{ containerPort: port, name: "app" }],
|
|
97
|
+
resources: {
|
|
98
|
+
limits: { cpu: cpuLimit, memory: memoryLimit },
|
|
99
|
+
},
|
|
100
|
+
volumeMounts: [{ name: "data", mountPath }],
|
|
101
|
+
...(env && { env }),
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
volumeClaimTemplates: [
|
|
107
|
+
{
|
|
108
|
+
metadata: { name: "data" },
|
|
109
|
+
spec: {
|
|
110
|
+
accessModes: ["ReadWriteOnce"],
|
|
111
|
+
...(storageClassName && { storageClassName }),
|
|
112
|
+
resources: { requests: { storage: storageSize } },
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Headless service (clusterIP: None) for StatefulSet DNS
|
|
120
|
+
const serviceProps: Record<string, unknown> = {
|
|
121
|
+
metadata: {
|
|
122
|
+
name,
|
|
123
|
+
...(namespace && { namespace }),
|
|
124
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "database" },
|
|
125
|
+
},
|
|
126
|
+
spec: {
|
|
127
|
+
selector: { "app.kubernetes.io/name": name },
|
|
128
|
+
ports: [{ port, targetPort: port, protocol: "TCP", name: "app" }],
|
|
129
|
+
clusterIP: "None",
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
return { statefulSet: statefulSetProps, service: serviceProps };
|
|
134
|
+
}
|