@intentius/chant-lexicon-k8s 0.124.31 → 0.124.33
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/actions/apps.js +18 -0
- package/dist/actions/batch.js +6 -0
- package/dist/actions/core.js +48 -0
- package/dist/actions/index.js +13 -0
- package/dist/actions/networking.js +11 -0
- package/dist/actions/rbac.js +8 -0
- package/dist/api/classify.js +123 -0
- package/dist/api/connect.js +62 -0
- package/dist/api/fake-cluster.js +175 -0
- package/dist/api/operation-surface.d.ts.map +1 -1
- package/dist/api/operation-surface.js +109 -0
- package/dist/api/owner-chain.js +100 -0
- package/dist/api/sweep-types.js +35 -0
- package/dist/behaviour-kinds.js +82 -0
- package/dist/codegen/docs-cli.js +2 -0
- package/dist/codegen/docs.js +146 -0
- package/dist/codegen/generate-cli.js +30 -0
- package/dist/codegen/generate-lexicon.js +74 -0
- package/dist/codegen/generate-list-map-keys.js +88 -0
- package/dist/codegen/generate-operations.js +53 -0
- package/dist/codegen/generate-typescript.js +124 -0
- package/dist/codegen/generate.js +173 -0
- package/dist/codegen/naming.js +174 -0
- package/dist/codegen/package.js +36 -0
- package/dist/codegen/patches.js +88 -0
- package/dist/codegen/typecheck.js +4 -0
- package/dist/codegen/versions.js +41 -0
- package/dist/components/argo-app.js +51 -0
- package/dist/components/builders.js +22 -0
- package/dist/components/capability-plugin.js +31 -0
- package/dist/components/flux-reconcile.js +61 -0
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +18 -0
- package/dist/components/kubectl-apply.d.ts +83 -1
- package/dist/components/kubectl-apply.d.ts.map +1 -1
- package/dist/components/kubectl-apply.js +274 -0
- package/dist/components/kustomize-apply.js +25 -0
- package/dist/composites/adot-collector.js +184 -0
- package/dist/composites/agic-ingress.js +85 -0
- package/dist/composites/aks-external-dns-agent.js +146 -0
- package/dist/composites/alb-ingress.js +86 -0
- package/dist/composites/argo-app.js +199 -0
- package/dist/composites/autoscaled-service.js +174 -0
- package/dist/composites/azure-disk-storage-class.js +47 -0
- package/dist/composites/azure-file-storage-class.js +48 -0
- package/dist/composites/azure-monitor-collector.js +176 -0
- package/dist/composites/batch-job.js +139 -0
- package/dist/composites/catalog.js +4110 -0
- package/dist/composites/cockroachdb-cluster.js +371 -0
- package/dist/composites/cockroachdb-region-stack.js +369 -0
- package/dist/composites/config-connector-context.js +40 -0
- package/dist/composites/configured-app.js +135 -0
- package/dist/composites/cron-schedule.js +38 -0
- package/dist/composites/cron-workload.js +109 -0
- package/dist/composites/ebs-storage-class.js +54 -0
- package/dist/composites/efs-storage-class.js +46 -0
- package/dist/composites/external-dns-agent.js +122 -0
- package/dist/composites/filestore-storage-class.js +49 -0
- package/dist/composites/fluent-bit-agent.js +157 -0
- package/dist/composites/flux-app.js +156 -0
- package/dist/composites/gce-ingress.js +83 -0
- package/dist/composites/gce-pd-storage-class.js +49 -0
- package/dist/composites/gke-external-dns-agent.js +129 -0
- package/dist/composites/gke-fluent-bit-agent.js +165 -0
- package/dist/composites/gke-gateway.js +91 -0
- package/dist/composites/gke-otel-collector.js +94 -0
- package/dist/composites/index.js +51 -0
- package/dist/composites/inference-service.js +76 -0
- package/dist/composites/irsa-service-account.js +77 -0
- package/dist/composites/member-defaults.js +14 -0
- package/dist/composites/metrics-server.js +181 -0
- package/dist/composites/model.js +92 -0
- package/dist/composites/monitored-service.js +152 -0
- package/dist/composites/namespace-env.js +114 -0
- package/dist/composites/network-isolated-app.js +124 -0
- package/dist/composites/node-agent.js +149 -0
- package/dist/composites/operator-stack.js +268 -0
- package/dist/composites/otel-collector-agent.js +142 -0
- package/dist/composites/otel-collector-gateway.js +216 -0
- package/dist/composites/otel-collector-node.js +176 -0
- package/dist/composites/otel-collector-rbac.js +115 -0
- package/dist/composites/otel-collector-shape.js +128 -0
- package/dist/composites/otel-collector.js +115 -0
- package/dist/composites/otel-operator-collector.js +123 -0
- package/dist/composites/ray-cluster.js +378 -0
- package/dist/composites/ray-job.js +136 -0
- package/dist/composites/ray-service.js +178 -0
- package/dist/composites/secure-ingress.js +100 -0
- package/dist/composites/security-context.js +1 -0
- package/dist/composites/sidecar-app.js +105 -0
- package/dist/composites/stateful-app.js +111 -0
- package/dist/composites/vllm-serving-runtime.js +114 -0
- package/dist/composites/web-app.js +157 -0
- package/dist/composites/worker-pool.js +167 -0
- package/dist/composites/workload-identity-sa.js +81 -0
- package/dist/composites/workload-identity-service-account.js +77 -0
- package/dist/config-schema.js +72 -0
- package/dist/config.js +59 -0
- package/dist/coverage.js +27 -0
- package/dist/crd/crd-sources.js +615 -0
- package/dist/crd/loader.js +181 -0
- package/dist/crd/oci-chart.js +144 -0
- package/dist/crd/parser.js +382 -0
- package/dist/crd/project-codegen.js +273 -0
- package/dist/crd/types.js +7 -0
- package/dist/deep-observe-hooks.js +306 -0
- package/dist/deep-observe.js +234 -0
- package/dist/default-labels.js +87 -0
- package/dist/describe-resources.js +699 -0
- package/dist/describe-stack-status.js +99 -0
- package/dist/detect.js +11 -0
- package/dist/effect-receipt-row.js +205 -0
- package/dist/export-resources.js +39 -0
- package/dist/generated/index.js +1092 -0
- package/dist/generated/runtime.js +4 -0
- package/dist/group-namespace.js +96 -0
- package/dist/import/embedded.js +197 -0
- package/dist/import/generator.js +288 -0
- package/dist/import/live-export.js +85 -0
- package/dist/import/parser.js +204 -0
- package/dist/import/testdata/embedded/fixtures.js +60 -0
- package/dist/include/entity.js +78 -0
- package/dist/include/resolve.js +170 -0
- package/dist/index.js +59 -0
- package/dist/integrity.json +3 -3
- package/dist/kube/apply.js +90 -0
- package/dist/kube/connect.js +46 -0
- package/dist/kube/delete.js +138 -0
- package/dist/kube/describe.js +169 -0
- package/dist/kube/events.js +132 -0
- package/dist/kube/flags.js +91 -0
- package/dist/kube/get.js +159 -0
- package/dist/kube/group.js +123 -0
- package/dist/kube/logs.js +82 -0
- package/dist/kube/project.js +94 -0
- package/dist/kube/render.js +227 -0
- package/dist/kube/source.js +122 -0
- package/dist/kube/target.js +63 -0
- package/dist/kube/testing.js +28 -0
- package/dist/kube/top.js +99 -0
- package/dist/kube/verdict.js +72 -0
- package/dist/kube/wait.js +129 -0
- package/dist/kustomize/render.js +73 -0
- package/dist/kustomize/rendered-entity.js +67 -0
- package/dist/kustomize/root.js +68 -0
- package/dist/lint/audit-catalog.js +61 -0
- package/dist/lint/audit-lineage.js +118 -0
- package/dist/lint/post-synth/argo-helpers.js +36 -0
- package/dist/lint/post-synth/argo002.js +31 -0
- package/dist/lint/post-synth/argo003.js +53 -0
- package/dist/lint/post-synth/argo005.js +54 -0
- package/dist/lint/post-synth/crd-schema-helpers.d.ts.map +1 -1
- package/dist/lint/post-synth/crd-schema-helpers.js +214 -0
- package/dist/lint/post-synth/flux002.js +56 -0
- package/dist/lint/post-synth/flux003.js +50 -0
- package/dist/lint/post-synth/index.js +104 -0
- package/dist/lint/post-synth/k8s-helpers.js +186 -0
- package/dist/lint/post-synth/monitoring-selection-helpers.js +163 -0
- package/dist/lint/post-synth/otel-placement-helpers.js +293 -0
- package/dist/lint/post-synth/sops-helpers.js +43 -0
- package/dist/lint/post-synth/wk8005.js +44 -0
- package/dist/lint/post-synth/wk8006.js +56 -0
- package/dist/lint/post-synth/wk8041.js +60 -0
- package/dist/lint/post-synth/wk8042.js +36 -0
- package/dist/lint/post-synth/wk8101.js +52 -0
- package/dist/lint/post-synth/wk8102.js +30 -0
- package/dist/lint/post-synth/wk8103.js +33 -0
- package/dist/lint/post-synth/wk8104.js +58 -0
- package/dist/lint/post-synth/wk8105.js +33 -0
- package/dist/lint/post-synth/wk8201.js +44 -0
- package/dist/lint/post-synth/wk8202.js +34 -0
- package/dist/lint/post-synth/wk8203.js +34 -0
- package/dist/lint/post-synth/wk8204.js +71 -0
- package/dist/lint/post-synth/wk8205.js +38 -0
- package/dist/lint/post-synth/wk8207.js +33 -0
- package/dist/lint/post-synth/wk8208.js +33 -0
- package/dist/lint/post-synth/wk8209.js +33 -0
- package/dist/lint/post-synth/wk8301.js +49 -0
- package/dist/lint/post-synth/wk8302.js +34 -0
- package/dist/lint/post-synth/wk8303.js +68 -0
- package/dist/lint/post-synth/wk8304.js +58 -0
- package/dist/lint/post-synth/wk8305.js +96 -0
- package/dist/lint/post-synth/wk8306.js +38 -0
- package/dist/lint/post-synth/wk8401.js +86 -0
- package/dist/lint/post-synth/wk8402.js +31 -0
- package/dist/lint/post-synth/wk8403.js +49 -0
- package/dist/lint/post-synth/wk8404.js +36 -0
- package/dist/lint/post-synth/wk8405.js +65 -0
- package/dist/lint/post-synth/wk8406.js +50 -0
- package/dist/lint/post-synth/wk8407.js +58 -0
- package/dist/lint/post-synth/wk8501.js +36 -0
- package/dist/lint/post-synth/wk8502.js +35 -0
- package/dist/lint/post-synth/wk8503.js +164 -0
- package/dist/lint/post-synth/wk8504.js +20 -0
- package/dist/lint/post-synth/wk8505.js +31 -0
- package/dist/lint/post-synth/wk8601.js +40 -0
- package/dist/lint/post-synth/wk8602.js +120 -0
- package/dist/lint/post-synth/wk8603.js +63 -0
- package/dist/lint/post-synth/wk8604.js +31 -0
- package/dist/lint/post-synth/wk8605.js +124 -0
- package/dist/lint/post-synth/wk8606.js +22 -0
- package/dist/lint/post-synth/wk8701.js +31 -0
- package/dist/lint/post-synth/wk8702.js +37 -0
- package/dist/lint/post-synth/wk8703.js +31 -0
- package/dist/lint/post-synth/wk8704.js +135 -0
- package/dist/lint/post-synth/wk8705.js +38 -0
- package/dist/lint/rules/argo-appset-single-project.js +56 -0
- package/dist/lint/rules/argo-ast.js +94 -0
- package/dist/lint/rules/argo-automated-prune.js +59 -0
- package/dist/lint/rules/flux-source-ref-pin.js +44 -0
- package/dist/lint/rules/hardcoded-namespace.js +57 -0
- package/dist/lint/rules/latest-image-tag.js +103 -0
- package/dist/lint/rules/missing-resource-limits.js +85 -0
- package/dist/lsp/completions.d.ts.map +1 -1
- package/dist/lsp/completions.js +18 -0
- package/dist/lsp/hover.d.ts.map +1 -1
- package/dist/lsp/hover.js +57 -0
- package/dist/manifest-entity.js +71 -0
- package/dist/manifest.json +1 -1
- package/dist/micro-time.js +49 -0
- package/dist/op/activities/apply-diff.d.ts +31 -0
- package/dist/op/activities/apply-diff.d.ts.map +1 -0
- package/dist/op/activities/apply-diff.js +79 -0
- package/dist/op/activities/argo.js +98 -0
- package/dist/op/activities/ensure-secret.js +38 -0
- package/dist/op/activities/index.js +51 -0
- package/dist/op/activities/kubectl.d.ts +64 -0
- package/dist/op/activities/kubectl.d.ts.map +1 -1
- package/dist/op/activities/kubectl.js +517 -0
- package/dist/op/activities/wait-for-ready.js +229 -0
- package/dist/op/builders.js +68 -0
- package/dist/package-cli.js +21 -0
- package/dist/patch/json-patch.js +187 -0
- package/dist/patch/rendered-patch.js +94 -0
- package/dist/plugin.js +741 -0
- package/dist/project-kinds.js +56 -0
- package/dist/receipt-store.js +216 -0
- package/dist/rules/crd-schema-helpers.ts +2 -1
- package/dist/secret-labels.js +33 -0
- package/dist/secret-store.js +116 -0
- package/dist/serializer.d.ts.map +1 -1
- package/dist/serializer.js +578 -0
- package/dist/sops/encrypted-secret-file.js +194 -0
- package/dist/sops/entity.js +32 -0
- package/dist/spec/fetch.js +58 -0
- package/dist/spec/parse.js +622 -0
- package/dist/subscribe-changes.js +168 -0
- package/dist/teardown.js +197 -0
- package/dist/telemetry-release.js +86 -0
- package/dist/validate-cli.js +17 -0
- package/dist/validate.js +47 -0
- package/dist/variables.js +34 -0
- package/dist/whoami.js +109 -0
- package/package.json +12 -12
- package/src/api/operation-surface.ts +2 -1
- package/src/codegen/package.ts +2 -2
- package/src/components/index.ts +3 -0
- package/src/components/kubectl-apply-plan.k3d.e2e.test.ts +127 -0
- package/src/components/kubectl-apply.test.ts +262 -0
- package/src/components/kubectl-apply.ts +341 -12
- package/src/deep-observe-hooks.ts +2 -2
- package/src/import/roundtrip-build.test.ts +46 -0
- package/src/lint/post-synth/crd-schema-helpers.ts +2 -1
- package/src/lsp/completions.ts +2 -1
- package/src/lsp/hover.ts +2 -1
- package/src/op/activities/apply-diff.ts +87 -0
- package/src/op/activities/kubectl-plan.test.ts +224 -0
- package/src/op/activities/kubectl.ts +204 -16
- package/src/serializer.ts +2 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kustomize build roots (#1548 piece 3).
|
|
3
|
+
*
|
|
4
|
+
* An estate that keeps its overlay tree has no typed chant source for those
|
|
5
|
+
* manifests — and piece 4 (a declarable Kustomization) is parked precisely
|
|
6
|
+
* because forcing a modelling migration onto every kustomize estate is the
|
|
7
|
+
* mistake behold#138 documented. What such an estate CAN declare is the
|
|
8
|
+
* directory: `k8s.kustomize.roots` in `chant.config.ts` names kustomization
|
|
9
|
+
* dirs that render at build time into the manifest set. The rendered
|
|
10
|
+
* documents become entities (see `./rendered-entity.ts`), so they are
|
|
11
|
+
* serialized into the build output, ownership-stamped, seen by post-synth
|
|
12
|
+
* checks, and observed by `lifecycle diff --live` — the declared side an
|
|
13
|
+
* overlay estate never had.
|
|
14
|
+
*
|
|
15
|
+
* Renders through the same injectable runner the `kustomize-apply`
|
|
16
|
+
* capability uses (`./render.ts`): `kustomize build`, `kubectl kustomize`
|
|
17
|
+
* fallback, and a both-binaries-missing failure that names them. Two
|
|
18
|
+
* build-time guarantees on top:
|
|
19
|
+
*
|
|
20
|
+
* - **Deterministic**: entity names derive from the root dir plus each
|
|
21
|
+
* document's kind/name/namespace, disambiguated in render order — the same
|
|
22
|
+
* overlay renders the same entity set every build.
|
|
23
|
+
* - **Fail loudly, not weirdly**: a root that doesn't exist, or a dir with no
|
|
24
|
+
* kustomization file, refuses with the path in the message before any
|
|
25
|
+
* subprocess runs — offline, the failure mode is a sentence, never a
|
|
26
|
+
* render attempt's stack trace.
|
|
27
|
+
*/
|
|
28
|
+
import { existsSync } from "node:fs";
|
|
29
|
+
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
30
|
+
import { defaultKustomizeRunner, renderKustomizeDocuments } from "./render.js";
|
|
31
|
+
import { addRenderedEntity, renderedManifestEntity } from "./rendered-entity.js";
|
|
32
|
+
import { applyRenderedPatches } from "../patch/rendered-patch.js";
|
|
33
|
+
const KUSTOMIZATION_FILES = ["kustomization.yaml", "kustomization.yml", "Kustomization"];
|
|
34
|
+
/**
|
|
35
|
+
* Render each configured kustomization root into entities, keyed
|
|
36
|
+
* `<root>/<kindName>` (namespace-qualified, then numbered, on collision).
|
|
37
|
+
*/
|
|
38
|
+
export async function renderKustomizeRoots(opts) {
|
|
39
|
+
const run = opts.run ?? defaultKustomizeRunner;
|
|
40
|
+
const entities = new Map();
|
|
41
|
+
const warnings = [];
|
|
42
|
+
for (const entry of opts.roots) {
|
|
43
|
+
const root = typeof entry === "string" ? entry : entry.path;
|
|
44
|
+
const patches = typeof entry === "string" ? undefined : entry.patches;
|
|
45
|
+
const rootDir = isAbsolute(root) ? root : resolve(opts.projectRoot, root);
|
|
46
|
+
// The label rendered docs carry as provenance: the declared (relative)
|
|
47
|
+
// form when possible, so it names the overlay the way the config does.
|
|
48
|
+
const rootLabel = isAbsolute(root) ? (relative(opts.projectRoot, root) || ".") : root;
|
|
49
|
+
if (!existsSync(rootDir)) {
|
|
50
|
+
throw new Error(`k8s.kustomize.roots entry "${root}": directory not found at ${rootDir}`);
|
|
51
|
+
}
|
|
52
|
+
if (!KUSTOMIZATION_FILES.some((f) => existsSync(join(rootDir, f)))) {
|
|
53
|
+
throw new Error(`k8s.kustomize.roots entry "${root}": ${rootDir} contains no kustomization file ` +
|
|
54
|
+
`(expected one of ${KUSTOMIZATION_FILES.join(", ")})`);
|
|
55
|
+
}
|
|
56
|
+
const rendered = await renderKustomizeDocuments(rootDir, run);
|
|
57
|
+
const documents = applyRenderedPatches(rendered, patches, `kustomize root "${rootLabel}"`);
|
|
58
|
+
for (const doc of documents) {
|
|
59
|
+
const entity = renderedManifestEntity(doc, rootLabel);
|
|
60
|
+
if (!entity) {
|
|
61
|
+
warnings.push(`kustomize root "${rootLabel}" rendered a document without apiVersion/kind — skipped`);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
addRenderedEntity(entities, rootLabel, entity);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return { entities, warnings };
|
|
68
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The k8s lexicon's chant audit catalog — metadata for its post-synth rules
|
|
3
|
+
* (WK8 and ARGO Kubernetes-manifest rules). Contributed via k8sPlugin.auditCatalog() (#687).
|
|
4
|
+
*/
|
|
5
|
+
import { auditRule, K8S_PSS, K8S_SECRETS, SCORECARD_PINNED, applyLineage } from "@intentius/chant/audit/catalog";
|
|
6
|
+
import { k8sAuditLineage } from "./audit-lineage.js";
|
|
7
|
+
export const k8sAuditCatalog = {
|
|
8
|
+
ARGO002: auditRule("ARGO002", "merge-worthy", "guidance", "Argo Application references an undeclared AppProject", "Declare the named AppProject or reference an existing project.", { category: "correctness" }),
|
|
9
|
+
ARGO003: auditRule("ARGO003", "merge-worthy", "guidance", "Argo Application targets an unregistered cluster", "Point spec.destination at a registered cluster or the in-cluster target.", { category: "correctness" }),
|
|
10
|
+
ARGO005: auditRule("ARGO005", "report-only", "guidance", "Argo source.path may not resolve", "Ensure the source path exists under the build root.", { category: "best-practice" }),
|
|
11
|
+
FLUX002: auditRule("FLUX002", "merge-worthy", "guidance", "Flux Kustomization references an undeclared source", "Declare the GitRepository/OCIRepository/Bucket, or point sourceRef at the bootstrap flux-system repo.", { category: "correctness" }),
|
|
12
|
+
FLUX003: auditRule("FLUX003", "report-only", "guidance", "Flux Kustomization dependsOn names an undeclared Kustomization", "Fix the dependsOn name, or ignore if the dependency is declared in another repo.", { category: "correctness" }),
|
|
13
|
+
WK8005: auditRule("WK8005", "merge-worthy", "guidance", "Hardcoded secret in env var", "Use a secretKeyRef instead of a literal value, and rotate the secret.", { authority: [K8S_SECRETS] }),
|
|
14
|
+
WK8006: auditRule("WK8006", "merge-worthy", "guidance", "Image uses :latest or no tag", "Pin the image to an explicit version tag (ideally a digest).", { authority: [SCORECARD_PINNED] }),
|
|
15
|
+
WK8041: auditRule("WK8041", "merge-worthy", "guidance", "Hardcoded API key in env var", "Move the key to a Secret and rotate it.", { authority: [K8S_SECRETS] }),
|
|
16
|
+
WK8042: auditRule("WK8042", "merge-worthy", "guidance", "Private key stored in a ConfigMap", "Store private keys in a Secret, not a ConfigMap.", { authority: [K8S_SECRETS] }),
|
|
17
|
+
WK8101: auditRule("WK8101", "merge-worthy", "guidance", "Deployment selector does not match template labels", "Align spec.selector with the pod template labels.", { category: "best-practice" }),
|
|
18
|
+
WK8102: auditRule("WK8102", "report-only", "guidance", "Resource missing metadata labels", "Add metadata labels for filtering and tooling.", { category: "best-practice" }),
|
|
19
|
+
WK8103: auditRule("WK8103", "merge-worthy", "guidance", "Container missing name", "Add the required container `name`.", { category: "best-practice" }),
|
|
20
|
+
WK8104: auditRule("WK8104", "report-only", "guidance", "Container ports not named", "Name ports for clearer Service/NetworkPolicy config.", { category: "best-practice" }),
|
|
21
|
+
WK8105: auditRule("WK8105", "report-only", "guidance", "imagePullPolicy not explicit", "Set imagePullPolicy explicitly to avoid surprising defaults.", { category: "best-practice" }),
|
|
22
|
+
WK8201: auditRule("WK8201", "report-only", "guidance", "Container missing resource limits", "Set CPU and memory limits.", { category: "best-practice" }),
|
|
23
|
+
WK8202: auditRule("WK8202", "merge-worthy", "guidance", "Privileged container", "Remove privileged: true; grant only the specific capabilities needed.", { authority: [K8S_PSS] }),
|
|
24
|
+
WK8203: auditRule("WK8203", "merge-worthy", "guidance", "Root filesystem is writable", "Set readOnlyRootFilesystem: true.", { authority: [K8S_PSS] }),
|
|
25
|
+
WK8204: auditRule("WK8204", "merge-worthy", "guidance", "Container may run as root", "Set runAsNonRoot: true (and a non-zero runAsUser).", { authority: [K8S_PSS] }),
|
|
26
|
+
WK8205: auditRule("WK8205", "merge-worthy", "guidance", "Capabilities not dropped", "drop: [ALL] and add back only what is required.", { authority: [K8S_PSS] }),
|
|
27
|
+
WK8207: auditRule("WK8207", "merge-worthy", "guidance", "Pod uses host network", "Remove hostNetwork; it bypasses network isolation.", { authority: [K8S_PSS] }),
|
|
28
|
+
WK8208: auditRule("WK8208", "merge-worthy", "guidance", "Pod shares host PID namespace", "Remove hostPID.", { authority: [K8S_PSS] }),
|
|
29
|
+
WK8209: auditRule("WK8209", "merge-worthy", "guidance", "Pod shares host IPC namespace", "Remove hostIPC.", { authority: [K8S_PSS] }),
|
|
30
|
+
WK8301: auditRule("WK8301", "report-only", "guidance", "Container missing probes", "Add liveness and readiness probes.", { category: "best-practice" }),
|
|
31
|
+
WK8302: auditRule("WK8302", "report-only", "guidance", "Deployment has a single replica", "Use replicas >= 2 for availability.", { category: "best-practice" }),
|
|
32
|
+
WK8303: auditRule("WK8303", "report-only", "guidance", "No PodDisruptionBudget for an HA Deployment", "Add a PDB to protect availability during disruptions.", { category: "best-practice" }),
|
|
33
|
+
WK8304: auditRule("WK8304", "report-only", "guidance", "SSL redirect without a certificate", "Provide a certificate and HTTPS listen-ports for the ssl-redirect annotation.", { category: "best-practice" }),
|
|
34
|
+
WK8305: auditRule("WK8305", "merge-worthy", "guidance", "Ingress backend port does not match the Service", "Point the Ingress backend at a declared Service port.", { category: "best-practice" }),
|
|
35
|
+
WK8306: auditRule("WK8306", "merge-worthy", "guidance", "Container command starts with a flag", "The first command element should be a binary, not a flag.", { category: "best-practice" }),
|
|
36
|
+
WK8401: auditRule("WK8401", "merge-worthy", "guidance", "shmSize exceeds the container memory limit", "Lower shmSize or raise the memory limit so the pod can schedule.", { category: "best-practice" }),
|
|
37
|
+
WK8402: auditRule("WK8402", "report-only", "guidance", "RayCluster missing spec.rayVersion", "Set spec.rayVersion so KubeRay picks the right autoscaler image.", { category: "best-practice" }),
|
|
38
|
+
WK8403: auditRule("WK8403", "report-only", "guidance", "rayVersion does not match the head image tag", "Align spec.rayVersion with the Ray version in the head container image.", { category: "best-practice" }),
|
|
39
|
+
WK8404: auditRule("WK8404", "merge-worthy", "guidance", "GPU request without a matching toleration", "Add a toleration for the nvidia.com/gpu taint (or a wildcard Exists toleration) so the pod can schedule onto a GPU node pool.", { category: "correctness" }),
|
|
40
|
+
WK8405: auditRule("WK8405", "report-only", "guidance", "Serving workload has no PodDisruptionBudget", "Add a PDB selecting the InferenceService/serving Deployment so a node drain can't take down every replica at once.", { category: "best-practice" }),
|
|
41
|
+
WK8406: auditRule("WK8406", "merge-worthy", "guidance", "GPU container missing resource limits", "Set CPU and memory limits on GPU-requesting containers to avoid starving the (scarce, expensive) GPU node.", { category: "best-practice" }),
|
|
42
|
+
WK8407: auditRule("WK8407", "merge-worthy", "guidance", "Unpinned model version", "Pin the InferenceService model storageUri to an explicit version segment (e.g. via the Model composite's required version) instead of a floating tag.", { category: "correctness" }),
|
|
43
|
+
WK8501: auditRule("WK8501", "merge-worthy", "guidance", "Custom resource spec has a field its CRD does not declare", "Fix the field name; the API server prunes unknown fields and the controller never sees them.", { category: "correctness" }),
|
|
44
|
+
WK8502: auditRule("WK8502", "merge-worthy", "guidance", "Custom resource spec field has the wrong type or enum value", "Match the CRD schema: use the declared scalar type and one of the enum values.", { category: "correctness" }),
|
|
45
|
+
WK8503: auditRule("WK8503", "merge-worthy", "guidance", "Workload consumes a Secret nothing in the output produces", "Produce the Secret in the same build (Secret, ExternalSecret, InfisicalSecret, cert-manager Certificate), or declare its provenance with declareSecret() if it exists out of band.", { category: "correctness" }),
|
|
46
|
+
WK8504: auditRule("WK8504", "merge-worthy", "guidance", "committed-encrypted secret declaration does not resolve", "Point `file` at the committed ciphertext, make its metadata.name match the declaration, and re-run `sops -e -i` so every data/stringData value is ENC[...].", { authority: [K8S_SECRETS] }),
|
|
47
|
+
WK8505: auditRule("WK8505", "report-only", "guidance", "committed-encrypted secret with no Flux decryption wiring", "Add decryption: \"sops\" to the FluxAppFor reconciling the path that carries the ciphertext sidecar, or ignore if that Kustomization is declared in a different build. This check sees one build root at a time, so it goes silent once the secret and the Kustomization live in different build roots (chant #1939).", { category: "correctness" }),
|
|
48
|
+
WK8601: auditRule("WK8601", "merge-worthy", "guidance", "OpenTelemetry Collector runs tail_sampling on every node", "Move tail_sampling from the DaemonSet agent to a gateway Deployment, and send traces to it through a loadbalancing exporter routed by traceID.", { category: "correctness" }),
|
|
49
|
+
WK8602: auditRule("WK8602", "merge-worthy", "guidance", "Multi-replica tail sampling gateway reached without trace-aware load balancing", "Send traces to the gateway through a loadbalancing exporter with routing_key traceID (k8s resolver, or dns on a headless Service), or run the gateway with one replica. It goes silent when the senders are in another build root (chant #1939).", { category: "correctness" }),
|
|
50
|
+
WK8603: auditRule("WK8603", "merge-worthy", "guidance", "k8s_cluster receiver runs in every collector copy", "Run the k8s_cluster receiver in a single-replica Deployment, or set its k8s_leader_elector to a k8s_leader_elector extension enabled in service.extensions.", { category: "correctness" }),
|
|
51
|
+
WK8604: auditRule("WK8604", "merge-worthy", "guidance", "Collector config in a ConfigMap fails the otel config checks", "Fix the collector config the ConfigMap carries: each finding keeps its OTEL1xx id and names the ConfigMap and key. Change the declared components and pipelines, not the rendered YAML.", { category: "correctness" }),
|
|
52
|
+
WK8605: auditRule("WK8605", "merge-worthy", "guidance", "Collector config reads the node but its workload lacks the node name or host mounts", "Set the node name variable from spec.nodeName with the downward API and mount the host paths hostmetrics and filelog read, read-only; OtelCollector adds them from the config.", { category: "correctness" }),
|
|
53
|
+
WK8606: auditRule("WK8606", "merge-worthy", "guidance", "PrometheusRule groups fail the prometheus rule-file checks", "Fix the rule group the PrometheusRule carries: each finding keeps its PROM1xx or PROM2xx id and names the PrometheusRule. Change the declared RuleGroup, not the rendered YAML.", { category: "correctness" }),
|
|
54
|
+
WK8701: auditRule("WK8701", "merge-worthy", "guidance", "PrometheusRule selected by no Prometheus or ThanosRuler", "Match the rule's labels and namespace with ruleSelector and ruleNamespaceSelector; null selects nothing, {} selects all.", { category: "correctness" }),
|
|
55
|
+
WK8702: auditRule("WK8702", "merge-worthy", "guidance", "Monitor selected by no Prometheus or PrometheusAgent", "Match the ServiceMonitor, PodMonitor, Probe or ScrapeConfig with the selector and namespace selector for its kind; null selects nothing, {} selects all.", { category: "correctness" }),
|
|
56
|
+
WK8703: auditRule("WK8703", "merge-worthy", "guidance", "AlertmanagerConfig selected by no Alertmanager", "Match the config's labels and namespace with alertmanagerConfigSelector and alertmanagerConfigNamespaceSelector; null selects nothing, {} selects all.", { category: "correctness" }),
|
|
57
|
+
WK8704: auditRule("WK8704", "merge-worthy", "guidance", "Monitor selects no Service or pod, or names a port that is not there", "Make the monitor's selector match the labels on the Service or pod template, and name a port the Service or container declares.", { category: "correctness" }),
|
|
58
|
+
WK8705: auditRule("WK8705", "merge-worthy", "guidance", "Prometheus selects no monitor at all", "Set serviceMonitorSelector, podMonitorSelector, probeSelector and scrapeConfigSelector for the monitor kinds it should scrape; {} selects all in the selected namespaces.", { category: "correctness" }),
|
|
59
|
+
};
|
|
60
|
+
// Prior art credits live beside the rules in ./audit-lineage.ts (see core audit/prior-art.ts).
|
|
61
|
+
applyLineage(k8sAuditCatalog, k8sAuditLineage);
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
export const k8sAuditLineage = {
|
|
2
|
+
FLUX002: [
|
|
3
|
+
{ tool: "flux-docs", rule: ".spec.sourceRef", url: "https://fluxcd.io/flux/components/kustomize/kustomizations/#source-reference", relation: "overlaps" },
|
|
4
|
+
],
|
|
5
|
+
FLUX003: [
|
|
6
|
+
{ tool: "flux-docs", rule: ".spec.dependsOn", url: "https://fluxcd.io/flux/components/kustomize/kustomizations/#dependencies", relation: "overlaps" },
|
|
7
|
+
],
|
|
8
|
+
WK8005: [
|
|
9
|
+
{ tool: "polaris", rule: "sensitiveContainerEnvVar", url: "https://polaris.docs.fairwinds.com/checks/security/", relation: "overlaps" },
|
|
10
|
+
{ tool: "kube-linter", rule: "env-var-secret", url: "https://github.com/stackrox/kube-linter/blob/main/docs/generated/checks.md#env-var-secret", relation: "overlaps" },
|
|
11
|
+
],
|
|
12
|
+
WK8006: [
|
|
13
|
+
{ tool: "kube-linter", rule: "latest-tag", url: "https://github.com/stackrox/kube-linter/blob/main/docs/generated/checks.md#latest-tag", relation: "equivalent" },
|
|
14
|
+
{ tool: "polaris", rule: "tagNotSpecified", url: "https://polaris.docs.fairwinds.com/checks/reliability/", relation: "equivalent" },
|
|
15
|
+
{ tool: "checkov", rule: "CKV_K8S_14", url: "https://www.checkov.io/5.Policy%20Index/kubernetes.html", relation: "equivalent" },
|
|
16
|
+
],
|
|
17
|
+
WK8041: [
|
|
18
|
+
{ tool: "polaris", rule: "sensitiveContainerEnvVar", url: "https://polaris.docs.fairwinds.com/checks/security/", relation: "overlaps" },
|
|
19
|
+
{ tool: "kube-linter", rule: "env-var-secret", url: "https://github.com/stackrox/kube-linter/blob/main/docs/generated/checks.md#env-var-secret", relation: "overlaps" },
|
|
20
|
+
{ tool: "datree", rule: "prevent-exposed-secrets-aws", url: "https://hub.datree.io/built-in-rules/prevent-exposed-secrets-aws", relation: "overlaps" },
|
|
21
|
+
],
|
|
22
|
+
WK8042: [
|
|
23
|
+
{ tool: "polaris", rule: "sensitiveConfigmapContent", url: "https://polaris.docs.fairwinds.com/checks/security/", relation: "overlaps" },
|
|
24
|
+
{ tool: "datree", rule: "prevent-exposed-secrets-privatekey", url: "https://hub.datree.io/built-in-rules/prevent-exposed-secrets-privatekey", relation: "overlaps" },
|
|
25
|
+
],
|
|
26
|
+
WK8101: [
|
|
27
|
+
{ tool: "kube-linter", rule: "mismatching-selector", url: "https://github.com/stackrox/kube-linter/blob/main/docs/generated/checks.md#mismatching-selector", relation: "equivalent" },
|
|
28
|
+
{ tool: "kube-score", rule: "deployment-pod-selector-labels-match-template-metadata-labels", url: "https://github.com/zegl/kube-score/blob/master/README_CHECKS.md", relation: "equivalent" },
|
|
29
|
+
],
|
|
30
|
+
WK8105: [
|
|
31
|
+
{ tool: "polaris", rule: "pullPolicyNotAlways", url: "https://polaris.docs.fairwinds.com/checks/reliability/", relation: "overlaps" },
|
|
32
|
+
{ tool: "kube-score", rule: "container-image-pull-policy", url: "https://github.com/zegl/kube-score/blob/master/README_CHECKS.md", relation: "overlaps" },
|
|
33
|
+
{ tool: "checkov", rule: "CKV_K8S_15", url: "https://www.checkov.io/5.Policy%20Index/kubernetes.html", relation: "overlaps" },
|
|
34
|
+
],
|
|
35
|
+
WK8201: [
|
|
36
|
+
{ tool: "polaris", rule: "cpuLimitsMissing", url: "https://polaris.docs.fairwinds.com/checks/efficiency/", relation: "overlaps" },
|
|
37
|
+
{ tool: "checkov", rule: "CKV_K8S_13", url: "https://www.checkov.io/5.Policy%20Index/kubernetes.html", relation: "overlaps" },
|
|
38
|
+
{ tool: "kube-score", rule: "container-resources", url: "https://github.com/zegl/kube-score/blob/master/README_CHECKS.md", relation: "overlaps" },
|
|
39
|
+
],
|
|
40
|
+
WK8202: [
|
|
41
|
+
{ tool: "kube-linter", rule: "privileged-container", url: "https://github.com/stackrox/kube-linter/blob/main/docs/generated/checks.md#privileged-container", relation: "equivalent" },
|
|
42
|
+
{ tool: "polaris", rule: "runAsPrivileged", url: "https://polaris.docs.fairwinds.com/checks/security/", relation: "equivalent" },
|
|
43
|
+
{ tool: "checkov", rule: "CKV_K8S_16", url: "https://www.checkov.io/5.Policy%20Index/kubernetes.html", relation: "equivalent" },
|
|
44
|
+
],
|
|
45
|
+
WK8203: [
|
|
46
|
+
{ tool: "kube-linter", rule: "no-read-only-root-fs", url: "https://github.com/stackrox/kube-linter/blob/main/docs/generated/checks.md#no-read-only-root-fs", relation: "equivalent" },
|
|
47
|
+
{ tool: "polaris", rule: "notReadOnlyRootFilesystem", url: "https://polaris.docs.fairwinds.com/checks/security/", relation: "equivalent" },
|
|
48
|
+
{ tool: "checkov", rule: "CKV_K8S_22", url: "https://www.checkov.io/5.Policy%20Index/kubernetes.html", relation: "equivalent" },
|
|
49
|
+
],
|
|
50
|
+
WK8204: [
|
|
51
|
+
{ tool: "polaris", rule: "runAsRootAllowed", url: "https://polaris.docs.fairwinds.com/checks/security/", relation: "equivalent" },
|
|
52
|
+
{ tool: "checkov", rule: "CKV_K8S_23", url: "https://www.checkov.io/5.Policy%20Index/kubernetes.html", relation: "equivalent" },
|
|
53
|
+
{ tool: "kube-linter", rule: "run-as-non-root", url: "https://github.com/stackrox/kube-linter/blob/main/docs/generated/checks.md#run-as-non-root", relation: "overlaps" },
|
|
54
|
+
],
|
|
55
|
+
WK8205: [
|
|
56
|
+
{ tool: "kubesec", rule: "containers[] .securityContext .capabilities .drop | index(\"ALL\")", url: "https://kubesec.io/basics/containers-securitycontext-capabilities-drop-index-all/", relation: "equivalent" },
|
|
57
|
+
{ tool: "kics", rule: "No Drop Capabilities for Containers", url: "https://docs.kics.io/latest/queries/kubernetes-queries/268ca686-7fb7-4ae9-b129-955a2a89064e/", relation: "overlaps" },
|
|
58
|
+
{ tool: "kube-linter", rule: "drop-net-raw-capability", url: "https://github.com/stackrox/kube-linter/blob/main/docs/generated/checks.md#drop-net-raw-capability", relation: "overlaps" },
|
|
59
|
+
],
|
|
60
|
+
WK8207: [
|
|
61
|
+
{ tool: "kube-linter", rule: "host-network", url: "https://github.com/stackrox/kube-linter/blob/main/docs/generated/checks.md#host-network", relation: "equivalent" },
|
|
62
|
+
{ tool: "polaris", rule: "hostNetworkSet", url: "https://polaris.docs.fairwinds.com/checks/security/", relation: "equivalent" },
|
|
63
|
+
{ tool: "checkov", rule: "CKV_K8S_19", url: "https://www.checkov.io/5.Policy%20Index/kubernetes.html", relation: "equivalent" },
|
|
64
|
+
],
|
|
65
|
+
WK8208: [
|
|
66
|
+
{ tool: "kube-linter", rule: "host-pid", url: "https://github.com/stackrox/kube-linter/blob/main/docs/generated/checks.md#host-pid", relation: "equivalent" },
|
|
67
|
+
{ tool: "polaris", rule: "hostPIDSet", url: "https://polaris.docs.fairwinds.com/checks/security/", relation: "equivalent" },
|
|
68
|
+
{ tool: "checkov", rule: "CKV_K8S_17", url: "https://www.checkov.io/5.Policy%20Index/kubernetes.html", relation: "equivalent" },
|
|
69
|
+
],
|
|
70
|
+
WK8209: [
|
|
71
|
+
{ tool: "kube-linter", rule: "host-ipc", url: "https://github.com/stackrox/kube-linter/blob/main/docs/generated/checks.md#host-ipc", relation: "equivalent" },
|
|
72
|
+
{ tool: "polaris", rule: "hostIPCSet", url: "https://polaris.docs.fairwinds.com/checks/security/", relation: "equivalent" },
|
|
73
|
+
{ tool: "checkov", rule: "CKV_K8S_18", url: "https://www.checkov.io/5.Policy%20Index/kubernetes.html", relation: "equivalent" },
|
|
74
|
+
],
|
|
75
|
+
WK8301: [
|
|
76
|
+
{ tool: "kube-score", rule: "pod-probes", url: "https://github.com/zegl/kube-score/blob/master/README_CHECKS.md", relation: "overlaps" },
|
|
77
|
+
{ tool: "polaris", rule: "readinessProbeMissing", url: "https://polaris.docs.fairwinds.com/checks/reliability/", relation: "overlaps" },
|
|
78
|
+
{ tool: "checkov", rule: "CKV_K8S_8", url: "https://www.checkov.io/5.Policy%20Index/kubernetes.html", relation: "overlaps" },
|
|
79
|
+
],
|
|
80
|
+
WK8302: [
|
|
81
|
+
{ tool: "polaris", rule: "deploymentMissingReplicas", url: "https://polaris.docs.fairwinds.com/checks/reliability/", relation: "equivalent" },
|
|
82
|
+
{ tool: "kube-score", rule: "deployment-replicas", url: "https://github.com/zegl/kube-score/blob/master/README_CHECKS.md", relation: "equivalent" },
|
|
83
|
+
{ tool: "datree", rule: "ensure-minimum-two-replicas", url: "https://hub.datree.io/built-in-rules/ensure-minimum-two-replicas", relation: "equivalent" },
|
|
84
|
+
],
|
|
85
|
+
WK8303: [
|
|
86
|
+
{ tool: "kube-score", rule: "deployment-has-poddisruptionbudget", url: "https://github.com/zegl/kube-score/blob/master/README_CHECKS.md", relation: "overlaps" },
|
|
87
|
+
{ tool: "polaris", rule: "missingPodDisruptionBudget", url: "https://polaris.docs.fairwinds.com/checks/reliability/", relation: "overlaps" },
|
|
88
|
+
{ tool: "kics", rule: "Deployment Without PodDisruptionBudget", url: "https://docs.kics.io/latest/queries/kubernetes-queries/b23e9b98-0cb6-4fc9-b257-1f3270442678/", relation: "overlaps" },
|
|
89
|
+
],
|
|
90
|
+
WK8304: [
|
|
91
|
+
{ tool: "polaris", rule: "tlsSettingsMissing", url: "https://polaris.docs.fairwinds.com/checks/security/", relation: "overlaps" },
|
|
92
|
+
],
|
|
93
|
+
WK8305: [
|
|
94
|
+
{ tool: "kube-linter", rule: "dangling-ingress", url: "https://github.com/stackrox/kube-linter/blob/main/docs/generated/checks.md#dangling-ingress", relation: "overlaps" },
|
|
95
|
+
{ tool: "kube-score", rule: "ingress-targets-service", url: "https://github.com/zegl/kube-score/blob/master/README_CHECKS.md", relation: "overlaps" },
|
|
96
|
+
],
|
|
97
|
+
WK8405: [
|
|
98
|
+
{ tool: "kube-score", rule: "deployment-has-poddisruptionbudget", url: "https://github.com/zegl/kube-score/blob/master/README_CHECKS.md", relation: "overlaps" },
|
|
99
|
+
{ tool: "polaris", rule: "missingPodDisruptionBudget", url: "https://polaris.docs.fairwinds.com/checks/reliability/", relation: "overlaps" },
|
|
100
|
+
],
|
|
101
|
+
WK8406: [
|
|
102
|
+
{ tool: "checkov", rule: "CKV_K8S_13", url: "https://www.checkov.io/5.Policy%20Index/kubernetes.html", relation: "overlaps" },
|
|
103
|
+
{ tool: "kube-score", rule: "container-resources", url: "https://github.com/zegl/kube-score/blob/master/README_CHECKS.md", relation: "overlaps" },
|
|
104
|
+
{ tool: "kube-linter", rule: "unset-memory-requirements", url: "https://github.com/stackrox/kube-linter/blob/main/docs/generated/checks.md#unset-memory-requirements", relation: "overlaps" },
|
|
105
|
+
],
|
|
106
|
+
WK8501: [
|
|
107
|
+
{ tool: "kube-linter", rule: "schema-validation", url: "https://github.com/stackrox/kube-linter/blob/main/docs/generated/checks.md#schema-validation", relation: "overlaps" },
|
|
108
|
+
],
|
|
109
|
+
WK8502: [
|
|
110
|
+
{ tool: "kube-linter", rule: "schema-validation", url: "https://github.com/stackrox/kube-linter/blob/main/docs/generated/checks.md#schema-validation", relation: "overlaps" },
|
|
111
|
+
],
|
|
112
|
+
WK8503: [
|
|
113
|
+
{ tool: "kube-linter", rule: "env-value-from", url: "https://github.com/stackrox/kube-linter/blob/main/docs/generated/checks.md#env-value-from", relation: "overlaps" },
|
|
114
|
+
],
|
|
115
|
+
WK8505: [
|
|
116
|
+
{ tool: "flux-docs", rule: ".spec.decryption", url: "https://fluxcd.io/flux/components/kustomize/kustomizations/#decryption", relation: "overlaps" },
|
|
117
|
+
],
|
|
118
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { docsToManifests } from "./k8s-helpers.js";
|
|
2
|
+
/** The always-present, in-cluster Argo destination. */
|
|
3
|
+
export const IN_CLUSTER_SERVER = "https://kubernetes.default.svc";
|
|
4
|
+
export const IN_CLUSTER_NAME = "in-cluster";
|
|
5
|
+
/** Label that marks a Secret as an Argo CD external cluster registration. */
|
|
6
|
+
export const CLUSTER_SECRET_TYPE_LABEL = "argocd.argoproj.io/secret-type";
|
|
7
|
+
/** All manifests across every lexicon output. */
|
|
8
|
+
export function allManifests(ctx) {
|
|
9
|
+
return docsToManifests(ctx);
|
|
10
|
+
}
|
|
11
|
+
/** Manifests of a given Argo kind. */
|
|
12
|
+
export function manifestsOfKind(manifests, kind) {
|
|
13
|
+
return manifests.filter((m) => m.kind === kind);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Read a string field from a Secret's stringData or data block.
|
|
17
|
+
* (Argo cluster Secrets conventionally use stringData.)
|
|
18
|
+
*/
|
|
19
|
+
export function secretField(manifest, key) {
|
|
20
|
+
const stringData = manifest.stringData;
|
|
21
|
+
const data = manifest.data;
|
|
22
|
+
const fromStringData = stringData?.[key];
|
|
23
|
+
if (typeof fromStringData === "string")
|
|
24
|
+
return fromStringData;
|
|
25
|
+
const fromData = data?.[key];
|
|
26
|
+
if (typeof fromData === "string")
|
|
27
|
+
return fromData;
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
/** True if the Secret is labelled as an Argo cluster registration. */
|
|
31
|
+
export function isClusterSecret(manifest) {
|
|
32
|
+
if (manifest.kind !== "Secret")
|
|
33
|
+
return false;
|
|
34
|
+
const labels = manifest.metadata?.labels;
|
|
35
|
+
return labels?.[CLUSTER_SECRET_TYPE_LABEL] === "cluster";
|
|
36
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { allManifests, manifestsOfKind } from "./argo-helpers.js";
|
|
2
|
+
export const argo002 = {
|
|
3
|
+
id: "ARGO002",
|
|
4
|
+
description: "Application.spec.project must reference a declared AppProject (or the built-in default)",
|
|
5
|
+
check(ctx) {
|
|
6
|
+
const diagnostics = [];
|
|
7
|
+
const manifests = allManifests(ctx);
|
|
8
|
+
const declaredProjects = new Set(manifestsOfKind(manifests, "AppProject")
|
|
9
|
+
.map((p) => p.metadata?.name)
|
|
10
|
+
.filter((n) => typeof n === "string"));
|
|
11
|
+
// Argo ships a built-in default project.
|
|
12
|
+
declaredProjects.add("default");
|
|
13
|
+
for (const app of manifestsOfKind(manifests, "Application")) {
|
|
14
|
+
const project = app.spec?.project;
|
|
15
|
+
const name = app.metadata?.name ?? "Application";
|
|
16
|
+
// No project set → defaults to `default` at sync time; not this check's job.
|
|
17
|
+
if (typeof project !== "string" || project === "")
|
|
18
|
+
continue;
|
|
19
|
+
if (declaredProjects.has(project))
|
|
20
|
+
continue;
|
|
21
|
+
diagnostics.push({
|
|
22
|
+
checkId: "ARGO002",
|
|
23
|
+
severity: "error",
|
|
24
|
+
message: `Application "${name}" references AppProject "${project}", which is not declared. Add an AppProject named "${project}" or reference an existing project.`,
|
|
25
|
+
entity: name,
|
|
26
|
+
lexicon: "k8s",
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return diagnostics;
|
|
30
|
+
},
|
|
31
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { allManifests, manifestsOfKind, isClusterSecret, secretField, IN_CLUSTER_SERVER, IN_CLUSTER_NAME, } from "./argo-helpers.js";
|
|
2
|
+
export const argo003 = {
|
|
3
|
+
id: "ARGO003",
|
|
4
|
+
description: "Application.spec.destination must reference a registered cluster or the in-cluster target",
|
|
5
|
+
check(ctx) {
|
|
6
|
+
const diagnostics = [];
|
|
7
|
+
const manifests = allManifests(ctx);
|
|
8
|
+
const registeredServers = new Set([IN_CLUSTER_SERVER]);
|
|
9
|
+
const registeredNames = new Set([IN_CLUSTER_NAME]);
|
|
10
|
+
for (const secret of manifests.filter(isClusterSecret)) {
|
|
11
|
+
const server = secretField(secret, "server");
|
|
12
|
+
if (server)
|
|
13
|
+
registeredServers.add(server);
|
|
14
|
+
const name = secretField(secret, "name") ?? secret.metadata?.name;
|
|
15
|
+
if (typeof name === "string")
|
|
16
|
+
registeredNames.add(name);
|
|
17
|
+
}
|
|
18
|
+
for (const app of manifestsOfKind(manifests, "Application")) {
|
|
19
|
+
const name = app.metadata?.name ?? "Application";
|
|
20
|
+
const destination = app.spec?.destination;
|
|
21
|
+
if (!destination || (destination.server === undefined && destination.name === undefined)) {
|
|
22
|
+
diagnostics.push({
|
|
23
|
+
checkId: "ARGO003",
|
|
24
|
+
severity: "error",
|
|
25
|
+
message: `Application "${name}" has no spec.destination.server or spec.destination.name — Argo cannot resolve a target cluster.`,
|
|
26
|
+
entity: name,
|
|
27
|
+
lexicon: "k8s",
|
|
28
|
+
});
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (typeof destination.server === "string" && !registeredServers.has(destination.server)) {
|
|
32
|
+
diagnostics.push({
|
|
33
|
+
checkId: "ARGO003",
|
|
34
|
+
severity: "error",
|
|
35
|
+
message: `Application "${name}" targets cluster server "${destination.server}", which is not registered. Register it with a cluster Secret (label argocd.argoproj.io/secret-type=cluster) or use the in-cluster target.`,
|
|
36
|
+
entity: name,
|
|
37
|
+
lexicon: "k8s",
|
|
38
|
+
});
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (typeof destination.name === "string" && !registeredNames.has(destination.name)) {
|
|
42
|
+
diagnostics.push({
|
|
43
|
+
checkId: "ARGO003",
|
|
44
|
+
severity: "error",
|
|
45
|
+
message: `Application "${name}" targets cluster name "${destination.name}", which is not registered. Register it with a cluster Secret or use the in-cluster target.`,
|
|
46
|
+
entity: name,
|
|
47
|
+
lexicon: "k8s",
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return diagnostics;
|
|
52
|
+
},
|
|
53
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ARGO005: Application source.path should point at an existing directory (warn)
|
|
3
|
+
*
|
|
4
|
+
* When an Argo `Application` syncs from a git source it names a `source.path`
|
|
5
|
+
* inside the repo. In the common monorepo layout — Argo watches the same repo
|
|
6
|
+
* Chant builds — that path is relative to the build root, so a typo'd or moved
|
|
7
|
+
* path surfaces as a sync error only after deploy. This check warns when the
|
|
8
|
+
* path doesn't resolve to a directory under the build root.
|
|
9
|
+
*
|
|
10
|
+
* It is a warning, not an error: Applications that sync from a *different*
|
|
11
|
+
* remote repo legitimately reference paths that don't exist locally. Helm chart
|
|
12
|
+
* sources (`source.chart`) and pathless sources are skipped.
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync, statSync } from "fs";
|
|
15
|
+
import { isAbsolute, resolve } from "path";
|
|
16
|
+
import { allManifests, manifestsOfKind } from "./argo-helpers.js";
|
|
17
|
+
function dirExists(path) {
|
|
18
|
+
try {
|
|
19
|
+
const full = isAbsolute(path) ? path : resolve(process.cwd(), path);
|
|
20
|
+
return existsSync(full) && statSync(full).isDirectory();
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export const argo005 = {
|
|
27
|
+
id: "ARGO005",
|
|
28
|
+
description: "Application source.path should resolve to an existing directory under the build root",
|
|
29
|
+
check(ctx) {
|
|
30
|
+
const diagnostics = [];
|
|
31
|
+
for (const app of manifestsOfKind(allManifests(ctx), "Application")) {
|
|
32
|
+
const name = app.metadata?.name ?? "Application";
|
|
33
|
+
const source = app.spec?.source;
|
|
34
|
+
if (!source)
|
|
35
|
+
continue;
|
|
36
|
+
// Helm chart sources have no filesystem path.
|
|
37
|
+
if (typeof source.chart === "string" && source.chart !== "")
|
|
38
|
+
continue;
|
|
39
|
+
const path = source.path;
|
|
40
|
+
if (typeof path !== "string" || path === "")
|
|
41
|
+
continue;
|
|
42
|
+
if (dirExists(path))
|
|
43
|
+
continue;
|
|
44
|
+
diagnostics.push({
|
|
45
|
+
checkId: "ARGO005",
|
|
46
|
+
severity: "warning",
|
|
47
|
+
message: `Application "${name}" source.path "${path}" does not resolve to a directory under the build root. Confirm the path (or ignore if it lives in a different repo).`,
|
|
48
|
+
entity: name,
|
|
49
|
+
lexicon: "k8s",
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return diagnostics;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crd-schema-helpers.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/crd-schema-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"crd-schema-helpers.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/crd-schema-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAGlE,YAAY,EAAE,cAAc,EAAE,CAAC;AAgB/B;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAkBlE;AAED,qFAAqF;AACrF,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,IAAI,GAAG,IAAI,CAEvF;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,WAAW,GAAG,cAAc,GAAG,SAAS,CAM/E;AAED,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,GAAG,EAAE,gBAAgB,GAAG,KAAK,CAAC;IAAE,QAAQ,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,cAAc,CAAA;CAAE,CAAC,CAO/G;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,GAAG,eAAe,CAAC;IACxC,uFAAuF;IACvF,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,SAAS,GAAG,WAAW,EAAE,CAIjG;AAgGD,gDAAgD;AAChD,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAcxD;AAED,oFAAoF;AACpF,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAWjF;AAED,oDAAoD;AACpD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM,CAE3D"}
|