@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.
Files changed (270) hide show
  1. package/dist/actions/apps.js +18 -0
  2. package/dist/actions/batch.js +6 -0
  3. package/dist/actions/core.js +48 -0
  4. package/dist/actions/index.js +13 -0
  5. package/dist/actions/networking.js +11 -0
  6. package/dist/actions/rbac.js +8 -0
  7. package/dist/api/classify.js +123 -0
  8. package/dist/api/connect.js +62 -0
  9. package/dist/api/fake-cluster.js +175 -0
  10. package/dist/api/operation-surface.d.ts.map +1 -1
  11. package/dist/api/operation-surface.js +109 -0
  12. package/dist/api/owner-chain.js +100 -0
  13. package/dist/api/sweep-types.js +35 -0
  14. package/dist/behaviour-kinds.js +82 -0
  15. package/dist/codegen/docs-cli.js +2 -0
  16. package/dist/codegen/docs.js +146 -0
  17. package/dist/codegen/generate-cli.js +30 -0
  18. package/dist/codegen/generate-lexicon.js +74 -0
  19. package/dist/codegen/generate-list-map-keys.js +88 -0
  20. package/dist/codegen/generate-operations.js +53 -0
  21. package/dist/codegen/generate-typescript.js +124 -0
  22. package/dist/codegen/generate.js +173 -0
  23. package/dist/codegen/naming.js +174 -0
  24. package/dist/codegen/package.js +36 -0
  25. package/dist/codegen/patches.js +88 -0
  26. package/dist/codegen/typecheck.js +4 -0
  27. package/dist/codegen/versions.js +41 -0
  28. package/dist/components/argo-app.js +51 -0
  29. package/dist/components/builders.js +22 -0
  30. package/dist/components/capability-plugin.js +31 -0
  31. package/dist/components/flux-reconcile.js +61 -0
  32. package/dist/components/index.d.ts +1 -1
  33. package/dist/components/index.d.ts.map +1 -1
  34. package/dist/components/index.js +18 -0
  35. package/dist/components/kubectl-apply.d.ts +83 -1
  36. package/dist/components/kubectl-apply.d.ts.map +1 -1
  37. package/dist/components/kubectl-apply.js +274 -0
  38. package/dist/components/kustomize-apply.js +25 -0
  39. package/dist/composites/adot-collector.js +184 -0
  40. package/dist/composites/agic-ingress.js +85 -0
  41. package/dist/composites/aks-external-dns-agent.js +146 -0
  42. package/dist/composites/alb-ingress.js +86 -0
  43. package/dist/composites/argo-app.js +199 -0
  44. package/dist/composites/autoscaled-service.js +174 -0
  45. package/dist/composites/azure-disk-storage-class.js +47 -0
  46. package/dist/composites/azure-file-storage-class.js +48 -0
  47. package/dist/composites/azure-monitor-collector.js +176 -0
  48. package/dist/composites/batch-job.js +139 -0
  49. package/dist/composites/catalog.js +4110 -0
  50. package/dist/composites/cockroachdb-cluster.js +371 -0
  51. package/dist/composites/cockroachdb-region-stack.js +369 -0
  52. package/dist/composites/config-connector-context.js +40 -0
  53. package/dist/composites/configured-app.js +135 -0
  54. package/dist/composites/cron-schedule.js +38 -0
  55. package/dist/composites/cron-workload.js +109 -0
  56. package/dist/composites/ebs-storage-class.js +54 -0
  57. package/dist/composites/efs-storage-class.js +46 -0
  58. package/dist/composites/external-dns-agent.js +122 -0
  59. package/dist/composites/filestore-storage-class.js +49 -0
  60. package/dist/composites/fluent-bit-agent.js +157 -0
  61. package/dist/composites/flux-app.js +156 -0
  62. package/dist/composites/gce-ingress.js +83 -0
  63. package/dist/composites/gce-pd-storage-class.js +49 -0
  64. package/dist/composites/gke-external-dns-agent.js +129 -0
  65. package/dist/composites/gke-fluent-bit-agent.js +165 -0
  66. package/dist/composites/gke-gateway.js +91 -0
  67. package/dist/composites/gke-otel-collector.js +94 -0
  68. package/dist/composites/index.js +51 -0
  69. package/dist/composites/inference-service.js +76 -0
  70. package/dist/composites/irsa-service-account.js +77 -0
  71. package/dist/composites/member-defaults.js +14 -0
  72. package/dist/composites/metrics-server.js +181 -0
  73. package/dist/composites/model.js +92 -0
  74. package/dist/composites/monitored-service.js +152 -0
  75. package/dist/composites/namespace-env.js +114 -0
  76. package/dist/composites/network-isolated-app.js +124 -0
  77. package/dist/composites/node-agent.js +149 -0
  78. package/dist/composites/operator-stack.js +268 -0
  79. package/dist/composites/otel-collector-agent.js +142 -0
  80. package/dist/composites/otel-collector-gateway.js +216 -0
  81. package/dist/composites/otel-collector-node.js +176 -0
  82. package/dist/composites/otel-collector-rbac.js +115 -0
  83. package/dist/composites/otel-collector-shape.js +128 -0
  84. package/dist/composites/otel-collector.js +115 -0
  85. package/dist/composites/otel-operator-collector.js +123 -0
  86. package/dist/composites/ray-cluster.js +378 -0
  87. package/dist/composites/ray-job.js +136 -0
  88. package/dist/composites/ray-service.js +178 -0
  89. package/dist/composites/secure-ingress.js +100 -0
  90. package/dist/composites/security-context.js +1 -0
  91. package/dist/composites/sidecar-app.js +105 -0
  92. package/dist/composites/stateful-app.js +111 -0
  93. package/dist/composites/vllm-serving-runtime.js +114 -0
  94. package/dist/composites/web-app.js +157 -0
  95. package/dist/composites/worker-pool.js +167 -0
  96. package/dist/composites/workload-identity-sa.js +81 -0
  97. package/dist/composites/workload-identity-service-account.js +77 -0
  98. package/dist/config-schema.js +72 -0
  99. package/dist/config.js +59 -0
  100. package/dist/coverage.js +27 -0
  101. package/dist/crd/crd-sources.js +615 -0
  102. package/dist/crd/loader.js +181 -0
  103. package/dist/crd/oci-chart.js +144 -0
  104. package/dist/crd/parser.js +382 -0
  105. package/dist/crd/project-codegen.js +273 -0
  106. package/dist/crd/types.js +7 -0
  107. package/dist/deep-observe-hooks.js +306 -0
  108. package/dist/deep-observe.js +234 -0
  109. package/dist/default-labels.js +87 -0
  110. package/dist/describe-resources.js +699 -0
  111. package/dist/describe-stack-status.js +99 -0
  112. package/dist/detect.js +11 -0
  113. package/dist/effect-receipt-row.js +205 -0
  114. package/dist/export-resources.js +39 -0
  115. package/dist/generated/index.js +1092 -0
  116. package/dist/generated/runtime.js +4 -0
  117. package/dist/group-namespace.js +96 -0
  118. package/dist/import/embedded.js +197 -0
  119. package/dist/import/generator.js +288 -0
  120. package/dist/import/live-export.js +85 -0
  121. package/dist/import/parser.js +204 -0
  122. package/dist/import/testdata/embedded/fixtures.js +60 -0
  123. package/dist/include/entity.js +78 -0
  124. package/dist/include/resolve.js +170 -0
  125. package/dist/index.js +59 -0
  126. package/dist/integrity.json +3 -3
  127. package/dist/kube/apply.js +90 -0
  128. package/dist/kube/connect.js +46 -0
  129. package/dist/kube/delete.js +138 -0
  130. package/dist/kube/describe.js +169 -0
  131. package/dist/kube/events.js +132 -0
  132. package/dist/kube/flags.js +91 -0
  133. package/dist/kube/get.js +159 -0
  134. package/dist/kube/group.js +123 -0
  135. package/dist/kube/logs.js +82 -0
  136. package/dist/kube/project.js +94 -0
  137. package/dist/kube/render.js +227 -0
  138. package/dist/kube/source.js +122 -0
  139. package/dist/kube/target.js +63 -0
  140. package/dist/kube/testing.js +28 -0
  141. package/dist/kube/top.js +99 -0
  142. package/dist/kube/verdict.js +72 -0
  143. package/dist/kube/wait.js +129 -0
  144. package/dist/kustomize/render.js +73 -0
  145. package/dist/kustomize/rendered-entity.js +67 -0
  146. package/dist/kustomize/root.js +68 -0
  147. package/dist/lint/audit-catalog.js +61 -0
  148. package/dist/lint/audit-lineage.js +118 -0
  149. package/dist/lint/post-synth/argo-helpers.js +36 -0
  150. package/dist/lint/post-synth/argo002.js +31 -0
  151. package/dist/lint/post-synth/argo003.js +53 -0
  152. package/dist/lint/post-synth/argo005.js +54 -0
  153. package/dist/lint/post-synth/crd-schema-helpers.d.ts.map +1 -1
  154. package/dist/lint/post-synth/crd-schema-helpers.js +214 -0
  155. package/dist/lint/post-synth/flux002.js +56 -0
  156. package/dist/lint/post-synth/flux003.js +50 -0
  157. package/dist/lint/post-synth/index.js +104 -0
  158. package/dist/lint/post-synth/k8s-helpers.js +186 -0
  159. package/dist/lint/post-synth/monitoring-selection-helpers.js +163 -0
  160. package/dist/lint/post-synth/otel-placement-helpers.js +293 -0
  161. package/dist/lint/post-synth/sops-helpers.js +43 -0
  162. package/dist/lint/post-synth/wk8005.js +44 -0
  163. package/dist/lint/post-synth/wk8006.js +56 -0
  164. package/dist/lint/post-synth/wk8041.js +60 -0
  165. package/dist/lint/post-synth/wk8042.js +36 -0
  166. package/dist/lint/post-synth/wk8101.js +52 -0
  167. package/dist/lint/post-synth/wk8102.js +30 -0
  168. package/dist/lint/post-synth/wk8103.js +33 -0
  169. package/dist/lint/post-synth/wk8104.js +58 -0
  170. package/dist/lint/post-synth/wk8105.js +33 -0
  171. package/dist/lint/post-synth/wk8201.js +44 -0
  172. package/dist/lint/post-synth/wk8202.js +34 -0
  173. package/dist/lint/post-synth/wk8203.js +34 -0
  174. package/dist/lint/post-synth/wk8204.js +71 -0
  175. package/dist/lint/post-synth/wk8205.js +38 -0
  176. package/dist/lint/post-synth/wk8207.js +33 -0
  177. package/dist/lint/post-synth/wk8208.js +33 -0
  178. package/dist/lint/post-synth/wk8209.js +33 -0
  179. package/dist/lint/post-synth/wk8301.js +49 -0
  180. package/dist/lint/post-synth/wk8302.js +34 -0
  181. package/dist/lint/post-synth/wk8303.js +68 -0
  182. package/dist/lint/post-synth/wk8304.js +58 -0
  183. package/dist/lint/post-synth/wk8305.js +96 -0
  184. package/dist/lint/post-synth/wk8306.js +38 -0
  185. package/dist/lint/post-synth/wk8401.js +86 -0
  186. package/dist/lint/post-synth/wk8402.js +31 -0
  187. package/dist/lint/post-synth/wk8403.js +49 -0
  188. package/dist/lint/post-synth/wk8404.js +36 -0
  189. package/dist/lint/post-synth/wk8405.js +65 -0
  190. package/dist/lint/post-synth/wk8406.js +50 -0
  191. package/dist/lint/post-synth/wk8407.js +58 -0
  192. package/dist/lint/post-synth/wk8501.js +36 -0
  193. package/dist/lint/post-synth/wk8502.js +35 -0
  194. package/dist/lint/post-synth/wk8503.js +164 -0
  195. package/dist/lint/post-synth/wk8504.js +20 -0
  196. package/dist/lint/post-synth/wk8505.js +31 -0
  197. package/dist/lint/post-synth/wk8601.js +40 -0
  198. package/dist/lint/post-synth/wk8602.js +120 -0
  199. package/dist/lint/post-synth/wk8603.js +63 -0
  200. package/dist/lint/post-synth/wk8604.js +31 -0
  201. package/dist/lint/post-synth/wk8605.js +124 -0
  202. package/dist/lint/post-synth/wk8606.js +22 -0
  203. package/dist/lint/post-synth/wk8701.js +31 -0
  204. package/dist/lint/post-synth/wk8702.js +37 -0
  205. package/dist/lint/post-synth/wk8703.js +31 -0
  206. package/dist/lint/post-synth/wk8704.js +135 -0
  207. package/dist/lint/post-synth/wk8705.js +38 -0
  208. package/dist/lint/rules/argo-appset-single-project.js +56 -0
  209. package/dist/lint/rules/argo-ast.js +94 -0
  210. package/dist/lint/rules/argo-automated-prune.js +59 -0
  211. package/dist/lint/rules/flux-source-ref-pin.js +44 -0
  212. package/dist/lint/rules/hardcoded-namespace.js +57 -0
  213. package/dist/lint/rules/latest-image-tag.js +103 -0
  214. package/dist/lint/rules/missing-resource-limits.js +85 -0
  215. package/dist/lsp/completions.d.ts.map +1 -1
  216. package/dist/lsp/completions.js +18 -0
  217. package/dist/lsp/hover.d.ts.map +1 -1
  218. package/dist/lsp/hover.js +57 -0
  219. package/dist/manifest-entity.js +71 -0
  220. package/dist/manifest.json +1 -1
  221. package/dist/micro-time.js +49 -0
  222. package/dist/op/activities/apply-diff.d.ts +31 -0
  223. package/dist/op/activities/apply-diff.d.ts.map +1 -0
  224. package/dist/op/activities/apply-diff.js +79 -0
  225. package/dist/op/activities/argo.js +98 -0
  226. package/dist/op/activities/ensure-secret.js +38 -0
  227. package/dist/op/activities/index.js +51 -0
  228. package/dist/op/activities/kubectl.d.ts +64 -0
  229. package/dist/op/activities/kubectl.d.ts.map +1 -1
  230. package/dist/op/activities/kubectl.js +517 -0
  231. package/dist/op/activities/wait-for-ready.js +229 -0
  232. package/dist/op/builders.js +68 -0
  233. package/dist/package-cli.js +21 -0
  234. package/dist/patch/json-patch.js +187 -0
  235. package/dist/patch/rendered-patch.js +94 -0
  236. package/dist/plugin.js +741 -0
  237. package/dist/project-kinds.js +56 -0
  238. package/dist/receipt-store.js +216 -0
  239. package/dist/rules/crd-schema-helpers.ts +2 -1
  240. package/dist/secret-labels.js +33 -0
  241. package/dist/secret-store.js +116 -0
  242. package/dist/serializer.d.ts.map +1 -1
  243. package/dist/serializer.js +578 -0
  244. package/dist/sops/encrypted-secret-file.js +194 -0
  245. package/dist/sops/entity.js +32 -0
  246. package/dist/spec/fetch.js +58 -0
  247. package/dist/spec/parse.js +622 -0
  248. package/dist/subscribe-changes.js +168 -0
  249. package/dist/teardown.js +197 -0
  250. package/dist/telemetry-release.js +86 -0
  251. package/dist/validate-cli.js +17 -0
  252. package/dist/validate.js +47 -0
  253. package/dist/variables.js +34 -0
  254. package/dist/whoami.js +109 -0
  255. package/package.json +12 -12
  256. package/src/api/operation-surface.ts +2 -1
  257. package/src/codegen/package.ts +2 -2
  258. package/src/components/index.ts +3 -0
  259. package/src/components/kubectl-apply-plan.k3d.e2e.test.ts +127 -0
  260. package/src/components/kubectl-apply.test.ts +262 -0
  261. package/src/components/kubectl-apply.ts +341 -12
  262. package/src/deep-observe-hooks.ts +2 -2
  263. package/src/import/roundtrip-build.test.ts +46 -0
  264. package/src/lint/post-synth/crd-schema-helpers.ts +2 -1
  265. package/src/lsp/completions.ts +2 -1
  266. package/src/lsp/hover.ts +2 -1
  267. package/src/op/activities/apply-diff.ts +87 -0
  268. package/src/op/activities/kubectl-plan.test.ts +224 -0
  269. package/src/op/activities/kubectl.ts +204 -16
  270. package/src/serializer.ts +2 -1
@@ -0,0 +1,99 @@
1
+ import { LABEL_OWNERSHIP_KEYS, OWNERSHIP_MANAGED_BY_VALUE } from "@intentius/chant/ownership";
2
+ import { defaultK8sConnector } from "./api/connect.js";
3
+ /** The kinds a chant-applied stack's objects land as — the sweep's bound. */
4
+ const SWEEP_KINDS = [
5
+ { apiVersion: "apps/v1", kind: "Deployment" },
6
+ { apiVersion: "apps/v1", kind: "StatefulSet" },
7
+ { apiVersion: "apps/v1", kind: "DaemonSet" },
8
+ { apiVersion: "v1", kind: "Service" },
9
+ { apiVersion: "v1", kind: "ConfigMap" },
10
+ { apiVersion: "v1", kind: "Secret" },
11
+ { apiVersion: "v1", kind: "ServiceAccount" },
12
+ { apiVersion: "v1", kind: "Namespace" },
13
+ { apiVersion: "networking.k8s.io/v1", kind: "Ingress" },
14
+ { apiVersion: "policy/v1", kind: "PodDisruptionBudget" },
15
+ { apiVersion: "autoscaling/v2", kind: "HorizontalPodAutoscaler" },
16
+ { apiVersion: "batch/v1", kind: "CronJob" },
17
+ // The definitions themselves: a crds unit deploys these and nothing else.
18
+ { apiVersion: "apiextensions.k8s.io/v1", kind: "CustomResourceDefinition" },
19
+ ];
20
+ /**
21
+ * Every kind the cluster's CRDs declare, one served version each (storage
22
+ * preferred). Read unfiltered — see the module doc. Failure to read the CRD
23
+ * list narrows the sweep back to the fixed bound rather than failing it.
24
+ */
25
+ async function crdBackedKinds(client) {
26
+ let crds;
27
+ try {
28
+ crds = await client.list({ apiVersion: "apiextensions.k8s.io/v1", kind: "CustomResourceDefinition" });
29
+ }
30
+ catch {
31
+ return [];
32
+ }
33
+ const kinds = [];
34
+ for (const crd of crds) {
35
+ const spec = crd.spec;
36
+ const group = spec?.group;
37
+ const kind = spec?.names?.kind;
38
+ const versions = spec?.versions ?? [];
39
+ const version = versions.find((v) => v.storage && v.served) ?? versions.find((v) => v.served);
40
+ if (!group || !kind || !version)
41
+ continue;
42
+ kinds.push({ apiVersion: `${group}/${version.name}`, kind });
43
+ }
44
+ return kinds;
45
+ }
46
+ /** A workload is healthy when its controller reports every replica ready; a
47
+ * kind with no readiness story (a ConfigMap, a Service) asserts nothing. */
48
+ function objectHealthy(obj) {
49
+ const status = obj.status;
50
+ if (!status)
51
+ return undefined;
52
+ if (typeof status.replicas === "number") {
53
+ return (typeof status.readyReplicas === "number" ? status.readyReplicas : 0) >= status.replicas;
54
+ }
55
+ if (typeof status.desiredNumberScheduled === "number") {
56
+ return (typeof status.numberReady === "number" ? status.numberReady : 0) >= status.desiredNumberScheduled;
57
+ }
58
+ return undefined;
59
+ }
60
+ export async function describeStackStatus(options, connect = defaultK8sConnector) {
61
+ const selector = `${LABEL_OWNERSHIP_KEYS.managedBy}=${OWNERSHIP_MANAGED_BY_VALUE},` +
62
+ `${LABEL_OWNERSHIP_KEYS.stack}=${options.stack}`;
63
+ let client;
64
+ try {
65
+ ({ client } = await connect({ environment: options.environment }));
66
+ }
67
+ catch {
68
+ // No cluster binding / unreadable kubeconfig — indeterminate, never a
69
+ // confident "not there".
70
+ return null;
71
+ }
72
+ const matched = [];
73
+ let anyRead = false;
74
+ const targets = [...SWEEP_KINDS, ...(await crdBackedKinds(client))];
75
+ for (const target of targets) {
76
+ try {
77
+ const items = await client.list(target, { labelSelector: selector });
78
+ anyRead = true;
79
+ matched.push(...items);
80
+ }
81
+ catch {
82
+ // One kind's list failing (RBAC, an aggregated API down) proves nothing
83
+ // about the others; keep sweeping.
84
+ }
85
+ }
86
+ if (!anyRead)
87
+ return null; // every read failed — indeterminate
88
+ if (matched.length === 0)
89
+ return { stack: options.stack, present: false };
90
+ const verdicts = matched.map(objectHealthy).filter((v) => v !== undefined);
91
+ const healthy = verdicts.length > 0 ? verdicts.every(Boolean) : true;
92
+ const ready = verdicts.filter(Boolean).length;
93
+ return {
94
+ stack: options.stack,
95
+ present: true,
96
+ status: verdicts.length > 0 ? `${ready}/${verdicts.length} workloads ready` : `${matched.length} objects present`,
97
+ healthy,
98
+ };
99
+ }
package/dist/detect.js ADDED
@@ -0,0 +1,11 @@
1
+ /** Edge-safe template detection for k8s — extracted from plugin.ts so it imports
2
+ * without the lint rules (and the TypeScript compiler they pull in). See #426. */
3
+ export function detectTemplate(data) {
4
+ if (typeof data !== "object" || data === null)
5
+ return false;
6
+ const obj = data;
7
+ // K8s manifests have apiVersion + kind
8
+ if (typeof obj.apiVersion === "string" && typeof obj.kind === "string")
9
+ return true;
10
+ return false;
11
+ }
@@ -0,0 +1,205 @@
1
+ /**
2
+ * The k8s effect-receipt materialization row (#2074, epic #1703): an effect
3
+ * receipt stored as a core `ConfigMap`, named
4
+ * `chant-receipt.<stack>.<env>.<effect>`, holding the expectation under
5
+ * `data.expectation`.
6
+ *
7
+ * Core's `EffectReceipt` factory (#1831) declares a receipt under the `chant`
8
+ * pseudo-lexicon, which no serializer claims. This module is the k8s
9
+ * materialization, built to the same shape as the aws SSM row (#1835,
10
+ * `lexicons/aws/src/effect-receipt-row.ts`): the {@link EffectReceipt} factory
11
+ * here produces core's declaration under `lexicon: "k8s"`, so the build
12
+ * partitions it to the k8s serializer, #1832's write-exclusion seam withholds
13
+ * it from the apply-bound entity set, and the serializer renders it for
14
+ * visibility through `SerializeContext.receipts` (see ./serializer.ts).
15
+ *
16
+ * Where the aws row parks its rendered rows in the CloudFormation template's
17
+ * `Metadata` (deliberately outside `Resources`, the section an applier writes
18
+ * from), this row parks them in a YAML COMMENT at the end of the manifest
19
+ * stream. Kubernetes YAML has no metadata channel outside the documents
20
+ * themselves, and a document is exactly what an applier applies: `loadAll`
21
+ * yields no object for a comment, so the receipt block is structurally
22
+ * unreachable from `applyManifest` while still riding the one build output the
23
+ * observation leg is handed. See {@link EFFECT_RECEIPTS_COMMENT_MARKER}.
24
+ *
25
+ * ## Name and namespace
26
+ *
27
+ * Path identity (epic decision 4): the ConfigMap name derives from the SAME
28
+ * ownership-block fields that stamp markers, `ownership.stack` and an explicit
29
+ * `ownership.env`, plus the receipt's `effect`. The separator is `.`, which no
30
+ * segment may contain, so `stack=a-b env=c` and `stack=a env=b-c` cannot
31
+ * produce the same name. The result is a valid RFC 1123 DNS subdomain, which
32
+ * is what a ConfigMap name has to be.
33
+ *
34
+ * The namespace is the project's `k8s.receipts.namespace`, and `default` when
35
+ * the project sets none, which is where every other namespace-less k8s read
36
+ * and write in this lexicon already lands. It is never invented from the stack
37
+ * or the environment: a namespace chant guessed would be a namespace chant has
38
+ * to create, and the receipt row creates nothing but the receipt.
39
+ *
40
+ * ## Ownership, and why the prune leaves it alone
41
+ *
42
+ * Every receipt ConfigMap carries chant's ownership marker labels plus
43
+ * {@link RECEIPT_LABEL_KEY}. The marker is what makes `chant kube get` and the
44
+ * observation classify it as chant's rather than foreign. The receipt label is
45
+ * what keeps `delete: "owned-only"` from pruning it: the receipt is owned and
46
+ * is never in any apply set (the effect step is its sole writer), which is the
47
+ * exact shape the owned-only sweep deletes. `pruneOrphans`
48
+ * (./op/activities/kubectl.ts) reports it `retained` instead, the same
49
+ * treatment a generated-once Secret gets (./secret-labels.ts) and for the same
50
+ * reason: destroying it silently converts at-least-once into a re-run nobody
51
+ * asked for, or worse, into never.
52
+ *
53
+ * Plain store (#1833's COR023): a ConfigMap is the plain half of the
54
+ * ConfigMap/Secret pair, and the entityType alone is what the guard reads. A
55
+ * receipt value is a witness (an existence marker or a `sha256:` digest),
56
+ * never a secret; materializing one into a `K8s::Core::Secret` fails COR023.
57
+ */
58
+ import { DECLARABLE_MARKER } from "@intentius/chant/declarable";
59
+ import { EffectReceipt as CoreEffectReceipt, EFFECT_RECEIPT_MARKER, } from "@intentius/chant/effect-receipt";
60
+ /** The entityType of the k8s materialization row, the real resource kind the
61
+ * receipt is stored as, which is what lint's plain-store guard checks. */
62
+ export const K8S_EFFECT_RECEIPT_ENTITY_TYPE = "K8s::Core::ConfigMap";
63
+ /** apiVersion/kind of the materialized row, for the client reads and writes. */
64
+ export const RECEIPT_CONFIGMAP_REF = { apiVersion: "v1", kind: "ConfigMap" };
65
+ /**
66
+ * The line prefix the serializer renders receipt rows behind, and the
67
+ * observation leg reads them back from. A YAML comment: an applier's `loadAll`
68
+ * produces no document for it, so a receipt can never enter an apply set, and
69
+ * `kubectl apply -f` ignores it exactly as it ignores every other comment.
70
+ * The remainder of the line is one JSON object keyed by entity name.
71
+ */
72
+ export const EFFECT_RECEIPTS_COMMENT_MARKER = "# chant:effect-receipts ";
73
+ /** First segment of every receipt ConfigMap name. */
74
+ export const RECEIPT_NAME_PREFIX = "chant-receipt";
75
+ /** The `data` key the expectation is stored under. One key, the same single
76
+ * value the aws row puts in the SSM parameter's `Value`. */
77
+ export const RECEIPT_DATA_KEY = "expectation";
78
+ /** The label a live receipt ConfigMap carries, valued with the effect it
79
+ * witnesses. Recognition for the observation leg and, more importantly, the
80
+ * exclusion the owned-only prune keys on. */
81
+ export const RECEIPT_LABEL_KEY = "chant.intentius.io/effect-receipt";
82
+ /** Where receipts live when the project names no namespace. */
83
+ export const RECEIPT_DEFAULT_NAMESPACE = "default";
84
+ /**
85
+ * The rendered value of a hash-flavor receipt that still carries reference
86
+ * inputs at synthesis. References resolve at plan and at run, never at
87
+ * synthesis (epic decision 5), so the row carries this note instead of a
88
+ * digest hashed over placeholders.
89
+ */
90
+ export const RECEIPT_UNRESOLVED_VALUE_NOTE = "unresolved at synthesis, reference inputs; the expectation resolves at plan and at run (chant #1703, decision 5)";
91
+ /** One segment of the receipt name. RFC 1123 DNS label, which is what each
92
+ * dot-separated piece of a ConfigMap name has to be, and the `.` separator is
93
+ * excluded from it so the identity stays unambiguous. */
94
+ const NAME_SEGMENT = /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/;
95
+ /** Kubernetes' own ceiling on a DNS subdomain, which a ConfigMap name is. */
96
+ const MAX_NAME_LENGTH = 253;
97
+ function checkSegment(field, value) {
98
+ if (value.length > 63 || !NAME_SEGMENT.test(value)) {
99
+ throw new Error(`receipt name: ${field} "${value}" is not a valid DNS-1123 label, ` +
100
+ `use lowercase letters, digits and "-" (the segment becomes one dot-separated piece of ` +
101
+ `${RECEIPT_NAME_PREFIX}.<stack>.<env>.<effect>, which must be a valid ConfigMap name)`);
102
+ }
103
+ return value;
104
+ }
105
+ /**
106
+ * The ConfigMap name of one effect's receipt:
107
+ * `chant-receipt.<stack>.<env>.<effect>`, from the resolved ownership marker
108
+ * fields (epic decision 4). The single source of the name identity: the
109
+ * serializer's rendered row, the receipt store's reads and writes, and the
110
+ * observation leg all call this.
111
+ */
112
+ export function receiptConfigMapName(stack, env, effect) {
113
+ const name = `${RECEIPT_NAME_PREFIX}.${checkSegment("stack", stack)}.${checkSegment("env", env)}.${checkSegment("effect", effect)}`;
114
+ if (name.length > MAX_NAME_LENGTH) {
115
+ throw new Error(`receipt name: "${name}" is ${name.length} characters, over Kubernetes' ${MAX_NAME_LENGTH}-character ` +
116
+ `limit for a ConfigMap name. Shorten the stack, the environment, or the effect.`);
117
+ }
118
+ return name;
119
+ }
120
+ /** The project's receipt namespace: `k8s.receipts.namespace`, else `default`.
121
+ * The serializer reads it off `SerializeContext.config` and the store reads it
122
+ * off the project config, so both derive one namespace from one setting. */
123
+ export function receiptNamespaceFrom(config) {
124
+ const k8s = config?.k8s;
125
+ const declared = k8s?.receipts?.namespace;
126
+ return typeof declared === "string" && declared.length > 0 ? declared : RECEIPT_DEFAULT_NAMESPACE;
127
+ }
128
+ /** The full address of one effect's receipt ConfigMap. */
129
+ export function receiptConfigMapRef(stack, env, effect, namespace = RECEIPT_DEFAULT_NAMESPACE) {
130
+ return { name: receiptConfigMapName(stack, env, effect), namespace };
131
+ }
132
+ /** True when a live object's labels mark it a chant effect receipt. Any
133
+ * non-empty value counts: the label's presence is the claim, and a sweep must
134
+ * err on the side of keeping. */
135
+ export function isEffectReceiptObject(labels) {
136
+ const value = labels?.[RECEIPT_LABEL_KEY];
137
+ return typeof value === "string" && value.length > 0;
138
+ }
139
+ /**
140
+ * Render the receipt block the serializer appends to the manifest stream.
141
+ * Deterministic: one line, entity names sorted.
142
+ */
143
+ export function renderReceiptComment(rows) {
144
+ const sorted = {};
145
+ for (const name of Object.keys(rows).sort())
146
+ sorted[name] = rows[name];
147
+ return `${EFFECT_RECEIPTS_COMMENT_MARKER}${JSON.stringify(sorted)}`;
148
+ }
149
+ /**
150
+ * Read the receipt rows back out of a build output. Returns an empty record
151
+ * for an output that carries no block, which is every project that declares no
152
+ * receipt. Never throws: an unparseable block is no block, and the observation
153
+ * leg reports the receipts it could not address as absent rather than
154
+ * inventing one.
155
+ */
156
+ export function parseReceiptComment(buildOutput) {
157
+ const start = buildOutput.lastIndexOf(EFFECT_RECEIPTS_COMMENT_MARKER);
158
+ if (start < 0)
159
+ return {};
160
+ const end = buildOutput.indexOf("\n", start);
161
+ const line = buildOutput.slice(start + EFFECT_RECEIPTS_COMMENT_MARKER.length, end < 0 ? undefined : end);
162
+ try {
163
+ const parsed = JSON.parse(line);
164
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
165
+ return {};
166
+ return parsed;
167
+ }
168
+ catch {
169
+ return {};
170
+ }
171
+ }
172
+ /**
173
+ * Declare a k8s-materialized effect receipt. Same signature and semantics as
174
+ * core's `EffectReceipt` (#1831), whose factory validates and freezes the
175
+ * options, but the declaration lands in the k8s partition, so the k8s
176
+ * serializer renders the ConfigMap row and the receipt store
177
+ * (./receipt-store.ts) is its writer. Pass the returned const straight to the
178
+ * `effect(...)` op step.
179
+ */
180
+ export function EffectReceipt(name, options) {
181
+ // Fail at declaration, not at serialize: the effect is the name's final
182
+ // segment, and a name that cannot become a segment has no receipt address.
183
+ if (typeof options?.effect === "string" && options.effect.length > 0) {
184
+ checkSegment("effect", options.effect);
185
+ }
186
+ const core = CoreEffectReceipt(name, options);
187
+ const decl = {
188
+ [DECLARABLE_MARKER]: true,
189
+ [EFFECT_RECEIPT_MARKER]: true,
190
+ lexicon: "k8s",
191
+ entityType: K8S_EFFECT_RECEIPT_ENTITY_TYPE,
192
+ name: core.name,
193
+ effect: core.effect,
194
+ flavor: core.flavor,
195
+ // The same frozen structure the core factory built: intrinsic inputs stay
196
+ // live so discovery can stamp logical names onto attr-refs.
197
+ inputs: core.inputs,
198
+ };
199
+ // Declared fields immutable, object extensible for discovery's own
200
+ // symbol-keyed metadata, the same lock the core factory applies.
201
+ for (const key of Object.keys(decl)) {
202
+ Object.defineProperty(decl, key, { writable: false, configurable: false });
203
+ }
204
+ return decl;
205
+ }
@@ -0,0 +1,39 @@
1
+ import { defaultK8sConnector } from "./api/connect.js";
2
+ import { operationFor } from "./api/operation-surface.js";
3
+ import { buildExportFromObjects } from "./import/live-export.js";
4
+ import { DEFAULT_IMPORT_TYPES } from "./api/sweep-types.js";
5
+ /**
6
+ * Entity types a bare `chant import --from <cluster>` sweeps: the workload,
7
+ * config, networking and RBAC kinds people actually author. Everything else is
8
+ * reachable by naming it with `--selector type=...`. Defined in
9
+ * `./api/sweep-types.ts` and re-exported here, its original home — chant
10
+ * #1075's ownership-scoped prune needs the same list without pulling the
11
+ * import parser in behind the apply activity.
12
+ */
13
+ export { DEFAULT_IMPORT_TYPES };
14
+ export async function exportResources(options, connect = defaultK8sConnector) {
15
+ const types = options.selector?.type ? [options.selector.type] : DEFAULT_IMPORT_TYPES;
16
+ const operations = types.map((t) => operationFor(t)).filter((o) => o !== undefined);
17
+ if (operations.length === 0) {
18
+ return { resources: [], parameters: [] };
19
+ }
20
+ const { client } = await connect({ environment: options.environment, cwd: options.cwd });
21
+ // One List per kind keeps parsing simple and isolates a missing-kind error
22
+ // to that kind rather than failing the whole export. They now run
23
+ // concurrently instead of serially.
24
+ const perKind = await client.concurrently(operations, async (operation) => {
25
+ try {
26
+ return await client.list({ apiVersion: operation.apiVersion, kind: operation.kind });
27
+ }
28
+ catch {
29
+ // Kind not served by this cluster / RBAC denied — skip it, don't fail
30
+ // the whole export.
31
+ return [];
32
+ }
33
+ });
34
+ return buildExportFromObjects(perKind.flat(), {
35
+ verbatim: options.verbatim,
36
+ selector: options.selector,
37
+ owned: options.owned,
38
+ });
39
+ }