@intentius/chant-lexicon-k8s 0.37.2 → 0.38.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.
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Infisical acceptance test.
3
+ *
4
+ * Transcribed from fountain's `k8s/infisicalsecret.yaml`. Every field in that
5
+ * manifest is policy — where the secret comes from, who may fetch it, what
6
+ * happens to the materialized Secret when the CR goes away. None of it is a
7
+ * value, which is why this kind is expressible in chant at all.
8
+ */
9
+
10
+ import { describe, test, expect } from "vitest";
11
+ import { InfisicalSecret } from "../generated";
12
+ import { k8sSerializer } from "../serializer";
13
+ import { parseYAML } from "@intentius/chant/yaml";
14
+
15
+ function synth(logicalName: string, resource: unknown): any {
16
+ const yaml = k8sSerializer.serialize(new Map([[logicalName, resource as never]]));
17
+ return parseYAML(yaml);
18
+ }
19
+
20
+ const fountainSecrets = new InfisicalSecret({
21
+ metadata: { name: "fountain-secrets", namespace: "fountain", labels: { app: "fountain" } },
22
+ spec: {
23
+ hostAPI: "http://infisical.infisical.svc.cluster.local:8080",
24
+ resyncInterval: 60,
25
+ authentication: {
26
+ kubernetesAuth: {
27
+ identityId: "6ccf748c-7fe1-4584-a206-b0a56d84530c",
28
+ autoCreateServiceAccountToken: true,
29
+ serviceAccountRef: { name: "fountain-infisical", namespace: "fountain" },
30
+ secretsScope: { projectSlug: "fountain-q-cwt", envSlug: "prod", secretsPath: "/" },
31
+ },
32
+ },
33
+ managedSecretReference: {
34
+ secretName: "fountain-secrets",
35
+ secretNamespace: "fountain",
36
+ creationPolicy: "Orphan",
37
+ },
38
+ },
39
+ });
40
+
41
+ describe("InfisicalSecret", () => {
42
+ test("carries the Infisical apiVersion and kind", () => {
43
+ const doc = synth("fountainSecrets", fountainSecrets);
44
+ expect(doc.apiVersion).toBe("secrets.infisical.com/v1alpha1");
45
+ expect(doc.kind).toBe("InfisicalSecret");
46
+ expect(doc.metadata.name).toBe("fountain-secrets");
47
+ });
48
+
49
+ test("reproduces fountain's infisicalsecret.yaml", () => {
50
+ const { spec } = synth("fountainSecrets", fountainSecrets);
51
+ expect(spec.hostAPI).toBe("http://infisical.infisical.svc.cluster.local:8080");
52
+ expect(spec.resyncInterval).toBe(60);
53
+ expect(spec.managedSecretReference).toEqual({
54
+ secretName: "fountain-secrets",
55
+ secretNamespace: "fountain",
56
+ creationPolicy: "Orphan",
57
+ });
58
+ });
59
+
60
+ test("serializes the whole kubernetesAuth block", () => {
61
+ const { spec } = synth("fountainSecrets", fountainSecrets);
62
+ // Kubernetes-native auth: the point is that no credential is stored
63
+ // anywhere. The operator presents a ServiceAccount token instead, so
64
+ // dropping any part of this block is what turns the deployment back into
65
+ // one that needs a secret to fetch its secrets.
66
+ expect(spec.authentication.kubernetesAuth).toEqual({
67
+ identityId: "6ccf748c-7fe1-4584-a206-b0a56d84530c",
68
+ autoCreateServiceAccountToken: true,
69
+ serviceAccountRef: { name: "fountain-infisical", namespace: "fountain" },
70
+ secretsScope: { projectSlug: "fountain-q-cwt", envSlug: "prod", secretsPath: "/" },
71
+ });
72
+ });
73
+
74
+ test("declares a source and never a value", () => {
75
+ // The reason this CRD fits chant at all: synthesis resolves nothing here.
76
+ // If a secret value could reach the manifest, the manifest would stop
77
+ // being safe to commit -- so assert on the serialized text, not the props.
78
+ const yaml = k8sSerializer.serialize(
79
+ new Map([["fountainSecrets", fountainSecrets as never]]),
80
+ );
81
+ expect(yaml).not.toMatch(/\bvalue\s*:/);
82
+ expect(yaml).not.toMatch(/\bdata\s*:/);
83
+ expect(yaml).not.toMatch(/\bstringData\s*:/);
84
+ });
85
+
86
+ test("keeps resyncInterval a number", () => {
87
+ const { spec } = synth("fountainSecrets", fountainSecrets);
88
+ // Quoted, the operator rejects the CR rather than defaulting -- worth
89
+ // pinning because seconds-as-a-string is a habit from other tools.
90
+ expect(typeof spec.resyncInterval).toBe("number");
91
+ });
92
+
93
+ test("creationPolicy Orphan survives, since the two policies differ on deletion", () => {
94
+ const { spec } = synth("fountainSecrets", fountainSecrets);
95
+ // Orphan leaves the materialized Secret behind when the CR is deleted;
96
+ // Owner takes it with it. Silently flipping to the default would make CR
97
+ // deletion destructive.
98
+ expect(spec.managedSecretReference.creationPolicy).toBe("Orphan");
99
+ });
100
+ });
package/src/crd/parser.ts CHANGED
@@ -44,6 +44,14 @@ const GROUP_NAMESPACE_OVERRIDES: Record<string, string> = {
44
44
  "image.toolkit.fluxcd.io": "Flux",
45
45
  "fluxcd.controlplane.io": "Flux",
46
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",
47
55
  };
48
56
 
49
57
  /**
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Traefik acceptance test.
3
+ *
4
+ * Transcribed from fountain's `k8s/ingressroute.yaml` — the websecure route
5
+ * with `tls.secretName`, and the web route whose middleware lives in another
6
+ * namespace. The cross-namespace reference is the reason this file exists: it
7
+ * is one extra key, it is easy to leave off, and leaving it off produces a
8
+ * route that applies cleanly and drops traffic.
9
+ */
10
+
11
+ import { describe, test, expect } from "vitest";
12
+ import { IngressRoute, Middleware } from "../generated";
13
+ import { k8sSerializer } from "../serializer";
14
+ import { parseYAML } from "@intentius/chant/yaml";
15
+
16
+ function synth(logicalName: string, resource: unknown): any {
17
+ const yaml = k8sSerializer.serialize(new Map([[logicalName, resource as never]]));
18
+ return parseYAML(yaml);
19
+ }
20
+
21
+ describe("Traefik IngressRoute", () => {
22
+ const secure = new IngressRoute({
23
+ metadata: { name: "fountain", namespace: "fountain", labels: { app: "fountain" } },
24
+ spec: {
25
+ entryPoints: ["websecure"],
26
+ routes: [
27
+ {
28
+ match: "Host(`fountain.inevitable.fyi`)",
29
+ kind: "Rule",
30
+ services: [{ name: "fountain", port: 80 }],
31
+ },
32
+ ],
33
+ tls: { secretName: "fountain-tls" },
34
+ },
35
+ });
36
+
37
+ const plain = new IngressRoute({
38
+ metadata: { name: "fountain-http", namespace: "fountain", labels: { app: "fountain" } },
39
+ spec: {
40
+ entryPoints: ["web"],
41
+ routes: [
42
+ {
43
+ match: "Host(`fountain.inevitable.fyi`)",
44
+ kind: "Rule",
45
+ middlewares: [{ name: "redirect-https", namespace: "default" }],
46
+ services: [{ name: "fountain", port: 80 }],
47
+ },
48
+ ],
49
+ },
50
+ });
51
+
52
+ test("carries the Traefik apiVersion and kind", () => {
53
+ const doc = synth("fountain", secure);
54
+ expect(doc.apiVersion).toBe("traefik.io/v1alpha1");
55
+ expect(doc.kind).toBe("IngressRoute");
56
+ });
57
+
58
+ test("reproduces the websecure route", () => {
59
+ const { spec } = synth("fountain", secure);
60
+ expect(spec.entryPoints).toEqual(["websecure"]);
61
+ expect(spec.tls).toEqual({ secretName: "fountain-tls" });
62
+ expect(spec.routes).toHaveLength(1);
63
+ expect(spec.routes[0].kind).toBe("Rule");
64
+ expect(spec.routes[0].services).toEqual([{ name: "fountain", port: 80 }]);
65
+ });
66
+
67
+ test("preserves the matcher grammar verbatim", () => {
68
+ const { spec } = synth("fountain", secure);
69
+ // Traefik's matcher is its own language, and the backticks around the host
70
+ // are part of it -- not YAML quoting. Anything that strips or re-quotes
71
+ // them produces a rule that parses and matches nothing.
72
+ expect(spec.routes[0].match).toBe("Host(`fountain.inevitable.fyi`)");
73
+ expect(spec.routes[0].match).toContain("`fountain.inevitable.fyi`");
74
+ });
75
+
76
+ test("keeps the namespace on a cross-namespace middleware ref", () => {
77
+ const { spec } = synth("fountainHttp", plain);
78
+ // `redirect-https` lives in `default`, the route in `fountain`. Without the
79
+ // qualifier Traefik looks in the route's own namespace, finds nothing, and
80
+ // serves the route with no redirect -- so plain HTTP quietly keeps working
81
+ // instead of upgrading.
82
+ expect(spec.routes[0].middlewares).toEqual([
83
+ { name: "redirect-https", namespace: "default" },
84
+ ]);
85
+ });
86
+
87
+ test("a route with no TLS omits the key rather than emptying it", () => {
88
+ const { spec } = synth("fountainHttp", plain);
89
+ expect(spec.tls).toBeUndefined();
90
+ expect(spec.entryPoints).toEqual(["web"]);
91
+ });
92
+ });
93
+
94
+ describe("Traefik Middleware", () => {
95
+ test("serializes the redirect the routes point at", () => {
96
+ const doc = synth(
97
+ "redirectHttps",
98
+ new Middleware({
99
+ metadata: { name: "redirect-https", namespace: "default" },
100
+ spec: { redirectScheme: { scheme: "https", permanent: true } },
101
+ }),
102
+ );
103
+ expect(doc.apiVersion).toBe("traefik.io/v1alpha1");
104
+ expect(doc.kind).toBe("Middleware");
105
+ expect(doc.metadata.namespace).toBe("default");
106
+ expect(doc.spec.redirectScheme).toEqual({ scheme: "https", permanent: true });
107
+ });
108
+ });
@@ -128,6 +128,20 @@ export declare class AppProject {
128
128
  readonly uid: string;
129
129
  }
130
130
 
131
+ export declare class Backup {
132
+ constructor(props: {
133
+ /** Specification of the desired behavior of the backup.
134
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status */
135
+ spec: Record<string, unknown>;
136
+ /** Standard object's metadata. */
137
+ metadata?: ObjectMeta;
138
+ });
139
+ readonly name: string;
140
+ readonly namespace: string;
141
+ readonly status: Record<string, unknown>;
142
+ readonly uid: string;
143
+ }
144
+
131
145
  export declare class BatchJob {
132
146
  constructor(props: {
133
147
  /** Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata */
@@ -228,6 +242,20 @@ export declare class Challenge {
228
242
  readonly uid: string;
229
243
  }
230
244
 
245
+ export declare class Cluster {
246
+ constructor(props: {
247
+ /** Specification of the desired behavior of the cluster.
248
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status */
249
+ spec: Record<string, unknown>;
250
+ /** Standard object's metadata. */
251
+ metadata?: ObjectMeta;
252
+ });
253
+ readonly name: string;
254
+ readonly namespace: string;
255
+ readonly status: Record<string, unknown>;
256
+ readonly uid: string;
257
+ }
258
+
231
259
  export declare class ClusterIssuer {
232
260
  constructor(props: {
233
261
  /** Desired state of the ClusterIssuer resource. */
@@ -1101,6 +1129,45 @@ export declare class ImageUpdateAutomation {
1101
1129
  readonly uid: string;
1102
1130
  }
1103
1131
 
1132
+ export declare class InfisicalDynamicSecret {
1133
+ constructor(props: {
1134
+ /** Standard object's metadata. */
1135
+ metadata?: ObjectMeta;
1136
+ /** InfisicalDynamicSecretSpec defines the desired state of InfisicalDynamicSecret. */
1137
+ spec?: Record<string, unknown>;
1138
+ });
1139
+ readonly name: string;
1140
+ readonly namespace: string;
1141
+ readonly status: Record<string, unknown>;
1142
+ readonly uid: string;
1143
+ }
1144
+
1145
+ export declare class InfisicalPushSecret {
1146
+ constructor(props: {
1147
+ /** Standard object's metadata. */
1148
+ metadata?: ObjectMeta;
1149
+ /** InfisicalPushSecretSpec defines the desired state of InfisicalPushSecret */
1150
+ spec?: Record<string, unknown>;
1151
+ });
1152
+ readonly name: string;
1153
+ readonly namespace: string;
1154
+ readonly status: Record<string, unknown>;
1155
+ readonly uid: string;
1156
+ }
1157
+
1158
+ export declare class InfisicalSecret {
1159
+ constructor(props: {
1160
+ /** Standard object's metadata. */
1161
+ metadata?: ObjectMeta;
1162
+ /** InfisicalSecretSpec defines the desired state of InfisicalSecret */
1163
+ spec?: Record<string, unknown>;
1164
+ });
1165
+ readonly name: string;
1166
+ readonly namespace: string;
1167
+ readonly status: Record<string, unknown>;
1168
+ readonly uid: string;
1169
+ }
1170
+
1104
1171
  export declare class Ing {
1105
1172
  constructor(props: {
1106
1173
  /** Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata */
@@ -1161,6 +1228,42 @@ export declare class IngressList {
1161
1228
  readonly uid: string;
1162
1229
  }
1163
1230
 
1231
+ export declare class IngressRoute {
1232
+ constructor(props: {
1233
+ /** IngressRouteSpec defines the desired state of IngressRoute. */
1234
+ spec: Record<string, unknown>;
1235
+ /** Standard object's metadata. */
1236
+ metadata?: ObjectMeta;
1237
+ });
1238
+ readonly name: string;
1239
+ readonly namespace: string;
1240
+ readonly uid: string;
1241
+ }
1242
+
1243
+ export declare class IngressRouteTCP {
1244
+ constructor(props: {
1245
+ /** IngressRouteTCPSpec defines the desired state of IngressRouteTCP. */
1246
+ spec: Record<string, unknown>;
1247
+ /** Standard object's metadata. */
1248
+ metadata?: ObjectMeta;
1249
+ });
1250
+ readonly name: string;
1251
+ readonly namespace: string;
1252
+ readonly uid: string;
1253
+ }
1254
+
1255
+ export declare class IngressRouteUDP {
1256
+ constructor(props: {
1257
+ /** IngressRouteUDPSpec defines the desired state of a IngressRouteUDP. */
1258
+ spec: Record<string, unknown>;
1259
+ /** Standard object's metadata. */
1260
+ metadata?: ObjectMeta;
1261
+ });
1262
+ readonly name: string;
1263
+ readonly namespace: string;
1264
+ readonly uid: string;
1265
+ }
1266
+
1164
1267
  export declare class IPAddress {
1165
1268
  constructor(props: {
1166
1269
  /** spec is the desired state of the IPAddress. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status */
@@ -1379,6 +1482,30 @@ export declare class MicroVMReplicaSet {
1379
1482
  readonly uid: string;
1380
1483
  }
1381
1484
 
1485
+ export declare class Middleware {
1486
+ constructor(props: {
1487
+ /** MiddlewareSpec defines the desired state of a Middleware. */
1488
+ spec: Record<string, unknown>;
1489
+ /** Standard object's metadata. */
1490
+ metadata?: ObjectMeta;
1491
+ });
1492
+ readonly name: string;
1493
+ readonly namespace: string;
1494
+ readonly uid: string;
1495
+ }
1496
+
1497
+ export declare class MiddlewareTCP {
1498
+ constructor(props: {
1499
+ /** MiddlewareTCPSpec defines the desired state of a MiddlewareTCP. */
1500
+ spec: Record<string, unknown>;
1501
+ /** Standard object's metadata. */
1502
+ metadata?: ObjectMeta;
1503
+ });
1504
+ readonly name: string;
1505
+ readonly namespace: string;
1506
+ readonly uid: string;
1507
+ }
1508
+
1382
1509
  export declare class MutatingAdmissionPolicy {
1383
1510
  constructor(props: {
1384
1511
  /** metadata is the standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata. */
@@ -1547,6 +1674,20 @@ export declare class NS {
1547
1674
  readonly uid: string;
1548
1675
  }
1549
1676
 
1677
+ export declare class ObjectStore {
1678
+ constructor(props: {
1679
+ /** Specification of the desired behavior of the ObjectStore.
1680
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status */
1681
+ spec: Record<string, unknown>;
1682
+ /** Standard object's metadata. */
1683
+ metadata?: ObjectMeta;
1684
+ });
1685
+ readonly name: string;
1686
+ readonly namespace: string;
1687
+ readonly status: Record<string, unknown>;
1688
+ readonly uid: string;
1689
+ }
1690
+
1550
1691
  export declare class OCIRepository {
1551
1692
  constructor(props: {
1552
1693
  /** Standard object's metadata. */
@@ -1728,6 +1869,20 @@ export declare class PodTemplateList {
1728
1869
  readonly uid: string;
1729
1870
  }
1730
1871
 
1872
+ export declare class Pooler {
1873
+ constructor(props: {
1874
+ /** Specification of the desired behavior of the Pooler.
1875
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status */
1876
+ spec: Record<string, unknown>;
1877
+ /** Standard object's metadata. */
1878
+ metadata?: ObjectMeta;
1879
+ });
1880
+ readonly name: string;
1881
+ readonly namespace: string;
1882
+ readonly status: Record<string, unknown>;
1883
+ readonly uid: string;
1884
+ }
1885
+
1731
1886
  export declare class PriorityClass {
1732
1887
  constructor(props: {
1733
1888
  /** description is an arbitrary string that usually provides guidelines on when this priority class should be used. */
@@ -2172,6 +2327,20 @@ export declare class Scale {
2172
2327
  readonly uid: string;
2173
2328
  }
2174
2329
 
2330
+ export declare class ScheduledBackup {
2331
+ constructor(props: {
2332
+ /** Specification of the desired behavior of the ScheduledBackup.
2333
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status */
2334
+ spec: Record<string, unknown>;
2335
+ /** Standard object's metadata. */
2336
+ metadata?: ObjectMeta;
2337
+ });
2338
+ readonly name: string;
2339
+ readonly namespace: string;
2340
+ readonly status: Record<string, unknown>;
2341
+ readonly uid: string;
2342
+ }
2343
+
2175
2344
  export declare class Sec {
2176
2345
  constructor(props: {
2177
2346
  /** Data contains the secret data. Each key must consist of alphanumeric characters, '-', '_' or '.'. The serialized form of the secret data is a base64 encoded string, representing the arbitrary (possibly non-string) data value here. Described in https://tools.ietf.org/html/rfc4648#section-4 */
@@ -2254,6 +2423,30 @@ export declare class SelfSubjectRulesReview {
2254
2423
  readonly uid: string;
2255
2424
  }
2256
2425
 
2426
+ export declare class ServersTransport {
2427
+ constructor(props: {
2428
+ /** ServersTransportSpec defines the desired state of a ServersTransport. */
2429
+ spec: Record<string, unknown>;
2430
+ /** Standard object's metadata. */
2431
+ metadata?: ObjectMeta;
2432
+ });
2433
+ readonly name: string;
2434
+ readonly namespace: string;
2435
+ readonly uid: string;
2436
+ }
2437
+
2438
+ export declare class ServersTransportTCP {
2439
+ constructor(props: {
2440
+ /** ServersTransportTCPSpec defines the desired state of a ServersTransportTCP. */
2441
+ spec: Record<string, unknown>;
2442
+ /** Standard object's metadata. */
2443
+ metadata?: ObjectMeta;
2444
+ });
2445
+ readonly name: string;
2446
+ readonly namespace: string;
2447
+ readonly uid: string;
2448
+ }
2449
+
2257
2450
  export declare class Service {
2258
2451
  constructor(props: {
2259
2452
  /** Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata */
@@ -2469,6 +2662,30 @@ export declare class SubjectAccessReview {
2469
2662
  readonly uid: string;
2470
2663
  }
2471
2664
 
2665
+ export declare class TLSOption {
2666
+ constructor(props: {
2667
+ /** TLSOptionSpec defines the desired state of a TLSOption. */
2668
+ spec: Record<string, unknown>;
2669
+ /** Standard object's metadata. */
2670
+ metadata?: ObjectMeta;
2671
+ });
2672
+ readonly name: string;
2673
+ readonly namespace: string;
2674
+ readonly uid: string;
2675
+ }
2676
+
2677
+ export declare class TLSStore {
2678
+ constructor(props: {
2679
+ /** TLSStoreSpec defines the desired state of a TLSStore. */
2680
+ spec: Record<string, unknown>;
2681
+ /** Standard object's metadata. */
2682
+ metadata?: ObjectMeta;
2683
+ });
2684
+ readonly name: string;
2685
+ readonly namespace: string;
2686
+ readonly uid: string;
2687
+ }
2688
+
2472
2689
  export declare class TokenRequest {
2473
2690
  constructor(props: {
2474
2691
  /** metadata is the standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata */
@@ -2493,6 +2710,18 @@ export declare class TokenReview {
2493
2710
  readonly uid: string;
2494
2711
  }
2495
2712
 
2713
+ export declare class TraefikService {
2714
+ constructor(props: {
2715
+ /** TraefikServiceSpec defines the desired state of a TraefikService. */
2716
+ spec: Record<string, unknown>;
2717
+ /** Standard object's metadata. */
2718
+ metadata?: ObjectMeta;
2719
+ });
2720
+ readonly name: string;
2721
+ readonly namespace: string;
2722
+ readonly uid: string;
2723
+ }
2724
+
2496
2725
  export declare class ValidatingAdmissionPolicy {
2497
2726
  constructor(props: {
2498
2727
  /** metadata is the standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata. */