@intentius/chant-lexicon-k8s 0.37.3 → 0.39.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/codegen/generate-list-map-keys.d.ts +53 -0
- package/dist/codegen/generate-list-map-keys.d.ts.map +1 -0
- package/dist/codegen/generate.d.ts.map +1 -1
- package/dist/composites/adot-collector.d.ts +7 -1
- package/dist/composites/adot-collector.d.ts.map +1 -1
- package/dist/composites/agic-ingress.d.ts +3 -1
- package/dist/composites/agic-ingress.d.ts.map +1 -1
- package/dist/composites/aks-external-dns-agent.d.ts +6 -1
- package/dist/composites/aks-external-dns-agent.d.ts.map +1 -1
- package/dist/composites/alb-ingress.d.ts +3 -1
- package/dist/composites/alb-ingress.d.ts.map +1 -1
- package/dist/composites/autoscaled-service.d.ts +6 -1
- package/dist/composites/autoscaled-service.d.ts.map +1 -1
- package/dist/composites/azure-disk-storage-class.d.ts +3 -1
- package/dist/composites/azure-disk-storage-class.d.ts.map +1 -1
- package/dist/composites/azure-file-storage-class.d.ts +3 -1
- package/dist/composites/azure-file-storage-class.d.ts.map +1 -1
- package/dist/composites/azure-monitor-collector.d.ts +7 -1
- package/dist/composites/azure-monitor-collector.d.ts.map +1 -1
- package/dist/composites/batch-job.d.ts +1 -1
- package/dist/composites/batch-job.d.ts.map +1 -1
- package/dist/composites/cockroachdb-cluster.d.ts +13 -1
- package/dist/composites/cockroachdb-cluster.d.ts.map +1 -1
- package/dist/composites/config-connector-context.d.ts +3 -1
- package/dist/composites/config-connector-context.d.ts.map +1 -1
- package/dist/composites/configured-app.d.ts +1 -1
- package/dist/composites/configured-app.d.ts.map +1 -1
- package/dist/composites/cron-workload.d.ts +6 -1
- package/dist/composites/cron-workload.d.ts.map +1 -1
- package/dist/composites/ebs-storage-class.d.ts +3 -1
- package/dist/composites/ebs-storage-class.d.ts.map +1 -1
- package/dist/composites/efs-storage-class.d.ts +3 -1
- package/dist/composites/efs-storage-class.d.ts.map +1 -1
- package/dist/composites/external-dns-agent.d.ts +6 -1
- package/dist/composites/external-dns-agent.d.ts.map +1 -1
- package/dist/composites/filestore-storage-class.d.ts +3 -1
- package/dist/composites/filestore-storage-class.d.ts.map +1 -1
- package/dist/composites/fluent-bit-agent.d.ts +7 -1
- package/dist/composites/fluent-bit-agent.d.ts.map +1 -1
- package/dist/composites/gce-ingress.d.ts +3 -1
- package/dist/composites/gce-ingress.d.ts.map +1 -1
- package/dist/composites/gce-pd-storage-class.d.ts +3 -1
- package/dist/composites/gce-pd-storage-class.d.ts.map +1 -1
- package/dist/composites/gke-external-dns-agent.d.ts +6 -1
- package/dist/composites/gke-external-dns-agent.d.ts.map +1 -1
- package/dist/composites/gke-fluent-bit-agent.d.ts +7 -1
- package/dist/composites/gke-fluent-bit-agent.d.ts.map +1 -1
- package/dist/composites/gke-gateway.d.ts +4 -1
- package/dist/composites/gke-gateway.d.ts.map +1 -1
- package/dist/composites/gke-otel-collector.d.ts +7 -1
- package/dist/composites/gke-otel-collector.d.ts.map +1 -1
- package/dist/composites/irsa-service-account.d.ts +1 -1
- package/dist/composites/irsa-service-account.d.ts.map +1 -1
- package/dist/composites/metrics-server.d.ts +10 -1
- package/dist/composites/metrics-server.d.ts.map +1 -1
- package/dist/composites/monitored-service.d.ts +1 -1
- package/dist/composites/monitored-service.d.ts.map +1 -1
- package/dist/composites/namespace-env.d.ts +1 -1
- package/dist/composites/namespace-env.d.ts.map +1 -1
- package/dist/composites/network-isolated-app.d.ts +5 -1
- package/dist/composites/network-isolated-app.d.ts.map +1 -1
- package/dist/composites/node-agent.d.ts +1 -1
- package/dist/composites/node-agent.d.ts.map +1 -1
- package/dist/composites/ray-cluster.d.ts +10 -1
- package/dist/composites/ray-cluster.d.ts.map +1 -1
- package/dist/composites/ray-job.d.ts +8 -1
- package/dist/composites/ray-job.d.ts.map +1 -1
- package/dist/composites/ray-service.d.ts +10 -1
- package/dist/composites/ray-service.d.ts.map +1 -1
- package/dist/composites/secure-ingress.d.ts +1 -1
- package/dist/composites/secure-ingress.d.ts.map +1 -1
- package/dist/composites/sidecar-app.d.ts +4 -1
- package/dist/composites/sidecar-app.d.ts.map +1 -1
- package/dist/composites/stateful-app.d.ts +1 -1
- package/dist/composites/stateful-app.d.ts.map +1 -1
- package/dist/composites/web-app.d.ts +1 -1
- package/dist/composites/web-app.d.ts.map +1 -1
- package/dist/composites/worker-pool.d.ts +1 -1
- package/dist/composites/worker-pool.d.ts.map +1 -1
- package/dist/composites/workload-identity-sa.d.ts +1 -1
- package/dist/composites/workload-identity-sa.d.ts.map +1 -1
- package/dist/composites/workload-identity-service-account.d.ts +1 -1
- package/dist/composites/workload-identity-service-account.d.ts.map +1 -1
- package/dist/config-schema.d.ts +40 -0
- package/dist/config-schema.d.ts.map +1 -0
- package/dist/config.d.ts +22 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/crd/crd-sources.d.ts.map +1 -1
- package/dist/crd/parser.d.ts.map +1 -1
- package/dist/deep-observe-hooks.d.ts +15 -18
- package/dist/deep-observe-hooks.d.ts.map +1 -1
- package/dist/deep-observe.d.ts.map +1 -1
- package/dist/describe-resources.d.ts +1 -0
- package/dist/describe-resources.d.ts.map +1 -1
- package/dist/generated/index.d.ts +134 -0
- package/dist/generated/index.d.ts.map +1 -1
- package/dist/integrity.json +4 -4
- package/dist/manifest.json +1 -1
- package/dist/meta.json +1086 -45
- package/dist/plugin.d.ts.map +1 -1
- package/dist/serializer.d.ts.map +1 -1
- package/dist/spec/parse.d.ts +17 -0
- package/dist/spec/parse.d.ts.map +1 -1
- package/dist/types/index.d.ts +229 -0
- package/package.json +3 -2
- package/src/codegen/generate-list-map-keys.test.ts +112 -0
- package/src/codegen/generate-list-map-keys.ts +99 -0
- package/src/codegen/generate.ts +9 -1
- package/src/composites/adot-collector.ts +1 -1
- package/src/composites/agic-ingress.ts +1 -1
- package/src/composites/aks-external-dns-agent.ts +1 -1
- package/src/composites/alb-ingress.ts +1 -1
- package/src/composites/autoscaled-service.ts +1 -1
- package/src/composites/azure-disk-storage-class.ts +1 -1
- package/src/composites/azure-file-storage-class.ts +1 -1
- package/src/composites/azure-monitor-collector.ts +1 -1
- package/src/composites/batch-job.ts +1 -1
- package/src/composites/cockroachdb-cluster.ts +1 -1
- package/src/composites/config-connector-context.ts +1 -1
- package/src/composites/configured-app.ts +1 -1
- package/src/composites/cron-workload.ts +1 -1
- package/src/composites/ebs-storage-class.ts +1 -1
- package/src/composites/efs-storage-class.ts +1 -1
- package/src/composites/external-dns-agent.ts +1 -1
- package/src/composites/filestore-storage-class.ts +1 -1
- package/src/composites/fluent-bit-agent.ts +1 -1
- package/src/composites/gce-ingress.ts +1 -1
- package/src/composites/gce-pd-storage-class.ts +1 -1
- package/src/composites/gke-external-dns-agent.ts +1 -1
- package/src/composites/gke-fluent-bit-agent.ts +1 -1
- package/src/composites/gke-gateway.ts +1 -1
- package/src/composites/gke-otel-collector.ts +1 -1
- package/src/composites/irsa-service-account.ts +1 -1
- package/src/composites/metrics-server.ts +1 -1
- package/src/composites/monitored-service.ts +1 -1
- package/src/composites/namespace-env.ts +1 -1
- package/src/composites/network-isolated-app.ts +1 -1
- package/src/composites/node-agent.ts +1 -1
- package/src/composites/ray-cluster.ts +1 -1
- package/src/composites/ray-job.ts +1 -1
- package/src/composites/ray-service.ts +1 -1
- package/src/composites/secure-ingress.ts +1 -1
- package/src/composites/sidecar-app.ts +1 -1
- package/src/composites/stateful-app.ts +1 -1
- package/src/composites/web-app.ts +1 -1
- package/src/composites/worker-pool.ts +1 -1
- package/src/composites/workload-identity-sa.ts +1 -1
- package/src/composites/workload-identity-service-account.ts +1 -1
- package/src/config-schema.ts +46 -0
- package/src/config.ts +25 -1
- package/src/crd/cnpg.test.ts +186 -0
- package/src/crd/crd-sources.ts +123 -0
- package/src/crd/infisical.test.ts +100 -0
- package/src/crd/parser.ts +8 -0
- package/src/crd/traefik.test.ts +108 -0
- package/src/deep-observe-hooks.ts +90 -10
- package/src/deep-observe.ts +7 -0
- package/src/describe-resources.test.ts +138 -1
- package/src/describe-resources.ts +98 -0
- package/src/generated/index.d.ts +229 -0
- package/src/generated/index.ts +134 -0
- package/src/generated/lexicon-k8s.json +1086 -45
- package/src/generated/list-map-keys.json +166 -0
- package/src/generated/operations.json +288 -0
- package/src/lifecycle-integration.test.ts +1 -0
- package/src/list-map-order-key.test.ts +119 -0
- package/src/plugin.ts +4 -0
- package/src/serializer.ts +4 -0
- package/src/spec/parse.test.ts +42 -0
- package/src/spec/parse.ts +51 -2
|
@@ -49,7 +49,7 @@ export interface AzureDiskStorageClassResult {
|
|
|
49
49
|
* });
|
|
50
50
|
* ```
|
|
51
51
|
*/
|
|
52
|
-
export const AzureDiskStorageClass = Composite
|
|
52
|
+
export const AzureDiskStorageClass = Composite((props: AzureDiskStorageClassProps) => {
|
|
53
53
|
const {
|
|
54
54
|
name,
|
|
55
55
|
skuName = "Premium_LRS",
|
|
@@ -47,7 +47,7 @@ export interface AzureFileStorageClassResult {
|
|
|
47
47
|
* });
|
|
48
48
|
* ```
|
|
49
49
|
*/
|
|
50
|
-
export const AzureFileStorageClass = Composite
|
|
50
|
+
export const AzureFileStorageClass = Composite((props: AzureFileStorageClassProps) => {
|
|
51
51
|
const {
|
|
52
52
|
name,
|
|
53
53
|
skuName = "Premium_LRS",
|
|
@@ -64,7 +64,7 @@ export interface AzureMonitorCollectorResult {
|
|
|
64
64
|
* });
|
|
65
65
|
* ```
|
|
66
66
|
*/
|
|
67
|
-
export const AzureMonitorCollector = Composite
|
|
67
|
+
export const AzureMonitorCollector = Composite((props: AzureMonitorCollectorProps) => {
|
|
68
68
|
const {
|
|
69
69
|
workspaceId,
|
|
70
70
|
clusterName,
|
|
@@ -133,7 +133,7 @@ export interface CockroachDbClusterResult {
|
|
|
133
133
|
* });
|
|
134
134
|
* ```
|
|
135
135
|
*/
|
|
136
|
-
export const CockroachDbCluster = Composite
|
|
136
|
+
export const CockroachDbCluster = Composite((props: CockroachDbClusterProps) => {
|
|
137
137
|
const {
|
|
138
138
|
name,
|
|
139
139
|
namespace,
|
|
@@ -42,7 +42,7 @@ export interface ConfigConnectorContextResult {
|
|
|
42
42
|
* });
|
|
43
43
|
* ```
|
|
44
44
|
*/
|
|
45
|
-
export const ConfigConnectorContext = Composite
|
|
45
|
+
export const ConfigConnectorContext = Composite((props: ConfigConnectorContextProps) => {
|
|
46
46
|
const {
|
|
47
47
|
name = "configconnectorcontext.core.cnrm.cloud.google.com",
|
|
48
48
|
googleServiceAccountEmail,
|
|
@@ -59,7 +59,7 @@ export interface ExternalDnsAgentResult {
|
|
|
59
59
|
* });
|
|
60
60
|
* ```
|
|
61
61
|
*/
|
|
62
|
-
export const ExternalDnsAgent = Composite
|
|
62
|
+
export const ExternalDnsAgent = Composite((props: ExternalDnsAgentProps) => {
|
|
63
63
|
const {
|
|
64
64
|
iamRoleArn,
|
|
65
65
|
domainFilters,
|
|
@@ -47,7 +47,7 @@ export interface FilestoreStorageClassResult {
|
|
|
47
47
|
* });
|
|
48
48
|
* ```
|
|
49
49
|
*/
|
|
50
|
-
export const FilestoreStorageClass = Composite
|
|
50
|
+
export const FilestoreStorageClass = Composite((props: FilestoreStorageClassProps) => {
|
|
51
51
|
const {
|
|
52
52
|
name,
|
|
53
53
|
tier = "standard",
|
|
@@ -67,7 +67,7 @@ export interface FluentBitAgentResult {
|
|
|
67
67
|
* });
|
|
68
68
|
* ```
|
|
69
69
|
*/
|
|
70
|
-
export const FluentBitAgent = Composite
|
|
70
|
+
export const FluentBitAgent = Composite((props: FluentBitAgentProps) => {
|
|
71
71
|
const {
|
|
72
72
|
name = "fluent-bit",
|
|
73
73
|
image = "public.ecr.aws/aws-observability/aws-for-fluent-bit:stable",
|
|
@@ -49,7 +49,7 @@ export interface GcePdStorageClassResult {
|
|
|
49
49
|
* });
|
|
50
50
|
* ```
|
|
51
51
|
*/
|
|
52
|
-
export const GcePdStorageClass = Composite
|
|
52
|
+
export const GcePdStorageClass = Composite((props: GcePdStorageClassProps) => {
|
|
53
53
|
const {
|
|
54
54
|
name,
|
|
55
55
|
type = "pd-balanced",
|
|
@@ -60,7 +60,7 @@ export interface GkeExternalDnsAgentResult {
|
|
|
60
60
|
* });
|
|
61
61
|
* ```
|
|
62
62
|
*/
|
|
63
|
-
export const GkeExternalDnsAgent = Composite
|
|
63
|
+
export const GkeExternalDnsAgent = Composite((props: GkeExternalDnsAgentProps) => {
|
|
64
64
|
const {
|
|
65
65
|
gcpServiceAccountEmail,
|
|
66
66
|
gcpProjectId,
|
|
@@ -65,7 +65,7 @@ export interface GkeFluentBitAgentResult {
|
|
|
65
65
|
* });
|
|
66
66
|
* ```
|
|
67
67
|
*/
|
|
68
|
-
export const GkeFluentBitAgent = Composite
|
|
68
|
+
export const GkeFluentBitAgent = Composite((props: GkeFluentBitAgentProps) => {
|
|
69
69
|
const {
|
|
70
70
|
clusterName,
|
|
71
71
|
projectId,
|
|
@@ -68,7 +68,7 @@ export interface GkeGatewayResult {
|
|
|
68
68
|
* });
|
|
69
69
|
* ```
|
|
70
70
|
*/
|
|
71
|
-
export const GkeGateway = Composite
|
|
71
|
+
export const GkeGateway = Composite((props: GkeGatewayProps) => {
|
|
72
72
|
const {
|
|
73
73
|
name,
|
|
74
74
|
gatewayClassName = "gke-l7-global-external-managed",
|
|
@@ -65,7 +65,7 @@ export interface GkeOtelCollectorResult {
|
|
|
65
65
|
* });
|
|
66
66
|
* ```
|
|
67
67
|
*/
|
|
68
|
-
export const GkeOtelCollector = Composite
|
|
68
|
+
export const GkeOtelCollector = Composite((props: GkeOtelCollectorProps) => {
|
|
69
69
|
const {
|
|
70
70
|
clusterName,
|
|
71
71
|
projectId,
|
|
@@ -55,7 +55,7 @@ export interface IrsaServiceAccountResult {
|
|
|
55
55
|
* });
|
|
56
56
|
* ```
|
|
57
57
|
*/
|
|
58
|
-
export const IrsaServiceAccount = Composite
|
|
58
|
+
export const IrsaServiceAccount = Composite((props: IrsaServiceAccountProps) => {
|
|
59
59
|
const {
|
|
60
60
|
name,
|
|
61
61
|
iamRoleArn,
|
|
@@ -57,7 +57,7 @@ export interface MetricsServerResult {
|
|
|
57
57
|
* const ms = MetricsServer({});
|
|
58
58
|
* ```
|
|
59
59
|
*/
|
|
60
|
-
export const MetricsServer = Composite
|
|
60
|
+
export const MetricsServer = Composite((props: MetricsServerProps) => {
|
|
61
61
|
const {
|
|
62
62
|
name = "metrics-server",
|
|
63
63
|
image = "registry.k8s.io/metrics-server/metrics-server:v0.7.2",
|
|
@@ -85,7 +85,7 @@ export interface NetworkIsolatedAppResult {
|
|
|
85
85
|
* });
|
|
86
86
|
* ```
|
|
87
87
|
*/
|
|
88
|
-
export const NetworkIsolatedApp = Composite
|
|
88
|
+
export const NetworkIsolatedApp = Composite((props: NetworkIsolatedAppProps) => {
|
|
89
89
|
const {
|
|
90
90
|
name,
|
|
91
91
|
image,
|
|
@@ -55,7 +55,7 @@ export interface WorkloadIdentityServiceAccountResult {
|
|
|
55
55
|
* });
|
|
56
56
|
* ```
|
|
57
57
|
*/
|
|
58
|
-
export const WorkloadIdentityServiceAccount = Composite
|
|
58
|
+
export const WorkloadIdentityServiceAccount = Composite((props: WorkloadIdentityServiceAccountProps) => {
|
|
59
59
|
const {
|
|
60
60
|
name,
|
|
61
61
|
clientId,
|
|
@@ -55,7 +55,7 @@ export interface WorkloadIdentityServiceAccountResult {
|
|
|
55
55
|
* });
|
|
56
56
|
* ```
|
|
57
57
|
*/
|
|
58
|
-
export const WorkloadIdentityServiceAccount = Composite
|
|
58
|
+
export const WorkloadIdentityServiceAccount = Composite((props: WorkloadIdentityServiceAccountProps) => {
|
|
59
59
|
const {
|
|
60
60
|
name,
|
|
61
61
|
gcpServiceAccountEmail,
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime shape of the `k8s` namespace in `chant.config.ts` (#1344).
|
|
3
|
+
*
|
|
4
|
+
* `./config.ts` describes the namespace for readers and for the `satisfies
|
|
5
|
+
* K8sChantConfig` form it documents. What it could not do is make an unknown
|
|
6
|
+
* key fail: the project config schema is `.passthrough()`, so a typo in
|
|
7
|
+
* `k8s.profiles.prod.contxt` was accepted and ignored — and a missing cluster
|
|
8
|
+
* binding is the failure this lexicon most cares about, since a wrong-cluster
|
|
9
|
+
* read reports every declared resource as missing (#1100).
|
|
10
|
+
*
|
|
11
|
+
* The schema is the runtime half. It is checked against `K8sChantConfig` below,
|
|
12
|
+
* so the two descriptions of the namespace cannot drift.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
import type { ChantConfig } from "@intentius/chant/config";
|
|
17
|
+
import type { K8sChantConfig } from "./config";
|
|
18
|
+
|
|
19
|
+
export const k8sClusterProfileSchema = z.strictObject({
|
|
20
|
+
context: z.string(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const k8sConfigSchema = z.strictObject({
|
|
24
|
+
profiles: z.record(z.string(), k8sClusterProfileSchema).optional(),
|
|
25
|
+
execCredentialPlugins: z.array(z.string()).optional(),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
declare module "@intentius/chant/config" {
|
|
29
|
+
interface ChantConfig {
|
|
30
|
+
k8s?: K8sChantConfig;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Compile-time proof the augmentation reaches `ChantConfig`. */
|
|
35
|
+
export type K8sConfigNamespace = NonNullable<ChantConfig["k8s"]>;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The schema and the documented interface describe the same namespace. If a
|
|
39
|
+
* field is added to one and not the other, this stops compiling.
|
|
40
|
+
*/
|
|
41
|
+
type SchemaMatchesInterface = z.infer<typeof k8sConfigSchema> extends K8sChantConfig
|
|
42
|
+
? K8sChantConfig extends z.infer<typeof k8sConfigSchema>
|
|
43
|
+
? true
|
|
44
|
+
: never
|
|
45
|
+
: never;
|
|
46
|
+
export type _SchemaAgreesWithInterface = SchemaMatchesInterface;
|
package/src/config.ts
CHANGED
|
@@ -39,7 +39,11 @@
|
|
|
39
39
|
*
|
|
40
40
|
* `ChantConfig` uses `.passthrough()` in its Zod schema so the `k8s` key is
|
|
41
41
|
* accepted at runtime without core changes, exactly like `temporal.profiles`
|
|
42
|
-
* (see `lexicons/temporal/src/config.ts`).
|
|
42
|
+
* (see `lexicons/temporal/src/config.ts`). The type side is the declaration
|
|
43
|
+
* merge at the bottom of this file — without it the snippet above compiles
|
|
44
|
+
* only until someone adds `satisfies ChantConfig`, which every example
|
|
45
|
+
* project does and which is the only thing type-checking the rest of the
|
|
46
|
+
* file (#1370).
|
|
43
47
|
*
|
|
44
48
|
* Deliberately out of scope here (see the issue for the full proposal):
|
|
45
49
|
* deriving the binding automatically from a declared `AWS::EKS::Cluster` /
|
|
@@ -51,6 +55,8 @@
|
|
|
51
55
|
* typed API client to inherit, rather than re-deriving it.
|
|
52
56
|
*/
|
|
53
57
|
|
|
58
|
+
import type { ChantConfig } from "@intentius/chant/config";
|
|
59
|
+
|
|
54
60
|
/** A single environment's cluster binding. */
|
|
55
61
|
export interface K8sClusterProfile {
|
|
56
62
|
/** kubectl context name this environment is bound to. */
|
|
@@ -78,3 +84,21 @@ export interface K8sChantConfig {
|
|
|
78
84
|
*/
|
|
79
85
|
execCredentialPlugins?: string[];
|
|
80
86
|
}
|
|
87
|
+
|
|
88
|
+
declare module "@intentius/chant/config" {
|
|
89
|
+
interface ChantConfig {
|
|
90
|
+
k8s?: K8sChantConfig;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Compile-time proof that the augmentation above reaches `ChantConfig`, the
|
|
96
|
+
* same guard the forgejo lexicon carries (#1344).
|
|
97
|
+
*
|
|
98
|
+
* Without it this line is `Property 'k8s' does not exist on type
|
|
99
|
+
* 'ChantConfig'` — which is exactly the error a project got for writing the
|
|
100
|
+
* documented snippet with `satisfies ChantConfig` (#1370). It lives here
|
|
101
|
+
* rather than in a test because the root tsconfig excludes test files from
|
|
102
|
+
* typechecking, so a compile-time claim asserted in one is checked by nothing.
|
|
103
|
+
*/
|
|
104
|
+
export type K8sConfigNamespace = NonNullable<ChantConfig["k8s"]>;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CNPG acceptance test.
|
|
3
|
+
*
|
|
4
|
+
* The bar set by #1319 is that fountain's real `k8s/` overlay — postgres.yaml,
|
|
5
|
+
* scheduledbackup.yaml, objectstore.yaml — is reproducible from typed chant
|
|
6
|
+
* source. So these assertions are transcribed from that overlay rather than
|
|
7
|
+
* from a fixture invented to pass, and every value here is one a running
|
|
8
|
+
* cluster has.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, test, expect } from "vitest";
|
|
12
|
+
import { Cluster, ScheduledBackup, ObjectStore } from "../generated";
|
|
13
|
+
import { k8sSerializer } from "../serializer";
|
|
14
|
+
import { parseYAML } from "@intentius/chant/yaml";
|
|
15
|
+
|
|
16
|
+
/** Serialize one declaration the way `chant build` would, and read it back. */
|
|
17
|
+
function synth(logicalName: string, resource: unknown): any {
|
|
18
|
+
const yaml = k8sSerializer.serialize(new Map([[logicalName, resource as never]]));
|
|
19
|
+
return parseYAML(yaml);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("CNPG Cluster", () => {
|
|
23
|
+
const cluster = new Cluster({
|
|
24
|
+
metadata: { name: "fountain-pg", namespace: "fountain", labels: { app: "fountain" } },
|
|
25
|
+
spec: {
|
|
26
|
+
instances: 1,
|
|
27
|
+
imageName: "ghcr.io/cloudnative-pg/postgresql:18.4-standard-trixie",
|
|
28
|
+
plugins: [
|
|
29
|
+
{
|
|
30
|
+
name: "barman-cloud.cloudnative-pg.io",
|
|
31
|
+
isWALArchiver: true,
|
|
32
|
+
parameters: { barmanObjectName: "fountain-backups" },
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
bootstrap: {
|
|
36
|
+
initdb: {
|
|
37
|
+
database: "fountain",
|
|
38
|
+
owner: "fountain",
|
|
39
|
+
postInitTemplateSQL: ["CREATE EXTENSION IF NOT EXISTS vector;"],
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
storage: { storageClass: "longhorn", size: "10Gi" },
|
|
43
|
+
resources: {
|
|
44
|
+
requests: { cpu: "100m", memory: "512Mi" },
|
|
45
|
+
limits: { memory: "1Gi" },
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("carries the CNPG apiVersion and kind", () => {
|
|
51
|
+
const doc = synth("fountainPg", cluster);
|
|
52
|
+
expect(doc.apiVersion).toBe("postgresql.cnpg.io/v1");
|
|
53
|
+
expect(doc.kind).toBe("Cluster");
|
|
54
|
+
expect(doc.metadata.name).toBe("fountain-pg");
|
|
55
|
+
expect(doc.metadata.namespace).toBe("fountain");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("reproduces fountain's postgres.yaml spec", () => {
|
|
59
|
+
const { spec } = synth("fountainPg", cluster);
|
|
60
|
+
expect(spec.instances).toBe(1);
|
|
61
|
+
expect(spec.imageName).toBe("ghcr.io/cloudnative-pg/postgresql:18.4-standard-trixie");
|
|
62
|
+
expect(spec.storage).toEqual({ storageClass: "longhorn", size: "10Gi" });
|
|
63
|
+
expect(spec.bootstrap.initdb.database).toBe("fountain");
|
|
64
|
+
expect(spec.bootstrap.initdb.owner).toBe("fountain");
|
|
65
|
+
expect(spec.bootstrap.initdb.postInitTemplateSQL).toEqual([
|
|
66
|
+
"CREATE EXTENSION IF NOT EXISTS vector;",
|
|
67
|
+
]);
|
|
68
|
+
// A memory limit with no CPU limit is deliberate upstream: a CPU limit
|
|
69
|
+
// throttles a database. It has to survive serialization as written.
|
|
70
|
+
expect(spec.resources).toEqual({
|
|
71
|
+
requests: { cpu: "100m", memory: "512Mi" },
|
|
72
|
+
limits: { memory: "1Gi" },
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("wires WAL archiving to the ObjectStore by name", () => {
|
|
77
|
+
const { spec } = synth("fountainPg", cluster);
|
|
78
|
+
// This block is the whole link between the two CRDs. `isWALArchiver` must
|
|
79
|
+
// stay a boolean -- quoted, the plugin ignores it and archiving is silently
|
|
80
|
+
// off, which looks exactly like a healthy cluster until a restore.
|
|
81
|
+
expect(spec.plugins).toEqual([
|
|
82
|
+
{
|
|
83
|
+
name: "barman-cloud.cloudnative-pg.io",
|
|
84
|
+
isWALArchiver: true,
|
|
85
|
+
parameters: { barmanObjectName: "fountain-backups" },
|
|
86
|
+
},
|
|
87
|
+
]);
|
|
88
|
+
expect(typeof spec.plugins[0].isWALArchiver).toBe("boolean");
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe("CNPG ScheduledBackup", () => {
|
|
93
|
+
const backup = new ScheduledBackup({
|
|
94
|
+
metadata: { name: "fountain-pg-base", namespace: "fountain", labels: { app: "fountain" } },
|
|
95
|
+
spec: {
|
|
96
|
+
schedule: "0 47 2 * * *",
|
|
97
|
+
backupOwnerReference: "self",
|
|
98
|
+
cluster: { name: "fountain-pg" },
|
|
99
|
+
method: "plugin",
|
|
100
|
+
pluginConfiguration: { name: "barman-cloud.cloudnative-pg.io" },
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("reproduces fountain's scheduledbackup.yaml", () => {
|
|
105
|
+
const doc = synth("fountainPgBase", backup);
|
|
106
|
+
expect(doc.apiVersion).toBe("postgresql.cnpg.io/v1");
|
|
107
|
+
expect(doc.kind).toBe("ScheduledBackup");
|
|
108
|
+
expect(doc.spec.backupOwnerReference).toBe("self");
|
|
109
|
+
expect(doc.spec.cluster).toEqual({ name: "fountain-pg" });
|
|
110
|
+
expect(doc.spec.method).toBe("plugin");
|
|
111
|
+
expect(doc.spec.pluginConfiguration).toEqual({ name: "barman-cloud.cloudnative-pg.io" });
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("round-trips a six-field schedule", () => {
|
|
115
|
+
const { spec } = synth("fountainPgBase", backup);
|
|
116
|
+
// CNPG schedules lead with seconds, so this is six fields where a
|
|
117
|
+
// Kubernetes CronJob takes five. The schema types it as a plain string, so
|
|
118
|
+
// nothing here or upstream rejects the five-field form -- it just means a
|
|
119
|
+
// different time. The count is the only thing that catches it.
|
|
120
|
+
expect(spec.schedule).toBe("0 47 2 * * *");
|
|
121
|
+
expect(spec.schedule.trim().split(/\s+/)).toHaveLength(6);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe("barman-cloud ObjectStore", () => {
|
|
126
|
+
const store = new ObjectStore({
|
|
127
|
+
metadata: { name: "fountain-backups", namespace: "fountain", labels: { app: "fountain" } },
|
|
128
|
+
spec: {
|
|
129
|
+
retentionPolicy: "14d",
|
|
130
|
+
configuration: {
|
|
131
|
+
destinationPath: "s3://fountain-backups/barman",
|
|
132
|
+
endpointURL: "http://s3.garage.svc.cluster.local:3900",
|
|
133
|
+
s3Credentials: {
|
|
134
|
+
accessKeyId: { name: "fountain-backup-s3-credentials", key: "AWS_ACCESS_KEY_ID" },
|
|
135
|
+
secretAccessKey: { name: "fountain-backup-s3-credentials", key: "AWS_SECRET_ACCESS_KEY" },
|
|
136
|
+
},
|
|
137
|
+
wal: { compression: "gzip" },
|
|
138
|
+
data: { compression: "gzip" },
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("reproduces fountain's objectstore.yaml", () => {
|
|
144
|
+
const doc = synth("fountainBackups", store);
|
|
145
|
+
// Its own group, mapped onto the same `Cnpg` namespace as the Cluster.
|
|
146
|
+
expect(doc.apiVersion).toBe("barmancloud.cnpg.io/v1");
|
|
147
|
+
expect(doc.kind).toBe("ObjectStore");
|
|
148
|
+
expect(doc.spec.retentionPolicy).toBe("14d");
|
|
149
|
+
expect(doc.spec.configuration.destinationPath).toBe("s3://fountain-backups/barman");
|
|
150
|
+
expect(doc.spec.configuration.endpointURL).toBe("http://s3.garage.svc.cluster.local:3900");
|
|
151
|
+
expect(doc.spec.configuration.wal).toEqual({ compression: "gzip" });
|
|
152
|
+
expect(doc.spec.configuration.data).toEqual({ compression: "gzip" });
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("keeps credentials as secret references, never values", () => {
|
|
156
|
+
const { spec } = synth("fountainBackups", store);
|
|
157
|
+
expect(spec.configuration.s3Credentials).toEqual({
|
|
158
|
+
accessKeyId: { name: "fountain-backup-s3-credentials", key: "AWS_ACCESS_KEY_ID" },
|
|
159
|
+
secretAccessKey: { name: "fountain-backup-s3-credentials", key: "AWS_SECRET_ACCESS_KEY" },
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("the Cluster's barmanObjectName matches this store's name", () => {
|
|
164
|
+
// The two CRDs are joined by a string. Nothing upstream checks it: a typo
|
|
165
|
+
// leaves the cluster running with archiving pointed at a store that does
|
|
166
|
+
// not exist.
|
|
167
|
+
const clusterDoc = synth(
|
|
168
|
+
"fountainPg",
|
|
169
|
+
new Cluster({
|
|
170
|
+
metadata: { name: "fountain-pg", namespace: "fountain" },
|
|
171
|
+
spec: {
|
|
172
|
+
instances: 1,
|
|
173
|
+
plugins: [
|
|
174
|
+
{
|
|
175
|
+
name: "barman-cloud.cloudnative-pg.io",
|
|
176
|
+
isWALArchiver: true,
|
|
177
|
+
parameters: { barmanObjectName: "fountain-backups" },
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
}),
|
|
182
|
+
);
|
|
183
|
+
const storeDoc = synth("fountainBackups", store);
|
|
184
|
+
expect(clusterDoc.spec.plugins[0].parameters.barmanObjectName).toBe(storeDoc.metadata.name);
|
|
185
|
+
});
|
|
186
|
+
});
|