@intentius/chant-lexicon-k8s 0.44.10 → 0.44.13
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.
|
@@ -90,6 +90,28 @@ export declare function revisionAttributes(obj: K8sObject): {
|
|
|
90
90
|
lastAttemptedRevision?: string;
|
|
91
91
|
artifactRevision?: string;
|
|
92
92
|
};
|
|
93
|
+
/**
|
|
94
|
+
* The declared repository protocol of a Flux HelmRepository (behold#298).
|
|
95
|
+
*
|
|
96
|
+
* A HelmRepository with `spec.type: oci` is never reconciled by
|
|
97
|
+
* source-controller — no conditions, no artifact, by design — so a consumer
|
|
98
|
+
* classifying health off conditions reads a healthy OCI repository exactly
|
|
99
|
+
* like one whose controller has not shown up. `spec.type` is the field that
|
|
100
|
+
* separates the two, and no spec field reached the wire before this. It rides
|
|
101
|
+
* the attributes flattened, as `type`, the same way `artifactRevision`
|
|
102
|
+
* flattens `status.artifact.revision`: every scalar on the wire is a plain
|
|
103
|
+
* key next to the others, and the wire stays lean — one field, not the spec.
|
|
104
|
+
*
|
|
105
|
+
* Kind-gated, unlike the module's usual read-by-path rule, because the PATH
|
|
106
|
+
* is not distinctive: a core Service's `spec.type` is
|
|
107
|
+
* `ClusterIP`/`LoadBalancer`, and forwarding that would change the wire shape
|
|
108
|
+
* of a kind this was never about. Only source.toolkit.fluxcd.io's
|
|
109
|
+
* HelmRepository speaks this meaning of the field.
|
|
110
|
+
*/
|
|
111
|
+
export declare function helmRepositoryTypeAttribute(gvk: {
|
|
112
|
+
apiVersion: string;
|
|
113
|
+
kind: string;
|
|
114
|
+
}, obj: K8sObject): string | undefined;
|
|
93
115
|
export interface DescribeResourcesOptions {
|
|
94
116
|
environment: string;
|
|
95
117
|
buildOutput: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe-resources.d.ts","sourceRoot":"","sources":["../src/describe-resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAwD,MAAM,0BAA0B,CAAC;AAGxH,OAAO,KAAK,EAAa,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAmBvE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,CA2CvD;AA4FD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,EAAE,GAAG,SAAS,CAuBtE;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,SAAS,GAAG;IAClD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAUA;AAQD,MAAM,WAAW,wBAAwB;IACvC,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;IAChB,sFAAsF;IACtF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;;;;;;;;;;;OAiBG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,EACjC,OAAO,GAAE,YAAkC,GAC1C,OAAO,CAAC,iBAAiB,CAAC,
|
|
1
|
+
{"version":3,"file":"describe-resources.d.ts","sourceRoot":"","sources":["../src/describe-resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAwD,MAAM,0BAA0B,CAAC;AAGxH,OAAO,KAAK,EAAa,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAmBvE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,CA2CvD;AA4FD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,EAAE,GAAG,SAAS,CAuBtE;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,SAAS,GAAG;IAClD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAUA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACzC,GAAG,EAAE,SAAS,GACb,MAAM,GAAG,SAAS,CAIpB;AAQD,MAAM,WAAW,wBAAwB;IACvC,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;IAChB,sFAAsF;IACtF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;;;;;;;;;;;OAiBG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,EACjC,OAAO,GAAE,YAAkC,GAC1C,OAAO,CAAC,iBAAiB,CAAC,CAgK5B"}
|
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "b6c625f7af4e812f458d344b2a2a8bf8eccdb1508fbe9eaf7c42108bf5f8179e",
|
|
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": "e9afd4601ffb6af967ef1826619470ff515e865d6c66ce1ce68bb8b8a3bedb7d"
|
|
58
58
|
}
|
package/dist/manifest.json
CHANGED
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.13",
|
|
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.13"
|
|
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.13",
|
|
88
88
|
"typescript": "^5.9.3"
|
|
89
89
|
}
|
|
90
90
|
}
|
|
@@ -15,7 +15,7 @@ vi.mock("@intentius/chant/config", () => ({
|
|
|
15
15
|
loadChantConfig: (...args: unknown[]) => loadChantConfigMock(...args),
|
|
16
16
|
}));
|
|
17
17
|
|
|
18
|
-
const { describeResources, statusFromObject, unhappyConditions, revisionAttributes } = await import("./describe-resources");
|
|
18
|
+
const { describeResources, statusFromObject, unhappyConditions, revisionAttributes, helmRepositoryTypeAttribute } = await import("./describe-resources");
|
|
19
19
|
const { fakeCluster, objectKey, ownedObject } = await import("./api/fake-cluster");
|
|
20
20
|
const { defaultK8sConnector } = await import("./api/connect");
|
|
21
21
|
const { fakeKubeconfig, statusBody } = await import("@intentius/chant-k8s-client/testing");
|
|
@@ -1628,6 +1628,126 @@ describe("observed attributes carry the Flux revisions (#1632)", () => {
|
|
|
1628
1628
|
});
|
|
1629
1629
|
});
|
|
1630
1630
|
|
|
1631
|
+
describe("helmRepositoryTypeAttribute — the one spec field on the wire (behold#298)", () => {
|
|
1632
|
+
const gvk = { apiVersion: "source.toolkit.fluxcd.io/v1", kind: "HelmRepository" };
|
|
1633
|
+
|
|
1634
|
+
test("carries a HelmRepository's spec.type when set", () => {
|
|
1635
|
+
expect(helmRepositoryTypeAttribute(gvk, { spec: { type: "oci" } } as never)).toBe("oci");
|
|
1636
|
+
expect(helmRepositoryTypeAttribute(gvk, { spec: { type: "default" } } as never)).toBe("default");
|
|
1637
|
+
});
|
|
1638
|
+
|
|
1639
|
+
test("says nothing when the field is absent — the CRD does not default it", () => {
|
|
1640
|
+
expect(helmRepositoryTypeAttribute(gvk, { spec: { url: "https://charts.example.com" } } as never)).toBeUndefined();
|
|
1641
|
+
expect(helmRepositoryTypeAttribute(gvk, {} as never)).toBeUndefined();
|
|
1642
|
+
});
|
|
1643
|
+
|
|
1644
|
+
test("ignores non-string and empty values", () => {
|
|
1645
|
+
expect(helmRepositoryTypeAttribute(gvk, { spec: { type: "" } } as never)).toBeUndefined();
|
|
1646
|
+
expect(helmRepositoryTypeAttribute(gvk, { spec: { type: 7 } } as never)).toBeUndefined();
|
|
1647
|
+
});
|
|
1648
|
+
|
|
1649
|
+
test("declines every other kind — spec.type is not a distinctive path", () => {
|
|
1650
|
+
// A core Service HAS a spec.type; forwarding it would change a wire shape
|
|
1651
|
+
// this field was never about.
|
|
1652
|
+
expect(
|
|
1653
|
+
helmRepositoryTypeAttribute({ apiVersion: "v1", kind: "Service" }, { spec: { type: "LoadBalancer" } } as never),
|
|
1654
|
+
).toBeUndefined();
|
|
1655
|
+
// Same kind name in a foreign group claims nothing either.
|
|
1656
|
+
expect(
|
|
1657
|
+
helmRepositoryTypeAttribute({ apiVersion: "example.io/v1", kind: "HelmRepository" }, { spec: { type: "oci" } } as never),
|
|
1658
|
+
).toBeUndefined();
|
|
1659
|
+
});
|
|
1660
|
+
});
|
|
1661
|
+
|
|
1662
|
+
describe("observed attributes carry a HelmRepository's spec.type (behold#298)", () => {
|
|
1663
|
+
test("an OCI HelmRepository reads type: 'oci'; a default one grows no type key", async () => {
|
|
1664
|
+
// The behold#298 shape: `spec.type: oci` means source-controller never
|
|
1665
|
+
// reconciles the object — no conditions, no artifact, by design — so a
|
|
1666
|
+
// consumer classifying off conditions needs this field to tell a healthy
|
|
1667
|
+
// OCI repository from a wedged default one.
|
|
1668
|
+
const oci = {
|
|
1669
|
+
apiVersion: "source.toolkit.fluxcd.io/v1",
|
|
1670
|
+
kind: "HelmRepository",
|
|
1671
|
+
metadata: { name: "charts-oci", namespace: "flux-system", uid: "hrepo-oci-uid" },
|
|
1672
|
+
spec: { type: "oci", url: "oci://ghcr.io/example/charts" },
|
|
1673
|
+
// No status at all — exactly what the API returns for an OCI one.
|
|
1674
|
+
};
|
|
1675
|
+
const plain = {
|
|
1676
|
+
apiVersion: "source.toolkit.fluxcd.io/v1",
|
|
1677
|
+
kind: "HelmRepository",
|
|
1678
|
+
metadata: { name: "charts-http", namespace: "flux-system", uid: "hrepo-http-uid" },
|
|
1679
|
+
// The CRD does not default spec.type: a default repository simply
|
|
1680
|
+
// carries no `type` field, and the wire says nothing for it.
|
|
1681
|
+
spec: { url: "https://charts.example.com" },
|
|
1682
|
+
status: { conditions: [{ type: "Ready", status: "True" }], artifact: { revision: "sha256:abc123" } },
|
|
1683
|
+
};
|
|
1684
|
+
const cluster = fakeCluster({
|
|
1685
|
+
objects: {
|
|
1686
|
+
[objectKey("source.toolkit.fluxcd.io/v1", "HelmRepository", "charts-oci", "flux-system")]: oci,
|
|
1687
|
+
[objectKey("source.toolkit.fluxcd.io/v1", "HelmRepository", "charts-http", "flux-system")]: plain,
|
|
1688
|
+
},
|
|
1689
|
+
});
|
|
1690
|
+
|
|
1691
|
+
const result = await describeResources(
|
|
1692
|
+
{
|
|
1693
|
+
environment: "prod",
|
|
1694
|
+
buildOutput: "",
|
|
1695
|
+
entityNames: ["chartsOci", "chartsHttp"],
|
|
1696
|
+
entities: makeEntities([
|
|
1697
|
+
{
|
|
1698
|
+
name: "chartsOci",
|
|
1699
|
+
entityType: "K8s::Flux::HelmRepository",
|
|
1700
|
+
props: { metadata: { name: "charts-oci", namespace: "flux-system" }, spec: { type: "oci", url: "oci://ghcr.io/example/charts" } },
|
|
1701
|
+
},
|
|
1702
|
+
{
|
|
1703
|
+
name: "chartsHttp",
|
|
1704
|
+
entityType: "K8s::Flux::HelmRepository",
|
|
1705
|
+
props: { metadata: { name: "charts-http", namespace: "flux-system" }, spec: { url: "https://charts.example.com" } },
|
|
1706
|
+
},
|
|
1707
|
+
]),
|
|
1708
|
+
},
|
|
1709
|
+
cluster.connector,
|
|
1710
|
+
);
|
|
1711
|
+
|
|
1712
|
+
expect(result.resources.chartsOci?.attributes).toMatchObject({ type: "oci" });
|
|
1713
|
+
// No conditions is the healthy shape for OCI — nothing unhappy to carry.
|
|
1714
|
+
expect(result.resources.chartsOci?.attributes).not.toHaveProperty("conditions");
|
|
1715
|
+
// The default repository's attributes are byte-for-byte what they were:
|
|
1716
|
+
// no `type` key appears, and its revision still rides #1632's path.
|
|
1717
|
+
expect(result.resources.chartsHttp?.attributes).not.toHaveProperty("type");
|
|
1718
|
+
expect(result.resources.chartsHttp?.attributes).toMatchObject({ artifactRevision: "sha256:abc123" });
|
|
1719
|
+
});
|
|
1720
|
+
|
|
1721
|
+
test("no other kind grows a type attribute — a Service's spec.type stays off the wire", async () => {
|
|
1722
|
+
const lb = {
|
|
1723
|
+
apiVersion: "v1",
|
|
1724
|
+
kind: "Service",
|
|
1725
|
+
metadata: { name: "web-lb", namespace: "prod", uid: "lb-uid" },
|
|
1726
|
+
spec: { type: "LoadBalancer", ports: [{ port: 80 }] },
|
|
1727
|
+
};
|
|
1728
|
+
const cluster = fakeCluster({ objects: { [objectKey("v1", "Service", "web-lb", "prod")]: lb } });
|
|
1729
|
+
|
|
1730
|
+
const result = await describeResources(
|
|
1731
|
+
{
|
|
1732
|
+
environment: "prod",
|
|
1733
|
+
buildOutput: "",
|
|
1734
|
+
entityNames: ["webLb"],
|
|
1735
|
+
entities: makeEntities([
|
|
1736
|
+
{
|
|
1737
|
+
name: "webLb",
|
|
1738
|
+
entityType: "K8s::Core::Service",
|
|
1739
|
+
props: { metadata: { name: "web-lb", namespace: "prod" }, spec: { type: "LoadBalancer" } },
|
|
1740
|
+
},
|
|
1741
|
+
]),
|
|
1742
|
+
},
|
|
1743
|
+
cluster.connector,
|
|
1744
|
+
);
|
|
1745
|
+
|
|
1746
|
+
expect(result.resources.webLb).toBeDefined();
|
|
1747
|
+
expect(result.resources.webLb?.attributes).not.toHaveProperty("type");
|
|
1748
|
+
});
|
|
1749
|
+
});
|
|
1750
|
+
|
|
1631
1751
|
// The behold#221 shape (#1629): the estate's control-plane project declares
|
|
1632
1752
|
// the Kustomization carrying `spec.targetNamespace`, and the app project
|
|
1633
1753
|
// declares bare objects the controller stamps at apply time. Reading the app
|
|
@@ -280,6 +280,33 @@ export function revisionAttributes(obj: K8sObject): {
|
|
|
280
280
|
};
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
+
/**
|
|
284
|
+
* The declared repository protocol of a Flux HelmRepository (behold#298).
|
|
285
|
+
*
|
|
286
|
+
* A HelmRepository with `spec.type: oci` is never reconciled by
|
|
287
|
+
* source-controller — no conditions, no artifact, by design — so a consumer
|
|
288
|
+
* classifying health off conditions reads a healthy OCI repository exactly
|
|
289
|
+
* like one whose controller has not shown up. `spec.type` is the field that
|
|
290
|
+
* separates the two, and no spec field reached the wire before this. It rides
|
|
291
|
+
* the attributes flattened, as `type`, the same way `artifactRevision`
|
|
292
|
+
* flattens `status.artifact.revision`: every scalar on the wire is a plain
|
|
293
|
+
* key next to the others, and the wire stays lean — one field, not the spec.
|
|
294
|
+
*
|
|
295
|
+
* Kind-gated, unlike the module's usual read-by-path rule, because the PATH
|
|
296
|
+
* is not distinctive: a core Service's `spec.type` is
|
|
297
|
+
* `ClusterIP`/`LoadBalancer`, and forwarding that would change the wire shape
|
|
298
|
+
* of a kind this was never about. Only source.toolkit.fluxcd.io's
|
|
299
|
+
* HelmRepository speaks this meaning of the field.
|
|
300
|
+
*/
|
|
301
|
+
export function helmRepositoryTypeAttribute(
|
|
302
|
+
gvk: { apiVersion: string; kind: string },
|
|
303
|
+
obj: K8sObject,
|
|
304
|
+
): string | undefined {
|
|
305
|
+
if (gvk.kind !== "HelmRepository" || !gvk.apiVersion.startsWith("source.toolkit.fluxcd.io/")) return undefined;
|
|
306
|
+
const type = (obj.spec as { type?: unknown } | undefined)?.type;
|
|
307
|
+
return typeof type === "string" && type.length > 0 ? type : undefined;
|
|
308
|
+
}
|
|
309
|
+
|
|
283
310
|
interface Declared {
|
|
284
311
|
entityName: string;
|
|
285
312
|
entityType: string;
|
|
@@ -454,6 +481,9 @@ export async function describeResources(
|
|
|
454
481
|
// What a GitOps controller says it has applied and is applying
|
|
455
482
|
// (#1632) — the convergence signal conditions cannot carry.
|
|
456
483
|
...revisionAttributes(obj),
|
|
484
|
+
// A HelmRepository's spec.type — `oci` means "no conditions is
|
|
485
|
+
// healthy", and only this field can say so (behold#298).
|
|
486
|
+
type: helmRepositoryTypeAttribute(operation, obj),
|
|
457
487
|
}),
|
|
458
488
|
};
|
|
459
489
|
} catch (err) {
|
|
@@ -750,6 +780,10 @@ async function addRuntimeChildren(
|
|
|
750
780
|
labels: obj.metadata?.labels,
|
|
751
781
|
resourceVersion: obj.metadata?.resourceVersion,
|
|
752
782
|
conditions: unhappyConditions(obj),
|
|
783
|
+
// The same one spec field the declared read forwards (behold#298):
|
|
784
|
+
// a swept HelmRepository (Flux's own bootstrap manages them through
|
|
785
|
+
// a Kustomization) must not read differently from a declared one.
|
|
786
|
+
type: helmRepositoryTypeAttribute(swept, obj),
|
|
753
787
|
}),
|
|
754
788
|
};
|
|
755
789
|
});
|