@intentius/chant-lexicon-k8s 0.0.18 → 0.0.24
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/integrity.json +9 -4
- package/dist/manifest.json +1 -1
- package/dist/skills/chant-k8s-aks.md +146 -0
- package/{src/skills/kubernetes-patterns.md → dist/skills/chant-k8s-deployment-strategies.md} +1 -1
- package/dist/skills/chant-k8s-eks.md +156 -0
- package/dist/skills/chant-k8s-gke.md +246 -0
- package/{src/skills/kubernetes-security.md → dist/skills/chant-k8s-security.md} +1 -1
- package/dist/skills/chant-k8s.md +66 -3
- package/package.json +20 -2
- package/src/composites/adot-collector.ts +34 -22
- package/src/composites/agic-ingress.ts +14 -6
- package/src/composites/aks-external-dns-agent.ts +29 -18
- package/src/composites/alb-ingress.ts +14 -6
- package/src/composites/autoscaled-service.ts +25 -20
- package/src/composites/azure-disk-storage-class.ts +14 -6
- package/src/composites/azure-file-storage-class.ts +14 -6
- package/src/composites/azure-monitor-collector.ts +34 -22
- package/src/composites/batch-job.ts +25 -17
- package/src/composites/cockroachdb-cluster.ts +148 -58
- package/src/composites/composites.test.ts +369 -363
- package/src/composites/config-connector-context.ts +15 -8
- package/src/composites/configured-app.ts +21 -15
- package/src/composites/cron-workload.ts +25 -20
- package/src/composites/ebs-storage-class.ts +14 -6
- package/src/composites/efs-storage-class.ts +14 -6
- package/src/composites/external-dns-agent.ts +26 -20
- package/src/composites/filestore-storage-class.ts +14 -6
- package/src/composites/fluent-bit-agent.ts +30 -24
- package/src/composites/gce-ingress.ts +14 -6
- package/src/composites/gce-pd-storage-class.ts +14 -6
- package/src/composites/gke-external-dns-agent.ts +34 -21
- package/src/composites/gke-fluent-bit-agent.ts +34 -22
- package/src/composites/gke-gateway.ts +19 -12
- package/src/composites/gke-otel-collector.ts +34 -22
- package/src/composites/irsa-service-account.ts +22 -14
- package/src/composites/metrics-server.ts +41 -26
- package/src/composites/monitored-service.ts +26 -19
- package/src/composites/namespace-env.ts +26 -17
- package/src/composites/network-isolated-app.ts +21 -16
- package/src/composites/node-agent.ts +33 -22
- package/src/composites/secure-ingress.ts +19 -11
- package/src/composites/sidecar-app.ts +17 -12
- package/src/composites/stateful-app.ts +21 -12
- package/src/composites/web-app.ts +25 -21
- package/src/composites/worker-pool.ts +40 -26
- package/src/composites/workload-identity-sa.ts +22 -14
- package/src/composites/workload-identity-service-account.ts +22 -16
- package/src/plugin.ts +130 -614
- package/src/serializer.ts +3 -0
- package/src/skills/chant-k8s-deployment-strategies.md +183 -0
- package/src/skills/chant-k8s-gke.md +55 -0
- package/src/skills/chant-k8s-patterns.md +245 -0
- package/src/skills/chant-k8s-security.md +237 -0
- package/src/skills/chant-k8s.md +305 -0
package/src/plugin.ts
CHANGED
|
@@ -6,10 +6,13 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { createRequire } from "module";
|
|
9
|
-
import type { LexiconPlugin,
|
|
9
|
+
import type { LexiconPlugin, InitTemplateSet, ResourceMetadata } from "@intentius/chant/lexicon";
|
|
10
10
|
const require = createRequire(import.meta.url);
|
|
11
11
|
import type { LintRule } from "@intentius/chant/lint/rule";
|
|
12
|
-
import
|
|
12
|
+
import { discoverPostSynthChecks } from "@intentius/chant/lint/discover";
|
|
13
|
+
import { createSkillsLoader, createDiffTool, createCatalogResource } from "@intentius/chant/lexicon-plugin-helpers";
|
|
14
|
+
import { join, dirname } from "path";
|
|
15
|
+
import { fileURLToPath } from "url";
|
|
13
16
|
import { k8sSerializer } from "./serializer";
|
|
14
17
|
|
|
15
18
|
export const k8sPlugin: LexiconPlugin = {
|
|
@@ -23,36 +26,9 @@ export const k8sPlugin: LexiconPlugin = {
|
|
|
23
26
|
return [hardcodedNamespaceRule, latestImageTagRule, missingResourceLimitsRule];
|
|
24
27
|
},
|
|
25
28
|
|
|
26
|
-
postSynthChecks()
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
const { wk8041 } = require("./lint/post-synth/wk8041");
|
|
30
|
-
const { wk8042 } = require("./lint/post-synth/wk8042");
|
|
31
|
-
const { wk8101 } = require("./lint/post-synth/wk8101");
|
|
32
|
-
const { wk8102 } = require("./lint/post-synth/wk8102");
|
|
33
|
-
const { wk8103 } = require("./lint/post-synth/wk8103");
|
|
34
|
-
const { wk8104 } = require("./lint/post-synth/wk8104");
|
|
35
|
-
const { wk8105 } = require("./lint/post-synth/wk8105");
|
|
36
|
-
const { wk8201 } = require("./lint/post-synth/wk8201");
|
|
37
|
-
const { wk8202 } = require("./lint/post-synth/wk8202");
|
|
38
|
-
const { wk8203 } = require("./lint/post-synth/wk8203");
|
|
39
|
-
const { wk8204 } = require("./lint/post-synth/wk8204");
|
|
40
|
-
const { wk8205 } = require("./lint/post-synth/wk8205");
|
|
41
|
-
const { wk8207 } = require("./lint/post-synth/wk8207");
|
|
42
|
-
const { wk8208 } = require("./lint/post-synth/wk8208");
|
|
43
|
-
const { wk8209 } = require("./lint/post-synth/wk8209");
|
|
44
|
-
const { wk8301 } = require("./lint/post-synth/wk8301");
|
|
45
|
-
const { wk8302 } = require("./lint/post-synth/wk8302");
|
|
46
|
-
const { wk8303 } = require("./lint/post-synth/wk8303");
|
|
47
|
-
const { wk8304 } = require("./lint/post-synth/wk8304");
|
|
48
|
-
const { wk8305 } = require("./lint/post-synth/wk8305");
|
|
49
|
-
const { wk8306 } = require("./lint/post-synth/wk8306");
|
|
50
|
-
return [
|
|
51
|
-
wk8005, wk8006, wk8041, wk8042,
|
|
52
|
-
wk8101, wk8102, wk8103, wk8104, wk8105,
|
|
53
|
-
wk8201, wk8202, wk8203, wk8204, wk8205, wk8207, wk8208, wk8209,
|
|
54
|
-
wk8301, wk8302, wk8303, wk8304, wk8305, wk8306,
|
|
55
|
-
];
|
|
29
|
+
postSynthChecks() {
|
|
30
|
+
const postSynthDir = join(dirname(fileURLToPath(import.meta.url)), "lint", "post-synth");
|
|
31
|
+
return discoverPostSynthChecks(postSynthDir, import.meta.url);
|
|
56
32
|
},
|
|
57
33
|
|
|
58
34
|
// K8s YAML has no template interpolation functions like CloudFormation's
|
|
@@ -299,49 +275,103 @@ export const service = new Service({
|
|
|
299
275
|
console.error(`Packaged ${stats.resources} resources, ${stats.ruleCount} rules, ${stats.skillCount} skills`);
|
|
300
276
|
},
|
|
301
277
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
278
|
+
async describeResources(options: {
|
|
279
|
+
environment: string;
|
|
280
|
+
buildOutput: string;
|
|
281
|
+
entityNames: string[];
|
|
282
|
+
}): Promise<Record<string, ResourceMetadata>> {
|
|
283
|
+
const { getRuntime } = await import("@intentius/chant/runtime-adapter");
|
|
284
|
+
const rt = getRuntime();
|
|
285
|
+
const resources: Record<string, ResourceMetadata> = {};
|
|
286
|
+
|
|
287
|
+
// Resolve namespace from environment (convention: env name = namespace)
|
|
288
|
+
const namespace = options.environment;
|
|
289
|
+
|
|
290
|
+
// Parse build output to extract kind/name pairs for each entity
|
|
291
|
+
let manifests: Array<{ kind: string; metadata: { name: string; namespace?: string }; apiVersion: string }> = [];
|
|
292
|
+
try {
|
|
293
|
+
// K8s build output is YAML with --- separators
|
|
294
|
+
const docs = options.buildOutput.split(/^---$/m).filter((d) => d.trim());
|
|
295
|
+
for (const doc of docs) {
|
|
296
|
+
// Simple YAML parsing — look for kind: and metadata.name:
|
|
297
|
+
const kindMatch = doc.match(/^kind:\s*(.+)$/m);
|
|
298
|
+
const nameMatch = doc.match(/^\s+name:\s*(.+)$/m);
|
|
299
|
+
const apiVersionMatch = doc.match(/^apiVersion:\s*(.+)$/m);
|
|
300
|
+
if (kindMatch && nameMatch && apiVersionMatch) {
|
|
301
|
+
manifests.push({
|
|
302
|
+
kind: kindMatch[1].trim(),
|
|
303
|
+
metadata: { name: nameMatch[1].trim() },
|
|
304
|
+
apiVersion: apiVersionMatch[1].trim(),
|
|
321
305
|
});
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
} catch {
|
|
309
|
+
// If build output parsing fails, skip
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Query each resource
|
|
313
|
+
for (const entityName of options.entityNames) {
|
|
314
|
+
// Find the corresponding manifest
|
|
315
|
+
const manifest = manifests.find((m) => {
|
|
316
|
+
// Try matching by entity name convention
|
|
317
|
+
return m.metadata.name === entityName ||
|
|
318
|
+
entityName.toLowerCase().includes(m.metadata.name.toLowerCase());
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
if (!manifest) continue;
|
|
322
|
+
|
|
323
|
+
// Build kubectl resource type from apiVersion + kind
|
|
324
|
+
const resourceType = manifest.kind.toLowerCase();
|
|
325
|
+
const getResult = await rt.spawn([
|
|
326
|
+
"kubectl", "get", resourceType, manifest.metadata.name,
|
|
327
|
+
"-n", namespace,
|
|
328
|
+
"-o", "json",
|
|
329
|
+
]);
|
|
330
|
+
|
|
331
|
+
if (getResult.exitCode !== 0) continue;
|
|
332
|
+
|
|
333
|
+
try {
|
|
334
|
+
const obj = JSON.parse(getResult.stdout) as {
|
|
335
|
+
metadata: { name: string; uid: string; creationTimestamp: string };
|
|
336
|
+
status?: { phase?: string; conditions?: Array<{ type: string; status: string }> };
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
// Derive status from conditions or phase
|
|
340
|
+
let status = "Unknown";
|
|
341
|
+
if (obj.status?.phase) {
|
|
342
|
+
status = obj.status.phase;
|
|
343
|
+
} else if (obj.status?.conditions) {
|
|
344
|
+
const ready = obj.status.conditions.find((c) => c.type === "Ready" || c.type === "Available");
|
|
345
|
+
status = ready?.status === "True" ? "Ready" : "NotReady";
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Build attributes, scrubbing sensitive data
|
|
349
|
+
const attributes: Record<string, unknown> = {
|
|
350
|
+
uid: obj.metadata.uid,
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
resources[entityName] = {
|
|
354
|
+
type: `${manifest.apiVersion}/${manifest.kind}`,
|
|
355
|
+
physicalId: obj.metadata.name,
|
|
356
|
+
status,
|
|
357
|
+
lastUpdated: obj.metadata.creationTimestamp,
|
|
358
|
+
attributes,
|
|
359
|
+
};
|
|
360
|
+
} catch {
|
|
361
|
+
// Skip parse failures
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return resources;
|
|
366
|
+
},
|
|
367
|
+
|
|
368
|
+
mcpTools() {
|
|
369
|
+
return [createDiffTool(k8sSerializer, "Compare current build output against previous output for Kubernetes manifests")];
|
|
326
370
|
},
|
|
327
371
|
|
|
328
372
|
mcpResources() {
|
|
329
373
|
return [
|
|
330
|
-
|
|
331
|
-
uri: "resource-catalog",
|
|
332
|
-
name: "Kubernetes Resource Catalog",
|
|
333
|
-
description: "JSON list of all supported Kubernetes resource types",
|
|
334
|
-
mimeType: "application/json",
|
|
335
|
-
async handler(): Promise<string> {
|
|
336
|
-
const lexicon = require("./generated/lexicon-k8s.json") as Record<string, { resourceType: string; kind: string }>;
|
|
337
|
-
const entries = Object.entries(lexicon).map(([className, entry]) => ({
|
|
338
|
-
className,
|
|
339
|
-
resourceType: entry.resourceType,
|
|
340
|
-
kind: entry.kind,
|
|
341
|
-
}));
|
|
342
|
-
return JSON.stringify(entries);
|
|
343
|
-
},
|
|
344
|
-
},
|
|
374
|
+
createCatalogResource(import.meta.url, "Kubernetes Resource Catalog", "JSON list of all supported Kubernetes resource types", "lexicon-k8s.json"),
|
|
345
375
|
{
|
|
346
376
|
uri: "examples/basic-deployment",
|
|
347
377
|
name: "Basic Deployment Example",
|
|
@@ -385,254 +415,11 @@ export const service = new Service({
|
|
|
385
415
|
];
|
|
386
416
|
},
|
|
387
417
|
|
|
388
|
-
skills
|
|
389
|
-
return [
|
|
418
|
+
skills: createSkillsLoader(import.meta.url, [
|
|
390
419
|
{
|
|
420
|
+
file: "chant-k8s.md",
|
|
391
421
|
name: "chant-k8s",
|
|
392
422
|
description: "Kubernetes manifest lifecycle — scaffold, generate, lint, build, apply, troubleshoot, rollback",
|
|
393
|
-
content: `---
|
|
394
|
-
skill: chant-k8s
|
|
395
|
-
description: Build, validate, and deploy Kubernetes manifests from a chant project
|
|
396
|
-
user-invocable: true
|
|
397
|
-
---
|
|
398
|
-
|
|
399
|
-
# Kubernetes Operational Playbook
|
|
400
|
-
|
|
401
|
-
## How chant and Kubernetes relate
|
|
402
|
-
|
|
403
|
-
chant is a **synthesis-only** tool — it compiles TypeScript source files into Kubernetes YAML manifests. chant does NOT call the Kubernetes API. Your job as an agent is to bridge the two:
|
|
404
|
-
|
|
405
|
-
- Use **chant** for: build, lint, diff (local YAML comparison)
|
|
406
|
-
- Use **kubectl / k8s API** for: apply, rollback, monitoring, troubleshooting
|
|
407
|
-
|
|
408
|
-
The source of truth for infrastructure is the TypeScript in \`src/\`. The generated YAML manifests are intermediate artifacts — never edit them by hand.
|
|
409
|
-
|
|
410
|
-
## Scaffolding a new project
|
|
411
|
-
|
|
412
|
-
### Initialize with a template
|
|
413
|
-
|
|
414
|
-
\`\`\`bash
|
|
415
|
-
chant init --lexicon k8s # default: Deployment + Service
|
|
416
|
-
chant init --lexicon k8s --template microservice # Deployment + Service + HPA + PDB
|
|
417
|
-
chant init --lexicon k8s --template stateful # StatefulSet + PVC + Service
|
|
418
|
-
\`\`\`
|
|
419
|
-
|
|
420
|
-
### Available templates
|
|
421
|
-
|
|
422
|
-
| Template | What it generates | Best for |
|
|
423
|
-
|----------|-------------------|----------|
|
|
424
|
-
| *(default)* | Deployment + Service | Simple stateless apps |
|
|
425
|
-
| \`microservice\` | Deployment + Service + HPA + PDB | Production microservices |
|
|
426
|
-
| \`stateful\` | StatefulSet + PVC + headless Service | Databases, caches |
|
|
427
|
-
|
|
428
|
-
## Build and validate
|
|
429
|
-
|
|
430
|
-
### Build manifests
|
|
431
|
-
|
|
432
|
-
\`\`\`bash
|
|
433
|
-
chant build src/ --output manifests.yaml
|
|
434
|
-
\`\`\`
|
|
435
|
-
|
|
436
|
-
Options:
|
|
437
|
-
- \`--format yaml\` — emit YAML (default for K8s)
|
|
438
|
-
- \`--watch\` — rebuild on source changes
|
|
439
|
-
- \`--output <path>\` — write to a specific file
|
|
440
|
-
|
|
441
|
-
### Lint the source
|
|
442
|
-
|
|
443
|
-
\`\`\`bash
|
|
444
|
-
chant lint src/
|
|
445
|
-
\`\`\`
|
|
446
|
-
|
|
447
|
-
### What each step catches
|
|
448
|
-
|
|
449
|
-
| Step | Catches | When to run |
|
|
450
|
-
|------|---------|-------------|
|
|
451
|
-
| \`chant lint\` | Hardcoded namespaces (WK8001) | Every edit |
|
|
452
|
-
| \`chant build\` | Post-synth: secrets in env (WK8005), latest tags (WK8006), API keys (WK8041), missing probes (WK8301), no resource limits (WK8201), privileged containers (WK8202), and more | Before apply |
|
|
453
|
-
| \`kubectl --dry-run=server\` | K8s API validation: schema errors, admission webhooks | Before production apply |
|
|
454
|
-
|
|
455
|
-
## Deploying to Kubernetes
|
|
456
|
-
|
|
457
|
-
### Apply manifests
|
|
458
|
-
|
|
459
|
-
\`\`\`bash
|
|
460
|
-
# Build
|
|
461
|
-
chant build src/ --output manifests.yaml
|
|
462
|
-
|
|
463
|
-
# Dry run first
|
|
464
|
-
kubectl apply -f manifests.yaml --dry-run=server
|
|
465
|
-
|
|
466
|
-
# Apply
|
|
467
|
-
kubectl apply -f manifests.yaml
|
|
468
|
-
\`\`\`
|
|
469
|
-
|
|
470
|
-
### Check rollout status
|
|
471
|
-
|
|
472
|
-
\`\`\`bash
|
|
473
|
-
kubectl rollout status deployment/my-app
|
|
474
|
-
\`\`\`
|
|
475
|
-
|
|
476
|
-
### Rollback
|
|
477
|
-
|
|
478
|
-
\`\`\`bash
|
|
479
|
-
kubectl rollout undo deployment/my-app
|
|
480
|
-
kubectl rollout undo deployment/my-app --to-revision=2
|
|
481
|
-
\`\`\`
|
|
482
|
-
|
|
483
|
-
## Debugging strategies
|
|
484
|
-
|
|
485
|
-
### Check pod status and events
|
|
486
|
-
|
|
487
|
-
\`\`\`bash
|
|
488
|
-
# Overview
|
|
489
|
-
kubectl get pods -l app.kubernetes.io/name=my-app
|
|
490
|
-
kubectl get events --sort-by=.lastTimestamp -n <namespace>
|
|
491
|
-
|
|
492
|
-
# Deep dive into a specific pod
|
|
493
|
-
kubectl describe pod <pod-name>
|
|
494
|
-
|
|
495
|
-
# Logs (current and previous crash)
|
|
496
|
-
kubectl logs <pod-name>
|
|
497
|
-
kubectl logs <pod-name> --previous
|
|
498
|
-
kubectl logs <pod-name> -c <container-name> # specific container
|
|
499
|
-
kubectl logs deployment/my-app --all-containers
|
|
500
|
-
|
|
501
|
-
# Debug containers (K8s 1.25+)
|
|
502
|
-
kubectl debug <pod-name> -it --image=busybox --target=<container>
|
|
503
|
-
|
|
504
|
-
# Port-forwarding for local testing
|
|
505
|
-
kubectl port-forward svc/my-app 8080:80
|
|
506
|
-
kubectl port-forward pod/<pod-name> 8080:8080
|
|
507
|
-
\`\`\`
|
|
508
|
-
|
|
509
|
-
### Common error patterns
|
|
510
|
-
|
|
511
|
-
| Status | Meaning | Diagnostic command | Typical fix |
|
|
512
|
-
|--------|---------|-------------------|-------------|
|
|
513
|
-
| Pending | Not scheduled | \`kubectl describe pod\` → Events | Check resource requests, node selectors, taints, PVC binding |
|
|
514
|
-
| CrashLoopBackOff | App crashing on start | \`kubectl logs --previous\` | Fix app startup, check probe config, increase initialDelaySeconds |
|
|
515
|
-
| ImagePullBackOff | Image not found | \`kubectl describe pod\` → Events | Verify image name/tag, check imagePullSecrets, registry auth |
|
|
516
|
-
| OOMKilled | Out of memory | \`kubectl describe pod\` → Last State | Increase memory limit, profile app memory usage |
|
|
517
|
-
| Evicted | Node disk/memory pressure | \`kubectl describe node\` | Increase limits, add node capacity, check for log/tmp bloat |
|
|
518
|
-
| CreateContainerError | Container config issue | \`kubectl describe pod\` → Events | Check volume mounts, configmap/secret refs, security context |
|
|
519
|
-
| Init:CrashLoopBackOff | Init container failing | \`kubectl logs -c <init-container>\` | Fix init container command, check dependencies |
|
|
520
|
-
|
|
521
|
-
## Production safety
|
|
522
|
-
|
|
523
|
-
### Pre-apply validation
|
|
524
|
-
|
|
525
|
-
\`\`\`bash
|
|
526
|
-
# Always diff before applying
|
|
527
|
-
kubectl diff -f manifests.yaml
|
|
528
|
-
|
|
529
|
-
# Server-side dry run (validates with admission webhooks)
|
|
530
|
-
kubectl apply -f manifests.yaml --dry-run=server
|
|
531
|
-
|
|
532
|
-
# Client-side dry run (fast, but no webhook validation)
|
|
533
|
-
kubectl apply -f manifests.yaml --dry-run=client
|
|
534
|
-
\`\`\`
|
|
535
|
-
|
|
536
|
-
### Deployment strategies
|
|
537
|
-
|
|
538
|
-
- **RollingUpdate** (default): Gradually replaces pods. Set \`maxSurge\` and \`maxUnavailable\`.
|
|
539
|
-
- **Recreate**: All pods terminated before new ones created. Use for stateful apps that cannot run multiple versions.
|
|
540
|
-
- **Canary**: Deploy a second Deployment with 1 replica + same selector labels. Route percentage via Ingress annotations or service mesh.
|
|
541
|
-
- **Blue/Green**: Two full Deployments (blue/green), switch Service selector between them.
|
|
542
|
-
|
|
543
|
-
## Choosing the Right Composite
|
|
544
|
-
|
|
545
|
-
Composites are higher-level functions that produce multiple coordinated K8s resources from a single call. They return plain prop objects — not class instances.
|
|
546
|
-
|
|
547
|
-
### Decision Tree
|
|
548
|
-
|
|
549
|
-
| Need | Composite | Resources |
|
|
550
|
-
|------|-----------|-----------|
|
|
551
|
-
| Stateless web app | **WebApp** | Deployment + Service + optional Ingress + optional PDB |
|
|
552
|
-
| Stateful database/cache | **StatefulApp** | StatefulSet + headless Service + PVC + optional PDB |
|
|
553
|
-
| Production HTTP service with autoscaling | **AutoscaledService** | Deployment + Service + HPA + PDB |
|
|
554
|
-
| Background queue workers | **WorkerPool** | Deployment + RBAC + optional ConfigMap + optional HPA + optional PDB |
|
|
555
|
-
| Scheduled jobs | **CronWorkload** | CronJob + RBAC |
|
|
556
|
-
| One-shot batch jobs | **BatchJob** | Job + optional RBAC |
|
|
557
|
-
| App with ConfigMap/Secret mounts | **ConfiguredApp** | Deployment + Service + optional ConfigMap |
|
|
558
|
-
| Multi-container sidecar patterns | **SidecarApp** | Deployment + Service |
|
|
559
|
-
| App with Prometheus monitoring | **MonitoredService** | Deployment + Service + ServiceMonitor + optional PrometheusRule |
|
|
560
|
-
| App with fine-grained network policies | **NetworkIsolatedApp** | Deployment + Service + NetworkPolicy |
|
|
561
|
-
| Namespace with quotas and isolation | **NamespaceEnv** | Namespace + ResourceQuota + LimitRange + NetworkPolicy |
|
|
562
|
-
| Per-node agent (custom) | **NodeAgent** | DaemonSet + RBAC + optional ConfigMap |
|
|
563
|
-
| Multi-host TLS Ingress (cert-manager) | **SecureIngress** | Ingress + optional Certificate |
|
|
564
|
-
| EKS IRSA ServiceAccount | **IrsaServiceAccount** | ServiceAccount + optional RBAC |
|
|
565
|
-
| AWS ALB Ingress | **AlbIngress** | Ingress with ALB annotations |
|
|
566
|
-
| EBS StorageClass | **EbsStorageClass** | StorageClass (ebs.csi.aws.com) |
|
|
567
|
-
| EFS StorageClass | **EfsStorageClass** | StorageClass (efs.csi.aws.com) |
|
|
568
|
-
| Fluent Bit for CloudWatch | **FluentBitAgent** | DaemonSet + RBAC + ConfigMap |
|
|
569
|
-
| ExternalDNS for Route53 | **ExternalDnsAgent** | Deployment + IRSA SA + ClusterRole |
|
|
570
|
-
| ADOT for CloudWatch/X-Ray | **AdotCollector** | DaemonSet + RBAC + ConfigMap |
|
|
571
|
-
|
|
572
|
-
### Hardening options (available on Deployment-based composites)
|
|
573
|
-
|
|
574
|
-
- \`minAvailable\` — creates a PodDisruptionBudget (WebApp, StatefulApp, WorkerPool; AutoscaledService always has one)
|
|
575
|
-
- \`initContainers\` — run before main containers (WebApp, StatefulApp, AutoscaledService, ConfiguredApp, SidecarApp)
|
|
576
|
-
- \`securityContext\` — container security settings (WebApp, StatefulApp, AutoscaledService, WorkerPool)
|
|
577
|
-
- \`terminationGracePeriodSeconds\` — graceful shutdown (WebApp, StatefulApp, AutoscaledService, WorkerPool)
|
|
578
|
-
- \`priorityClassName\` — pod scheduling priority (WebApp, StatefulApp, AutoscaledService, WorkerPool)
|
|
579
|
-
|
|
580
|
-
### Common patterns across all composites
|
|
581
|
-
|
|
582
|
-
- All resources carry \`app.kubernetes.io/name\`, \`app.kubernetes.io/managed-by: chant\`, and \`app.kubernetes.io/component\` labels
|
|
583
|
-
- Pass \`labels: { team: "platform" }\` to add extra labels to all resources
|
|
584
|
-
- Pass \`namespace: "prod"\` to set namespace on all namespaced resources
|
|
585
|
-
- Pass \`env: [{ name: "KEY", value: "val" }]\` for container environment variables
|
|
586
|
-
|
|
587
|
-
## Troubleshooting reference table
|
|
588
|
-
|
|
589
|
-
| Symptom | Likely cause | Resolution |
|
|
590
|
-
|---------|-------------|------------|
|
|
591
|
-
| Pod stuck in Pending | Insufficient CPU/memory on nodes | Scale up cluster or reduce resource requests |
|
|
592
|
-
| Pod stuck in Pending | PVC not bound | Check StorageClass exists, PV available |
|
|
593
|
-
| Pod stuck in Pending | Node selector/affinity mismatch | Verify node labels match selectors |
|
|
594
|
-
| Pod stuck in ContainerCreating | ConfigMap/Secret not found | Ensure referenced ConfigMaps/Secrets exist |
|
|
595
|
-
| Service returns 503 | No ready endpoints | Check pod readiness probes, selector match |
|
|
596
|
-
| Ingress returns 404 | Backend service not found | Check Ingress rules, service name/port |
|
|
597
|
-
| HPA not scaling | Metrics server not installed | Install metrics-server |
|
|
598
|
-
| HPA not scaling | Resource requests not set | Add CPU/memory requests to containers |
|
|
599
|
-
| CronJob not running | Invalid cron expression | Validate cron syntax (5-field format) |
|
|
600
|
-
| NetworkPolicy blocking | Default deny applied | Add explicit allow rules for required traffic |
|
|
601
|
-
| RBAC permission denied | Missing Role/RoleBinding | Check ServiceAccount bindings and verb permissions |
|
|
602
|
-
|
|
603
|
-
## Quick reference
|
|
604
|
-
|
|
605
|
-
\`\`\`bash
|
|
606
|
-
# Build
|
|
607
|
-
chant build src/ --output manifests.yaml
|
|
608
|
-
|
|
609
|
-
# Lint
|
|
610
|
-
chant lint src/
|
|
611
|
-
|
|
612
|
-
# Validate
|
|
613
|
-
kubectl apply -f manifests.yaml --dry-run=server
|
|
614
|
-
|
|
615
|
-
# Diff
|
|
616
|
-
kubectl diff -f manifests.yaml
|
|
617
|
-
|
|
618
|
-
# Apply
|
|
619
|
-
kubectl apply -f manifests.yaml
|
|
620
|
-
|
|
621
|
-
# Status
|
|
622
|
-
kubectl get pods,svc,deploy
|
|
623
|
-
|
|
624
|
-
# Logs
|
|
625
|
-
kubectl logs deployment/my-app
|
|
626
|
-
|
|
627
|
-
# Rollback
|
|
628
|
-
kubectl rollout undo deployment/my-app
|
|
629
|
-
|
|
630
|
-
# Debug
|
|
631
|
-
kubectl describe pod <name>
|
|
632
|
-
kubectl logs <name> --previous
|
|
633
|
-
kubectl get events --sort-by=.lastTimestamp
|
|
634
|
-
\`\`\`
|
|
635
|
-
`,
|
|
636
423
|
triggers: [
|
|
637
424
|
{ type: "file-pattern", value: "**/*.k8s.ts" },
|
|
638
425
|
{ type: "file-pattern", value: "**/k8s/**/*.ts" },
|
|
@@ -722,254 +509,9 @@ const { job, serviceAccount, role, roleBinding } = BatchJob({
|
|
|
722
509
|
],
|
|
723
510
|
},
|
|
724
511
|
{
|
|
512
|
+
file: "chant-k8s-patterns.md",
|
|
725
513
|
name: "chant-k8s-patterns",
|
|
726
514
|
description: "Advanced K8s patterns — sidecars, observability, TLS, network isolation, config/secret mounting",
|
|
727
|
-
content: `---
|
|
728
|
-
skill: chant-k8s-patterns
|
|
729
|
-
description: Advanced Kubernetes deployment patterns and composites
|
|
730
|
-
user-invocable: true
|
|
731
|
-
---
|
|
732
|
-
|
|
733
|
-
# Advanced Kubernetes Patterns
|
|
734
|
-
|
|
735
|
-
## Sidecar Patterns
|
|
736
|
-
|
|
737
|
-
### SidecarApp — multi-container Deployment
|
|
738
|
-
|
|
739
|
-
\`\`\`typescript
|
|
740
|
-
import { SidecarApp } from "@intentius/chant-lexicon-k8s";
|
|
741
|
-
|
|
742
|
-
// Envoy sidecar proxy
|
|
743
|
-
const { deployment, service } = SidecarApp({
|
|
744
|
-
name: "api",
|
|
745
|
-
image: "api:1.0",
|
|
746
|
-
port: 8080,
|
|
747
|
-
sidecars: [
|
|
748
|
-
{
|
|
749
|
-
name: "envoy",
|
|
750
|
-
image: "envoyproxy/envoy:v1.28",
|
|
751
|
-
ports: [{ containerPort: 9901, name: "admin" }],
|
|
752
|
-
resources: { requests: { cpu: "100m", memory: "128Mi" }, limits: { cpu: "200m", memory: "256Mi" } },
|
|
753
|
-
},
|
|
754
|
-
],
|
|
755
|
-
initContainers: [
|
|
756
|
-
{ name: "migrate", image: "api:1.0", command: ["python", "manage.py", "migrate"] },
|
|
757
|
-
],
|
|
758
|
-
sharedVolumes: [{ name: "tmp", emptyDir: {} }],
|
|
759
|
-
});
|
|
760
|
-
\`\`\`
|
|
761
|
-
|
|
762
|
-
Common sidecar use cases:
|
|
763
|
-
- **Envoy proxy** — service mesh, mTLS, traffic management
|
|
764
|
-
- **Log forwarder** — Fluent Bit sidecar for app-specific log routing
|
|
765
|
-
- **Auth proxy** — OAuth2 Proxy for authentication
|
|
766
|
-
- **Config watcher** — reload config on ConfigMap changes
|
|
767
|
-
|
|
768
|
-
## Config and Secret Mounting
|
|
769
|
-
|
|
770
|
-
### ConfiguredApp — automatic volume wiring
|
|
771
|
-
|
|
772
|
-
\`\`\`typescript
|
|
773
|
-
import { ConfiguredApp } from "@intentius/chant-lexicon-k8s";
|
|
774
|
-
|
|
775
|
-
const { deployment, service, configMap } = ConfiguredApp({
|
|
776
|
-
name: "api",
|
|
777
|
-
image: "api:1.0",
|
|
778
|
-
port: 8080,
|
|
779
|
-
// Mount ConfigMap as volume
|
|
780
|
-
configData: { "app.conf": "key=value\\nother=setting" },
|
|
781
|
-
configMountPath: "/etc/api",
|
|
782
|
-
// Mount existing Secret as volume
|
|
783
|
-
secretName: "api-creds",
|
|
784
|
-
secretMountPath: "/secrets",
|
|
785
|
-
// Inject as environment variables
|
|
786
|
-
envFrom: { secretRef: "api-env-secret", configMapRef: "api-env-config" },
|
|
787
|
-
// Run migrations before the app starts
|
|
788
|
-
initContainers: [
|
|
789
|
-
{ name: "migrate", image: "api:1.0", command: ["./migrate.sh"] },
|
|
790
|
-
],
|
|
791
|
-
});
|
|
792
|
-
\`\`\`
|
|
793
|
-
|
|
794
|
-
### Volume patterns
|
|
795
|
-
|
|
796
|
-
| Pattern | Use Case | ConfiguredApp Props |
|
|
797
|
-
|---------|----------|---------------------|
|
|
798
|
-
| ConfigMap as file | Config files, templates | \`configData\` + \`configMountPath\` |
|
|
799
|
-
| Secret as file | TLS certs, credentials | \`secretName\` + \`secretMountPath\` |
|
|
800
|
-
| ConfigMap as env | Simple key-value config | \`envFrom.configMapRef\` |
|
|
801
|
-
| Secret as env | Database URLs, API keys | \`envFrom.secretRef\` |
|
|
802
|
-
|
|
803
|
-
## TLS / cert-manager
|
|
804
|
-
|
|
805
|
-
### SecureIngress — multi-host TLS with cert-manager
|
|
806
|
-
|
|
807
|
-
\`\`\`typescript
|
|
808
|
-
import { SecureIngress } from "@intentius/chant-lexicon-k8s";
|
|
809
|
-
|
|
810
|
-
const { ingress, certificate } = SecureIngress({
|
|
811
|
-
name: "app-ingress",
|
|
812
|
-
hosts: [
|
|
813
|
-
{
|
|
814
|
-
hostname: "api.example.com",
|
|
815
|
-
paths: [
|
|
816
|
-
{ path: "/v1", serviceName: "api-v1", servicePort: 80 },
|
|
817
|
-
{ path: "/v2", serviceName: "api-v2", servicePort: 80 },
|
|
818
|
-
],
|
|
819
|
-
},
|
|
820
|
-
{
|
|
821
|
-
hostname: "admin.example.com",
|
|
822
|
-
paths: [{ path: "/", serviceName: "admin", servicePort: 80 }],
|
|
823
|
-
},
|
|
824
|
-
],
|
|
825
|
-
clusterIssuer: "letsencrypt-prod",
|
|
826
|
-
ingressClassName: "nginx",
|
|
827
|
-
});
|
|
828
|
-
\`\`\`
|
|
829
|
-
|
|
830
|
-
Features:
|
|
831
|
-
- Multiple hosts and paths per Ingress
|
|
832
|
-
- Automatic cert-manager Certificate when \`clusterIssuer\` set
|
|
833
|
-
- TLS secret auto-provisioned by cert-manager
|
|
834
|
-
|
|
835
|
-
### cert-manager setup (prerequisite)
|
|
836
|
-
|
|
837
|
-
\`\`\`bash
|
|
838
|
-
# Install cert-manager
|
|
839
|
-
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
|
|
840
|
-
|
|
841
|
-
# Create ClusterIssuer for Let's Encrypt
|
|
842
|
-
kubectl apply -f - <<EOF
|
|
843
|
-
apiVersion: cert-manager.io/v1
|
|
844
|
-
kind: ClusterIssuer
|
|
845
|
-
metadata:
|
|
846
|
-
name: letsencrypt-prod
|
|
847
|
-
spec:
|
|
848
|
-
acme:
|
|
849
|
-
server: https://acme-v02.api.letsencrypt.org/directory
|
|
850
|
-
email: admin@example.com
|
|
851
|
-
privateKeySecretRef:
|
|
852
|
-
name: letsencrypt-prod-key
|
|
853
|
-
solvers:
|
|
854
|
-
- http01:
|
|
855
|
-
ingress:
|
|
856
|
-
class: nginx
|
|
857
|
-
EOF
|
|
858
|
-
\`\`\`
|
|
859
|
-
|
|
860
|
-
## Observability
|
|
861
|
-
|
|
862
|
-
### MonitoredService — Prometheus monitoring
|
|
863
|
-
|
|
864
|
-
\`\`\`typescript
|
|
865
|
-
import { MonitoredService } from "@intentius/chant-lexicon-k8s";
|
|
866
|
-
|
|
867
|
-
const { deployment, service, serviceMonitor, prometheusRule } = MonitoredService({
|
|
868
|
-
name: "api",
|
|
869
|
-
image: "api:1.0",
|
|
870
|
-
port: 8080,
|
|
871
|
-
metricsPort: 9090,
|
|
872
|
-
metricsPath: "/metrics",
|
|
873
|
-
scrapeInterval: "15s",
|
|
874
|
-
alertRules: [
|
|
875
|
-
{
|
|
876
|
-
name: "HighErrorRate",
|
|
877
|
-
expr: 'rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05',
|
|
878
|
-
for: "5m",
|
|
879
|
-
severity: "critical",
|
|
880
|
-
annotations: { summary: "High error rate on {{ $labels.instance }}" },
|
|
881
|
-
},
|
|
882
|
-
{
|
|
883
|
-
name: "HighLatency",
|
|
884
|
-
expr: 'histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 1',
|
|
885
|
-
for: "10m",
|
|
886
|
-
severity: "warning",
|
|
887
|
-
},
|
|
888
|
-
],
|
|
889
|
-
});
|
|
890
|
-
\`\`\`
|
|
891
|
-
|
|
892
|
-
### Prerequisites
|
|
893
|
-
|
|
894
|
-
- Prometheus Operator installed (for ServiceMonitor/PrometheusRule CRDs)
|
|
895
|
-
- Prometheus configured to discover ServiceMonitors
|
|
896
|
-
|
|
897
|
-
## Network Isolation
|
|
898
|
-
|
|
899
|
-
### NetworkIsolatedApp — per-app firewall rules
|
|
900
|
-
|
|
901
|
-
\`\`\`typescript
|
|
902
|
-
import { NetworkIsolatedApp } from "@intentius/chant-lexicon-k8s";
|
|
903
|
-
|
|
904
|
-
const { deployment, service, networkPolicy } = NetworkIsolatedApp({
|
|
905
|
-
name: "api",
|
|
906
|
-
image: "api:1.0",
|
|
907
|
-
port: 8080,
|
|
908
|
-
allowIngressFrom: [
|
|
909
|
-
{ podSelector: { "app.kubernetes.io/name": "frontend" } },
|
|
910
|
-
{ namespaceSelector: { "kubernetes.io/metadata.name": "monitoring" } },
|
|
911
|
-
],
|
|
912
|
-
allowEgressTo: [
|
|
913
|
-
{ podSelector: { "app.kubernetes.io/name": "postgres" }, ports: [{ port: 5432 }] },
|
|
914
|
-
{ podSelector: { "app.kubernetes.io/name": "redis" }, ports: [{ port: 6379 }] },
|
|
915
|
-
],
|
|
916
|
-
});
|
|
917
|
-
\`\`\`
|
|
918
|
-
|
|
919
|
-
### Combining with NamespaceEnv
|
|
920
|
-
|
|
921
|
-
Use NamespaceEnv for namespace-level default-deny, then NetworkIsolatedApp for per-app allow rules:
|
|
922
|
-
|
|
923
|
-
\`\`\`typescript
|
|
924
|
-
// Namespace: deny all by default
|
|
925
|
-
const ns = NamespaceEnv({ name: "prod", defaultDenyIngress: true, defaultDenyEgress: true });
|
|
926
|
-
|
|
927
|
-
// App: allow specific traffic
|
|
928
|
-
const app = NetworkIsolatedApp({
|
|
929
|
-
name: "api",
|
|
930
|
-
image: "api:1.0",
|
|
931
|
-
namespace: "prod",
|
|
932
|
-
allowIngressFrom: [{ podSelector: { "app.kubernetes.io/name": "gateway" } }],
|
|
933
|
-
allowEgressTo: [{ podSelector: { "app.kubernetes.io/name": "db" }, ports: [{ port: 5432 }] }],
|
|
934
|
-
});
|
|
935
|
-
\`\`\`
|
|
936
|
-
|
|
937
|
-
## Blue/Green and Canary
|
|
938
|
-
|
|
939
|
-
These patterns use standard K8s resources — no special composite needed.
|
|
940
|
-
|
|
941
|
-
### Blue/Green
|
|
942
|
-
|
|
943
|
-
\`\`\`typescript
|
|
944
|
-
// Two Deployments with different versions
|
|
945
|
-
const blue = WebApp({ name: "app-blue", image: "app:1.0", labels: { version: "blue" } });
|
|
946
|
-
const green = WebApp({ name: "app-green", image: "app:2.0", labels: { version: "green" } });
|
|
947
|
-
|
|
948
|
-
// Service points to active version — switch by changing selector
|
|
949
|
-
// Active: blue → green (update the Service selector)
|
|
950
|
-
\`\`\`
|
|
951
|
-
|
|
952
|
-
### Canary
|
|
953
|
-
|
|
954
|
-
\`\`\`typescript
|
|
955
|
-
// Main deployment (90% traffic)
|
|
956
|
-
const main = AutoscaledService({ name: "app", image: "app:1.0", minReplicas: 9, maxReplicas: 20, ... });
|
|
957
|
-
|
|
958
|
-
// Canary deployment (10% traffic) — same app label, fewer replicas
|
|
959
|
-
const canary = WebApp({ name: "app-canary", image: "app:2.0", replicas: 1, labels: { track: "canary" } });
|
|
960
|
-
// Both share the same Service selector ("app.kubernetes.io/name": "app") for traffic splitting
|
|
961
|
-
\`\`\`
|
|
962
|
-
|
|
963
|
-
## Gateway API (future direction)
|
|
964
|
-
|
|
965
|
-
Gateway API is the successor to Ingress. Key differences:
|
|
966
|
-
- **HTTPRoute** replaces Ingress rules
|
|
967
|
-
- **Gateway** replaces IngressClass
|
|
968
|
-
- Built-in traffic splitting, header matching, URL rewriting
|
|
969
|
-
- Currently in beta — use Ingress/SecureIngress/AlbIngress for production today
|
|
970
|
-
|
|
971
|
-
When Gateway API reaches GA, new composites will be added. For now, use CRD import if you need Gateway API resources.
|
|
972
|
-
`,
|
|
973
515
|
triggers: [
|
|
974
516
|
{ type: "context", value: "sidecar" },
|
|
975
517
|
{ type: "context", value: "init-container" },
|
|
@@ -1019,26 +561,17 @@ const { deployment, service, serviceMonitor, prometheusRule } = MonitoredService
|
|
|
1019
561
|
},
|
|
1020
562
|
],
|
|
1021
563
|
},
|
|
1022
|
-
];
|
|
1023
|
-
|
|
1024
|
-
// Load file-based skills from src/skills/
|
|
1025
|
-
const { readFileSync } = require("fs");
|
|
1026
|
-
const { join, dirname } = require("path");
|
|
1027
|
-
const { fileURLToPath } = require("url");
|
|
1028
|
-
const dir = dirname(fileURLToPath(import.meta.url));
|
|
1029
|
-
|
|
1030
|
-
const skillFiles = [
|
|
1031
564
|
{
|
|
1032
|
-
file: "
|
|
1033
|
-
name: "
|
|
565
|
+
file: "chant-k8s-deployment-strategies.md",
|
|
566
|
+
name: "chant-k8s-deployment-strategies",
|
|
1034
567
|
description: "Kubernetes deployment strategies, stateful workloads, RBAC, and networking patterns",
|
|
1035
568
|
triggers: [
|
|
1036
|
-
{ type: "context"
|
|
1037
|
-
{ type: "context"
|
|
1038
|
-
{ type: "context"
|
|
1039
|
-
{ type: "context"
|
|
1040
|
-
{ type: "context"
|
|
1041
|
-
{ type: "context"
|
|
569
|
+
{ type: "context", value: "deployment strategy" },
|
|
570
|
+
{ type: "context", value: "statefulset" },
|
|
571
|
+
{ type: "context", value: "rbac" },
|
|
572
|
+
{ type: "context", value: "network policy" },
|
|
573
|
+
{ type: "context", value: "rolling update" },
|
|
574
|
+
{ type: "context", value: "blue green" },
|
|
1042
575
|
],
|
|
1043
576
|
parameters: [],
|
|
1044
577
|
examples: [
|
|
@@ -1050,15 +583,15 @@ const { deployment, service, serviceMonitor, prometheusRule } = MonitoredService
|
|
|
1050
583
|
],
|
|
1051
584
|
},
|
|
1052
585
|
{
|
|
1053
|
-
file: "
|
|
1054
|
-
name: "
|
|
586
|
+
file: "chant-k8s-security.md",
|
|
587
|
+
name: "chant-k8s-security",
|
|
1055
588
|
description: "Kubernetes pod security, image scanning, network policies, and secrets management",
|
|
1056
589
|
triggers: [
|
|
1057
|
-
{ type: "context"
|
|
1058
|
-
{ type: "context"
|
|
1059
|
-
{ type: "context"
|
|
1060
|
-
{ type: "context"
|
|
1061
|
-
{ type: "context"
|
|
590
|
+
{ type: "context", value: "k8s security" },
|
|
591
|
+
{ type: "context", value: "pod security" },
|
|
592
|
+
{ type: "context", value: "image security" },
|
|
593
|
+
{ type: "context", value: "k8s secrets" },
|
|
594
|
+
{ type: "context", value: "security context" },
|
|
1062
595
|
],
|
|
1063
596
|
parameters: [],
|
|
1064
597
|
examples: [
|
|
@@ -1074,11 +607,11 @@ const { deployment, service, serviceMonitor, prometheusRule } = MonitoredService
|
|
|
1074
607
|
name: "chant-k8s-eks",
|
|
1075
608
|
description: "EKS-specific Kubernetes composites — IRSA, ALB, EBS/EFS, Fluent Bit, ExternalDNS, ADOT",
|
|
1076
609
|
triggers: [
|
|
1077
|
-
{ type: "context"
|
|
1078
|
-
{ type: "context"
|
|
1079
|
-
{ type: "context"
|
|
1080
|
-
{ type: "context"
|
|
1081
|
-
{ type: "context"
|
|
610
|
+
{ type: "context", value: "eks composites" },
|
|
611
|
+
{ type: "context", value: "irsa" },
|
|
612
|
+
{ type: "context", value: "alb ingress" },
|
|
613
|
+
{ type: "context", value: "ebs storage" },
|
|
614
|
+
{ type: "context", value: "karpenter" },
|
|
1082
615
|
],
|
|
1083
616
|
parameters: [],
|
|
1084
617
|
examples: [
|
|
@@ -1094,9 +627,9 @@ const { deployment, service, serviceMonitor, prometheusRule } = MonitoredService
|
|
|
1094
627
|
name: "chant-k8s-gke",
|
|
1095
628
|
description: "GKE-specific Kubernetes composites — Workload Identity, GCE PD, Filestore, FluentBit, OTel, ExternalDNS, Gateway",
|
|
1096
629
|
triggers: [
|
|
1097
|
-
{ type: "context"
|
|
1098
|
-
{ type: "context"
|
|
1099
|
-
{ type: "context"
|
|
630
|
+
{ type: "context", value: "gke composites" },
|
|
631
|
+
{ type: "context", value: "workload identity gke" },
|
|
632
|
+
{ type: "context", value: "config connector k8s" },
|
|
1100
633
|
],
|
|
1101
634
|
parameters: [],
|
|
1102
635
|
examples: [
|
|
@@ -1112,9 +645,9 @@ const { deployment, service, serviceMonitor, prometheusRule } = MonitoredService
|
|
|
1112
645
|
name: "chant-k8s-aks",
|
|
1113
646
|
description: "AKS-specific Kubernetes composites — Workload Identity, AGIC, Azure Disk/File, ExternalDNS, Azure Monitor",
|
|
1114
647
|
triggers: [
|
|
1115
|
-
{ type: "context"
|
|
1116
|
-
{ type: "context"
|
|
1117
|
-
{ type: "context"
|
|
648
|
+
{ type: "context", value: "aks composites" },
|
|
649
|
+
{ type: "context", value: "workload identity aks" },
|
|
650
|
+
{ type: "context", value: "agic ingress" },
|
|
1118
651
|
],
|
|
1119
652
|
parameters: [],
|
|
1120
653
|
examples: [
|
|
@@ -1125,22 +658,5 @@ const { deployment, service, serviceMonitor, prometheusRule } = MonitoredService
|
|
|
1125
658
|
},
|
|
1126
659
|
],
|
|
1127
660
|
},
|
|
1128
|
-
]
|
|
1129
|
-
|
|
1130
|
-
for (const skill of skillFiles) {
|
|
1131
|
-
try {
|
|
1132
|
-
const content = readFileSync(join(dir, "skills", skill.file), "utf-8");
|
|
1133
|
-
skills.push({
|
|
1134
|
-
name: skill.name,
|
|
1135
|
-
description: skill.description,
|
|
1136
|
-
content,
|
|
1137
|
-
triggers: skill.triggers,
|
|
1138
|
-
parameters: skill.parameters,
|
|
1139
|
-
examples: skill.examples,
|
|
1140
|
-
});
|
|
1141
|
-
} catch { /* skip missing skills */ }
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
return skills;
|
|
1145
|
-
},
|
|
661
|
+
]),
|
|
1146
662
|
};
|