@intentius/chant-lexicon-k8s 0.46.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/components/kubectl-apply.d.ts +5 -0
- package/dist/components/kubectl-apply.d.ts.map +1 -1
- package/dist/composites/flux-app.d.ts +12 -0
- package/dist/composites/flux-app.d.ts.map +1 -1
- package/dist/composites/index.d.ts +8 -0
- package/dist/composites/index.d.ts.map +1 -1
- package/dist/composites/inference-service.d.ts +86 -0
- package/dist/composites/inference-service.d.ts.map +1 -0
- package/dist/composites/model.d.ts +80 -0
- package/dist/composites/model.d.ts.map +1 -0
- package/dist/composites/operator-stack.d.ts +183 -0
- package/dist/composites/operator-stack.d.ts.map +1 -0
- package/dist/composites/vllm-serving-runtime.d.ts +100 -0
- package/dist/composites/vllm-serving-runtime.d.ts.map +1 -0
- package/dist/crd/crd-sources.d.ts.map +1 -1
- package/dist/deep-observe-hooks.d.ts.map +1 -1
- package/dist/generated/index.d.ts +167 -40
- package/dist/generated/index.d.ts.map +1 -1
- package/dist/group-namespace.d.ts.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +41 -33
- package/dist/kube/apply.d.ts.map +1 -1
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- 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/index.d.ts.map +1 -1
- package/dist/lint/post-synth/k8s-helpers.d.ts +56 -6
- package/dist/lint/post-synth/k8s-helpers.d.ts.map +1 -1
- package/dist/lint/post-synth/sops-helpers.d.ts +38 -0
- package/dist/lint/post-synth/sops-helpers.d.ts.map +1 -0
- 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 +12 -0
- package/dist/lint/post-synth/wk8404.d.ts.map +1 -0
- package/dist/lint/post-synth/wk8405.d.ts +13 -0
- package/dist/lint/post-synth/wk8405.d.ts.map +1 -0
- package/dist/lint/post-synth/wk8406.d.ts +12 -0
- package/dist/lint/post-synth/wk8406.d.ts.map +1 -0
- package/dist/lint/post-synth/wk8407.d.ts +21 -0
- package/dist/lint/post-synth/wk8407.d.ts.map +1 -0
- package/dist/lint/post-synth/wk8503.d.ts +39 -0
- package/dist/lint/post-synth/wk8503.d.ts.map +1 -0
- package/dist/lint/post-synth/wk8504.d.ts +28 -0
- package/dist/lint/post-synth/wk8504.d.ts.map +1 -0
- package/dist/lint/post-synth/wk8505.d.ts +28 -0
- package/dist/lint/post-synth/wk8505.d.ts.map +1 -0
- package/dist/manifest.json +1 -1
- package/dist/meta.json +1039 -68
- package/dist/okf/index.md +26 -2
- package/dist/okf/rules/FLUX002.md +0 -1
- package/dist/okf/rules/WK8404.md +11 -0
- package/dist/okf/rules/WK8405.md +17 -0
- package/dist/okf/rules/WK8406.md +11 -0
- package/dist/okf/rules/WK8407.md +15 -0
- package/dist/okf/rules/WK8503.md +17 -0
- package/dist/okf/rules/WK8504.md +11 -0
- package/dist/okf/rules/WK8505.md +15 -0
- package/dist/okf/types/AWSClusterControllerIdentity.md +14 -0
- package/dist/okf/types/AWSManagedCluster.md +14 -0
- package/dist/okf/types/AWSManagedControlPlane.md +14 -0
- package/dist/okf/types/AWSManagedMachinePool.md +14 -0
- package/dist/okf/types/CAPICluster.md +14 -0
- package/dist/okf/types/Certificate.md +1 -0
- package/dist/okf/types/ClusterResourceSet.md +14 -0
- package/dist/okf/types/ClusterServingRuntime.md +14 -0
- package/dist/okf/types/{Cluster.md → CnpgCluster.md} +2 -2
- package/dist/okf/types/DBInstance.md +14 -0
- package/dist/okf/types/{Bucket.md → FluxBucket.md} +2 -6
- package/dist/okf/types/HelmChartProxy.md +14 -0
- package/dist/okf/types/IamRole.md +14 -0
- package/dist/okf/types/InferenceService.md +19 -0
- package/dist/okf/types/InfisicalDynamicSecret.md +4 -0
- package/dist/okf/types/InfisicalSecret.md +4 -0
- package/dist/okf/types/Kustomization.md +1 -0
- package/dist/okf/types/MachinePool.md +14 -0
- package/dist/okf/types/PDB.md +1 -0
- package/dist/okf/types/PodIdentityAssociation.md +14 -0
- package/dist/okf/types/Policy.md +14 -0
- package/dist/okf/types/S3Bucket.md +14 -0
- package/dist/okf/types/ServingRuntime.md +14 -0
- package/dist/okf/types/StatefulSet.md +1 -0
- package/dist/okf/types/User.md +14 -0
- package/dist/op/activities/ensure-secret.d.ts +41 -0
- package/dist/op/activities/ensure-secret.d.ts.map +1 -0
- package/dist/op/activities/index.d.ts +2 -0
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/kubectl.d.ts +9 -0
- package/dist/op/activities/kubectl.d.ts.map +1 -1
- package/dist/op/activities/wait-for-ready.d.ts.map +1 -1
- package/dist/op/builders.d.ts +69 -0
- package/dist/op/builders.d.ts.map +1 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/rules/argo-helpers.ts +2 -6
- package/dist/rules/crd-schema-helpers.ts +4 -6
- package/dist/rules/k8s-helpers.ts +87 -24
- package/dist/rules/sops-helpers.ts +76 -0
- 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 +49 -0
- package/dist/rules/wk8405.ts +85 -0
- package/dist/rules/wk8406.ts +55 -0
- package/dist/rules/wk8407.ts +66 -0
- package/dist/rules/wk8503.ts +220 -0
- package/dist/rules/wk8504.ts +51 -0
- package/dist/rules/wk8505.ts +63 -0
- package/dist/secret-labels.d.ts +31 -0
- package/dist/secret-labels.d.ts.map +1 -0
- package/dist/secret-store.d.ts +73 -0
- package/dist/secret-store.d.ts.map +1 -0
- package/dist/serializer.d.ts.map +1 -1
- package/dist/sops/encrypted-secret-file.d.ts +52 -0
- package/dist/sops/encrypted-secret-file.d.ts.map +1 -0
- package/dist/sops/entity.d.ts +31 -0
- package/dist/sops/entity.d.ts.map +1 -0
- package/dist/teardown.d.ts.map +1 -1
- package/dist/types/index.d.ts +261 -9
- package/package.json +4 -4
- package/src/components/kubectl-apply.ts +5 -0
- package/src/composites/composites.test.ts +556 -0
- package/src/composites/flux-app.ts +23 -0
- package/src/composites/index.ts +15 -0
- package/src/composites/inference-service.ts +140 -0
- package/src/composites/model.ts +150 -0
- package/src/composites/operator-stack.ts +385 -0
- package/src/composites/vllm-serving-runtime.test.ts +167 -0
- package/src/composites/vllm-serving-runtime.ts +196 -0
- package/src/crd/cnpg.test.ts +8 -3
- package/src/crd/crd-sources.ts +183 -0
- package/src/crd/parser.test.ts +14 -0
- package/src/deep-observe-hooks.ts +24 -0
- package/src/deep-observe.test.ts +89 -0
- package/src/generated/index.d.ts +261 -9
- package/src/generated/index.ts +167 -40
- package/src/generated/lexicon-k8s.json +1039 -68
- package/src/generated/operations.json +272 -0
- package/src/group-namespace.test.ts +31 -0
- package/src/group-namespace.ts +13 -0
- package/src/index.ts +17 -0
- package/src/kube/apply.ts +5 -0
- package/src/lint/audit-catalog.ts +7 -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/index.ts +14 -0
- package/src/lint/post-synth/k8s-helpers.test.ts +26 -9
- package/src/lint/post-synth/k8s-helpers.ts +87 -24
- package/src/lint/post-synth/post-synth.test.ts +826 -0
- package/src/lint/post-synth/sops-helpers.ts +76 -0
- 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 +49 -0
- package/src/lint/post-synth/wk8405.ts +85 -0
- package/src/lint/post-synth/wk8406.ts +55 -0
- package/src/lint/post-synth/wk8407.ts +66 -0
- package/src/lint/post-synth/wk8503.ts +220 -0
- package/src/lint/post-synth/wk8504.ts +51 -0
- package/src/lint/post-synth/wk8505.ts +63 -0
- package/src/op/activities/ensure-secret.ts +71 -0
- package/src/op/activities/index.ts +7 -0
- package/src/op/activities/kubectl.test.ts +32 -0
- package/src/op/activities/kubectl.ts +28 -5
- package/src/op/activities/wait-for-ready.test.ts +34 -0
- package/src/op/activities/wait-for-ready.ts +26 -0
- package/src/op/builders.test.ts +82 -0
- package/src/op/builders.ts +95 -0
- package/src/plugin.ts +21 -2
- package/src/secret-labels.ts +36 -0
- package/src/secret-store.test.ts +184 -0
- package/src/secret-store.ts +165 -0
- package/src/serializer.ts +41 -2
- package/src/sops/encrypted-secret-file.test.ts +333 -0
- package/src/sops/encrypted-secret-file.ts +264 -0
- package/src/sops/entity.ts +53 -0
- package/src/teardown.test.ts +36 -0
- package/src/teardown.ts +14 -0
- package/src/testdata/sops/db-credentials.sops.yaml +23 -0
|
@@ -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, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
|
|
16
16
|
|
|
17
17
|
export const wk8301: PostSynthCheck = {
|
|
18
18
|
id: "WK8301",
|
|
@@ -21,35 +21,30 @@ export const wk8301: PostSynthCheck = {
|
|
|
21
21
|
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
22
22
|
const diagnostics: PostSynthDiagnostic[] = [];
|
|
23
23
|
|
|
24
|
-
for (const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" is missing ${missing.join(" and ")}`,
|
|
49
|
-
entity: resourceName,
|
|
50
|
-
lexicon: "k8s",
|
|
51
|
-
});
|
|
52
|
-
}
|
|
24
|
+
for (const manifest of docsToManifests(ctx)) {
|
|
25
|
+
if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
|
|
26
|
+
// CronJobs and Jobs are short-lived — probes are less relevant
|
|
27
|
+
if (manifest.kind === "Job" || manifest.kind === "CronJob") continue;
|
|
28
|
+
|
|
29
|
+
const containers = extractContainers(manifest);
|
|
30
|
+
const resourceName = manifest.metadata?.name ?? manifest.kind;
|
|
31
|
+
|
|
32
|
+
for (const container of containers) {
|
|
33
|
+
// No declared port → no inbound traffic → nothing to HTTP-probe.
|
|
34
|
+
if (!container.ports || container.ports.length === 0) continue;
|
|
35
|
+
|
|
36
|
+
const missing: string[] = [];
|
|
37
|
+
if (!container.livenessProbe) missing.push("livenessProbe");
|
|
38
|
+
if (!container.readinessProbe) missing.push("readinessProbe");
|
|
39
|
+
|
|
40
|
+
if (missing.length > 0) {
|
|
41
|
+
diagnostics.push({
|
|
42
|
+
checkId: "WK8301",
|
|
43
|
+
severity: "warning",
|
|
44
|
+
message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" is missing ${missing.join(" and ")}`,
|
|
45
|
+
entity: resourceName,
|
|
46
|
+
lexicon: "k8s",
|
|
47
|
+
});
|
|
53
48
|
}
|
|
54
49
|
}
|
|
55
50
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
9
|
-
import {
|
|
9
|
+
import { docsToManifests } from "./k8s-helpers";
|
|
10
10
|
|
|
11
11
|
export const wk8302: PostSynthCheck = {
|
|
12
12
|
id: "WK8302",
|
|
@@ -15,29 +15,24 @@ export const wk8302: PostSynthCheck = {
|
|
|
15
15
|
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
16
16
|
const diagnostics: PostSynthDiagnostic[] = [];
|
|
17
17
|
|
|
18
|
-
for (const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
message: `Deployment "${resourceName}" has ${replicas ?? 1} replica(s) — consider at least 2 for high availability`,
|
|
37
|
-
entity: resourceName,
|
|
38
|
-
lexicon: "k8s",
|
|
39
|
-
});
|
|
40
|
-
}
|
|
18
|
+
for (const manifest of docsToManifests(ctx)) {
|
|
19
|
+
if (manifest.kind !== "Deployment") continue;
|
|
20
|
+
|
|
21
|
+
const resourceName = manifest.metadata?.name ?? "Deployment";
|
|
22
|
+
const spec = manifest.spec;
|
|
23
|
+
if (!spec) continue;
|
|
24
|
+
|
|
25
|
+
const replicas = spec.replicas;
|
|
26
|
+
|
|
27
|
+
// If replicas is omitted, Kubernetes defaults to 1
|
|
28
|
+
if (replicas === undefined || replicas === null || replicas === 1) {
|
|
29
|
+
diagnostics.push({
|
|
30
|
+
checkId: "WK8302",
|
|
31
|
+
severity: "info",
|
|
32
|
+
message: `Deployment "${resourceName}" has ${replicas ?? 1} replica(s) — consider at least 2 for high availability`,
|
|
33
|
+
entity: resourceName,
|
|
34
|
+
lexicon: "k8s",
|
|
35
|
+
});
|
|
41
36
|
}
|
|
42
37
|
}
|
|
43
38
|
|
|
@@ -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
|
import type { K8sManifest } from "./k8s-helpers";
|
|
12
12
|
|
|
13
13
|
export const wk8303: PostSynthCheck = {
|
|
@@ -17,38 +17,35 @@ export const wk8303: PostSynthCheck = {
|
|
|
17
17
|
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
18
18
|
const diagnostics: PostSynthDiagnostic[] = [];
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
const yaml = getPrimaryOutput(output);
|
|
22
|
-
const manifests = parseK8sManifests(yaml);
|
|
20
|
+
const manifests = docsToManifests(ctx);
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
// Collect all PDB selectors to match against Deployments
|
|
23
|
+
const pdbSelectors = collectPdbSelectors(manifests);
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
for (const manifest of manifests) {
|
|
26
|
+
if (manifest.kind !== "Deployment") continue;
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
const spec = manifest.spec;
|
|
29
|
+
if (!spec) continue;
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
const replicas = spec.replicas;
|
|
32
|
+
// Only check HA deployments (replicas >= 2)
|
|
33
|
+
if (typeof replicas !== "number" || replicas < 2) continue;
|
|
36
34
|
|
|
37
|
-
|
|
35
|
+
const resourceName = manifest.metadata?.name ?? "Deployment";
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
// Check if any PDB targets this Deployment's labels
|
|
38
|
+
const selector = spec.selector as Record<string, unknown> | undefined;
|
|
39
|
+
const matchLabels = selector?.matchLabels as Record<string, string> | undefined;
|
|
42
40
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
41
|
+
if (!matchLabels || !hasCoveringPdb(matchLabels, pdbSelectors)) {
|
|
42
|
+
diagnostics.push({
|
|
43
|
+
checkId: "WK8303",
|
|
44
|
+
severity: "info",
|
|
45
|
+
message: `Deployment "${resourceName}" has ${replicas} replicas but no PodDisruptionBudget — add a PDB to ensure availability during voluntary disruptions`,
|
|
46
|
+
entity: resourceName,
|
|
47
|
+
lexicon: "k8s",
|
|
48
|
+
});
|
|
52
49
|
}
|
|
53
50
|
}
|
|
54
51
|
|
|
@@ -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 wk8304: PostSynthCheck = {
|
|
13
13
|
id: "WK8304",
|
|
@@ -16,51 +16,46 @@ export const wk8304: 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 !== "Ingress") continue;
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const annotations = manifest.metadata?.annotations as Record<string, string> | undefined;
|
|
23
|
+
if (!annotations) continue;
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
const sslRedirect = annotations["alb.ingress.kubernetes.io/ssl-redirect"];
|
|
26
|
+
if (!sslRedirect) continue;
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
const resourceName = manifest.metadata?.name ?? "Ingress";
|
|
29
|
+
const certArn = annotations["alb.ingress.kubernetes.io/certificate-arn"];
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
});
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
31
|
+
if (!certArn) {
|
|
32
|
+
diagnostics.push({
|
|
33
|
+
checkId: "WK8304",
|
|
34
|
+
severity: "warning",
|
|
35
|
+
message: `Ingress "${resourceName}" has ssl-redirect annotation but no certificate-arn — HTTPS redirect will fail without a TLS certificate`,
|
|
36
|
+
entity: resourceName,
|
|
37
|
+
lexicon: "k8s",
|
|
38
|
+
});
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
45
41
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
} catch {
|
|
62
|
-
// Can't parse listen-ports — skip this check
|
|
42
|
+
// Check listen-ports includes HTTPS
|
|
43
|
+
const listenPorts = annotations["alb.ingress.kubernetes.io/listen-ports"];
|
|
44
|
+
if (listenPorts) {
|
|
45
|
+
try {
|
|
46
|
+
const ports = JSON.parse(listenPorts) as Array<Record<string, number>>;
|
|
47
|
+
const hasHttps = ports.some((p) => "HTTPS" in p);
|
|
48
|
+
if (!hasHttps) {
|
|
49
|
+
diagnostics.push({
|
|
50
|
+
checkId: "WK8304",
|
|
51
|
+
severity: "warning",
|
|
52
|
+
message: `Ingress "${resourceName}" has ssl-redirect but listen-ports does not include HTTPS`,
|
|
53
|
+
entity: resourceName,
|
|
54
|
+
lexicon: "k8s",
|
|
55
|
+
});
|
|
63
56
|
}
|
|
57
|
+
} catch {
|
|
58
|
+
// Can't parse listen-ports — skip this check
|
|
64
59
|
}
|
|
65
60
|
}
|
|
66
61
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
9
|
-
import {
|
|
9
|
+
import { docsToManifests } from "./k8s-helpers";
|
|
10
10
|
import type { K8sManifest } from "./k8s-helpers";
|
|
11
11
|
|
|
12
12
|
export const wk8305: PostSynthCheck = {
|
|
@@ -16,60 +16,57 @@ export const wk8305: PostSynthCheck = {
|
|
|
16
16
|
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
17
17
|
const diagnostics: PostSynthDiagnostic[] = [];
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
const yaml = getPrimaryOutput(output);
|
|
21
|
-
const manifests = parseK8sManifests(yaml);
|
|
19
|
+
const manifests = docsToManifests(ctx);
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
// Build a map of Service name+namespace → set of port numbers
|
|
22
|
+
const servicePorts = collectServicePorts(manifests);
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
for (const manifest of manifests) {
|
|
25
|
+
if (manifest.kind !== "Ingress") continue;
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
const ingressName = manifest.metadata?.name ?? "Ingress";
|
|
28
|
+
const ingressNamespace = manifest.metadata?.namespace ?? "default";
|
|
29
|
+
const spec = manifest.spec;
|
|
30
|
+
if (!spec) continue;
|
|
33
31
|
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
const rules = spec.rules as Array<Record<string, unknown>> | undefined;
|
|
33
|
+
if (!rules) continue;
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
for (const rule of rules) {
|
|
36
|
+
const http = rule.http as Record<string, unknown> | undefined;
|
|
37
|
+
if (!http) continue;
|
|
40
38
|
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
const paths = http.paths as Array<Record<string, unknown>> | undefined;
|
|
40
|
+
if (!paths) continue;
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
for (const pathEntry of paths) {
|
|
43
|
+
const backend = pathEntry.backend as Record<string, unknown> | undefined;
|
|
44
|
+
if (!backend) continue;
|
|
47
45
|
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
const service = backend.service as Record<string, unknown> | undefined;
|
|
47
|
+
if (!service) continue;
|
|
50
48
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
const svcName = service.name as string | undefined;
|
|
50
|
+
const port = service.port as Record<string, unknown> | undefined;
|
|
51
|
+
const portNumber = port?.number as number | undefined;
|
|
54
52
|
|
|
55
|
-
|
|
53
|
+
if (!svcName || portNumber === undefined) continue;
|
|
56
54
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
// Look up the Service in the manifest set
|
|
56
|
+
const key = `${ingressNamespace}/${svcName}`;
|
|
57
|
+
const knownPorts = servicePorts.get(key);
|
|
60
58
|
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
// Skip if the Service is not in the manifest set (external service)
|
|
60
|
+
if (!knownPorts) continue;
|
|
63
61
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
62
|
+
if (!knownPorts.has(portNumber)) {
|
|
63
|
+
diagnostics.push({
|
|
64
|
+
checkId: "WK8305",
|
|
65
|
+
severity: "warning",
|
|
66
|
+
message: `Ingress "${ingressName}" references Service "${svcName}" port ${portNumber}, but the Service only declares ports [${[...knownPorts].join(", ")}]`,
|
|
67
|
+
entity: ingressName,
|
|
68
|
+
lexicon: "k8s",
|
|
69
|
+
});
|
|
73
70
|
}
|
|
74
71
|
}
|
|
75
72
|
}
|
|
@@ -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, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
|
|
12
12
|
|
|
13
13
|
export const wk8306: PostSynthCheck = {
|
|
14
14
|
id: "WK8306",
|
|
@@ -17,30 +17,25 @@ export const wk8306: PostSynthCheck = {
|
|
|
17
17
|
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
18
18
|
const diagnostics: PostSynthDiagnostic[] = [];
|
|
19
19
|
|
|
20
|
-
for (const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" has command[0]="${firstArg}" which starts with a flag — the first element should be the binary, flags belong in args`,
|
|
40
|
-
entity: resourceName,
|
|
41
|
-
lexicon: "k8s",
|
|
42
|
-
});
|
|
43
|
-
}
|
|
20
|
+
for (const manifest of docsToManifests(ctx)) {
|
|
21
|
+
if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
|
|
22
|
+
|
|
23
|
+
const resourceName = manifest.metadata?.name ?? manifest.kind;
|
|
24
|
+
const containers = extractContainers(manifest);
|
|
25
|
+
|
|
26
|
+
for (const container of containers) {
|
|
27
|
+
const command = (container as Record<string, unknown>).command as unknown[] | undefined;
|
|
28
|
+
if (!Array.isArray(command) || command.length === 0) continue;
|
|
29
|
+
|
|
30
|
+
const firstArg = String(command[0]);
|
|
31
|
+
if (firstArg.startsWith("-")) {
|
|
32
|
+
diagnostics.push({
|
|
33
|
+
checkId: "WK8306",
|
|
34
|
+
severity: "error",
|
|
35
|
+
message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" has command[0]="${firstArg}" which starts with a flag — the first element should be the binary, flags belong in args`,
|
|
36
|
+
entity: resourceName,
|
|
37
|
+
lexicon: "k8s",
|
|
38
|
+
});
|
|
44
39
|
}
|
|
45
40
|
}
|
|
46
41
|
}
|
|
@@ -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
|
}
|