@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,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Committed SOPS ciphertext as a build artifact (epic lex00/iac-cd-bench#6).
|
|
3
|
+
*
|
|
4
|
+
* A `declareSecret({ provenance: "committed-encrypted", file })` names a
|
|
5
|
+
* repo-relative path to ciphertext that lives in git. This module is the
|
|
6
|
+
* resolution stage: at `buildRoots()` — chant's one sanctioned impure seam,
|
|
7
|
+
* the same one the kustomize roots and cedar's `Schema` use — it reads those
|
|
8
|
+
* bytes, validates the document is what the declaration claims, and wraps
|
|
9
|
+
* them in an entity the serializer copies out VERBATIM as a sidecar file.
|
|
10
|
+
*
|
|
11
|
+
* Three properties this is built to keep:
|
|
12
|
+
*
|
|
13
|
+
* - **Never inlined.** The bytes go to `SerializerResult.files`, never into
|
|
14
|
+
* the primary multi-document YAML. chant's own appliers read the primary
|
|
15
|
+
* output, so "chant pushes an undecrypted Secret into a cluster" is
|
|
16
|
+
* structurally impossible rather than merely unlikely. Flux is the only
|
|
17
|
+
* thing that applies the sidecar, and Flux decrypts first.
|
|
18
|
+
* - **Deterministic and offline.** The bytes are copied, not re-serialized:
|
|
19
|
+
* no parse-and-re-emit round trip, no key sorting, no digest. No `sops`
|
|
20
|
+
* binary is invoked in either direction, so a missing binary and a missing
|
|
21
|
+
* age key are not failure modes — the plaintext has no representation here
|
|
22
|
+
* at any stage.
|
|
23
|
+
* - **Fails loudly, not weirdly.** A missing file, a non-Secret document, a
|
|
24
|
+
* name mismatch, a missing `sops` block, or a `data`/`stringData` value
|
|
25
|
+
* that is not `ENC[...]`-shaped refuses with the declared path in the
|
|
26
|
+
* message. That last check is the one that earns the feature: it catches
|
|
27
|
+
* "edited the file and forgot to re-encrypt", the exact failure mode that
|
|
28
|
+
* puts plaintext into git.
|
|
29
|
+
*
|
|
30
|
+
* The entity is deliberately internal — not exported from the package entry
|
|
31
|
+
* point, not constructible by an author. The authored surface is
|
|
32
|
+
* `declareSecret`; a public entity taking inline bytes is the one thing this
|
|
33
|
+
* whole design exists to prevent.
|
|
34
|
+
*
|
|
35
|
+
* Design: docs/design/committed-encrypted-sops-provenance.md §3.
|
|
36
|
+
*/
|
|
37
|
+
import { readFileSync } from "node:fs";
|
|
38
|
+
import { resolve } from "node:path";
|
|
39
|
+
import { loadAll } from "js-yaml";
|
|
40
|
+
import { DECLARABLE_MARKER } from "@intentius/chant/declarable";
|
|
41
|
+
import { ENCRYPTED_SECRET_ENTITY_PREFIX, ENCRYPTED_SECRET_FILE_MARKER, ENCRYPTED_SECRET_FILE_TYPE, sidecarFilename, } from "./entity.js";
|
|
42
|
+
import { collectSecretDeclarations, isCommittedEncryptedSecret, } from "@intentius/chant/secret-provenance";
|
|
43
|
+
export { ENCRYPTED_SECRET_FILE_MARKER, ENCRYPTED_SECRET_FILE_TYPE, ENCRYPTED_SECRET_ENTITY_PREFIX, isEncryptedSecretFileEntity, sidecarFilename, } from "./entity.js";
|
|
44
|
+
/**
|
|
45
|
+
* Validate one committed ciphertext document against its declaration.
|
|
46
|
+
*
|
|
47
|
+
* Returns every problem found, so an author sees all of them at once rather
|
|
48
|
+
* than one per build. Both consumers share this: the `buildRoots()` stage
|
|
49
|
+
* turns the list into a refusal, and WK8504 turns it into diagnostics.
|
|
50
|
+
*
|
|
51
|
+
* A value is only ever tested for the `ENC[` prefix, inside this one frame,
|
|
52
|
+
* and only the offending KEY name is reported — the discipline
|
|
53
|
+
* `declareSecret`'s own runtime guard uses.
|
|
54
|
+
*/
|
|
55
|
+
export function validateEncryptedSecretDocument(text, declared) {
|
|
56
|
+
const problems = [];
|
|
57
|
+
let docs;
|
|
58
|
+
try {
|
|
59
|
+
docs = loadAll(text);
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
return {
|
|
63
|
+
problems: [`${declared.file} is not valid YAML: ${err instanceof Error ? err.message : String(err)}`],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const present = docs.filter((d) => d !== null && d !== undefined);
|
|
67
|
+
if (present.length === 0) {
|
|
68
|
+
return { problems: [`${declared.file} contains no YAML document`] };
|
|
69
|
+
}
|
|
70
|
+
if (present.length > 1) {
|
|
71
|
+
// v1 keys a declaration by one `name`; a declaration-per-document sharing
|
|
72
|
+
// a file, or a `names: string[]` field, is a design change to make
|
|
73
|
+
// deliberately rather than by accident.
|
|
74
|
+
return {
|
|
75
|
+
problems: [
|
|
76
|
+
`${declared.file} contains ${present.length} YAML documents — ` +
|
|
77
|
+
`a committed-encrypted declaration covers exactly one Secret`,
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const doc = present[0];
|
|
82
|
+
if (typeof doc !== "object" || doc === null || Array.isArray(doc)) {
|
|
83
|
+
return { problems: [`${declared.file} is not a Kubernetes object`] };
|
|
84
|
+
}
|
|
85
|
+
const record = doc;
|
|
86
|
+
if (record.apiVersion !== "v1" || record.kind !== "Secret") {
|
|
87
|
+
problems.push(`${declared.file} is not a v1 Secret ` +
|
|
88
|
+
`(apiVersion "${String(record.apiVersion)}", kind "${String(record.kind)}")`);
|
|
89
|
+
}
|
|
90
|
+
const metadata = asRecord(record.metadata);
|
|
91
|
+
const name = typeof metadata?.name === "string" ? metadata.name : undefined;
|
|
92
|
+
const namespace = typeof metadata?.namespace === "string" ? metadata.namespace : undefined;
|
|
93
|
+
if (name !== declared.name) {
|
|
94
|
+
problems.push(`${declared.file} has metadata.name "${name ?? "(none)"}", ` +
|
|
95
|
+
`but the declaration names "${declared.name}"`);
|
|
96
|
+
}
|
|
97
|
+
// SOPS's own metadata block. Its absence means the file was never encrypted
|
|
98
|
+
// (or was decrypted in place and committed), which is the whole point of
|
|
99
|
+
// checking. `sops.mac` is a MAC over the PLAINTEXT — it is carried by the
|
|
100
|
+
// verbatim copy and must never be read as a semantic value, so nothing here
|
|
101
|
+
// looks past the block's presence.
|
|
102
|
+
if (asRecord(record.sops) === undefined) {
|
|
103
|
+
problems.push(`${declared.file} has no top-level \`sops\` block — it does not look encrypted. ` +
|
|
104
|
+
`Run \`sops -e -i\` before committing.`);
|
|
105
|
+
}
|
|
106
|
+
// Scoped to data/stringData alone: SOPS's `encrypted_regex` leaves
|
|
107
|
+
// apiVersion, kind and metadata cleartext by design.
|
|
108
|
+
const keys = [];
|
|
109
|
+
for (const field of ["data", "stringData"]) {
|
|
110
|
+
const block = asRecord(record[field]);
|
|
111
|
+
if (block === undefined)
|
|
112
|
+
continue;
|
|
113
|
+
for (const [key, value] of Object.entries(block)) {
|
|
114
|
+
keys.push(key);
|
|
115
|
+
if (typeof value !== "string" || !value.startsWith("ENC[")) {
|
|
116
|
+
problems.push(`${declared.file}: ${field}."${key}" is not encrypted (expected an ENC[...] value) — ` +
|
|
117
|
+
`re-run \`sops -e -i\` before committing`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return { problems, document: { name, namespace, keys } };
|
|
122
|
+
}
|
|
123
|
+
function asRecord(value) {
|
|
124
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
125
|
+
? value
|
|
126
|
+
: undefined;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Read and validate every committed-encrypted declaration, returning one
|
|
130
|
+
* entity per resolved file. Throws on the first declaration that does not
|
|
131
|
+
* resolve, with every problem for that declaration in the message — a build
|
|
132
|
+
* that would emit a broken or unencrypted Secret must not proceed.
|
|
133
|
+
*/
|
|
134
|
+
export function resolveEncryptedSecrets(options) {
|
|
135
|
+
const read = options.readFile ?? ((path) => readFileSync(path, "utf-8"));
|
|
136
|
+
const entities = new Map();
|
|
137
|
+
const byFilename = new Map();
|
|
138
|
+
for (const decl of options.declarations.values()) {
|
|
139
|
+
const absolute = resolve(options.projectRoot, decl.file);
|
|
140
|
+
let text;
|
|
141
|
+
try {
|
|
142
|
+
text = read(absolute);
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
throw new Error(`declareSecret("${decl.name}"): committed ciphertext not readable at ${absolute} ` +
|
|
146
|
+
`(declared as "${decl.file}") — ${err instanceof Error ? err.message : String(err)}`);
|
|
147
|
+
}
|
|
148
|
+
const { problems, document } = validateEncryptedSecretDocument(text, decl);
|
|
149
|
+
if (problems.length > 0) {
|
|
150
|
+
throw new Error(`declareSecret("${decl.name}"): committed ciphertext does not resolve —\n ` +
|
|
151
|
+
problems.join("\n "));
|
|
152
|
+
}
|
|
153
|
+
const filename = sidecarFilename(decl.file);
|
|
154
|
+
const collides = byFilename.get(filename);
|
|
155
|
+
if (collides !== undefined) {
|
|
156
|
+
throw new Error(`declareSecret("${decl.name}"): sidecar filename "${filename}" collides with the one ` +
|
|
157
|
+
`"${collides}" already emits — additional files share one flat namespace, so two ` +
|
|
158
|
+
`declarations whose paths share a basename would silently overwrite each other`);
|
|
159
|
+
}
|
|
160
|
+
byFilename.set(filename, decl.file);
|
|
161
|
+
const entity = {
|
|
162
|
+
lexicon: "k8s",
|
|
163
|
+
entityType: ENCRYPTED_SECRET_FILE_TYPE,
|
|
164
|
+
filename,
|
|
165
|
+
text,
|
|
166
|
+
secretName: decl.name,
|
|
167
|
+
...(document?.namespace !== undefined ? { namespace: document.namespace } : {}),
|
|
168
|
+
sourcePath: decl.file,
|
|
169
|
+
[DECLARABLE_MARKER]: true,
|
|
170
|
+
[ENCRYPTED_SECRET_FILE_MARKER]: true,
|
|
171
|
+
};
|
|
172
|
+
entities.set(`${ENCRYPTED_SECRET_ENTITY_PREFIX}${decl.name}`, entity);
|
|
173
|
+
}
|
|
174
|
+
return entities;
|
|
175
|
+
}
|
|
176
|
+
/** Every committed-encrypted declaration in an entity map, keyed by entity name. */
|
|
177
|
+
export function committedEncryptedDeclarations(entities) {
|
|
178
|
+
const out = new Map();
|
|
179
|
+
for (const [name, decl] of collectSecretDeclarations(entities)) {
|
|
180
|
+
if (isCommittedEncryptedSecret(decl))
|
|
181
|
+
out.set(name, decl);
|
|
182
|
+
}
|
|
183
|
+
return out;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* The `buildRoots` contribution: one entity per committed-encrypted
|
|
187
|
+
* declaration, holding the file's bytes verbatim.
|
|
188
|
+
*/
|
|
189
|
+
export async function encryptedSecretBuildRoot(ctx) {
|
|
190
|
+
const declarations = committedEncryptedDeclarations(ctx.entities ?? new Map());
|
|
191
|
+
if (declarations.size === 0)
|
|
192
|
+
return { entities: new Map() };
|
|
193
|
+
return { entities: resolveEncryptedSecrets({ projectRoot: ctx.projectRoot, declarations }) };
|
|
194
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The committed-ciphertext entity: marker, shape and type guard only.
|
|
3
|
+
*
|
|
4
|
+
* Split from `./encrypted-secret-file.ts` so the serializer can recognise the
|
|
5
|
+
* entity without pulling `node:fs` and a YAML parser into its module graph —
|
|
6
|
+
* the resolution stage needs both, the emission stage needs neither.
|
|
7
|
+
*
|
|
8
|
+
* Not exported from the package entry point and not constructible by an
|
|
9
|
+
* author: the authored surface is `declareSecret({ provenance:
|
|
10
|
+
* "committed-encrypted", file })`. A public entity taking inline bytes is
|
|
11
|
+
* exactly what this design exists to prevent.
|
|
12
|
+
*/
|
|
13
|
+
import { basename } from "node:path";
|
|
14
|
+
/** Marks an entity whose bytes are committed ciphertext, emitted verbatim. */
|
|
15
|
+
export const ENCRYPTED_SECRET_FILE_MARKER = Symbol.for("chant.k8s.encryptedSecretFile");
|
|
16
|
+
/**
|
|
17
|
+
* The entity's type name. Deliberately outside the `K8s::{Group}::{Kind}`
|
|
18
|
+
* surface a real Secret speaks: an observer must NOT read this against a live
|
|
19
|
+
* cluster — the ciphertext is not the thing in the cluster, the decrypted
|
|
20
|
+
* Secret is — so a type with no generated operation surface, reporting the
|
|
21
|
+
* honest NOT-OBSERVED, is the right answer.
|
|
22
|
+
*/
|
|
23
|
+
export const ENCRYPTED_SECRET_FILE_TYPE = "K8s::Sops::EncryptedSecretFile";
|
|
24
|
+
/** Entity-map key prefix, colon-separated so it cannot collide with a TS export name. */
|
|
25
|
+
export const ENCRYPTED_SECRET_ENTITY_PREFIX = "sops:";
|
|
26
|
+
export function isEncryptedSecretFileEntity(entity) {
|
|
27
|
+
return entity[ENCRYPTED_SECRET_FILE_MARKER] === true;
|
|
28
|
+
}
|
|
29
|
+
/** The sidecar filename a declaration's file is emitted under. */
|
|
30
|
+
export function sidecarFilename(file) {
|
|
31
|
+
return basename(file);
|
|
32
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kubernetes OpenAPI schema fetching — downloads the Swagger 2.0 spec
|
|
3
|
+
* and caches it locally.
|
|
4
|
+
*/
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
import { fetchWithCache, clearCacheFile } from "@intentius/chant/codegen/fetch";
|
|
8
|
+
/**
|
|
9
|
+
* Pinned Kubernetes version for schema download.
|
|
10
|
+
*/
|
|
11
|
+
export const K8S_SCHEMA_VERSION = "v1.36.2";
|
|
12
|
+
/**
|
|
13
|
+
* Build the schema URL for a given version.
|
|
14
|
+
*/
|
|
15
|
+
function schemaUrl(version) {
|
|
16
|
+
return `https://raw.githubusercontent.com/kubernetes/kubernetes/${version}/api/openapi-spec/swagger.json`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Get the cache file path for the K8s schema.
|
|
20
|
+
*/
|
|
21
|
+
export function getCachePath(version) {
|
|
22
|
+
const ref = version ?? K8S_SCHEMA_VERSION;
|
|
23
|
+
return join(homedir(), ".chant", `k8s-swagger-${ref}.json`);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Fetch the Kubernetes OpenAPI Swagger spec, returning the raw JSON buffer.
|
|
27
|
+
* Uses local file caching with 24-hour TTL.
|
|
28
|
+
*
|
|
29
|
+
* @param force Bypass cache and download fresh.
|
|
30
|
+
* @param version Kubernetes version tag. Defaults to {@link K8S_SCHEMA_VERSION}.
|
|
31
|
+
*/
|
|
32
|
+
export async function fetchK8sSchema(force, version) {
|
|
33
|
+
const ref = version ?? K8S_SCHEMA_VERSION;
|
|
34
|
+
return fetchWithCache({
|
|
35
|
+
url: schemaUrl(ref),
|
|
36
|
+
cacheFile: getCachePath(ref),
|
|
37
|
+
}, force);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Fetch the K8s schema and return it as a Map<typeName, Buffer>
|
|
41
|
+
* compatible with the generatePipeline fetchSchemas callback.
|
|
42
|
+
*
|
|
43
|
+
* The K8s schema is a single document, so we return a single entry
|
|
44
|
+
* keyed by "Kubernetes::OpenAPI" — the parse step will split it
|
|
45
|
+
* into multiple resources.
|
|
46
|
+
*/
|
|
47
|
+
export async function fetchSchemas(force, version) {
|
|
48
|
+
const data = await fetchK8sSchema(force, version);
|
|
49
|
+
const schemas = new Map();
|
|
50
|
+
schemas.set("Kubernetes::OpenAPI", data);
|
|
51
|
+
return schemas;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Clear the cached schema file.
|
|
55
|
+
*/
|
|
56
|
+
export function clearCache(version) {
|
|
57
|
+
clearCacheFile(getCachePath(version));
|
|
58
|
+
}
|