@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,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkerPool composite — Deployment + ServiceAccount + Role + RoleBinding + optional ConfigMap + optional HPA.
|
|
3
|
+
*
|
|
4
|
+
* A higher-level construct for background queue workers (Sidekiq, Celery, Bull)
|
|
5
|
+
* that need RBAC for secrets/configmaps and optional autoscaling, but no Service.
|
|
6
|
+
*/
|
|
7
|
+
import { Composite, mergeDefaults } from "@intentius/chant";
|
|
8
|
+
import { Deployment, ServiceAccount, Role, RoleBinding, ConfigMap, HorizontalPodAutoscaler, PodDisruptionBudget, } from "../generated/index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Create a WorkerPool composite — returns prop objects for
|
|
11
|
+
* a Deployment, ServiceAccount, Role, RoleBinding, optional ConfigMap, and optional HPA.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { WorkerPool } from "@intentius/chant-lexicon-k8s";
|
|
16
|
+
*
|
|
17
|
+
* const { deployment, serviceAccount, role, roleBinding } = WorkerPool({
|
|
18
|
+
* name: "email-worker",
|
|
19
|
+
* image: "worker:1.0",
|
|
20
|
+
* command: ["bundle", "exec", "sidekiq"],
|
|
21
|
+
* config: { REDIS_URL: "redis://redis:6379" },
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export const WorkerPool = Composite((props) => {
|
|
26
|
+
const { name, image, command, args, replicas = 1, config, rbacRules, autoscaling, minAvailable, securityContext, terminationGracePeriodSeconds, priorityClassName, cpuRequest = "100m", memoryRequest = "128Mi", cpuLimit = "500m", memoryLimit = "256Mi", labels: extraLabels = {}, namespace, env, defaults: defs, } = props;
|
|
27
|
+
const saName = `${name}-sa`;
|
|
28
|
+
const roleName = `${name}-role`;
|
|
29
|
+
const bindingName = `${name}-binding`;
|
|
30
|
+
const configMapName = `${name}-config`;
|
|
31
|
+
const commonLabels = {
|
|
32
|
+
"app.kubernetes.io/name": name,
|
|
33
|
+
"app.kubernetes.io/managed-by": "chant",
|
|
34
|
+
...extraLabels,
|
|
35
|
+
};
|
|
36
|
+
// undefined → default RBAC rules; explicit [] → no RBAC resources
|
|
37
|
+
const createRbac = rbacRules === undefined || rbacRules.length > 0;
|
|
38
|
+
const effectiveRbacRules = rbacRules === undefined
|
|
39
|
+
? [{ apiGroups: [""], resources: ["secrets", "configmaps"], verbs: ["get"] }]
|
|
40
|
+
: rbacRules;
|
|
41
|
+
const effectiveReplicas = autoscaling ? autoscaling.minReplicas : replicas;
|
|
42
|
+
const container = {
|
|
43
|
+
name,
|
|
44
|
+
image,
|
|
45
|
+
...(command && { command }),
|
|
46
|
+
...(args && { args }),
|
|
47
|
+
resources: {
|
|
48
|
+
limits: { cpu: cpuLimit, memory: memoryLimit },
|
|
49
|
+
requests: { cpu: cpuRequest, memory: memoryRequest },
|
|
50
|
+
},
|
|
51
|
+
...(env && { env }),
|
|
52
|
+
...(config && {
|
|
53
|
+
envFrom: [{ configMapRef: { name: configMapName } }],
|
|
54
|
+
}),
|
|
55
|
+
...(securityContext && { securityContext }),
|
|
56
|
+
};
|
|
57
|
+
const podSpec = {
|
|
58
|
+
...(createRbac && { serviceAccountName: saName }),
|
|
59
|
+
containers: [container],
|
|
60
|
+
...(terminationGracePeriodSeconds !== undefined && { terminationGracePeriodSeconds }),
|
|
61
|
+
...(priorityClassName && { priorityClassName }),
|
|
62
|
+
};
|
|
63
|
+
const deployment = new Deployment(mergeDefaults({
|
|
64
|
+
metadata: {
|
|
65
|
+
name,
|
|
66
|
+
...(namespace && { namespace }),
|
|
67
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "worker" },
|
|
68
|
+
},
|
|
69
|
+
spec: {
|
|
70
|
+
replicas: effectiveReplicas,
|
|
71
|
+
selector: { matchLabels: { "app.kubernetes.io/name": name } },
|
|
72
|
+
template: {
|
|
73
|
+
metadata: { labels: { "app.kubernetes.io/name": name, ...extraLabels } },
|
|
74
|
+
spec: podSpec,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
}, defs?.deployment));
|
|
78
|
+
const result = { deployment };
|
|
79
|
+
if (createRbac) {
|
|
80
|
+
result.serviceAccount = new ServiceAccount(mergeDefaults({
|
|
81
|
+
metadata: {
|
|
82
|
+
name: saName,
|
|
83
|
+
...(namespace && { namespace }),
|
|
84
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "worker" },
|
|
85
|
+
},
|
|
86
|
+
}, defs?.serviceAccount));
|
|
87
|
+
result.role = new Role(mergeDefaults({
|
|
88
|
+
metadata: {
|
|
89
|
+
name: roleName,
|
|
90
|
+
...(namespace && { namespace }),
|
|
91
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
|
|
92
|
+
},
|
|
93
|
+
rules: effectiveRbacRules,
|
|
94
|
+
}, defs?.role));
|
|
95
|
+
result.roleBinding = new RoleBinding(mergeDefaults({
|
|
96
|
+
metadata: {
|
|
97
|
+
name: bindingName,
|
|
98
|
+
...(namespace && { namespace }),
|
|
99
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
|
|
100
|
+
},
|
|
101
|
+
roleRef: {
|
|
102
|
+
apiGroup: "rbac.authorization.k8s.io",
|
|
103
|
+
kind: "Role",
|
|
104
|
+
name: roleName,
|
|
105
|
+
},
|
|
106
|
+
subjects: [
|
|
107
|
+
{
|
|
108
|
+
kind: "ServiceAccount",
|
|
109
|
+
name: saName,
|
|
110
|
+
...(namespace && { namespace }),
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
}, defs?.roleBinding));
|
|
114
|
+
}
|
|
115
|
+
if (config) {
|
|
116
|
+
result.configMap = new ConfigMap(mergeDefaults({
|
|
117
|
+
metadata: {
|
|
118
|
+
name: configMapName,
|
|
119
|
+
...(namespace && { namespace }),
|
|
120
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "config" },
|
|
121
|
+
},
|
|
122
|
+
data: config,
|
|
123
|
+
}, defs?.configMap));
|
|
124
|
+
}
|
|
125
|
+
if (minAvailable !== undefined) {
|
|
126
|
+
result.pdb = new PodDisruptionBudget(mergeDefaults({
|
|
127
|
+
metadata: {
|
|
128
|
+
name,
|
|
129
|
+
...(namespace && { namespace }),
|
|
130
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "disruption-budget" },
|
|
131
|
+
},
|
|
132
|
+
spec: {
|
|
133
|
+
minAvailable,
|
|
134
|
+
selector: { matchLabels: { "app.kubernetes.io/name": name } },
|
|
135
|
+
},
|
|
136
|
+
}, defs?.pdb));
|
|
137
|
+
}
|
|
138
|
+
if (autoscaling) {
|
|
139
|
+
const targetCPUPercent = autoscaling.targetCPUPercent ?? 70;
|
|
140
|
+
result.hpa = new HorizontalPodAutoscaler(mergeDefaults({
|
|
141
|
+
metadata: {
|
|
142
|
+
name,
|
|
143
|
+
...(namespace && { namespace }),
|
|
144
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "autoscaler" },
|
|
145
|
+
},
|
|
146
|
+
spec: {
|
|
147
|
+
scaleTargetRef: {
|
|
148
|
+
apiVersion: "apps/v1",
|
|
149
|
+
kind: "Deployment",
|
|
150
|
+
name,
|
|
151
|
+
},
|
|
152
|
+
minReplicas: autoscaling.minReplicas,
|
|
153
|
+
maxReplicas: autoscaling.maxReplicas,
|
|
154
|
+
metrics: [
|
|
155
|
+
{
|
|
156
|
+
type: "Resource",
|
|
157
|
+
resource: {
|
|
158
|
+
name: "cpu",
|
|
159
|
+
target: { type: "Utilization", averageUtilization: targetCPUPercent },
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
}, defs?.hpa));
|
|
165
|
+
}
|
|
166
|
+
return result;
|
|
167
|
+
}, "WorkerPool");
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkloadIdentityServiceAccount composite — ServiceAccount + Workload Identity annotation + optional RBAC.
|
|
3
|
+
*
|
|
4
|
+
* @aks Creates a ServiceAccount with the `azure.workload.identity/client-id`
|
|
5
|
+
* annotation and `azure.workload.identity/use: "true"` label for AKS Workload Identity.
|
|
6
|
+
*/
|
|
7
|
+
import { Composite, mergeDefaults } from "@intentius/chant";
|
|
8
|
+
import { ServiceAccount, Role, RoleBinding } from "../generated/index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Create a WorkloadIdentityServiceAccount composite — returns prop objects for
|
|
11
|
+
* a ServiceAccount with AKS Workload Identity annotation, and optional Role + RoleBinding.
|
|
12
|
+
*
|
|
13
|
+
* @aks
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { WorkloadIdentityServiceAccount } from "@intentius/chant-lexicon-k8s";
|
|
17
|
+
*
|
|
18
|
+
* const { serviceAccount, role, roleBinding } = WorkloadIdentityServiceAccount({
|
|
19
|
+
* name: "app-sa",
|
|
20
|
+
* clientId: "00000000-0000-0000-0000-000000000000",
|
|
21
|
+
* rbacRules: [
|
|
22
|
+
* { apiGroups: [""], resources: ["secrets"], verbs: ["get"] },
|
|
23
|
+
* ],
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export const WorkloadIdentityServiceAccount = Composite((props) => {
|
|
28
|
+
const { name, clientId, rbacRules, labels: extraLabels = {}, namespace, defaults: defs, } = props;
|
|
29
|
+
const roleName = `${name}-role`;
|
|
30
|
+
const bindingName = `${name}-binding`;
|
|
31
|
+
const commonLabels = {
|
|
32
|
+
"app.kubernetes.io/name": name,
|
|
33
|
+
"app.kubernetes.io/managed-by": "chant",
|
|
34
|
+
...extraLabels,
|
|
35
|
+
};
|
|
36
|
+
const serviceAccount = new ServiceAccount(mergeDefaults({
|
|
37
|
+
metadata: {
|
|
38
|
+
name,
|
|
39
|
+
...(namespace && { namespace }),
|
|
40
|
+
labels: {
|
|
41
|
+
...commonLabels,
|
|
42
|
+
"app.kubernetes.io/component": "service-account",
|
|
43
|
+
"azure.workload.identity/use": "true",
|
|
44
|
+
},
|
|
45
|
+
annotations: {
|
|
46
|
+
"azure.workload.identity/client-id": clientId,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
}, defs?.serviceAccount));
|
|
50
|
+
const result = { serviceAccount };
|
|
51
|
+
if (rbacRules && rbacRules.length > 0) {
|
|
52
|
+
result.role = new Role(mergeDefaults({
|
|
53
|
+
metadata: {
|
|
54
|
+
name: roleName,
|
|
55
|
+
...(namespace && { namespace }),
|
|
56
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
|
|
57
|
+
},
|
|
58
|
+
rules: rbacRules,
|
|
59
|
+
}, defs?.role));
|
|
60
|
+
result.roleBinding = new RoleBinding(mergeDefaults({
|
|
61
|
+
metadata: {
|
|
62
|
+
name: bindingName,
|
|
63
|
+
...(namespace && { namespace }),
|
|
64
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
|
|
65
|
+
},
|
|
66
|
+
roleRef: {
|
|
67
|
+
apiGroup: "rbac.authorization.k8s.io",
|
|
68
|
+
kind: "Role",
|
|
69
|
+
name: roleName,
|
|
70
|
+
},
|
|
71
|
+
subjects: [
|
|
72
|
+
{
|
|
73
|
+
kind: "ServiceAccount",
|
|
74
|
+
name,
|
|
75
|
+
...(namespace && { namespace }),
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
}, defs?.roleBinding));
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}, "WorkloadIdentityServiceAccount");
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkloadIdentityServiceAccount composite — ServiceAccount + GKE Workload Identity annotation + optional RBAC.
|
|
3
|
+
*
|
|
4
|
+
* @gke Creates a ServiceAccount with the `iam.gke.io/gcp-service-account`
|
|
5
|
+
* annotation for GKE Workload Identity Federation.
|
|
6
|
+
*/
|
|
7
|
+
import { Composite, mergeDefaults } from "@intentius/chant";
|
|
8
|
+
import { ServiceAccount, Role, RoleBinding } from "../generated/index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Create a WorkloadIdentityServiceAccount composite — returns prop objects for
|
|
11
|
+
* a ServiceAccount with GKE Workload Identity annotation, and optional Role + RoleBinding.
|
|
12
|
+
*
|
|
13
|
+
* @gke
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { WorkloadIdentityServiceAccount } from "@intentius/chant-lexicon-k8s";
|
|
17
|
+
*
|
|
18
|
+
* const { serviceAccount, role, roleBinding } = WorkloadIdentityServiceAccount({
|
|
19
|
+
* name: "app-sa",
|
|
20
|
+
* gcpServiceAccountEmail: "sa@my-project.iam.gserviceaccount.com",
|
|
21
|
+
* rbacRules: [
|
|
22
|
+
* { apiGroups: [""], resources: ["secrets"], verbs: ["get"] },
|
|
23
|
+
* ],
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export const WorkloadIdentityServiceAccount = Composite((props) => {
|
|
28
|
+
const { name, gcpServiceAccountEmail, rbacRules, labels: extraLabels = {}, namespace, defaults: defs, } = props;
|
|
29
|
+
const roleName = `${name}-role`;
|
|
30
|
+
const bindingName = `${name}-binding`;
|
|
31
|
+
const commonLabels = {
|
|
32
|
+
"app.kubernetes.io/name": name,
|
|
33
|
+
"app.kubernetes.io/managed-by": "chant",
|
|
34
|
+
...extraLabels,
|
|
35
|
+
};
|
|
36
|
+
const serviceAccount = new ServiceAccount(mergeDefaults({
|
|
37
|
+
metadata: {
|
|
38
|
+
name,
|
|
39
|
+
...(namespace && { namespace }),
|
|
40
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "service-account" },
|
|
41
|
+
annotations: {
|
|
42
|
+
"iam.gke.io/gcp-service-account": gcpServiceAccountEmail,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
}, defs?.serviceAccount));
|
|
46
|
+
const result = { serviceAccount };
|
|
47
|
+
if (rbacRules && rbacRules.length > 0) {
|
|
48
|
+
result.role = new Role(mergeDefaults({
|
|
49
|
+
metadata: {
|
|
50
|
+
name: roleName,
|
|
51
|
+
...(namespace && { namespace }),
|
|
52
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
|
|
53
|
+
},
|
|
54
|
+
rules: rbacRules,
|
|
55
|
+
}, defs?.role));
|
|
56
|
+
result.roleBinding = new RoleBinding(mergeDefaults({
|
|
57
|
+
metadata: {
|
|
58
|
+
name: bindingName,
|
|
59
|
+
...(namespace && { namespace }),
|
|
60
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
|
|
61
|
+
},
|
|
62
|
+
roleRef: {
|
|
63
|
+
apiGroup: "rbac.authorization.k8s.io",
|
|
64
|
+
kind: "Role",
|
|
65
|
+
name: roleName,
|
|
66
|
+
},
|
|
67
|
+
subjects: [
|
|
68
|
+
{
|
|
69
|
+
kind: "ServiceAccount",
|
|
70
|
+
name,
|
|
71
|
+
...(namespace && { namespace }),
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
}, defs?.roleBinding));
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}, "WorkloadIdentityServiceAccount");
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime shape of the `k8s` namespace in `chant.config.ts` (#1344).
|
|
3
|
+
*
|
|
4
|
+
* `./config.ts` describes the namespace for readers and for the `satisfies
|
|
5
|
+
* K8sChantConfig` form it documents. What it could not do is make an unknown
|
|
6
|
+
* key fail: the project config schema is `.passthrough()`, so a typo in
|
|
7
|
+
* `k8s.profiles.prod.contxt` was accepted and ignored — and a missing cluster
|
|
8
|
+
* binding is the failure this lexicon most cares about, since a wrong-cluster
|
|
9
|
+
* read reports every declared resource as missing (#1100).
|
|
10
|
+
*
|
|
11
|
+
* The schema is the runtime half. It is checked against `K8sChantConfig` below,
|
|
12
|
+
* so the two descriptions of the namespace cannot drift.
|
|
13
|
+
*/
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
export const k8sClusterProfileSchema = z.strictObject({
|
|
16
|
+
context: z.string(),
|
|
17
|
+
});
|
|
18
|
+
const kinds = z.array(z.string().min(1)).optional();
|
|
19
|
+
/** A `k8s.crds` entry. Remote sources must carry their pin. */
|
|
20
|
+
export const k8sProjectCrdSourceSchema = z.discriminatedUnion("type", [
|
|
21
|
+
z.strictObject({ type: z.literal("file"), path: z.string().min(1), kinds }),
|
|
22
|
+
z.strictObject({
|
|
23
|
+
type: z.literal("url"),
|
|
24
|
+
url: z.string().url(),
|
|
25
|
+
sha256: z.string().regex(/^(sha256:)?[0-9a-f]{64}$/, "must be the hex sha256 of the URL's content"),
|
|
26
|
+
kinds,
|
|
27
|
+
}),
|
|
28
|
+
z.strictObject({
|
|
29
|
+
type: z.literal("helm"),
|
|
30
|
+
chart: z.string().min(1),
|
|
31
|
+
version: z.string().min(1),
|
|
32
|
+
digest: z.string().optional(),
|
|
33
|
+
chartSubdir: z.string().optional(),
|
|
34
|
+
kinds,
|
|
35
|
+
}),
|
|
36
|
+
]);
|
|
37
|
+
const jsonPatchOperationSchema = z.discriminatedUnion("op", [
|
|
38
|
+
z.strictObject({ op: z.literal("add"), path: z.string(), value: z.unknown() }),
|
|
39
|
+
z.strictObject({ op: z.literal("remove"), path: z.string() }),
|
|
40
|
+
z.strictObject({ op: z.literal("replace"), path: z.string(), value: z.unknown() }),
|
|
41
|
+
z.strictObject({ op: z.literal("move"), from: z.string(), path: z.string() }),
|
|
42
|
+
z.strictObject({ op: z.literal("copy"), from: z.string(), path: z.string() }),
|
|
43
|
+
z.strictObject({ op: z.literal("test"), path: z.string(), value: z.unknown() }),
|
|
44
|
+
]);
|
|
45
|
+
const renderedSelectorShape = {
|
|
46
|
+
kind: z.string().min(1),
|
|
47
|
+
name: z.string().min(1),
|
|
48
|
+
namespace: z.string().optional(),
|
|
49
|
+
apiVersion: z.string().optional(),
|
|
50
|
+
};
|
|
51
|
+
/** A `RenderedPatch` (#3566): a selector plus exactly one of `jsonPatch` and `merge`. */
|
|
52
|
+
export const renderedPatchSchema = z.union([
|
|
53
|
+
z.strictObject({ ...renderedSelectorShape, jsonPatch: z.array(jsonPatchOperationSchema), merge: z.never().optional() }),
|
|
54
|
+
z.strictObject({ ...renderedSelectorShape, merge: z.record(z.string(), z.unknown()), jsonPatch: z.never().optional() }),
|
|
55
|
+
]);
|
|
56
|
+
export const k8sConfigSchema = z.strictObject({
|
|
57
|
+
profiles: z.record(z.string(), k8sClusterProfileSchema).optional(),
|
|
58
|
+
execCredentialPlugins: z.array(z.string()).optional(),
|
|
59
|
+
kustomize: z
|
|
60
|
+
.strictObject({
|
|
61
|
+
roots: z
|
|
62
|
+
.array(z.union([z.string(), z.strictObject({ path: z.string(), patches: z.array(renderedPatchSchema).optional() })]))
|
|
63
|
+
.optional(),
|
|
64
|
+
})
|
|
65
|
+
.optional(),
|
|
66
|
+
receipts: z
|
|
67
|
+
.strictObject({
|
|
68
|
+
namespace: z.string().optional(),
|
|
69
|
+
})
|
|
70
|
+
.optional(),
|
|
71
|
+
crds: z.array(k8sProjectCrdSourceSchema).optional(),
|
|
72
|
+
});
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* K8s environment → cluster binding — chant #1100.
|
|
3
|
+
*
|
|
4
|
+
* Every cloud lexicon binds an environment to a scope: AWS resolves `<env>`
|
|
5
|
+
* to a CloudFormation stack, Azure treats `<env>` as the resource group.
|
|
6
|
+
* Before this, k8s bound
|
|
7
|
+
* nothing — `describeResources` shelled out to `kubectl get` with no
|
|
8
|
+
* `--context`, so `chant lifecycle diff prod --live` read whichever cluster
|
|
9
|
+
* `kubectl config current-context` happened to point at.
|
|
10
|
+
*
|
|
11
|
+
* Add this to `chant.config.ts` to pin an environment to a cluster:
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* import type { K8sChantConfig } from "@intentius/chant-lexicon-k8s";
|
|
15
|
+
*
|
|
16
|
+
* export default {
|
|
17
|
+
* lexicons: ["k8s"],
|
|
18
|
+
* k8s: {
|
|
19
|
+
* profiles: {
|
|
20
|
+
* prod: { context: "prod-eks" },
|
|
21
|
+
* staging: { context: "staging-eks" },
|
|
22
|
+
* },
|
|
23
|
+
* } satisfies K8sChantConfig,
|
|
24
|
+
* };
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* `describeResources` (in `./describe-resources.ts`, and the GCP lexicon's
|
|
28
|
+
* Config Connector equivalent, which observes through the same kubectl path)
|
|
29
|
+
* resolves this via `@intentius/chant/kubectl-context`'s
|
|
30
|
+
* `resolveClusterTarget`:
|
|
31
|
+
*
|
|
32
|
+
* - A declared binding is passed explicitly to the typed client on every
|
|
33
|
+
* read and write, regardless of what `kubectl` is ambiently pointed at
|
|
34
|
+
* (#1488 — the binding is a selection, not a check). A bound context the
|
|
35
|
+
* kubeconfig does not have fails with an error naming the context and the
|
|
36
|
+
* `k8s.profiles.<env>.context` binding — never by falling back to ambient.
|
|
37
|
+
* - No binding for the environment keeps today's behavior — the ambient
|
|
38
|
+
* context — but logs a visible note that nothing is pinned, so the
|
|
39
|
+
* fallback is never silent. Read failures name the context that was read
|
|
40
|
+
* and the missing binding.
|
|
41
|
+
*
|
|
42
|
+
* `ChantConfig` uses `.passthrough()` in its Zod schema so the `k8s` key is
|
|
43
|
+
* accepted at runtime without core changes, exactly like the `terraform` key
|
|
44
|
+
* (see `lexicons/terraform/src/config.ts`). The type side is the declaration
|
|
45
|
+
* merge at the bottom of this file — without it the snippet above compiles
|
|
46
|
+
* only until someone adds `satisfies ChantConfig`, which every example
|
|
47
|
+
* project does and which is the only thing type-checking the rest of the
|
|
48
|
+
* file (#1370).
|
|
49
|
+
*
|
|
50
|
+
* Deliberately out of scope here (see the issue for the full proposal):
|
|
51
|
+
* deriving the binding automatically from a declared `AWS::EKS::Cluster` /
|
|
52
|
+
* AKS / GKE cluster resource, and threading the resolved context into the
|
|
53
|
+
* Op write path (`op/activities/kubectl.ts`, `wait-for-ready.ts`) — both
|
|
54
|
+
* already accept an optional `context` a workflow author can pass, and can
|
|
55
|
+
* call `resolveClusterTarget` themselves to agree with what the read path
|
|
56
|
+
* resolved. This also keeps the binding available for #1073/#1074's future
|
|
57
|
+
* typed API client to inherit, rather than re-deriving it.
|
|
58
|
+
*/
|
|
59
|
+
export {};
|
package/dist/coverage.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coverage analysis for the Kubernetes lexicon.
|
|
3
|
+
*
|
|
4
|
+
* Analyzes generated resources for coverage of property constraints,
|
|
5
|
+
* lifecycle attributes, and other dimensions.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync } from "fs";
|
|
8
|
+
import { join, dirname } from "path";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
10
|
+
import { computeCoverage, overallPct } from "@intentius/chant/codegen/coverage";
|
|
11
|
+
const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
12
|
+
/**
|
|
13
|
+
* Analyze coverage of the K8s lexicon.
|
|
14
|
+
*/
|
|
15
|
+
export async function analyzeK8sCoverage(opts) {
|
|
16
|
+
const generatedDir = join(pkgDir, "src", "generated");
|
|
17
|
+
const lexiconJSON = readFileSync(join(generatedDir, "lexicon-k8s.json"), "utf-8");
|
|
18
|
+
const report = computeCoverage(lexiconJSON);
|
|
19
|
+
const pct = overallPct(report);
|
|
20
|
+
console.error(`K8s lexicon coverage: ${pct.toFixed(1)}%`);
|
|
21
|
+
console.error(` Resources: ${report.resourceCount}`);
|
|
22
|
+
console.error(` Property constraints: ${report.propertyPct.toFixed(1)}%`);
|
|
23
|
+
if (opts?.minOverall && pct < opts.minOverall) {
|
|
24
|
+
console.error(`Coverage ${pct.toFixed(1)}% is below minimum ${opts.minOverall}%`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
}
|