@intentius/chant-lexicon-k8s 0.26.0 → 0.28.0
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/config.d.ts +62 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/describe-resources.d.ts +8 -0
- package/dist/describe-resources.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +3 -3
- package/dist/manifest.json +1 -1
- package/dist/op/activities/index.d.ts +2 -0
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/kubectl.d.ts +6 -1
- package/dist/op/activities/kubectl.d.ts.map +1 -1
- package/dist/op/activities/wait-for-ready.d.ts +6 -1
- package/dist/op/activities/wait-for-ready.d.ts.map +1 -1
- package/dist/skills/chant-k8s-eks.md +18 -3
- package/package.json +2 -2
- package/src/config.ts +63 -0
- package/src/describe-resources.test.ts +79 -1
- package/src/describe-resources.ts +19 -1
- package/src/index.ts +3 -0
- package/src/op/activities/index.ts +12 -0
- package/src/op/activities/kubectl.ts +6 -1
- package/src/op/activities/wait-for-ready.ts +6 -1
- package/src/skills/chant-k8s-eks.md +18 -3
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
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
|
+
* Temporal looks up `temporal.profiles.<env>`. 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 as `kubectl ... --context <bound>`
|
|
33
|
+
* on every invocation, and checked against the ambient context first — a
|
|
34
|
+
* mismatch refuses loudly (naming the environment, the expected context,
|
|
35
|
+
* and the ambient one) instead of silently reading the wrong cluster.
|
|
36
|
+
* - No binding for the environment keeps today's behavior — the ambient
|
|
37
|
+
* context — but logs a visible note that nothing is pinned, so the
|
|
38
|
+
* fallback is never silent.
|
|
39
|
+
*
|
|
40
|
+
* `ChantConfig` uses `.passthrough()` in its Zod schema so the `k8s` key is
|
|
41
|
+
* accepted at runtime without core changes, exactly like `temporal.profiles`
|
|
42
|
+
* (see `lexicons/temporal/src/config.ts`).
|
|
43
|
+
*
|
|
44
|
+
* Deliberately out of scope here (see the issue for the full proposal):
|
|
45
|
+
* deriving the binding automatically from a declared `AWS::EKS::Cluster` /
|
|
46
|
+
* AKS / GKE cluster resource, and threading the resolved context into the
|
|
47
|
+
* Op write path (`op/activities/kubectl.ts`, `wait-for-ready.ts`) — both
|
|
48
|
+
* already accept an optional `context` a workflow author can pass, and can
|
|
49
|
+
* call `resolveClusterTarget` themselves to agree with what the read path
|
|
50
|
+
* resolved. This also keeps the binding available for #1073/#1074's future
|
|
51
|
+
* typed API client to inherit, rather than re-deriving it.
|
|
52
|
+
*/
|
|
53
|
+
/** A single environment's cluster binding. */
|
|
54
|
+
export interface K8sClusterProfile {
|
|
55
|
+
/** kubectl context name this environment is bound to. */
|
|
56
|
+
context: string;
|
|
57
|
+
}
|
|
58
|
+
export interface K8sChantConfig {
|
|
59
|
+
/** Named environment → cluster bindings, keyed by environment name. */
|
|
60
|
+
profiles?: Record<string, K8sClusterProfile>;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH,8CAA8C;AAC9C,MAAM,WAAW,iBAAiB;IAChC,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;CAC9C"}
|
|
@@ -10,6 +10,14 @@
|
|
|
10
10
|
* Resource-not-found is silent — `state diff --live` then reports it as
|
|
11
11
|
* missing (declared, not in cloud). Unknown entity types are warn-skipped;
|
|
12
12
|
* extending the KUBECTL_RESOURCE map covers more.
|
|
13
|
+
*
|
|
14
|
+
* Before touching any resource, the environment is resolved to a cluster
|
|
15
|
+
* identity (chant #1100) via `resolveClusterTarget` — see `./config.ts` for
|
|
16
|
+
* the `k8s.profiles.<env>.context` binding shape. A declared binding is
|
|
17
|
+
* passed explicitly as `--context` on every kubectl call below; an ambient
|
|
18
|
+
* context that disagrees with it aborts the whole describe with a loud
|
|
19
|
+
* error rather than silently reading the wrong cluster. No binding keeps
|
|
20
|
+
* today's behavior (ambient context), with a visible warning.
|
|
13
21
|
*/
|
|
14
22
|
import type { ResourceMetadata } from "@intentius/chant/lexicon";
|
|
15
23
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe-resources.d.ts","sourceRoot":"","sources":["../src/describe-resources.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"describe-resources.d.ts","sourceRoot":"","sources":["../src/describe-resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAOjE;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAoBnD,CAAC;AAsCF,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IAC9E,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CA2D5C"}
|
package/dist/index.d.ts
CHANGED
|
@@ -17,4 +17,5 @@ export type { PackageOptions, PackageResult } from "./codegen/package.js";
|
|
|
17
17
|
export type { CRDSource, CRDSpec } from "./crd/types.js";
|
|
18
18
|
export { parseCRD, parseCRDSpec } from "./crd/parser.js";
|
|
19
19
|
export { loadCRDs, loadMultipleCRDs } from "./crd/loader.js";
|
|
20
|
+
export type { K8sChantConfig, K8sClusterProfile } from "./config.js";
|
|
20
21
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "63b8e4b79b980ec7b6fb5e49b43686eeb7b024e6feab86d6d71169c65db10587",
|
|
5
5
|
"meta.json": "6d112006daa7196eb28619ac5a3893b709dc2b845edbd537db7d7b471e8dc104",
|
|
6
6
|
"types/index.d.ts": "7b873148146a5512c22baabfde6cfcc3248c24c2c7578f68a19544af5dc675f6",
|
|
7
7
|
"rules/argo-appset-single-project.ts": "afa9f310753aa2d475f35012b13135b2dfaebed2a35d44edbe185ebc07673674",
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"skills/chant-k8s-patterns.md": "683148e9f0f9da6879bd65d97c82cc29dbff897e31bc11b3c1de654cd6baeea3",
|
|
46
46
|
"skills/chant-k8s-deployment-strategies.md": "c4cc93cba064230567baa755b5a3493dc69bfc6e74a62e1990d12039ffaaf5f8",
|
|
47
47
|
"skills/chant-k8s-security.md": "f4250284000fde0d380192ba4cd73f2190471da2142e3bf77d848881552807f7",
|
|
48
|
-
"skills/chant-k8s-eks.md": "
|
|
48
|
+
"skills/chant-k8s-eks.md": "9e953d6a2c39518c4b797a2bf54ff5b4a33530503e5b6d66e35921e8f952432f",
|
|
49
49
|
"skills/chant-k8s-gke.md": "8938840bf9ef5ed58d6333fdd773b3dd54ecaf25a9df35e58f7f5c3355d4928f",
|
|
50
50
|
"skills/chant-k8s-aks.md": "e18f0e2b055f72cd7a37deaf258d7027c2d4d3e286e8fd4975b27a1f981a3ad9",
|
|
51
51
|
"skills/chant-k8s-argo.md": "b1a0b826559d8c5033a479c5781efaf650320f0aee4419d8841170bd3393cea5"
|
|
52
52
|
},
|
|
53
|
-
"composite": "
|
|
53
|
+
"composite": "d6deea6f0e6f08944da12ce71f90784b9fce48659eee22cbf223ae331c7e1e22"
|
|
54
54
|
}
|
package/dist/manifest.json
CHANGED
|
@@ -19,4 +19,6 @@ export { waitForArgoSync, defaultArgoStatusFetcher, ArgoSyncFailedError } from "
|
|
|
19
19
|
export type { WaitForArgoSyncArgs, ArgoAppStatus, ArgoStatusFetcher } from "./argo.js";
|
|
20
20
|
export { waitForReady, defaultResourceFetcher, ReadinessFailedError, readinessFor, isReady, firstTerminal, DEFAULT_READINESS, READINESS_OVERRIDES, } from "./wait-for-ready.js";
|
|
21
21
|
export type { WaitForReadyArgs, ResourceFetcher, ReadinessSpec, ReadinessMatch, ConditionMatch, PathMatch, } from "./wait-for-ready.js";
|
|
22
|
+
export { resolveClusterTarget, ClusterBindingMismatchError } from "@intentius/chant/kubectl-context";
|
|
23
|
+
export type { ResolvedClusterTarget, K8sClusterProfile, K8sConfigShape } from "@intentius/chant/kubectl-context";
|
|
22
24
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/op/activities/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,YAAY,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAElD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AACvF,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAEpD,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AACxF,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAEpF,OAAO,EACL,YAAY,EACZ,sBAAsB,EACtB,oBAAoB,EACpB,YAAY,EACZ,OAAO,EACP,aAAa,EACb,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,cAAc,EACd,cAAc,EACd,SAAS,GACV,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/op/activities/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,YAAY,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAElD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AACvF,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAEpD,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AACxF,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAEpF,OAAO,EACL,YAAY,EACZ,sBAAsB,EACtB,oBAAoB,EACpB,YAAY,EACZ,OAAO,EACP,aAAa,EACb,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,cAAc,EACd,cAAc,EACd,SAAS,GACV,MAAM,kBAAkB,CAAC;AAW1B,OAAO,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AACrG,YAAY,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC"}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
export interface KubectlApplyArgs {
|
|
2
2
|
manifest: string;
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* kubectl context name. Uses the ambient context if omitted. To target the
|
|
5
|
+
* same cluster the read path (`describeResources`) resolved for an
|
|
6
|
+
* environment — chant #1100 — resolve it with `resolveClusterTarget` from
|
|
7
|
+
* `./index.ts` and pass `.context` through.
|
|
8
|
+
*/
|
|
4
9
|
context?: string;
|
|
5
10
|
}
|
|
6
11
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kubectl.d.ts","sourceRoot":"","sources":["../../../src/op/activities/kubectl.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB
|
|
1
|
+
{"version":3,"file":"kubectl.d.ts","sourceRoot":"","sources":["../../../src/op/activities/kubectl.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB9F"}
|
|
@@ -65,7 +65,12 @@ export interface WaitForReadyArgs {
|
|
|
65
65
|
name: string;
|
|
66
66
|
/** Namespace (omit for cluster-scoped). */
|
|
67
67
|
namespace?: string;
|
|
68
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* kubectl context. To target the same cluster the read path
|
|
70
|
+
* (`describeResources`) resolved for an environment — chant #1100 —
|
|
71
|
+
* resolve it with `resolveClusterTarget` from `./index.ts` and pass
|
|
72
|
+
* `.context` through.
|
|
73
|
+
*/
|
|
69
74
|
context?: string;
|
|
70
75
|
/** API group, used to pick a readiness override when `spec` is not given. */
|
|
71
76
|
group?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wait-for-ready.d.ts","sourceRoot":"","sources":["../../../src/op/activities/wait-for-ready.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;GAUG;AAIH,sEAAsE;AACtE,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,2DAA2D;AAC3D,MAAM,WAAW,SAAS;IACxB,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACnC,4CAA4C;IAC5C,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;CAEhC;AAED,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG,SAAS,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,kDAAkD;IAClD,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;IAC5B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,aAG/B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAS7D,CAAC;AAEF,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,CAEnF;AAwCD,qFAAqF;AACrF,wBAAgB,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAGlE;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,GAAG,cAAc,GAAG,SAAS,CAE3F;AAID,6EAA6E;AAC7E,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,gGAAgG;IAChG,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB
|
|
1
|
+
{"version":3,"file":"wait-for-ready.d.ts","sourceRoot":"","sources":["../../../src/op/activities/wait-for-ready.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;GAUG;AAIH,sEAAsE;AACtE,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,2DAA2D;AAC3D,MAAM,WAAW,SAAS;IACxB,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACnC,4CAA4C;IAC5C,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;CAEhC;AAED,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG,SAAS,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,kDAAkD;IAClD,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;IAC5B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,aAG/B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAS7D,CAAC;AAEF,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,CAEnF;AAwCD,qFAAqF;AACrF,wBAAgB,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAGlE;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,GAAG,cAAc,GAAG,SAAS,CAE3F;AAID,6EAA6E;AAC7E,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,gGAAgG;IAChG,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,mEAAmE;AACnE,MAAM,MAAM,eAAe,GAAG,CAC5B,IAAI,EAAE,gBAAgB,EACtB,MAAM,CAAC,EAAE,WAAW,KACjB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAUtC,eAAO,MAAM,sBAAsB,EAAE,eAAiE,CAAC;AAEvG;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,gBAAgB,EACtB,MAAM,CAAC,EAAE,WAAW,EACpB,OAAO,GAAE,eAAwC,GAChD,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAuBlC"}
|
|
@@ -127,13 +127,28 @@ For Pod Identity, no K8s-side composite is needed — configure the association
|
|
|
127
127
|
|
|
128
128
|
## Karpenter
|
|
129
129
|
|
|
130
|
-
Karpenter replaces Cluster Autoscaler for node provisioning. Karpenter NodePool and EC2NodeClass are simple CRDs —
|
|
130
|
+
Karpenter replaces Cluster Autoscaler for node provisioning. Karpenter's `NodePool` and `EC2NodeClass` are simple CRDs — add them to the k8s lexicon's CRD source list rather than importing per-project. There is no `chant import --url` flag; CRD-from-URL is lexicon-authoring-time codegen, done once in `lexicons/k8s/src/crd/crd-sources.ts`, not a per-project command:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// lexicons/k8s/src/crd/crd-sources.ts
|
|
134
|
+
const KARPENTER_VERSION = "v1.0.0"; // pin an operator release, not `main`
|
|
135
|
+
const KARPENTER_CRD_BASE = `https://raw.githubusercontent.com/aws/karpenter-provider-aws/${KARPENTER_VERSION}/pkg/apis/crds`;
|
|
136
|
+
|
|
137
|
+
export const CRD_SOURCES: CRDSource[] = [
|
|
138
|
+
// ...existing sources
|
|
139
|
+
{ type: "url", url: `${KARPENTER_CRD_BASE}/karpenter.sh_nodepools.yaml` },
|
|
140
|
+
{ type: "url", url: `${KARPENTER_CRD_BASE}/karpenter.k8s.aws_ec2nodeclasses.yaml` },
|
|
141
|
+
];
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Then regenerate the lexicon so the new kinds are typed:
|
|
131
145
|
|
|
132
146
|
```bash
|
|
133
|
-
|
|
134
|
-
chant import --url https://raw.githubusercontent.com/aws/karpenter/main/pkg/apis/crds/karpenter.sh_nodepools.yaml
|
|
147
|
+
npm run generate -w @intentius/chant-lexicon-k8s
|
|
135
148
|
```
|
|
136
149
|
|
|
150
|
+
See [Add a Third-Party CRD](/chant/lexicon-authoring/crd-sources/) for the full workflow (namespace mapping, verifying the generated type, adding rules).
|
|
151
|
+
|
|
137
152
|
## Fargate Considerations
|
|
138
153
|
|
|
139
154
|
When running on EKS Fargate:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-k8s",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "Kubernetes lexicon for chant — declarative IaC in TypeScript",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://intentius.io/chant",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"typescript": "^5.9.3"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
|
-
"@intentius/chant": "^0.
|
|
78
|
+
"@intentius/chant": "^0.28.0",
|
|
79
79
|
"typescript": "^5.9.3"
|
|
80
80
|
}
|
|
81
81
|
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
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
|
+
* Temporal looks up `temporal.profiles.<env>`. 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 as `kubectl ... --context <bound>`
|
|
33
|
+
* on every invocation, and checked against the ambient context first — a
|
|
34
|
+
* mismatch refuses loudly (naming the environment, the expected context,
|
|
35
|
+
* and the ambient one) instead of silently reading the wrong cluster.
|
|
36
|
+
* - No binding for the environment keeps today's behavior — the ambient
|
|
37
|
+
* context — but logs a visible note that nothing is pinned, so the
|
|
38
|
+
* fallback is never silent.
|
|
39
|
+
*
|
|
40
|
+
* `ChantConfig` uses `.passthrough()` in its Zod schema so the `k8s` key is
|
|
41
|
+
* accepted at runtime without core changes, exactly like `temporal.profiles`
|
|
42
|
+
* (see `lexicons/temporal/src/config.ts`).
|
|
43
|
+
*
|
|
44
|
+
* Deliberately out of scope here (see the issue for the full proposal):
|
|
45
|
+
* deriving the binding automatically from a declared `AWS::EKS::Cluster` /
|
|
46
|
+
* AKS / GKE cluster resource, and threading the resolved context into the
|
|
47
|
+
* Op write path (`op/activities/kubectl.ts`, `wait-for-ready.ts`) — both
|
|
48
|
+
* already accept an optional `context` a workflow author can pass, and can
|
|
49
|
+
* call `resolveClusterTarget` themselves to agree with what the read path
|
|
50
|
+
* resolved. This also keeps the binding available for #1073/#1074's future
|
|
51
|
+
* typed API client to inherit, rather than re-deriving it.
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/** A single environment's cluster binding. */
|
|
55
|
+
export interface K8sClusterProfile {
|
|
56
|
+
/** kubectl context name this environment is bound to. */
|
|
57
|
+
context: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface K8sChantConfig {
|
|
61
|
+
/** Named environment → cluster bindings, keyed by environment name. */
|
|
62
|
+
profiles?: Record<string, K8sClusterProfile>;
|
|
63
|
+
}
|
|
@@ -11,6 +11,11 @@ vi.mock("node:child_process", async () => {
|
|
|
11
11
|
} };
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
+
const loadChantConfigMock = vi.fn();
|
|
15
|
+
vi.mock("@intentius/chant/config", () => ({
|
|
16
|
+
loadChantConfig: (...args: unknown[]) => loadChantConfigMock(...args),
|
|
17
|
+
}));
|
|
18
|
+
|
|
14
19
|
const { describeResources } = await import("./describe-resources");
|
|
15
20
|
|
|
16
21
|
function makeEntities(records: Array<{ name: string; entityType: string; props: Record<string, unknown> }>) {
|
|
@@ -20,6 +25,10 @@ function makeEntities(records: Array<{ name: string; entityType: string; props:
|
|
|
20
25
|
describe("k8s describeResources", () => {
|
|
21
26
|
beforeEach(() => {
|
|
22
27
|
execMock.mockReset();
|
|
28
|
+
loadChantConfigMock.mockReset();
|
|
29
|
+
// No binding declared by default — matches every test below except the
|
|
30
|
+
// dedicated cluster-binding tests, which override this per case.
|
|
31
|
+
loadChantConfigMock.mockResolvedValue({ config: {} });
|
|
23
32
|
});
|
|
24
33
|
|
|
25
34
|
test("queries kubectl for each declared K8s entity and maps response to ResourceMetadata", async () => {
|
|
@@ -86,7 +95,8 @@ describe("k8s describeResources", () => {
|
|
|
86
95
|
|
|
87
96
|
expect(result).toEqual({});
|
|
88
97
|
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("kubectl mapping"));
|
|
89
|
-
|
|
98
|
+
const mappingWarning = warnSpy.mock.calls.find((c) => String(c[0]).includes("kubectl mapping"));
|
|
99
|
+
expect(mappingWarning?.[0]).toContain("K8s::Custom::CRD::SomeOperator");
|
|
90
100
|
warnSpy.mockRestore();
|
|
91
101
|
});
|
|
92
102
|
|
|
@@ -159,4 +169,72 @@ describe("k8s describeResources", () => {
|
|
|
159
169
|
expect(result).toEqual({});
|
|
160
170
|
expect(execMock).not.toHaveBeenCalled();
|
|
161
171
|
});
|
|
172
|
+
|
|
173
|
+
// chant #1100 — environment→cluster binding: bound-and-matching,
|
|
174
|
+
// bound-and-mismatched (loud refusal), and unbound (unchanged) paths.
|
|
175
|
+
describe("cluster binding (chant #1100)", () => {
|
|
176
|
+
function deploymentEntities() {
|
|
177
|
+
return makeEntities([
|
|
178
|
+
{ name: "web", entityType: "K8s::Apps::Deployment", props: { metadata: { name: "web", namespace: "prod" } } },
|
|
179
|
+
]);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const deploymentStdout = JSON.stringify({
|
|
183
|
+
metadata: { name: "web", namespace: "prod", uid: "uid-1", creationTimestamp: "2026-05-01T00:00:00Z" },
|
|
184
|
+
status: { readyReplicas: 1, replicas: 1 },
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("bound and ambient context matches: observes explicitly via --context", async () => {
|
|
188
|
+
loadChantConfigMock.mockResolvedValue({ config: { k8s: { profiles: { prod: { context: "prod-eks" } } } } });
|
|
189
|
+
let receivedCmd = "";
|
|
190
|
+
execMock.mockImplementation((cmd: string) => {
|
|
191
|
+
if (cmd.includes("current-context")) return { stdout: "prod-eks\n", stderr: "" };
|
|
192
|
+
receivedCmd = cmd;
|
|
193
|
+
return { stdout: deploymentStdout, stderr: "" };
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const result = await describeResources({ environment: "prod", buildOutput: "", entityNames: ["web"], entities: deploymentEntities() });
|
|
197
|
+
|
|
198
|
+
expect(receivedCmd).toContain("--context prod-eks");
|
|
199
|
+
expect(result["web"]).toMatchObject({ type: "K8s::Apps::Deployment", physicalId: "uid-1", status: "READY" });
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("bound and ambient context mismatches: refuses loudly instead of observing the wrong cluster", async () => {
|
|
203
|
+
loadChantConfigMock.mockResolvedValue({ config: { k8s: { profiles: { prod: { context: "prod-eks" } } } } });
|
|
204
|
+
execMock.mockImplementation((cmd: string) => {
|
|
205
|
+
if (cmd.includes("current-context")) return { stdout: "staging-eks\n", stderr: "" };
|
|
206
|
+
throw new Error(`unexpected cmd (should have refused before any kubectl get): ${cmd}`);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
await expect(
|
|
210
|
+
describeResources({ environment: "prod", buildOutput: "", entityNames: ["web"], entities: deploymentEntities() }),
|
|
211
|
+
).rejects.toThrow(/environment "prod".*"prod-eks".*"staging-eks"/s);
|
|
212
|
+
|
|
213
|
+
// Only the ambient-context probe ran — no per-entity kubectl get was attempted.
|
|
214
|
+
expect(execMock).toHaveBeenCalledTimes(1);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test("unbound: ambient context is used unchanged, but the fallback is visible (not silent)", async () => {
|
|
218
|
+
loadChantConfigMock.mockResolvedValue({ config: {} });
|
|
219
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
220
|
+
let receivedCmd = "";
|
|
221
|
+
execMock.mockImplementation((cmd: string) => {
|
|
222
|
+
receivedCmd = cmd;
|
|
223
|
+
return { stdout: deploymentStdout, stderr: "" };
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const result = await describeResources({ environment: "prod", buildOutput: "", entityNames: ["web"], entities: deploymentEntities() });
|
|
227
|
+
|
|
228
|
+
// Unchanged: no ambient-context probe, no --context flag, same result shape as today.
|
|
229
|
+
expect(receivedCmd).not.toContain("--context");
|
|
230
|
+
expect(receivedCmd).not.toContain("current-context");
|
|
231
|
+
expect(result["web"]).toMatchObject({ type: "K8s::Apps::Deployment", physicalId: "uid-1", status: "READY" });
|
|
232
|
+
|
|
233
|
+
// Visible, not silent: a warning names the environment and the missing binding.
|
|
234
|
+
const bindingWarning = warnSpy.mock.calls.find((c) => String(c[0]).includes("no cluster binding"));
|
|
235
|
+
expect(bindingWarning?.[0]).toContain('environment "prod"');
|
|
236
|
+
expect(bindingWarning?.[0]).toContain("k8s.profiles.prod.context");
|
|
237
|
+
warnSpy.mockRestore();
|
|
238
|
+
});
|
|
239
|
+
});
|
|
162
240
|
});
|
|
@@ -10,12 +10,22 @@
|
|
|
10
10
|
* Resource-not-found is silent — `state diff --live` then reports it as
|
|
11
11
|
* missing (declared, not in cloud). Unknown entity types are warn-skipped;
|
|
12
12
|
* extending the KUBECTL_RESOURCE map covers more.
|
|
13
|
+
*
|
|
14
|
+
* Before touching any resource, the environment is resolved to a cluster
|
|
15
|
+
* identity (chant #1100) via `resolveClusterTarget` — see `./config.ts` for
|
|
16
|
+
* the `k8s.profiles.<env>.context` binding shape. A declared binding is
|
|
17
|
+
* passed explicitly as `--context` on every kubectl call below; an ambient
|
|
18
|
+
* context that disagrees with it aborts the whole describe with a loud
|
|
19
|
+
* error rather than silently reading the wrong cluster. No binding keeps
|
|
20
|
+
* today's behavior (ambient context), with a visible warning.
|
|
13
21
|
*/
|
|
14
22
|
|
|
15
23
|
import { exec } from "node:child_process";
|
|
16
24
|
import { promisify } from "node:util";
|
|
17
25
|
import type { ResourceMetadata } from "@intentius/chant/lexicon";
|
|
18
26
|
import { hasOwnershipMarker, classifyOwnership, LABEL_OWNERSHIP_KEYS } from "@intentius/chant/ownership";
|
|
27
|
+
import { loadChantConfig } from "@intentius/chant/config";
|
|
28
|
+
import { resolveClusterTarget } from "@intentius/chant/kubectl-context";
|
|
19
29
|
|
|
20
30
|
const execAsync = promisify(exec);
|
|
21
31
|
|
|
@@ -91,6 +101,14 @@ export async function describeResources(options: {
|
|
|
91
101
|
const result: Record<string, ResourceMetadata> = {};
|
|
92
102
|
const skippedTypes = new Set<string>();
|
|
93
103
|
|
|
104
|
+
// Resolve the cluster identity for this environment before touching any
|
|
105
|
+
// resource — a declared-but-mismatched binding throws here, aborting the
|
|
106
|
+
// whole describe rather than letting a per-entity try/catch below absorb
|
|
107
|
+
// it as an ordinary "not found".
|
|
108
|
+
const { config } = await loadChantConfig(process.cwd());
|
|
109
|
+
const target = await resolveClusterTarget(config as Record<string, unknown>, options.environment, "k8s");
|
|
110
|
+
const ctxArg = target.context ? ["--context", target.context] : [];
|
|
111
|
+
|
|
94
112
|
for (const [entityName, { entityType, props }] of options.entities) {
|
|
95
113
|
const kubectlResource = KUBECTL_RESOURCE[entityType];
|
|
96
114
|
if (!kubectlResource) {
|
|
@@ -103,7 +121,7 @@ export async function describeResources(options: {
|
|
|
103
121
|
if (!name) continue;
|
|
104
122
|
|
|
105
123
|
const nsArg = metadata.namespace ? ["-n", metadata.namespace] : [];
|
|
106
|
-
const cmd = ["kubectl", "get", kubectlResource, name, ...nsArg, "-o", "json"].join(" ");
|
|
124
|
+
const cmd = ["kubectl", "get", kubectlResource, name, ...nsArg, ...ctxArg, "-o", "json"].join(" ");
|
|
107
125
|
|
|
108
126
|
try {
|
|
109
127
|
const { stdout } = await execAsync(cmd);
|
package/src/index.ts
CHANGED
|
@@ -85,3 +85,6 @@ export type { PackageOptions, PackageResult } from "./codegen/package";
|
|
|
85
85
|
export type { CRDSource, CRDSpec } from "./crd/types";
|
|
86
86
|
export { parseCRD, parseCRDSpec } from "./crd/parser";
|
|
87
87
|
export { loadCRDs, loadMultipleCRDs } from "./crd/loader";
|
|
88
|
+
|
|
89
|
+
// Environment → cluster binding config shape (chant #1100)
|
|
90
|
+
export type { K8sChantConfig, K8sClusterProfile } from "./config";
|
|
@@ -38,3 +38,15 @@ export type {
|
|
|
38
38
|
ConditionMatch,
|
|
39
39
|
PathMatch,
|
|
40
40
|
} from "./wait-for-ready";
|
|
41
|
+
|
|
42
|
+
// Environment→cluster binding (chant #1100) — the same resolver
|
|
43
|
+
// `describeResources` (the read path) uses. `kubectlApply` and `waitForReady`
|
|
44
|
+
// above already accept an optional `context`; a workflow author who wants the
|
|
45
|
+
// write path to target the environment's bound cluster (rather than an
|
|
46
|
+
// ambient one, closing the read/write split) resolves it here and passes the
|
|
47
|
+
// result through, e.g. `kubectlApply({ manifest, context: (await
|
|
48
|
+
// resolveClusterTarget(config, environment, "k8s")).context })`. Re-exported
|
|
49
|
+
// from core rather than duplicated so both paths agree on one source of
|
|
50
|
+
// truth — see `lexicons/k8s/src/config.ts` for the config shape.
|
|
51
|
+
export { resolveClusterTarget, ClusterBindingMismatchError } from "@intentius/chant/kubectl-context";
|
|
52
|
+
export type { ResolvedClusterTarget, K8sClusterProfile, K8sConfigShape } from "@intentius/chant/kubectl-context";
|
|
@@ -6,7 +6,12 @@ const execAsync = promisify(exec);
|
|
|
6
6
|
|
|
7
7
|
export interface KubectlApplyArgs {
|
|
8
8
|
manifest: string;
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* kubectl context name. Uses the ambient context if omitted. To target the
|
|
11
|
+
* same cluster the read path (`describeResources`) resolved for an
|
|
12
|
+
* environment — chant #1100 — resolve it with `resolveClusterTarget` from
|
|
13
|
+
* `./index.ts` and pass `.context` through.
|
|
14
|
+
*/
|
|
10
15
|
context?: string;
|
|
11
16
|
}
|
|
12
17
|
|
|
@@ -147,7 +147,12 @@ export interface WaitForReadyArgs {
|
|
|
147
147
|
name: string;
|
|
148
148
|
/** Namespace (omit for cluster-scoped). */
|
|
149
149
|
namespace?: string;
|
|
150
|
-
/**
|
|
150
|
+
/**
|
|
151
|
+
* kubectl context. To target the same cluster the read path
|
|
152
|
+
* (`describeResources`) resolved for an environment — chant #1100 —
|
|
153
|
+
* resolve it with `resolveClusterTarget` from `./index.ts` and pass
|
|
154
|
+
* `.context` through.
|
|
155
|
+
*/
|
|
151
156
|
context?: string;
|
|
152
157
|
/** API group, used to pick a readiness override when `spec` is not given. */
|
|
153
158
|
group?: string;
|
|
@@ -127,13 +127,28 @@ For Pod Identity, no K8s-side composite is needed — configure the association
|
|
|
127
127
|
|
|
128
128
|
## Karpenter
|
|
129
129
|
|
|
130
|
-
Karpenter replaces Cluster Autoscaler for node provisioning. Karpenter NodePool and EC2NodeClass are simple CRDs —
|
|
130
|
+
Karpenter replaces Cluster Autoscaler for node provisioning. Karpenter's `NodePool` and `EC2NodeClass` are simple CRDs — add them to the k8s lexicon's CRD source list rather than importing per-project. There is no `chant import --url` flag; CRD-from-URL is lexicon-authoring-time codegen, done once in `lexicons/k8s/src/crd/crd-sources.ts`, not a per-project command:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// lexicons/k8s/src/crd/crd-sources.ts
|
|
134
|
+
const KARPENTER_VERSION = "v1.0.0"; // pin an operator release, not `main`
|
|
135
|
+
const KARPENTER_CRD_BASE = `https://raw.githubusercontent.com/aws/karpenter-provider-aws/${KARPENTER_VERSION}/pkg/apis/crds`;
|
|
136
|
+
|
|
137
|
+
export const CRD_SOURCES: CRDSource[] = [
|
|
138
|
+
// ...existing sources
|
|
139
|
+
{ type: "url", url: `${KARPENTER_CRD_BASE}/karpenter.sh_nodepools.yaml` },
|
|
140
|
+
{ type: "url", url: `${KARPENTER_CRD_BASE}/karpenter.k8s.aws_ec2nodeclasses.yaml` },
|
|
141
|
+
];
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Then regenerate the lexicon so the new kinds are typed:
|
|
131
145
|
|
|
132
146
|
```bash
|
|
133
|
-
|
|
134
|
-
chant import --url https://raw.githubusercontent.com/aws/karpenter/main/pkg/apis/crds/karpenter.sh_nodepools.yaml
|
|
147
|
+
npm run generate -w @intentius/chant-lexicon-k8s
|
|
135
148
|
```
|
|
136
149
|
|
|
150
|
+
See [Add a Third-Party CRD](/chant/lexicon-authoring/crd-sources/) for the full workflow (namespace mapping, verifying the generated type, adding rules).
|
|
151
|
+
|
|
137
152
|
## Fargate Considerations
|
|
138
153
|
|
|
139
154
|
When running on EKS Fargate:
|