@intentius/chant-lexicon-k8s 0.49.0 → 0.50.0
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/composites/index.d.ts +2 -0
- package/dist/composites/index.d.ts.map +1 -1
- package/dist/composites/operator-stack.d.ts +183 -0
- package/dist/composites/operator-stack.d.ts.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +35 -35
- package/dist/lint/post-synth/argo-helpers.d.ts.map +1 -1
- package/dist/lint/post-synth/crd-schema-helpers.d.ts.map +1 -1
- package/dist/lint/post-synth/k8s-helpers.d.ts +28 -5
- package/dist/lint/post-synth/k8s-helpers.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8005.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8006.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8041.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8042.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8101.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8102.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8103.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8104.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8105.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8201.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8202.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8203.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8204.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8205.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8207.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8208.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8209.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8301.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8302.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8303.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8304.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8305.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8306.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8401.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8402.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8403.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8404.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8405.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8406.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8407.d.ts.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/op/builders.d.ts +69 -0
- package/dist/op/builders.d.ts.map +1 -0
- package/dist/rules/argo-helpers.ts +2 -6
- package/dist/rules/crd-schema-helpers.ts +4 -6
- package/dist/rules/k8s-helpers.ts +32 -23
- package/dist/rules/wk8005.ts +27 -32
- package/dist/rules/wk8006.ts +33 -38
- package/dist/rules/wk8041.ts +19 -24
- package/dist/rules/wk8042.ts +17 -22
- package/dist/rules/wk8101.ts +29 -34
- package/dist/rules/wk8102.ts +13 -18
- package/dist/rules/wk8103.ts +17 -22
- package/dist/rules/wk8104.ts +30 -35
- package/dist/rules/wk8105.ts +16 -21
- package/dist/rules/wk8201.ts +26 -31
- package/dist/rules/wk8202.ts +17 -22
- package/dist/rules/wk8203.ts +17 -22
- package/dist/rules/wk8204.ts +46 -51
- package/dist/rules/wk8205.ts +27 -32
- package/dist/rules/wk8207.ts +14 -19
- package/dist/rules/wk8208.ts +14 -19
- package/dist/rules/wk8209.ts +14 -19
- package/dist/rules/wk8301.ts +25 -30
- package/dist/rules/wk8302.ts +19 -24
- package/dist/rules/wk8303.ts +23 -26
- package/dist/rules/wk8304.ts +35 -40
- package/dist/rules/wk8305.ts +39 -42
- package/dist/rules/wk8306.ts +20 -25
- package/dist/rules/wk8401.ts +59 -64
- package/dist/rules/wk8402.ts +13 -18
- package/dist/rules/wk8403.ts +32 -37
- package/dist/rules/wk8404.ts +15 -21
- package/dist/rules/wk8405.ts +15 -18
- package/dist/rules/wk8406.ts +29 -34
- package/dist/rules/wk8407.ts +17 -22
- package/dist/serializer.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/composites/composites.test.ts +283 -0
- package/src/composites/index.ts +4 -0
- package/src/composites/operator-stack.ts +385 -0
- package/src/index.ts +11 -0
- package/src/lint/post-synth/argo-helpers.ts +2 -6
- package/src/lint/post-synth/crd-schema-helpers.ts +4 -6
- package/src/lint/post-synth/k8s-helpers.test.ts +26 -9
- package/src/lint/post-synth/k8s-helpers.ts +32 -23
- package/src/lint/post-synth/wk8005.ts +27 -32
- package/src/lint/post-synth/wk8006.ts +33 -38
- package/src/lint/post-synth/wk8041.ts +19 -24
- package/src/lint/post-synth/wk8042.ts +17 -22
- package/src/lint/post-synth/wk8101.ts +29 -34
- package/src/lint/post-synth/wk8102.ts +13 -18
- package/src/lint/post-synth/wk8103.ts +17 -22
- package/src/lint/post-synth/wk8104.ts +30 -35
- package/src/lint/post-synth/wk8105.ts +16 -21
- package/src/lint/post-synth/wk8201.ts +26 -31
- package/src/lint/post-synth/wk8202.ts +17 -22
- package/src/lint/post-synth/wk8203.ts +17 -22
- package/src/lint/post-synth/wk8204.ts +46 -51
- package/src/lint/post-synth/wk8205.ts +27 -32
- package/src/lint/post-synth/wk8207.ts +14 -19
- package/src/lint/post-synth/wk8208.ts +14 -19
- package/src/lint/post-synth/wk8209.ts +14 -19
- package/src/lint/post-synth/wk8301.ts +25 -30
- package/src/lint/post-synth/wk8302.ts +19 -24
- package/src/lint/post-synth/wk8303.ts +23 -26
- package/src/lint/post-synth/wk8304.ts +35 -40
- package/src/lint/post-synth/wk8305.ts +39 -42
- package/src/lint/post-synth/wk8306.ts +20 -25
- package/src/lint/post-synth/wk8401.ts +59 -64
- package/src/lint/post-synth/wk8402.ts +13 -18
- package/src/lint/post-synth/wk8403.ts +32 -37
- package/src/lint/post-synth/wk8404.ts +15 -21
- package/src/lint/post-synth/wk8405.ts +15 -18
- package/src/lint/post-synth/wk8406.ts +29 -34
- package/src/lint/post-synth/wk8407.ts +17 -22
- package/src/op/builders.test.ts +82 -0
- package/src/op/builders.ts +95 -0
- package/src/serializer.ts +9 -1
package/dist/rules/wk8401.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
11
|
-
import {
|
|
11
|
+
import { docsToManifests, parseMemoryBytes } from "./k8s-helpers";
|
|
12
12
|
|
|
13
13
|
export const wk8401: PostSynthCheck = {
|
|
14
14
|
id: "WK8401",
|
|
@@ -17,77 +17,72 @@ export const wk8401: PostSynthCheck = {
|
|
|
17
17
|
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
18
18
|
const diagnostics: PostSynthDiagnostic[] = [];
|
|
19
19
|
|
|
20
|
-
for (const
|
|
21
|
-
|
|
22
|
-
const manifests = parseK8sManifests(yaml);
|
|
20
|
+
for (const manifest of docsToManifests(ctx)) {
|
|
21
|
+
if (manifest.kind !== "RayCluster") continue;
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
const clusterName = manifest.metadata?.name ?? "RayCluster";
|
|
24
|
+
const spec = manifest.spec as Record<string, unknown> | undefined;
|
|
25
|
+
if (!spec) continue;
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
const spec = manifest.spec as Record<string, unknown> | undefined;
|
|
29
|
-
if (!spec) continue;
|
|
27
|
+
const groups: Array<{ label: string; templateSpec: Record<string, unknown> }> = [];
|
|
30
28
|
|
|
31
|
-
|
|
29
|
+
// Head group
|
|
30
|
+
const headGroupSpec = spec.headGroupSpec as Record<string, unknown> | undefined;
|
|
31
|
+
if (headGroupSpec) {
|
|
32
|
+
const tmpl = headGroupSpec.template as Record<string, unknown> | undefined;
|
|
33
|
+
const podSpec = tmpl?.spec as Record<string, unknown> | undefined;
|
|
34
|
+
if (podSpec) groups.push({ label: "head", templateSpec: podSpec });
|
|
35
|
+
}
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
// Worker groups
|
|
38
|
+
const workerGroupSpecs = spec.workerGroupSpecs as Array<Record<string, unknown>> | undefined;
|
|
39
|
+
if (Array.isArray(workerGroupSpecs)) {
|
|
40
|
+
for (const wg of workerGroupSpecs) {
|
|
41
|
+
const name = (wg.groupName as string | undefined) ?? "worker";
|
|
42
|
+
const tmpl = wg.template as Record<string, unknown> | undefined;
|
|
37
43
|
const podSpec = tmpl?.spec as Record<string, unknown> | undefined;
|
|
38
|
-
if (podSpec) groups.push({ label: "
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Worker groups
|
|
42
|
-
const workerGroupSpecs = spec.workerGroupSpecs as Array<Record<string, unknown>> | undefined;
|
|
43
|
-
if (Array.isArray(workerGroupSpecs)) {
|
|
44
|
-
for (const wg of workerGroupSpecs) {
|
|
45
|
-
const name = (wg.groupName as string | undefined) ?? "worker";
|
|
46
|
-
const tmpl = wg.template as Record<string, unknown> | undefined;
|
|
47
|
-
const podSpec = tmpl?.spec as Record<string, unknown> | undefined;
|
|
48
|
-
if (podSpec) groups.push({ label: `worker "${name}"`, templateSpec: podSpec });
|
|
49
|
-
}
|
|
44
|
+
if (podSpec) groups.push({ label: `worker "${name}"`, templateSpec: podSpec });
|
|
50
45
|
}
|
|
46
|
+
}
|
|
51
47
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
break; // Only check the first container per group
|
|
48
|
+
for (const { label, templateSpec } of groups) {
|
|
49
|
+
// Find the emptyDir Memory volume (dshm)
|
|
50
|
+
const volumes = templateSpec.volumes as Array<Record<string, unknown>> | undefined;
|
|
51
|
+
if (!Array.isArray(volumes)) continue;
|
|
52
|
+
|
|
53
|
+
for (const vol of volumes) {
|
|
54
|
+
const emptyDir = vol.emptyDir as Record<string, unknown> | undefined;
|
|
55
|
+
if (!emptyDir || emptyDir.medium !== "Memory") continue;
|
|
56
|
+
|
|
57
|
+
const sizeLimit = emptyDir.sizeLimit as string | undefined;
|
|
58
|
+
if (!sizeLimit) continue;
|
|
59
|
+
|
|
60
|
+
const shmBytes = parseMemoryBytes(sizeLimit);
|
|
61
|
+
if (isNaN(shmBytes)) continue;
|
|
62
|
+
|
|
63
|
+
// Find memory limit from the first container that mounts this volume
|
|
64
|
+
const containers = templateSpec.containers as Array<Record<string, unknown>> | undefined;
|
|
65
|
+
if (!Array.isArray(containers)) continue;
|
|
66
|
+
|
|
67
|
+
for (const container of containers) {
|
|
68
|
+
const resources = container.resources as Record<string, unknown> | undefined;
|
|
69
|
+
const limits = resources?.limits as Record<string, unknown> | undefined;
|
|
70
|
+
const memLimit = limits?.memory as string | undefined;
|
|
71
|
+
if (!memLimit) continue;
|
|
72
|
+
|
|
73
|
+
const memBytes = parseMemoryBytes(memLimit);
|
|
74
|
+
if (isNaN(memBytes)) continue;
|
|
75
|
+
|
|
76
|
+
if (shmBytes > memBytes) {
|
|
77
|
+
diagnostics.push({
|
|
78
|
+
checkId: "WK8401",
|
|
79
|
+
severity: "error",
|
|
80
|
+
message: `RayCluster "${clusterName}" ${label}: shmSize "${sizeLimit}" exceeds memory limit "${memLimit}" — pod will not schedule`,
|
|
81
|
+
entity: clusterName,
|
|
82
|
+
lexicon: "k8s",
|
|
83
|
+
});
|
|
90
84
|
}
|
|
85
|
+
break; // Only check the first container per group
|
|
91
86
|
}
|
|
92
87
|
}
|
|
93
88
|
}
|
package/dist/rules/wk8402.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
10
|
-
import {
|
|
10
|
+
import { docsToManifests } from "./k8s-helpers";
|
|
11
11
|
|
|
12
12
|
export const wk8402: PostSynthCheck = {
|
|
13
13
|
id: "WK8402",
|
|
@@ -16,25 +16,20 @@ export const wk8402: PostSynthCheck = {
|
|
|
16
16
|
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
17
17
|
const diagnostics: PostSynthDiagnostic[] = [];
|
|
18
18
|
|
|
19
|
-
for (const
|
|
20
|
-
|
|
21
|
-
const manifests = parseK8sManifests(yaml);
|
|
19
|
+
for (const manifest of docsToManifests(ctx)) {
|
|
20
|
+
if (manifest.kind !== "RayCluster") continue;
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const name = manifest.metadata?.name ?? "RayCluster";
|
|
23
|
+
const rayVersion = (manifest.spec as Record<string, unknown> | undefined)?.rayVersion;
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
entity: name,
|
|
35
|
-
lexicon: "k8s",
|
|
36
|
-
});
|
|
37
|
-
}
|
|
25
|
+
if (!rayVersion) {
|
|
26
|
+
diagnostics.push({
|
|
27
|
+
checkId: "WK8402",
|
|
28
|
+
severity: "warning",
|
|
29
|
+
message: `RayCluster "${name}" is missing spec.rayVersion — KubeRay autoscaler will pull the "latest" image tag`,
|
|
30
|
+
entity: name,
|
|
31
|
+
lexicon: "k8s",
|
|
32
|
+
});
|
|
38
33
|
}
|
|
39
34
|
}
|
|
40
35
|
|
package/dist/rules/wk8403.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
10
|
-
import {
|
|
10
|
+
import { docsToManifests, extractRayVersion } from "./k8s-helpers";
|
|
11
11
|
|
|
12
12
|
export const wk8403: PostSynthCheck = {
|
|
13
13
|
id: "WK8403",
|
|
@@ -16,42 +16,37 @@ export const wk8403: PostSynthCheck = {
|
|
|
16
16
|
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
17
17
|
const diagnostics: PostSynthDiagnostic[] = [];
|
|
18
18
|
|
|
19
|
-
for (const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
message: `RayCluster "${clusterName}": spec.rayVersion "${rayVersion}" does not match head image tag "${imageVersion}" — autoscaler may run a mismatched Ray version`,
|
|
51
|
-
entity: clusterName,
|
|
52
|
-
lexicon: "k8s",
|
|
53
|
-
});
|
|
54
|
-
}
|
|
19
|
+
for (const manifest of docsToManifests(ctx)) {
|
|
20
|
+
if (manifest.kind !== "RayCluster") continue;
|
|
21
|
+
|
|
22
|
+
const clusterName = manifest.metadata?.name ?? "RayCluster";
|
|
23
|
+
const spec = manifest.spec as Record<string, unknown> | undefined;
|
|
24
|
+
if (!spec) continue;
|
|
25
|
+
|
|
26
|
+
const rayVersion = spec.rayVersion as string | undefined;
|
|
27
|
+
if (!rayVersion) continue; // WK8402 covers the missing case
|
|
28
|
+
|
|
29
|
+
// Extract version from head container image
|
|
30
|
+
const headGroupSpec = spec.headGroupSpec as Record<string, unknown> | undefined;
|
|
31
|
+
const tmpl = headGroupSpec?.template as Record<string, unknown> | undefined;
|
|
32
|
+
const podSpec = tmpl?.spec as Record<string, unknown> | undefined;
|
|
33
|
+
const containers = podSpec?.containers as Array<Record<string, unknown>> | undefined;
|
|
34
|
+
if (!Array.isArray(containers) || containers.length === 0) continue;
|
|
35
|
+
|
|
36
|
+
const image = containers[0].image as string | undefined;
|
|
37
|
+
if (!image) continue;
|
|
38
|
+
|
|
39
|
+
const imageVersion = extractRayVersion(image);
|
|
40
|
+
if (!imageVersion) continue; // Can't determine version from tag
|
|
41
|
+
|
|
42
|
+
if (imageVersion !== rayVersion) {
|
|
43
|
+
diagnostics.push({
|
|
44
|
+
checkId: "WK8403",
|
|
45
|
+
severity: "warning",
|
|
46
|
+
message: `RayCluster "${clusterName}": spec.rayVersion "${rayVersion}" does not match head image tag "${imageVersion}" — autoscaler may run a mismatched Ray version`,
|
|
47
|
+
entity: clusterName,
|
|
48
|
+
lexicon: "k8s",
|
|
49
|
+
});
|
|
55
50
|
}
|
|
56
51
|
}
|
|
57
52
|
|
package/dist/rules/wk8404.ts
CHANGED
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
12
12
|
import {
|
|
13
|
-
|
|
14
|
-
parseK8sManifests,
|
|
13
|
+
docsToManifests,
|
|
15
14
|
extractContainers,
|
|
16
15
|
extractPodSpec,
|
|
17
16
|
requestsGpu,
|
|
@@ -26,28 +25,23 @@ export const wk8404: PostSynthCheck = {
|
|
|
26
25
|
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
27
26
|
const diagnostics: PostSynthDiagnostic[] = [];
|
|
28
27
|
|
|
29
|
-
for (const
|
|
30
|
-
|
|
31
|
-
const manifests = parseK8sManifests(yaml);
|
|
28
|
+
for (const manifest of docsToManifests(ctx)) {
|
|
29
|
+
if (!manifest.kind || !GPU_POD_KINDS.has(manifest.kind)) continue;
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
const containers = extractContainers(manifest);
|
|
32
|
+
if (!requestsGpu(containers)) continue;
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
const podSpec = extractPodSpec(manifest);
|
|
35
|
+
if (toleratesGpu(podSpec?.tolerations)) continue;
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
entity: resourceName,
|
|
48
|
-
lexicon: "k8s",
|
|
49
|
-
});
|
|
50
|
-
}
|
|
37
|
+
const resourceName = manifest.metadata?.name ?? manifest.kind;
|
|
38
|
+
diagnostics.push({
|
|
39
|
+
checkId: "WK8404",
|
|
40
|
+
severity: "error",
|
|
41
|
+
message: `${manifest.kind} "${resourceName}" requests nvidia.com/gpu but has no toleration for the nvidia.com/gpu taint — it will not schedule onto a tainted GPU node pool`,
|
|
42
|
+
entity: resourceName,
|
|
43
|
+
lexicon: "k8s",
|
|
44
|
+
});
|
|
51
45
|
}
|
|
52
46
|
|
|
53
47
|
return diagnostics;
|
package/dist/rules/wk8405.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
13
|
-
import {
|
|
13
|
+
import { docsToManifests } from "./k8s-helpers";
|
|
14
14
|
import type { K8sManifest } from "./k8s-helpers";
|
|
15
15
|
|
|
16
16
|
/** `app.kubernetes.io/component` values this lexicon's composites use for serving workloads. */
|
|
@@ -30,27 +30,24 @@ export const wk8405: PostSynthCheck = {
|
|
|
30
30
|
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
31
31
|
const diagnostics: PostSynthDiagnostic[] = [];
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
const yaml = getPrimaryOutput(output);
|
|
35
|
-
const manifests = parseK8sManifests(yaml);
|
|
33
|
+
const manifests = docsToManifests(ctx);
|
|
36
34
|
|
|
37
|
-
|
|
35
|
+
const pdbSelectors = collectPdbSelectors(manifests);
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
for (const manifest of manifests) {
|
|
38
|
+
if (!isServingWorkload(manifest)) continue;
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
const labels = manifest.metadata?.labels;
|
|
41
|
+
const resourceName = manifest.metadata?.name ?? manifest.kind ?? "workload";
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
43
|
+
if (!labels || !hasCoveringPdb(labels, pdbSelectors)) {
|
|
44
|
+
diagnostics.push({
|
|
45
|
+
checkId: "WK8405",
|
|
46
|
+
severity: "info",
|
|
47
|
+
message: `${manifest.kind} "${resourceName}" is a serving workload with no PodDisruptionBudget — add a PDB so a node drain or cluster upgrade cannot take down every replica at once`,
|
|
48
|
+
entity: resourceName,
|
|
49
|
+
lexicon: "k8s",
|
|
50
|
+
});
|
|
54
51
|
}
|
|
55
52
|
}
|
|
56
53
|
|
package/dist/rules/wk8406.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
12
|
-
import {
|
|
12
|
+
import { docsToManifests, extractContainers, GPU_POD_KINDS, NVIDIA_GPU_RESOURCE } from "./k8s-helpers";
|
|
13
13
|
|
|
14
14
|
export const wk8406: PostSynthCheck = {
|
|
15
15
|
id: "WK8406",
|
|
@@ -18,39 +18,34 @@ export const wk8406: PostSynthCheck = {
|
|
|
18
18
|
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
19
19
|
const diagnostics: PostSynthDiagnostic[] = [];
|
|
20
20
|
|
|
21
|
-
for (const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (!limits
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
message: `GPU container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" is missing resource limits for ${missing.join(", ")} — a GPU pod without cpu/memory limits can starve its (scarce, expensive) node`,
|
|
50
|
-
entity: resourceName,
|
|
51
|
-
lexicon: "k8s",
|
|
52
|
-
});
|
|
53
|
-
}
|
|
21
|
+
for (const manifest of docsToManifests(ctx)) {
|
|
22
|
+
if (!manifest.kind || !GPU_POD_KINDS.has(manifest.kind)) continue;
|
|
23
|
+
|
|
24
|
+
const containers = extractContainers(manifest);
|
|
25
|
+
const resourceName = manifest.metadata?.name ?? manifest.kind;
|
|
26
|
+
|
|
27
|
+
for (const container of containers) {
|
|
28
|
+
const requests = container.resources?.requests as Record<string, unknown> | undefined;
|
|
29
|
+
const limits = container.resources?.limits as Record<string, unknown> | undefined;
|
|
30
|
+
const requestsGpu = Boolean(requests?.[NVIDIA_GPU_RESOURCE]) || Boolean(limits?.[NVIDIA_GPU_RESOURCE]);
|
|
31
|
+
if (!requestsGpu) continue;
|
|
32
|
+
|
|
33
|
+
const missing: string[] = [];
|
|
34
|
+
if (!limits || typeof limits !== "object") {
|
|
35
|
+
missing.push("cpu", "memory");
|
|
36
|
+
} else {
|
|
37
|
+
if (!limits.cpu) missing.push("cpu");
|
|
38
|
+
if (!limits.memory) missing.push("memory");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (missing.length > 0) {
|
|
42
|
+
diagnostics.push({
|
|
43
|
+
checkId: "WK8406",
|
|
44
|
+
severity: "warning",
|
|
45
|
+
message: `GPU container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" is missing resource limits for ${missing.join(", ")} — a GPU pod without cpu/memory limits can starve its (scarce, expensive) node`,
|
|
46
|
+
entity: resourceName,
|
|
47
|
+
lexicon: "k8s",
|
|
48
|
+
});
|
|
54
49
|
}
|
|
55
50
|
}
|
|
56
51
|
}
|
package/dist/rules/wk8407.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
15
|
-
import {
|
|
15
|
+
import { docsToManifests } from "./k8s-helpers";
|
|
16
16
|
|
|
17
17
|
/** Trailing path segments that mean "no explicit version" even though present. */
|
|
18
18
|
const FLOATING_TAGS = new Set(["latest", "main", "master", "head"]);
|
|
@@ -40,29 +40,24 @@ export const wk8407: PostSynthCheck = {
|
|
|
40
40
|
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
41
41
|
const diagnostics: PostSynthDiagnostic[] = [];
|
|
42
42
|
|
|
43
|
-
for (const
|
|
44
|
-
|
|
45
|
-
const manifests = parseK8sManifests(yaml);
|
|
43
|
+
for (const manifest of docsToManifests(ctx)) {
|
|
44
|
+
if (manifest.kind !== "InferenceService") continue;
|
|
46
45
|
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
const spec = manifest.spec as Record<string, unknown> | undefined;
|
|
47
|
+
const predictor = spec?.predictor as Record<string, unknown> | undefined;
|
|
48
|
+
const model = predictor?.model as Record<string, unknown> | undefined;
|
|
49
|
+
const storageUri = model?.storageUri as string | undefined;
|
|
50
|
+
if (!storageUri) continue;
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
checkId: "WK8407",
|
|
60
|
-
severity: "warning",
|
|
61
|
-
message: `InferenceService "${resourceName}": model storageUri "${storageUri}" has no explicit version segment — pin it (e.g. via the Model composite's required \`version\`) so a redeploy can't silently pick up different weights`,
|
|
62
|
-
entity: resourceName,
|
|
63
|
-
lexicon: "k8s",
|
|
64
|
-
});
|
|
65
|
-
}
|
|
52
|
+
if (!isPinnedStorageUri(storageUri)) {
|
|
53
|
+
const resourceName = manifest.metadata?.name ?? "InferenceService";
|
|
54
|
+
diagnostics.push({
|
|
55
|
+
checkId: "WK8407",
|
|
56
|
+
severity: "warning",
|
|
57
|
+
message: `InferenceService "${resourceName}": model storageUri "${storageUri}" has no explicit version segment — pin it (e.g. via the Model composite's required \`version\`) so a redeploy can't silently pick up different weights`,
|
|
58
|
+
entity: resourceName,
|
|
59
|
+
lexicon: "k8s",
|
|
60
|
+
});
|
|
66
61
|
}
|
|
67
62
|
}
|
|
68
63
|
|
package/dist/serializer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAsC,MAAM,6BAA6B,CAAC;AA+MlG;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAsC,MAAM,6BAA6B,CAAC;AA+MlG;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,UAiN3B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-k8s",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.50.0",
|
|
4
4
|
"description": "Kubernetes lexicon for chant — declarative IaC in TypeScript",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://intentius.io/chant",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"@types/js-yaml": "^4.0.9"
|
|
77
77
|
},
|
|
78
78
|
"optionalDependencies": {
|
|
79
|
-
"@intentius/chant-k8s-client": "^0.
|
|
79
|
+
"@intentius/chant-k8s-client": "^0.50.0"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
82
|
"@intentius/chant": "*",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
},
|
|
85
85
|
"peerDependencies": {
|
|
86
86
|
"zod": "^4.3.6",
|
|
87
|
-
"@intentius/chant": "^0.
|
|
87
|
+
"@intentius/chant": "^0.50.0",
|
|
88
88
|
"typescript": "^5.9.3"
|
|
89
89
|
}
|
|
90
90
|
}
|