@intentius/chant-lexicon-k8s 0.0.13 → 0.0.15

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 (54) hide show
  1. package/dist/integrity.json +20 -15
  2. package/dist/manifest.json +1 -1
  3. package/dist/rules/wk8204.ts +33 -1
  4. package/dist/rules/wk8304.ts +70 -0
  5. package/dist/rules/wk8305.ts +115 -0
  6. package/dist/rules/wk8306.ts +50 -0
  7. package/dist/skills/chant-k8s-eks.md +156 -0
  8. package/dist/skills/chant-k8s-patterns.md +245 -0
  9. package/dist/skills/chant-k8s.md +36 -227
  10. package/package.json +27 -24
  11. package/src/codegen/docs.ts +5 -5
  12. package/src/composites/adot-collector.ts +245 -0
  13. package/src/composites/agic-ingress.ts +149 -0
  14. package/src/composites/alb-ingress.ts +152 -0
  15. package/src/composites/autoscaled-service.ts +51 -0
  16. package/src/composites/azure-disk-storage-class.ts +82 -0
  17. package/src/composites/azure-file-storage-class.ts +77 -0
  18. package/src/composites/azure-monitor-collector.ts +232 -0
  19. package/src/composites/batch-job.ts +221 -0
  20. package/src/composites/composites.test.ts +1584 -0
  21. package/src/composites/config-connector-context.ts +62 -0
  22. package/src/composites/configured-app.ts +224 -0
  23. package/src/composites/cron-workload.ts +6 -0
  24. package/src/composites/ebs-storage-class.ts +96 -0
  25. package/src/composites/efs-storage-class.ts +77 -0
  26. package/src/composites/external-dns-agent.ts +174 -0
  27. package/src/composites/filestore-storage-class.ts +79 -0
  28. package/src/composites/fluent-bit-agent.ts +220 -0
  29. package/src/composites/gce-pd-storage-class.ts +85 -0
  30. package/src/composites/gke-gateway.ts +143 -0
  31. package/src/composites/index.ts +47 -0
  32. package/src/composites/irsa-service-account.ts +114 -0
  33. package/src/composites/metrics-server.ts +224 -0
  34. package/src/composites/monitored-service.ts +221 -0
  35. package/src/composites/network-isolated-app.ts +202 -0
  36. package/src/composites/node-agent.ts +6 -0
  37. package/src/composites/secure-ingress.ts +149 -0
  38. package/src/composites/security-context.ts +10 -0
  39. package/src/composites/sidecar-app.ts +207 -0
  40. package/src/composites/stateful-app.ts +67 -15
  41. package/src/composites/web-app.ts +104 -35
  42. package/src/composites/worker-pool.ts +38 -4
  43. package/src/composites/workload-identity-sa.ts +118 -0
  44. package/src/composites/workload-identity-service-account.ts +116 -0
  45. package/src/index.ts +24 -2
  46. package/src/lint/post-synth/post-synth.test.ts +362 -1
  47. package/src/lint/post-synth/wk8204.ts +33 -1
  48. package/src/lint/post-synth/wk8304.ts +70 -0
  49. package/src/lint/post-synth/wk8305.ts +115 -0
  50. package/src/lint/post-synth/wk8306.ts +50 -0
  51. package/src/plugin.test.ts +2 -2
  52. package/src/plugin.ts +556 -242
  53. package/src/serializer.test.ts +120 -0
  54. package/src/serializer.ts +16 -4
@@ -143,6 +143,76 @@ describe("k8sSerializer", () => {
143
143
  expect(result).not.toContain("spec:");
144
144
  });
145
145
 
146
+ test("ClusterRole is specless type", () => {
147
+ const entities = new Map<string, any>();
148
+ entities.set(
149
+ "viewRole",
150
+ mockResource("K8s::Rbac::ClusterRole", {
151
+ metadata: { name: "view-role" },
152
+ rules: [{ apiGroups: [""], resources: ["pods"], verbs: ["get", "list"] }],
153
+ }),
154
+ );
155
+
156
+ const result = k8sSerializer.serialize(entities);
157
+ expect(result).toContain("kind: ClusterRole");
158
+ expect(result).toContain("name: view-role");
159
+ expect(result).not.toContain("spec:");
160
+ });
161
+
162
+ test("ClusterRoleBinding is specless type", () => {
163
+ const entities = new Map<string, any>();
164
+ entities.set(
165
+ "viewBinding",
166
+ mockResource("K8s::Rbac::ClusterRoleBinding", {
167
+ metadata: { name: "view-binding" },
168
+ roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "ClusterRole", name: "view-role" },
169
+ subjects: [{ kind: "ServiceAccount", name: "default", namespace: "default" }],
170
+ }),
171
+ );
172
+
173
+ const result = k8sSerializer.serialize(entities);
174
+ expect(result).toContain("kind: ClusterRoleBinding");
175
+ expect(result).not.toContain("spec:");
176
+ });
177
+
178
+ test("StorageClass is specless type", () => {
179
+ const entities = new Map<string, any>();
180
+ entities.set(
181
+ "gp3",
182
+ mockResource("K8s::Storage::StorageClass", {
183
+ metadata: { name: "gp3-encrypted" },
184
+ provisioner: "ebs.csi.aws.com",
185
+ parameters: { type: "gp3", encrypted: "true" },
186
+ reclaimPolicy: "Delete",
187
+ volumeBindingMode: "WaitForFirstConsumer",
188
+ }),
189
+ );
190
+
191
+ const result = k8sSerializer.serialize(entities);
192
+ expect(result).toContain("kind: StorageClass");
193
+ expect(result).toContain("provisioner: ebs.csi.aws.com");
194
+ expect(result).not.toContain("spec:");
195
+ });
196
+
197
+ test("APIService is specless type", () => {
198
+ const entities = new Map<string, any>();
199
+ entities.set(
200
+ "metricsApi",
201
+ mockResource("K8s::Admissionregistration::APIService", {
202
+ metadata: { name: "v1beta1.metrics.k8s.io" },
203
+ group: "metrics.k8s.io",
204
+ version: "v1beta1",
205
+ service: { name: "metrics-server", namespace: "kube-system" },
206
+ groupPriorityMinimum: 100,
207
+ versionPriority: 100,
208
+ }),
209
+ );
210
+
211
+ const result = k8sSerializer.serialize(entities);
212
+ expect(result).toContain("kind: APIService");
213
+ expect(result).not.toContain("spec:");
214
+ });
215
+
146
216
  test("multi-resource entities joined by ---", () => {
147
217
  const entities = new Map<string, any>();
148
218
  entities.set(
@@ -248,6 +318,56 @@ describe("k8sSerializer", () => {
248
318
  expect(result).toBe("");
249
319
  });
250
320
 
321
+ test("Namespaces appear before other resources regardless of insertion order", () => {
322
+ const entities = new Map<string, any>();
323
+ // Insert Deployment first, then Namespace — Namespace should still come first in output
324
+ entities.set(
325
+ "deploy",
326
+ mockResource("K8s::Apps::Deployment", {
327
+ metadata: { name: "app", namespace: "my-ns" },
328
+ spec: { replicas: 1 },
329
+ }),
330
+ );
331
+ entities.set(
332
+ "ns",
333
+ mockResource("K8s::Core::Namespace", {
334
+ metadata: { name: "my-ns" },
335
+ }),
336
+ );
337
+
338
+ const result = k8sSerializer.serialize(entities);
339
+ const docs = result.split("---");
340
+ // First document should be the Namespace
341
+ expect(docs[0]).toContain("kind: Namespace");
342
+ expect(docs[0]).toContain("name: my-ns");
343
+ // Second document should be the Deployment
344
+ expect(docs[1]).toContain("kind: Deployment");
345
+ });
346
+
347
+ test("ConfigMap multiline data emits as | block scalar", () => {
348
+ const entities = new Map<string, any>();
349
+ const multilineConfig = "[SERVICE]\n Flush 5\n Log_Level info\n\n[INPUT]\n Name tail\n";
350
+ entities.set(
351
+ "config",
352
+ mockResource("K8s::Core::ConfigMap", {
353
+ metadata: { name: "app-config" },
354
+ data: { "fluent-bit.conf": multilineConfig },
355
+ }),
356
+ );
357
+
358
+ const result = k8sSerializer.serialize(entities);
359
+ expect(result).toContain("kind: ConfigMap");
360
+ // Must use | block scalar, not flatten to single line
361
+ expect(result).toContain("fluent-bit.conf: |");
362
+ // Content lines should be preserved (indented under the key)
363
+ expect(result).toContain("[SERVICE]");
364
+ expect(result).toContain("Flush 5");
365
+ expect(result).toContain("[INPUT]");
366
+ expect(result).toContain("Name tail");
367
+ // Should NOT contain literal \n in the output
368
+ expect(result).not.toContain("\\n");
369
+ });
370
+
251
371
  test("key ordering: apiVersion, kind, metadata, spec, then rest", () => {
252
372
  const entities = new Map<string, any>();
253
373
  entities.set(
package/src/serializer.ts CHANGED
@@ -26,6 +26,13 @@ const SPECLESS_TYPES = new Set([
26
26
  "Secret",
27
27
  "Namespace",
28
28
  "ServiceAccount",
29
+ "ClusterRole",
30
+ "ClusterRoleBinding",
31
+ "Role",
32
+ "RoleBinding",
33
+ "StorageClass",
34
+ "PersistentVolume",
35
+ "APIService",
29
36
  ]);
30
37
 
31
38
  /**
@@ -157,7 +164,8 @@ export const k8sSerializer: Serializer = {
157
164
  }
158
165
  }
159
166
 
160
- const documents: string[] = [];
167
+ const namespaceDocs: string[] = [];
168
+ const otherDocs: string[] = [];
161
169
 
162
170
  for (const [name, entity] of entities) {
163
171
  if (isPropertyDeclarable(entity)) continue;
@@ -230,12 +238,16 @@ export const k8sSerializer: Serializer = {
230
238
  }
231
239
  }
232
240
 
233
- // Emit as YAML
241
+ // Emit as YAML — sort Namespaces first so kubectl apply succeeds
234
242
  const yamlDoc = emitK8sManifest(manifest);
235
- documents.push(yamlDoc);
243
+ if (gvk.kind === "Namespace") {
244
+ namespaceDocs.push(yamlDoc);
245
+ } else {
246
+ otherDocs.push(yamlDoc);
247
+ }
236
248
  }
237
249
 
238
- return documents.join("\n---\n");
250
+ return [...namespaceDocs, ...otherDocs].join("\n---\n");
239
251
  },
240
252
  };
241
253