@intentius/chant-lexicon-k8s 0.44.3 → 0.44.4
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/crd/parser.d.ts.map +1 -1
- package/dist/group-namespace.d.ts +46 -0
- package/dist/group-namespace.d.ts.map +1 -0
- package/dist/import/parser.d.ts.map +1 -1
- package/dist/integrity.json +2 -2
- package/dist/manifest.json +1 -1
- package/dist/spec/parse.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/crd/parser.ts +2 -67
- package/src/describe-resources.test.ts +1 -1
- package/src/group-namespace.test.ts +64 -0
- package/src/group-namespace.ts +79 -0
- package/src/import/parser.ts +7 -6
- package/src/kustomize/root.test.ts +3 -1
- package/src/spec/parse.ts +6 -14
package/dist/crd/parser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/crd/parser.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAwD,MAAM,eAAe,CAAC;AAC1G,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/crd/parser.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAwD,MAAM,eAAe,CAAC;AAC1G,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAKvC;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,EAAE,CAqB1D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,EAAE,CAoD5D"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API group → `K8s::{Namespace}::{Kind}` namespace segment — the ONE copy.
|
|
3
|
+
*
|
|
4
|
+
* Four surfaces build entityTypes from a group string: CRD codegen
|
|
5
|
+
* (`crd/parser.ts`), the swagger pass (`spec/parse.ts`), live discovery
|
|
6
|
+
* (`describe-resources.ts` via `gvkToTypeName`), and YAML import
|
|
7
|
+
* (`import/parser.ts`). Until #1628 each had its own copy of the rule and only
|
|
8
|
+
* CRD codegen consulted the overrides, so a Flux Kustomization arriving through
|
|
9
|
+
* a kustomize root or `chant import` was typed `K8s::Kustomize::Kustomization`
|
|
10
|
+
* — a name that matches nothing in operations.json (NOT-OBSERVED live) and
|
|
11
|
+
* nothing downstream that dispatches on kind. Every path resolves here now;
|
|
12
|
+
* the declared, rendered, imported and discovered spellings of a kind cannot
|
|
13
|
+
* skew.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Group names whose first segment doesn't yield the conventional namespace.
|
|
17
|
+
* "argoproj.io" → "Argo" (not "Argoproj") to match the Argo CD vocabulary
|
|
18
|
+
* and the ArgoAppFor / ArgoAppSetForRegions composites.
|
|
19
|
+
*
|
|
20
|
+
* The Flux toolkit spreads across five `*.toolkit.fluxcd.io` groups plus the
|
|
21
|
+
* Flux Operator's `fluxcd.controlplane.io`; all six collapse to a single `Flux`
|
|
22
|
+
* namespace so a GitRepository and a Kustomization read as `K8s::Flux::*`
|
|
23
|
+
* siblings rather than scattering to Source / Kustomize / Helm / Notification /
|
|
24
|
+
* Image / Fluxcd. (`helm.toolkit.fluxcd.io` → `Helm` would also collide
|
|
25
|
+
* confusingly with the separate helm lexicon.)
|
|
26
|
+
*
|
|
27
|
+
* KubeMicroVM's `lambda.aws.amazon.com` would take `Lambda` by the
|
|
28
|
+
* first-segment rule, which reads as AWS Lambda proper and would sit
|
|
29
|
+
* confusingly beside the aws lexicon's real Lambda functions. These are
|
|
30
|
+
* Kubernetes CRs belonging to a community operator, so they take the
|
|
31
|
+
* operator's own name. `MicroVM` alone was the alternative and was rejected
|
|
32
|
+
* for stuttering: `K8s::MicroVM::MicroVM`.
|
|
33
|
+
*
|
|
34
|
+
* Note the official AWS controller uses a different group,
|
|
35
|
+
* `lambdamicrovms.services.k8s.aws`, so it can coexist here and will want its
|
|
36
|
+
* own entry rather than sharing this one.
|
|
37
|
+
*/
|
|
38
|
+
export declare const GROUP_NAMESPACE_OVERRIDES: Record<string, string>;
|
|
39
|
+
/**
|
|
40
|
+
* Normalize an API group to its PascalCase namespace segment.
|
|
41
|
+
* "" → "Core", overrides first ("argoproj.io" → "Argo"), then the first
|
|
42
|
+
* dot-segment kebab→PascalCased: "cert-manager.io" → "CertManager",
|
|
43
|
+
* "rbac.authorization.k8s.io" → "Rbac", "apps" → "Apps".
|
|
44
|
+
*/
|
|
45
|
+
export declare function namespaceSegmentForGroup(group: string): string;
|
|
46
|
+
//# sourceMappingURL=group-namespace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"group-namespace.d.ts","sourceRoot":"","sources":["../src/group-namespace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAuB5D,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAS9D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/import/parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAc,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/import/parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAc,MAAM,gCAAgC,CAAC;AAyH7F;;;;;GAKG;AACH,qBAAa,SAAU,YAAW,cAAc;IAC9C,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU;IAsClC,OAAO,CAAC,aAAa;CA8BtB"}
|
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "994f303cfb6edefd9d0eb5d18406f784172b3fb0bd5b524e7e3255a96f4bf338",
|
|
5
5
|
"meta.json": "f568fb8a0cbd4adf041baccaa3e1164c2e3c5c0090186236f887f0084f5521a8",
|
|
6
6
|
"types/index.d.ts": "ef2c9df6a4d1ebfbe33f96c094fbbe3fc7909522ea38e086666fbf3c8ac477fd",
|
|
7
7
|
"rules/argo-appset-single-project.ts": "afa9f310753aa2d475f35012b13135b2dfaebed2a35d44edbe185ebc07673674",
|
|
@@ -54,5 +54,5 @@
|
|
|
54
54
|
"skills/chant-k8s-argo.md": "b1a0b826559d8c5033a479c5781efaf650320f0aee4419d8841170bd3393cea5",
|
|
55
55
|
"skills/chant-k8s-flux.md": "9670efc2e9854bd14686b5adeb707396b9030a4dcaee6529b30abf56efe2c50c"
|
|
56
56
|
},
|
|
57
|
-
"composite": "
|
|
57
|
+
"composite": "815ab02fe7f7325c6fbec07be9f9f8423df8d9771ddd23babef1af5ae60c0acd"
|
|
58
58
|
}
|
package/dist/manifest.json
CHANGED
package/dist/spec/parse.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/spec/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/spec/parse.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-k8s",
|
|
3
|
-
"version": "0.44.
|
|
3
|
+
"version": "0.44.4",
|
|
4
4
|
"description": "Kubernetes lexicon for chant — declarative IaC in TypeScript",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://intentius.io/chant",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"@types/js-yaml": "^4.0.9"
|
|
77
77
|
},
|
|
78
78
|
"optionalDependencies": {
|
|
79
|
-
"@intentius/chant-k8s-client": "^0.44.
|
|
79
|
+
"@intentius/chant-k8s-client": "^0.44.4"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
82
|
"@intentius/chant": "*",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
},
|
|
85
85
|
"peerDependencies": {
|
|
86
86
|
"zod": "^4.3.6",
|
|
87
|
-
"@intentius/chant": "^0.44.
|
|
87
|
+
"@intentius/chant": "^0.44.4",
|
|
88
88
|
"typescript": "^5.9.3"
|
|
89
89
|
}
|
|
90
90
|
}
|
package/src/crd/parser.ts
CHANGED
|
@@ -9,75 +9,10 @@
|
|
|
9
9
|
|
|
10
10
|
import type { K8sParseResult, ParsedProperty, ParsedPropertyType, GroupVersionKind } from "../spec/parse";
|
|
11
11
|
import type { CRDSpec } from "./types";
|
|
12
|
+
import { namespaceSegmentForGroup } from "../group-namespace";
|
|
12
13
|
import type { PropertyConstraints } from "@intentius/chant/codegen/json-schema";
|
|
13
14
|
import { loadAll } from "js-yaml";
|
|
14
15
|
|
|
15
|
-
/**
|
|
16
|
-
* Group names whose first segment doesn't yield the conventional namespace.
|
|
17
|
-
* "argoproj.io" → "Argo" (not "Argoproj") to match the Argo CD vocabulary
|
|
18
|
-
* and the ArgoAppFor / ArgoAppSetForRegions composites.
|
|
19
|
-
*
|
|
20
|
-
* The Flux toolkit spreads across five `*.toolkit.fluxcd.io` groups plus the
|
|
21
|
-
* Flux Operator's `fluxcd.controlplane.io`; all six collapse to a single `Flux`
|
|
22
|
-
* namespace so a GitRepository and a Kustomization read as `K8s::Flux::*`
|
|
23
|
-
* siblings rather than scattering to Source / Kustomize / Helm / Notification /
|
|
24
|
-
* Image / Fluxcd. (`helm.toolkit.fluxcd.io` → `Helm` would also collide
|
|
25
|
-
* confusingly with the separate helm lexicon.)
|
|
26
|
-
*
|
|
27
|
-
* KubeMicroVM's `lambda.aws.amazon.com` would take `Lambda` by the
|
|
28
|
-
* first-segment rule, which reads as AWS Lambda proper and would sit
|
|
29
|
-
* confusingly beside the aws lexicon's real Lambda functions. These are
|
|
30
|
-
* Kubernetes CRs belonging to a community operator, so they take the
|
|
31
|
-
* operator's own name. `MicroVM` alone was the alternative and was rejected
|
|
32
|
-
* for stuttering: `K8s::MicroVM::MicroVM`.
|
|
33
|
-
*
|
|
34
|
-
* Note the official AWS controller uses a different group,
|
|
35
|
-
* `lambdamicrovms.services.k8s.aws`, so it can coexist here and will want its
|
|
36
|
-
* own entry rather than sharing this one.
|
|
37
|
-
*/
|
|
38
|
-
const GROUP_NAMESPACE_OVERRIDES: Record<string, string> = {
|
|
39
|
-
"argoproj.io": "Argo",
|
|
40
|
-
"source.toolkit.fluxcd.io": "Flux",
|
|
41
|
-
"kustomize.toolkit.fluxcd.io": "Flux",
|
|
42
|
-
"helm.toolkit.fluxcd.io": "Flux",
|
|
43
|
-
"notification.toolkit.fluxcd.io": "Flux",
|
|
44
|
-
"image.toolkit.fluxcd.io": "Flux",
|
|
45
|
-
"fluxcd.controlplane.io": "Flux",
|
|
46
|
-
"lambda.aws.amazon.com": "KubeMicroVM",
|
|
47
|
-
// CNPG and its barman-cloud plugin ship under two groups but are one thing to
|
|
48
|
-
// an author: a Cluster's `plugins[]` names an ObjectStore. The first-segment
|
|
49
|
-
// rule would scatter them into `Postgresql` and `Barmancloud`.
|
|
50
|
-
"postgresql.cnpg.io": "Cnpg",
|
|
51
|
-
"barmancloud.cnpg.io": "Cnpg",
|
|
52
|
-
// Not `Secrets`: `K8s::Secrets::InfisicalSecret` reads like a core Secret,
|
|
53
|
-
// and `K8s::Core::Secret` is right there to be confused with.
|
|
54
|
-
"secrets.infisical.com": "Infisical",
|
|
55
|
-
// k3s's bundled controllers ship under two groups but are one thing to an
|
|
56
|
-
// author — the k3s auto-deploy surface. The first-segment rule would give
|
|
57
|
-
// `K8s::Helm::HelmChart`, which reads like it belongs to the helm lexicon,
|
|
58
|
-
// and would split HelmChart from the Addon that tracks its deployment.
|
|
59
|
-
"helm.cattle.io": "K3s",
|
|
60
|
-
"k3s.cattle.io": "K3s",
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Normalize a CRD group to a PascalCase namespace segment.
|
|
65
|
-
* "cert-manager.io" → "CertManager"
|
|
66
|
-
* "monitoring.coreos.com" → "Monitoring"
|
|
67
|
-
* "argoproj.io" → "Argo" (override)
|
|
68
|
-
*/
|
|
69
|
-
function normalizeGroupName(group: string): string {
|
|
70
|
-
const override = GROUP_NAMESPACE_OVERRIDES[group];
|
|
71
|
-
if (override) return override;
|
|
72
|
-
// Take the first segment before the first dot
|
|
73
|
-
const firstSegment = group.split(".")[0];
|
|
74
|
-
// Convert kebab-case to PascalCase
|
|
75
|
-
return firstSegment
|
|
76
|
-
.split("-")
|
|
77
|
-
.map((s) => s.charAt(0).toUpperCase() + s.slice(1))
|
|
78
|
-
.join("");
|
|
79
|
-
}
|
|
80
|
-
|
|
81
16
|
/**
|
|
82
17
|
* Parse a CRD YAML document string into K8sParseResult entries.
|
|
83
18
|
* Returns one result per served version in the CRD.
|
|
@@ -111,7 +46,7 @@ export function parseCRD(content: string): K8sParseResult[] {
|
|
|
111
46
|
*/
|
|
112
47
|
export function parseCRDSpec(spec: CRDSpec): K8sParseResult[] {
|
|
113
48
|
const results: K8sParseResult[] = [];
|
|
114
|
-
const groupNs =
|
|
49
|
+
const groupNs = namespaceSegmentForGroup(spec.group);
|
|
115
50
|
|
|
116
51
|
// Find the storage version (the canonical version)
|
|
117
52
|
const storageVersion = spec.versions.find((v) => v.storage && v.served);
|
|
@@ -731,7 +731,7 @@ describe("k8s describeResources", () => {
|
|
|
731
731
|
);
|
|
732
732
|
|
|
733
733
|
expect(result.resources["microvm-demo/kmv-dev-a-vm-42sgf"]).toMatchObject({
|
|
734
|
-
type: "K8s::
|
|
734
|
+
type: "K8s::KubeMicroVM::MicroVM",
|
|
735
735
|
physicalId: "vm-uid",
|
|
736
736
|
ownerChain: { root: "declared", entity: "workloadReplicaSet" },
|
|
737
737
|
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
|
+
import { namespaceSegmentForGroup } from "./group-namespace";
|
|
3
|
+
import { renderedManifestEntity } from "./kustomize/rendered-entity";
|
|
4
|
+
import { K8sParser } from "./import/parser";
|
|
5
|
+
|
|
6
|
+
describe("namespaceSegmentForGroup", () => {
|
|
7
|
+
test("the empty core group is Core", () => {
|
|
8
|
+
expect(namespaceSegmentForGroup("")).toBe("Core");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("a single-segment group PascalCases", () => {
|
|
12
|
+
expect(namespaceSegmentForGroup("apps")).toBe("Apps");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("only the first dot-segment counts", () => {
|
|
16
|
+
expect(namespaceSegmentForGroup("rbac.authorization.k8s.io")).toBe("Rbac");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("a hyphenated first segment kebab→Pascals", () => {
|
|
20
|
+
expect(namespaceSegmentForGroup("cert-manager.io")).toBe("CertManager");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("argoproj.io takes the Argo override", () => {
|
|
24
|
+
expect(namespaceSegmentForGroup("argoproj.io")).toBe("Argo");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("the toolkit groups collapse to Flux", () => {
|
|
28
|
+
expect(namespaceSegmentForGroup("kustomize.toolkit.fluxcd.io")).toBe("Flux");
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// #1628: rendered and imported entityTypes used to bypass the overrides, so a
|
|
33
|
+
// Flux CR arriving through a kustomize root or `chant import` was typed to a
|
|
34
|
+
// name that matches nothing in operations.json.
|
|
35
|
+
describe("the shared rule reaches the render and import paths", () => {
|
|
36
|
+
test("a rendered Flux Kustomization is typed K8s::Flux::Kustomization", () => {
|
|
37
|
+
const entity = renderedManifestEntity(
|
|
38
|
+
{
|
|
39
|
+
apiVersion: "kustomize.toolkit.fluxcd.io/v1",
|
|
40
|
+
kind: "Kustomization",
|
|
41
|
+
metadata: { name: "apps", namespace: "flux-system" },
|
|
42
|
+
spec: { path: "./apps/prod", prune: true },
|
|
43
|
+
},
|
|
44
|
+
"clusters/prod",
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
expect(entity?.entityType).toBe("K8s::Flux::Kustomization");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("an imported Argo Application is typed K8s::Argo::Application", () => {
|
|
51
|
+
const ir = new K8sParser().parse(`
|
|
52
|
+
apiVersion: argoproj.io/v1alpha1
|
|
53
|
+
kind: Application
|
|
54
|
+
metadata:
|
|
55
|
+
name: guestbook
|
|
56
|
+
namespace: argocd
|
|
57
|
+
spec:
|
|
58
|
+
project: default
|
|
59
|
+
`);
|
|
60
|
+
|
|
61
|
+
expect(ir.resources.length).toBe(1);
|
|
62
|
+
expect(ir.resources[0].type).toBe("K8s::Argo::Application");
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API group → `K8s::{Namespace}::{Kind}` namespace segment — the ONE copy.
|
|
3
|
+
*
|
|
4
|
+
* Four surfaces build entityTypes from a group string: CRD codegen
|
|
5
|
+
* (`crd/parser.ts`), the swagger pass (`spec/parse.ts`), live discovery
|
|
6
|
+
* (`describe-resources.ts` via `gvkToTypeName`), and YAML import
|
|
7
|
+
* (`import/parser.ts`). Until #1628 each had its own copy of the rule and only
|
|
8
|
+
* CRD codegen consulted the overrides, so a Flux Kustomization arriving through
|
|
9
|
+
* a kustomize root or `chant import` was typed `K8s::Kustomize::Kustomization`
|
|
10
|
+
* — a name that matches nothing in operations.json (NOT-OBSERVED live) and
|
|
11
|
+
* nothing downstream that dispatches on kind. Every path resolves here now;
|
|
12
|
+
* the declared, rendered, imported and discovered spellings of a kind cannot
|
|
13
|
+
* skew.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Group names whose first segment doesn't yield the conventional namespace.
|
|
18
|
+
* "argoproj.io" → "Argo" (not "Argoproj") to match the Argo CD vocabulary
|
|
19
|
+
* and the ArgoAppFor / ArgoAppSetForRegions composites.
|
|
20
|
+
*
|
|
21
|
+
* The Flux toolkit spreads across five `*.toolkit.fluxcd.io` groups plus the
|
|
22
|
+
* Flux Operator's `fluxcd.controlplane.io`; all six collapse to a single `Flux`
|
|
23
|
+
* namespace so a GitRepository and a Kustomization read as `K8s::Flux::*`
|
|
24
|
+
* siblings rather than scattering to Source / Kustomize / Helm / Notification /
|
|
25
|
+
* Image / Fluxcd. (`helm.toolkit.fluxcd.io` → `Helm` would also collide
|
|
26
|
+
* confusingly with the separate helm lexicon.)
|
|
27
|
+
*
|
|
28
|
+
* KubeMicroVM's `lambda.aws.amazon.com` would take `Lambda` by the
|
|
29
|
+
* first-segment rule, which reads as AWS Lambda proper and would sit
|
|
30
|
+
* confusingly beside the aws lexicon's real Lambda functions. These are
|
|
31
|
+
* Kubernetes CRs belonging to a community operator, so they take the
|
|
32
|
+
* operator's own name. `MicroVM` alone was the alternative and was rejected
|
|
33
|
+
* for stuttering: `K8s::MicroVM::MicroVM`.
|
|
34
|
+
*
|
|
35
|
+
* Note the official AWS controller uses a different group,
|
|
36
|
+
* `lambdamicrovms.services.k8s.aws`, so it can coexist here and will want its
|
|
37
|
+
* own entry rather than sharing this one.
|
|
38
|
+
*/
|
|
39
|
+
export const GROUP_NAMESPACE_OVERRIDES: Record<string, string> = {
|
|
40
|
+
"argoproj.io": "Argo",
|
|
41
|
+
"source.toolkit.fluxcd.io": "Flux",
|
|
42
|
+
"kustomize.toolkit.fluxcd.io": "Flux",
|
|
43
|
+
"helm.toolkit.fluxcd.io": "Flux",
|
|
44
|
+
"notification.toolkit.fluxcd.io": "Flux",
|
|
45
|
+
"image.toolkit.fluxcd.io": "Flux",
|
|
46
|
+
"fluxcd.controlplane.io": "Flux",
|
|
47
|
+
"lambda.aws.amazon.com": "KubeMicroVM",
|
|
48
|
+
// CNPG and its barman-cloud plugin ship under two groups but are one thing to
|
|
49
|
+
// an author: a Cluster's `plugins[]` names an ObjectStore. The first-segment
|
|
50
|
+
// rule would scatter them into `Postgresql` and `Barmancloud`.
|
|
51
|
+
"postgresql.cnpg.io": "Cnpg",
|
|
52
|
+
"barmancloud.cnpg.io": "Cnpg",
|
|
53
|
+
// Not `Secrets`: `K8s::Secrets::InfisicalSecret` reads like a core Secret,
|
|
54
|
+
// and `K8s::Core::Secret` is right there to be confused with.
|
|
55
|
+
"secrets.infisical.com": "Infisical",
|
|
56
|
+
// k3s's bundled controllers ship under two groups but are one thing to an
|
|
57
|
+
// author — the k3s auto-deploy surface. The first-segment rule would give
|
|
58
|
+
// `K8s::Helm::HelmChart`, which reads like it belongs to the helm lexicon,
|
|
59
|
+
// and would split HelmChart from the Addon that tracks its deployment.
|
|
60
|
+
"helm.cattle.io": "K3s",
|
|
61
|
+
"k3s.cattle.io": "K3s",
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Normalize an API group to its PascalCase namespace segment.
|
|
66
|
+
* "" → "Core", overrides first ("argoproj.io" → "Argo"), then the first
|
|
67
|
+
* dot-segment kebab→PascalCased: "cert-manager.io" → "CertManager",
|
|
68
|
+
* "rbac.authorization.k8s.io" → "Rbac", "apps" → "Apps".
|
|
69
|
+
*/
|
|
70
|
+
export function namespaceSegmentForGroup(group: string): string {
|
|
71
|
+
if (!group || group === "") return "Core";
|
|
72
|
+
const override = GROUP_NAMESPACE_OVERRIDES[group];
|
|
73
|
+
if (override) return override;
|
|
74
|
+
const firstSegment = group.split(".")[0];
|
|
75
|
+
return firstSegment
|
|
76
|
+
.split("-")
|
|
77
|
+
.map((s) => s.charAt(0).toUpperCase() + s.slice(1))
|
|
78
|
+
.join("");
|
|
79
|
+
}
|
package/src/import/parser.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import type { TemplateParser, TemplateIR, ResourceIR } from "@intentius/chant/import/parser";
|
|
10
10
|
import { parseYAML } from "@intentius/chant/yaml";
|
|
11
|
+
import { namespaceSegmentForGroup } from "../group-namespace";
|
|
11
12
|
|
|
12
13
|
// ── GVK to type name mapping ───────────────────────────────────────
|
|
13
14
|
|
|
@@ -91,20 +92,20 @@ function resolveTypeName(apiVersion: string, kind: string): string {
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
/**
|
|
94
|
-
* Extract
|
|
95
|
+
* Extract the namespace segment from an apiVersion string.
|
|
95
96
|
* "apps/v1" → "Apps"
|
|
96
97
|
* "v1" → "Core"
|
|
97
|
-
* "networking.k8s.io/v1" → "Networking"
|
|
98
98
|
* "rbac.authorization.k8s.io/v1" → "Rbac"
|
|
99
|
+
* "argoproj.io/v1alpha1" → "Argo" (override)
|
|
100
|
+
*
|
|
101
|
+
* Splits the version off and hands the group to the shared rule, so an
|
|
102
|
+
* imported manifest is typed exactly as a declared one is.
|
|
99
103
|
*/
|
|
100
104
|
function apiVersionToGroup(apiVersion: string): string {
|
|
101
105
|
const slashIdx = apiVersion.indexOf("/");
|
|
102
106
|
if (slashIdx === -1) return "Core"; // core group (v1)
|
|
103
107
|
|
|
104
|
-
|
|
105
|
-
const firstSegment = groupStr.split(".")[0];
|
|
106
|
-
if (firstSegment === "rbac") return "Rbac";
|
|
107
|
-
return firstSegment.charAt(0).toUpperCase() + firstSegment.slice(1);
|
|
108
|
+
return namespaceSegmentForGroup(apiVersion.slice(0, slashIdx));
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
/**
|
|
@@ -94,7 +94,9 @@ describe("renderKustomizeRoots", () => {
|
|
|
94
94
|
// The generated operation surface's naming, so `lifecycle diff --live`
|
|
95
95
|
// observes a rendered Deployment through the same reader a declared one uses.
|
|
96
96
|
expect(deployment.entityType).toBe("K8s::Apps::Deployment");
|
|
97
|
-
|
|
97
|
+
// Hyphenated groups PascalCase the same way the declarable surface does
|
|
98
|
+
// (#1628), so this Certificate matches the reader a declared one gets.
|
|
99
|
+
expect(entities.get("overlays/prod/certificateProdWebTls")!.entityType).toBe("K8s::CertManager::Certificate");
|
|
98
100
|
|
|
99
101
|
const props = (deployment as unknown as { props: Record<string, unknown> }).props;
|
|
100
102
|
const metadata = props.metadata as { annotations: Record<string, string> };
|
package/src/spec/parse.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
primaryType,
|
|
14
14
|
type JsonSchemaProperty,
|
|
15
15
|
} from "@intentius/chant/codegen/json-schema";
|
|
16
|
+
import { namespaceSegmentForGroup } from "../group-namespace";
|
|
16
17
|
|
|
17
18
|
// ── Types ──────────────────────────────────────────────────────────
|
|
18
19
|
|
|
@@ -343,22 +344,13 @@ export function gvkToApiVersion(gvk: GroupVersionKind): string {
|
|
|
343
344
|
}
|
|
344
345
|
|
|
345
346
|
/**
|
|
346
|
-
* Normalize API group to a PascalCase segment
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
* "rbac.authorization.k8s.io" → "Rbac"
|
|
351
|
-
* "networking.k8s.io" → "Networking"
|
|
347
|
+
* Normalize API group to a PascalCase segment — the shared rule, so the
|
|
348
|
+
* swagger-generated surface, kustomize-rendered entities and live discovery
|
|
349
|
+
* (all three reach here through `gvkToTypeName`) spell a kind exactly as CRD
|
|
350
|
+
* codegen and `chant import` do. See `../group-namespace`.
|
|
352
351
|
*/
|
|
353
352
|
function normalizeGroup(group: string): string {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
// Take the first segment before any dots
|
|
357
|
-
const firstSegment = group.split(".")[0];
|
|
358
|
-
// Special-case RBAC
|
|
359
|
-
if (firstSegment === "rbac") return "Rbac";
|
|
360
|
-
// PascalCase the first segment
|
|
361
|
-
return firstSegment.charAt(0).toUpperCase() + firstSegment.slice(1);
|
|
353
|
+
return namespaceSegmentForGroup(group);
|
|
362
354
|
}
|
|
363
355
|
|
|
364
356
|
/**
|