@intentius/chant-lexicon-k8s 0.1.4 → 0.1.6
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/integrity.json +42 -42
- package/dist/manifest.json +1 -1
- package/package.json +10 -8
- package/src/actions/actions.test.ts +1 -1
- package/src/codegen/docs.ts +5 -1
- package/src/codegen/generate-cli.ts +1 -1
- package/src/codegen/naming.test.ts +1 -1
- package/src/codegen/package.ts +2 -5
- package/src/codegen/snapshot.test.ts +1 -1
- package/src/codegen/typecheck.test.ts +1 -1
- package/src/composites/cockroachdb-region-stack.ts +553 -0
- package/src/composites/composites.test.ts +4 -4
- package/src/composites/index.ts +2 -0
- package/src/coverage.test.ts +1 -1
- package/src/crd/loader.ts +13 -21
- package/src/crd/parser.test.ts +1 -1
- package/src/default-labels.test.ts +1 -1
- package/src/import/generator.test.ts +1 -1
- package/src/import/parser.test.ts +1 -1
- package/src/import/roundtrip.test.ts +1 -1
- package/src/index.ts +2 -1
- package/src/lint/post-synth/k8s-helpers.test.ts +1 -1
- package/src/lint/post-synth/post-synth.test.ts +1 -1
- package/src/lint/rules/rules.test.ts +1 -1
- package/src/lsp/completions.test.ts +1 -1
- package/src/lsp/hover.test.ts +1 -1
- package/src/package-cli.ts +1 -1
- package/src/plugin.test.ts +1 -1
- package/src/plugin.ts +7 -9
- package/src/serializer.test.ts +1 -1
- package/src/spec/fetch.test.ts +1 -1
- package/src/spec/parse.test.ts +1 -1
- package/src/validate-cli.ts +1 -1
- package/src/validate.test.ts +1 -1
package/dist/integrity.json
CHANGED
|
@@ -1,46 +1,46 @@
|
|
|
1
1
|
{
|
|
2
|
-
"algorithm": "
|
|
2
|
+
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
5
|
-
"meta.json": "
|
|
6
|
-
"types/index.d.ts": "
|
|
7
|
-
"rules/hardcoded-namespace.ts": "
|
|
8
|
-
"rules/
|
|
9
|
-
"rules/
|
|
10
|
-
"rules/
|
|
11
|
-
"rules/
|
|
12
|
-
"rules/
|
|
13
|
-
"rules/
|
|
14
|
-
"rules/
|
|
15
|
-
"rules/
|
|
16
|
-
"rules/
|
|
17
|
-
"rules/
|
|
18
|
-
"rules/
|
|
19
|
-
"rules/
|
|
20
|
-
"rules/
|
|
21
|
-
"rules/
|
|
22
|
-
"rules/
|
|
23
|
-
"rules/
|
|
24
|
-
"rules/
|
|
25
|
-
"rules/
|
|
26
|
-
"rules/
|
|
27
|
-
"rules/
|
|
28
|
-
"rules/
|
|
29
|
-
"rules/
|
|
30
|
-
"rules/
|
|
31
|
-
"rules/
|
|
32
|
-
"rules/
|
|
33
|
-
"rules/
|
|
34
|
-
"rules/
|
|
35
|
-
"rules/
|
|
36
|
-
"rules/
|
|
37
|
-
"skills/chant-k8s.md": "
|
|
38
|
-
"skills/chant-k8s-patterns.md": "
|
|
39
|
-
"skills/chant-k8s-deployment-strategies.md": "
|
|
40
|
-
"skills/chant-k8s-security.md": "
|
|
41
|
-
"skills/chant-k8s-eks.md": "
|
|
42
|
-
"skills/chant-k8s-gke.md": "
|
|
43
|
-
"skills/chant-k8s-aks.md": "
|
|
4
|
+
"manifest.json": "7c2c231f5582d9228dc65679781e924d847bb5e8bb09a8861b6ec41c73bb17af",
|
|
5
|
+
"meta.json": "970c70eb6eea1686f7cb8ce6ceccc46ce2e57d696d344f1dd52460e266f9a56c",
|
|
6
|
+
"types/index.d.ts": "07473e029254345488bc9952585e88bcf18da79d1026cc26744027683f472554",
|
|
7
|
+
"rules/hardcoded-namespace.ts": "ba3f43f2adbffdd87db20a2c45839354ceecda1b9f04f29ae31c4c077dddc7ec",
|
|
8
|
+
"rules/latest-image-tag.ts": "9dd961b791b4f424fc4edcdbdce69ad30476dc1cd03f66b8c8e19cb5f8a4082d",
|
|
9
|
+
"rules/missing-resource-limits.ts": "d64b5ed1c7c87d60e38eecfdad7668620222bbaf6ff6f4860adf54e816265bb2",
|
|
10
|
+
"rules/k8s-helpers.ts": "d7d5020bc9b47bfb1827e8c8cb13ad1d406890d08a964a521c9fbcb93b230827",
|
|
11
|
+
"rules/wk8005.ts": "71c3f6e68da973c466b2d5cf109bf4728b1671794aeca8e8fde79c80c8715ec1",
|
|
12
|
+
"rules/wk8006.ts": "8705b24db14c9af12c6c45c5a8ecb5f4f427a5f76706ac2c82ba2949d41c498c",
|
|
13
|
+
"rules/wk8041.ts": "e55f97a9e1bc407ca681366c1f9e3dd93b73bbff5f5593dfcf0991c068c28658",
|
|
14
|
+
"rules/wk8042.ts": "31ef0636bd036b09bfcec1d3b65da405288868732b3183c504a6cd1afc3d431a",
|
|
15
|
+
"rules/wk8101.ts": "2e4087bb89412e88a3208d82876e4b7fd45d2c7ac698c334bf2c110974e4a05e",
|
|
16
|
+
"rules/wk8102.ts": "d5b0a0b3f2f097ca9a0b20af935152c5962a27536b563344460729c22df0211f",
|
|
17
|
+
"rules/wk8103.ts": "2afae27ed705a9b1f6fd2380bf02b393597a20772166673b94fcf069df428da7",
|
|
18
|
+
"rules/wk8104.ts": "cb0033e65b7545b1f484691b0419d5f21777b2b64063b514b134f44e195fe209",
|
|
19
|
+
"rules/wk8105.ts": "1e52f22cdb9683bc8ed2da56ff68daf8d9fcfa00aacd551c5d7985103206b9ac",
|
|
20
|
+
"rules/wk8201.ts": "594a8f353ffb30e356a12bc1414f59c5c61da421701e3e59b9e1064fe5ff3442",
|
|
21
|
+
"rules/wk8202.ts": "54fbc1f31ceb949b50ae898da5c97d6cedcd1b4c1bfc07c54b582f57ceb5ccf2",
|
|
22
|
+
"rules/wk8203.ts": "3ac5b7c7090ae225dd3fee36ba0fef9cff4904a9658d1fcbb4b8268aa1833259",
|
|
23
|
+
"rules/wk8204.ts": "464e1d1723a09fbbecb593f559873ce9b3555cd704b731916b31fb6f7c3c5880",
|
|
24
|
+
"rules/wk8205.ts": "1721e8fae72163ef38cfd72aaceaf0b7b87cf758488a2cbee9e48cfd082e8589",
|
|
25
|
+
"rules/wk8207.ts": "e82dbf47e20fc186a105069b5923a8532553f236c58178332f9264f39ac31390",
|
|
26
|
+
"rules/wk8208.ts": "0e9d654fa70f9ee91de4711df43689fcee4fde831ce0eb0081fdc4753632e9dd",
|
|
27
|
+
"rules/wk8209.ts": "617324b7988a7c842502c9e76388412c5855dcd321e40a2df6039c9faf87ac47",
|
|
28
|
+
"rules/wk8301.ts": "12321c2b217db4a1f8842844e0d9875def589e54056dd66d6c0f4a65e50e1213",
|
|
29
|
+
"rules/wk8302.ts": "3e8e336e72618b543c3208f9b2b7a582b4535d3d284481cc6c77812c35812e71",
|
|
30
|
+
"rules/wk8303.ts": "fdf8b072063359d2e654b1e810491738316eae01e85f9d18df1c341578caf2a6",
|
|
31
|
+
"rules/wk8304.ts": "fd98b1bd1e62713b29af5fbb8e45d8ddf2b8b751eab1819caa9ea813efb9e54d",
|
|
32
|
+
"rules/wk8305.ts": "9241685fe0f8ddaae9cf5f83b3e43f347924b3f4f1f5a95fe6ec16753e31b0bf",
|
|
33
|
+
"rules/wk8306.ts": "8a95b948df271384e86b564ea58e7032bddb339859c9be10d3ef0c7162f21db3",
|
|
34
|
+
"rules/wk8401.ts": "860e5104d53158ebea790f5acc3d34b82d666c4808d8ee5de44b68c1c602af77",
|
|
35
|
+
"rules/wk8402.ts": "91c58dc146c6aa44b93f167fd5ed8c16cb5dc95e9d99f40685d9d0acdf7eb474",
|
|
36
|
+
"rules/wk8403.ts": "abf42e48d9539768856d71d365b5e6cbb9d4800b9f890809b70d181641a86b98",
|
|
37
|
+
"skills/chant-k8s.md": "222268f9e6b19912ece4b066d8897f009b90a3fe6f14e9c5059d0717e7ee0f82",
|
|
38
|
+
"skills/chant-k8s-patterns.md": "683148e9f0f9da6879bd65d97c82cc29dbff897e31bc11b3c1de654cd6baeea3",
|
|
39
|
+
"skills/chant-k8s-deployment-strategies.md": "c4cc93cba064230567baa755b5a3493dc69bfc6e74a62e1990d12039ffaaf5f8",
|
|
40
|
+
"skills/chant-k8s-security.md": "f4250284000fde0d380192ba4cd73f2190471da2142e3bf77d848881552807f7",
|
|
41
|
+
"skills/chant-k8s-eks.md": "c391217039b35d1c9d88214e2cb7b8c1166331d6ab26996f5f7d907027508ab9",
|
|
42
|
+
"skills/chant-k8s-gke.md": "8938840bf9ef5ed58d6333fdd773b3dd54ecaf25a9df35e58f7f5c3355d4928f",
|
|
43
|
+
"skills/chant-k8s-aks.md": "e18f0e2b055f72cd7a37deaf258d7027c2d4d3e286e8fd4975b27a1f981a3ad9"
|
|
44
44
|
},
|
|
45
|
-
"composite": "
|
|
45
|
+
"composite": "0c9b24ec573fdf75f6cc905b73496a3dc14c77121003ccfb4ea30a74cae054ef"
|
|
46
46
|
}
|
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.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Kubernetes lexicon for chant — declarative IaC in TypeScript",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://intentius.io/chant",
|
|
@@ -36,16 +36,18 @@
|
|
|
36
36
|
"./types": "./dist/types/index.d.ts"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
|
-
"generate": "
|
|
40
|
-
"bundle": "
|
|
41
|
-
"validate": "
|
|
42
|
-
"docs": "
|
|
43
|
-
"prepack": "
|
|
39
|
+
"generate": "tsx src/codegen/generate-cli.ts",
|
|
40
|
+
"bundle": "tsx src/package-cli.ts",
|
|
41
|
+
"validate": "tsx src/validate-cli.ts",
|
|
42
|
+
"docs": "tsx src/codegen/docs-cli.ts",
|
|
43
|
+
"prepack": "npm run generate && npm run bundle && npm run validate"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"js-yaml": "^4.1.1"
|
|
44
47
|
},
|
|
45
48
|
"devDependencies": {
|
|
46
|
-
"@intentius/chant": "
|
|
49
|
+
"@intentius/chant": "*",
|
|
47
50
|
"@types/js-yaml": "^4.0.9",
|
|
48
|
-
"js-yaml": "^4.1.1",
|
|
49
51
|
"typescript": "^5.9.3"
|
|
50
52
|
},
|
|
51
53
|
"peerDependencies": {
|
package/src/codegen/docs.ts
CHANGED
|
@@ -548,7 +548,11 @@ const { statefulSet, service } = StatefulApp({
|
|
|
548
548
|
slug: "composite-examples",
|
|
549
549
|
title: "Examples: Composites",
|
|
550
550
|
description: "Composite examples — WebApp, CronWorkload, AutoscaledService, WorkerPool, NamespaceEnv, NodeAgent",
|
|
551
|
-
content: `
|
|
551
|
+
content: `import Diagram from '../../components/Diagram.astro';
|
|
552
|
+
|
|
553
|
+
Composites are higher-level constructs that produce multiple coordinated K8s resources from a single function call.
|
|
554
|
+
|
|
555
|
+
<Diagram name="composite-expansion" alt="Single CockroachDbCluster composite declaration expanding into Namespace, StatefulSet, Service, ServiceAccount, NetworkPolicy, PodDisruptionBudget, and ConfigMap resources serialized to one YAML file" caption="Composite expansion: one declaration → many resources" />
|
|
552
556
|
|
|
553
557
|
## WebApp
|
|
554
558
|
|
package/src/codegen/package.ts
CHANGED
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
* with K8s-specific manifest building and skill collection.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { createRequire } from "module";
|
|
7
6
|
import { readFileSync } from "fs";
|
|
8
|
-
|
|
7
|
+
import { k8sPlugin } from "../plugin";
|
|
9
8
|
import { join, dirname } from "path";
|
|
10
9
|
import { fileURLToPath } from "url";
|
|
11
10
|
import {
|
|
@@ -43,9 +42,7 @@ export async function packageLexicon(opts: PackageOptions = {}): Promise<Package
|
|
|
43
42
|
|
|
44
43
|
srcDir: pkgDir,
|
|
45
44
|
|
|
46
|
-
collectSkills: () => {
|
|
47
|
-
const { k8sPlugin } = require("../plugin");
|
|
48
|
-
const skillDefs = k8sPlugin.skills?.() ?? [];
|
|
45
|
+
collectSkills: () => { const skillDefs = k8sPlugin.skills?.() ?? [];
|
|
49
46
|
return collectSkills(skillDefs);
|
|
50
47
|
},
|
|
51
48
|
|
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CockroachDbRegionStack composite — all K8s resources for one CockroachDB region.
|
|
3
|
+
*
|
|
4
|
+
* Collapses the 8-file per-region K8s pattern into a single composite call:
|
|
5
|
+
* namespace, storage, CockroachDB StatefulSet, External Secrets, GCE Ingress,
|
|
6
|
+
* ExternalDNS, Cloud Armor BackendConfig, TLS cert, and optional Prometheus monitoring.
|
|
7
|
+
*
|
|
8
|
+
* @gke Requires: External Secrets Operator (ESO), ExternalDNS (via GkeExternalDnsAgent).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Composite, mergeDefaults } from "@intentius/chant";
|
|
12
|
+
import { createResource } from "@intentius/chant/runtime";
|
|
13
|
+
import {
|
|
14
|
+
NetworkPolicy,
|
|
15
|
+
Deployment,
|
|
16
|
+
Service,
|
|
17
|
+
ConfigMap,
|
|
18
|
+
} from "../generated";
|
|
19
|
+
import { NamespaceEnv } from "./namespace-env";
|
|
20
|
+
import { GcePdStorageClass } from "./gce-pd-storage-class";
|
|
21
|
+
import { CockroachDbCluster } from "./cockroachdb-cluster";
|
|
22
|
+
import { GceIngress } from "./gce-ingress";
|
|
23
|
+
import { GkeExternalDnsAgent } from "./gke-external-dns-agent";
|
|
24
|
+
|
|
25
|
+
// ── External CRDs (not in main generated index) ──────────────────────────────────
|
|
26
|
+
|
|
27
|
+
const ClusterSecretStore = createResource("K8s::ExternalSecrets::ClusterSecretStore", "k8s", {});
|
|
28
|
+
const ExternalSecret = createResource("K8s::ExternalSecrets::ExternalSecret", "k8s", {});
|
|
29
|
+
const BackendConfig = createResource("K8s::GKE::BackendConfig", "k8s", {});
|
|
30
|
+
const ManagedCertificate = createResource("K8s::NetworkingGKE::ManagedCertificate", "k8s", {});
|
|
31
|
+
const FrontendConfig = createResource("K8s::NetworkingGKEBeta::FrontendConfig", "k8s", {});
|
|
32
|
+
|
|
33
|
+
// ── Types ─────────────────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
export interface CockroachDbRegionCockroachConfig {
|
|
36
|
+
/** Cluster name (default: "cockroachdb"). */
|
|
37
|
+
name?: string;
|
|
38
|
+
/** StatefulSet replicas (default: 3). */
|
|
39
|
+
replicas?: number;
|
|
40
|
+
/** CockroachDB image (default: "cockroachdb/cockroach:v24.3.0"). */
|
|
41
|
+
image?: string;
|
|
42
|
+
/** PVC storage size per node (default: "100Gi"). */
|
|
43
|
+
storageSize?: string;
|
|
44
|
+
/** CPU limit per pod (default: "2"). */
|
|
45
|
+
cpuLimit?: string;
|
|
46
|
+
/** Memory limit per pod (default: "8Gi"). */
|
|
47
|
+
memoryLimit?: string;
|
|
48
|
+
/** Locality flag (e.g. "cloud=gcp,region=us-east4"). */
|
|
49
|
+
locality?: string;
|
|
50
|
+
/** Join addresses for multi-region cluster. */
|
|
51
|
+
joinAddresses?: string[];
|
|
52
|
+
/**
|
|
53
|
+
* Skip the cockroach init Job (set true for secondary regions).
|
|
54
|
+
* Only the first region should run cockroach init.
|
|
55
|
+
*/
|
|
56
|
+
skipInit?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Mount the client certs secret into pods (needed for multi-region init via kubectl exec).
|
|
59
|
+
*/
|
|
60
|
+
mountClientCerts?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Extra DNS names to include in node cert SANs (e.g. cross-region ExternalDNS names).
|
|
63
|
+
*/
|
|
64
|
+
extraCertNodeAddresses?: string[];
|
|
65
|
+
/**
|
|
66
|
+
* Domain for cross-cluster advertise address (e.g. "east.crdb.internal").
|
|
67
|
+
* When set, pods advertise "${HOSTNAME}.${advertiseHostDomain}" for cross-cluster gossip.
|
|
68
|
+
*/
|
|
69
|
+
advertiseHostDomain?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface CockroachDbRegionTlsConfig {
|
|
73
|
+
/**
|
|
74
|
+
* GCP Secret Manager secret names to sync into K8s Secrets via External Secrets Operator.
|
|
75
|
+
*/
|
|
76
|
+
gcpSecretNames: {
|
|
77
|
+
/** CA cert secret name in GCP Secret Manager. */
|
|
78
|
+
ca: string;
|
|
79
|
+
/** Node cert secret name. */
|
|
80
|
+
nodeCrt: string;
|
|
81
|
+
/** Node cert key secret name. */
|
|
82
|
+
nodeKey: string;
|
|
83
|
+
/** Client root cert secret name. */
|
|
84
|
+
clientRootCrt: string;
|
|
85
|
+
/** Client root key secret name. */
|
|
86
|
+
clientRootKey: string;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface CockroachDbRegionStackConfig {
|
|
91
|
+
/**
|
|
92
|
+
* Short region identifier used in resource names and labels (e.g. "east").
|
|
93
|
+
* Used to distinguish resources across regions.
|
|
94
|
+
*/
|
|
95
|
+
region: string;
|
|
96
|
+
/** K8s namespace for all namespaced resources (e.g. "crdb-east"). */
|
|
97
|
+
namespace: string;
|
|
98
|
+
/** Public UI domain for this region (e.g. "east.crdb.example.com"). */
|
|
99
|
+
domain: string;
|
|
100
|
+
/** Cross-cluster ExternalDNS domain (e.g. "east.crdb.internal"). */
|
|
101
|
+
internalDomain: string;
|
|
102
|
+
/** Root public domain for ExternalDNS domain filter (e.g. "crdb.example.com"). */
|
|
103
|
+
publicRootDomain: string;
|
|
104
|
+
|
|
105
|
+
/** GCP project ID — used in ClusterSecretStore Workload Identity config. */
|
|
106
|
+
projectId: string;
|
|
107
|
+
/** GKE cluster name — used in ClusterSecretStore Workload Identity config. */
|
|
108
|
+
clusterName: string;
|
|
109
|
+
/** GKE cluster region (e.g. "us-east4") — used in ClusterSecretStore config. */
|
|
110
|
+
clusterRegion: string;
|
|
111
|
+
/** GCP service account email for CRDB Workload Identity annotation on the K8s SA. */
|
|
112
|
+
crdbGsaEmail: string;
|
|
113
|
+
/** GCP service account email for ExternalDNS Workload Identity. */
|
|
114
|
+
externalDnsGsaEmail: string;
|
|
115
|
+
|
|
116
|
+
/** CockroachDB cluster configuration. */
|
|
117
|
+
cockroachdb: CockroachDbRegionCockroachConfig;
|
|
118
|
+
/** TLS certificate sync config (External Secrets Operator). */
|
|
119
|
+
tls: CockroachDbRegionTlsConfig;
|
|
120
|
+
|
|
121
|
+
/** Namespace resource quotas. */
|
|
122
|
+
quota?: {
|
|
123
|
+
/** Total CPU limit quota (e.g. "8"). */
|
|
124
|
+
cpu?: string;
|
|
125
|
+
/** Total memory limit quota (e.g. "20Gi"). */
|
|
126
|
+
memory?: string;
|
|
127
|
+
/** Maximum pods. */
|
|
128
|
+
maxPods?: number;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* CIDRs that may send traffic to CockroachDB ports (26257 + 8080).
|
|
133
|
+
* Used in the allow-cockroachdb NetworkPolicy.
|
|
134
|
+
*/
|
|
135
|
+
allowCidrs?: string[];
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Cloud Armor WAF configuration.
|
|
139
|
+
* When set, a BackendConfig is created attaching the policy to the public service.
|
|
140
|
+
*/
|
|
141
|
+
cloudArmor?: {
|
|
142
|
+
/** Cloud Armor security policy name. */
|
|
143
|
+
policyName: string;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Emit Prometheus ConfigMap + Deployment + Service for CockroachDB metrics.
|
|
148
|
+
* Uses a lightweight Prometheus scraping `/_status/vars` (default: false).
|
|
149
|
+
*/
|
|
150
|
+
monitoring?: boolean;
|
|
151
|
+
|
|
152
|
+
/** Additional labels for all resources. */
|
|
153
|
+
labels?: Record<string, string>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Create a CockroachDbRegionStack composite — returns all K8s resources for one
|
|
158
|
+
* CockroachDB region in a single call.
|
|
159
|
+
*
|
|
160
|
+
* Replaces: namespace.ts, storage.ts, cockroachdb.ts, external-secrets.ts,
|
|
161
|
+
* ingress.ts, tls.ts, backend-config.ts, monitoring.ts (8 files → 1 call).
|
|
162
|
+
*
|
|
163
|
+
* @gke
|
|
164
|
+
* @example
|
|
165
|
+
* ```ts
|
|
166
|
+
* import { CockroachDbRegionStack } from "@intentius/chant-lexicon-k8s";
|
|
167
|
+
*
|
|
168
|
+
* export const east = CockroachDbRegionStack({
|
|
169
|
+
* region: "east",
|
|
170
|
+
* namespace: "crdb-east",
|
|
171
|
+
* domain: `east.${CRDB_DOMAIN}`,
|
|
172
|
+
* internalDomain: `east.${INTERNAL_DOMAIN}`,
|
|
173
|
+
* publicRootDomain: CRDB_DOMAIN,
|
|
174
|
+
* projectId: GCP_PROJECT_ID,
|
|
175
|
+
* clusterName: "gke-crdb-east",
|
|
176
|
+
* clusterRegion: "us-east4",
|
|
177
|
+
* crdbGsaEmail: `gke-crdb-east-crdb@${GCP_PROJECT_ID}.iam.gserviceaccount.com`,
|
|
178
|
+
* externalDnsGsaEmail: `gke-crdb-east-dns@${GCP_PROJECT_ID}.iam.gserviceaccount.com`,
|
|
179
|
+
* cockroachdb: {
|
|
180
|
+
* locality: "cloud=gcp,region=us-east4",
|
|
181
|
+
* joinAddresses: [...],
|
|
182
|
+
* advertiseHostDomain: `east.${INTERNAL_DOMAIN}`,
|
|
183
|
+
* skipInit: false,
|
|
184
|
+
* mountClientCerts: true,
|
|
185
|
+
* },
|
|
186
|
+
* tls: {
|
|
187
|
+
* gcpSecretNames: {
|
|
188
|
+
* ca: "crdb-ca-crt", nodeCrt: "crdb-node-crt", nodeKey: "crdb-node-key",
|
|
189
|
+
* clientRootCrt: "crdb-client-root-crt", clientRootKey: "crdb-client-root-key",
|
|
190
|
+
* },
|
|
191
|
+
* },
|
|
192
|
+
* allowCidrs: ALL_CIDRS,
|
|
193
|
+
* cloudArmor: { policyName: "crdb-ui-waf" },
|
|
194
|
+
* monitoring: true,
|
|
195
|
+
* });
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
198
|
+
export const CockroachDbRegionStack = Composite<CockroachDbRegionStackConfig>((props) => {
|
|
199
|
+
const {
|
|
200
|
+
region,
|
|
201
|
+
namespace: ns,
|
|
202
|
+
domain,
|
|
203
|
+
internalDomain,
|
|
204
|
+
publicRootDomain,
|
|
205
|
+
projectId,
|
|
206
|
+
clusterName,
|
|
207
|
+
clusterRegion,
|
|
208
|
+
crdbGsaEmail,
|
|
209
|
+
externalDnsGsaEmail,
|
|
210
|
+
cockroachdb: crdbProps,
|
|
211
|
+
tls,
|
|
212
|
+
quota,
|
|
213
|
+
allowCidrs = [],
|
|
214
|
+
cloudArmor,
|
|
215
|
+
monitoring = false,
|
|
216
|
+
labels: extraLabels = {},
|
|
217
|
+
} = props;
|
|
218
|
+
|
|
219
|
+
const crdbName = crdbProps.name ?? "cockroachdb";
|
|
220
|
+
|
|
221
|
+
const commonLabels: Record<string, string> = {
|
|
222
|
+
"app.kubernetes.io/managed-by": "chant",
|
|
223
|
+
"app.kubernetes.io/part-of": `cockroachdb-multi-region`,
|
|
224
|
+
"app.kubernetes.io/instance": region,
|
|
225
|
+
...extraLabels,
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// ── Namespace + Quotas ─────────────────────────────────────────────────────────
|
|
229
|
+
|
|
230
|
+
const nsResult = NamespaceEnv({
|
|
231
|
+
name: ns,
|
|
232
|
+
...(quota?.cpu && { cpuQuota: quota.cpu }),
|
|
233
|
+
...(quota?.memory && { memoryQuota: quota.memory }),
|
|
234
|
+
...(quota?.maxPods !== undefined && { maxPods: quota.maxPods }),
|
|
235
|
+
defaultCpuRequest: "250m",
|
|
236
|
+
defaultMemoryRequest: "512Mi",
|
|
237
|
+
defaultCpuLimit: "1",
|
|
238
|
+
defaultMemoryLimit: "4Gi",
|
|
239
|
+
defaultDenyIngress: true,
|
|
240
|
+
labels: {
|
|
241
|
+
"pod-security.kubernetes.io/enforce": "baseline",
|
|
242
|
+
"pod-security.kubernetes.io/enforce-version": "latest",
|
|
243
|
+
"pod-security.kubernetes.io/warn": "restricted",
|
|
244
|
+
"pod-security.kubernetes.io/audit": "restricted",
|
|
245
|
+
...commonLabels,
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Allow CockroachDB inter-node (26257) + HTTP UI (8080) from all region CIDRs.
|
|
250
|
+
const crdbNetworkPolicy = new NetworkPolicy(mergeDefaults({
|
|
251
|
+
metadata: {
|
|
252
|
+
name: "allow-cockroachdb",
|
|
253
|
+
namespace: ns,
|
|
254
|
+
labels: commonLabels,
|
|
255
|
+
},
|
|
256
|
+
spec: {
|
|
257
|
+
podSelector: { matchLabels: { "app.kubernetes.io/name": crdbName } },
|
|
258
|
+
policyTypes: ["Ingress"],
|
|
259
|
+
ingress: allowCidrs.length > 0
|
|
260
|
+
? [{
|
|
261
|
+
from: allowCidrs.map((cidr) => ({ ipBlock: { cidr } })),
|
|
262
|
+
ports: [
|
|
263
|
+
{ protocol: "TCP", port: 26257 },
|
|
264
|
+
{ protocol: "TCP", port: 8080 },
|
|
265
|
+
],
|
|
266
|
+
}]
|
|
267
|
+
: [],
|
|
268
|
+
},
|
|
269
|
+
}, {}));
|
|
270
|
+
|
|
271
|
+
// ── Storage ────────────────────────────────────────────────────────────────────
|
|
272
|
+
|
|
273
|
+
const { storageClass } = GcePdStorageClass({
|
|
274
|
+
name: "pd-ssd",
|
|
275
|
+
type: "pd-ssd",
|
|
276
|
+
labels: commonLabels,
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// ── CockroachDB Cluster ────────────────────────────────────────────────────────
|
|
280
|
+
|
|
281
|
+
const crdb = CockroachDbCluster({
|
|
282
|
+
name: crdbName,
|
|
283
|
+
namespace: ns,
|
|
284
|
+
replicas: crdbProps.replicas ?? 3,
|
|
285
|
+
image: crdbProps.image,
|
|
286
|
+
storageSize: crdbProps.storageSize,
|
|
287
|
+
storageClassName: "pd-ssd",
|
|
288
|
+
cpuLimit: crdbProps.cpuLimit,
|
|
289
|
+
memoryLimit: crdbProps.memoryLimit,
|
|
290
|
+
locality: crdbProps.locality,
|
|
291
|
+
joinAddresses: crdbProps.joinAddresses,
|
|
292
|
+
secure: true,
|
|
293
|
+
skipCertGen: true,
|
|
294
|
+
skipInit: crdbProps.skipInit,
|
|
295
|
+
mountClientCerts: crdbProps.mountClientCerts,
|
|
296
|
+
advertiseHostDomain: crdbProps.advertiseHostDomain,
|
|
297
|
+
extraCertNodeAddresses: crdbProps.extraCertNodeAddresses,
|
|
298
|
+
labels: commonLabels,
|
|
299
|
+
defaults: {
|
|
300
|
+
serviceAccount: {
|
|
301
|
+
metadata: {
|
|
302
|
+
annotations: {
|
|
303
|
+
"iam.gke.io/gcp-service-account": crdbGsaEmail,
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
publicService: {
|
|
308
|
+
metadata: {
|
|
309
|
+
annotations: {
|
|
310
|
+
"cloud.google.com/backend-config": `{"default":"${crdbName}-ui-backend"}`,
|
|
311
|
+
"cloud.google.com/app-protocols": `{"http":"HTTPS"}`,
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
headlessService: {
|
|
316
|
+
metadata: {
|
|
317
|
+
annotations: {
|
|
318
|
+
"external-dns.alpha.kubernetes.io/hostname": internalDomain,
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// ── External Secrets (TLS certs from GCP Secret Manager) ──────────────────────
|
|
326
|
+
|
|
327
|
+
const secretStoreName = "gcp-secret-manager";
|
|
328
|
+
|
|
329
|
+
const gcpSecretStore = new ClusterSecretStore({
|
|
330
|
+
metadata: { name: secretStoreName },
|
|
331
|
+
spec: {
|
|
332
|
+
provider: {
|
|
333
|
+
gcpsm: {
|
|
334
|
+
projectID: projectId,
|
|
335
|
+
auth: {
|
|
336
|
+
workloadIdentity: {
|
|
337
|
+
clusterLocation: clusterRegion,
|
|
338
|
+
clusterName,
|
|
339
|
+
serviceAccountRef: { name: "external-secrets-sa", namespace: "kube-system" },
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
const nodeCertsSecret = new ExternalSecret({
|
|
348
|
+
metadata: { name: `${crdbName}-node-certs-eso`, namespace: ns },
|
|
349
|
+
spec: {
|
|
350
|
+
refreshInterval: "1h",
|
|
351
|
+
secretStoreRef: { name: secretStoreName, kind: "ClusterSecretStore" },
|
|
352
|
+
target: { name: `${crdbName}-node-certs`, creationPolicy: "Owner" },
|
|
353
|
+
data: [
|
|
354
|
+
{ secretKey: "ca.crt", remoteRef: { key: tls.gcpSecretNames.ca } },
|
|
355
|
+
{ secretKey: "node.crt", remoteRef: { key: tls.gcpSecretNames.nodeCrt } },
|
|
356
|
+
{ secretKey: "node.key", remoteRef: { key: tls.gcpSecretNames.nodeKey } },
|
|
357
|
+
],
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
const clientCertsSecret = new ExternalSecret({
|
|
362
|
+
metadata: { name: `${crdbName}-client-certs-eso`, namespace: ns },
|
|
363
|
+
spec: {
|
|
364
|
+
refreshInterval: "1h",
|
|
365
|
+
secretStoreRef: { name: secretStoreName, kind: "ClusterSecretStore" },
|
|
366
|
+
target: { name: `${crdbName}-client-certs`, creationPolicy: "Owner" },
|
|
367
|
+
data: [
|
|
368
|
+
{ secretKey: "ca.crt", remoteRef: { key: tls.gcpSecretNames.ca } },
|
|
369
|
+
{ secretKey: "client.root.crt", remoteRef: { key: tls.gcpSecretNames.clientRootCrt } },
|
|
370
|
+
{ secretKey: "client.root.key", remoteRef: { key: tls.gcpSecretNames.clientRootKey } },
|
|
371
|
+
],
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// ── TLS: Managed Certificate + FrontendConfig ─────────────────────────────────
|
|
376
|
+
|
|
377
|
+
const certName = `${crdbName}-ui-cert`;
|
|
378
|
+
const frontendName = `${crdbName}-ui-frontend`;
|
|
379
|
+
const backendName = `${crdbName}-ui-backend`;
|
|
380
|
+
|
|
381
|
+
const crdbManagedCert = new ManagedCertificate({
|
|
382
|
+
metadata: {
|
|
383
|
+
name: certName,
|
|
384
|
+
namespace: ns,
|
|
385
|
+
labels: commonLabels,
|
|
386
|
+
},
|
|
387
|
+
spec: { domains: [domain] },
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
const crdbFrontendConfig = new FrontendConfig({
|
|
391
|
+
metadata: {
|
|
392
|
+
name: frontendName,
|
|
393
|
+
namespace: ns,
|
|
394
|
+
labels: commonLabels,
|
|
395
|
+
},
|
|
396
|
+
spec: {
|
|
397
|
+
redirectToHttps: {
|
|
398
|
+
enabled: true,
|
|
399
|
+
responseCodeName: "MOVED_PERMANENTLY_DEFAULT",
|
|
400
|
+
},
|
|
401
|
+
},
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
// ── GCE Ingress ────────────────────────────────────────────────────────────────
|
|
405
|
+
|
|
406
|
+
const { ingress: gceIngress } = GceIngress({
|
|
407
|
+
name: `${crdbName}-ui`,
|
|
408
|
+
hosts: [{
|
|
409
|
+
hostname: domain,
|
|
410
|
+
paths: [{ path: "/", serviceName: `${crdbName}-public`, servicePort: 8080 }],
|
|
411
|
+
}],
|
|
412
|
+
namespace: ns,
|
|
413
|
+
managedCertificate: certName,
|
|
414
|
+
frontendConfig: frontendName,
|
|
415
|
+
labels: commonLabels,
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// ── ExternalDNS ────────────────────────────────────────────────────────────────
|
|
419
|
+
|
|
420
|
+
const dnsAgent = GkeExternalDnsAgent({
|
|
421
|
+
gcpServiceAccountEmail: externalDnsGsaEmail,
|
|
422
|
+
gcpProjectId: projectId,
|
|
423
|
+
domainFilters: [publicRootDomain, internalDomain.split(".").slice(1).join(".")],
|
|
424
|
+
txtOwnerId: clusterName,
|
|
425
|
+
source: ["service", "ingress"],
|
|
426
|
+
labels: commonLabels,
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
// ── Result object ─────────────────────────────────────────────────────────────
|
|
430
|
+
|
|
431
|
+
const result: Record<string, any> = {
|
|
432
|
+
// Namespace
|
|
433
|
+
...nsResult,
|
|
434
|
+
crdbNetworkPolicy,
|
|
435
|
+
|
|
436
|
+
// Storage
|
|
437
|
+
storageClass,
|
|
438
|
+
|
|
439
|
+
// CockroachDB
|
|
440
|
+
cockroachdbServiceAccount: crdb.serviceAccount,
|
|
441
|
+
cockroachdbRole: crdb.role,
|
|
442
|
+
cockroachdbRoleBinding: crdb.roleBinding,
|
|
443
|
+
cockroachdbClusterRole: crdb.clusterRole,
|
|
444
|
+
cockroachdbClusterRoleBinding: crdb.clusterRoleBinding,
|
|
445
|
+
cockroachdbPublicService: crdb.publicService,
|
|
446
|
+
cockroachdbHeadlessService: crdb.headlessService,
|
|
447
|
+
cockroachdbPdb: crdb.pdb,
|
|
448
|
+
cockroachdbStatefulSet: crdb.statefulSet,
|
|
449
|
+
...(crdb.initJob && { cockroachdbInitJob: crdb.initJob }),
|
|
450
|
+
|
|
451
|
+
// External Secrets
|
|
452
|
+
gcpSecretStore,
|
|
453
|
+
nodeCertsSecret,
|
|
454
|
+
clientCertsSecret,
|
|
455
|
+
|
|
456
|
+
// TLS
|
|
457
|
+
crdbManagedCert,
|
|
458
|
+
crdbFrontendConfig,
|
|
459
|
+
|
|
460
|
+
// Ingress + ExternalDNS
|
|
461
|
+
gceIngress,
|
|
462
|
+
dnsDeployment: dnsAgent.deployment,
|
|
463
|
+
dnsServiceAccount: dnsAgent.serviceAccount,
|
|
464
|
+
dnsClusterRole: dnsAgent.clusterRole,
|
|
465
|
+
dnsClusterRoleBinding: dnsAgent.clusterRoleBinding,
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
// Optional: Cloud Armor BackendConfig
|
|
469
|
+
if (cloudArmor) {
|
|
470
|
+
result.crdbBackendConfig = new BackendConfig({
|
|
471
|
+
metadata: {
|
|
472
|
+
name: backendName,
|
|
473
|
+
namespace: ns,
|
|
474
|
+
labels: commonLabels,
|
|
475
|
+
},
|
|
476
|
+
spec: {
|
|
477
|
+
securityPolicy: { name: cloudArmor.policyName },
|
|
478
|
+
healthCheck: { type: "HTTPS", requestPath: "/health", port: 8080 },
|
|
479
|
+
},
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Optional: Prometheus monitoring
|
|
484
|
+
if (monitoring) {
|
|
485
|
+
result.prometheusConfig = new ConfigMap({
|
|
486
|
+
metadata: { name: "prometheus-config", namespace: ns },
|
|
487
|
+
data: {
|
|
488
|
+
"prometheus.yml": [
|
|
489
|
+
`global:`,
|
|
490
|
+
` scrape_interval: 15s`,
|
|
491
|
+
`scrape_configs:`,
|
|
492
|
+
` - job_name: "cockroachdb"`,
|
|
493
|
+
` kubernetes_sd_configs:`,
|
|
494
|
+
` - role: pod`,
|
|
495
|
+
` namespaces:`,
|
|
496
|
+
` names: ["${ns}"]`,
|
|
497
|
+
` relabel_configs:`,
|
|
498
|
+
` - source_labels: [__meta_kubernetes_pod_label_app_kubernetes_io_name]`,
|
|
499
|
+
` regex: "cockroachdb"`,
|
|
500
|
+
` action: keep`,
|
|
501
|
+
` - source_labels: [__meta_kubernetes_pod_ip]`,
|
|
502
|
+
` target_label: __address__`,
|
|
503
|
+
` replacement: "$1:8080"`,
|
|
504
|
+
` - source_labels: [__meta_kubernetes_pod_name]`,
|
|
505
|
+
` target_label: instance`,
|
|
506
|
+
` metrics_path: "/_status/vars"`,
|
|
507
|
+
].join("\n"),
|
|
508
|
+
},
|
|
509
|
+
} as Record<string, unknown>);
|
|
510
|
+
|
|
511
|
+
result.prometheusDeployment = new Deployment({
|
|
512
|
+
metadata: {
|
|
513
|
+
name: "prometheus",
|
|
514
|
+
namespace: ns,
|
|
515
|
+
labels: { "app.kubernetes.io/name": "prometheus", ...commonLabels },
|
|
516
|
+
},
|
|
517
|
+
spec: {
|
|
518
|
+
replicas: 1,
|
|
519
|
+
selector: { matchLabels: { "app.kubernetes.io/name": "prometheus" } },
|
|
520
|
+
template: {
|
|
521
|
+
metadata: { labels: { "app.kubernetes.io/name": "prometheus" } },
|
|
522
|
+
spec: {
|
|
523
|
+
containers: [{
|
|
524
|
+
name: "prometheus",
|
|
525
|
+
image: "prom/prometheus:v2.51.0",
|
|
526
|
+
args: [
|
|
527
|
+
"--config.file=/etc/prometheus/prometheus.yml",
|
|
528
|
+
"--storage.tsdb.retention.time=15d",
|
|
529
|
+
],
|
|
530
|
+
ports: [{ name: "http", containerPort: 9090 }],
|
|
531
|
+
resources: {
|
|
532
|
+
requests: { cpu: "500m", memory: "1Gi" },
|
|
533
|
+
limits: { cpu: "1", memory: "2Gi" },
|
|
534
|
+
},
|
|
535
|
+
volumeMounts: [{ name: "config", mountPath: "/etc/prometheus" }],
|
|
536
|
+
}],
|
|
537
|
+
volumes: [{ name: "config", configMap: { name: "prometheus-config" } }],
|
|
538
|
+
},
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
} as Record<string, unknown>);
|
|
542
|
+
|
|
543
|
+
result.prometheusService = new Service({
|
|
544
|
+
metadata: { name: "prometheus", namespace: ns },
|
|
545
|
+
spec: {
|
|
546
|
+
selector: { "app.kubernetes.io/name": "prometheus" },
|
|
547
|
+
ports: [{ name: "http", port: 9090, targetPort: "http" }],
|
|
548
|
+
},
|
|
549
|
+
} as Record<string, unknown>);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
return result;
|
|
553
|
+
}, "CockroachDbRegionStack");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, test, expect,
|
|
1
|
+
import { describe, test, expect, vi } from "vitest";
|
|
2
2
|
import { isCompositeInstance } from "@intentius/chant";
|
|
3
3
|
import { emitYAML } from "@intentius/chant/yaml";
|
|
4
4
|
import { WebApp } from "./web-app";
|
|
@@ -899,7 +899,7 @@ describe("NamespaceEnv", () => {
|
|
|
899
899
|
});
|
|
900
900
|
|
|
901
901
|
test("warns when quota set without limits", () => {
|
|
902
|
-
const warnSpy =
|
|
902
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
903
903
|
NamespaceEnv({ name: "warn-test", cpuQuota: "4", defaultDenyIngress: false });
|
|
904
904
|
expect(warnSpy).toHaveBeenCalledWith(
|
|
905
905
|
expect.stringContaining("ResourceQuota set but no LimitRange defaults"),
|
|
@@ -908,7 +908,7 @@ describe("NamespaceEnv", () => {
|
|
|
908
908
|
});
|
|
909
909
|
|
|
910
910
|
test("no warning when both quota and limits set", () => {
|
|
911
|
-
const warnSpy =
|
|
911
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
912
912
|
NamespaceEnv({
|
|
913
913
|
name: "both-test",
|
|
914
914
|
cpuQuota: "4",
|
|
@@ -920,7 +920,7 @@ describe("NamespaceEnv", () => {
|
|
|
920
920
|
});
|
|
921
921
|
|
|
922
922
|
test("no warning when limits only (no quota)", () => {
|
|
923
|
-
const warnSpy =
|
|
923
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
924
924
|
NamespaceEnv({
|
|
925
925
|
name: "limits-only",
|
|
926
926
|
defaultCpuRequest: "100m",
|
package/src/composites/index.ts
CHANGED
|
@@ -73,6 +73,8 @@ export { AksExternalDnsAgent } from "./aks-external-dns-agent";
|
|
|
73
73
|
export type { AksExternalDnsAgentProps, AksExternalDnsAgentResult } from "./aks-external-dns-agent";
|
|
74
74
|
export { CockroachDbCluster } from "./cockroachdb-cluster";
|
|
75
75
|
export type { CockroachDbClusterProps, CockroachDbClusterResult } from "./cockroachdb-cluster";
|
|
76
|
+
export { CockroachDbRegionStack } from "./cockroachdb-region-stack";
|
|
77
|
+
export type { CockroachDbRegionStackConfig, CockroachDbRegionCockroachConfig, CockroachDbRegionTlsConfig } from "./cockroachdb-region-stack";
|
|
76
78
|
export { RayCluster } from "./ray-cluster";
|
|
77
79
|
export type { RayClusterProps, RayClusterResult, RayClusterSpec, ResourceSpec, HeadGroupSpec, WorkerGroupSpec } from "./ray-cluster";
|
|
78
80
|
export { RayJob } from "./ray-job";
|
package/src/coverage.test.ts
CHANGED
package/src/crd/loader.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* live cluster introspection via kubectl.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { existsSync, readFileSync } from "fs";
|
|
8
9
|
import type { CRDSource, CRDSpec } from "./types";
|
|
9
10
|
import type { K8sParseResult } from "../spec/parse";
|
|
10
11
|
import { parseCRD } from "./parser";
|
|
@@ -53,13 +54,11 @@ async function loadFromFile(source: CRDSource): Promise<string> {
|
|
|
53
54
|
throw new Error("CRD source type 'file' requires a 'path' property");
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
const exists = await file.exists();
|
|
58
|
-
if (!exists) {
|
|
57
|
+
if (!existsSync(source.path)) {
|
|
59
58
|
throw new Error(`CRD file not found: ${source.path}`);
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
return
|
|
61
|
+
return readFileSync(source.path, "utf8");
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
/**
|
|
@@ -85,28 +84,21 @@ async function loadFromURL(source: CRDSource): Promise<string> {
|
|
|
85
84
|
* requires kubectl access and proper authentication.
|
|
86
85
|
*/
|
|
87
86
|
async function loadFromCluster(source: CRDSource): Promise<string> {
|
|
88
|
-
const
|
|
89
|
-
const
|
|
87
|
+
const { execFile } = await import("child_process");
|
|
88
|
+
const { promisify } = await import("util");
|
|
89
|
+
const execFileAsync = promisify(execFile);
|
|
90
90
|
|
|
91
|
-
const args = ["
|
|
92
|
-
if (
|
|
93
|
-
if (
|
|
91
|
+
const args = ["get", "crds", "-o", "yaml"];
|
|
92
|
+
if (source.context) args.push(`--context=${source.context}`);
|
|
93
|
+
if (source.namespace) args.push(`--namespace=${source.namespace}`);
|
|
94
94
|
|
|
95
|
-
const
|
|
96
|
-
stdout: "pipe",
|
|
97
|
-
stderr: "pipe",
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
const stdout = await new Response(proc.stdout).text();
|
|
101
|
-
const stderr = await new Response(proc.stderr).text();
|
|
102
|
-
const exitCode = await proc.exited;
|
|
103
|
-
|
|
104
|
-
if (exitCode !== 0) {
|
|
95
|
+
const { stdout, stderr } = await execFileAsync("kubectl", args).catch((err: NodeJS.ErrnoException & { stdout?: string; stderr?: string }) => {
|
|
105
96
|
throw new Error(
|
|
106
|
-
`kubectl failed
|
|
97
|
+
`kubectl failed: ${err.stderr?.trim() || err.message}. ` +
|
|
107
98
|
"Ensure kubectl is installed and configured with access to the target cluster.",
|
|
108
99
|
);
|
|
109
|
-
}
|
|
100
|
+
});
|
|
110
101
|
|
|
102
|
+
void stderr;
|
|
111
103
|
return stdout;
|
|
112
104
|
}
|
package/src/crd/parser.test.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -21,7 +21,7 @@ export {
|
|
|
21
21
|
BatchJob, SecureIngress, ConfiguredApp, SidecarApp, MonitoredService, NetworkIsolatedApp,
|
|
22
22
|
IrsaServiceAccount, AlbIngress, EbsStorageClass, EfsStorageClass, FluentBitAgent, ExternalDnsAgent, AdotCollector,
|
|
23
23
|
MetricsServer, WorkloadIdentityServiceAccount, GcePdStorageClass, FilestoreStorageClass, GkeGateway, ConfigConnectorContext,
|
|
24
|
-
GceIngress, CockroachDbCluster,
|
|
24
|
+
GceIngress, CockroachDbCluster, CockroachDbRegionStack,
|
|
25
25
|
RayCluster, RayJob, RayService,
|
|
26
26
|
AgicIngress, AzureDiskStorageClass, AzureFileStorageClass, AzureMonitorCollector,
|
|
27
27
|
AksWorkloadIdentityServiceAccount,
|
|
@@ -45,6 +45,7 @@ export type {
|
|
|
45
45
|
GkeGatewayProps, GkeGatewayResult,
|
|
46
46
|
ConfigConnectorContextProps, ConfigConnectorContextResult,
|
|
47
47
|
GceIngressProps, GceIngressResult, CockroachDbClusterProps, CockroachDbClusterResult,
|
|
48
|
+
CockroachDbRegionStackConfig, CockroachDbRegionCockroachConfig, CockroachDbRegionTlsConfig,
|
|
48
49
|
RayClusterProps, RayClusterResult, RayClusterSpec, ResourceSpec, HeadGroupSpec, WorkerGroupSpec,
|
|
49
50
|
RayJobProps, RayJobResult,
|
|
50
51
|
RayServiceProps, RayServiceResult,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, test, expect } from "
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
2
|
import { hardcodedNamespaceRule } from "./hardcoded-namespace";
|
|
3
3
|
import { latestImageTagRule } from "./latest-image-tag";
|
|
4
4
|
import { missingResourceLimitsRule } from "./missing-resource-limits";
|
package/src/lsp/hover.test.ts
CHANGED
package/src/package-cli.ts
CHANGED
package/src/plugin.test.ts
CHANGED
package/src/plugin.ts
CHANGED
|
@@ -5,24 +5,26 @@
|
|
|
5
5
|
* lint rules, and LSP/MCP integration for Kubernetes manifests.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { createRequire } from "module";
|
|
9
8
|
import type { LexiconPlugin, InitTemplateSet, ResourceMetadata } from "@intentius/chant/lexicon";
|
|
10
|
-
const require = createRequire(import.meta.url);
|
|
11
9
|
import type { LintRule } from "@intentius/chant/lint/rule";
|
|
12
10
|
import { discoverPostSynthChecks } from "@intentius/chant/lint/discover";
|
|
13
11
|
import { createSkillsLoader, createDiffTool, createCatalogResource } from "@intentius/chant/lexicon-plugin-helpers";
|
|
14
12
|
import { join, dirname } from "path";
|
|
15
13
|
import { fileURLToPath } from "url";
|
|
16
14
|
import { k8sSerializer } from "./serializer";
|
|
15
|
+
import { hardcodedNamespaceRule } from "./lint/rules/hardcoded-namespace";
|
|
16
|
+
import { latestImageTagRule } from "./lint/rules/latest-image-tag";
|
|
17
|
+
import { missingResourceLimitsRule } from "./lint/rules/missing-resource-limits";
|
|
18
|
+
import { k8sCompletions } from "./lsp/completions";
|
|
19
|
+
import { k8sHover } from "./lsp/hover";
|
|
20
|
+
import { K8sParser } from "./import/parser";
|
|
21
|
+
import { K8sGenerator } from "./import/generator";
|
|
17
22
|
|
|
18
23
|
export const k8sPlugin: LexiconPlugin = {
|
|
19
24
|
name: "k8s",
|
|
20
25
|
serializer: k8sSerializer,
|
|
21
26
|
|
|
22
27
|
lintRules(): LintRule[] {
|
|
23
|
-
const { hardcodedNamespaceRule } = require("./lint/rules/hardcoded-namespace");
|
|
24
|
-
const { latestImageTagRule } = require("./lint/rules/latest-image-tag");
|
|
25
|
-
const { missingResourceLimitsRule } = require("./lint/rules/missing-resource-limits");
|
|
26
28
|
return [hardcodedNamespaceRule, latestImageTagRule, missingResourceLimitsRule];
|
|
27
29
|
},
|
|
28
30
|
|
|
@@ -204,22 +206,18 @@ export const service = new Service({
|
|
|
204
206
|
},
|
|
205
207
|
|
|
206
208
|
completionProvider(ctx: import("@intentius/chant/lsp/types").CompletionContext) {
|
|
207
|
-
const { k8sCompletions } = require("./lsp/completions");
|
|
208
209
|
return k8sCompletions(ctx);
|
|
209
210
|
},
|
|
210
211
|
|
|
211
212
|
hoverProvider(ctx: import("@intentius/chant/lsp/types").HoverContext) {
|
|
212
|
-
const { k8sHover } = require("./lsp/hover");
|
|
213
213
|
return k8sHover(ctx);
|
|
214
214
|
},
|
|
215
215
|
|
|
216
216
|
templateParser() {
|
|
217
|
-
const { K8sParser } = require("./import/parser");
|
|
218
217
|
return new K8sParser();
|
|
219
218
|
},
|
|
220
219
|
|
|
221
220
|
templateGenerator() {
|
|
222
|
-
const { K8sGenerator } = require("./import/generator");
|
|
223
221
|
return new K8sGenerator();
|
|
224
222
|
},
|
|
225
223
|
|
package/src/serializer.test.ts
CHANGED
package/src/spec/fetch.test.ts
CHANGED
package/src/spec/parse.test.ts
CHANGED
package/src/validate-cli.ts
CHANGED
package/src/validate.test.ts
CHANGED